Legal Information

Microsoft® Windows NT®
Version 4.0
Device Driver Kit

THIS DOCUMENT IS PROVIDED FOR INFORMATIONAL PURPOSES ONLY AND MICROSOFT CORPORATION MAKES NO WARRANTIES, EITHER EXPRESS OR IMPLIED, IN THIS DOCUMENT. THE ENTIRE RISK OF THE USE OR RESULTS OF THE USE OF THIS DOCUMENT REMAINS WITH THE USER.

Information in this document is subject to change without notice. Companies, names, and data used in examples herein are fictitious unless otherwise noted. No part of this document may be reproduced or transmitted in any form or by any means, electronic or mechanical, for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents or pending patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. The furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property rights except as expressly provided in any written license agreement from Microsoft.

© 1985-96 Microsoft Corporation. All rights reserved.

Microsoft, Microsoft Press, MS, MS-DOS, Visual Basic, Visual C++, Windows, Win32, Win32s, and Windows NT are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries. OS/2 is a registered trademark licensed to Microsoft Corporation.
PostScript is a trademark of Adobe Systems, Inc.

Macintosh and TrueType are registered trademarks of Apple Computer, Inc.

Sound Blaster and Sound Blaster Pro are trademarks of Creative Technology, Ltd.

Alpha AXP, DEC, and VAX are trademarks of Digital Equipment Corporation.

Kodak is a registered trademark of Eastman Kodak Company.

Hewlett-Packard, HP, LaserJet, Openview, and PCL are registered trademarks of Hewlett-Packard Company.

Intel and Pentium are registered trademarks; i386, i486, and Indeo are trademarks of Intel Corporation.

AT, IBM, Micro Channel, OS/2, and XGA are registered trademarks, and PC/XT and RISC System/6000 are trademarks of International Business Machines Corporation.

Lotus is a registered trademark of Lotus Development Corporation.

Novell and NetWare are registered trademarks of Novell, Inc.

Silicon Graphics is a registered trademark and OpenGL is a trademark of Silicon Graphics, Inc.

Stacker is a registered trademark of STAC Electronics.

Java is a trademark of Sun Microsystems, Incorporated.

Paintbrush is a trademark of Wordstar Atlanta Technology Center.

UNIX is a registered trademark in the United States and other countries, licensed exclusively through X/Open Company, Ltd.

Other product and company names mentioned herein may be the trademarks of their respective owners.


Microsoft Windows NT DDK Documentation Survey

The Microsoft Windows NT DDK Documentation Group asks you take a few minutes to provide some information regarding the current DDK documentation, how it is used, and who uses it. If you’ll copy and paste your response into your email system, we’ll use the information to improve future versions of this documentation.

Please copy this completed survey to the clipboard and email to ddksurv1@microsoft.com
1. Which on-line manuals do you use most frequently?

2. Which topics in these manuals are of most use to you?

3. Are the topics organized logically for the work you do?

4. Is it easy to navigate through the on-line information (sufficient jumps, etc.)?

5. Is the information presented in a usable form (enough graphics, etc.)?

6. Have you detected any errors or missing information? Please provide specific details.

7. Can you suggest any additional improvements you would like to see in this documentation?

8. What kind(s) of driver(s) are you writing or have written?

9. Which operating system environments are you writing drivers for?

10. What is your technical background?

11. How many years have you been a device driver writer?

12. Would you be willing to be contacted to discuss your survey responses in more detail? If so, please supply your business telephone number.

The DDK Documentation Group thanks you for your time. Please copy this form and paste it into your email document and send to:

ddksurv1@microsoft.com

If you prefer to mail a hard copy response, please send the survey to:

DDK Documentation Survey

One Microsoft Way

Redmond, WA

98052-6399, USA


Release Notes for V4.0

This document provides a concise overview of how Windows NT® Version 4.0 impacts device driver writers. This information is useful to both new and experienced driver writers and is divided into the following sections:

  • What’s New for This Release

  • Hints and Tips

  • Documentation Changes for V4.0


What’s New for This Release

This section describes enhancements, new features, and changes that are relevant to device driver writers. This information is divided into the following categories:

  • Driver Development and Debugging

  • Kernel-Mode Drivers

  • Graphics Drivers

  • Multimedia Drivers

  • Network Drivers

See Documentation Changes for V4.0 for information on how the documentation has been revised.


Driver Development and Debugging

The following changes are important for driver development:

  • New Debugger Command for Displaying IRPs

    Due to changes in the way IRPs are allocated and stored, !irpzone is no longer a supported debugger extension command. See the Programmer’s Guide for more information on the replacement command, !irpfind.

  • New Debugger Command for Examining Lookaside Lists

    Use the new !lookaside debugger extension command to examine lookaside lists. See the Programmer's Guide for more information on this command. See the Kernel-Mode Driver Reference for more information on lookaside lists.


Kernel-Mode Drivers

The following sections describe enhancements that are relevant to kernel-mode drivers. See the Kernel-Mode Driver Design Guide and Kernel-Mode Driver Reference for further details.

  • Synchronization and Timers

  • Queues and Memory

  • IRPs and I/O Cancellation

  • The Registry

  • I/O Control Codes (IOCTLs)

  • Tape Drivers


Synchronization and Timers

  • New Periodic Timers

    The new KeSetTimerEx routine accepts an optional Period parameter that specifies a recurring interval for the timer. Periodic timers are more efficient than resetting a timer manually because periodic timers do not incur an extra trip through the kernel. Periodic timers also do not drift.

  • New Synchronization Timers

    Previously a timer was always a notification timer, but now a timer can be a notification timer or a synchronization timer.

    When a notification timer is signaled, all waiting threads have their wait satisfied and the state of the timer remains Signaled until it is explicitly reset. When a synchronization timer expires, its state is set to Signaled until a single waiting thread is released and then the timer is reset to the Not-Signaled state.

    KeInitializeTimer always creates notification timers. The new KeInitializeTimerEx routine accepts a Type parameter which can be NotificationTimer or SynchronizationTimer.

    Both notification and synchronization timers can have an associated CustomTimerDpc routine.

  • New IoCreateNotificationEvent routine

    This new routine creates or opens a named notification event. Notification events notify one or more threads of execution that an event has occurred. When a notification event is Signaled, it remains in that state until it is explicitly set to Not-Signaled.


Queues and Memory

  • New S-Lists

    S-Lists are multiprocessor-safe, sequenced, singly linked lists that can be used instead of simple, singly linked lists. S-Lists can provide significant performance improvements over simple lists because the routines do not use spin locks for synchronization on most platforms. See the Ex..SList.. routines for more information.

  • New Lookaside Lists

    Lookaside lists are efficient, multiprocessor-safe mechanisms for managing pools of fixed-size entries from either paged or nonpaged memory. Lookaside lists are more efficient than zones because the routines do not use spin locks on most platforms. See the Ex..LookasideList routines for more information.

  • New Interlocked Routines

    InterlockedCompareExchange compares a pointer value with a destination pointer value. If the values are equal, then a third pointer value is stored in the destination; otherwise, no exchange is performed. The compare and exchange are performed as an atomic operation.

    InterlockedExchangeAdd adds a value to a given integer as an atomic operation and returns the initial value of the given integer. This routine is faster than ExInterlockedAddUlong because it is inlined and it does not require a spin lock.


IRPs and I/O Cancellation

  • Change in Spin Lock Usage and Return Value for IoSetCancelRoutine

    If a driver manages its own queue(s) of IRPs, it does not need to hold the cancel spin lock when calling IoSetCancelRoutine. IoSetCancelRoutine now uses an interlocked exchange intrinsic to set the address of a cancel routine in an IRP as an atomic operation. Drivers that manage their own queues should move calls to IoSetCancelRoutine out of code holding the cancel spin lock, to reduce contention for the spin lock and possibly improve performance.

    If a driver uses the I/O-manager-supplied device queue in the device object and uses the IoStart..Packet..routines, then the driver must still hold the system cancel spin lock when calling IoSetCancelRoutine.

    IoSetCancelRoutine now returns the previous value of
    Irp->CancelRoutine, which is of type PDRIVER_CANCEL.

  • Optional Additional Pointers in IRP When Not Using the Device Queue

    If a driver is not using the DeviceQueueEntry field in the IRP, it can use that field to store up to four pointers. The driver can use those pointers for any purpose as long as it owns the IRP. For example, one pointer could point to the listhead for the queue in which the IRP resides. The definition of that field in the IRP has been expanded as follows:

    union {
        KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
        struct {
            PVOID DriverContext[4];
        };
    };


The Registry

  • Enhanced Scatter/Gather List Support for Very Large SCSI I/O’s

    The way that a miniport driver interprets NumberOfPhysicalBreaks in PORT_CONFIGURATION_INFORMATION has changed to take full advantage of the capabilities of a controller in some specialized cases where very large I/Os are necessary. NumberOfPhysicalBreaks is either set to SP_UNINITIALIZED_VALUE or to some valid number of physical breaks.

  • New Registry Subkey for SCSI Scatter/Gather List Size

    The new registry subkey to specify the maximum number of scatter/gather list elements for each device on a given bus is:
    \Registry\Machine\System\CurrentControlSet\Services\DriverName\
    Parameters\Device     n\MaximumSGList

    DriverName is the name of the miniport driver, such as AIC78xx, and n is the bus number assigned at initialization. If a value is present in this subkey at device initialization, the SCSI port driver uses MaximumSGList as the initial value for NumberOfPhysicalBreaks. The miniport driver’s HwScsiFindAdapter routine can set NumberOfPhysicalBreaks to a lower value, if appropriate.

    The maximum value for MaximumSGList is 255, but use this adjustment sparingly because scatter/gather lists consume nonpaged memory. MaximumSGList is a REG_DWORD.

  • New Registry Subkey for Number of Concurrent SCSI Requests

    The new registry subkey to specify the maximum number of outstanding requests on a bus is:
    \Registry\Machine\System\CurrentControlSet\Services\DriverName\
    Parameters\Device     n\NumberOfRequests

    DriverName is the name of the miniport driver, such as AIC78xx, and n is the bus number assigned at initialization. This subkey is a REG_DWORD between 16 and 512. Use this adjustment sparingly because SRB extensions consume nonpaged memory.

  • New Registry Subkey for Request Timeout

    The new registry subkey to configure request timeout is:
    \Registry\Machine\System\CurrentControlSet\Services\Disk\
    TimeOutValue

    This subkey is a REG_DWORD specifying the number of seconds.

    Most systems do not need this subkey. If you have a mass storage device that is continually timing out on complex operations, you can use this subkey to set the timeout period to a slightly higher value.


I/O Control Codes (IOCTLs)

  • New Serial IOCTLs

    IOCTL_SERIAL_GET_STATS returns communication statistics for a serial device, including the number of characters transmitted/received and various errors. IOCTL_SERIAL_CLEAR_STATS resets the statistics values to zero. Serial drivers should implement these IOCTLs to support higher-level applications that require these statistics.

    If a driver does not implement these IOCTLs, it should return an error and not modify the output buffer. If the modem driver is involved in processing the I/O, it adjusts the return value from error to success but only return zeros for the counter values.

  • New Self-Monitoring Analysis and Reporting (SMART) IOCTLs

    Three new IOCTLs have been added to support SMART reporting: SMART_GET_VERSION, SMART_RCV_DRIVE_DATA, and SMART_SEND_DRIVE_COMMAND. Any IDE drivers that support SMART must implement these IOTCLs.

  • New Media Change Count in IOCTL_XXX_CHECK_VERIFY

    CD-ROM and disk drivers that handle IOCTL_XXX_CHECK_VERIFY should handle the new optional parameter for media change count. This parameter provides a more accurate way to check for media changes, increasing system reliability.

  • New Common IOCTL_STORAGE_XXX Control Codes

    New IOCTL_STORAGE_XXX control codes replace the previous IOCTL_DeviceType_XXX control codes, where DeviceType was DISK, TAPE, or CDROM. For example, IOCTL_STORAGE_RESERVE replaces IOCTL_DISK_RESERVE, IOCTL_TAPE_RESERVE, and IOCTL_CDROM_RESERVE. The new storage control codes have identical values for function code, transfer method, and required access as the previous disk, tape, and CD-ROM codes.

    Drivers that pass unrecognized IOCTLs to the SCSI class driver do not need to make any changes for these new IOCTLs.


Tape Drivers

  • New Tape Driver Model

    The tape class/subclass driver model has been changed for greater modularity and portability.

    Windows NT now provides an operating-system-specific tape class driver that implements generic tape operations and supports queuing, memory management, and communication with the system-supplied SCSI port driver. New tape-device support should be added as tape miniclass drivers, which can be portable across Microsoft operating systems. Old-style tape subclass drivers should be converted to miniclass drivers.


Graphics Drivers

The following changes are relevant to graphics drivers:

  • GDI and Graphics Drivers Must be Ported to Kernel Mode

    GDI and graphics drivers have been moved to kernel mode for improved graphics performance and memory savings. See the Appendix in the Graphics Driver Design Guide for a snapshot of the new architecture and for porting tips. The Appendix also lists new and obsolete functions.

  • Bidirectional Printer Support is Available

    Windows NT now supports language monitors. Vendors with bidirectional devices that use PJL can use the system-supplied pjlmon; other vendors can develop their own language monitors. The port monitor design has been enhanced to support bidirectional port communication. See the Graphics Driver Design Guide for details.

  • DirectDraw Driver Support is Available

    Minimal DirectDraw driver support is available to driver writers who want to offer DirectDraw support in their Windows NT 4.0 display drivers. Reference pages for supported DirectDraw driver functions and structures are found in the Graphics Driver Reference.

  • 3D Support

    Direct3D hardware support will be available in a post-4.0 Windows NT release. Hardware manufacturers targeting the games market should write Direct3D drivers for Windows 95 and keep in touch for news of availability on Windows NT. Those manufacturers targeting the mid- to high-end 3D market should consider using the Mini Client Driver (MCD) model to accelerate OpenGL applications. The MCD interface is appropriate for a wide range of 3D hardware. Manufacturers targeting the high-end market should consider moving to the Installable Client Driver (ICD) model to support OpenGL. For further information on these alternatives send email to ihv@microsoft.com with a subject line including the text “3D Drivers on Windows NT”.

    The device driver interface for 3D hardware (3D DDI) is not supported in Windows NT Version 4.0.


Multimedia Drivers

A new message has been added to the standard driver messages that user-mode multimedia drivers can receive:

  • DRV_PNPINSTALL

    The new DRV_PNPINSTALL message, like the existing DRV_INSTALL message, is sent to user-mode multimedia drivers at driver installation time. Parameters passed to the driver with the DRV_PNPINSTALL message provide Plug and Play configuration information in a format that can be passed directly to device installation functions provided by setupapi.dll.

    The DRV_PNPINSTALL message is described in the Introduction to Multimedia Drivers. Device installation functions are described in the Programmer’s Guide.


Network Drivers

NDIS 4.0 support has been added. New NDIS features of interest to developers of miniport and protocol drivers include support for the following:

  • Full-duplex operation on SMP machines

  • Multipacket sends and multipacket receive indications

  • Specifying out-of-band information, such as packet priorities and timestamps, with packet descriptors

  • Transferring ownership of received packets instead of copying

  • Asynchronous memory allocation at raised IRQL

  • Support for NDIS intermediate drivers

  • New system functions

While NDIS Version 4.0 continues to support drivers written to the NDIS Version 3.0 standard, NDIS Version 4.0 drivers can be installed only on Windows NT Version 4.0 platforms or, for binary-compatible NIC miniport drivers, on platforms running the corresponding update version of Windows® 95.


Hints and Tips

This section describes any known issues, workarounds, and suggestions:

  • Compaq LS-120 Disk Drives

  • Configuring PCMCIA Net/Modem Combo Cards

  • Controlling Driver Load Order

  • Hardware Compatibility Tests (HCT)

  • Sample PnP ISA Class Installer

  • SCSI Miniport Drivers

  • Viewing Wide Documentation Tables and Illustrations

See also the Knowledge Base section of the Windows NT DDK documentation for additional information.


Compaq LS-120 Disk Drives

  • Malfunctioning Progress Indicator When Formatting Floppy Disks

    When formatting a 1.44 MB floppy disk in a Compaq LS-120 drive, the progress indicator in the dialog box lags behind the formatting activity. After a minute or so, the progress indicator advances quickly and the format operation completes successfully.


Configuring PCMCIA Net/Modem Combo Cards

  • Configuring PCMCIA Combo Net/Modem Cards

    Registry values must be set correctly for combo net/modem cards to function properly.

    In \Machine\System\CurrentControlSet\Services\DriverName#
    \Parameters you must add ModemFunction:REG_DWORD:1 to identify the combo card. (DriverName is the name of the combo card driver and # is the instance number.) The first I/O port location, IoBaseAddress, must identify the modem location. There must be only one InterruptNumber value entry, identifying the IRQL to be shared by the components of the combo card.

    See the Programmer's Guide for information on installing drivers.


Controlling Driver Load Order

  • ServiceGroupOrder Key is Not Preserved on System Upgrade

    \Machine\System\CurrentControlSet\Control\ServiceGroupOrder is reset to its original value during a system upgrade. Drivers that modify this registry key might need to be reinstalled after a system upgrade.


Hardware Compatibility Tests (HCT)

  • No Setup Program

    There is no HCT setup program. See \hct\readme.1st on the DDK test CD-ROM for information on setting up HCT tests.


Sample PnP ISA Class Installer

The Windows NT DDK includes a sample control panel applet and class installer for a PnP (Plug and Play) ISA device. The sample illustrates providing your own device configuration user interface. It is a pared down version of the Ports applet, focusing on PnP COM ports.

The sample is with the other setup samples and includes three main files:
cpl.c | Control panel management
pnpports.c | The class installer and the dialog routines for the control panel applet
util.c | Support routines

Some of the significant routines in pnpports.c include:
PortsClassInstaller | Main entry point for the class installer.
ShortCommDlg | Main dialog routine; finds and lists all the PnP ISA COM ports.
CommDlg | Displays the COM port settings.
DoPnPAdvancedPortDialog | Displays the Advanced Port Settings dialog, creates the Properties Page, and calls setup APIs to get the resource selection property page. This routine also contains the code for respecting the "quiet install" flag (DI_QUIETINSTALL) for OEM preinstallation. If this flag is present, and the device has already been assigned a forced configuration, then the class installer should suppress any configuration dialogs.
InstallPnPSerialPort | Given a device information element, this routine installs a serial port, performs PnP configuration, and displays an advanced settings dialog to the user.

To replace the default Windows NT Ports class installer with the PnP Ports sample class installer, register pnpports.cpl as the class installer for Ports devices as follows:

  1. Run regedt32 and go to the following key:

    Machine\System\CurrentControlSet\Control\Class

  2. Locate the following subkey representing the class GUID for Ports devices:

    {4D36E978-E325-11CE-BFC1-08002BE10318}

    This subkey contains a Class value entry set to "Ports".

  3. Modify the Installer32 value entry to appear as follows:

    Installer32 : REG_SZ : pnpports.cpl,PortsClassInstaller

Any future device installer requests for Ports-class devices will go to pnpports.cpl.

See the Programmer's Guide in the Windows NT DDK documentation for more information on registering a class installer and other information about device installation.


SCSI Miniport Drivers

  • SCSI Miniports Should Not Use Both DMA and PIO

    The SCSI port driver cannot guarantee synchronization between DMA transfers and the Srb.DataBuffer that has been mapped for the miniport while the SRB is being processed. As a result, miniport drivers that use DMA should not:

    • Set both NeedPhysicalAddresses and MapBuffers to TRUE in the PORT_CONFIGURATION_INFORMATION structure.

    • Modify the contents of Srb.DataBuffer while they are processing an SRB.

    • Access data transferred from the adapter into Srb.DataBuffer.

    The SCSI port driver ensures the contents of Srb.DataBuffer are synchronized with the DMA transfer after the request has been completed (provided the miniport has flushed the DMA channel).

  • SCSI Miniport IOCTLs and ISA Adapters

    SRB_FUNCTION_IO_CONTROL requests are set up by the SCSI port driver to transfer data from the adapter. The port driver does not copy the contents of Srb.DataBuffer to any pages that the system has remapped (pages are remapped below 16 MB so they are accessible by ISA devices).

    As a result, SCSI miniport drivers should not DMA transfer data in Srb.DataBuffer directly to the adapter. Miniport drivers can transfer the data by copying the buffer’s contents into a section of non-cached extension and to the adapter from there.


Viewing Wide Documentation Tables and Illustrations

  • Print Wide Tables and Illustrations in Landscape Mode

    A few tables and illustrations in the DDK documentation are significantly wider than a normal screen window or printed page. To view these elements most effectively, print them with the printer properties set to landscape orientation. For more information on printing see also Getting Started.


Documentation Changes for V4.0

The following table outlines how the Windows NT DDK documentation has changed for this release.
Online Documentation | Changes and Status
Getting Started | Revised for V4.0 Provided in a separate help file. Added documentation roadmap, documentation conventions, and instructions on how to navigate through the documentation.
Legal Information | Revised for V4.0 Minor revisions to update version numbers, and so forth.
Release Notes | NEW for V4.0 Highlights changes for this release.
Programmer’s Guide | Revised for V4.0 Includes documentation for the new INF file format and the device installation functions provided by the Setupapi DLL. Driver debugging information has been updated and expanded.
Kernel-Mode Drivers | Revised for V4.0 Includes documentation for a variety of new features, routines, structures, IOCTLs, and so forth. The graphics material has been moved to the graphics driver manuals.
Graphics Drivers | NEW for V4.0 The Graphics Driver Design Guide provides design information about kernel-mode graphics drivers and user-mode spooler components that can be developed by IHVs. The Graphics Driver Reference contains reference pages for GDI, DDI, the DirectDraw driver model, the videoport driver, video miniport drivers, and for all printer-related components that can be developed by an IHV.
Multimedia Drivers | Revised for V4.0 Contains extensively revised introductory, MCI, audio, audio compression, and video capture chapters.
Network Drivers | Rewritten for V4.0 The Network Driver Design Guide has been completely rewritten. The Network Driver Reference also has been completely rewritten.
Virtual DOS Drivers | Same as V3.51 This material has not changed for V4.0.
Code Samples | NEW for V4.0. Code samples are now included in the online Help.
Knowledge Base | Updated for V4.0 Contains new articles.


Chapter 1 System Initialization Overview

This chapter briefly describes how hardware and software are initialized and configured. Microsoft® Windows NT® registry is used throughout the machine and operating system boot process.


1.1 Loading the System

System initialization occurs in eight phases:

Phase 1 Firmware

This phase is platform-specific.

  • In x86-based platforms, the hardware boot ROM loads a boot sector, which loads the NTLDR.

  • In most RISC-based platforms, the firmware loads necessary ARC drivers, acquires hardware configuration data, and loads the OSLOADER.

Phase 2 OS Loader

This phase sets up memory, captures hardware configuration data, constructs a description of the machine hardware in memory, and puts a pointer to this description into the loader block. NTLDR or OSLOADER then loads and relocates the kernel image, the HAL, and the driver(s) for the device and file system for the volume from which the system is booted. Any other device drivers that have a Start value of 0 are loaded at this time. For more information on Start values, see Section 1.2.3.

  • In x86-based platforms, a hardware recognizer called Ntdetect queries the ROMs, probes the hardware, and collects as much information as possible about the I/O buses and peripheral devices in the machine.

  • In RISC-based platforms, the ARC Query Config functions are called to accomplish the same thing.

Phase 3 Kernel Startup

This phase calls a routine to fetch load-control information from the registry, such as the default or user-set size of resident system-space memory (nonpaged pool) and of the registry quota.

Phase 4 CmInitSystem

The Configuration Manager initializes two subtrees under the \Registry\Machine tree in the registry, enables the registry API, and creates the CurrentControlSet symbolic link in preparation for loading device drivers. At the end of this phase, the registry namespaces under \Registry\Machine\Hardware and \Registry\Machine\System can be both read and written.

Phase 5 IoInitSystem

This phase initializes and loads the rest of the Windows NT Executive, including certain kernel-mode drivers, according to the CurrentControlSet. First, the drivers loaded into memory in Phase 2 are initialized from the boot driver list. Next, other base drivers are loaded and initialized. At the end of this phase, the system’s core drivers are all active, unless a critical driver fails its initialization and the machine is rebooted.

Phase 6 MmFreeLoaderBlock

This phase releases the loader block, some of configuration data that was collected in Phase 2, and the initial copy of the \Registry\Machine\System hive. However, the \Registry\Machine\Hardware configuration information is retained.

Phase 7 Session Manager

This phase runs certain programs (such as autocheck), calls the Windows NT Executive to open paging files so files can be written, and initializes the remainder of the registry. Before this phase, certain registry I/O requests have been cached, such as information core drivers might have written to the registry for use by still higher-level drivers that load later. At the end of this phase, all configuration information under \Registry\Machine is available.

Phase 8 Service Controller

This phase loads remaining drivers according to the CurrentControlSet and creates the LastKnownGood tree in the registry.


1.2 Loading Drivers

The following subsections briefly describe how the system initialization process works as a general guide to understanding how initialization works for any Windows NT driver.


1.2.1 Machine Initialization—Phase 2

1.2.1 Machine Initialization: Phase 2

On x86 platforms, the machine hardware is initialized at boot time by the NTLDR. To initialize the video hardware, NTLDR makes a BIOS call to set the video adapter in 80x50, 16-color, alphanumeric mode. It then clears the screen and displays the Boot Manager messages.

On RISC platforms, the entire machine, including video hardware, is initialized by the OSLOADER, which programs an appropriate alphanumeric mode to display the Boot Manager information.


1.2.2 HAL Control of Video—Phases 3 to 5

1.2.2 HAL Control of Video: Phases 3 to 5

After the operating system and boot drivers are loaded from the disk into memory by NTLDR or OSLOADER, control is passed to the Windows NT Executive.

The HAL is an Executive component that exports the platform-specific functionality. It supports limited output functionality to allow the Kernel to display informational and error messages as other components of the kernel-mode Executive are initialized.

The NTLDR’s initial BIOS call (see Section 1.2.1) is made in x86 real mode.

HAL support for video output also is necessary if the machine crashes. The HAL can be called by the Kernel to reinitialize the video adapter to an alphanumeric mode and to display information, such as a register dump and stack backtrace.


1.2.3 Kernel Driver Initialization: Phase 5

The Registry’s System hive, which is visible to users under the HKEY_LOCAL_MACHINE tree after the system is running, determines the load order of all drivers in the system. The NTLDR or OSLOADER and the I/O Manager both access the contents of the System hive to determine which drivers should be loaded and when each driver should be initialized.

Each driver key under the registry node HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services has a value entry named Start. The value assigned to Start determines when any particular driver is loaded during the system initialization process, as follows:

  • Start = 0 indicates the driver is loaded by NTLDR or OSLOADER during Phase 2.

  • Start = 1 indicates the driver is loaded by the I/O Manager during Phase 5.

  • Start = 2 indicates the driver is started by Service Controller manager.

  • Start = 3 indicates the driver must be manually started by using the services icon of the Control Panel.

  • Start = 4 indicates the driver was disabled and made unloadable.

When each driver is loaded, its DriverEntry routine is automatically called by the I/O Manager.

For each device object created, a driver may create a corresponding value entry with a name that is the same as the device object in the registry under HKEY_LOCAL_MACHINE\Hardware\DeviceMap.

Both the created value entry in the registry DeviceMap and the symbolic link are volatile: each is regenerated automatically every time the machine is booted, based on the drivers that were initialized by the I/O Manager. Both are logical names representing a device in the machine.


1.2.4 Service Controller Initialization: Phase 8

In this phase of initialization the Service Control Manager will proceed to load any remaining drivers that have a Start value of 2. These drivers will be displayed in the control panel applet as being started automatically. These drivers are loaded in order by load order group, if specified, and then all remaining drivers will be loaded alphabetically. For information on controlling load order, see Controlling Driver Load Order.


1.2.5 Controlling Driver Load Order

The first driver to claim a device obtains ownership of that device, either shared or exclusive.

Driver load order is determined by entries in the \Machine\System\CurrentControlSet key in the registry. This key has a Control subkey containing system-wide information and a Services subkey containing driver-specific information.

During system initialization, when a driver is loaded depends on the following characteristics:

  • During which phase does it load (Services\DriverName\Start value in the registry)?

  • During a given phase, in which group does it reside (\Services\DriverName\Group) and in what order does its group load (\Control\ServiceGroupOrder)?

  • Within a given group, in what order does this driver load (\Services\DriverName\Tag and \Control\GroupOrderList)?

  • Does the driver require that any prerequisite drivers or services be loaded (\Services\DriverName\DependOnGroup and DependOnService)?

Driver Start Value

A driver can have one of the following values in \Machine\System\CurrentControlSet\Services\DriverName\Start:
Start Value | When Started
SERVICE_BOOT_START (0x0) | By OS loader (Phase 2)
SERVICE_SYSTEM_START (0x01) | During OS initialization (Phase 5)
SERVICE_AUTO_START (0x02) | By Service Control Manager during startup (Phase 8)
SERVICE_DEMAND_START (0x03) | On demand, by Service Control Manager
SERVICE_DISABLED (0x04) | Not started

Driver Load Service Group

Drivers with start values of SERVICE_BOOT_START and SERVICE_SYSTEM_START can be organized into groups to further structure the order in which they are loaded.

\Machine\System\CurrentControlSet\Control\ServiceGroupOrder contains a list of the driver load service groups. The list is scanned twice. First, all drivers with start value SERVICE_BOOT_START are loaded; then all drivers with start value SERVICE_SYSTEM_START are loaded.

The ServiceGroupOrder list looks something like the following:
System Bus Extender
Scsi miniport
port
Primary disk
SCSI class
SCSI CDROM class
filter
boot file system
Base
Pointer Port
Keyboard Port
Pointer Class
Keyboard Class
Video Init
Video
Video Save
file system
Event log
Streams Drivers
PNP_TDI
NDIS
TDI
NetBIOSGroup
SpoolerGroup
NetDDEGroup
Parallel Arbitrator
extended base
RemoteValidation
PCI Configuration

If a driver is a member of a load service group, the group name is listed in the driver’s services subkey in the registry (\Machine\Hardware\System\
CurrentControlSet\Services\DriverName\Group).

After all ServiceGroupOrder groups of a given Start value have been loaded, the system loads all groups not in the list and then all drivers without a group.

Load Order Within a Group

Drivers that are in a load group and have a start value of SERVICE_BOOT_START or SERVICE_SYSTEM_START can use tags to indicate their load order within the group.

The services registry entry for the disk class driver looks something like the following:
\Machine\Hardware\System\CurrentControlSet\Services\Disk
    DependOnGroup:REG_MULTI_SZ:SCSI miniport
    ErrorControl:REG_DWORD:0  // = SERVICE_ERROR_IGNORE
    Group:REG_SZ:SCSI class
    Start:REG_DWORD:0     // = SERVICE_BOOT_START
    Type:REG_DWORD:0x1    // = SERVICE_KERNEL_DRIVER
    Tag:REG_DWORD:0x2
    :

The disk class driver is in the SCSI class group and its group tag is 2.

The registry key \Machine\Hardware\System\CurrentControlSet\Control\
GroupOrderList defines the number of tags in each group and the order in which tagged drivers are loaded within a group. An excerpt of this key including the SCSI class group looks something like the following:
\Machine\Hardware\System\CurrentControlSet\Control\GroupOrderList
        :
    SCSI CDROM Class:REG_BINARY:02 00 00 00 01 00 00 00 02 00 00 00
    SCSI Class:REG_BINARY:03 00 00 00 01 00 00 00 02 00 00 00 03...
    SCSI miniport:REG_BINARY:24 00 00 00 00 01 00 00 01 ...
        :

Within the SCSI class group, drivers with tag 1 are loaded first, then drivers with tag 2, and then drivers with tag 3. Drivers without a tag are loaded last in their group.

Not every group is included in the GroupOrderList. When a group is not in the GroupOrderList, the order in which the drivers load within the group cannot be guaranteed.

Dependencies

Prerequisite drivers are listed in a driver’s services subkey in the DependOnGroup and DependOnService entries. The services subkey for the disk class driver shown above indicates that this driver cannot be loaded until at least one driver from the SCSI miniport group has been loaded.

Defining Driver Load Order for New Drivers

To define the load order for a new driver, assign the driver a start value, a service group (optional), a group tag (optional), and prerequisite drivers (optional).

The start value must be one of the system-defined SERVICE_xxx values.

The service group can be an existing system-defined group or a new group. If you define a new group, add it to \Machine\System\CurrentControlSet
\Control\ServiceGroupOrder. Note that ServiceGroupOrder is reset during a system upgrade, so drivers that modify this registry key may need to be reinstalled after an upgrade.

Use an existing group tag, or define a new one in \Machine\System
\CurrentControlSet\Control\GroupOrderList.

List prerequisite drivers in DependOnGroup and DependOnService in the driver’s services key.

A driver writer can modify the registry using routines, INF files, or the Windows NT registry editor (regedt32). During driver development it may be convenient to edit the registry manually with the registry editor. When the driver is nearing completion, it is best for the driver to make any necessary registry changes using routines (RtlCreateRegistryKey and RtlWriteRegistryValue) or through the driver’s INF file. For more information on runtime library routines, see the Kernel-Mode Driver Reference. For more information on INF files, see Chapter 2.


Chapter 2 Driver Installation

This chapter describes the requirements and methods for installing a device driver on a machine using the Microsoft®Windows NT® operating system. Windows NT 4.0 installation methods are presented in this chapter. Most drivers are installed using the Windows NT 4.0 information (INF) files and Setup functions; however, some drivers (for example, network and multimedia) are installed with the legacy methods described in Appendix A. Driver installation can occur either during or after the installation of Windows NT on the machine.

When Windows NT is installed on a machine, the initial phase of the Windows NT Setup program installs only the minimum number of drivers needed for NT to run: keyboard, mouse, video, SCSI/Disk, and Machine/HAL. This phase of the Setup program is known as text setup. If you want the user to be able to install your driver during text setup, your distribution disk must include a text file named txtsetup.oem. The Text Setup section in this chapter discusses the format of this file.

After the text setup phase has been completed, the Setup program boots Windows NT and proceeds with the GUI-mode phase of the Windows NT installation. During this phase, the Setup program gives the user an opportunity to install network components.

After Windows NT has been installed, a user with administrator privileges can install drivers. This would be necessary, for example, if a device is added to the hardware configuration that was not present when NT was installed or if the user wants to install a different or updated driver for an existing device. For this situation, the following installation methods are available:

  • To install a display driver, keyboard driver, modem driver, mouse driver, PC Card (PCMCIA) driver, port driver, printer driver, SCSI adapter driver, tape device driver, or telephony driver, the user would run the corresponding applet in the Control Panel folder.

  • To install network components, the user would run the Networks applet (ncpa.cpl ) in the Control Panel folder. This applet calls the Windows NT Setup program, and expects to find an oemsetup.inf (or oemsetnt.inf ) file on the supplied distribution disk. For more information, see Appendix A.

  • To install a multimedia device, the user would run the Multimedia applet (multimed.cpl ) in the Control Panel folder. The driver distribution disk must contain an oemsetup.inf (or oemsetnt.inf ) file. For more information, see Appendix A.

For driver types not discussed above, the driver developer can write an installation program and provide this with the driver. The installation program would use functions in the Setup API to set up the registry and to copy files.


2.1 Driver Installation Requirements

Windows NT device driver installation includes the following components:

  • Copying the files required by the driver into the appropriate system directories.

  • Storing information about the driver in the Windows NT configuration registry.


2.1.1 Driver Files

All of the files required for the operation or configuration of a device driver are stored in the Windows NT system directory or one of its subdirectories. The various installation methods provide ways to determine the Windows system directory on the local machine. The appropriate locations in which to store the various file types are:

.sys
Copy the driver’s image file into the NT_root\system32\drivers directory.
.dll
Copy the driver’s library files into the NT_root\system32 directory.
.inf
If you distribute an oemsetup.inf (or oemsetnt.inf ) file with your driver, the installation procedure should copy it (renaming it with a unique name) into the NT_root\system32 directory.


2.1.2 Configuration Registry

The registry is a database where configuration information is stored. This database takes the place of most configuration and initialization files. Data in the registry is structured as a hierarchical tree of keys and values. The database is something like a file system, in that each key can contain both subkeys (analogous to directories) and data entries or values (analogous to files). A key will have an associated security descriptor to control access to its data. For more information about the registry, refer to the Registry overview in the Win32 online documentation.

Device drivers can use the registry to store a variety of information. In general, drivers should make use of the registry to store all data they wish to maintain across boots. In addition, drivers can access the registry to obtain information stored there by the system or by other programs or drivers. For a discussion of registry use by device drivers, refer to the Kernel-mode Drivers Design Guide.

Two trees in the registry are useful during driver installation:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

Each installed driver is required to have a key under this tree. At boot time, the system looks for a set of value entries under each driver’s key in the Services tree to determine whether and when to load the driver. A driver can store additional driver-defined data under its key in the Services tree. Information stored under this key is available to the driver during its initialization. This is the safest place to preserve data that is vital to your driver, because the data is stored in the system hive. The system takes extra precautions to protect the system hive (for example, keeping multiple copies). For additional information about the Services database, refer to the Services overview in the Win32 SDK online documentation.

HKEY_LOCAL_MACHINE\SOFTWARE

Use this tree to store information about the driver software.


2.1.2.1 The Driver’s Services Database Key

This is the required part of a driver’s installation in the registry. The complete path of this required key for an installed driver is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName. Refer to the Win32 SDK online documentation to determine the appropriate entries and values for your driver.

After the text mode phase of Windows NT Setup, the Service Controller should be used to create a service of any kind, whether kernel driver or Win32 service. If you alter the registry and treat the result as a service, the system must be restarted for the service to be recognized by the Service Controller. Refer to the Win32 SDK online documentation for more details. The information below is intended for those who must create services during text mode operation.

Creating a Service

To create a service, the following value entries must be made in the registry:

Type
Specifies the type of driver. One of the following is specified:

SERVICE_KERNEL_DRIVER (0x1)

SERVICE_FILE_SYSTEM_DRIVER (0x2)

SERVICE_ADAPTER (0x4)

Start
Specifies when to start the driver. One of the following is specified:
SERVICE_BOOT_START (0x0)
Indicates a driver started by the operating system loader, and should only be used for drivers that are essential to loading the OS (for example, Atdisk).
SERVICE_SYSTEM_START (0x1)
Indicates a driver started during OS initialization.
SERVICE_AUTO_START (0x2)
Indicates a driver started by the Service Control Manager during system startup.
SERVICE_DEMAND_START (0x3)
Indicates a driver started on demand, by the Service Control Manager.
SERVICE_DISABLED (0x4)
Indicates a driver that cannot be started.
Controlling the Load Order of Drivers

For a detailed discussion of controlling the load order of drivers, see Chapter 1.

In addition to the required values under the driver’s Services key, the following optional values can be specified to control the order in which the driver is loaded:

Group
Identifies the load order group of which this driver is a member. The HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\ServiceGroupOrder key has a list of groups that determines the order in which the system loads drivers. Examples of groups are: SCSI miniport, port, Primary disk, and so forth.
Tag
Determines the order in which this driver is started within its group. The HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Control\GroupOrderList key has values that specify a tag order vector for some groups. Tags are only evaluated for drivers that have start types of SERVICE_BOOT_START or SERVICE_SYSTEM_START.
DependOnGroup
Specifies a load order group on which the driver depends. The driver is started only if at least one member of the specified group has been started.
DependOnService
Specifies another driver or service that must be running before this driver is started.
Optional Keys and Values

You can create the following optional keys and values under a driver’s Services key:

ImagePath
A value entry that specifies the fully qualified path of the driver’s image file. If this value is not specified, the default path is NT_root\system32\Drivers\DriverName.sys, where DriverName is the name of the driver’s Services key.
Parameters
A value entry used to store any device or driver-specific data that you want. For some types of drivers, the system expects to find specific value entries.
Performance
A value entry used to specify needed information if a driver supports performance monitoring. The values under this key specify the name of the driver’s performance DLL and the names of certain exported functions in that DLL.


2.1.2.2 The HKEY_LOCAL_MACHINE\SOFTWARE Tree

This tree of the registry contains per-machine information about the software on the local computer. Per-machine information is independent of per-user configurations. To install information about your product in this tree, create a CompanyName key. Under this key, create a key for each software product. Under the product key, create a key for each version of the product. You can create value entries and additional subkeys to store whatever information you prefer.

HKEY_LOCAL_MACHINE\SOFTWARE\CompanyName\ProductName\Version


2.2 Text-Mode Setup

During the text setup phase of Windows NT installation, the Windows NT Setup program installs drivers for the following components: keyboard, mouse, video, SCSI, disk, CD-ROM, and machine/HAL. If text setup cannot find a driver for any of these components, it prompts the user to insert a disk containing the driver. The user can also select Other from a list of drivers for a component; this causes Setup to prompt the user for a disk.

If you are distributing a driver for one of these hardware components and intend to enable the user to install it during text setup, your distribution disk must include a file named txtsetup.oem. A txtsetup.oem file is a text file containing the following information:

  • Identifies the hardware components supported by this txtsetup.oem file.

  • Lists the files to copy from the distribution disk for this component.

  • Lists the registry keys and values to create for this component.


2.2.1 The txtsetup.oem File

This section describes the format of the txtsetup.oem file and includes a sample txtsetup.oem file.

A txtsetup.oem file consists of several sections that use the following general format:

[SectionName]
key = value1,value2,...

The name of the section is enclosed in square brackets, [ ]. The pound sign (#) or semicolon (;) character at the beginning of a line indicates a comment. Strings with embedded spaces, commas, or hashes must be enclosed in double-quotes (“ ”).

The following sections must be included:

Disks Section

The [Disks] section lists all disks in the disk set.

[Disks]
d1 = description,tagfile,directory
d2 = description,tagfile,directory
   .
   .
   .

d1, d2, ...
Key that can be used in subsequent sections to identify the disk.
description
Disk name string used to prompt the user to insert the disk.
tagfile
Specifies the name of the file whose presence on the disk indicates to the Setup program that the correct disk has been inserted. The filename should be specified as a full path from the root; for example, \yourtagfile.ext, but it should not specify a drive.
directory
Specifies the directory on the disk where the files are located. The directory should be specified as a full path from the root; for example, \diskdir, but it should not specify a drive.
Defaults Section

The [Defaults] section lists the default option for each hardware component supported by this file (as indicated by the presence of a [component] section for the component). The default is highlighted in the menu of options presented to the user.

[Defaults]
component = ID
    .
    .
    .

component
Specifies one of the following components: computer, display, keyboard, mouse, CD-ROM, or SCSI.
ID
Specifies a string that identifies the default option. This string matches an ID specified in the corresponding [component] section.
Component Section

A [component] section lists the options available for a particular component.

[component]
ID = description
    .
    .
    .

ID
Specifies a unique string (within this section) that identifies the option. For the computer component, if this string ends in “_up”, Setup copies the uniprocessor kernel; if this string ends in “_mp”, Setup copies the multiprocessor kernel; if it ends in neither, the results are unspecified and you might get either kernel.
Files.component.ID Section

A [Files.component.ID] section lists the files that should be copied if the user selects a particular component option. One section of this type must be present for each option listed in each component section. The component portion of the section name corresponds to the name of a [component] section, and the ID portion corresponds to an ID key in a [component] section.

[Files.component.ID]
file_type = source_disk,filename[,keyname]

filetype
Identifies the type of file. One of the following is specified:
driver
Valid for all components. File is copied to systemroot\system32\drivers.
port
Valid for keyboard, mouse, and SCSI components. Allows distinction between port and class driver -- but equivalent to driver type.
class
Valid for keyboard and mouse components. If specified, replaces the standard class driver. File is copied to systemroot\system32\drivers.
dll
Valid for all components. Useful for GDI portion of a display driver. File is copied to systemroot\system32.
hal
Valid only for computer component. File is copied to systemroot\system32\hal.dll (80386/80486), or to \os\winnt\hal.dll on the system partition (ARC).
inf
Valid for all components. Used to copy a GUI INF file for use with system maintenance setup. File is copied to systemroot\system32.
detect
Valid for the computer component (80386/80486 only). If specified, replaces the standard 80386/80486 hardware recognizer. File is copied to c:\ntdetect.com.
source_disk
Identifies the disk from which to copy the file, and must match an entry in the [Disks] section.
filename
Name of the file not including the directory path. The filename is appended to the directory specified for the disk in the [Disks] section to form the full path of the file on the disk.
DriverKey
Name of the key to be created in the registry services tree for this file, if the file is of type driver, port, or class. This value is used to form [Config.DriverKey] section names.
Config.DriverKey Section

A [Config.DriverKey] section specifies values to be set in the registry for particular component options. Required values in the Services\DriverKey key are created automatically. Use this section to specify additional keys to be created under Services\DriverKey and values under Services\DriverKey and Services\DriverKey\subkey_name.

[Config.DriverKey]
value = subkey_name,value_name,value_type,value...
    .
    .
    .

subkey_name
Specifies the name of a key under the Services\DriverKey tree in which to place the specified value. The key is created if it does not exist. If the empty string (““) is specified, the value is placed under the Services\DriverKey tree.
value_name
Specifies the name of the value to be set within the key.
value_type
A string like REG_DWORD that identifies the type of data for this value.
value
Specifies the actual value; its format depends on value_type.

The following types can be specified in the value_type field in [Config.DriverKey] sections:

REG_DWORD
One value is allowed; it must be a string of 1-8 hex digits.

For example:

value = parameters,NumberOfButtons,REG_DWORD,0X2.

REG_SZ, REG_EXPAND_SZ
One value is allowed; it is interpreted as the zero-terminated string to be stored.

For example:

value = parameters,Description,REG_SZ,"This is a text string"

REG_BINARY
One value is allowed; it is a string of hex digits, each pair of which is interpreted as a byte value.

For example (stores the byte stream 00,34,ec,4d,04,5a):

value = parameters,Data,REG_BINARY,0034eC4D045a

REG_MULTI_SZ
Multiple value arguments are allowed; each is interpreted as a component of the multisz.

For example:

value = parameters,Strings,REG_MULTI_SZ,String1,"String 2",string3

Example txtsetup.oem file

The following is an example of a txtsetup.oem file:

[Disks]
d1 = "Oem mouse driver disk 1",\oemmou01.tag,\

[Defaults]
mouse = m2

[mouse]
m1 = "Oem mouse type 1"
m2 = "Oem mouse type 2"

[Files.mouse.m1]
port = d1,m1.sys,oemmou1

[Files.mouse.m2]
port = d1,m2.sys,oemmoup
#replace the mouse class driver
class = d1,oemmoucl.sys,oemmouc

[Config.oemmou1]
value = parameters,xres,REG_DWORD,10
value = parameters,yres,REG_DWORD,20
value = parameters,description,REG_SZ,"Mouse type 1"

[Config.oemmoup]
value = parameters,xres,REG_DWORD,10
value = parameters,yres,REG_DWORD,20
value = parameters,description,REG_SZ,"Mouse type 2"

[Config.oemmouc]
value = parameters,description,REG_SZ,"Mouse class"

A single distribution disk with a single txtsetup.oem file can be used to support multiple hardware devices. To do this, the txtsetup.oem file would contain a single [Defaults] section with an entry specifying the default choice for each hardware type. The file would also contain a [Component] entry for each hardware type and the appropriate associated [Files.Component.ID] and [Config.DriverKey] entries.


2.3 INF Files

This section describes Windows NT 4.0- and Windows 95-style INF files. An INF file is a formatted reference file that contains information about installation files and devices such as filenames, version information, and so on. You can create or modify an INF file using any text editor. If you are providing a Windows NT device driver to be installed with a Control Panel application or other installation program, you must create an INF file. The INF file enables your device to be installed and to work in the Windows NT environment.


2.3.1 INF File Format Reference

The format of the Windows NT 4.0- and Windows 95-style INF files differs from the Windows NT 3.x INF files. These new INF files do not contain installation scripts. All of the installation procedures are included in the setup program; the INF file acts as a resource, containing formatting and file information.

An INF file is made up of a set of named sections. To be used by the operating system installer, a section must contain one or more items. There can be any number of sections in an INF file.

There are approximately 20 types of sections that can be used in an INF file. Each type of section has a particular purpose; for example, to install a service or add entries to the registry.

INF files must follow these general rules:

  • Sections begin with the section name enclosed in brackets.

  • Values can be expressed as replaceable strings using the form %strkey%. To use a % character in the string, use %%. The strkey must be defined in a Strings section of the INF file.

  • Each INF file must contain a Version section identifying it as a Windows NT 4.0/Windows 95-compatible file.

Three sections from a sample INF file are shown below, so you can more easily visualize the an INF file being made up of sections and each section containing one or more items. The section named Version contains five items, the section named DestinationDirs contains one item, and the section named ControlFlags contains three items.

[Version]
Signature="$Windows NT$"
Class=Mouse
ClassGUID={4D36E96F-E325-11CE-BFC1-08002BE10318}
Provider=%Provider%
LayoutFile=layout.inf

[DestinationDirs]
DefaultDestDir=12

[ControlFlags]
ExcludeFromSelect.NTAlpha=*PNP0F0D,*PNP0F11,*PNP0F00,*PNP0F02,*PNP0F15
ExcludeFromSelect.NTMIPS=*PNP0F0D,*PNP0F11,*PNP0F00,*PNP0F02,*PNP0F15
ExcludeFromSelect.NTPPC=*PNP0F0D,*PNP0F11,*PNP0F00,*PNP0F02,*PNP0F15

The remainder of this section describes the syntax and meaning of the items used in each type of INF file section. The following sections are described:

Add Registry

ClassInstall32

ControlFlags

Copy Files

Delete Registry

Delete Files

DestinationDirs

Device

EventLog Install

HW

Ini File to Registry

Install

Log Config

Manufacturer

Rename Files

Service Install

Services

SourceDisksFiles

SourceDisksNames

Strings

Update INI Fields

Update INI File

Version


Add Registry Section

The Add Registry section adds subkeys or value names to the registry, optionally setting the value. The add-registry-section name must appear in an AddReg item in an Install section.

[add-registry-section]
reg-root-string, [subkey], [value-name], [flags], [value]
[reg-root-string, [subkey], [value-name], [flags], [value]]
.
.
.

reg-root-string
Registry root name. Can be one of these values:

HKCR

Same as HKEY_CLASSES_ROOT.

HKCU

Same as HKEY_CURRENT_USER.

HKLM

Same as HKEY_LOCAL_MACHINE.

HKU

Same as HKEY_USERS.

HKR

Relative to the key passed into SetupInstallFromInfSection.

subkey
Optional. Identifies the subkey to set. Has the form key1\key2\key3....
value-name
Optional. Identifies the value name for the subkey. For string type, if the value-name parameter is left empty, the default value in the key is set.
flag
Optional. Establishes the value data type and the AddReg item action. The flag value is a bit map where the low word contains basic flags that define the general data type and AddReg item action. The high word contains values that more specifically identify the data type of the registry value. The high word is ignored by the 16-bit Windows 95 Setup functions provided by the Setupx DLL. The flags are defined as follows:
FLG_ADDREG_BINVALUETYPE
(0x00000001)
The value is “raw” data.
FLG_ADDREG_NOCLOBBER
(0x00000002)
Do not write over the registry key if it already exists.
FLG_ADDREG_DELVAL
(0x00000004)
Delete the value from the registry.
FLG_ADDREG_APPEND
(0x00000008)
Append a value to an existing value. This flag is currently supported only for REG_MULTI_SZ values.
FLG_ADDREG_TYPE_MASK
(0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
Mask.
FLG_ADDREG_TYPE_SZ (0x00000000)
Registry data type REG_SZ.
FLG_ADDREG_TYPE_MULTI_SZ
(0x00010000)
Registry data type REG_MULTI_SZ.
FLG_ADDREG_TYPE_EXPAND_SZ
(0x00020000)
Registry data type REG_EXPAND_SZ.
FLG_ADDREG_TYPE_BINARY
(0x00000000 | FLG_ADDREG_BINVALUETYPE)
Registry data type REG_BINARY.
FLG_ADDREG_TYPE_DWORD
(0x00010000 | FLG_ADDREG_BINVALUETYPE)
Registry data type REG_DWORD.
FLG_ADDREG_TYPE_NONE
(0x00020000 | FLG_ADDREG_BINVALUETYPE)
Registry data type REG_NONE.

Bit 0 of the flag distinguishes between character and binary data as it does in the Windows 95 Setup functions, thus a Windows 95 installation program will see the extended data types as REG_SZ or REG_BINARY. To allow REG_DWORD entries to be compatible with both operating systems, the following formats are supported:

  • Non-compatible format. If compatibility with Windows 95 Setup functions is not required, a REG_DWORD entry can contain a single data value field. This value can be prefixed with a sign and be either decimal or hexadecimal. For example:

    HKLM,"Software\Microsoft\Windows NT\CurrentVersion\FontDPI","LogPixels",0x10001,120

  • Windows 95 compatible format. If compatibility with Windows 95 Setup functions is required, the data of a FLG_ADDREG_TYPE_DWORD entry must be formatted like a REG_BINARY entry. The Windows NT Setup functions recognize a REG_DWORD line with exactly four data elements as conforming to this format. The Setup functions interpret the four data elements as one DWORD. Hexadecimal number fields are only supported by the Windows 95 Setup functions as members of a REG_BINARY data list, in which case the data is assumed to be in hexadecimal format (the 0x prefix must not be used). The example listed above can be written in Windows 95-compatible format as follows:

    HKLM,"Software\Microsoft\Windows NT\CurrentVersion\FontDPI","LogPixels", 65537,78,0,0,0

To represent a number with a data type other than the predefined REG_* types, you can specify the type number in the flag's high word and specify binary type in its low word. You must enter the data in binary format, one byte per field. For example, to store 16 bytes of data with a new data type of 0x38, you would have an AddReg item as follows:
HKR,,MYValue,0x00380001,0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F

This technique may be used with any data type except REG_EXPAND_SZ, REG_MULTI_SZ, REG_NONE, and REG_SZ.

value
Optional. Value to set. Can be either a string or a number in hexadecimal notation. At least two fields are required; however, one can be null. Therefore, at least one comma is required when using this form.

The two items in the example Add Registry section below, which is named sermouse_EventLog_AddReg, add two value names to the registry, EventMessageFile and TypesSupported, and set the value of these names.

[sermouse_EventLog_AddReg]
HKR,,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\sermouse.sys"
HKR,,TypesSupported,0x00010001,7


ClassInstall32 Section

The ClassInstall32 section installs a new class for a device in the class section of the registry.

[ClassInstall32]
AddReg=add-registry-section[,add-registry-section]...
Copyfiles=file-list-section[,file-list-section]...
Delfiles=file-list section[,file-list-section]...
DelReg=del-registry-section[,del-registry-section]...
Renfiles=file-list-section[,file-list-section]...
UpdateInis=update-ini-section[,update-ini-section]...
UpdateIniFields=update-inifields-section[,update-inifields-section]...

Not all the item types shown in the syntax above are needed or required. Typically, a ClassInstall32 section will use AddReg items to add a class description and a class icon to the registry.

Every device installed in Windows NT has a class associated with it even if the class is “Unknown.” Every device installed in Windows NT also has a class installer associated with it. In addition, a ClassInstall32 section can specify a property provider and exert control over how the class is handled in the user interface.

The installer processes the ClassInstall32 section if one of the devices defined in an INF file is about to be installed and the class is not built into Windows NT. The following hardware class names are built into Windows NT:
Display

Keyboard

Modem

Mouse

Ports

Printer

SCSI Adapter

Tape Drive

Unknown


ControlFlags Section

The ControlFlags section controls how a device is handled by the device installer. The New Device Wizard builds its list of installable devices by searching through INF files. The device installer extracts information about devices from the INF files and displays it.

[ControlFlags]
ExcludeFromSelect=device-id[device-id]...

ExcludeFromSelect
Listing the device-id of a device in an ExcludeFromSelect item removes the device from the device installer. If you use an asterisk (*) in place of the device-id list, ExcludeFromSelect is set for all devices listed in the INF file.

By appending an extension to ExcludeFromSelect entry, you can exclude devices from device-selection dialog boxes based on a particular operating system or platform. The following extensions are recognized by the Setup functions:

.win

Exclude device(s) on Windows 9x

.nt

Exclude device(s) on Windows NT (all platforms)

.ntx86

Exclude device(s) on Windows NT (x86 only)

.ntmips

Exclude device(s) on Windows NT (MIPS only)

.ntalpha

Exclude device(s) on Windows NT (Alpha only)

.ntppc

Exclude device(s) on Windows NT (PPC only)

The extensions are not case sensitive. The following example excludes all bus mice and InPort mice on RISC platforms:

[ControlFlags]
ExcludeFromSelect.ntalpha=*PNP0F0D,*PNP0F11,*PNP0F00,*PNP0F02,*PNP0F15
ExcludeFromSelect.ntmips=*PNP0F0D,*PNP0F11,*PNP0F00,*PNP0F02,*PNP0F15
ExcludeFromSelect.ntppc=*PNP0F0D,*PNP0F11,*PNP0F00,*PNP0F02,*PNP0F15


Copy Files Section

The Copy Files section lists the files to copy from a source disk to a destination directory. The source disk and destination directory associated with each file are specified in other sections of the INF file. The file-list-section name must appear in the CopyFiles item of an Install section.

You can copy a single file with the CopyFiles item of an Install section, without building a Copy Files section.

[file-list-section]
destination-file-name[,source-file-name][,temporary-file-name][,flag]
[destination-file-name[,source-file-name][,temporary-file-name]][,flag]
.
.
.

destination-file-name
Name of the destination file. If no source filename is given, this is also the name of the source file.
source-file-name
Name of the source file. If the source and destination filenames for the file copy operation are the same, source-file-name is not required.
temporary-file-name
Name of a temporary file for the file copy operation. The installer copies the source file, but gives it a temporary filename. The next time the operating system starts, it renames the temporary file to the destination filename. Temporary files can be used to copy files to a destination which is currently open or in use. This parameter is not currently implemented. The Setup functions automatically generate temporary filenames when necessary.
flag
Optional. These flags can be used to control how files are copied. Values are specified in base 10 or, if prefixed with 0x, in base 16.
COPYFLG_WARN_IF_SKIP (0x00000001)
Send a warning if the user elects to not copy a file.
COPYFLG_NOSKIP (0x00000002)
Do not allow the user to skip copying a file.
COPYFLG_NOVERSIONCHECK (0x00000004)
Ignore file versions and write over existing files in the destination directory.
COPYFLG_FORCE_FILE_IN_USE (0x00000008)
Force file-in-use behavior.
COPYFLG_NO_OVERWRITE (0x00000010)
Do not write over an existing file in the destination directory with a source file.
COPYFLG_NO_VERSION_DIALOG (0x00000020)
Do not write over a file in the destination directory with the source file if the existing file is newer than the source file.
COPYFLG_REPLACEONLY (0x00000040)
Copy the source file to the destination directory only if the file is already present in the destination directory.

This example copies two files:

[CopyTheseFilesSec]
file11                        ; copies file11
file31, file32                ; copies file32 to file31

All the source filenames used in this example must be defined in a SourceDisksFiles section and the Directory identifiers that appear in the SourceDisksFiles section must be defined in a SourceDisksNames section. As an alternative, you can use a layout.inf file to supply this information.


Delete Registry Section

A Delete Registry section deletes a subkey or value name from the registry. The del-registry-section name must appear in a DelReg item in an Install section.

A Delete Registry section can contain any number of items. Each item deletes one subkey or value name from the registry.

[del-registry-section]
reg-root-string, subkey, [value-name]
[reg-root-string, subkey, [value-name]]
.
.
.

reg-root-string
Registry root name. Can be one of these values:

HKCR

Same as HKEY_CLASSES_ROOT.

HKCU

Same as HKEY_CURRENT_USER.

HKLM

Same as HKEY_LOCAL_MACHINE.

HKU

Same as HKEY_USERS.

HKR

Relative to the key passed into SetupInstallFromInfSection.

subkey
Identifies the subkey to delete. Has the form key1\key2\key3.... This parameter can be expressed as a replaceable string. For example, you could use %Subkey1% where the string to replace %Subkey1% is defined in the Strings section of the INF file.
value-name
Optional. Identifies the value name for the subkey. The value-name parameter can be expressed as a replaceable string. For example, you could use %Valname1% where the string to replace %Valname1% is defined in the Strings section of the INF file.


Delete Files Section

A Delete Files section lists the files to be deleted. The file-list-section name must appear in the Delfiles item of an Install section.

[file-list-section]
file-name[,,,flag]
.
.
.

file-name
Identifies a file to be deleted. File-name must be defined in a SourceDisksFiles section.
flag
Optional. The following flags are available:
DELFLG_IN_USE (0x00000001)
Setting this flag deletes the file named in the item if the file is in use during the installation process. Set the flag to queue the file deletion operation until the system has restarted. If a file with this flag set cannot be deleted because it is in use, the system will restart after the device installation is complete.

If you do not use this flag along with a file-name parameter and the file is in use when the Delete Files section is executed, the file will not be deleted from the system.

DELFLG_IN_USE1 (0x00010000)
This flag is a high-word version of the DELFLG_IN_USE. Setting DELFLG_IN_USE1 causes the same behavior as setting DELFLG_IN_USE. Using DELFLG_IN_USE1 allows you to use the same section as Copy Files section and a Delete Files section without conflicting with the COPYFLG_WARN_IF_SKIP flag.

This example deletes three files:

[DeleteOldFilesSec]
file1
file2
file3


DestinationDirs Section

The DestinationDirs section defines the destination directories for the operations specified in file-list sections (Copy Files, Rename Files, or Delete Files). Optionally, a default destination directory may be specified for any Copy Files, Rename Files, or Delete Files sections in the INF file that are not explicitly named in the DestinationDirs section.

[DestinationDirs]
file-list-section=drid[,subdir]
.
.
.
[DefaultDestDir=drid[,subdir]]

file-list-section
Name of a Copy Files, Rename Files, or Delete Files section. This name must be referred to in a CopyFiles, RenFiles, or DelFiles item in an Install section.
drid
A directory identifier (DRID). Can be one of these values:

-01 Absolute path (32-bit).

    0xffff Absolute path (16-bit).

    01 SourceDrive:\pathname. The directory from which the INF
    file was installed.

    10 Windows directory.

    11 System directory. This is %windir%\system32 on
    Windows NT and %windir%\system on Windows 95.

    12 Drivers directory. This is %windir%\system32\drivers on
    Windows NT.

    17 INF file directory.

    18 Help directory.

    20 Fonts directory.

    21 Viewers directory.

    24 Applications directory.

    25 Shared directory.

    30 Root directory of the boot drive.

    50 %windir%\system directory.

    51 Spool directory.

    52 Spool drivers directory.

    53 User Profile directory.

    54 Directory where ntldr.exe and osloader.exe are located.

subdir
Name of the directory, within the directory named by drid, to be the destination directory.

The optional DefaultDestDir item provides a default destination for any CopyFiles items that use the direct copy (@filename) notation or any Copy Files, Rename Files, or Delete Files sections not specified in the DestinationDirs section. If a DefaultDestDir is not used in a DestinationDirs section, the default directory is set to drid_system.

This example sets the default directory for all the sections of the INF file to the drivers directory:

[DestinationDirs]
DefaultDestDir = 12


Device Section

The Device section gives the device description, identifies the Install section, and specifies the device identifier for a device. Optionally, a Device section may specify one or more device identifiers of compatible devices.

There can be one or more items in the Device section, depending upon how many devices the INF file installs for a manufacturer.

[device-section]
device-description=install-section-name,device-id[,compatible-device-id]...
[device-description=install-section-name,device-id[,compatible-device-id]...]

device-description
Description of a device to install. This can be any combination of printable characters or a strings key.
install-section-name
Name of the Install section for this device. The Install section name will be suffixed with an extension denoting a specific operating system or platform. This extension allows you to create custom Install sections if necessary.
device-id
Identifier for this device. The device identifier can be used in a ControlFlags section to control the way the device is handled by the Device Installer during installation.
compatible-device-id
Identifier of a compatible device. More than one compatible-device identifier can be given, but each must be preceded with a comma.

This example shows a typical Device section containing one item. The name of the Install section for this device is Ser_Inst and the device-id is *PNP0F08.

[LogiMfg]
%*pnp0f08.DeviceDesc%=Ser_Inst,*PNP0F08        ; Logi serial mouse

For each driver installed using this INF file, the Device Installer uses the information in the Manufacturer and Device sections to generate Driver Description, Manufacturer Name, Device ID, and Compatibility List entries in the registry.


EventLog Install Section

The EventLog Install section adds an event message file for the driver to the registry. You can also use an EventLog Install section to remove an event message file.

[install-section-name_EventLogInstallSection]
AddReg=add-registry-section
DelReg=del-registry-section

add-registry-section
The section of the INF file that specifies subkeys to add to the registry to install the event message file.
del-registry-section
The section of the INF file that specifies subkeys to delete from the registry to remove the event message file.

The following is an example of a typical EventLog Install section:

[sermouse_EventLog_Inst]
AddReg=sermouse_EventLog_AddReg

[sermouse_EventLog_AddReg]
HKR,EventMessageFile,0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\sermouse.sys"
HKR,,TypesSupported,0x00010001,7


HW Section

The HW section is a special case of an Install section. It is the only type of INF file section that allows entries to be made to the Enum branch of the registry. The HW section is used to add or delete device-related, driver-independent information from the registry.

[install-section-name.HW]
AddReg=add-registry-section[,add-registry-section]...
DelReg=del-registry-section[,del-registry-section]...

The only items that can appear in a HW section are AddReg and DelReg. Using the HKR designation in an AddReg item adds registry entries into the registry Enum branch and using the HKR designation in a DelReg item deletes entries from the registry Enum branch. HW sections are useful for installing multifunction devices.


Ini File to Registry Section

Moves lines or sections from an INI file to the registry, creating or replacing a registry entry under the specified key in the registry. The section name ini-to-registry-section must appear in an Ini2Reg item in an Install section of the INF file.

[ini-to-registry-section]
ini-file, ini-section, [ini-key],reg-root-string,subkey[,flags]
.
.
.

ini-file
Name of the INI file containing the key to copy. For more information about specifying the INI filename, see the Update INI File section.
ini-section
Name of the section in the INI file containing the key to copy.
ini-key
Name of the key in the INI file to copy to the registry. If ini-key is empty, the whole section is transferred to the specified registry key.
reg-root-string
Registry root name. Can be one of these values:

HKCR

Same as HKEY_CLASSES_ROOT.

HKCU

Same as HKEY_CURRENT_USER.

HKLM

Same as HKEY_LOCAL_MACHINE.

HKU

Same as HKEY_USERS.

HKR

Relative to the key passed into SetupInstallFromInfSection.

subkey
Identifies the subkey to receive the value. Has the form key1\key2\key3...
flags
Indicates whether to delete the INI key after transfer to the registry and whether to overwrite the value in the registry if the registry key already exists. The possible values for flags are listed below.

Bit

Value

Meaning

0

0

(Default) Do not delete the INI entry from the INI file after moving the information in the entry to the registry.

0

1

Delete the INI entry from the INI file after moving the information in the entry to the registry.

1

0

(Default) If the registry subkey exists, do not replace its current value.

1

1

If the registry subkey exists, replace its current value with the value from the INI file entry.


Install Section

The Install section identifies the sections in the INF file that contain descriptions of the device and instructions for installing files and information needed by the device driver(s).

The name of this section must be listed in a Device section that is associated with a Manufacturer section.

[install-section-name]
LogConfig=log-config-section-name[,log-config-section-name]...
Copyfiles=file-list-section-name[,file-list-section-name]...
Renfiles=file-list-section-name[,file-list-section-name]...
Delfiles=file-list-section-name[,file-list-section-name]...
UpdateInis=update-ini-section-name[,update-ini-section-name]...
UpdateIniFields=update-inifields-section-name[,update-inifields-section-name]...
AddReg=add-registry-section-name[,add-registry-section-name]...
DelReg=del-registry-section-name[,del-registry-section-name]...
Ini2Reg=ini-to-registry-section-name[,ini-to-registry-section-name]...

Not all the types of items shown in the syntax are needed or required in an Install section. Section names must consist of printable characters. More than one section name can be listed in an item; each additional name must be preceded with a comma. Items must specify the name of the corresponding section in the INF file. The only exception to this is the CopyFiles item, which need not specify a section if only one file is being copied.

You can use a special notation in the CopyFiles item to copy a single file directly from the CopyFiles line. An individual file can be copied by prefixing the filename with an @ symbol. The destination for any file copied using this notation is the DefaultDestDir, as defined in a DestinationDirs section.

By appending an extension to the name of the Install section, you can have different Install sections for different operating systems or platforms if necessary. The following extensions are recognized by the Setup functions:
.win | Windows 9x
.nt | Windows NT (all platforms)
.ntx86 | Windows NT (x86 only)
.ntmips | Windows NT (MIPS only)
.ntalpha | Windows NT (Alpha only)
.ntppc | Windows NT (PPC only)

The extensions are not case sensitive. The following example Install section consists of a single line that lists two Copy Files sections:

[Ser_Inst]
CopyFiles=Ser_CopyFiles, mouclass_CopyFiles

[Ser_CopyFiles]
sermouse.sys

[mouclass_CopyFiles]
mouclass.sys


Log Config Section

A Log Config section defines device configuration details, such as interrupt request lines, memory ranges, I/O ports, and DMA channels. The name of the section, log-config-section-name, must be used in the LogConfig item in an Install section of the INF file.

[log-config-section-name]
ConfigPriority=Priority_Value[,Config_Type]
[DMAConfig=[DMAattrs:]DMANum[,DMANum]]
[IOConfig=io-range[,io-range]...]
[IRQConfig=[IRQattrs:]IRQNum[,IRQNum]]
[MemConfig=mem-range[,mem-range]...

Priority_Value
Sets the priority value for the logical configuration. Only one ConfigPriority item can be used in a Log Config section. Can be one of the following string values:

Value

Meaning

HARDWIRED

Cannot be reset.

DESIRED

Soft configured, most optimal.

NORMAL

Soft configured, medium optimal.

SUBOPTIMAL

Soft configured, least optimal.

DISABLED

Hardware disabled.

RESTART

Requires Windows NT restart to take effect.

REBOOT

For Windows NT this is the same as RESTART.

POWEROFF

Requires power cycle to take effect.

HARDRECONFIG

Requires a jumper change.

Config_Type
Optional. Can be one of the following string values:

Value

Meaning

FORCED

The Configuration Manager must use the settings in this section. If set, Priority_Value must be NORMAL.

BASIC

The settings in this section indicate a range of possibilities.

DMAattrs
Optional. Can be either the letter D for 32-bit DMA channels or the letter W for 16-bit channels. These values are specific to the bus being used. If no value is specified, the default is 8-bit channels.
DMANum
The DMA channel number in decimal notation.
io-range
Sets the I/O range for the logical configuration. This value has two forms: Type 1 I/O Range and Type 2 I/O Range.
Type 1 I/O Range Value
start-end[([decode-mask]:[alias-offset]:[attr])]

start

Starting address of the I/O port region. This is a 64-bit value in hexadecimal notation.

end

Ending address of the I/O port region. This is a 64-bit value in hexadecimal notation. The start and end values must be separated by a hyphen.

decode-mask

Optional. Currently ignored.

alias-offset

Optional. A 32-bit value to indicate additional ports decoded by the adapter. The additional ports are calculated by adding multiples of this offset to the base I/O port region. Leaving this field blank indicates that driver software will only access the ports in the base I/O region and never access any of the aliases.

attr

Optional. The letter M specifies that the I/O port is a memory address. The default (no attr) indicates that the port is an I/O address.

Type 2 I/O Range Value
size@min-max[%align-mask][([decode-mask]:[alias-offset]:[attr])]

size

Size of the required I/O port region in bytes. This is a 32-bit value in hexadecimal notation.

min

The smallest possible starting I/O port. This is a 64-bit value in hexadecimal notation.

max

The largest possible ending I/O port. This a 64-bit value in hexadecimal notation. The min and max values must be separated by a hyphen.

align-mask

Optional. This 64-bit hexadecimal mask is applied to the range specified by min and max to determine legal starting addresses for the I/O region. To construct the mask, all bits that must be 0 in the starting address must be 0 in the mask. All other bits should be 1.

decode-mask

Optional. Currently ignored.

alias-offset

Optional. A 32-bit value to indicate additional ports decoded by the adapter. The additional ports are calculated by adding multiples of this offset to the base I/O port region. Leaving this field blank indicates that driver software will only access the ports in the base I/O region and never access any of the aliases.

attr

Optional. The letter M specifies that the I/O port is a memory address. The default (no attr) indicates that the port is an I/O address.

The following example illustrates the use of a list of three Type 1 I/O range values in an IOConfig item. The example specifies an I/O port region of 8 bytes which can start at 1F8, 2F8, or 3F8 and does not use any of its aliases.

IOConfig=1F8-1FF, 2F8-2FF, 3F8-3FF

The following example shows the use of a Type 2 I/O range value in an IOConfig item. The example specifies an I/O port region of 8 bytes which can start at 300, 308, 310, 318, 320, or 328. The device does not use any of its aliases.

IOConfig=8@300-32F%FF8

IRQattrs
Optional. Can be any combination of the following letters:

S

Indicates a shareable interrupt. If used, this flag should appear only once in an IRQConfig item. It affects all IRQs listed in that item. If this flag is not specified, the default is a non-shareable interrupt.

L

Indicates a level-triggered interrupt. If this flag is not specified, the default is an edge-triggered interrupt.

IRQNum
Sets the IRQ number for the logical configuration. This is the IRQ number in decimal notation.
mem-range
Sets the memory range(s) for the logical configuration.
This value has two forms:
start-end[(attr)]

size@min-max[%align-mask][(attr)]

start

Starting address of the physical memory region. This is a 64-bit value in hexadecimal notation.

end

Ending address of the physical memory region. This is 64-bit value in hexadecimal notation. The start and end values must be separated by a hyphen.

size

Size of the required region in bytes. This is a 32-bit value in hexadecimal notation.

min

Smallest possible starting address of the memory region. This is 64-bit value in hexadecimal notation.

max

Largest possible ending address of the memory region. This is 64-bit value in hexadecimal notation. The min and max values must be separated by a hyphen.

align-mask

Optional. A 32-bit hexadecimal mask applied to the range specified by min and max to determine legal starting addresses for the memory region. To construct the mask, all bits that must be 0 in the starting address must be 0 in the mask. All other bits should be 1.

attr

Optional. Can be any combination of the following letters:

C indicates that combined-write is allowed, otherwise combined write is not allowed.

D Indicates a 32-bit address; otherwise a 24-bit address is assumed.

F indicates that the memory is prefetchable; otherwise it is not.

R indicates read-only memory.

W indicates write-only memory.

RW indicates read/write memory.

The following examples illustrate the use of the MemConfig item.

This example specifies a memory region of 32K bytes that can start at either C0000 or D0000.

MemConfig=C0000-C7FFF, D0000-D7FFF

This example specifies a memory region of 32k bytes starting on 64K boundaries.

MemConfig=8000@C0000-D7FFF%F0000

Each item in a Log Config section can specify more than one resource. However, during installation one and only one resource from an item is used. If a device needs multiple resources of the same type, multiple items of that type must be used in the Log Config section. For example, to ensure two IRQs for a device, two IRQConfig items must be used in the Log Config section. If a device does not require an IRQ, an IRQConfig item should not be used.

An INF file can contain any number of Log Config sections. However, each section must contain complete details for installing one device. More than one Log Config section can be specified for a device. If this is done, only one of the Log Config sections will be used during installation.

For each Log Config section item, the installer builds binary logical configuration records and stores them in the registry.


Manufacturer Section

The Manufacturer section identifies a manufacturer of a device that can be installed using the INF file and lists a Device section that describes the device.

[Manufacturer]
manufacturer-name | %strings-key%=device-section-name

manufacturer-name
Name of the device manufacturer. This name can be any combination of printable characters, but it must uniquely identify the manufacturer.
strings-key
Name of a string defined in the Strings section of the INF file.
device-section-name
Name of the Device section. This name can be any combination of printable characters, but must uniquely identify another section in the INF file.

The following example shows a typical Manufacturer section:

[Manufacturer]
%LogiMfg%=LogiMfg        ; Logitech


Rename Files Section

The Rename Files section lists the names of files to be renamed. The name of the section must appear in a Renfiles item in an Install section of the INF file.

[rename-files-section-name]
new-file-name,old-file-name
.
.
.

new-file-name
New name of the file.
old-file-name
Old name of the file. The old-file-name parameter must be defined in a SourceDisksFiles section.

The following example renames file42 to file41, file52 to file51, and file62 to file61:

[RenameOldFilesSec]
file41, file42
file51, file52
file61, file62


Service Install Section

The Service Install section installs the service listed in the AddService entry of the Services section:

[install-section-name_ServiceInstallSection]
DisplayName=[name]
ServiceType=type-code
StartType=start-code
ErrorControl=error-control-level
ServiceBinary=path-to-service
LoadOrderGroup=[load-order-group-name]
Dependencies=+depend-on-group-name[[,depend-on-service-name]...]
StartName=[driver-object-name]

name
Optional. A friendly name for the service.
type-code
Specifies the type of driver. This can be any type allowed by the CreateService function. See the Win32 SDK online documentation for more information.
start-code
Specifies when to start the driver. One of the following:
SERVICE_BOOT_START (0x0)
Indicates a driver started by the operating system loader. This code should only be used for drivers that are essential to loading the OS.
SERVICE_SYSTEM_START (0x1)
Indicates a driver started during OS initialization.
SERVICE_AUTO_START (0x2)
Indicates a driver started by the Service Control Manager during system startup.
SERVICE_DEMAND_START (0x3)
Indicates a driver started on demand by the Service Control Manager.
SERVICE_DISABLED (0x4)
Indicates a driver that cannot be started.
error-control-level
Specifies the level of error control as follows:
CRITICAL (0x3)
If the driver fails to load, fail the attempted startup. If the startup is not using the LastKnownGood control set, switch to LastKnownGood. If the startup attempt is using LastKnownGood, run a bug-check routine.
SEVERE (0x2)
If the startup is not using the LastKnownGood control set, switch to LastKnownGood. If the startup attempt is using LastKnownGood, continue on in case of error.
NORMAL (0x1)
If the driver fails to load or initialize, startup should proceed, but display a warning.
IGNORE (0x0)
If the driver fails to load or initialize, startup proceeds. No warning is displayed.
path-to-service
The path to the binary for the service.
load-order-group-name
Optional. Identifies the load order group of which this driver is a member. Examples of groups are: pointer, port, primary disk, and so forth. For detailed discussion of load order groups and controlling load order, see Chapter 1.
+depend-on-group-name
Optional. Specifies a load order group on which the driver depends. The driver is started only if at least one member of the specified group has been started. For detailed discussion of load order groups and controlling load order, see Chapter 1.
depend-on-service-name
Optional. Specifies a service that must be running before this driver is started.
driver-object-name
Optional. If type-code specifies a Kernel driver or a file system driver, this name is the Windows NT driver object name that the I/O Manager uses to load the device driver.

The following example shows a typical Service Install section:

[mouclass_Service_Inst]
DisplayName    = %mouclass.SvcDesc%
ServiceType    = 1                  ; SERVICE_KERNEL_DRIVER
StartType      = 1                  ; SERVICE_SYSTEM_START
ErrorControl   = 1                  ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\mouclass.sys
LoadOrderGroup = Pointer Class


Services Section

You can add services to a machine with a Services section. Services can be removed by including a DelService entry in the Services section.

[install-section-name.Services]
AddService=ServiceName,flag,service-install-section-name[,event-log-install-section-name]
DelService=ServiceName

ServiceName
The service to install or delete.
flag
Either of these flags:
SPSVCINST_TAGTOFRONT (0x1)
Move the service’s tag to the front of its group order list.
SPSVCINST_ASSOCSERVICE (0x2)
Associate the service in this AddService item with the device being installed by this INF file.
service-install-section-name
The name of the Service Install section.
event-log-install-section-name
The name of the EventLog Install section.

The following example shows a typical Services section:

[Ser_Inst.Services]
AddService = sermouse, 0x00000002, sermouse_Service_Inst, sermouse_EventLog_Inst
AddService = mouclass,, mouclass_Service_Inst, mouclass_EventLog_Inst


SourceDisksFiles Section

The SourceDisksFiles section names the source files used during installation and identifies the source disks that contain the files. In order to allow multi-platform distribution of source files, you can construct a platform-specific SourceDisksFiles section. For example, all Setup functions that use a SourceDisksFiles section will first look for a SourceDisksFiles.mips section on a MIPS platform and only look in a SourceDisksFiles section if the line was not found in SourceDisksFiles.mips. This behavior affects any Setup function that directly or indirectly references a SourceDisksFiles section as part of its processing.

[SourceDisksFiles]
filename=disk-number[,subdir][,size]
.
.
.

filename
Name of the file on the source disk.
disk-number
Ordinal of the source disk that contains the file. This ordinal must be defined in a SourceDisksNames section, and must have a value greater than or equal to 1.
subdir
Optional parameter that specifies the subdirectory on the source disk where the file resides. If this parameter is not used, the source disk root directory is the default.
size
Optional parameter that specifies the uncompressed size of the file.

The following example SourceDisksFiles section identifies a single source file, SRS01.x86, on the disk with the ordinal of 1.

[SourceDisksFiles]
SRS01.x86 = 1


SourceDisksNames Section

The SourceDisksNames section identifies and names the disk that contains the source files for file copying and renaming operations. In order to allow multi-platform distribution of source files, you can construct a platform-specific SourceDisksNames section. For example, all Setup functions that use a SourceDisksNames section will first look for a SourceDisksNames.mips section on a MIPS platform and only look in a SourceDisksNames section if the line was not found in SourceDisksNames.mips. This behavior affects any Setup function that directly or indirectly references a SourceDisksNames section as part of its processing.

[SourceDisksNames]
disk-ordinal="disk-description",tag-file,unused[,path]
.
.
.

disk-ordinal
A unique number that identifies a source disk. If there is more than one source disk, each must have a unique ordinal.
disk-description
A string or strings key describing the content or purpose of the disk. The installer displays this string to the user to identify the disk. The description must be enclosed in double quotation marks.
tag-file
This is the name (filename part only) of the tag file. The tag file indicates the presence of a particular removable media volume.
unused
This parameter is not used.
path
Optional parameter that specifies the absolute path to the source files. If this parameter is not used, the root directory is the default.

In the following example, the write.exe file is the same for all platforms and is located in the \common directory on the CD-ROM. The disk-number of 1 directs the Setup functions to the \common directory regardless of which platform the files are being installed on. The cmd.exe file is a platform-specific file that is distributed for all platforms. The disk-number of 2 directs the Setup functions to the correct directory for each platform. The halnecmp.dll file is a MIPS platform-specific file.

[SourceDisksNames]
1 = “Windows NT CD-ROM”, file.tag,,\common

[SourceDisksNames.mips]
2 = “Windows NT CD-ROM”, file.tag,, \mips

[SourceDisksNames.x86]
2 = “Windows NT CD-ROM”, file.tag,, \i386

[SourceDisksNames.ppc]
2 = “Windows NT CD-ROM”, file.tag,, \ppc

[SourceDisksFiles]
write.exe = 1
cmd.exe = 2

[SourceDisksFiles.mips]
halnecmp.dll = 2


Strings Section

The Strings section defines one or more strings keys. A strings key is a name that represents a string of printable characters. Although the Strings section is usually the last section in an INF file, a strings key defined in a Strings section can be used anywhere in the INF file that uses the string. The installer expands the string key to the specified string and uses it for further processing. When a strings key is used, it must be enclosed by percent signs (%).

[Strings]
strings-key=value
.
.
.

strings-key
A unique name consisting of letters and digits.
value
A string consisting of letters, digits, or other printable characters. It should be enclosed in double quotation marks if the corresponding strings key is used in a type of item that requires double quotation marks.

The Strings section simplifies translation of strings for international markets by placing all strings that can be displayed in the user interface in a single section of the INF file. Strings keys should be used whenever possible. The following example shows the Strings section of a typical INF file.

[Strings]
String0="Corporation X"
String1="Corporation X"
String2="CS2590 SCSI Adapter"


Update INI Fields Section

The Update INI Fields section replaces, adds, and deletes fields in the value of an INI entry. Unlike an Update INI File section, this type of section replaces, adds, or deletes portions of a value in an INI file entry rather than the whole value. The section name, update-inifields-section-name, must appear in the UpdateIniFields item in an Install section of the INF file.

[update-inifields-section-name]
ini-file,ini-section,profile-name,[old-field],[new-field],[flags]
.
.
.

ini-file
Name of the INI file containing the entry to change.
ini-section
Name of the INI file section containing the entry to change.
profile-name
Name of the entry to change.
old-field
Field value to delete.
new-field
Field value to add, if it does not exist.
flags
Specifies whether to treat the old-field and new-field arguments as if they have a wild card character or not and which separator character to use when appending a new field to an INI file entry. The flags argument can be any of the following values:

Bit

Value

Meaning

0

0

(Default) Treat the “*” character literally when matching fields and not as a wild card character.

0

1

Treat the “*” character as a wild card character when matching fields.

1

0

(Default) Use a blank (“ ”) as a separator when adding a new field to an entry.

1

1

Use a comma (“,”) as a separator when adding a new field to an entry.

Any comments in the INI file line are removed because they might not be applicable after changes. When looking for fields in the line in the INI file, spaces, tabs, and commas are used as field delimiters. However, a space is used as the separator when the new field is appended to the line.


Update INI File Section

The Update INI File section replaces, deletes, or adds complete entries in the INI file. The section name, update-ini-section-name, must appear in the UpdateInis item in the Install section of the INF file.

[update-ini-section-name]
ini-file,ini-section,[old-ini-entry],[new-ini-entry],[flags]
.
.
.

ini-file
The INI file containing the entry to change.
ini-section
Name of the section containing the entry to change.
old-ini-entry
Optional. Usually in the form key=value.
new-ini-entry
Optional. Usually in the form key=value. Either the key or value may specify replaceable strings. For example, either the key or value specified in the new-ini-entry parameter may be %String1%, where the string that replaces %String1% is defined in the Strings section of the INF file.
flags
Optional action flags. The flags parameter can be one of these values:

Value

Meaning

0

Default. If old-ini-entry key is present in an INI file entry, that entry is replaced with new-ini-entry. Only the keys of the old-ini-entry parameter and the INF file entry must match; the value of each entry is ignored.

To add new-ini-entry to the INI file unconditionally, set old-ini-entry to NULL. To delete old-ini-entry from the INI file unconditionally, set new-ini-entry to NULL.

1

If both key and value of old-ini-entry exist in an INI file entry, that entry is replaced with new-ini-entry. The old-ini-entry parameter and the INF file entry must match on both key and value for the replacement to be made (this is in contrast to using an action flag value of 0, where only the keys must match for the replacement to be made).

2

If the key in the old-ini-entry parameter does not exist in the INI file, then no operation is performed on the INI file.

If the key in the old-ini-entry parameter exists in an INI file entry and the key in the new-ini-entry parameter exists in an INI file entry, the INI file entry that matches the key in the new-ini-entry parameter is deleted, and the key of the INI file entry that matches the old-ini-entry parameter is replaced with the key in the new-ini-entry parameter.

If the key in the old-ini-entry parameter exists in an INI file entry and the key in the new-ini-entry parameter does not exist in an INI file entry, an entry made up of the key in the new-ini-entry parameter and the old value is added to the INI file.

The match of the old-ini-entry parameter and an INI file entry is based on key alone, not key and value.

3

Same as flags parameter value of 2 described above, except the match of the old-ini-entry parameter and an entry in the INF file is based on matching both key and value, not just the key.

The wild card character (*) can be used in specifying the key and value and they will be interpreted correctly.

The ini-file name can be a string or a strings key. A strings key has the form %strkey% where strkey is defined in the Strings section in the INF file. The name must be a valid filename.

The ini-file name should include the name of the directory containing the file, but the directory name should be given as a DIRID rather than an actual name. The installer replaces a DIRID with an actual name during installation.

A DIRID has the form %dirid% where dirid is one of the predefined identifiers or a user-defined by a call to the SetupSetDirectoryId function. When using a DIRID, you should use the backslash in a path. For example, %11%\card.ini can be used to reference card.ini in the system32 directory.

The following examples illustrate individual items in an Update INI File section:

%11%\sample.ini, Section1,, Value1=2             ; adds new entry
%11%\sample.ini, Section2, Value3=*,             ; deletes old entry
%11%\sample.ini, Section4, Value5=1, Value5=4    ; replaces old entry


Version Section

The Version section is the standard header for all Windows NT 4.0 and Windows 95 INF files.

[Version]
Signature="signature-name"
Class=class-name
ClassGUID=GUID
Provider=INF-creator
LayoutFile=filename.inf[,filename.inf]...

signature-name
Can be either $Windows NT$, $Chicago$, or $Windows 95$. If signature-name is not one of these strings, the file is not accepted as an INF file for the classes of devices recognized by Windows NT. Signature string recognition is case-insensitive.
class-name
Defines the class name in the registry for any device installed from this INF file.
GUID
Optional. Defines the GUID in the registry for any device installed from this INF file.

The following GUIDs are defined for Windows NT:

Adapter

4d36e964L-e325-11ce-bfc1-08002be10318

CD-ROM

4d36e965L-e325-11ce-bfc1-08002be10318

Computer

4d36e966L-e325-11ce-bfc1-08002be10318

Disk Drive

4d36e967L-e325-11ce-bfc1-08002be10318

Display

4d36e968L-e325-11ce-bfc1-08002be10318

FDC

4d36e969L-e325-11ce-bfc1-08002be10318

HDC

4d36e96aL-e325-11ce-bfc1-08002be10318

Keyboard

4d36e96bL-e325-11ce-bfc1-08002be10318

Media

4d36e96cL-e325-11ce-bfc1-08002be10318

Modem

4d36e96dL-e325-11ce-bfc1-08002be10318

Monitor

4d36e96eL-e325-11ce-bfc1-08002be10318

Mouse

4d36e96fL-e325-11ce-bfc1-08002be10318

MTD

4d36e970L-e325-11ce-bfc1-08002be10318

Multifunction

4d36e971L-e325-11ce-bfc1-08002be10318

Network

4d36e972L-e325-11ce-bfc1-08002be10318

Network Client

4d36e973L-e325-11ce-bfc1-08002be10318

Network Service

4d36e974L-e325-11ce-bfc1-08002be10318

Network Transport

4d36e975L-e325-11ce-bfc1-08002be10318

No Driver

4d36e976L-e325-11ce-bfc1-08002be10318

PCMCIA

4d36e977L-e325-11ce-bfc1-08002be10318

Ports

4d36e978L-e325-11ce-bfc1-08002be10318

Printer

4d36e979L-e325-11ce-bfc1-08002be10318

Printer Upgrade

4d36e97aL-e325-11ce-bfc1-08002be10318

SCSI Adapter

4d36e97bL-e325-11ce-bfc1-08002be10318

Sound

4d36e97cL-e325-11ce-bfc1-08002be10318

System

4d36e97dL-e325-11ce-bfc1-08002be10318

Tape Drive

6d807884L-7d21-11cf-801c-08002be10318

Unknown

4d36e97eL-e325-11ce-bfc1-08002be10318

INF-creator
Identifies the creator of the INF file. Typically, this is the name of the organization that creates the INF file.
filename.inf
An INF file that contains layout information (source disks and files) required for installing this driver software. If a filename.inf is not specified, the SourceDisksNames and SourceDisksFiles sections must be included in this INF file.

The following example shows a typical Version section:

[Version]
Signature="$Windows NT$"
Class=Mouse
ClassGUID={4D36E96F-E325-11CE-BFC1-08002BE10318}
Provider=%Provider%
LayoutFile=layout.inf

[Strings]
Provider="Corporation X"


2.3.2 Using INF Files

This section describes several common operations that can be carried out with INF files and the general Setup functions. For complete information on how to use these functions, see the Win32 SDK documentation.


2.3.2.1 Opening and Closing an INF File

Before the information in the INF file can be accessed, it must be opened by calling SetupOpenInfFile. This function returns a handle to the INF file.

If you do not know the name of the INF file that you need to open, SetupGetInfFileList can be used to obtain a list of all the INF files of a particular type (either legacy INF files, new INF files, or both) in a directory.

Once an INF file has been opened, additional INF files can be appended to the opened INF file by using SetupOpenAppendInfFile. When subsequent Setup functions reference an open INF file, they will also be able to access any information stored in any appended files.

If no INF file is specified during the call to SetupOpenAppendInfFile, the file specified by the LayoutFile entry in the Version section of the INF file opened during the call to SetupOpenInfFile is appended.

When the information in the INF file is no longer needed, SetupCloseInfFile should be called to release resources allocated during the call to SetupOpenInfFile.


2.3.2.2 Retrieving Information From an INF File

Once you have a handle to an INF file, you can retrieve information from it in a variety of ways. Functions such as SetupGetInfInformation, SetupQueryInfFileInformation, and SetupQueryInfVersionInformation retrieve information about the specified INF file.

Other functions such as SetupGetSourceInfo and SetupGetTargetPath acquire information about the source files and target directories.

Low-level functions like SetupGetLineText and SetupGetStringField enable you to directly access information stored in a line or field of an INF file. These functions are used internally by the higher-level Setup functions, but are available should you need to directly access information at the line or field level.


2.4 Setup Functions

The Setup functions work with INF files to provide functionality such as:

  • General Setup Functions

    • Queuing files

    • Prompting for media and handling errors

    • Installing files

    • Updating registry entries

    • Logging installed files

    • Storing the most recently used source paths

  • Device Installation Functions

    • Searching INF files for compatible drivers

    • Display driver choices in user selection dialogs

    • Install drivers

Unicode and ANSI versions are available for most Setup functions. The ANSI functions can be used to create setup applications for the Windows 95 operating system.


2.4.1 General Setup Functions

This section contains a summary of the general Setup functions used for INF file processing, disk prompting, file operation queues, and so on.


2.4.1.1 INF File Processing Functions

The INF file processing functions provide setup and installation functionality that includes:

  • Opening and closing an INF file

  • Retrieving information about an INF file

  • Retrieving information about source files and target directories for copy operations

  • Processing the installation actions in an INF file section

The INF file processing functions are summarized in the following list. For detailed information on these functions, see the Win32 SDK documentation.
SetupCloseInfFile | Frees resources and closes the INF handle.
SetupDecompressOrCopyFile | Copies a file and, if necessary, decompresses it.
SetupFindFirstLine | Finds the first line in a section of an INF file or, if a key is specified, the first line that matches that key.
SetupFindNextLine | Returns the next line in a section relative to the specified HINFLINE.
SetupFindNextMatchLine | Returns the next line in a section relative to the specified HINFLINE that matches a specified key.
SetupGetBinaryField | Retrieves data from a line whose fields are in binary format.
SetupGetFieldCount | Returns number of fields in a line.
SetupGetFileCompressionInfo | Retrieves file compression information from an INF file.
SetupGetInfFileList | Gets a list of the types of INF files in a specified directory.
SetupGetInfInformation | Returns information about an INF file.
SetupGetIntField | Returns the specified integer field of the line in the INF file.
SetupGetLineByIndex | Returns an HINFLINE for the line at a specified index in the specified section.
SetupGetLineCount | Returns the number of lines in the specified section.
SetupGetLineText | Retrieves the content of a specified line from an INF file.
SetupGetMultiSzField | Returns the list of strings starting at the specified field of a line in an INF file.
SetupGetSourceFileLocation | Gets the source disk ordinal and path (relative to source root) where the source file is located.
SetupGetSourceFileSize | Gets the file size for an individual source file or a Copy Files section of an INF file.
SetupGetSourceInfo | Retrieves the path, tag file, or description for a source.
SetupGetStringField | Returns the specified string field of a line in an INF file.
SetupGetTargetPath | Gets the target path for a Copy Files section in an INF file.
SetupInstallFile | Installs a file.
SetupInstallFileEx | Installs a file and returns a variable indicating whether or not the file was in use.
SetupInstallFilesFromInfSection | Queues all the files in an INF file Copy Files section.
SetupInstallFromInfSection | Performs the directives specified in an INF file Install section.
SetupInstallServicesFromInfSection | Performs service installation and deletion operations as specified in a Service Install section of an INF file.
SetupOpenAppendInfFile | Opens an INF file and appends it to an existing INF handle.
SetupOpenInfFile | Opens an INF file and returns a handle to it.
SetupOpenMasterInf | Opens the master INF file that contains file and layout information for files shipped with Windows NT. (Windows NT only.)
SetupQueryInfFileInformation | Queries an INF information structure about its associated INF filename(s).
SetupQueryInfVersionInformation | Queries an INF information structure for version information on one of its constituent INF files.
SetupSetDirectoryId | Associates a directory ID (DIRID) with a particular directory.


2.4.1.2 Disk Prompting and Error Handling Functions

The Setup functions provide basic functionality to prompt the user to enter new media, or to handle errors that arise during file copy, delete, or rename operations.

The default queue callback routine that is included with the Setup functions also provides queue-specific error handling and progress indicators for the queue that is being committed.

The following functions provide disk prompting and an error handling user interface.
SetupCopyError | Generates a dialog box that informs the user of a copy error.
SetupDeleteError | Generates a dialog box that informs the user of a delete error.
SetupPromptForDisk | Generates a dialog box that prompts the user for a media disk or source file location.
SetupRenameError | Generates a dialog box that informs the user of a rename error.

For more information on the disk prompting and error handling functions, see the Win32 SDK documentation.


2.4.1.3 File Queuing Functions

Using the Setup functions, you can queue files for various operations. File queues can be established for file copying, renaming, and deletion. Typically, all of the file operations necessary for an entire installation are queued to the file queue and processed, when the queue is committed, in a single batch.

The following functions are used with file queues.
SetupCloseFileQueue | Terminates the queue. Any remaining transactions are not committed.
SetupCommitFileQueue | Commits all queued transactions.
SetupOpenFileQueue | Initializes and returns a handle to the file queue.
SetupPromptReboot | Prompts the user to reboot his or her machine if necessary.
SetupQueueCopy | Queues a file copy.
SetupQueueCopySection | Queues the files in an INF Copy Files section for copying.
SetupQueueDefaultCopy | Queues the files in an INF Copy Files section using the default information specified in an INF file.
SetupQueueDelete | Queues a file deletion.
SetupQueueDeleteSection | Queues the files in a INF Delete Files section for deletion.
SetupQueueRename | Queues a file for renaming.
SetupQueueRenameSection | Queues the files in an INF Rename Files section for renaming.
SetupScanFileQueue | Scans the file queue.
SetupSetPlatformPathOverride | Sets the platform path override.

For more information on the file queue functions, see the Win32 SDK documentation.


2.4.1.4 Default Queue Callback Routine Functions

The default queue callback routine handles common notifications sent from a committing queue in a generic manner. By using the default routine, you get a ready-made user interface to handle common setup user interaction and some error handling processes.

The following functions are used with the default queue callback routine.
SetupDefaultQueueCallback | The default queue callback routine.
SetupInitDefaultQueueCallback | Initializes context information needed by the default queue callback routine.
SetupInitDefaultQueueCallbackEx | Initializes context information needed by the default queue callback routine and specifies a separate window to display progress messages.
SetupTermDefaultQueueCallback | Releases resources used for context information needed by the default queue message handler callback routine.

For more information on the default queue callback functions, see the Win32 SDK documentation.


2.4.1.5 Cabinet File Function

Cabinets are a single file, usually suffixed with .CAB, that stores several compressed files in a file library. They are used to organize installation files to be copied to the user’s system. A compressed file can be spread over several cabinet files.

The following function is used with cabinets.

SetupIterateCabinet

Returns a notification for each file stored in a cabinet file.

For more information on the cabinet file function, see the Win32 SDK documentation.


2.4.1.6 MRU Source List Functions

The most recently used (MRU) source list is a list that is resident on the user’s machine that can be used to store information about source paths used in previous installations. This information can be used when prompting the user for a source path.

Depending on your permissions, you can create a user list, one that is specific to a particular user, or a system list, one that is the same for all users. In addition to system and user source lists, you can create temporary source lists that are discarded when the setup application exits.

The following functions are used with MRU source file lists.
SetupAddToSourceList | Adds an entry to a source list.
SetupCancelTemporarySourceList | Cancels any temporary list or no-browse behavior and reestablishes standard list behavior.
SetupFreeSourceList | Frees resources allocated to a source list.
SetupQuerySourceList | Queries the current list of installation sources.
SetupRemoveFromSourceList | Removes an entry from an installation source list.
SetupSetSourceList | Sets the installation source list to the system MRU list, the user MRU list, or a temporary list.

For more information on the MRU source list functions, see the Win32 SDK documentation.


2.4.1.7 File Log Functions

A file log records information about the files copied to a system during an installation. It can be used for emergency repair or as a diagnostic tool.

The file used to log file installations can either be the system log, that stores a record of the files installed with the Windows NT operating system, or your own file log.

The following functions are used with file logs.
SetupInitializeFileLog | Initializes a log file for use.
SetupLogFile | Adds an entry to the log file.
SetupQueryFileLog | Retrieves information from a log file.
SetupRemoveFileLogEntry | Removes an entry from a log file.
SetupTerminateFileLog | Releases resources allocated to a log file.

For more information on the file log functions, see the Win32 SDK documentation.


2.4.2 Device Installation Overview

A set of default device installers and Control Panel applets are supplied with Windows NT. Device developers have the option of using the default installers and Control Panel applets or creating a new device or class installer or a new Control Panel applet. Device developers must create an INF file for their device.


2.4.2.1 About Class Installers

Class installers are dynamic-link libraries that install, configure, and remove devices or classes of devices in the system. Windows NT provides a set of default installers that can be used to install devices belonging to the default classes. If your class of device has features that are not supported by the default installers, you can create a new installer for your class and direct the system to use that installer whenever the user installs that particular class of device. Additionally, Windows NT supports a set of default device installation requests that can be used on a device of any class.


2.4.2.2 Classes and Installers

Windows NT has a number of subsystems to control various classes of devices. Classes identify logical device types such as display, keyboard, and network. Because each subsystem has different user options and different compatibility constraints, it is impossible to have a single utility support installation of all possible devices. Instead, each class is responsible for providing its own installer.

A single installer can support installation of both the class and individual devices. However, this depends on the class and in some cases separate device installers are used. Typically, the installer is combined with the Control Panel applet for the given class, although this is not a requirement.

The class and device installers are responsible for displaying the appropriate dialog boxes or carrying out appropriate installation tasks. Although a class installer gives the device installer full control of the installation process, in most cases, the device installer simply directs the class installer to complete the task for it. The device installer can insert additional configuration dialogs, hardware detection, and other features into the process as is appropriate for the device.


2.4.2.3 Default Classes and Class Installers

Windows NT defines a set of default device classes and provides corresponding class installers for these classes. The system uses the default installers to install devices belonging to the default classes. The default classes and their corresponding default installer are as follows:
Display | DisplayClassInstaller (desk.cpl)
Keyboard | KeyboardClassInstaller (syssetup.dll)
Modem | ClassInstall32 (modem.cpl)
Mouse | MouseClassInstaller (syssetup.dll)
Ports (COM and LPT) | PortClassInstaller (syssetup.dll)
SCSI Controllers | ScsiClassInstaller (syssetup.dll)
Tape Drive | TapeClassInstaller (syssetup.dll)
Unknown Hardware | Default class installer

If your device belongs to a default class but the default installer does not provide the installation support you need for your device, you can create your own installer to be called whenever the user installs a device of that class.

The default class installer adds new classes to the system if the specified INF file contains a ClassInstall32 section and the class has not been previously installed. If you specify a class installer for this new class in the ClassInstall32 section of the INF file, the installer is loaded and called when appropriate.


2.4.2.4 Installer Function

Every installer exports an installer function. The default name for the function is ClassInstall. The device installer dynamically links to and calls this function whenever it needs the class installer to carry out a specific installation task.

The prototype for the installer function is as follows:

typedef DWORD  (CALLBACK* CLASS_INSTALL_PROC) (
    IN DI_FUNCTION       InstallFunction,
    IN HDEVINFO          DeviceInfoSet,
    IN PSP_DEVINFO_DATA  DeviceInfoData  /* optional */
    );

The installer function receives two or three arguments: InstallFunction which identifies the installation request to perform, a handle to the device information set, and, optionally, a pointer to an SP_DEVINFO_DATA structure that contains information about the device to install. The function either carries out the requested task or directs the class installer to carry out the task for it.

To direct the class installer to carry out the task, the installer function must return ERROR_DI_DO_DEFAULT. If the device installer performs its own tasks, it must return the value NO_ERROR to notify the class installer that the task completed successfully.


2.4.2.5 Installation Requests

The device installer issues an installation request with a call to SetupDiCallClassInstaller and the appropriate class installer is called to carry out the installation task.

A DIF_SELECTDEVICE request directs the installer to choose a driver for a new device or a new driver for an existing device. The installer either makes the selection, prompts the user to make the selection, or directs the class installer to prompt the user (the default action).

A DIF_INSTALLDEVICE request directs the installer to install the given driver. The installer either installs the driver or directs the class installer to do it (the default action). In some cases, an installer can carry out pre-processing, such as copying support drivers and prompting for user input, before requesting the default action.

A DIF_PROPERTIES request directs the installer to display the properties sheet for the selected device. This request is sent when the user clicks on the Properties button. The installer can display the property sheet or direct the class installer to display the default property sheet (the default action).

A DIF_REMOVE request directs the installer to remove the specified device. The installer typically removes the corresponding registry entries for the device.

A DIF_FIRSTTIMESETUP request directs the installer to carry out any class-specific installation tasks that need to be complete during initial installation of the operating system.

A DIF_CALCDISKSPACE request directs the installer to calculate the disk space requirements for installing a driver for the specified device. By default action, the class installer calculates the disk requirements based on the contents of the INF file.

A DIF_DESTROYPRIVATEDATA request directs the installer to free any memory or resources it has allocated and stored in the private data portion of the SP_DEVINSTALL_PARAMS structure.

A set of requests (DIF_SELECTCLASSDRIVERS, DIF_VALIDATECLASSDRIVERS, and DIF_INSTALLCLASSDRIVERS) can be used to install a multiple set of drivers instead of a single driver. An installer should process these requests only if it intends to carry out the complete installation on its own.

A DIF_SELECTCLASSDRIVERS request directs the installer to select the driver or drivers to install for the given class; DIF_VALIDATECLASSDRIVERS directs the installer to validate all settings for the given devices; and DIF_INSTALLCLASSDRIVERS directs the installer to install the selected and validated drivers. The default action for these requests is to do nothing.

A DIF_INSTALLWIZARD request directs the installer to add pages to the New Device Install wizard for the class associated with a device that is being installed and the DIF_DESTROYWIZARDDATA request directs the installer to destroy any private data allocated by the DIF_INSTALLWIZARD request.

A DIF_MOVEDEVICE request directs the installer to move the device to a new location in the registry’s Enum branch.

A DIF_DETECT request directs the installer to detect any devices of the class associated with a particular HDEVINFO handle and adds the devices to the device information set.


2.4.2.6 Device Installation Functions Summary

Installers can use the device installation functions provided by Setup to carry out installation operations. The device installation functions allow installers to search INF files for compatible drivers, display driver choices to the user through selection dialogs, and perform the actual driver installation.

Most of the device installation functions rely on information in the SP_DEVINFO_DATA structure to carry out installation tasks. Each device has an SP_DEVINFO_DATA structure associated with it. You can retrieve a handle (HDEVINFO) to a device information set that contains all installed devices in a particular class by using the SetupDiGetClassDevs function. You can use the SetupDiCreateDeviceInfo function to add a new device to a device information set. You can free all SP_DEVINFO_DATA structures in a device information set by using the SetupDiDestroyDeviceInfoList function. This function also frees any compatible device and class device lists that may have been added to the structure.

By using the SetupDiBuildDriverInfoList function, you can generate a list of selections from which the installer or the user can choose the driver or device to install. SetupDiBuildDriverInfoList creates a list of compatible drivers or a list of all devices of a particular class.

Once you have a list of compatible drivers, you can prompt the user to select from the list by using the SetupDiSelectDevice function. This function displays a dialog box that contains information about each device in the device information set. You can install a selected driver by using the SetupDiInstallDevice function. This function uses information in the driver’s INF file to create to any new registry entries required, set the configuration of the device hardware, and copy the driver files to the appropriate directories.

An installer might need to examine and set values under the registry key for a device that is about to be installed. You can open the hardware or driver key for a device by using the SetupDiCreateDevRegKey or SetupDiOpenDevRegKey function.

You can install a new class of device by using the SetupDiInstallClass function. This function installs the new class of device from an INF file that contains a ClassInstall32 section.

You can remove a device from the system by using the SetupDiRemoveDevice function. This function deletes the device’s registry entries and stops the device if possible. If the device cannot be dynamically stopped, the function sets flags that eventually cause the user to be prompted to shut down the system.

Some device installation functions are intended to be used by the system and may not be useful for ordinary installers. You can call the installer for a particular class by using the SetupDiCallClassInstaller function. This function is useful if a device installer needs support from the corresponding class installer to complete installation. The function uses the class GUID in the HDEVINFO handle or SP_DEVINFO_DATA structure to locate, load, and call the appropriate class installer.

The device installation functions are briefly summarized below. See Device Installation Functions Reference for a detailed description of the these functions.
SetupDiAskForOEMDisk | Displays a dialog that asks the user for the path to an OEM installation disk.
SetupDiBuildClassInfoList | Returns a list of class GUIDs that includes every class installed on the system.
SetupDiBuildDriverInfoList | Builds a list of drivers associated with a specified device instance or with the device information set's global class driver list. The drivers can be either class drivers or device drivers.
SetupDiCallClassInstaller | Calls the appropriate class installer with the specified installation request.
SetupDiCancelDriverInfoSearch | Cancels a driver list search that is currently underway in a different thread.
SetupDiChangeState | The default handler for the DIF_PROPERTYCHANGE class installation request. It can be used to change the state of an installed device.
SetupDiClassGuidsFromName | Retrieves the GUID(s) associated with the specified class name. This list is built based on what classes are currently installed on the system.
SetupDiClassNameFromGuid | Retrieves the class name associated with the class GUID.
SetupDiCreateDeviceInfo | Creates a new device information element and adds it as a new member to the specified device information set.
SetupDiCreateDeviceInfoList | Creates an empty device information set. This set can be associated with a class GUID.
SetupDiCreateDevRegKey | Creates a registry storage key for device-specific configuration information and returns a handle to the key.
SetupDiDeleteDeviceInfo | Deletes a member from the specified device information set. This function does not delete the actual device.
SetupDiDeleteDevRegKey | Deletes the specified registry key(s) associated with a device information element.
SetupDiDestroyClassImageList | Destroys a class image list.
SetupDiDestroyDeviceInfoList | Destroys a device information set and frees all associated memory.
SetupDiDestroyDriverInfoList | Destroys a driver information list.
SetupDiDrawMiniIcon | Draws the specified mini-icon at the location requested.
SetupDiEnumDeviceInfo | Enumerates the members of the specified device information set.
SetupDiEnumDriverInfo | Enumerates the members of a driver information list.
SetupDiGetActualSectionToInstall | Finds the appropriate Install section to use when installing a device from a Windows NT 4.0/Windows 95 INF file.
SetupDiGetClassBitmapIndex | Retrieves the index of the mini-icon supplied for the specified class.
SetupDiGetClassDescription | Retrieves the class description associated with the specified class GUID.
SetupDiGetClassDevs | Returns a device information set that contains all installed devices of a specified class.
SetupDiGetClassImageIndex | Retrieves the index within the class image list of a specified class.
SetupDiGetClassImageList | Builds an image list that contains bitmaps for every installed class and returns the list in a data structure.
SetupDiGetClassInstallParams | Retrieves class install parameters for a device information set or a particular device information element.
SetupDiGetDeviceInfoListClass | Retrieves the class GUID associated with a device information set if it has an associated class.
SetupDiGetDeviceInstallParams | Retrieves install parameters for a device information set or a particular device information element.
SetupDiGetDeviceInstanceId | Retrieves the device instance ID associated with a device information element.
SetupDiGetDeviceRegistryProperty | Retrieves the specified property from the Plug and Play device storage location in the registry.
SetupDiGetDriverInfoDetail | Retrieves detailed information for a specified driver.
SetupDiGetDriverInstallParams | Retrieves install parameters for the specified driver.
SetupDiGetHwProfileFriendlyName | Retrieves the friendly name associated with a hardware profile ID.
SetupDiGetHwProfileList | Retrieves a list of all currently defined hardware profile IDs.
SetupDiGetINFClass | Retrieves the class of a specified Windows NT 4.0-style INF file.
SetupDiGetSelectedDevice | Retrieves the currently-selected device for the specified device information set.
SetupDiGetSelectedDriver | Retrieves the member of a driver list that has been selected as the controlling driver.
SetupDiGetWizardPage | Retrieves a handle to one of the wizards provided by the Setup program for an application to include in its own wizard.
SetupDiInstallClass | Installs the ClassInstall32 section of the specified INF file.
SetupDiInstallDevice | The default handler for the DIF_INSTALLDEVICE class installation request.
SetupDiInstallDriverFiles | The default handler for the DIF_INSTALLDEVICEFILES class installation request.
SetupDiLoadClassIcon | Loads both the large and mini-icon for the specified class.
SetupDiMoveDuplicateDevice | The default handler for the DIF_MOVEDEVICE class installation request.
SetupDiOpenClassRegKey | Opens the class registry key or a specific class' subkey.
SetupDiOpenDeviceInfo | Retrieves information about an existing device instance and adds it to the specified device information set.
SetupDiOpenDevRegKey | Opens a registry storage key for device-specific configuration information and returns a handle to the key.
SetupDiRegisterDeviceInfo | Registers a device instance with the Plug and Play Manager.
SetupDiRemoveDevice | Removes a device from the system.
SetupDiSelectDevice | The default handler for the DIF_SELECTDEVICE class installation request.
SetupDiSelectOEMDrv | Selects a driver for a device using an OEM path supplied by the user.
SetupDiSetClassInstallParams | Sets or clears class install parameters for a device information set or a particular device information element.
SetupDiSetDeviceInstallParams | Sets install parameters for a device information set or a particular device information element.
SetupDiSetDeviceRegistryProperty | Sets the specified Plug and Play device registry property.
SetupDiSetSelectedDevice | Sets the specified device information element to be the currently-selected member of a device information set. This function is typically used by an installation wizard.
SetupDiSetSelectedDriver | Sets the specified member of a driver list as the currently selected-driver. It can also be used to reset the driver list so that there is no currently-selected driver.


2.4.2.7 Installer Registration

You must register your class or device installer by adding Installer32 and Icon entries to the registry for the class. Windows NT maintains a System\CurrentControlSet\Control\Class branch under HKEY_LOCAL_MACHINE for information about each class. (Windows 95 maintains a System\CurrentControlSet\Services\Class branch under HKEY_LOCAL_MACHINE for information about each class.) The value of the key contains the localized description of the class.

The Installer32 and Icon entries have this form:

Installer32=installer-DLL-name [,Installer Entry Point]

Icon=index

The system uses the icon to represent your installer to the user and calls it to install devices in the class. If the ICON value is negative, the absolute value represents a predefined icon in the class’ registry. If the ICON value is positive, it represents an icon in the class installer’s executable that will be extracted. The value 1 is reserved. See SetupDiDrawMiniIcon for a list of the predefined mini-icons.


2.4.2.8 Creating a New Control Panel Applet

When no existing Control Panel applet addresses your device installation requirements, you can create your own. For example, this might be necessary for BIOS setup interfaces. For information about creating your own Control Panel applet, see the Win32 SDK documentation.


2.4.3 Device Installation Functions Reference

This section describes the Setup functions and structures that support Windows NT 4.0 device installation. These functions provide high-level device installation support used by system installation and maintenance utilities. They provide essentially the same functionality as the Windows 95 Device Installer functions. The functions are listed in alphabetical order.


SetupDiAskForOEMDisk

BOOLEAN
SetupDiAskForOEMDisk(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiAskForOEMDisk displays a dialog that asks the user for the path to an OEM installation disk.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the device being installed.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure for the device being installed. If this parameter is not specified, the driver being installed is associated with the global class list of the device information set.
Return Value

The function returns TRUE if it is successful and the DriverPath field of the SP_DEVINSTALLPARAMS structure is updated to reflect the new path. If the user cancels the dialog, the function returns FALSE and a call to GetLastError returns ERROR_CANCELLED.

Comments

This function allows the user to browse local and network drives for OEM installation files.

See Also

SetupDiSelectOEMDrv


SetupDiBuildClassInfoList

BOOLEAN
SetupDiBuildClassInfoList(
IN DWORD Flags,
OUT LPGUID ClassGuidList,
IN DWORD ClassGuidListSize,
OUT PDWORD RequiredSize
);

SetupDiBuildClassInfoList returns a list of class GUIDs that includes every class installed on the system.

Parameters
Flags
Flags used to control exclusion of classes from the list. If no flags are specified, all classes are included in the list. Can be a combination of the following values:
DIBCI_NOINSTALLCLASS
Exclude a class if it has the NoInstallClass value entry in its registry key.
DIBCI_NODISPLAYCLASS
Exclude a class if it has the NoDisplayClass value entry in its registry key.
ClassGuidList
Supplies a pointer to a buffer that receives a list of class GUIDs.
ClassGuidListSize
Supplies the number of GUIDs in the ClassGuildList array.
RequiredSize
Supplies a pointer to a variable that receives the number of GUIDs returned. If this number is greater than the size of the ClassGuidList, the number indicates how large the ClassGuidList array must be in order to contain the list.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiGetClassDescription, SetupDiGetINFClass


SetupDiBuildDriverInfoList

BOOLEAN
SetupDiBuildDriverInfoList(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN DWORD DriverType
);

SetupDiBuildDriverInfoList builds a list of drivers associated with a specified device instance or with the device information set's global class driver list.

Parameters
DeviceInfoSet
Supplies a handle to the device information set to contain the driver information list (either globally for all members or specifically for a single member).
DeviceInfoData
Supplies a pointer to the SP_DEVINFO_DATA structure for the device information member for which to build a driver list. If this parameter is NULL, the list is associated with the device information set and not with a particular device information element. Use NULL with driver lists of type SPDIT_CLASSDRIVER only.

If the class of this device is updated as a result of building a compatible driver list, the ClassGuid field of the SP_DEVINFO_DATA structure is updated upon return.

DriverType
Specifies what type of driver list to build. Must be one of the following values:
SPDIT_CLASSDRIVER
Build a list of class drivers.
SPDIT_COMPATDRIVER
Build a list of drivers for this device. DeviceInfoData must be specified if this flag is set.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

After this function has built the specified driver list, its constituent elements may be enumerated by a call to SetupDiEnumDriverInfo.

If the driver list is associated with a device instance (that is, DeviceInfoData is specified), the resulting list is composed of drivers that have the same class as the device instance with which they are associated. If this is a global class driver list (that is, DriverType is SPDIT_CLASSDRIVER and DeviceInfoData is not specified), the class that is used when building the list is the class associated with the device information set. If the device information set has no associated class, drivers of all classes are used when building the list.

Another thread can terminate the building of a driver list by a call to SetupDiCancelDriverInfoSearch.

See Also

SetupDiCancelDriverInfoSearch, SetupDiDestroyDriverInfoList, SetupDiEnumDriverInfo


SetupDiCallClassInstaller

BOOLEAN
SetupDiCallClassInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiCallClassInstaller calls the appropriate class installer with the specified installation request (DI_FUNCTION).

Parameters
InstallFunction
Specifies the class installation request to call. Can be one of the following values:
DIF_SELECTDEVICE
Select a driver to be installed for the device. This request uses the SP_SELECTDEVICE_PARAMS structure.
DIF_INSTALLDEVICE
Install the driver for the device.
DIF_PROPERTIES
Display a properties dialog for the device (must specify a particular device in DeviceInfoData).
DIF_REMOVE
Remove the device. This request uses the SP_REMOVEDEVICE_PARAMS structure.
DIF_FIRSTTIMESETUP
Perform first-time class initialization. This request is only used for the global class information associated with the device information set.
DIF_SELECTCLASSDRIVERS
Select drivers for all devices of the class associated with the device information set or element.
DIF_VALIDATECLASSDRIVERS
Ensure that all devices of the class associated with the device information set or element are ready to be installed.
DIF_INSTALLCLASSDRIVERS
Install drivers for all devices of the class associated with the device information set or element.
DIF_CALCDISKSPACE
Compute the amount of disk space required by drivers for the device.
DIF_DESTROYPRIVATEDATA
Destroy any allocated private data stored in the ClassInstallReserved field of the SP_DEVINSTALL_PARAMS structure for the specified device information set or element.
DIF_MOVEDEVICE
Move the device to a new location in the Enum registry branch. This request uses the SP_MOVEDEV_PARAMS structure.
DIF_DETECT
Detect any devices of the class associated with the device information set or element.
DIF_INSTALLWIZARD
Add any pages necessary to the New Device Wizard for the class associated with the device information set or element. This request uses the SP_INSTALLWIZARD_DATA structure.
DIF_DESTROYWIZARDDATA
Destroy any private data allocated by a DIF_INSTALLWIZARD request. This request uses the SP_INSTALLWIZARD_DATA structure.
DIF_PROPERTYCHANGE
The device’s properties are changing. The device is being enabled, disabled, started, stopped, or has had a resource change. This request uses the SP_PROPCHANGE_PARAMS structure.
DIF_INSTALLDEVICEFILES
Install only the driver files for the selected device.
DeviceInfoSet
Supplies a handle to the device information set of the class to install.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating a particular member whose class installer should be called. If this parameter is not specified, the class installer associated with the device information set is called.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

This function attempts to load and call the class installer for the class associated with the device information element or set specified. If a class installer does not exist for this class or the class installer returns ERROR_DI_DO_DEFAULT, SetupDiCallClassInstaller calls a default procedure for the specified class installation request.

See Also

SetupDiChangeState, SetupDiInstallClass, SetupDiInstallDevice, SetupDiInstallDriverFiles, SetupDiSelectDevice


SetupDiCancelDriverInfoSearch

BOOLEAN
SetupDiCancelDriverInfoSearch(
IN HDEVINFO DeviceInfoSet
);

SetupDiCancelDriverInfoSearch cancels a driver list search that is currently underway in a different thread.

Parameters
DeviceInfoSet
Supplies a handle to the device information set for which a driver list is being built.
Return Value

If a driver list search is underway for the specified device information set when this function is called, the search is terminated. SetupDiCancelDriverInfoSearch returns TRUE when the termination is confirmed. Otherwise it returns FALSE and a call to GetLastError returns ERROR_INVALID_HANDLE.

Comments

SetupDiCancelDriverInfoSearch is a synchronous call; therefore, it does not return until the driver search thread responds to the termination request.

See Also

SetupDIBuildDriverInfoList


SetupDiChangeState

BOOLEAN
SetupDiChangeState(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiChangeState is the default handler for the DIF_PROPERTYCHANGE class installation request. It can be used to change the state of an installed device.

Parameters
DeviceInfoSet
Supplies a handle to the device information set for which a device's state is to be changed.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure for which a device's state is to be changed. If the parameter is not specified, the state of the device is changed for the global class driver list associated with the device information set.
Return Value

If the function is successful, and there are files to copy, it returns TRUE. However, if there no files to copy, it returns FALSE and a call to GetLastError returns ERROR_DI_NOFILECOPY.

If the function fails, it returns FALSE and a call to GetLastError returns a different ERROR_* code.

See Also

SetupDiCallClassInstaller, SP_PROPCHANGE_PARAMS


SetupDiClassGuidsFromName

BOOLEAN
SetupDiClassGuidsFromName(
IN PCTSTR ClassName,
OUT LPGUID ClassGuidList,
IN DWORD ClassGuidListSize,
OUT PDWORD RequiredSize
);

SetupDiClassGuidsFromName retrieves the GUID(s) associated with the specified class name. This list is built based on the classes currently installed on the system.

Parameters
ClassName
Supplies the name of the class for which to retrieve the class GUID.
ClassGuidList
Supplies a pointer to an array to receive the list of GUIDs associated with the specified class name.
ClassGuidListSize
Supplies the number of GUIDs in the ClassGuidList array.
RequiredSize
Supplies a pointer to a variable that receives the number of GUIDs associated with the class name. If this number is greater than the size of the ClassGuidList buffer, the number indicates how large the array must be in order to store all the GUIDs.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiClassNameFromGuid


SetupDiClassNameFromGuid

BOOLEAN
SetupDiClassNameFromGuid(
IN LPGUID ClassGuid,
OUT PTSTR ClassName,
IN DWORD ClassNameSize,
OUT PDWORD RequiredSize /* optional */
);

SetupDiClassNameFromGuid retrieves the class name associated with the class GUID.

Parameters
ClassGuid
Supplies the class GUID of the class name to retrieve.
ClassName
Receives the name of the class for the specified GUID.
ClassNameSize
Supplies the size, in characters, of the ClassName buffer.
RequiredSize
Receives the number of characters required to store the class name (including terminating NULL). RequiredSize is always less than MAX_CLASS_NAME_LEN.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiClassGuidsFromName


SetupDiCreateDeviceInfo

BOOLEAN
SetupDiCreateDeviceInfo(
IN HDEVINFO DeviceInfoSet,
IN PCTSTR DeviceName,
IN LPGUID ClassGuid,
IN PCTSTR DeviceDescription, /* optional */
IN HWND hwndParent, /* optional */
IN DWORD CreationFlags,
 OUT PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiCreateDeviceInfo creates a new device information element and adds it as a new member to the specified device information set.

Parameters
DeviceInfoSet
Supplies a handle to the device information set.
DeviceName
Supplies either a full device instance ID (for example, Root\*PNP0500\0000) or a root-enumerated device ID without the Enum branch prefix and instance ID suffix (for example, *PNP0500). The root-enumerated device ID can be used only if the DICD_GENERATE_ID flag is specified in the CreationFlags parameter.
ClassGuid
Supplies a pointer to the GUID for this device’s class. If the class is not known, this value should be GUID_NULL.
DeviceDescription
Supplies a textual description of the device.
hwndParent
Supplies the window handle of the top-level window to use for any user interface related to installing the device.
CreationFlags
Controls how the device information element is created. Can be a combination of the following values:
DICD_GENERATE_ID
If this flag is specified, DeviceName contains only a Root-enumerated device ID and the system creates a unique device instance key for it. This unique device instance key is generated as:

Enum\Root\DeviceName\InstanceID
where InstanceID is a four-digit, base-10 number that is unique among all subkeys under Enum\Root\DeviceName. Call SetupDiGetDeviceInstanceId to find out what ID was generated for this device information element.

DICD_INHERIT_CLASSDRVS
If this flag is specified, the resulting device information element inherits the class driver list, if any, associated with the device information set. In addition, if there is a selected driver for the device information set, that same driver is selected for the new device information element.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that will be initialized for this new device information element.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

If this device instance is being added to a set that has an associated class, the device class must be the same or the call fails. In this case, a call to GetLastError returns ERROR_CLASS_MISMATCH.

If the specified device instance is the same as an existing device instance key in the registry, the call fails. In this case, a call to GetLastError returns ERROR_DEVINST_ALREADY_EXISTS. This occurs only if the DCID_GENERATE_ID flag is not set.

If the new device information element was successfully created but the caller-supplied DeviceInfoData buffer is invalid, the function returns FALSE. In this case, a call to GetLastError returns ERROR_INVALID_USER_BUFFER. However, the device information element will have been added as a new member of the set already.

See Also

SetupDiDeleteDeviceInfo, SetupDiEnumDeviceInfo, SetupDiOpenDeviceInfo


SetupDiCreateDeviceInfoList

HDEVINFO
SetupDiCreateDeviceInfoList(
IN LPGUID ClassGuid, /* optional */
IN HWND hwndParent /* optional */
);

SetupDiCreateDeviceInfoList creates an empty device information set. This set can be associated with a class GUID.

Parameters
ClassGuid
Supplies the GUID of the class associated with this device information set. If this parameter is specified, only devices of this class may be included in this device information set.
hwndParent
Supplies the window handle of the top-level window to use for any user interface related to installation of the class driver contained in this set's global class driver list.
Return Value

The function returns a handle to an empty device information set if it is successful. Otherwise it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.

See Also

SetupDiGetClassDevs, SetupDiDestroyDeviceInfoList, SetupDiGetDeviceInfoListClass


SetupDiCreateDevRegKey

HKEY
SetupDiCreateDevRegKey(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN DWORD Scope,
IN DWORD HwProfile,
IN DWORD KeyType,
IN HINF InfHandle, /* optional */
IN PCTSTR InfSectionName /* optional */
);

SetupDiCreateDevRegKey creates a registry storage key for device-specific configuration information and returns a handle to the key.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing information about the device instance whose registry configuration storage key is to be created.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating the device instance for which to create the registry key.
Scope
Specifies the scope of the registry key to be created. The scope determines where the information is stored. The key created can be global or hardware profile-specific. Can be one of the following values:
DICS_FLAG_GLOBAL
Create a key to store global configuration information. On Windows NT this opens the key associated with the device instance that has the form:

HKLM\SYSTEM\CurrentControlSet\Control\Class\ClassGUID\InstanceID.

where classGUID is the GUID representing the device’s class and InstanceID is a base-10, four-digit ordinal representing this device instance within the list of device instances for this class.

On Windows 95 the key has the following form:

HKLM\SYSTEM\CurrentControlSet\Services\Class\ClassName\InstanceID.

where ClassName is the name representing the device’s class and InstanceID is a base-10, four-digit ordinal representing this device instance within the list of device instances for this class.

DICS_FLAG_CONFIGSPECIFIC
Create a key to store hardware profile-specific configuration information. This key has a path identical to the one created when SPDICS_FLAG_GLOBAL is set, except that it is rooted at one of the hardware-profile specific branches, instead of HKEY_LOCAL_MACHINE.
HwProfile
Specifies the hardware profile for which to create a key if HwProfileFlags is set to SPDICS_FLAG_CONFIGSPECIFIC. If HwProfile is 0, the key for the current hardware profile is created. If HwProfileFlags is SPDICS_FLAG_GLOBAL, HwProfile is ignored.
KeyType
Specifies the type of registry storage key to create. Can be one of the following values:
DIREG_DEV
Create a hardware registry key for the device. This is the key for storage of driver-independent configuration information. This key is in the Device Instance key of the Enum branch.
DIREG_DRV
Create a software, or driver, registry key for the device. This key is located in the Class branch.
InfHandle
Supplies the handle of an open INF file that contains an Install section to be executed for the newly-created key. If this parameter is specified, InfSectionName must be specified as well.
InfSectionName
Supplies the name of an Install section in the INF file specified by InfHandle. This section is executed for the newly created key. If this parameter is specified, InfHandle must be specified as well.
Return Value

If the function is successful, it returns a handle to the newly-created registry key where private configuration data pertaining to this device instance can be stored/retrieved. If the function fails, it returns INVALID_HANDLE_VALUE. Call GetLastError to get extended error information.

Comments

Close the handle returned from this function by calling RegCloseKey.

The specified device instance must be registered before calling this function. After creating the device instance with SetupDiCreateDeviceInfo, call SetupDiRegisterDeviceInfo to register it.

See Also

SetupDiCreateDeviceInfo, SetupDiGetHwProfileList, SetupDiOpenDevRegKey, SetupDiRegisterDeviceInfo


SetupDiDeleteDeviceInfo

BOOLEAN
SetupDiDeleteDeviceInfo(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiDeleteDeviceInfo deletes a member from the specified device information set. This function does not delete the actual device.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing the device information member to delete.
DeviceInfoData
Supplies a pointer to the SP_DEVINFO_DATA structure for the device information member to delete.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

If the specified device information element is in use (for example, by a wizard page), the function fails. In this case, a call to GetLastError returns ERROR_DEVINFO_DATA_LOCKED. This happens if a handle to a wizard page is retrieved with a call to SetupDiGetWizardPage with this device information element specified and the DIWP_FLAG_USE_DEVINFO_DATA flag set. To delete this device information element, you must first close the wizard’s HPROPSHEETPAGE handle.

See Also

SetupDiCreateDeviceInfo, SetupDiEnumDeviceInfo, SetupDiGetWizardPage, SetupDiOpenDeviceInfo


SetupDiDeleteDevRegKey

HKEY
SetupDiDeleteDevRegKey(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN DWORD Scope,
IN DWORD HwProfile,
IN DWORD KeyType
);

SetupDiDeleteDevRegKey deletes the specified registry key(s) associated with a device information element.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing the device instance whose registry configuration storage key is to be deleted.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating the device instance for which to delete registry key(s).
Scope
Specifies the scope of the registry key to delete. The scope indicates where the information is located. The key can be global or hardware profile-specific. Can be one of the following values:
DICS_FLAG_GLOBAL
Delete the key that stores global configuration information.
DICS_FLAG_CONFIGSPECIFIC
Delete the key that stores hardware profile-specific configuration information.
HwProfile
If Scope is set to DICS_FLAG_CONFIGSPECIFIC, the HwProfile parameter specifies the hardware profile for which to delete the registry key. If HwProfile is 0, the key for the current hardware profile is deleted. If HwProfile is 0xFFFFFFFF, the registry key for all hardware profiles is deleted.
KeyType
Specifies the type of registry storage key to delete. Can be one of the following values:
DIREG_DEV
Delete the hardware registry key for the device. This is the key for storage of driver-independent configuration information. This key is in the Device Instance key of the Enum branch.
DIREG_DRV
Delete a software, or driver, registry key for the device. This key is located in the Class branch.
DIREG_BOTH
Delete both the hardware and software keys for the device.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiCreateDevRegKey, SetupDiGetHwProfileList


SetupDiDestroyClassImageList

BOOLEAN
SetupDiDestroyClassImageList(
IN PSP_CLASSIMAGELIST_DATA ClassImageListData
);

SetupDiDestroyClassImageList destroys a class image list that was built by a call to SetupDiGetClassImageList.

Parameters
ClassImageListData
Supplies a pointer to an SP_CLASSIMAGELIST_DATA structure that contains the class image list to destroy.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

See Also

SetupDiGetClassImageList


SetupDiDestroyDeviceInfoList

BOOLEAN
SetupDiDestroyDeviceInfoList(
IN HDEVINFO DeviceInfoSet
);

SetupDiDestroyDeviceInfoList destroys a device information set and frees all associated memory.

Parameters
DeviceInfoSet
Supplies a handle to the device information set to destroy.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiCreateDeviceInfoList, SetupDiGetClassDevs


SetupDiDestroyDriverInfoList

BOOLEAN
SetupDiDestroyDriverInfoList(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN DWORD DriverType
);

SetupDiDestroyDriverInfoList destroys a driver information list.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the driver information list to destroy.
DeviceInfoData
Supplies a pointer to the SP_DEVINFO_DATA structure associated with the driver information list to destroy. If this parameter is not specified, the global class driver list is destroyed.
DriverType
Specifies what type of driver list to destroy. Must be one of the following values:
SPDIT_CLASSDRIVER
Destroy a list of class drivers.
SPDIT_COMPATDRIVER
Destroy a list of compatible drivers for the specified device. DeviceInfoData must be specified if this flag is set.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

If the currently selected driver is a member of the list being destroyed, the selection is reset.

If a class driver list is being destroyed, the DI_FLAGSEX_DIDINFOLIST and DI_DIDCLASS flags are reset for the corresponding device information set or device information element. The DI_MULTMFGS flags is also reset.

If a compatible driver list is being destroyed, the DI_FLAGSEX_DIDCOMPATINFO and DI_DIDCOMPAT flags are reset for the corresponding device information element.

See Also

SetupDiBuildDriverInfoList


SetupDiDrawMiniIcon

INT
SetupDiDrawMiniIcon(
IN HDC hdc,
IN RECT rc,
IN INT MiniIconIndex,
IN DWORD Flags
);

SetupDiDrawMiniIcon draws the specified mini-icon at the location requested.

Parameters
hdc
Supplies the handle of the device context in which the mini-icon will be drawn.
rc
The rectangle in the specified device context handle to draw the mini-icon in.
MiniIconIndex
The index of the mini-icon, as retrieved from SetupDiLoadClassIcon or SetupDiGetClassBitmapIndex. The following predefined indexes for devices can be used:

Class

Index

Computer

0

Display

2

Mouse

5

Keyboard

6

FDC

9

HDC

9

Ports

10

Net

15

System

0

Sound

8

Printer

14

Monitor

2

Network Transport

3

Network Client

16

Network Service

17

Unknown

18

Flags
These flags control the drawing operation. The LOWORD contains the actual flags defined as follows:
DMI_MASK
Draw the mini-icon’s mask into HDC.
DMI_BKCOLOR
Use the system color index specified in the HIWORD of Flags as the background color. If this flag is not set, COLOR_WINDOW is used.
DMI_USERECT
If set, SetupDiDrawMiniIcon uses the supplied rectangle and stretches the icon to fit.
Return Value

This function returns the offset from the left side of rc where the string should start.

Comments

By default, the icon is centered vertically and butted against the left side of the specified rectangle.

See Also

SetupDiGetClassBitmapIndex, SetupDiLoadClassIcon


SetupDiEnumDeviceInfo

BOOLEAN
SetupDiEnumDeviceInfo(
IN HDEVINFO DeviceInfoSet,
 IN DWORD MemberIndex,
OUT PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiEnumDeviceInfo enumerates the members of the specified device information set.

Parameters
DeviceInfoSet
Supplies a handle to the device information set.
MemberIndex
Supplies the 0-based index of the device information member to retrieve.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure to receive information about this member.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

To enumerate device information members, an installer should initially call SetupDiEnumDeviceInfo with the MemberIndex parameter set to 0. The installer should then increment MemberIndex and call SetupDiEnumDeviceInfo until there are no more values (the function fails and a call to GetLastError returns ERROR_NO_MORE_ITEMS).

See Also

SetupDiCreateDeviceInfo, SetupDiDeleteDeviceInfo, SetupDiEnumDeviceInfo, SetupDiOpenDeviceInfo


SetupDiEnumDriverInfo

BOOLEAN
SetupDiEnumDriverInfo(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN DWORD DriverType,
IN DWORD MemberIndex,
OUT PSP_DRVINFO_DATA DriverInfoData
);

SetupDiEnumDriverInfo enumerates the members of a driver information list.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing a driver information list to enumerate.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains a driver information list to enumerate. If this parameter is not specified, the global driver list owned by the device information set is used (this list is of type SPDIT_CLASSDRIVER).
DriverType
Specifies what type of driver list to enumerate. Must be one of the following values:
SPDIT_CLASSDRIVER
Enumerate a class driver list.
SPDIT_COMPATDRIVER
Enumerate a list of compatible drivers for the specified device. DeviceInfoData must be specified if this flag is set.
MemberIndex
Supplies the 0-based index of the driver information member to retrieve.
DriverInfoData
Supplies a pointer to an SP_DRVINFO_DATA structure to receive information about the enumerated driver.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

To enumerate driver information set members, an installer should first call SetupDiEnumDriverInfo with the MemberIndex parameter set to 0. It should then increment MemberIndex and call SetupDiEnumDriverInfo until there are no more values. When there are no more values, the function fails and a call to GetLastError returns ERROR_NO_MORE_ITEMS.

See Also

SetupDiBuildDriverInfoList


SetupDiGetActualSectionToInstall

HKEY
SetupDiGetActualSectionToInstall(
IN HINF InfHandle,
IN PCTSTR InfSectionName,
OUT PTSTR InfSectionWithExt, /* optional */
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, /* optional */
OUT PTSTR *Extension /* optional */
);

SetupDiGetActualSectionToInstall finds the appropriate Install section to use when installing a device from a Windows NT 4.0-style device INF file.

Parameters
InfHandle
Supplies the handle of the INF file that contains the Install section.
InfSectionName
Supplies a pointer to the name of the Install section as specified by the driver node being installed.
InfSectionWithExt
Supplies a pointer to a character buffer to receive the name of the Install section that should be used for installation. If this parameter is NULL, InfSectionWithExtSize must be 0. The caller can use the NULL value to determine the required buffer size, because the function returns TRUE and RequiredSize is set to the size, in characters, necessary to store the Install section name.
InfSectionWithExtSize
Supplies the size, in characters, of the InfSectionWithExt buffer.
RequiredSize
Supplies a pointer to a variable that receives the size, in characters, required to store the actual Install section name, including the terminating NULL.
Extension
Supplies a pointer to a variable that receives a pointer to the extension, including ".". This parameter is NULL if an extension is not required. Extension points to the extension within the caller-supplied buffer. If the InfSectionWithExt buffer is not supplied, this variable is not filled in.
Return Value

If the function is successful, it returns TRUE. If the function fails, it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.

Comments

This function supports an OS/architecture-specific extension that can be used to specify multiple installation behaviors for a single device, dependent on the operating environment. An extension is appended to the INF file Install section name to identify that it contains OS/architecture-specific installation instructions. SetupDiGetActualSectionToInstall searches for the different Install section names in the manner described below.

Starting with the Install section name as specified in the driver node (for example, InstallSec) the function attempts to find one of the following section names (the search is carried out in the order listed):

In Windows NT 4.0:
  1. InstallSec.NT platform
    where platform is x86, MIPS, Alpha, or PPC.

  2. InstallSec.NT

  3. InstallSec
In Windows 95:
  1. InstallSec.Win

  2. InstallSec

The first Install section located is used for the installation. This section name is also used as the base for Hardware and Services section names. For example, if the Install section name found is InstallSec.NTAlpha, the Services section name must be named InstallSec.NTAlpha.Services.

The original Install section name specified in the driver node is written to the driver's registry key's InfSection value entry. The extension that was found is stored in the key as the REG_SZ value InfSectionExt. For example:

InfSection       : REG_SZ :    "InstallSec"
InfSectionExt    : REG_SZ :    ".NTMIPS"

If a driver is not selected for the specified device information element, a NULL driver is installed. Upon return, the Install Parameters flags in the device’s SP_DEVINSTALL_PARAMS structure indicate whether the system should be restarted or rebooted for the device to start.

See Also

SetupDiInstallDevice


SetupDiGetClassBitmapIndex

BOOLEAN
SetupDiGetClassBitmapIndex(
IN PGUID ClassGuid,
OUT PINT MiniIconIndex
);

SetupDiGetClassBitmapIndex retrieves the index of the mini-icon supplied for the specified class.

Parameters
ClassGuid
Supplies the GUID of the class for which to retrieve the mini-icon.
MiniIconIndex
A pointer to a buffer to receive the index of the mini-icon for the specified class. This buffer is always filled in. It receives the index of the Unknown mini-icon if there is no mini-icon for the specified class.
Return Value

If there is a min-icon for the specified class, the function returns TRUE.

If there is no mini-icon for the specified class, the function returns FALSE and the MiniIconIndex buffer receives the index for the Unknown mini-icon.

See Also

SetupDiDrawMiniIcon, SetupDiLoadClassIcon


SetupDiGetClassDescription

BOOLEAN
SetupDiGetClassDescription(
IN LPGUID ClassGuid,
OUT PTSTR ClassDescription,
IN DWORD ClassDescriptionSize,
OUT PDWORD RequiredSize, /* optional */
);

SetupDiGetClassDescription retrieves the class description associated with the specified class GUID.

Parameters
ClassGuid
Supplies the GUID of the class whose description is to be retrieved.
ClassDescription
Supplies a pointer to a character buffer that receives the class description.
ClassDescriptionSize
Supplies the size, in characters, of the ClassDescription buffer.
RequiredSize
Receives the size, in characters, required to store the class description (including terminating NULL). RequiredSize is always less than LINE_LEN.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiBuildClassInfoList, SetupDiGetINFClass


SetupDiGetClassDevs

HDEVINFO
SetupDiGetClassDevs(
IN PGUID ClassGuid, /* optional */
IN PCTSTR Enumerator, /* optional */
IN HWND hwndParent, /* optional */
IN DWORD Flags
);

SetupDiGetClassDevs returns a device information set that contains all installed devices of a specified class.

Parameters
ClassGuid
Supplies a pointer to the class GUID to use when creating the list of devices. If the DIGCF_ALLCLASSES flag is set, this parameter is ignored and the resulting list contains all installed classes.
Enumerator
Supplies the name of the key under the Enum registry branch that contains device instances for which information is to be retrieved. If this parameter is not specified, device information is retrieved for all device instances in the entire Enum tree.
hwndParent
Supplies the handle of the top-level window to be used for any user interface relating to the members of this set.
Flags
Supplies control options used in building the device information set. Can be one of the following values:
DIGCF_PRESENT
Return only devices that are currently present.
DIGCF_ALLCLASSES
Return a list of installed devices for all classes. If this flag is set, the ClassGuid parameter is ignored.
DIGCF_PROFILE
Return only devices that are a part of the current hardware profile.
Return Value

If the function succeeds, it returns a handle to a device information set containing all installed devices matching the specified parameters.

If the function fails, it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.

See Also

SetupDiCreateDeviceInfoList, SetupDiDestroyDeviceInfoList


SetupDiGetClassImageIndex

BOOLEAN
SetupDiGetClassImageIndex(
IN PSP_CLASSIMAGELIST_DATA ClassImageListData,
 IN LPGUID ClassGuid,
OUT PINT ImageIndex
);

SetupDiGetClassImageIndex retrieves the index within the class image list of a specified class.

Parameters
ClassImageListData
Supplies a pointer to an SP_CLASSIMAGELIST_DATA structure that contains the class' image.
ClassGuid
Supplies a pointer to the GUID for the class.
ImageIndex
Supplies a pointer to a variable that receives the index of the specified class' image within the class image list.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

See Also

SetupDiGetClassImageList


SetupDiGetClassImageList

BOOLEAN
SetupDiGetClassImageList(
OUT PSP_CLASSIMAGELIST_DATA ClassImageListData
);

SetupDiGetClassImageList builds an image list that contains bitmaps for every installed class and returns the list in a data structure.

Parameters
ClassImageListData
Supplies a pointer to an SP_CLASSIMAGELIST_DATA structure to receive information regarding the class image list, including a handle to the image list. The cbSize field of this structure must be initialized with the size of the structure, in bytes, before calling this function or it will fail.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

Comments

The image list built by this function should be destroyed by calling SetupDiDestroyClassImageList.

See Also

SetupDiDestroyClassImageList


SetupDiGetClassInstallParams

BOOLEAN
SetupDiGetClassInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
OUT PSP_CLASSINSTALL_HEADER ClassInstallParams, /* optional */
IN DWORD ClassInstallParamsSize,
OUT PDWORD RequiredSize /* optional */
 );

SetupDiGetClassInstallParams retrieves class install parameters for a device information set or a particular device information element.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the class install parameters to retrieve.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains the class install parameters to retrieve. If this parameter is not specified, the class install parameters retrieved are associated with the device information set for the global class driver list.
ClassInstallParams
Supplies a pointer to a buffer that contains a class install header structure. This structure must have its cbSize field set to sizeof(SP_CLASSINSTALL_HEADER) on input or the buffer is considered to be invalid. On output, the InstallFunction field is filled with the DI_FUNCTION code for the class install parameters being retrieved. If the buffer is large enough, it receives the class install parameters structure specific to the function code. If ClassInstallParams is not specified, ClassInstallParamsSize must be 0.
ClassInstallParamsSize
Supplies the size, in bytes, of the ClassInstallParams buffer. If the buffer is supplied, it must be at least as large as sizeof(SP_CLASSINSTALL_HEADER). If the buffer is not supplied, ClassInstallParamsSize must be 0.
RequiredSize
Supplies a pointer to a variable to receive the number of bytes required to store the class install parameters.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

The class install parameters are specific to a particular device installation request (DI_FUNCTION) that is stored in the ClassInstallHeader field located at the beginning of the ClassInstallParams buffer.

See Also

SetupDiSetClassInstallParams


SetupDiGetDeviceInfoListClass

BOOLEAN
SetupDiGetDeviceInfoListClass(
IN HDEVINFO DeviceInfoSet,
OUT LPGUID ClassGuid
);

SetupDiGetDeviceInfoListClass retrieves the class GUID associated with a device information set if it has an associated class.

Parameters
DeviceInfoSet
Supplies a handle to the device information set to query.
ClassGuid
Supplies a pointer to the variable that receives the GUID for the associated class.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

If the specified device information set does not have an associated class because a class GUID was not specified when the set was created with SetupDiCreateDeviceInfoList, the function fails. In this case, a call to GetLastError returns ERROR_NO_ASSOCIATED_CLASS.

See Also

SetupDiCreateDeviceInfoList, SetupDiGetClassDevs


SetupDiGetDeviceInstallParams

BOOLEAN
SetupDiGetDeviceInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
OUT PSP_DEVINSTALL_PARAMS DeviceInstallParams
);

SetupDiGetDeviceInstallParams retrieves install parameters for a device information set or a particular device information element.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing information about the device instance to retrieve.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains the device install parameters to retrieve. If this parameter is not specified, the install parameters retrieved are those associated with the device information set.
DeviceInstallParams
Supplies a pointer to an SP_DEVINSTALL_PARAMS structure that receives the install parameters. The cbSize field of this structure must be set to the size, in bytes, of the structure before calling this function.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiSetDeviceInstallParams


SetupDiGetDeviceInstanceId

BOOLEAN
SetupDiGetDeviceInstanceId(
IN HDEVINFO DeviceInfoSet,
 IN PSP_DEVINFO_DATA DeviceInfoData
OUT PTSTR DeviceInstanceId,
IN DWORD DeviceInstanceIdSize,
OUT PDWORD RequiredSize /* optional */
);

SetupDiGetDeviceInstanceId retrieves the device instance ID associated with a device information element.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the device information element to retrieve.
DeviceInfoData
Supplies a pointer to the SP_DEVINFO_DATA structure for the device information element whose ID is to be retrieved.
DeviceInstanceId
Supplies a pointer to the character buffer that will receive the ID for the specified device information element.
DeviceInstanceIdSize
Supplies the size, in characters, of the DeviceInstanceId buffer.
RequiredSize
Supplies a pointer to the variable that receives the number of characters required to store the device instance ID.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiCreateDeviceInfo, SetupDiCreateDevRegKey, SetupDiOpenDeviceInfo, SetupDiOpenDevRegKey


SetupDiGetDeviceRegistryProperty

BOOLEAN
SetupDiGetDeviceRegistryProperty
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN DWORD Property,
OUT PDWORD PropertyRegDataType, /* optional */
OUT PBYTE PropertyBuffer,
IN DWORD PropertyBufferSize,
OUT PDWORD RequiredSize /* optional */
);

SetupDiGetDeviceRegistryProperty retrieves the specified property from the Plug and Play device storage location in the registry.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing information about the device instance for which to retrieve a Plug and Play registry property.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating the device instance for which to retrieve the Plug and Play property.
Property
Supplies an ordinal specifying the property to be retrieved. Can be one of the following values:

Code

Property

SPDRP_DEVICEDESC (0x00)

DeviceDesc

SPDRP_HARDWAREID (0x01)

HardwareID

SPDRP_COMPATIBLEIDS (0x02)

CompatibleIDs

SPDRP_NTDEVICEPATHS (0x03)

NtDevicePaths

SPDRP_SERVICE (0x04)

Service

SPDRP_CONFIGURATION (0x05)

Configuration

SPDRP_CONFIGURATIONVECTOR (0x06)

ConfigurationVector

SPDRP_CLASS (0x07)

Class

SPDRP_CLASSGUID (0x08)

ClassGUID

SPDRP_DRIVER (0x09)

Driver

SPDRP_CONFIGFLAGS (0x0A)

ConfigFlags

SPDRP_MFG (0x0B)

Mfg

SPDRP_FRIENDLYNAME (0x0C)

FriendlyName

PropertyRegDataType
Supplies a pointer to a variable to receive the data type of the property being retrieved. This is one of the standard registry data types.
PropertyBuffer
Supplies a pointer to a buffer to receive the property being retrieved.
PropertyBufferSize
Supplies the length, in bytes, of PropertyBuffer.
RequiredSize
Supplies a pointer to a variable to receive the number of bytes required to store the requested property in PropertyBuffer.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiSetDeviceRegistryProperty


SetupDiGetDriverInfoDetail

BOOLEAN
SetupDiGetDriverInfoDetail(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN PSP_DRVINFO_DATA DriverInfoData,
OUT PSP_DRVINFO_DETAIL_DATA DriverInfoDetailData, /* optional */
 IN DWORD DriverInfoDetailDataSize,
 OUT PDWORD RequiredSize /* optional */
);

SetupDiGetDriverInfoDetail retrieves detailed information for a specified driver.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains a driver information structure for which to retrieve details.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains a driver information structure for which to retrieve details. If this parameter is not specified, the driver referenced is a member of the global class driver list owned by the device information set.
DriverInfoData
Supplies a pointer to an SP_DRVINFO_DATA structure that specifies the driver for which details are to be retrieved.
DriverInfoDetailData
Supplies a pointer to an SP_DRVINFO_DETAIL_DATA structure that receives detailed information about the specified driver. If this parameter is not specified, DriverInfoDetailDataSize must be 0. If this parameter is specified, the cbSize field of this structure must be set to the size, in bytes, of the structure before calling SetupDiGetDriverInfoDetail.
DriverInfoDetailDataSize
Supplies the size, in bytes, of the DriverInfoDetailData buffer.
RequiredSize
Supplies a pointer to a variable that receives the number of bytes required to store the detailed driver information. This value includes both the size of the structure and the additional bytes required for the variable-length character buffer at the end that holds the hardware ID and the compatible IDs MULTI_SZ list.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

If the specified driver information member and the caller-supplied buffer are both valid, this function is guaranteed to fill in all static fields in the SP_DRVINFO_DETAIL_DATA structure and as many IDs as possible in the variable-length buffer at the end while still maintaining MULTI_SZ format. In this case, the function returns FALSE and a call to GetLastError returns ERROR_INSUFFICIENT_BUFFER. If specified, RequiredSize contains the total number of bytes required for the structure with all IDs.

See Also

SetupDiEnumDriverInfo, SetupDiGetSelectedDriver


SetupDiGetDriverInstallParams

BOOLEAN
SetupDiGetDriverInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN PSP_DRVINFO_DATA DriverInfoData,
OUT PSP_DRVINSTALL_PARAMS DriverInstallParams
);

SetupDiGetDriverInstallParams retrieves install parameters for the specified driver.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing a driver information structure for which to retrieve installation parameters.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains a driver information structure for which to retrieve installation parameters. If DeviceInfoData is not specified, the driver referenced is a member of the global class driver list owned by the device information set.
DriverInfoData
Supplies a pointer to an SP_DRVINFO_DATA structure that specifies the driver for which install parameters are to be retrieved.
DriverInstallParams
Supplies a pointer to an SP_DRVINSTALL_PARAMS structure to receive the install parameters for this driver. The cbSize field of this structure must be set to the size, in bytes, of the structure before calling this function.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

See Also

SetupDiSetDriverInstallParams


SetupDiGetHwProfileFriendlyName

BOOLEAN
SetupDiGetHwProfileFriendlyName(
IN DWORD HwProfile,
OUT PTSTR FriendlyName,
IN DWORD FriendlyNameSize,
OUT PDWORD RequiredSize /* optional */
);

SetupDiGetHwProfileFriendlyName retrieves the friendly name associated with a hardware profile ID.

Parameters
HwProfile
Supplies the hardware profile ID associated with the friendly name to retrieve. If this parameter is 0, the friendly name for the current hardware profile is retrieved.
FriendlyName
Supplies a pointer to a character buffer to receive the friendly name.
FriendlyNameSize
Supplies the size, in characters, of the FriendlyName buffer.
RequiredSize
Supplies a pointer to a variable to receive the number of characters required to store the friendly name (including the terminating NULL).
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiGetHwProfileList


SetupDiGetHwProfileList

BOOLEAN
SetupDiGetHwProfileList
OUT PDWORD HwProfileList,
IN DWORD HwProfileListSize,
OUT PDWORD RequiredSize,
OUT PDWORD CurrentlyActiveIndex /* optional */
);

SetupDiGetHwProfileList retrieves a list of all currently defined hardware profile IDs.

Parameters
HwProfileList
Supplies a pointer to an array to receive the list of currently defined hardware profile IDs.
HwProfileListSize
Supplies the size, in bytes, of the HwProfileList buffer.
RequiredSize
Supplies a pointer to a variable that receives the number of hardware profiles currently defined. If the number is larger than HwProfileListSize, the list is truncated to fit the array size. The value returned in RequiredSize indicates the array size required to store the entire list of hardware profiles. In this case, the function fails and a call to GetLastError returns ERROR_INSUFFICIENT_BUFFER.
CurrentlyActiveIndex
Supplies a pointer to a variable that receives the index within the returned HwProfileList of the currently active hardware profile.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiCreateDevRegKey, SetupDiOpenDevRegKey


SetupDiGetINFClass

BOOLEAN
SetupDiGetINFClass(
IN PCTSTR InfName,
OUT LPGUID ClassGuid,
OUT PTSTR ClassName,
IN DWORD ClassNameSize,
OUT PDWORD RequiredSize /* optional */
);

SetupDiGetINFClass returns the class of a specified Windows NT 4.0-style INF file.

Parameters
InfName
Supplies the name of a Windows 4.0-style INF file. This name can include a path. However, if just the filename is specified, the file is searched for in each directory listed in the DevicePath entry under the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion subkey of the registry.
ClassGuid
Receives the class GUID for the specified INF file. If the INF file does not specify a class name, this variable is set to GUID_NULL. Call SetupDiClassGuidsFromName to determine if one or more classes with this name are already installed.
ClassName
Receives the name of the class for the specified INF file. If the INF file does not specify a class name, but does specify a GUID, this buffer receives the name retrieved by calling SetupDiClassNameFromGuid. However, if SetupDiClassNameFromGuid cannot retrieve a class name (for example, the class is not installed), it returns an empty string.
ClassNameSize
Supplies the size, in characters, of the ClassName buffer.
RequiredSize
Receives the number of characters required to store the class name (including terminating NULL). RequiredSize is always less than MAX_CLASS_NAME_LEN.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiBuildClassInfoList, SetupDiClassGuidsFromName, SetupDiClassNameFromGuid, SetupDiGetClassDescription


SetupDiGetSelectedDevice

BOOLEAN
SetupDiGetSelectedDevice(
IN HDEVINFO DeviceInfoSet,
OUT PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiGetSelectedDevice retrieves the currently-selected device for the specified device information set.

Parameters
DeviceInfoSet
Supplies a handle to the device information set from which to retrieve the selected device.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that receives the currently-selected device. If a device is not currently selected, the function fails and a call to GetLastError returns ERROR_NO_DEVICE_SELECTED.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

SetupDiGetSelectedDevice is usually used by an installation wizard.

See Also

SetupDiSetSelectedDevice


SetupDiGetSelectedDriver

BOOLEAN
SetupDiGetSelectedDriver(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
OUT PSP_DRVINFO_DATA DriverInfoData
);

SetupDiGetSelectedDriver retrieves the member of a driver list that has been selected as the controlling driver.

Parameters
DeviceInfoSet
Supplies a handle to the device information set to query.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that indicates the device instance for which to retrieve the selected driver. If this parameter is NULL, the selected class driver for the global class driver list is retrieved.
DriverInfoData
Supplies a pointer to an SP_DRVINFO_DATA structure that receives information about the selected driver.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError. If a driver has not been selected for the specified device instance, the logged error is ERROR_NO_DRIVER_SELECTED.

See Also

SetupDiSetSelectedDriver


SetupDiGetWizardPage

HPROPSHEETPAGE
SetupDiGetWizardPage(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN PSP_INSTALLWIZARD_DATA InstallWizardData,
IN DWORD PageType,
IN DWORD Flags
);

SetupDiGetWizardPage retrieves a handle to one of the wizard pages provided by Setup for an application to include in its own wizard.

Parameters
DeviceInfoSet
Supplies the handle of the device information set for which to retrieve a wizard page.
DeviceInfoData
Supplies a pointer to a device information set with which to associate the wizard page. This parameter is only used if the Flags parameter includes DIWP_FLAG_USE_DEVINFO_DATA. If that flag is set and DeviceInfoData is not specified, the wizard page is associated with the global class driver list.
InstallWizardData
Supplies a pointer to an SP_INSTALLWIZARD_DATA structure that contains parameters to be used by the wizard page. The cbSize field of the structure must be set to the size of the structure, in bytes, or it is invalid.
PageType
Supplies an ordinal indicating the type of wizard page to retrieve. Can be the following value:
SPWPT_SELECTDEVICE
Retrieve a Select Device wizard page.
Flags
Specifies how to create the wizard page. Can be the following value:
SPWP_USE_DEVINFO_DATA
Use the device information element specified by the DeviceInfoData parameter or use the global class driver list if DeviceInfoData is not supplied. If this flag is not set, the wizard page acts upon the currently-selected device, as set by SetupDiSetSelectedDevice, or upon the global class driver list if a device is not selected.
Return Value

The function returns the handle to the requested wizard page if it is successful. Otherwise it returns NULL and the logged error can be retrieved with a call to GetLastError.

Comments

A device information set cannot be destroyed as long as it is being used by an active wizard page. In addition, if the wizard page is associated with a particular device information element, that element cannot be deleted as long as it is being used by an active wizard page.

See Also

SetupDiSetSelectedDevice


SetupDiInstallClass

BOOLEAN
SetupDiInstallClass(
IN HWND hwndParent, /* optional */
IN PCTSTR InfFileName,
IN DWORD Flags,
IN HSPFILEQ FileQueue /* optional */
);

SetupDiInstallClass installs the ClassInstall32 section of the specified INF file.

Parameters
hwndParent
Supplies the handle of the parent window for any user interface used to install this class.
InfFileName
Specifies the name of the INF file containing a ClassInstall32 section.
Flags
These flags control the installation process. Can be a combination of the following:
DI_NOVCP
Set this flag if FileQueue is supplied. DI_NOVCP instructs the SetupInstallFromInfSection function not to create a queue of its own and to use the caller-supplied queue instead. If this flag is set, files are not copied just queued.
DI_NOBROWSE
Set this flag to disable browsing in the event a copy operation cannot find a specified file. If the caller supplies a file queue, this flag is ignored.
DI_FORCECOPY
Set this flag to always copy files, even if they are already present on the user's machine. If the caller supplies a file queue, this flag is ignored.
DI_QUIETINSTALL
Set this flag to suppress the user interface unless absolutely necessary. For example, do not display the progress dialog. If the caller supplies a file queue, this flag is ignored.
FileQueue
If the DI_NOVCP flag is set, this parameter supplies a handle to a file queue where file operations should be queued but not committed.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

This function is called by a class installer when it installs a device of a new class.

See Also

SetupDiCallClassInstaller


SetupDiInstallDevice

BOOLEAN
SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiInstallDevice is the default handler for the DIF_INSTALLDEVICE class installation request. It installs a device by calling the SetupInstallFromInfSection function for the Install section of the selected driver’s INF file.

Parameters
DeviceInfoSet
Supplies a handle to the device information set for which a driver is to be installed.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure for which a driver is to be installed. This is an IN OUT parameter because the DevInst field of the structure can be updated with a new handle value upon return.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

After a device is installed by the SetupDiInstallDevice function, the device is started if possible. If, however, the DI_NEEDRESTART, DI_NEEDREBOOT, or DI_DONOTCALLCONFIGMG flag in the SP_DEVINSTALL_PARAMS structure is set, the device is not started.

See Also

SetupDiCallClassInstaller, SetupDiInstallDriverFiles


SetupDiInstallDriverFiles

BOOLEAN
SetupDiInstallDriverFiles(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiInstallDriverFiles is the default handler for the DIF_INSTALLDEVICEFILES class installation request. It is similar to the SetupDiInstallDevice function; however, it performs only the file copy commands in an INF file Install section. The SetupDiInstallDriverFiles function does not configure the device.

Parameters
DeviceInfoSet
Supplies a handle to the device information set for which driver files are to be installed.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating a particular member for which to perform file installation.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

A driver must be selected for the specified device information set or element before this function is called.

See Also

SetupDiCallClassInstaller, SetupDiInstallDevice


SetupDiLoadClassIcon

BOOLEAN
SetupDiLoadClassIcon(
IN LPGUID ClassGuid,
OUT HICON *LargeIcon, /* optional */
OUT LPINT MiniIconIndex /* optional */
);

SetupDiLoadClassIcon loads both the large and mini-icon for the specified class.

Parameters
ClassGuid
Supplies the GUID of the class for which the icon(s) should be loaded.
LargeIcon
Supplies a pointer to a variable to receive a handle for the loaded large icon for the specified class. If this parameter is not specified, the large icon is not loaded.
MiniIconIndex
Supplies a pointer to a variable to receive the index of the mini-icon for the specified class. The mini-icon is stored in the device installer's mini-icon cache.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

Comments

The icons of the class are either predefined and loaded from the device installer’s internal cache, or they are loaded directly from the class installer’s executable. This function querys the registry value ICON in the specified class’ section. If the ICON value is specified, it indicates which mini-icon to load.

If the ICON value is negative, the absolute value represents a predefined icon in the class’ registry. See SetupDiDrawMiniIcon for a list of the predefined mini-icons.

If the ICON value is positive, it represents an icon in the class installer’s executable that will be extracted. The value 1 is reserved. This function also uses the INSTALLER32 registry value and then the ENUMPROPPAGES32 registry value to determine which executable to extract the icon(s) from.

See Also

SetupDiDrawMiniIcon, SetupDiGetClassBitmapIndex


SetupDiMoveDuplicateDevice

BOOLEAN
SetupDiMoveDuplicateDevice(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DestinationDeviceInfoData
);

SetupDiMoveDuplicateDevice is the default handler for the DIF_MOVEDEVICE class installation request. This function moves a device to a new location of the Enum branch of the registry.

Parameters
DeviceInfoSet
Supplies a handle to the device information set for the device which is to be moved.
DestinationDeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure for the device instance that is the destination of the move. This device instance must contain class install parameters for DIF_MOVEDEVICE or the function fails with an error of ERROR_NO_CLASSINSTALL_PARAMS.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SP_MOVEDEV_PARAMS


SetupDiOpenClassRegKey

HKEY
SetupDiOpenClassRegKey(
IN PGUID ClassGuid, /* optional */
IN REGSAM samDesired
 );

SetupDiOpenClassRegKey opens the class registry key or a specific class' subkey.

Parameters
ClassGuid
Supplies the GUID of the class whose key is to be opened. If this parameter is NULL, the root of the class tree (HKLM\SYSTEM\CurrentControlSet\Control\Class) is opened.
samDesired
Specifies the access you require for the key.
Return Value

If the function is successful, it returns TRUE. If the function fails, it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.

Comments

This function does not create a registry key if it does not already exist.

The handle returned from this function must be closed by calling RegCloseKey.

See Also

SetupDiOpenDevRegKey


SetupDiOpenDeviceInfo

BOOLEAN
SetupDiOpenDeviceInfo(
IN HDEVINFO DeviceInfoSet,
IN PCTSTR DeviceInstanceId,
IN HWND hwndParent, /* optional */
IN DWORD OpenFlags
 OUT PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiOpenDeviceInfo retrieves information about an existing device instance and adds it to the specified device information set. If a device information element already exists for this device instance, the function returns the existing element.

Parameters
DeviceInfoSet
Supplies a handle to a device information set to which the opened device information element should be added.
DeviceInstanceId
Supplies the ID of the device instance. This is the registry path relative to the Enum path of the device instance key. For example, Root\*PNP0500\0000.
hwndParent
Supplies the window handle of the top-level window to use for any user interface related to installing the device.
OpenFlags
Controls how the device information element is opened. Can be the following:
DIOD_INHERIT_CLASSDRVS
If this flag is specified, the resulting device information element inherits the class driver list, if any, associated with the device information set. In addition, if there is a selected driver for the device information set, that same driver is selected for the new device information element.

If the device information element was already present, its class driver list, if any, is replaced with the inherited list.

DeviceInfoData
Supplies a pointer to a variable that receives a context structure that is initialized for the newly-opened device information element.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

If this device instance is being added to a set that has an associated class, the device class must be the same or the call will fail. In this case, a call to GetLastError returns ERROR_CLASS_MISMATCH.

If the new device information element is successfully opened but the caller-supplied DeviceInfoData buffer is invalid, this function returns FALSE. In this case, a call to GetLastError returns ERROR_INVALID_USER_BUFFER. However, the device information element is added as a new member of the set anyway.

See Also

SetupDiCreateDeviceInfo, SetupDiDeleteDeviceInfo, SetupDiEnumDeviceInfo


SetupDiOpenDevRegKey

HKEY
SetupDiOpenDevRegKey(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN DWORD Scope,
IN DWORD HwProfile,
IN DWORD KeyType,
IN REGSAM samDesired
);

SetupDiOpenDevRegKey opens a registry storage key for device-specific configuration information and returns a handle to the key.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing information about the device instance whose registry configuration storage key is to be opened.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating the device instance for which to open the registry key.
Scope
Specifies the scope of the registry key to open. The scope determines where the information is stored. The key opened can be global or hardware profile-specific. Can be one of the following values:
DICS_FLAG_GLOBAL
Open a key to store global configuration information. On Windows NT this opens the key associated with the device instance that has the form:

HKLM\SYSTEM\CurrentControlSet\Control\Class\ClassGUID\InstanceID.

where classGUID is the GUID representing the device’s class and InstanceID is a base-10, four-digit ordinal representing this device instance within the list of device instances for this class.

On Windows 95 the key has the following form:

HKLM\SYSTEM\CurrentControlSet\Services\Class\ClassName\InstanceID.

where ClassName is the name representing the device’s class and InstanceID is a base-10, four-digit ordinal representing this device instance within the list of device instances for this class.

DICS_FLAG_CONFIGSPECIFIC
Open a key to store hardware profile-specific configuration information. This key has a path identical to the one opened by SPDICS_FLAG_GLOBAL, except that it is rooted at one of the hardware-profile specific branches, instead of HKEY_LOCAL_MACHINE.
HwProfile
Specifies the hardware profile to open a key for, if Scope is set to SPDICS_FLAG_CONFIGSPECIFIC. If HwProfile is 0, the key for the current hardware profile is opened. If Scope is SPDICS_FLAG_GLOBAL, this parameter is ignored.
KeyType
Specifies the type of registry storage key to open. Can be one of the following values:
DIREG_DEV
Open a hardware registry key for the device. This is the key for storage of driver-independent configuration information. This key is in the Device Instance key of the Enum branch.
DIREG_DRV
Open a software, or driver, registry key for the device. This key is located in the Class branch.
samDesired
Specifies the access you require for this key.
Return Value

If the function is successful, it returns a handle to an opened registry key where private configuration data pertaining to this device instance can be stored/retrieved.

If the function fails, it returns INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.

Comments

Close the handle returned from this function by calling RegCloseKey.

The specified device instance must be registered before calling this function. After creating the device instance with SetupDiCreateDeviceInfo, call SetupDiRegisterDeviceInfo to register it.

See Also

SetupDiCreateDeviceInfo, SetupDiCreateDevRegKey, SetupDiGetHwProfileList, SetupDiRegisterDeviceInfo


SetupDiRegisterDeviceInfo

BOOLEAN
SetupDiRegisterDeviceInfo(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData,
IN DWORD Flags,
IN PSP_DETSIG_CMPPROC CompareProc, /* optional */
IN PVOID CompareContext, /* optional */
 OUT PSP_DEVINFO_DATA DupDeviceInfoData /* optional */
);

SetupDiRegisterDeviceInfo registers a device instance with the Plug and Play Manager.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the device information element for this device instance.
DeviceInfoData
Supplies a pointer to the SP_DEVINFO_DATA structure for the device instance being registered. This is an IN OUT parameter because the DevInst field of the structure can be updated with a new handle value upon return.
Flags
Controls how the device is registered. Can be the following value:
SPRDI_FIND_DUPS
Search for a previously-existing device instance corresponding to the device information pointed to by DeviceInfoData. If this flag is not specified, the device instance is registered regardless of whether or not a device instance already exists for it.
CompareProc
Supplies a comparison callback function to use in duplicate detection. If specified, the function is called for each device instance that is of the same class as the device instance being registered. The prototype of the callback function is as follows:
typedef  DWORD (CALLBACK* PSP_DETSIG_CMPPROC) (
    IN HDEVINFO          DeviceInfoSet,
    IN PSP_DEVINFO_DATA  NewDeviceData,
    IN PSP_DEVINFO_DATA  ExistingDeviceData,
    IN PVOID             CompareContext         /* optional */
    );

The compare function must return ERROR_DUPLICATE_FOUND if it finds that the two devices are duplicates. Otherwise it should return NO_ERROR. If some other error is encountered, the callback function should return the appropriate ERROR_* code to indicate the failure.

If CompareProc is not specified and duplication detection is requested, a default comparison behavior is used. The default is to compare the new device’s detect signature with the detect signature of all other devices in the class. The detect signature is contained in the class-specific resource descriptor of the device’s bootlog configuration.

CompareContext
Supplies a pointer to a caller-supplied context buffer that is passed into the callback function. This parameter is ignored if CompareProc is not specified.
DupDeviceInfoData
Supplies a pointer to a device information element that is initialized for the duplicate device instance, if any, discovered as a result of attempting to register this device. This will be filled in if the function returns FALSE, and GetLastError returns ERROR_DUPLICATE_FOUND. This device information element is added as a member of the specified DeviceInfoSet, if not already a member. If DupDeviceInfoData is not specified, the duplicate is not added to the device information set.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

After registering a device information element, the caller should refresh any stored copies of the DevInst handle associated with this device. This is necessary because the handle value might have changed during registration. The caller need not retrieve the SP_DEVINFO_DATA structure again because the DevInst field of the structure is updated to reflect the current value of the handle. The SP_DEVINSTALL_PARAMS structure should be retrieved because registering a device information element may require a system reboot (if the DI_NEEDREBOOT flag is set).

See Also

SP_DEVINFO_DATA, SP_DEVINSTALL_PARAMS


SetupDiRemoveDevice

BOOLEAN
SetupDiRemoveDevice(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiRemoveDevice is the default handler for the DIF_REMOVE installation request. It removes a device from the system.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing the device to be removed.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure for which the device instance should be removed. This is an IN OUT parameter because the DevInst field of the structure can be updated with a new handle value upon return. If this is a global removal or the last hardware profile-specific removal, all traces of the device instance are deleted from the registry and the DeviceInfoSet handle is NULL.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

Comments

This function removes the device from the system. It deletes both of the device's registry keys and dynamically stops the device if its DevInst is active. If the device cannot be dynamically stopped, flags are set in the Install Parameter block of the device information set that eventually cause the user to be prompted to shut down the system. Device removal is either global to all hardware profiles or specific to one hardware profile as specified by the ClassInstallParams field of the structure.

See Also

SP_DEVINFO_DATA, SP_REMOVEDEVICE_PARAMS


SetupDiSelectDevice

BOOLEAN
SetupDiSelectDevice(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiSelectDevice is the default handler for the DIF_SELECTDEVICE class installation request.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing the device for which a driver is to be selected.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that indicates the member of the device information set for which a driver is to be selected. If this parameter is not specified, a driver is selected for the global class driver list associated with the device information set.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

Comments

The function handles the user interface that allows the user to select a driver for the device specified. By setting the Flags field of the SP_DEVINSTALL_PARAMS structure, the caller can specify special handling of the user interface such as to allow users to select a driver from an OEM installation disk.

See Also

SetupDiCallClassInstaller, SP_DEVINSTALL_PARAMS


SetupDiSelectOEMDrv

BOOLEAN
SetupDiSelectOEMDrv(
IN HWND hwndParent, /* optional */
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData /* optional */
);

SetupDiSelectOEMDrv selects a driver for a device using an OEM path supplied by the user.

Parameters
hwndParent
Supplies a window handle that will be the parent of any dialogs created during the processing of this function. This parameter can be used to override the hwndParent field in the install parameters block of the specified device information set or element.
DeviceInfoSet
Supplies a handle to the device information set that contains the device being installed.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure for the device being installed. If this parameter is not specified, the device being installed is associated with the global class driver list of the device information set.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

Comments

This function asks the user for the OEM path and then calls the class installer to select a driver from the OEM path.

See Also

SetupDiAskForOEMDisk


SetupDiSetClassInstallParams

BOOLEAN
SetupDiSetClassInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN PSP_CLASSINSTALL_HEADER ClassInstallParams, /* optional */
IN DWORD ClassInstallParamsSize
 );

SetupDiSetClassInstallParams sets or clears class install parameters for a device information set or a particular device information element.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the class install parameters to set.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains the class install parameters to set. If DeviceInfoData is not specified, the class install parameters to set are associated with the device information set.
ClassInstallParams
Supplies a pointer to a buffer that contains the new class install parameters to use. The SP_CLASSINSTALL_HEADER structure at the beginning of this buffer must have its cbSize field set to sizeof(SP_CLASSINSTALL_HEADER) and the InstallFunction field must be set to the DI_FUNCTION code that reflects the type of parameters contained in the rest of the buffer.

If ClassInstallParams is not specified, the current class install parameters, if any, are cleared for the specified device information set or element.

ClassInstallParamsSize
Supplies the size, in bytes, of the ClassInstallParams buffer. If the buffer is not supplied (that is, the class install parameters are being cleared), ClassInstallParamsSize must be 0.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

All parameters are validated before any changes are made. Therefore, a return value of FALSE indicates that no parameters where modified.

A consequence of setting class install parameters is that the DI_CLASSINSTALLPARAMS flag is set. If the caller wants to set the parameters, but disable their use, this flag must be cleared by a call to SetupDiSetDeviceInstallParams.

If the class install parameters are cleared, the DI_CLASSINSTALLPARAMS flag is reset.

See Also

SetupDiGetClassInstallParams, SetupDiSetDeviceInstallParams


SetupDiSetDeviceInstallParams

BOOLEAN
SetupDiSetDeviceInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN PSP_DEVINSTALL_PARAMS DeviceInstallParams
);

SetupDiSetDeviceInstallParams sets install parameters for a device information set or a particular device information element.

Parameters
DeviceInfoSet
Supplies a handle to the device information set that contains the device install parameters to set.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains device install parameters to set. If this parameter is not specified, the install parameters set are associated with the device information set for the global class driver list.
DeviceInstallParams
Supplies a pointer to an SP_DEVINSTALL_PARAMS structure that contains the new values of the parameters. The cbSize field of this structure must be set to the size, in bytes, of the structure before calling this function.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

All parameters are validated before any changes are made. Therefore, a return value of FALSE indicates that no parameters were modified.

See Also

SetupDiGetDeviceInstallParams


SetupDiSetDeviceRegistryProperty

BOOLEAN
SetupDiSetDeviceRegistryProperty(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData,
IN DWORD Property,
IN PBYTE PropertyBuffer,
IN DWORD PropertyBufferSize
);

SetupDiSetDeviceRegistryProperty sets the specified Plug and Play device registry property.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing information about the device instance whose Plug and Play registry property is to be modified.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating the device instance whose Plug and Play registry property is to be modified. If the ClassGUID property is being set, this structure is updated upon return to reflect the device's new class.
Property
Supplies an ordinal specifying the property to be set. Can be one of the following values:

Code

Property

SPDRP_DEVICEDESC (0x00)

DeviceDesc

SPDRP_HARDWAREID (0x01)

HardwareID

SPDRP_COMPATIBLEIDS (0x02)

CompatibleIDs

SPDRP_SERVICE (0x04)

Service

SPDRP_CLASSGUID (0x08)

ClassGUID

SPDRP_DRIVER (0x09)

Driver

SPDRP_CONFIGFLAGS (0x0A)

ConfigFlags

SPDRP_MFG (0x0B)

Mfg

SPDRP_FRIENDLYNAME (0x0C)

FriendlyName

PropertyBuffer
Buffer that contains the new data for the property.
PropertyBufferSize
Supplies the size, in bytes, of PropertyBuffer.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

Comments

The class name property cannot be set because it is based on the corresponding class GUID and is automatically updated when that property is changed. When the ClassGUID property changes, SetupDiSetDeviceRegistryProperty automatically cleans up any software keys associated with the device.

See Also

SetupDiGetDeviceRegistryProperty


SetupDiSetDriverInstallParams

BOOLEAN
SetupDiSetDriverInstallParams(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN PSP_DRVINFO_DATA DriverInfoData,
IN PSP_DRVINSTALL_PARAMS DriverInstallParams
);

SetupDiSetDriverInstallParams establishes install parameters for the specified driver.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing a driver information structure for which to set installation parameters.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains a driver information structure for which to set installation parameters. If this parameter is not specified, the driver referenced is a member of the global class driver list owned by the device information set.
DriverInfoData
Supplies a pointer to an SP_DRVINFO_DATA structure that specifies the driver for which install parameters are to be set.
DriverInstallParams
Supplies a pointer to an SP_DRVINSTALL_PARAMS structure that specifies what the new driver install parameters should be. The cbSize field of this structure must be set to the size, in bytes, of the structure before this function is called.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved by a call to GetLastError.

See Also

SetupDiGetDriverInstallParams


SetupDiSetSelectedDevice

BOOLEAN
SetupDiSetSelectedDevice(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData
);

SetupDiSetSelectedDevice sets the specified device information element to be the currently-selected member of a device information set. This function is typically used by an installation wizard.

Parameters
DeviceInfoSet
Supplies a handle to the device information set for which the selected device should be set.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that specifies the device information element to select.
Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiGetSelectedDevice


SetupDiSetSelectedDriver

BOOLEAN
SetupDiSetSelectedDriver(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, /* optional */
IN OUT PSP_DRVINFO_DATA DriverInfoData /* optional */
);

SetupDiSetSelectedDriver sets the specified member of a driver list as the currently-selected driver. It can also be used to reset the driver list so that there is no currently-selected driver.

Parameters
DeviceInfoSet
Supplies a handle to the device information set containing information about the device instance for which to set a selected driver.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure that contains the device information element for which to select a driver. If this parameter is NULL, a class driver for the global class driver list is selected.
DriverInfoData
If this parameter is specified, it supplies a pointer to a driver information structure that indicates the driver to be selected. If this parameter is NULL, the driver list is reset (that is, no driver is selected).

If the Reserved field of the SP_DEVINFO_DATA structure is 0, the caller is requesting a search for a driver node with the specified parameters (DriverType, Description, and ProviderName). If a match is found, that driver node is selected. The Reserved field is updated on output to reflect the actual driver node where the match was found. If a match is not found, the function fails and a call to GetLastError returns ERROR_INVALID_PARAMETER.

Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE and the logged error can be retrieved with a call to GetLastError.

See Also

SetupDiGetSelectedDriver


SP_CLASSIMAGELIST_DATA

typedef struct _SP_CLASSIMAGE_DATA {
    DWORD cbSize;
    HIMAGELIST ImageList;
    DWORD Reserved;
} SP_CLASSIMAGE_DATA, *PSP_CLASSIMAGE_DATA;

SP_CLASSIMAGELIST_DATA contains the class image list.

Members
cbSize
Contains the size, in bytes, of the SP_CLASSIMAGE_DATA structure.
ImageList
A handle to the class image list.
See Also

SetupDiDestroyClassImageList, SetupDiGetClassImageIndex, SetupDiGetClassImageList


SP_CLASSINSTALL_HEADER

typedef struct _SP_CLASSINSTALL_HEADER {
    DWORD cbSize;
    DI_FUNCTION InstallFunction;
} SP_CLASSINSTALL_HEADER, *PSP_CLASSINSTALL_HEADER;

SP_CLASSINSTALL_HEADER must be the first member of any class install parameter structure. It defines the class installation request of the install parameter structure.

Members
cbSize
Contains the size, in bytes, of the SP_CLASSINSTALL_HEADER structure.
InstallFunction
Specifies the class installation request for a class install parameter structure. Can be one of the following:
DIF_SELECTDEVICE
Select a driver to be installed for the device. This request uses the SP_SELECTDEVICE_PARAMS structure.
DIF_INSTALLDEVICE
Install the driver for the device.
DIF_PROPERTIES
Display a properties dialog for the device (must specify a particular device in DeviceInfoData).
DIF_REMOVE
Remove the device. This request uses the SP_REMOVEDEVICE_PARAMS structure.
DIF_FIRSTTIMESETUP
Perform first-time class initialization. This request is only used for the global class information associated with the device information set.
DIF_SELECTCLASSDRIVERS
Select drivers for all devices of the class associated with the device information set or element.
DIF_VALIDATECLASSDRIVERS
Ensure that all devices of the class associated with the device information set or element are ready to be installed.
DIF_INSTALLCLASSDRIVERS
Install drivers for all devices of the class associated with the device information set or element.
DIF_CALCDISKSPACE
Compute the amount of disk space required by drivers for the device.
DIF_DESTROYPRIVATEDATA
Destroy any allocated private data stored in the ClassInstallReserved field of the SP_DEVINSTALL_PARAMS structure for the specified device information set or element.
DIF_MOVEDEVICE
Move the device to a new location in the Enum registry branch. This request uses the SP_MOVEDEV_PARAMS structure.
DIF_DETECT
Detect any devices of the class associated with the device information set or element.
DIF_INSTALLWIZARD
Add any pages necessary to the New Device Wizard for the class associated with the device information set or element. This request uses the SP_INSTALLWIZARD_DATA structure.
DIF_DESTROYWIZARDDATA
Destroy any private data allocated by a DIF_INSTALLWIZARD request. This request uses the SP_INSTALLWIZARD_DATA structure.
DIF_PROPERTYCHANGE
The device’s properties are changing. The device is being enabled, disabled, started, stopped, or has had a resource change. This request uses the SP_PROPCHANGE_PARAMS structure.
DIF_INSTALLDEVICEFILES
Install only the driver files for the selected device.
DeviceInfoSet
Supplies a handle to the device information set of the class to install.
DeviceInfoData
Supplies a pointer to an SP_DEVINFO_DATA structure indicating a particular member whose class installer should be called. If this parameter is not specified, the class installer associated with the device information set is called.
Comments

The InstallFunction member must be set to the function code corresponding to the type of structure and cbSize must be set to the size of the SP_CLASSINSTALL_HEADER structure. For example:

SP_REMOVEDEVICE_PARAMS RemoveDeviceParams;
RemoveDeviceParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
RemoveDeviceParams.ClassInstallHeader.InstallFunction = DIF_REMOVE;

See Also

SetupDiCallClassInstaller, SetupDiGetClassInstallParams, SetupDiSetClassInstallParams, SP_INSTALLWIZARD_DATA, SP_MOVEDEV_PARAMS, SP_PROPCHANGE_PARAMS, SP_REMOVEDEVICE_PARAMS, SP_SELECTDEVICE_PARAMS


SP_DEVINFO_DATA

typedef struct _SP_DEVINFO_DATA {
    DWORD cbSize;
    GUID ClassGuid;
    DWORD DevInst;
    DWORD Reserved;
} SP_DEVINFO_DATA, *PSP_DEVINFO_DATA;

SP_DEVINFO_DATA defines a device instance that is a member of a device information set.

Members
cbSize
Contains the size, in bytes, of the SP_DEVINFO_DATA structure.
ClassGuid
Contains the GUID of this device’s class.
DevInst
Contains the handle to this device instance. This handle is used with certain Configuration Manager functions.


SP_DEVINSTALL_PARAMS

typedef struct _SP_DEVINSTALL_PARAMS {
    DWORD cbSize;
    DWORD Flags;
    DWORD FlagsEx;
    HWND hwndParent;
    PSP_FILE_CALLBACK InstallMsgHandler;
    PVOID InstallMsgHandlerContext;
    HSPFILEQ FileQueue;
    DWORD ClassInstallReserved;
    DWORD Reserved;
    TCHAR DriverPath[MAX_PATH];
} SP_DEVINSTALL_PARAMS, *PSP_DEVINSTALL_PARAMS;

SP_DEVINSTALL_PARAMS defines device install parameters associated with a particular device information element or globally with a device information set.

Members
cbSize
Contains the size, in bytes, of the SP_DEVINSTALL_PARAMS structure.
Flags
Flags that control installation and user interface functions. Some flags can be set prior to calling the device installation functions while others are set automatically during the processing of some functions. Flags can be a combination of the following values:
DI_SHOWOEM
Set to allow support for OEM disks.
DI_NOVCP
Set to disable creation of a new copy queue. Use the caller-supplied copy queue.
DI_DIDCOMPAT
Will be set if SetupDiBuildDriverInfoList has already built a list of compatible drivers for this device. If this list has already been built, it contains all the driver information and this flag is always set.
DI_DIDCLASS
Will be set if SetupDiBuildDriverInfoList has already built a list of class drivers for this device. If this list has already been built, it contains all the driver information and this flag is always set.
DI_NEEDRESTART
Will be set by SetupDiInstallDevice if the device requires that the machine be rebooted after device installation or a device state change.
DI_NEEDREBOOT
For Windows NT, this is the same as DI_NEEDRESTART.
DI_NOBROWSE
Set to disable browsing when the user is selecting an OEM disk path.
DI_MULTMFGS
Will be set by SetupDiBuildDriverInfoList if a class driver list contains drivers provided by multiple manufacturers.
DI_GENERALPAGE_ADDED
Set by a property page provider if a general properties page has been added to the device’s property sheet.
DI_RESOURCEPAGE_ADDED
Set by a property page provider if a resource properties page has been added to the device’s property sheet.
DI_PROPERTIES_CHANGE
Set if a device’s properties have been changed and require an update of the installer’s user interface.
DI_INF_IS_SORTED
Set to indicate that the sorting order from the INF file should be used.
DI_ENUMSINGLEINF
Set if SetupDiBuildDriverInfoList should only search the specified INF file.
DI_DONOTCALLCONFIGMG
Set if the Configuration Manager should not be called to modify the registry hardware tree during the execution of certain device installation functions (for example, SetupDiInstallDevice).
DI_INSTALLDISABLED
Set if the device should be installed in a disabled state by default.
DI_COMPAT_FROM_CLASS
Set to force SetupDiBuildDriverInfoList to build a device's list of compatible drivers from its class driver list instead of the INF file.
DI_CLASSINSTALLPARAMS
Set to use the Class Install parameters.
DI_NODI_DEFAULTACTION
Set if SetupDiCallClassInstaller should not perform any default action if the class installer returns ERR_DI_DO_DEFAULT or there is not a class installer.
DI_QUIETINSTALL
Set if the device installer functions should be as silent as possible and use default choices wherever possible.
DI_NOFILECOPY
Set if SetupDiInstallDevice should skip file copying.
DI_FORCECOPY
Set if SetupDiInstallDevice should always copy files, even if they are already present on the system.
DI_DRIVERPAGE_ADDED
Set by a property page provider if a driver properties page has been added to the device’s property sheet.
DI_USECI_SELECTSTRINGS
Set if class installer-provided strings should be used during SetupDiSelectDevice.
DI_PROPS_NOCHANGEUSAGE
Set if there should be no Enable/Disable capability on the device’s general property page.
DI_NOSELECTICONS
Set if small icons should not be used in select device dialogs.
DI_NOWRITE_IDS
Set if SetupDiInstallDevice should not write the device’s hardware and compatible IDs to the registry.
FlagsEx
Flags that provide extended control over installation and user interface functions. Some flags can be set prior to calling the device installer functions while others are set automatically during the processing of some functions. Flags can be a combination of the following values:
DI_FLAGSEX_AUTOSELECTRANK0
Set if SetupDiSelectDevice should automatically select all rank 0 drivers.
DI_FLAGSEX_CI_FAILED
Is set internally if a class installer failed to load or start.
DI_FLAGSEX_DIDINFOLIST
A class driver list has been built.
DI_FLAGSEX_DIDCOMPATINFO
A compatible driver list has been built.
DI_FLAGSEX_FILTERCLASSES
If set, SetupDiBuildClassInfoList will check for class inclusion filters. This means that a device will not be included in the class list if its class is marked as NoInstallClass.
DI_FLAGSEX_SETFAILEDINSTALL
If set, the FAILEDINSTALL flag will be set when SetupDiInstallDevice installs a NULL driver.
DI_FLAGSEX_DEVICECHANGE
If set, any user interface that displays a list of devices will rebuild its list of devices after the device property sheet is closed.
DI_FLAGSEX_ALWAYSWRITEIDS
If set and the DI_NOWRITE_IDS flag is clear, always write hardware and compatible IDs to the registry.
DI_FLAGSEX_ALLOWEXCLUDEDDRVS
If set, SetupDiSelectDevice displays drivers that have the Exclude From Select state.
DI_FLAGSEX_USECLASSFORCOMPAT
Filter INF files on the device's class when building a list of compatible drivers. If a device’s class is known, setting this flag can decrease the time required to build a list of compatible drivers.
DI_FLAGSEX_OLDINF_IN_CLASSLIST
Search legacy INF files when building a class driver list. This flag is valid for Windows NT only.
DI_FLAGSEX_NO_DRVREG_MODIFY
Do not process the AddReg and DelReg items for the device’s software (driver) key.
hwndParent
Window handle that will own the user interface dialogs related to this device.
InstallMsgHandler
Callback used to handle events during file copying.
InstallMsgHandlerContext
Private data used by the InstallMsgHandler callback.
FileQueue
A handle to a caller-supplied file queue where file operations should be queued but not committed.
DriverPath
Specifies the path to this device’s INF file. This is a non-empty string only if the driver was installed from an OEM INF file.
See Also

SetupDiBuildClassInfoList, SetupDiBuildDriverInfoList, SetupDiCallClassInstaller, SetupDiGetDeviceInstallParams, SetupDiInstallDevice, SetupDiSelectDevice, SetupDiSetDeviceInstallParams


SP_DRVINFO_DATA

typedef struct _SP_DRVINFO_DATA {
    DWORD cbSize;
    DWORD DriverType;
    DWORD Reserved;
    TCHAR Description[LINE_LEN];
    TCHAR MfgName[LINE_LEN];
    TCHAR ProviderName[LINE_LEN];
} SP_DRVINFOR_DATA, *PSP_DRVINFO_DATA;

SP_DRVINFO_DATA defines a driver information structure. This is a member of a driver information list that can be associated with a particular device instance or globally with a device information set.

Members
cbSize
Contains the size, in bytes, of the SP_DRVINFO_DATA structure.
DriverType
Specifies what type of driver is represented by this structure. Must be one of the following values:
SPDIT_CLASSDRIVER
This structure represents a class driver.
SPDIT_COMPATDRIVER
This structure represents a compatible driver.
Description
A NULL-terminated string that describes the device supported by this driver.
MfgName
A NULL-terminated string that contains the name of the manufacturer of the device supported by this driver.
ProviderName
A NULL-terminated string giving the provider of this driver. This is typically the name of the organization that creates the driver or INF file. ProviderName can be an empty string.
See Also

SetupDiEnumDriverInfo, SetupDiGetDriverInstallParams, SetupDiGetSelectedDriver, SetupDiSetDriverInstallParams, SetupDiSetSelectedDriver


SP_DRVINFO_DETAIL_DATA

typedef struct _SP_DRVINFO_DETAIL_DATA {
    DWORD cbSize;
    FILETIME InfDate;
    DWORD CompatIDsOffset;
    DWORD CompatIDsLength;
    DWORD Reserved;
    TCHAR SectionName[LINE_LEN];
    TCHAR InfFileName[MAX_PATH];
    TCHAR DrvDescription[LINE_LEN];
    TCHAR HardwareID[ANYSIZE_ARRAY];
} SP_DRVINFO_DETAIL_DATA, *PSP_DRVINFO_DETAIL_DATA;

SP_DRVINFO_DETAIL_DATA defines detailed information about a particular driver information structure.

Members
cbSize
Contains the size, in bytes, of the SP_DRVINFO_DETAIL_DATA structure.
InfDate
Date of the INF file for this driver.
CompatIDsOffset
Contains the offset, in characters, from the beginning of the HardwareID buffer where the CompatIDs list begins.
CompatIDsLength
Contains the length, in characters, of the CompatIDs list starting at offset CompatIDsOffset from the beginning of the HardwareID buffer. The CompatIDs list is a list of NULL-terminated strings with an extra NULL at the end of the list.
SectionName
A NULL-terminated string that contains the name of the INF file Install section for this driver. This must be the basic Install section name without any OS/architecture-specific extensions; for example, InstallSec.
InfFileName
A NULL-terminated string that contains the full-qualified name of the INF file for this driver.
DrvDescription
A NULL-terminated string that describes the driver.
HardwareID
A buffer that contains the HardwareID and Compatible IDs list. This is a list of NULL-terminated strings, with an extra NULL at the end of the list.
See Also

SetupDiGetDriverInfoDetail


SP_DRVINSTALL_PARAMS

typedef struct _SP_DRVINSTALL_PARAMS {
    DWORD cbSize;
    DWORD Rank;
    DWORD Flags;
    DWORD PrivateData;
    DWORD Reserved;
} SP_DRVINSTALL_PARAMS, *PSP_DRVINSTALL_PARAMS;

SP_DRVINSTALL_PARAMS defines driver install parameters associated with a particular driver information element.

Members
cbSize
Contains the size, in bytes, of the SP_DRVINSTALL_PARAMS structure.
Rank
Specifies the rank match of this driver. Ranges from 0 to n, where 0 is the most compatible.
Flags
Specifies flags that control functions operating on this driver. Can be a combination of the following:
DNF_DUPDESC
There are other providers supplying drivers that have the same description as this driver.
DNF_OLDDRIVER
This driver presently/previously controlled the associated device.
DNF_EXCLUDEFROMLIST
Do not display this driver in any driver select dialogs.
DNF_NODRIVER
Set if no physical driver is to be installed for this logical driver.
DNF_LEGACYINF
This driver node comes from a legacy INF file. This flag is valid for Windows NT only.
See Also

SetupDiGetDriverInstallParams, SetupDiSetDriverInstallParams


SP_ENABLE_CLASS_PARAMS

typedef struct _SP_ENABLE_CLASS_PARAMS {
    SP_CLASSINSTALL_HEADER ClassInstallHeader;
    GUID ClassGuid;
    DWORD EnableMessage;
} SP_ENABLE_CLASS_PARAMS, *PSP_ENABLE_CLASS_PARAMS;

The SP_ENABLE_CLASS_PARAMS structure corresponds to a DIF_ENABLECLASS installation request.

Members
ClassInstallHeader
Defines the class installation request for the install parameter structure.
ClassGuid
The GUID of the class being installed.
EnableMessage
Specifies the stage of enabling. Can be one of the following values:
ENABLECLASS_QUERY
The class is about to be enabled.
ENABLECLASS_SUCCESS
The class has been enabled.
ENABLE_FAILURE
The attempt to enable the class failed.
See Also

SetupDiCallClassInstaller


SP_INSTALLWIZARD_DATA

typedef struct _SP_INSTALLWIZARD_DATA {
    SP_CLASSINSTALL_HEADER ClassInstallHeader;
    DWORD Flags;
    HPROPSHEETPAGE DynamicPages[MAX_INSTALLWIZARD_DYNAPAGES];
    DWORD NumDynamicPages;
    DWORD DynamicPageFlags;
    DWORD PrivateFlags;
    LPARAM PrivateData;
    HWND hwndWizardDlg;
} SP_INSTALLWIZARD_DATA, *PSP_INSTALLWIZARD_DATA;

SP_INSTALLWIZARD_DATA is used by class installers to extend the operation of the hardware installation wizard by adding custom pages. It is used for both the DIF_INSTALLWIZARD and DIF_DESTROYWIZARDDATA install functions.

Members
ClassInstallHeader
Defines the class installation request for the install parameter structure.
Flags
These are internal flags used by the device installer.
DynamicPages
Contains an array of property sheet page handles. The class installer can use this array to create custom wizard pages and insert their handles into this array.
NumDynamicPages
The number of pages inserted into the DynamicPages array.
DynamicPageFlags
Flags that control the behavior of the installation wizard when dynamic pages have been added. Can be a combination of the following values:
DYNAWIZ_FLAG_PAGESADDED
Set by the hardware installation wizard if the class installer adds custom pages.
DYNAWIZ_FLAG_INSTALLDET_NEXT
If set, the hardware installation wizard allows the user to go forward from the Detected Devices page; otherwise, Finish is the default option for the Detected Devices page.
DYNAWIZ_FLAG_INSTALLDET_PREV
If set, the hardware installation wizard allows the user to go back from the Detected Devices page.
DYNAWIZ_FLAG_ANALYZE_HANDLECONFLICT
Set this flag if you jump to the Analyze page of the hardware installation wizard and want it to handle conflicts. Control will not be returned to the class installer if this flag is set and a conflict occurs.

The following flags are resource IDs for pre-defined pages of the hardware installation wizard:

IDD_DYNAWIZ_FIRSTPAGE
Resource ID for the first page that the hardware installation wizard will go to after adding the custom class installer pages.
IDD_DYNAWIZ_SELECT_PREVPAGE
Resource ID for the page that the Select Device page will go back to.
IDD_DYNAWIZ_SELECT_NEXTPAGE
Resource ID for the page that the Select Device page will go forward to.
IDD_DYNAWIZ_ANALYZE_PREVPAGE
Resource ID for the page that the Analyze dialog will go back to. This page is only used in the event that there is a problem and the user clicks on the Back button from the Analyze page.
IDD_DYNAWIZ_ANALYZE_NEXTPAGE
Resource ID for the page that the Analyze dialog will go to if it continues forward.
IDD_DYNAWIZ_INSTALLDETECTED_PREVPAGE
Resource ID for the page that the Install Detected Devices page will go back to.
IDD_DYNAWIZ_INSTALLDETECTED_NEXTPAGE
Resource ID for the page that the Install Detected Devices page will go forward to.
IDD_DYNAWIZ_INSTALLDETECTED_NODEVS
Resource ID for the page that the Install Detected Devices page will go to in the event that no devices are detected.
IDD_DYNAWIZ_SELECTDEV_PAGE
Resource ID of the hardware installation wizard's Select Device page.
IDD_DYNAWIZ_ANALYZEDEV_PAGE
Resource ID of the hardware installation wizard's Analyze page.
IDD_DYNAWIZ_INSTALLDETECTEDDEVS_PAGE
Resource ID of the hardware installation wizard's Install Detected Devices page.
IDD_DYNAWIZ_SELECTCLASS_PAGE
Resource ID of the hardware installation wizard's Select Class page.
PrivateFlags
Flags that can be defined and used by the class installer.
PrivateData
Pointer to private data defined and set by the class installer.
hwndWizardDlg
Window handle of the hardware installation wizard top-level window.
See Also

SetupDiGetWizardPage


SP_MOVEDEV_PARAMS

typedef struct _SP_MOVEDEV_PARAMS {
    SP_CLASSINSTALL_HEADER ClassInstallHeader;
    SP_DEVINFO_DATA SourceDeviceInfoData;
} SP_MOVEDEV_PARAMS, *PSP_MOVEDEV_PARAMS;

The SP_MOVEDEV_PARAMS structure corresponds to a DIF_MOVEDEVICE installation request.

Members
ClassInstallHeader
Defines the class installation request for the install parameter structure.
SourceDeviceInfoData
A pointer to an SP_DEVINFO_DATA structure for the device being moved.
See Also

SetupDiCallClassInstaller, SetupDiMoveDuplicateDevice


SP_PROPCHANGE_PARAMS

typedef struct _SP_PROPCHANGE_PARAMS {
    SP_CLASSINSTALL_HEADER ClassInstallHeader;
    DWORD StateChange;
    DWORD Scope;
    DWORD HwProfile;
} SP_PROPCHANGE_PARAMS, *PSP_PROPCHANGE_PARAMS;

The SP_PROPCHANGE_PARAMS structure corresponds to a DIF_PROPERTYCHANGE installation request.

Members
ClassInstallHeader
Defines the class installation request for the install parameter structure.
StateChange
State change action. Can be one of the following values:
DICS_ENABLE
The device is being enabled.
DICS_DISABLE
The device is being disabled.
DICS_PROPCHANGE
The properties of the device have changed.
DICS_START
The device is being started. If the device is currently disabled, it will be enabled also.
DICS_START
The device is being stopped.
Scope
Flags that specify the scope of a device property change. Can be one of the following:
DICS_FLAG_GLOBAL
Make the change in all hardware profiles.
DICS_FLAG_CONFIGSPECIFIC
Make the change in the specified profile.
DICS_FLAG_CONFIGGENERAL
One or more hardware profile-specific changes follow.
HwProfile
Supplies the hardware profile ID for profile-specific changes.
See Also

SetupDiCallClassInstaller, SetupDiChangeState


SP_PROPSHEETPAGE_REQUEST

typedef struct _SP_PROPSHEETPAGE_REQUEST {
    DWORD CbSize;
    DWORD PageRequested;
    HDEVINFO DeviceInfoSet;
    PSP_DEVINFO_DATA DeviceInfoData;
} SP_PROPSHEETPAGE_REQUEST, *PSP_PROPSHEETPAGE_REQUEST;

The SP_PROPSHEETPAGE_REQUEST structure can be passed as the first parameter (lpv) to the ExtensionPropSheetPageProc entry point in the Setupapi DLL. ExtensionPropSheetPageProc is used to retrieve a handle to a specified property sheet page. For information on ExtensionPropSheetPageProc and related functions, see the Win32 SDK documentation.

Members
CbSize
Contains the size, in bytes, of the SP_PROPSHEETPAGE_REQUEST structure.
PageRequested
The property sheet page to add to the to property sheet. Can be the following value:
SPPSR_SELECT_DEVICE_RESOURCES
Specifies the Resource Selection Page supplied by the Setupapi DLL.
DeviceInfoSet
The handle for the device information set that contains the device being installed.
DeviceInfoData
A pointer to an SP_DEVINFO_DATA structure for the device being installed.


SP_REMOVEDEVICE_PARAMS

typedef struct _SP_REMOVEDEVICE_PARAMS {
    SP_CLASSINSTALL_HEADER ClassInstallHeader;
    DWORD Scope;
    DWORD HwProfile;
} SP_REMOVEDEVICE_PARAMS, *PSP_REMOVEDEVICE_PARAMS;

The SP_REMOVEDEVICE_PARAMS structure corresponds to the DIF_REMOVE installation request.

Members
ClassInstallHeader
Defines the class installation request for the install parameter structure.
Scope
Flags that indicate the scope of the device removal. Can be one of the following values:
DI_REMOVEDEVICE_GLOBAL
Make this change in all hardware profiles.
DI_REMOVEDEVICE_CONFIGSPECIFIC
Make this change to only the hardware profile specified by HwProfile.
HwProfile
Supplies the hardware profile ID for profile-specific changes.
See Also

SetupDiCallClassInstaller, SetupDiRemoveDevice


SP_SELECTDEVICE_PARAMS

typedef struct _SP_SELECTDEVICE_PARAMS {
    SP_CLASSINSTALL_HEADER ClassInstallHeader;
    TCHAR Title[MAX_TITLE_LEN];
    TCHAR Instructions[MAX_INSTRUCTION_LEN];
    TCHAR ListLabel[MAX_LABEL_LEN];
} SP_SELECTDEVICE_PARAMS, *PSPS_SELECTDEVICE_PARAMS;

The SP_SELECTDEVICE_PARAMS structure corresponds to a DIF_SELECTDEVICE installation request.

Members
ClassInstallHeader
Defines the class installation request for the install parameter structure.
Title
Buffer that contains a class installer-provided title for the Select Device dialogs.
Instructions
Buffer that contains class installer-provided Select Device instructions.
ListLabel
Buffer that contains a label for the Select Device list of drivers.
See Also

SetupDiCallClassInstaller, SetupDiSelectDevice


Chapter 3 Building Windows NT® Drivers — The BUILD Utility

Chapter 3 Building Windows NT® Drivers ¾ The BUILD Utility

The BUILD utility is used to create every driver in the Microsoft® Windows NT® DDK. This utility automatically establishes file dependencies and simplifies the creation of platform independent driver projects. BUILD isolates all target-dependent information, such as header and library include paths, compiler names and switches, and linker switches, in a centralized location.


3.1 The BUILD Utility

Invoking BUILD with the “-?” option displays the syntax (command-line options) for this utility.

In the text below, the term build product refers to a binary file created by a BUILD operation. In the Windows NT DDK, this is a driver. However, BUILD is used to create every binary in the Windows NT product: executables, dynamic link libraries, and drivers.

BUILD supports a library-linked-to-main model. If a build product consists of several source files, specify all but the main source file as components of a library. This library is then linked with the file containing main.


3.1.1 BUILD Macros

You describe build products in a file called sources, which resides in each source code directory. This file contains a series of macro definitions that are recognized by BUILD. Following is a list of macros that are typically defined in a sources file:

TARGETNAME
Specifies the name of the library being built, excluding the file extension.
TARGETPATH
Specifies a directory name that is the destination of all build products (EXEs, DLLs, LIBs, and so on). Note that object files are always created in the \obj subdirectory.
TARGETTYPE
Specifies the type of product being built. This is typically LIBRARY or DYNLINK (for DLLs), but can specify other values.
INCLUDES
Contains a list of paths to be searched for include files during compilation. Entries in this list are separated by semicolons and can be absolute or relative.
SOURCES
Contains a list of source filenames (except for the file containing main) with extensions. This is the list of source files which will comprise the library or DLL being constructed. Entries in this list are separated by spaces or tabs.
UMTYPE
Specifies the type of product being built:

windows

Specifies Win32 (user-mode)

NT

Specifies kernel-mode

console

Specifies a Win32 Console application

UMTEST
Contains a list of named source files containing a main function. These filenames are specified without extensions, and are separated by asterisks. Each file in this list is compiled and linked with the library or DLL generated by the SOURCES line.
UMAPPL
Contains a list of named source files containing a main function. These filenames are specified without extensions, and are separated by asterisks. Each file in this list is compiled and linked with the library or DLL generated by the SOURCES line. The difference between UMTEST and UMAPPL is that if you use UMTEST, you must specify the executable names that need to be built through the command line to build.exe. If you use UMAPPL, you need not specify anything on the command line, and build.exe will automatically build all the executables.
UMAPPLEXT
Specifies the filename extension (.com, .scr, and so on) if you want image files to have something other than .exe as the extension.
TARGETEXT
Specifies the filename extension, such as .cpl, if you want DLLs to have something other than .dll as the extension.
UMLIBS
Contains a list of library path names to be linked to the files specified by UMTEST or UMAPPL. The library generated by SOURCES should be included here. Entries in this list are separated by spaces or tabs. Currently, these path names must be absolute.

The UMLIBS path names need to be defined in a special way. Since BUILD provides a mechanism for building products for several hardware targets, the destination path for build products is constructed as follows:

%TARGETPATH%\<cpu_type>\

where TARGETPATH is defined in the sources file, and cpu_type specifies the CPU for which the products were built. For example, if you define TARGETPATH as OBJ, and request a build for i386 and MIPS, the build products would be directed to the following subdirectories:

obj\i386\		// build products for i386
obj\mips\		// build products for MIPS

Because of this convention, UMLIBS entries must specify library names in the following form:

<targetpath>\*\<library_name>

where targetpath is identical to the TARGETPATH defined in the sources file, and library_name is the full filename of the library to be linked to the application. The “*” is automatically replaced by the current CPU target during the build process.

It is possible to build your applications on a logical drive other than the one that has the DDK source tree, by making use of the BASEDIR environment variable. BUILD defines this environment variable to be \ddk_root by default. If you have your source tree on a logical drive that is different from the one that has the DDK sources, then you need to prepend the BASEDIR environment variable to the libraries that you need to link to, as follows:

$(BASEDIR)\lib\*\base.lib

In addition to sources, a standard file called makefile is required in each directory containing source code to be built. This file should not vary from one source directory to another and should never be altered.

To build a particular executable, change to the directory containing the sources file and invoke BUILD. BUILD automatically builds the library specified in sources, but it builds the executables only if explicitly told to do so (unless you use UMAPPL).

For example, if a sources file defined a library called “mylib” (as TARGETNAME), and an executable (UMTEST) called “myexe,” both products would be built for the i386 with the following invocation:

build -386 myexe

If “myexe” were not specified, only “mylib” would be built by default.

On the other hand, if you had defined a library called “mylib” (as TARGETNAME) and an executable (UMAPPL) called “myexe,” both products would be built for the i386 with the following invocation:

build -386

For simple programs consisting of a single source file, a library is not required. In this situation, define the sources file as follows:

  • Set TARGETNAME and TARGETPATH as if you were going to generate a library.

  • Set TARGETTYPE to be UMAPPL_NOLIB.

  • Set SOURCES to nothing.

  • Do not include the TARGETNAME library in UMLIBS.

  • Invoke BUILD, specifying the filename listed in UMTEST.

BUILD will generate the executable file.


3.1.2 BUILD Environment Variables

The environment setup utilities set the following environment variables used by the BUILD procedure:

BUILD_DEFAULT
A list of default parameters to pass to the BUILD utility (such as “-CEF”)
BUILD_DEFAULT_TARGETS
A list of default target switches (such as “-386 -MIPS”)
BUILD_MAKE_PROGRAM
The name of the make utility used by BUILD. Currently, this must be NMAKE.

The BUILD_DEFAULT and BUILD_DEFAULT_TARGETS environment variables are set by setenv.bat as follows:

set BUILD_DEFAULT= -e -i -nmake -i
set BUILD_DEFAULT_TARGETS=-386

The BUILD_OPTIONS environment variable also is set by setenv.bat. Usually, it specifies additional components that need to be built. A typical command would be:

set BUILD_OPTIONS=v7 et4000 vga


3.1.3 Building a Source Tree

BUILD can be instructed to build an entire source tree recursively using a dirs file. This file resides in a directory that contains subdirectories (for example, at the root of a source code tree or subtree). Each subdirectory can be a source directory or another source tree. A dirs file should exist at the root of each source code subtree, and a sources file should exist in each “leaf” directory (that is, directories containing actual source code).

You can define the following macros in a dirs file:

DIRS
Contains a list of subdirectories to be built unconditionally. Entries in this list are separated by spaces or tabs.
OPTIONAL_DIRS
Contains a list of subdirectories to be built only if they are explicitly specified on the original BUILD command line.

By default, BUILD causes all x86 compilations to occur at warning level 3. You can override this warning level by defining the following in the sources file:

386_WARNING_LEVEL=-W4


3.1.4 BUILD Log Files

The BUILD utility generates the following files, which are useful in determining build product status:
build.log | Contains a log of commands invoked via NMAKE.
build.wrn | Contains a list of warnings generated during the build.
build.err | Contains a list of errors generated during the build.

Note Some compiler and linker warnings are filtered out by the BUILD utility. Filtered warnings do not appear in the statistics reported by BUILD, but they can be found in the build.log and build.wrn files.


3.1.5 Debugging Information

When checked executables or DLLs are built using this procedure, the files contain their external symbols by default. Thus, the symbols are available to the debugger as soon as the program or DLL is loaded. When free executables or DLLs are built, symbol files with the filename extension .dbg are put in the same directory as the built executables.

It is possible to get line-number (local) information in the image files using BUILD. However, this is not the default since it would have a severe impact on disk space requirements needed to build the system. This information can be included by setting the NTDEBUG environment variable as follows:

set NTDEBUG=ntsd

Setting this environment variable causes BUILD to generate .obj files and image files with full symbolic debugging information.


3.1.6 BUILD Limitations and Rules

BUILD is subject to the following limitations and rules:

  1. Only one DLL or library can be built in a given source directory. The equivalent of an import library will be generated automatically when you build a DLL.

  2. Any number of executables can be generated, as long as each consists of a single source file (containing main) that is to be linked to the library or DLL being generated.

  3. When you build a DLL, the LIBRARY statement in your .def file must match the TARGETNAME statement in the sources file exactly. The comparison is case-sensitive.

  4. If your DLL must link with other libraries, these libraries must be specified in the TARGETLIBS statement in your sources file. These libraries must be specified with the “\*\” notation already described for UMLIBS.

  5. There should be no blank spaces between a BUILD keyword and the equal sign. For example, the following line is invalid:

    SOURCES = file.c

    while the following line is valid:

    SOURCES= file.c

  6. BUILD does not process NMAKE macros or conditional syntax. Consequently, the value of a BUILD keyword in a sources file must be textual, and it cannot contain references to NMAKE macros.


Chapter 4 Debugging Windows NT Drivers

Use WinDbg (windbg.exe) from the Microsoft® Win32® SDK to debug Windows NT® drivers. Install your driver on one machine (the target) and run WinDbg on another (the host).

For a kernel-mode driver, you run WinDbg (with kernel debugging options) on the host machine. The host and target, that has had debugging enabled, communicate with each other across a serial cable. For information on kernel-mode driver debugging, see Section 4.1.

For a user-mode driver, you run a remote debugging session with WinDbg on the host and WinDbgRm, the WinDbg remote debugging component, on the target. In remote debugging, the host and target machines communicate with each other across the network or across a serial cable. For information on user-mode driver debugging, see Section 4.2.

To assist in debugging device drivers for Windows NT, WinDbg is extensible through the use of debugger extensions. There are system supplied debugger extensions for common operations as well as the ability to define custom extensions. For information on debugger extensions, see Section 4.4.


4.1 Debugging Kernel Mode Drivers

This section contains the necessary information and options that are relevant to debugging kernel mode device drivers. The following material is covered in this section:
Information | Section
Procedure for establishing a kernel debugging session. | 4.1.1
Hardware requirements for the serial debugging cable. | 4.1.2
Using the free and checked builds of Windows NT. | 4.1.3
Enabling kernel debugging on the target machine. | 4.1.4
Setting up symbols for debugging. | 4.1.5
Starting WinDbg for kernel mode debugging. | 4.1.6
Kernel deugging options. | 4.1.7
Debugging crash dumps from failed systems. | 4.1.8
How to connect to a halted target machine. | 4.1.9
Using WinDbg for kernel mode debugging over a network. | 4.1.10
The command line options for WinDbg. | 4.1.11
How to seperate symbols from the driver binary. | 4.1.12


4.1.1 Establishing a Kernel Debugging Session

Set up a kernel debugging session as follows:

  1. Connect the host and target machines with a null-modem serial cable, using any available port. The connection should then be tested. For details on the cable and how to test a connection, see Section 4.1.2.

  2. On the target machine:

    • Install the checked build of Windows NT. It is necessary that this be the same version of Windows NT as you install on the host. For more information on installing a checked build of Windows NT, please see the Getting Started guide.

    • Install your driver. For more information on installing Windows NT device drivers, see Chapter 2 of this guide.

    • Enable kernel debugging for this system using the appropriate options for the port that is connected to the host machine. For more information on enabling debugging, see Section 4.1.4

  3. On the host machine:

    • Install the free build of Windows NT. It is necessary that this be the same version of Windows NT as you install on the target. For more information on installing a free build of Windows NT, please see the Getting Started guide.

    • Install the SDK, which also installs WinDbg.

    • Copy the symbols from the target machine to the host machine into the symbol tree. For information on the exact layout of the symbol tree see Section 4.1.5.

    • Copy your driver’s source to the host machine.

    • Start WinDbg with kernel debugging enabled. For more information on starting WinDbg as a kernel debugger, see Section 4.1.6

    • Using the User DLLs dialog box from the Options menu, specify the symbol search path to the symbol tree where you copied the required symbols.

    • Using the Debug dialog box from the Options menu, specify the source search path to where you copied the source for your driver.

    • Select Go from the Run menu and wait for “KD: waiting to connect...” message to appear in the Command window.

  4. Boot the target machine and select the version of Windows NT for which you enabled kernel debugging. To have WinDbg automatically stop the system at initial load time, you can set the Initial Breakpoint option in the Options dialog.

  5. To break into WinDbg, press CTRL+C on the host machine, or press SYSRQ on the target machine. To continue execution, select Go from the Run menu.


4.1.2 Using a Serial Cable for Kernel Debugging

The hardware requirements of a cable for kernel debugging require only to connect RXD to TXD, TXD to RXD, and GND to GND. The following table lists the pin numbers of these signals for 9-pin and 25-pin connectors.
Signal | Pin numbers for 9-pin connector | Pin numbers for 25-pin connector
RXD (Receive Data) | 2 | 3
TXD (Transmit Data) | 3 | 2
GND (Signal Ground) | 5 | 7

Once a connection has been made using a cable as described above, the connection should then be tested using an available serial communications package (e.g. terminal.exe). The communications application should be started on both the target and the host. Type a few keystrokes from both machines to make sure that the data is received on both ends.


4.1.3 Using the Free and Checked Builds of Windows NT

The DDK provides two versions of the Windows NT operating system, called the free build and the checked build. Getting Started explains how to install each version.

The free build of Windows NT is the retail version that end users buy. Free binaries are built with full optimization and contain a minimal set of debugging symbols, such as function entry points and global variables.

The checked build is shipped only with the DDK; it is used in debugging drivers and other system code. Checked binaries provide error checking, argument verification, and system debugging code not present in the free binaries.

Much of the additional code in the checked binaries is in the form of ASSERT (see Section 4.3.3.1) macros that test an expression. If the expression evaluates to FALSE, the macro generates a kernel debugger error message and breaks into the debugger. This lets you immediately determine the cause and location of the error. For example, recursive acquisition of a spin lock (where a thread tries to acquire a spin lock that it already owns) hangs the system in the free build of the operating system, but generates a breakpoint on the checked build of the operating system.

Use the free and checked builds of Windows NT as follows:

  • Use the free build on the host machine, running WinDbg.

  • Use the free build to develop and build a driver. Performance suffers in the checked build due to the additional code that is executed.

  • Use the checked build on the target machine to test and debug a driver during the initial phases of its development.

  • Use the free build on the target machine to test and debug your driver during the final phases of its development. The final testing of your driver must be done on a free version of the driver running the free build of Windows NT.

The extra protection of the checked build of Windows NT carries some penalties. Because of the additional error checking code and debugging information, the checked binaries are larger and run slower than the free binaries. This can conceal synchronization or other timing-related problems, such as race conditions, that become apparent only in the free build.

If you run the checked build of Windows NT without having enabled kernel debugging (refer to Section 4.1.4), unexpected system shutdowns can occur. This is because the additional checks in the checked build increase the likelihood of encountering a breakpoint. A kernel-mode breakpoint with kernel debugging disabled is an unhandled kernel-mode exception, so the system calls the KeBugCheckEx function and shuts down.

However, if kernel debugging is enabled, a breakpoint freezes all threads in the target system except for the component that communicates with WinDbg. This lets WinDbg connect to a running target machine that has kernel debugging enabled, even after a breakpoint has been encountered. See Section 4.1.9 for details.


4.1.4 Enabling Kernel Debugging on the Target

The following information is for enabling the debugging component on the target machine so that the host debugger can communicate with the target. For information on starting the host debugger, see Section 4.1.6.

You must enable kernel debugging on the target machine before you can connect to it with WinDbg.

The method of enabling kernel debugging on the target machine is different for the different platforms supported by Windows NT. For an ARC machine (RISC-based platforms), you modify the OSLOADOPTIONS environment variable in the ARC firmware; for an x86-based machine, you modify a line in the [operating systems] section of boot.ini.

In the appropriate section for the target platform as outlined in the preceeding section, you can specify the following options:

DEBUG
Enables kernel debugging.
NODEBUG
Disables kernel debugging. If no options are specified, NODEBUG is implied. NODEBUG disables DEBUGPORT, BAUDRATE, and CRASHDEBUG.
DEBUGPORT=Port
Specifies the serial port (Port is COM1, COM2, and so forth.) used on the target machine. This parameter is the functional equivalent of DEBUG with the additional ability to override the default debug COM port. The default port without using DEBUGPORT is COM2, if it exists, on an x86 machine; in all other cases the default is COM1.
BAUDRATE=BaudRate
Specifies the baud rate (BaudRate is 9600, 19200, etc.) used by the target machine. Use the highest rate that works for your machines.

Specifing BAUDRATE enables kernel debugging and renders DEBUG redundant.

CRASHDEBUG
Causes the debugger to activate only when the system bugchecks.
MAXMEM=SizeInMB
Specifies the amount of RAM to be made available to the system in megabytes. This option can be used to simulate a system with a low amount of physical memory to test the performance and operation of a driver under those conditions. For example, you can restrict a 16 MB machine to 10 MB.
SOS
Causes the OS loader to display the names of the drivers as they load when Windows NT is booting.

For an RISC based machine, the following OSLOADOPTIONS would boot Windows NT in a simulated 10 MB environment, with kernel debugging enabled on COM1, and communicating at 115200 baud.

OSLOADOPTIONS= MAXMEM=10 DEBUGPORT=COM1 BAUDRATE=115200

The following is an equivalent entry, for an Intel x86 platform, in boot.ini.

multi(0)disk(0)rdisk(0)partition(1)\NT="Windows NT" /MAXMEM=10 /DEBUGPORT=COM1 /BAUDRATE=115200


4.1.5 Accessing the Target Machine’s Symbols

WinDbg KD must have access to the symbols for the target machine so that the system can accurately interpret the information on the target machine. The symbols for the Windows NT kernel and drivers are stored in .dbg files; the symbols for your driver are stored in the driver’s .sys file itself, or may have been split off into a .dbg file. For more information on splitting symbols from a driver executable image into a .dbg file see Section 4.1.12.

These symbols must be copied into a specific tree layout on disk in order for WinDbg to access them correctly. The root directory of the symbol tree may be created at any level in the disk drives existing directory tree structure. However, it must specifically be named SYMBOLS. The Symbols directory must then have three subdirectories specifically named DLL, SYS, and EXE. When the layout is finished it will have the following layout:

\Symbols
    \DLL
    \EXE
    \SYS

Symbol files for the system can be found in on the Windows NT system CD-ROM in the \SUPPORT directory. The following files should be copied into the directories specified for each file:

  • ntoskrnl.dbg should be copied into \SYMBOLS\EXE. If this computer is a multiprocessor machine, it will be necessary to copy ntkrnlmp.dbg to ntoskrnl.dbg in the \SYMBOLS\EXE directory.

  • hal.dbg should be copied into \SYMBOLS\DLL. If this computer does not use the standard x86 HAL, then the appropriate HAL symbols for that machine should be copied to hal.dbg in the \SYMBOLS\DLL directory.

  • yourdriver.sys (or yourdriver.dbg, if the symbols have been split off into a separate .dbg file) should be copied into \SYMBOLS\SYS.

  • otherdrivers.sys with which your driver interacts (or otherdrivers.dbg, if the symbols have been split off into separate .dbg files) should be copied into \SYMBOLS\SYS.

Specify the symbol search path with the User DLLs command from the Options menu of WinDbg. If you start WinDbg KD from the command line, you can use the -y command-line option to specify the symbol search path. The path should point to the named Symbols directory. For example, if Symbols was created a subdirectory of C:\Foo, then the path to apply would be C:\Foo\Symbols.


4.1.6 Starting WinDbg as the Kernel Debugger

There are several methods that you can use to start WinDbg as the kernel debugger.

Method 1:
Start WinDbg from the command prompt with the kernel debugging command-line options:

start windbg -k platform port speed

where platform is the target machine type (i386, MIPS, Alpha, PPC), port is the COM port (COM1 ... COMn) to which the serial cable on the host is attached, and speed is the com port speed (9600, 19200, 57600, ...). See Section 4.1.11 for a description of the command-line options.

Method 2:
Start WinDbg from the command prompt with ntoskrnl or ntoskrnl.exe as a command-line option:

start windbg ntoskrnl[.exe]

Use the Kernel Debugger dialog from the Options menu to specify the target platform, com port, and speed.

Method3:
Start WinDbg by double-clicking the WinDbg icon in the Program Manager. Use the Kernel Debugger command from the Options menu to enable kernel debugging and specify the target platform, com port, and speed.

Once you have set up a kernel debugging session with the appropriate parameters, save it as a named workspace with the Save As command from the Program menu. You can then start a kernel debugging session with those parameters by specifying the workspace name on the command line:

start windbg -w workspacename ntoskrnl


4.1.7 Other Kernel Debugging Options

Several other options for kernel debugging are available with the Kernel Debugger dialog box from the Options menu.

  • Check Stop at Initial Breakpoint to set an initial breakpoint and halt the target machine at the earliest possible time.

  • Check Go on Exit to issue a Go command to the target when you exit WinDbg.

  • Use the Cache Size option to specify the size of the cache that WinDbg uses at startup for memory values. Normal operations, such as single-stepping or the G command, invalidate the cache. If WinDbg has frozen the target machine, but hardware on the target can change memory (for example, through shared memory or DMA), disable the cache to see the memory changes. Use the .cache command to examine the current cache settings, specify a new cache size (in KB), or disable the cache (.cache 0). The default cache size is 100 KB.

  • Use the Crash Dump option to specify the name of a crash dump file for WinDbg to debug. See Section 4.1.8 for information on debugging crash dumps.

You can set several options with the Debug dialog box from the Options menu that are useful with WinDbg .

  • Make sure that Alternate Single Stepping is not checked, as this option causes WinDbg to use a single-stepping algorithm that slows down kernel-mode debugging.

  • Turn off Ignore Bad Symbols to receive a popup when WinDbg tries to load a bad symbol file. The popup lets you browse another directory for the correct symbols, to add the new directory to the symbol search path, or ignore any further bad symbol files encountered.

  • Check the Disassembler option Open Window on Demand. If this option is not checked, and there is no source file for a breakpoint, WinDbg will automatically open a Disassembly window and read in a block of machine code from the target to display as the source for the breakpoint. This is a time-consuming operation that slows down kernel debugging.

  • Specify a log file from the Command window to generate a log of events. You can append to or overwrite an existing logfile using the commands .logopen and .logclose.


4.1.8 Debugging a Crash Dump

To configure the target to generate a crash dump, use the System applet in the Control Panel. Click on the Recovery button and then select the option that enables writing debugging information to a file when a STOP occurs. That file is the crash dump file. Its default name is memory.dmp.

Specify the name of the crash dump file in the Kernel Debugger Options dialog box or use the -z command-line option to specify the name of the crash dump file.

Two utilities, DUMPCHK and DUMPREF shipped with the DDK are useful in debugging crash dumps:

DUMPCHK [options] CrashDumpFile

DUMPCHK is used to check the validity of a crash dump file.

Parameters
Options
The following are valid program options:
-?
Displays a help message. This overrides any other option specified.
-v
Verbose mode.
-p
Print header of crash dump file only, do not validate the file.
-q
Perform a quick test only.
CrashDumpFile
The name of the crash dump file.

DUMPREF [options] SourceDumpFile ShareName SymbolPath

DUMPREF is used to create a reference file to point to a dump check file in another location.

Parameters
Options
The following are valid program options:
-?
Displays a help message. This overrides any other option specified.
-d FileName
Specifies a file name for the destination.
SourceDumpFile
The name of the crash dump file.
ShareName
The name of the network share where the crash dump file is located.
SymbolPath
The path to the symbols for the crash dump file.
Comments

This utility creates a small reference file that points to the actual crash dump file. You can use the reference file as though it were the crash dump file; WinDbg KD will find the actual crash dump file out on the network. This lets you send the reference file, which is only a few bytes long, to someone for debugging, rather than sending the actual crash dump file, which may be many megabytes long.


4.1.9 Connecting to a Running Target

WinDbg can be connected to a target machine that has already booted. This is especially useful when an error or exception causes the target to crash. Connect to a running target as follows:

  1. Start WinDbg on the host.

  2. WinDbg will either connect immediately or wait to connect, depending upon what has happened on the target. If WinDbg KD is waiting to connect, press CTRL+C on the host or SYSRQ on the target.

  3. Reload the symbols for the target by running !reload in the Command window of WinDbg on the host.


4.1.10 WinDbg as a remote.exe Server

Use remote.exe to access the Command window of WinDbg across a network. This allows an establishment of a kernel debugging session between a host and target, which must be connected by a serial cable. This allows for the kernel debugging from a remote machine that is connected to the host through a named pipe interface. Using this method, you can only use WinDbg’s Command-window interface on the remote machine. Input and output normally permitted on the hosts Command window can be used by the remotely connected machine.

Set up a remote.exe session as follows:

  1. In the Command window of WinDbg on the host machine run

    remote pipename

    where pipename is the name you choose for the pipe.

  2. In a Windows NT command prompt on the remote machine, run

    remote /c hostname pipename

    where hostname is the machine name of the host that is running WinDbg.

You now have access from the remote machine to the host’s Command window.

The syntax for the remote command in the host’s Command window is:

remote [pipename | STOP]

Parameters:

pipename
The name that you want to use for the remote server pipe.
STOP
Ends a currently active remote server.

If you do not specify any parameters to remote, the command displays its connection status and the name of the client.


4.1.11 WinDbg Command-Line Options

From the command line the following options are available:

windbg [-a] [-g] [-h] [-i] [-k [platform, port, speed]] [-l[text]] [-m] [-p id [-e event]] [-s[pipe]] [-v] [-w name ] [-y path] [-z crashfile] [filename[.ext] [arguments]]
Options | Explanation
-a | Ignore all bad symbols (but still print warning message).
-g | Go now; start executing the process.
-h | Causes child processes to inherit access to WinDbg’s handles.
-I | Ignore workspace; like running without any registry data.
-k [platform port speed] | Run as a kernel debugger with the specified options: platform is the target machine type (i386, MIPS, Alpha) port is the com port (com1 ... comn) speed is the com port speed (9600, 19200, 57600, ...)
-l [text] | Sets the window title for WinDbg.
-m | Start WinDbg minimized.
-p id | Attach to the process with the given id.
-s [pipe] | Start a remote.exe server, using the named pipe in pipe..
-v | Verbose option; WinDbg prints module load and unload messages.
-w name | Load the named workspace.
-y path | Search for symbols along the specified path(s). You can specify multiple paths by separating them with a semicolon.
-z crashfile | Debug the specified crash dump file.
filename[.ext] | Program to debug or file to edit.


4.1.12 Splitting Symbols from Kernel Drivers

This explains how to remove symbolic debugging information from drivers into a .dbg file. This reduces the amount of disk space that the driver consumes as well as creating a separate file that contains the debugging information.

To create a .dbg file from a driver created using this DDK, do the following:

  1. Obtain the base address, in the optional headers information, of the driver by using dumpbin.exe:
DUMPBIN /headers driver

Where driver is the name of the driver image to obtain information for.

  1. With the address obtained, use rebase.exe, to remove the symbolic debugging information into a separate file:
REBASE -b address -x path driver

Where address is the base address obtained for the driver and path is the path

When this has been done a new .sys file will have been created for the driver that does not contain the symbolic debugging information used by the debugger and a .dbg file will have be created that contains the information previous found in the original driver .sys file.


4.2 Debugging User-Mode Drivers

This gives an overview of how to set up a remote debugging session for user-mode drivers with WinDbg on the host and WinDbgRm on the target. The following material is covered in this section:
Information | Section
Setting up a user-mode driver debugging session | 4.2.1
Serial cable hardware requirements | 4.2.2
Enabling user-mode driver debugging on the target system | 4.2.3
Starting WinDbgRm | 4.2.4


4.2.1 Overview of User-Mode Debugging

To set up a remote debugging session, do the following:

  1. To debug across a serial cable, connect the cable to both the target and host.

  2. On the target machine:

    • If running the free build of Windows NT, make sure the CSR-disable bit is not set. For more information on this bit please see Section 4.2.3.

    • Start WinDbgRm.

    • If you are debugging across a serial cable, select the serial transport layer for WinDbgRm that provides the fastest baud rate that the target and host can handle.

  3. On the host machine:

    • Start WinDbg.

    • Check the Disconnect on Exit option in the Debug dialog from the Options menu.

    • Change the transport layer to match the one used by WinDbgRm on the target.

    • Copy your driver and source files to the host. Use the User DLLs dialog from the Options menu to specify the symbol search path to your driver; use the Debug dialog from the Options menu to specify the source search path to your source files.

    • Select the Attach command from the Run menu.

    • Choose the CSR process (csrss.exe) from the task list.

    • Select Go from the Run menu or press F5.


4.2.2 Serial Cable for Debugging User-Mode Drivers

Hardware protocol is used by WinDbg and WinDbgRm to communicate with each other across a serial cable. Hardware protocol requires a cable that connects RXD to TXD, TXD to RXD, DTR to DTR, GND to GND, and DSR to DSR. In addition, each side of the cable should connect DTR to DSR and RTS to CTS. The following table lists the pin numbers of these signals for 9-pin and 25-pin connectors.
Signal | 9-pin Pin # | 25-pin Pin #
RXD (Receive Data) | 2 | 3
TXD (Transmit Data) | 3 | 2
DTR (Data Terminal Ready) | 4 | 20
GND (Signal Ground) | 5 | 7
DSR (Data Set Ready) | 6 | 6
RTS (Request to Send) | 7 | 4
CTS (Clear to Send) | 8 | 5

The connections are a superset of the connections necessary for a kernel debugging cable. You can use this cable for kernel debugging.


4.2.3 The Free Build and the CSR-disable Bit

4.2.3 Free Build and the CSR-Disable Bit

When a free build of Windows NT is installed, there is a bit in the registry that if set, prevents debugging the Windows subsystem (csrss.exe). Before you can debug user-mode drivers that are a part of this subsystem, you must clear this bit and then reboot the machine. This bit only needs to be disabled on the target machine. To clear the bit, use the registry editor to edit the following registry key:

HKEY_LOCAL_MACHINE
    \SYSTEM
        \CurrentControlSet
            \Control
                \Session Manager

Edit the GlobalFlag value entry (REG_DWORD) to clear the bit FLG_ENABLE_CSRDEBUG (0x20000).

When a checked build of Windows NT is installed, the CSR-disable bit is cleared, which lets you debug the Windows subsystem.


4.2.4 Starting WinDbgRm on the Target

WinDbgRm starts minimized as an icon. To change its transport layer from the default of communicating across the network through a named pipe, double-click on the icon and select the Transport DLL command from the Options menu. Choose the appropriate transport layer in the Known Transport Layers list box.


4.3 Building Drivers for Debugging

This section explains how to write and build a driver so that it provides debugging information to WinDbg. It includes the following topics:

  • How to use the DDK supplied checked and free build environments to control the compiler and linker options and definitions that the build utility uses to build a driver.

  • How to use debugging functions in your driver code to insert breakpoints and print messages to WinDbg.

  • How to use the debugging macros and print mechanisms provided in the driver samples.


4.3.1 Compiler and Linker Options

The DDK’s BUILD utility uses the settings created by the environments represented by the two build environment icons, the Checked Build Environment and the Free Build Environment, created during the installation process to provide a simple mechanism that can be customized for setting up the build environment. These environments set various environment variables that control the compiler and linker options and definitions used when the BUILD utility uses the makefile.def file. These include the command-line switches that determine the level of debugging symbols stored in the executable files, and the compiler-specific definitions that cause optional sections of code to be included or excluded.

Using the Checked Build Environment sets up the build environment to produce a debugging version of the driver that corresponds to the checked build of Windows NT. Similarly, using the Free Build Environment results in a streamlined version of the driver that corresponds to the free build of Windows NT. When you are building a driver for debugging, use the Checked Build Environment to build a driver with the appropriate symbolic debugging information.

The specific compiler and linker options that are enabled for checked and free builds vary depending on the platform for which you are building the driver. By default the Checked Build Environment will build the driver with CodeView symbolic information which will allow for source level debugging of the device driver. In contrast, the Free Build Environment only enables COFF symbolic debugging information providing minimal symbolic debugging information.


4.3.2 Debugging Functions

The DDK’s master kernel-mode include file, ntddk.h, defines a pair of functions that are useful in debugging kernel-mode drivers. The DbgBreakPoint function inserts a breakpoint, and the DbgPrint function prints a formatted string to WinDbg . For debugging Win32 user-mode drivers, the Win32 API provides two similar functions, DebugBreak and OutputDebugString, which print to WinDbg.


4.3.2.1 Print Functions

DbgPrint and OutputDebugString display output to the host debugger Command window.

ULONG DbgPrint(PCH pchFormat, ...);

DbgPrint writes a string to the WinDbg KD Command window. The parameters of DbgPrint are the same as those used by the C runtime function printf. Only kernel-mode drivers can call DbgPrint. The ntddk.h file defines a KdPrint macro that is equivalent to DbgPrint, except that it is a no-op in free builds and outputs a string only in checked builds.

VOID OutputDebugString(LPCTSTR lpszOutputString);

OutputDebugString sends a string to the debugger of the calling process. When debugging a user-mode driver, OutputDebugString displays the string in the WinDbg Command window. OutputDebugString does not support the variable arguments of a printf formatted string.


4.3.2.2 Breakpoint Functions

There are several ways to insert a breakpoint into the source code of a driver. The DbgBreakPoint function works in user-mode or kernel-mode code, while the DebugBreak function works only in user-mode code.

The function prototypes for DbgBreakPoint and DebugBreak are as follows:

VOID DbgBreakPoint(VOID);
VOID DebugBreak(VOID);

Each of these breakpoint functions breaks into the debugger associated with the calling process.

Calling a breakpoint function on a system that is not connected to a debugger hangs the machine. You can, however, connect WinDbg to a hung target machine that has kernel debugging enabled. Refer to Section 4.1.9, Connecting to a Running Target, for details.


4.3.3 Debugging Macros

This section describes the ASSERT and PRINT macros provided with the DDK.

The ntddk.h file defines a pair of macros that provide additional debugging information when used in a checked version of a kernel-mode driver. The ASSERT and ASSERTMSG macros are defined as no-ops if the DBG compiler variable is defined as 0 (free build).


4.3.3.1 ASSERT Macros

The format of an ASSERT macro is as follows.

ASSERT (Expression);

If Expression evaluates to FALSE, the macro displays a message in the WinDbg KD Command window. The message displays the source-code string of the expression that evaluated to FALSE, as well as the path of the source-code file and the line number in that file of the instruction that called the macro. You can then choose to break into the debugger, ignore the ASSERT, or terminate the process or thread in which the ASSERT occurred. If you break into the debugger, you can trace the stack frames, dump or edit memory, and so on. If you choose to ignore the ASSERT, execution continues as if you had entered the g command.

In a free builds of the driver, the ASSERT macro is not compiled.


4.3.3.2 ASSERTMSG Macro

ASSERTMSG (Message, Expression);

This macro is identical to the ASSERT macro, except that Message is displayed in addition to the expression, path, and line number information.


4.3.3.3 Sample Print Macros

In addition to the ASSERT macros, a number of the sample kernel-mode drivers define a print macro that uses a global debugging-level variable to control the level of interaction with WinDbg. The macros differ for different drivers, but they have a number of features in common:

  • As with the ASSERT macros, these macros are conditionally compiled.

  • Each macro’s arguments include the message to print and a level parameter that indicates the severity of the error.

  • The level parameter and the global debugging level determine the action taken by the macro. The actions range from doing nothing, to printing the message string, to executing an ASSERT macro.

For example, the ATDISK driver defines an AtDebugLevel global variable whose default value is 0, and it defines an AtDump macro as follows:

#if DBG
extern ULONG AtDebugLevel;
#define ATBUGCHECK      ((ULONG)0x80000000)
#define ATDIAG1       ((ULONG)0x00000001)
#define ATDIAG2       ((ULONG)0x00000002)
#define ATERRORS       ((ULONG)0x00000004)
#define ATINIT        ((ULONG)0x00000008)
#define AtDump(LEVEL,STRING) \
  do { \
    ULONG _level = (LEVEL); \
    if (AtDebugLevel & _level) { \
      DbgPrint STRING; \
    } \
    if (_level == ATBUGCHECK) { \
      ASSERT(FALSE); \
    } \
  } while (0)
#else
#define AtDump(LEVEL,STRING) do {NOTHING;} while (0)
#endif

Using this macro, you could insert statements like the following into your code:

if (SomeVariable == -1)
AtDump(ATERRORS, ("Bad value for SomeVariable") );

If AtDebugLevel is 0, the message is not displayed. To enable the display of certain levels of error messages, you can assign a hard-coded value to AtDebugLevel and recompile. To set the variable at run time, you can use the ed command of WinDbg to modify the value of AtDebugLevel or have the driver obtain the debugging-level from a registry parameter for the driver.


4.4 Debugger Extensions

The Win32 SDK and the WinDbg online help file provide a complete description of the syntax for WinDbg’s commands, including kernel debugging commands. There are also built-in kernel debugging extensions that are not described in the SDK documentation. In addition, you can create your own extensions to the kernel debugging command set. Use the !? command at the WinDbg prompt for a listing of the built-in kernel debugging commands and built-in command extensions.


4.4.1 Built in Kernel Debugging Extensions

WinDbg provides a set of debugging extensions for kernel-mode driver debugging. These extensions allow the user to obtain additional extended information specific to kernel-mode debugging not available through the standard WinDbg commands.

The command-line syntax for a built-in extension is:

!extension_name [arguments]

!cxr

!cxr address

!cxr displays the context record at the specified address.

Parameters
address
Address of the system context record.
Platforms

Power PC, Alpha, MIPS, x86

Comments

The information from a context record can be used to assist in debugging a system halt where an unhandled exception has occurred and an exact stack trace is not available.

!db

!db [PhysicalAddress]

!db displays a hexadecimal and ASCII dump of 128 bytes from the specified address on the target machine.

Parameters
PhysicalAddress
This parameter is required on first usage. On subsequent usgae, the parameter is optional. If this parameter is not specified, !db will display the next 128 bytes starting at the first byte following the end of the previous !db command.
Platforms

Power PC, Alpha, MIPS, x86

!dd

!dd [PhysicalAddress]

!dd displays a hexadecimal dump of 32 ULONG values from the specified address on the target machine.

Parameters
PhysicalAddress
This parameter is required on first usage. On subsequent usage, the parameter is optional. If the parameter is not specified, !dd will display the next 32 ULONG values starting at the first byte following the end of the previous !dd command.
Platforms

Power PC, Alpha, MIPS, x86

!default

!default DLLName

!default changes the default extension DLL for WinDbg.

Parameters
DLLName
The name and path of the extension DLL..
Platforms

Power PC, Alpha, MIPS, x86

Comments

WinDbg maintains a default extension DLL which is implicity loaded when WinDbg loads. This allows the user to specify an extension command without first having to load a extension DLL. This command allows modification of which DLL is loaded as the default DLL.

!devobj

!devobj address

!devobj displays detailed information about a DEVICE_OBJECT.

Parameters
Address
The virtual address of a DEVICE_OBJECT.
Platforms

Power PC, Alpha, MIPS, x86

!drvobj

!drvobj address

!drvobj displays detailed information about a DRIVER_OBJECT.

Parameters
Address
The virtual address of a DRIVER_OBJECT.
Platforms

Power PC, Alpha, MIPS, x86

!drivers

!drivers

!drivers displays memory usage information and a list of each driver loaded on the target machine.

Platforms

Power PC, Alpha, MIPS, x86

!ed

!ed PhysicalAddress Data0 [Data1 ... DataN]

!ed writes a sequence of ULONG values into a specified physical address.

Parameters
PhysicalAddress
The physical address on the target machine that the data will be written to.
Data0
A ULONG value to be written into physical memory.
Data1 ... DataN
These parameters are optional. If specified, the values will be written in sequence into the physical address on the target machine specified by the parameter PhysicalAddress.
Platforms

Power PC, Alpha, MIPS, x86

!errlog

!errlog

!errlog displays the contents of any pending error log entries.

Platforms

Power PC, Alpha, MIPS, x86

Comments

This command will only display entries that were queued by IoWriteErrorLogEntry but have not been committed to the error log. This command can be used as a diagnostic aid to see pending error information which was unable to be committed to the error log before the system halted.

!exr

!exr Address

!exr displays the contents of an exception record.

Parameters
Address
The virtual address of an exception record.
Platforms

Power PC, Alpha, MIPS, x86

Comments

This command will display information related to an exception encountered on the target machine. The information displayed will include the exception address, exception code, exception flags, and a variable list of parameters to the exception.

!frag

!frag Flags

!frag displays fragmentation information about pool memory on the target system.

Parameters
Flags
The Flags parameter is used to control the detail level of the output from this command. The parameter can be one of the three following values:
    0  Minimum detail.
    1  Moderate detail
    2  Maximum detail.

Platforms

Power PC, Alpha, MIPS, x86

!filecache

!filecache

!filecache displays information regarding the system file cache memory and PTE usage.

Platforms

Power PC, Alpha, MIPS, x86

!handle

!handle [Handle] [Flags] [Process] [TypeName]

!handle displays information about a handle or handles owned by one or all processes in the target system.

Parameters
Handle
Specifies a handle value for which information should be displayed. This parameter is optional. However, if a value is specified for this parameter, then a value must be specified for Process.

Flags

Process
Specifies a process ID for a currently running process on the target system. This parameter is optional.

TypeName

Platforms

Power PC, Alpha, MIPS, x86

Comments

This command can be used to display all handles for a specified process. In order to do this, a value of 0xFFFFFFFF is required for Handle with a valid process ID specified for Process.

!heap

!heap [Address] [Detail]

!heap displays heap usage information in varying levels of detail for the current process.

Parameters

Address

Detail
The detail parameter is used to control the level of output that is displayed by this command. This parameter is optional. However, when specified the following values are valid:
    0 (default)    Summary information about the heap
    1        Level 0 information with the size and location
             all the committed and uncommitted regions.
    3        Level 1 information with the allocated and free
             blocks contained in each committed region
    4        Level 3 information with a dump of the free lists
             in the heap.

!irp

!irp Address [Verbose]

!irp displays information about an IRP.

Parameters
Verbose
If a value is specified for Verbose then a more detailed output of the IRP will be given.
Platforms

Power PC, Alpha, MIPS, x86

!irpfind

!irpfind [Options]

!irpfind displays information about all IRPs currently allocated in the target system.

Parameters
Options
If a nonzero value is specified for Options then a more detailed output of the IRPs allocated in the target system is displayed.
Platforms

Power PC, Alpha, MIPS, x86

!irpzone

!irpzone is an obsolete debugger extension command and debugger users should instead see !irpfind.

!kb

!kb

!kb displays a stack trace which is obtained from a trap frame. This command must be used after a !trap so that !kb can use the loaded trap frame information to find the call stack.

Platforms

Power PC, Alpha, MIPS, x86

!load

!load [DllName]

!load is used to load a new extension DLL into the debugger. Once this command has been completed, it allows the user to access the commands stored in the loaded extension.

Parameters
DllName
Specifies the fully qualified path and filename of the DLL to be loaded as a new extension.
Platforms

Power PC, Alpha,. MIPS, x86

!locks

!locks [Options] [Address]

!locks displays information about kernel ERESOURCE locks.

Parameters
Options
This parameter is optional. The following options can be specified to control the information output.
-V
Maximum output about each lock should be displayed.
-P
Performance lock information should be displayed
Address
This parameter is optional. Displays information about the ERESOURCE lock at the specified address. If Address is not specified, then information about all ERESOURCE locks in the system will be displayed.
Platforms

Power PC, Alpha, MIPS, x86

!lookaside

!lookaside [Address] [Options] [Depth]

!lookaside displays information or modifies information of a lookaside list on the target system.

Parameters
Address
Specifies an address for a lookaside list to be used. This parameter is optional. If Address is not specified, or if it is 0, then the system lookaside lists will be displayed.
Options
This parameter is used to control what operation will be taken with the lookaside list which was specified. This parameter is optional, however if it is specified then the following options are available:
    0 (default)    Displays information about the lookaside list
    1              Resets the counters of the lookaside list
    2              Modifies the depth of the lookaside list

Depth
This parameter is only used if a value of 2 is specified for Options. The value will be the new depth value set in the specified lookaside list.
Platforms

Power PC, Alpha, MIPS, x86

Comments

Options must be 0 or 1 if this command is being used to look or modify the system lookaside lists. If a 1 is specified, then it will reset the counters for all of the system lookaside lists which are displayed as part of this command.

!lpc

!lpc

!lpc displays information about all LPC ports and messages in the target system.

Platforms

Power PC, Alpha, MIPS, x86

!memusage

!memusage

!memusage displays a dump of the page frame database table from the Memory Manager.

Platforms

Power PC, Alpha, MIPS, x86

!noversion

!noversion

!noversion causes WinDbg to disable all version checking for extension DLLs.

Platforms

Power PC, Alpha, MIPS, x86

!object

!object [Address] [Name]

!object displays information about a system object.

Parameters
Address
Specifies the hexadecimal address of a valid system object to display information about.
Name
This parameter is optional. Specifies the name of a class of system object for which to display all instances. If specified, Address must be zero.
Comments

!object can also be used with to display information about the root directory holding all objects. If Address is specified to be the character ‘\’, then information about the root directory object will be displayed.

Platforms

Power PC, Alpha, MIPS, x86

!pcr

!pcr [Processor]

!pcr displays the current status of the PCR register on a specific processor.

Parameters
Processor
This parameter is optional. If specified, the value indicates the processor number to retrieve the PCR information from. If this value is not specified, the current processor will be used.
Platforms

Power PC, Alpha, MIPS, x86

!pfn

!pfn [Number] [Flags]

!pfn displays information about a specific page frame or the whole page frame database.

Parameters
Number
The hexadecimal number of the page frame to be displayed.
Flags
This parameter is optional. If specified as a nonzero value, it will cause !pfn to display the entire page frame table database.
Platforms

Power PC, Alpha, MIPS, x86

!pool

!pool [Address] [Flags]

!pool displays information about a specific pool allocation or can display information about the system wide pool.

Parameters
Address
Specifies a pool entry to display information about. If this is value is -1, it will information about the entire pool.
Flags
This parameter is optional. The following values are valid flag options.
0
Only summary information should be displayed.
1
Level 0 information with the addition of information about the location or size of regions in the block.
3
Level 1 information with the addition of information about the allocated and free blocks of memory in the committed regions.
4
Level 3 information with a display of the free list.
Platforms

Power PC, Alpha, MIPS, x86

!poolfind

!poolfind [Tag] [PoolType]

!poolfind is used to find all instances of a specific pool tag in either nonpaged or paged memory pools.

Parameters
Tag
Specifies the four-byte tag, used in the ExAllocateXxx family of functions, to find.
PoolType
This parameter is optional. If not specified or if zero, nonpaged pool memory is searched. If nonzero, paged pool memory is searched.
Comments

This command can take a significant amount of time to execute depending on the size of pool memory which must be searched.

Platforms

Power PC, Alpha, MIPS, x86

!poolused

!poolused [Flags]

!poolused displays memory usage summaries based on the tag used for each pool allocation.

Parameters
Flags
This parameter is optional. The following values are valid flags:
0
Sort by tag, summary level of information only. This is the default behavior if Flags is not specified.
1
Sort by tag, display verbose level of information.
2
Sort by nonpaged memory usage, display summary level of information.
3
Sort by nonpaged memory usage, display verbose level of information.
4
Sort by paged memory usage, display summary level of information.
5
Sort by paged memory usage, display verbose level of information.
Platforms

Power PC, Alpha, MIPS, x86

!process

!process [Address]

!process displays summary information about a process.

Parameters
Address
The hexadecimal address of the process on the target machine.
Platforms

Power PC, Alpha, MIPS, x86

!ready

!ready

!ready displays summary information about each thread in the system in a READY state.

Platforms

Power PC, Alpha, MIPS, x86

!reload

!reload [Module]

!reload reloads the symbols for a specified module.

Parameters
Module
Specifies the name of an image on the target system to reload symbols for on the host machine. If this parameter is specified to be the character ‘*’, all symbols will be reloaded.
Platforms

Power PC, Alpha, MIPS, x86

!srb

!srb [Address]

!srb displays information about an SRB request.

Parameters
Address
Specifies the hexadecimal address of the SRB on the target machine.
Platforms

Power PC, Alpha, MIPS, x86

!sympath

!sympath [NewPath]

!sympath changes the default path of the host debugger for symbol search.

Parameters
NewPath
The fully qualified path, on the host machine, of a new symbol search path.
Platforms

Power PC, Alpha, MIPS, x86

!thread

!thread [Address]

!thread displays summary information about a thread on the target system.

Parameters
Address
Specifies the hexadecimal address of the thread on the target machine.
Platforms

Power PC, Alpha, MIPS, x86

!trap

!trap [Address]

!trap displays summary information about a trap frame.

Parameters
Address
The hexadecimal address of the trap frame on the target system.
Platforms

Power PC, Alpha, MIPS, x86

!unload

!unload

!unload unloads the current extension DLL from the debugger on the host system.

Platforms

Power PC, Alpha, MIPS, x86

!vad

!vad [Address] [Flags]

!vad displays information about virtual address(s) on the target machine.

Parameters
Address
A virtual address on the target machine. This parameter must be a valid virtual address. However, if the Flags parameter is specified, this field will be ignored, but must remain valid.
Flags
If nonzero, displays information about only this virtual address. Otherwise, information about all virtual addresses will be displayed.
Platforms

Power PC, Alpha, MIPS, x86

!vm

!vm

!vm displays summary information about memory usage statistics on the target system.

Platforms

Power PC, Alpha, MIPS, x86


4.4.2 Creating Custom Extensions

You can create your own debugging commands by writing an extension DLL. You might want to write a command, for example, to dump a complex data structure.

To run a command called foo, which is an exported function from mydll.dll, enter the following at the WinDbg command prompt:

!mydll.foo [args]

WinDbg will load mydll.dll, call the entry point foo, and pass args to foo. Once WinDbg has loaded mydll.dll, you can run an extension command contained in it by using just the command's name:

!foo [args]

You can explicitly load an extension DLL with either of the following commands:

!mydll.foo
!load mydll.dll

There can be as many as 32 extension DLLs loaded, including the default extension DLL.

When you run !foo, WinDbg looks for foo in the current extension DLL, which is the last one loaded or the one that you specify with

!default mydll

If it does not find foo in the current extension DLL, WinDbg searches through the loaded extension DLLs, in the order that they were loaded, and runs the first instance of foo that it finds.

You can unload the current extension DLL with the following command:

!unload

The DDK provides source code for a few simple debugger extension samples in the src\krnldbg\kdexts directory of the DDK tree.

The following are requirements for a user-defined DLL:

  1. Extension command names must be lowercase.

  2. You must export a function named WinDbgExtensionDllInit (refer to the source for dbgexts.c, below). When WinDbg loads an extension DLL, it first calls WinDbgExtensionDllInit and passes it the following arguments:

    Pointer to an extension API structure

    This structure contains the callbacks to functions that you can use to do standard operations. For example, to print a string you can use dprintf(“string”). Include the header file wdbgexts.h in your extension source and use the #define function definitions from that file. The file wdbgexts.h also defines the parameters to the functions.

    Save the pointer in a global variable named ExtensionApis.

    Major version

    This indicates whether the target system is running a checked build of Windows NT (0x0c) or a free build (0x0f).

    Minor version

    This is the Windows NT build number of the target system (for example, 1381).

  3. You must export a function called ExtensionApiVersion. WinDbg calls this function and expects back a pointer to API_VERSION. The version number of your extension DLL must match the version number of WinDbg. If the numbers don't match, WinDbg will not load the extension DLL.

  4. You can optionally export a function called CheckVersion. WinDbg calls this function every time you use your DLL. Use it to verify that the version of your DLL matches the target system. To disable version checking, issue the !noversion command at the WinDbg prompt.

  5. Use the DECLARE_API macro defined in wdbgexts.h to declare your command functions. The basic format is:

    DECLARE_API(foo)
{
code for foo
}

WinDbg does a try/except around a call to an extension DLL. Even though they won't crash WinDbg, bugs in your code can still cause it not to work properly, and you will have to quit and restart WinDbg.


4.5 Debugging Strategies

While each problem in a device driver is different in the mechanism necessary to identify the exact problem, there are several initial debugging procedures that can be used to find a starting point in identifying the problem. These techniques include using system bug check information, obtaining stack trace information, and checking hang conditions.

Interpreting System Bug Check Information

When Windows NT encounters a condition that compromises safe system operation, the system halts and displays a blue character-mode screen. This screen is commonly referred to as the “blue screen” or STOP screen. Windows NT attempts to display as much information as possible about the current state of the machine when it encountered the error. If crash dumps were enabled on the system, a crash dump file is created that can be used at a later time for more detailed debugging. If a debugger is attached and active, the system causes a breakpoint so the debugger can be used to investigate the crash.

The following example of a STOP screen contains several sections of information that will be discussed in detail. The sections are the STOP information at the top of the screen, information about the loaded modules in the system, basic stack trace information, and information regarding the status of crash dumps and kernel debugger activity.

*** STOP: 0x0000001E (0x80000003,0x80106fc0,0x8025ea21,0xfd6829e8)
Unhandled Kernel exception c0000047 from fa8418b4 (8025ea21,fd6829e8)

Dll Base  Date Stamp - Name               Dll Base  Date Stamp - Name
80100000  2be154c9   - ntoskrnl.exe       80400000  2bc153b0   - hal.dll
80258000  2bd49628   - ncrc710.sys        8025c000  2bd49688   - SCSIPORT.SYS
80267000  2bd49683   - scsidisk.sys       802a6000  2bd496b9   - Fastfat.sys
fa800000  2bd49666   - Floppy.SYS         fa810000  2bd496db   - Hpfs_Rec.SYS
fa820000  2bd49676   - Null.SYS           fa830000  2bd4965a   - Beep.SYS
fa840000  2bdaab00   - i8042prt.SYS       fa850000  2bd5a020   - SERMOUSE.SYS
fa860000  2bd4966f   - kbdclass.SYS       fa870000  2bd49671   - MOUCLASS.SYS
fa880000  2bd9c0be   - Videoprt.SYS       fa890000  2bd49638   - NCR77C22.SYS
fa8a0000  2bd4a4ce   - Vga.SYS            fa8b0000  2bd496d0   - Msfs.SYS
fa8c0000  2bd496c3   - Npfs.SYS           fa8e0000  2bd496c9   - Ntfs.SYS
fa940000  2bd496df   - NDIS.SYS           fa930000  2bd49707   - wdlan.sys
fa970000  2bd49712   - TDI.SYS            fa950000  2bd5a7fb   - nbf.sys
fa980000  2bd72406   - streams.sys        fa9b0000  2bd4975f   - ubnb.sys
fa9c0000  2bd5bfd7   - mcsxns.sys         fa9d0000  2bd4971d   - netbios.sys
fa9e0000  2bd49678   - Parallel.sys       fa9f0000  2bd4969f   - serial.SYS
faa00000  2bd49739   - mup.sys            faa40000  2bd4971f   - SMBTRSUP.SYS
faa10000  2bd6f2a2   - srv.sys            faa50000  2bd4971a   - afd.sys
faa60000  2bd6fd80   - rdr.sys            faaa0000  2bd49735   - bowser.sys

Address  dword dump                                     Dll Base - Name
801afc20 80106fc0 80106fc0 00000000 00000000 80149905 : fa840000 - i8042prt.SYS
801afc24 80149905 80149905 ff8e6b8c 80129c2c ff8e6b94 : 8025c000 - SCSIPORT.SYS
801afc2c 80129c2c 80129c2c ff8e6b94 00000000 ff8e6b94 : 80100000 - ntoskrnl.exe
801afc34 801240f2 80124f02 ff8e6df4 ff8e6f60 ff8e6c58 : 80100000 - ntoskrnl.exe
801afc54 80124f16 80124f16 ff8e6f60 ff8e6c3c 8015ac7e : 80100000 - ntoskrnl.exe
801afc64 8015ac7e 8015ac7e ff8e6df4 ff8e6f60 ff8e6c58 : 80100000 - ntoskrnl.exe
801afc70 80129bda 80129bda 00000000 80088000 80106fc0 : 80100000 - ntoskrnl.exe

Kernel Debugger Using: COM2 (Port 0x2f8, Baud Rate 19200)
Restart and set the recovery options in the system control panel or the
/CRASHDEBUG system start option. If this message reappears, contact
your system administrator or technical support group.

There is a significant amount of information available for identifying the problem that caused the system to halt. This information can be used in conjunction with WinDbg to identify the problem.

At the top of the screen is the bug check code labeled STOP. This code will vary depending on the individual problem that caused the system to halt. The most common bug check codes are:
Bug Check Code | Definition
0x0000000A | IRQL_NOT_LESS_OR_EQUAL
0x0000001E | KMODE_EXCEPTION_NOT_HANDLED
0x0000007F | UNEXPECTED_KERNEL_MODE_TRAP

A bug check code of IRQL_NOT_LESS_OR_EQUAL generally indicates a software failure by a driver or other system component. This code indicates that an attempt was made to execute an operation an improper IRQL. This is generally caused by calling routines that are invalid at the IRQL of the caller, by using invalid addresses to system routines, or by attempting to touch pageable memory at raised IRQL.

A bug check code of KMODE_EXECPTION_NOT_HANDLED indicates that an exception taken in kernel mode was not handled. This bugcheck can happen for any number of different exceptions than can occur. Some of the most common include 0xc0000005 (access violation) and 0x80000003 (a breakpoint was encountered without a kernel debugger being attached to the system).

A bug check code of UNEXPECTED_KERNEL_MODE_TRAP indicates that a software condition too serious to continue has been encountered. Examples include divide by zero, a corrupt task state segment, or a fault occurring while processing a fault (this is known as a double fault condition).

The four values following the STOP message are parameters to KeBugCheckEx, the support routine called when the system must halt. For each bug check code, the parameters will vary. The following is a description of what each parameter means for the most common bug check codes:
Code | Parameter 1 | Parameter 2 | Parameter 3 | Parameter 4
0x0000000A | Memory referenced | IRQL Value | 0 - Read 1 - Write | Address that referenced the memory.
0x0000001E | Exception code | Address where the exception occurred. | Parameter 0 of the exception. | Parameter 1 of the exception.
0x0000007F | Trap code | Not used | Not used | Not used

Following the bug check information is information about each loaded driver or base module in the system. This information consists of the base address where each module is loaded, a hexadecimal representation of the date stamp of the binary image, and the name of the driver or base component. The base address information can be interpreted to determine in what image an address is found. This information is useful in an attempt to determine which driver contains the address where, for example, a faulting instruction was executed.

Following the list of loaded drivers in the system is a brief stack trace. This information indicates what drivers and routines were being executed when the system failed. However, it is important to note that the last calls are not necessarily the cause of the system failure. The last calls on the stack trace can be system routines attempting to handle or process the error condition.

The final layer of information displayed is information regarding kernel debugger and crash dump status. This information indicates if the target side of kernel debugging is now active. If a crash dump file has been generated, that information will be displayed as well.

Debugging a System Bug Check

If a system has bug checked, there are steps to obtain useful information for further debugging. These steps include obtaining the stack trace and getting trap frame information.

When the debugger has connected and the system is accessible from the command window of the host machine, type kv. This command displays a verbose kernel trace from the target machine. The following is a sample trace:

ChildEBP RetAddr  Args to Child
8013ed5c 801263ba 00000000 00000000 e12ab000 NT!_DbgBreakPoint (FPO: [0,0,0])
8013eecc 801389ee 0000000a 00000000 0000001c NT!_KeBugCheckEx+0x194
8013eecc 00000000 0000000a 00000000 0000001c NT!_KiTrap0E+0x256 (FPO: [0,0] TrapFrame @ 8013eee8)
8013ed5c 801263ba 00000000 00000000 e12ab000
8013ef64 00000246 fe551aa1 ff690268 00000002 NT!_KeBugCheckEx+0x194

The information from a kernel trace includes the list of calling routines, the parameters of each call, and, if a routine stored a trap frame, a trap frame address. If a trap frame was stored, the information that caused the trap can be retrieved by using the !trap command. In the preceeding example a trap frame was stored by NT!_KiTrap0E (which is very common) at 8013eee8. When the trap frame has been retrieved, a !kb will generate a stack trace for that trap frame.

Taking a page fault at raised IRQL or at an otherwise inappropriate is a frequent cause of bug check conditions. There are many operations that can cause this to happen including calling support routines at an IRQL level higher than permitted for that routine, using incorrect pointers, or touching pageable memory at a raised IRQL. A page fault condition generally creates a trap frame and a fault situation that will require additional investigation to find the cause of the problem.

A bug check of 0x0000001E (KMODE_EXCEPTION_NOT_HANDLED) often does not display full stack information and, thus, requires additional investigation. See Debugging Unhandled Exceptions.

Debugging Unhandled Exceptions

A bugcheck condition of KMODE_EXCEPTION_NOT_HANDLED might require additional investigation if a full stack trace could not obtained through normal stack trace operations. Use the following procedure to get the necessary information to identify the problem:

  1. Look for the first parameter to NT!PspUnhandledExceptionInSystemThread. Use the kb command, which displays parameters in the stack trace to find this value. For optimized RISC machines which might not have the parameters on the stack, see Manually Obtaining Call Stack Trace Information on RISC Machines for more information on how to obtain this parameter.

  2. The first parameter to NT!PspUnhandledExceptionInSystemThread is a pointer to a structure which contains pointers to an except. A dd command on that address will display the necessary data.

  3. The first value retrieved is an exception record. The exception record can be displayed by using the !exr command. The second value retrieved is a context record that can be displayed by using the !cxr command.

  4. After executing the !cxr command, the !kb command displays a stack trace based on the context record information. This indicates the calling stack when the unhandled exception occurred.

The stack trace information that is returned from the exception record information displays a normal stack trace with parameters that can then be used to debug the problem and find the offending operation.

Manually Obtaining Call Stack Trace Information on RISC Machines

On an optimized version of Windows NT running on a RISC system, such as the free build of Windows NT, parameters might not be stored on the stack. Instead, they can be stored in registers for performance. Attempting to debug a problem on a RISC machine presents unique problems, as a kb command might not reveal the correct parameters passed to each function.

To unwind the stack to find where various parameters are stored, it is necessary to understand of the assembly code for each platform. The movement of the parameters in and out of registers, as well as the values stored in the them, needs to be tracked. For more information on the assembly language for each platform and how that processor architecture handles call stacks and parameters, see the appropriate processor reference from the manufacturer.

Parameters are stored, in first-to-last order, in registers a0 through a3 and, if necessary, registers t0 through t7 on MIPS machines. On Alpha machines, parameters are stored first-to-last in registers a0 to a5 and, if necessary, registers t0 through t7. On Power PC machines, parameters are stored in first-to-last order in registers r3 through r31.

Unassemble the function to a point where the parameter values can be identified in memory or until a function call on the stack is made without the register being saved. This gives you and indication of where each parameter was stored and what its value was. Do this for each module as you trace downward in the system. Some of the parameters can have been stored on the stack before use, making retrieving their values relatively simple.

Checking Hang Conditions

Sometimes a system can hang without breaking into the debugger. The symptoms can vary from no mouse or keyboard response to the video update and no I/O response.

The following is a starting point for debugging a hang condition:

  1. Break into the target system by pressing CTRL+C on the host debugger. This causes the target system to halt as if it had encountered a breakpoint. Use !process to identify the currently running process.

  2. The most useful information is the time values, the handle count, and the thread status information. A high time value can indicate that this is a suspect process. If the current process is idle, it indicates that the machine is either truly idle or in an indeterminate state. If this process does not seem to be the problem, try using !process with its other options to get more information on other processes in the system or with more detail.

  3. After a suspect process has been identified, use !process <process> 7 to show the kernel stacks for each thread in the process. This can indicate what the problem could be in kernel mode and what the suspect process is calling.

To identify other information about a possible hang condition, the following table contains some additional useful commands:
Command | Usage in debugging hang conditions
!ready | Identifies threads in a ready condition in order of priority.
!locks | Identifies any resource locks.
!vm | Displays virtual memory usage.
!poolused | Checks pool allocation. On a checked build with pool tags this can identify excessive allocations.
!memusage | Checks physical memory status.
!heap | Checks the validity of the heap.
!irpfind | Displays any information about pending IRP requests.

To determine if a single process is causing the machine to hang, set a breakpoint at KiSwapContext. If this breakpoint is hit, then the system is scheduling other processes. If the breakpoint is not hit, then a single process is causing the hang.


Appendix A Legacy Driver Installation Methods

This appendix describes driver installation methods for those drivers that use Windows NT 3.x script-based INF files. These drivers include network and multimedia drivers.


A.1 Text Setup

During the text setup phase of Windows NT installation, the Windows NT Setup program installs drivers for the following components: keyboard, mouse, video, SCSI, disk, CD-ROM, machine/HAL. If text setup cannot find a driver for any of these components, it prompts the user to insert a disk containing the driver. The user can also select Other from a list of drivers for a component, and this causes Setup to prompt the user for a disk.

If you are distributing a driver for one of these hardware components, and intend to enable the user to install it during text setup, your distribution disk must include a file named txtsetup.oem. Note that for your distribution disk to be used during the text setup phase, the disk must use the FAT file system. A txtsetup.oem file is a text file containing the following information:

  • Identifies the hardware components supported by this txtsetup.oem file.

  • Lists the files to copy from the distribution disk for this component.

  • Lists the registry keys and values to create for this component.


A.1 txtsetup.oem Files

A.2 txtsetup.oem Files

This section describes the format of a txtsetup.oem file, and includes a sample txtsetup.oem file at the end of the section.

A txtsetup.oem file consists of several sections that use the following general format:

[SectionName]
key = value1,value2,...

The name of the section is enclosed in square brackets, [ ]. The pound sign (#) or semicolon (;) character at the beginning of a line indicates a comment. Strings with embedded spaces, commas, or hashes must be enclosed in double-quotes (“ ”).

The following sections must be included:

Disks Section

The [Disks] section lists all disks in the disk set.

[Disks]
d1 = description,tagfile,directory
d2 = description,tagfile,directory
   .
   .
   .

d1, d2, ...
Key that can be used in subsequent sections to identify the disk.
description
Disk name string used to prompt the user to insert the disk
tagfile
Specifies the name of the file whose presence on the disk indicates to the Setup program that the correct disk has been inserted. The file name should be specified as a full path from the root, for example, \yourtagfile.ext, but it should not specify a drive.
directory
Specifies the directory on the disk where the files are located. The directory should be specified as a full path from the root, for example, \diskdir, but it should not specify a drive.
Defaults Section

The [Defaults] section lists the default option for each hardware component supported by this file (as indicated by the presence of a [component] section for the component). The default is highlighted in the menu of options presented to the user.

[Defaults]
component = ID
    .
    .
    .

component
Specifies one of the following components: computer, display, keyboard, mouse, CD-ROM, or SCSI.
ID
Specifies a string that identifies the default option. This string matches an ID specified in the corresponding [component] section.
Component Section

A [component] section lists the options available for a particular component.

[component]
ID = description
    .
    .
    .

ID
Specifies a unique string (within this section) that identifies the option. For the computer component, if this string ends in “_up”, Setup copies the uniprocessor kernel; if this string ends in “_mp”, Setup copies the multiprocessor kernel; if it ends in neither, the results are unspecified and you might get either kernel.
Files.component.ID Section

A [Files.component.ID] section lists the files that should be copied if the user selects a particular component option. One section of this type must be present for each option listed in each component section. The component portion of the section name corresponds to the name of a [component] section, and the ID portion corresponds to an ID key in a [component] section.

[Files.component.ID]
file_type = source_disk,filename[,keyname]

filetype
Identifies the type of file. One of the following is specified:
driver
Valid for all components. File is copied to systemroot\system32\drivers.
port
Valid for keyboard, mouse, and SCSI components. Allows distinction between port and class driver -- but equivalent to driver type.
class
Valid for keyboard and mouse components. If specified, replaces the standard class driver. File is copied to systemroot\system32\drivers.
dll
Valid for all components. Useful for GDI portion of a display driver. File is copied to systemroot\system32.
hal
Valid only for computer component. File is copied to systemroot\system32\hal.dll (80386/80486), or to \os\winnt\hal.dll on the system partition (ARC).
inf
Valid for all components. Used to copy a GUI INF file for use with system maintenance setup. File is copied to systemroot\system32.
detect
Valid for the computer component (80386/80486 only). If specified, replaces the standard 80386/80486 hardware recognizer. File is copied to c:\ntdetect.com.
source_disk
Identifies the disk from which to copy the file, and must match an entry in the [Disks] section.
filename
Name of the file not including the directory path. The filename is appended to the directory specified for the disk in the [Disks] section to form the full path of the file on the disk.
DriverKey
Name of the key to be created in the registry services tree for this file, if the file is of type driver, port, or class. This value is used to form [Config.DriverKey] section names (see below).
Config.DriverKey Section

A [Config.DriverKey] section specifies values to be set in the registry for particular component options. Required values in the Services\DriverKey key are created automatically. Use this section to specify additional keys to be created under Services\DriverKey and values under Services\DriverKey and Services\DriverKey\subkey_name.

[Config.DriverKey]
value = subkey_name,value_name,value_type,value...
    .
    .
    .

subkey_name
Specifies the name of a key under the Services\DriverKey tree in which to place the specified value. The key is created if it does not exist. If the empty string (““) is specified, the value is placed under the Services\DriverKey tree.
value_name
Specifies the name of the value to be set within the key.
value_type
A string like REG_DWORD that identifies the type of data for this value. See below.
value
Specifies the actual value; its format depends on value_type.

The following types can be specified in the value_type field in [Config.DriverKey] sections:

REG_DWORD
One value is allowed; it must be a string of 1-8 hex digits.

For example:

value = parameters,NumberOfButtons,REG_DWORD,0X2.

REG_SZ, REG_EXPAND_SZ
One value is allowed; it is interpreted as the zero-terminated string to be stored.

For example:

value = parameters,Description,REG_SZ,"This is a text string"

REG_BINARY
One value is allowed; it is a string of hex digits, each pair of which is interpreted as a byte value.

For example (stores the byte stream 00,34,ec,4d,04,5a):

value = parameters,Data,REG_BINARY,0034eC4D045a

REG_MULTI_SZ
Multiple value arguments are allowed; each is interpreted as a component of the multisz.

For example:

value = parameters,Strings,REG_MULTI_SZ,String1,"String 2",string3

Example txtsetup.oem file

The following is an example of a txtsetup.oem file:

[Disks]
d1 = "Oem mouse driver disk 1",\oemmou01.tag,\

[Defaults]
mouse = m2

[mouse]
m1 = "Oem mouse type 1"
m2 = "Oem mouse type 2"

[Files.mouse.m1]
port = d1,m1.sys,oemmou1

[Files.mouse.m2]
port = d1,m2.sys,oemmoup
#replace the mouse class driver
class = d1,oemmoucl.sys,oemmouc

[Config.oemmou1]
value = parameters,xres,REG_DWORD,10
value = parameters,yres,REG_DWORD,20
value = parameters,description,REG_SZ,"Mouse type 1"

[Config.oemmoup]
value = parameters,xres,REG_DWORD,10
value = parameters,yres,REG_DWORD,20
value = parameters,description,REG_SZ,"Mouse type 2"

[Config.oemmouc]
value = parameters,description,REG_SZ,"Mouse class"

A single distribution disk with a single txtsetup.oem file can be used to support multiple hardware devices. To do this, the txtsetup.oem file would contain a single [Defaults] section with an entry specifying the default choice for each hardware type. The file would also contain a [Component] entry for each hardware type and the appropriate associated [Files.Component.ID] and [Config.DriverKey] entries.


A.2 The oemsetup.inf Files

During the Windows phase of a Windows NT installation, or when the program is run in maintenance mode, it is possible for the user to install a driver that is not part of the Windows NT distribution. The user is presented with a menu that includes an Other item which, if selected, causes the Setup program to prompt the user to enter the location of the driver’s files. The Setup program fails if it does not find an oemsetup.inf file in the directory indicated by the user.

You can create an oemsetup.inf file for your driver using an extended GUI INF script language. The Windows NT Setup program acts as an interpreter and executes the instructions in these text file scripts. The GUI INF script language enables you to create a sophisticated installation script with the following features:

  • One section can call another section as if it were a subroutine, passing multiple parameters and receiving multiple return values.

  • Operators are available for manipulating string, list, and integer variables.

  • Flow of control is supported through if-else contructs and goto statements.

  • Built-in commands are provided for a number of common operations, such as accessing the configuration registry, calling a function in a DLL, or starting a new process.

  • Built-in dialog templates can be displayed using text specified in the INF file. There is also an easy to use mechanism for providing text in multiple languages (for example, English, French, and so forth).

The GUI INF Script Language section below provides a detailed description of the GUI INF script language.


A.2.1 Required oemsetup.inf Sections

An oemsetup.inf file must include a set of required shell sections that serve as entry points to be called by the Setup program. You can add additional sections as needed. The DDK includes sample oemsetup.inf files that illustrate the use of the required sections. You can use a sample file as a template in creating your own version of the file. The following sections are required in an oemsetup.inf file:

The Identify Section

The [Identify] section is the first section executed by Setup. It returns information to Setup about the type of driver or drivers that can be installed by this INF file. Setup will not proceed with the installation if the driver type returned by the [Identify] section does not match the driver type specified by the user. For example, if the user runs Setup, and chooses to add a SCSI driver, installation fails if the file’s [Identify] section returns an option type other than SCSI.

The [Identify] section also returns a string that specifies the name of the floppy disk containing the driver’s files. Setup ignores this string.

The ReturnOptions Section

If the [Identify] section indicates that the INF file supports the appropriate type of driver, Setup calls the [ReturnOptions] section. Setup passes a single parameter to this section, indicating the language (English, Spanish, and so on) to be used for dialog strings. This enables the INF file to support a variety of languages. This section returns information to the Setup program, listing the names of any drivers that can be installed by this INF file. It also returns the text strings for each driver (in the requested language) to be used in the dialog box that asks the user to select the driver to install.

The InstallOption Section

If the [ReturnOptions] section returns successfully, indicating the language is supported, Setup displays a dialog box from which the user can select a driver to install. If the user proceeds with the installation, Setup calls the [InstallOption] section. Setup passes the following parameters to the [InstallOption] section:

Language to use
OptionID to install
SourceDirectory
AddCopy  (YES | NO)
DoCopy   (YES | NO)
DoConfig (YES | NO)

The Source Media Descriptions Section

The [Source Media Descriptions] section is a detect section required in all oemsetup.inf files. It consists of a line for each floppy disk that is part of your distribution set. The format for each line is as follows:

LineKey = [DiskDescription, TAGFILE = TagFileName]

LineKey
An integer [1 to 999] that identifies the line for later access. Lines must be numbered consecutively beginning at 1.
DiskDescription
Disk name string for prompting the user to insert the disk. This is ignored if the source is not removeable media.
TagFileName
Specifies the name of the file whose presence on the disk indicates to the Setup program that the correct disk has been inserted. This is ignored if the source is not removeable media. Note that TAGFILE, =, and TagFileName must be separated by whitespace.

For example:

[Source Media Descriptions]
1  = "Driver Disk #1"  , TAGFILE = disk1
2  = "Driver Disk #2"  , TAGFILE = disk2


A.3 GUI INF Script Language

This section describes the GUI INF script language used in oemsetup.inf files. A driver writer can use this language to create custom installation and configuration scripts for Windows NT device drivers. The Windows NT Setup program acts as an interpreter and executes the instructions in these text file scripts.

The following sections describe components of the GUI INF script language:

  • Sections

  • INF variables

  • INF operators

  • Flow control

  • Using DLLs and external programs

  • Built-in commands


A.3.1 Sections

In the GUI INF script language, files are broken into logical groups called sections. A section is identified by a header line that consists of a name in square brackets (for example, [Identify]). Following the header line is the body of the section, consisting of one or more lines that are interpreted by the Windows NT Setup program.

The GUI INF script supports three types of sections: shell, install, and detect. All three section types begin with a header line. The section types differ in the command used to call the section, and in the commands and keywords that can be used within the section.


A.3.1.1 Shell Sections

A shell section is similar to a function in other programming languages. You can pass multiple parameters to a shell section and receive multiple return values. Each shell section has a separate symbol table for storing its variables and their values.

The entrypoint sections used by the Setup program ([Identify], [ReturnOptions], [InstallOption]) are shell sections. The other types of sections ([Install] and [Detect]) are called by a command line in a shell section.

Shell Syntax

The following is the syntax for calling a shell section:

shell inf_file_path section_name [Args]*

inf_file_path
Specifies the full or partial path of the INF file in which the section is located. An empty string (““) indicates the current file.
section_name
Identifies the section (name in square brackets).
Args
Zero or more comma-separated or whitespace-separated arguments.

You can use the shell command from within a shell section to call another shell section (in the same file or in a different INF file).

$Shell Code

For a shell operation, Setup sets the value of the variable $ShellCode to one of the following:
!SHELL_CODE_OK | Shelling operation was successful
!SHELL_CODE_NO_SUCH_INF | Named INF file could not be located
!SHELL_CODE_NO_SUCH_SECTION | The INF file did not contain named section
!SHELL_CODE_ERROR | Parsing error in INF section

Programmers must check the value of $ShellCode before assuming that the return values and side-effects of the shelling operation actually apply.

Symbol Table Levels

Setup stores all its variable names and their values in its internal memory in an area called the symbol table. The shell statement has a special and very important effect on the symbol table.

When a shell statement is processed, a completely new symbol table is created. The original symbol table values are no longer directly accessible, and new variables and values are stored in this new, separate table. When the shelled section returns, this new symbol table is discarded. This behavior is similar to the “block scoping” rules common in procedural programming languages such as C.

The reason for such behavior is to avoid duplication or “collision” of variable names. The writer of an INF subroutine which is the target of a shell statement can be sure that variables he creates will not accidentally override global values.

This multi-level symbol table mechanism creates a new problem: how does a shelled section change or reference the value of a variable outside of its private, temporary symbol table? The answer is that Setup has special syntax for referencing variables which are nonlocal. The possibilities are:
local | $(MyVar) | reference to local variable MyVar
global | $(!MyVar) | reference to global variable MyVar
global | $(!G:MyVar) | reference to global variable MyVar
parent | $(!P:MyVar) | reference to caller’s variable MyVar

The outermost (or base) Setup symbol table is called the global symbol table. Variables created by Setup itself during initialization and the top-level INF file reside in the global symbol table. Setup allows access to three possibly distinct symbol tables: the local one, the one belonging to the shelling INF section, and the global one.

Passing Arguments to a Shell Section

The Setup program stores a shell’s input parameters in the shell’s symbol table using variables of the form $N, where N refers to the zero-based position of the parameter in the argument list. The shell can then use the $(var) notation to retrieve the argument’s value, that is, $($0) retrieves the value of Arg0, $($1) is the value of Arg1, and so on. Using the Return command, a shell section can return zero or more values to the section that called it. The following example shows a shell section that retrieves its arguments and returns two values:

[SampleShellSection]

; retrieve the input parameters

set Arg0 = $($0)
set Arg1 = $($1)
   .
   .
   .
; return two values

set Option = "OptionString"
Return STATUS_SUCCESSFUL $(Option)

Return Values From a Shell Section

The Setup program stores a shell section’s return values in the symbol table of its parent shell section (that is, the shell that called it), using variables of the form $RN, where N refers to the zero-based position of value in the list of return values. The parent shell can then use the $(var) notation to retrieve the return values (that is, $($R0) retrieves the first return value, $($R1)the second return value, and so on). The Setup program uses the $RN variable to store the number of return values. In addition to the values returned by a shell section, the Setup program sets the ShellCode global variable to indicate whether errors occurred during its interpretation of the section. The following example shows the command lines that call a shell section, then retrieve the results:

; pass two parameters to AShellSection

set Arg0 = "argument 0"
set Arg1 = argument1

; invoke section named AShellSection in the current inf file

shell "" AShellSection $(Arg0) $(Arg1)

; check ShellCode for internal errors during AShellSection execution

ifint $($ShellCode) != $(!SHELL_CODE_OK)
exit
endif

; get the return values

set Status = $($R0)
set Option = $($R1)
ifint $($R#) > 2
set OptionText = $($R2)
endif


A.3.1.2 Install Sections

An install section is like a subroutine of the shell section that called it. It can access the variables of the shell section’s symbol table. Like a shell section, the lines of an install section are interpreted sequentially, and you can use flow control keywords (for example, ifstr, goto) to control execution. There are a number of built-in commands that can be used in both shell sections and install sections. There are commands that can only be used in install sections, and other commands that can only be used in shell sections. It is not possible to call a shell, install, or detect section from within an install section.

An oemsetup.inf file uses install sections to perform such actions as copying files and setting up the configuration registry.


A.3.1.3 Detect Sections

A detect section is used to assign values to INF variables, or to store constant information for use by shell sections. Typically, the lines of a detect section use the following syntax:

key = value [? LibHandle_or_Path FunctionName [Args]*]

A shell section uses the read-syms or detect commands to cause the Setup program to interpret the lines of a detect section. The read-syms command creates a variable named key and assigns the string in value to it. The value string can be a literal string, or it can include other INF variables, in which case, the Setup program performs the variable substitution. INF variables used in a detect section can be any local or global variable accessible to the shell section that called read-syms. The detect command ignores all lines that do not contain the question mark (?) operator. For lines with this operator, it assigns to key the string returned by the specified DLL function.


A.3.2 INF Variables

The GUI INF script language supports variables, symbols for which a value string is stored in a symbol table maintained by the Setup program. An INF script uses the $(var) syntax to access the value of the variable named var. This causes the Setup program to perform a run-time textual substitution. Variable names are compared on a case-sensitive basis.

The syntax for assigning a value string to a variable depends on the type of section. In a shell or install section, use the set command to assign a value to a variable, as in:

set Status = STATUS_SUCCESSFUL

It is important to note that Setup treats names as self-defining. This means that the assignment statement above changes the variable status to contain the string “STATUS_SUCCESSFUL”. The following are completely equivalent:

set Status = STATUS_SUCCESSFUL
set Status = "STATUS_SUCCESSFUL"
set Temporary = STATUS_SUCCESSFUL
set Status = $(Temporary)

There are a number of other “set-...” commands (such as set-subst or set-add) you can use to set or modify the value string assigned to a variable. There are also built-in commands (such as GetRegValue) that perform an operation and then assign a return value to a specified variable.

These commands cannot be used in a detect section, where you would use the following syntax when assigning a value to a variable:

key = value [? LibHandle_or_Path FunctionName [Args]*]

For example:

[SampleDetectSection]
    Company = "XYZ "
    Product = "WidgetMaster"
    Version = 1.0
    Now = "no-time" ? $(!LIBHANDLE) GetSystemDate

A shell section uses the read-syms or detect commands to read the variables and values specified in a detect section. The shell section can then use the variables as if they had been specified with a set command. For example, the following lines read the variables from the detect section shown above, and then use the variables to assign a value string to a new variable:

read-syms SampleDetectSection
set ProductDescription = $(Company)$(Product)", Version "$(Version)
; the value string assigned to the ProductDescription key is
;   "XYZ WidgetMaster, Version 1.0"

The optional component of the detect section syntax enables assigning a value string returned by a DLL function. This is done using the detect command, which searches a specified detect section for lines containing the ‘?’ operator. For these lines, the DLL function is called and the returned value string is assigned to the specified variable. The detect command does not assign values to variables in lines that do not contain the ‘?’ operator. Similarly, the read-syms command always uses the value string specified in value, and ignores the DLL function. Thus, using read-syms on the SampleDetectSection shown above would cause the string “no-time” to be stored as the Now key.


A.3.2.1 Value Types

All values are stored in the symbol table as strings. However, there are built-in commands and operators that enable you to manipulate the strings as if they were integers or lists.

The ifint flow control command allows you to compare values as if they were integers. To make the comparison, the Setup program uses the atol function to convert the value string to an integer. There are also a set of commands (set-add, set-mul, and so forth) that enable you to perform simple arithmetic operations on value strings.

A list value is a string that begins with the’{‘ character and ends with a’}’ character. A list contains zero or more items separated by commas. Lists can be nested, that is, a list item can begin another list. Examples of lists are:

{}
{"item1", "item2"}
{"{"name1","value1"}","{"name2","value2"}"}


A.3.2.2 Accessing the Value of a Variable

As discussed earlier, the Setup program maintains a separate symbol table for storing the variables and their values for each shell section called. Consequently, different shell sections can use the same variable names without conflict. Any install section called by a shell section can access all the variables that have been defined in its shell section. Similarly, variables defined in an install section are available in the shell section, even after the install section has been exited. A shell section can use the exclamation point (!) operator to access variables that were defined by its shell section ancestors. When control returns from a shell section, the shell section’s symbol table is deleted.

Use the following syntax to access the value associated with a variable:

$([!]var)

When the Setup program is interpreting a line in an INF script, it replaces this with the value string associated with var. The optional ‘!’ operator enables accessing global variables (that is, variables defined in other shell sections, including sections in other INF files). The ‘!’ operator is useful in accessing the global variables defined in the INF scripts distributed with the Setup program, such as initial.inf.

Examples:

$(Option)        ; a local variable
$(!STF_NTPATH)   ; a global variable

Other operators for accessing variable values are described in the next section, INF operators.


A.3.3 INF operators

This section summarizes the operators of the GUI INF script language.

;
Comment operator. All characters after the semicolon on the same line are ignored.
+
The line continuation operator. Indicates that Setup should interpret the line as a continuation of the same line.
!
Used with a variable, as !var. Indicates that var is a variable that might have been defined in another shell section. Use this operator to refer to global variables defined by the Setup program.
?
Detect operator. Used only in detect sections, this operator indicates that a variable’s value can be a value string returned from a DLL function. For instance, var = value[?LibHandle_or_PathFunctionName[Args]*].
{}
List operators. When the first character of a value string is ‘{‘, the Setup program interprets the string as a list. A list value is a string that begins with ‘{‘, ends with ‘}’, and contains zero or more comma-separated items.

The following operators are used with parentheses to perform an operation on the arguments within the parentheses.

$
The syntax is $(var). The interpreter looks up the var variable in the symbol table and performs a textual substitution. Use with the ! operator for global variables.
*
The syntax is *(ListVar,N). N represents the Nth item from a list operator. The interpreter performs a textual substitution using the Nth item from the specified INF list. List item numbering begins with one. For example:
set List = {one, two, three, four}
set Item = *($(List), 3)         ;sets Item to "three"

>
This operator is an append to list operator. The syntax is >(ListVar,ListItem) where the ListItem string is appended to the ListVar list. For example:
set List = {one, two, three, four}
set List = >($(List), five)
; value of List is now {one, two, three, four, five}

This operator can be used to append a new item to the end of an existing list to form a new list:

set MyList = {1,2,3}
set MyNewList = ($(MyList), 4)
; creates MyNewList {1,2,3,4}

^
The syntax is ^(SectionName, N). Denotes the Nth item from each line of the section indicated by SectionName. An example of this syntax is:
[key =] value1 [, value2]*

Note that the section lines can include or omit the leading "key =", and can have multiple values separated by commas or whitespace. In the example above, key is item 0, value1 is item 1, value2 is item 2, and so on. Thus given the following [Languages] section contents:

[Languages]
FRN, French
ENG, English
SPN, Spanish

the set commands, using the ^ operator, set the CodeList and DescList variables as follows:

set CodeList = ^(Languages, 1)
; CodeList is now {"FRN", "ENG", "SPN"}
set DescList = ^(Languages, 2)
; DescList is now {"French", "English", "Spanish"}
#
The syntax is #(DetectSection, var, N). The interpreter performs a textual substitution using the Nth item from a DetectSection line identified by a specified var. Item numbering is the same as described for the ^ operator. For example given the following [Languages] section:
[Languages]
FRN, French
ENG, English
SPN, Spanish

the set command, using the # operator, sets the Language variable to Spanish.

set Language = #(Languages, C, 2)


A.3.4 Flow Control Mechanisms

The GUI INF script language provides mechanisms for controlling the flow of execution in shell and install sections. These mechanisms include a set of if-else constructs, a ForListDo command for repeating a set of commands on the items of a list, and a goto command.


A.3.4.1 If/Else Statements

The following if commands enable branching based on different conditions:
Command | Description
ifstr | ifstr Value1 {==|!=} Value2 Compare Value1 to Value2, case sensitively
ifstr(i) | ifstr(i) Value1 {==|!=} Value2 Compare Value1 to Value2, case insensitively
ifint | ifint Value1 {==|!=|<=|>=|<|>} Value2 Compare Value1 to Value2 as integers, after using the atol() function to convert the value strings into LONG values.
ifcontains | ifcontains Value {in|not-in} List Compares case-sensitively the Value string to the string items in List. The “in” and “not-in” comparison operators enable branching based on whether the string is in or not in the list.
ifcontains(i) | ifcontains(i) Value {in|not-in} List Same as ifcontains, except that comparisons are case-insensitive.

For every if... command variation, there is a corresponding else-if... command, that uses the same syntax.

The various if statements are followed by a block of lines that are executed if the expression evaluates to TRUE. The block is terminated by either an endif statement or by one of a set of else statements (else, else-ifstr, else-ifstr(i), else-ifint, else-ifcontains, or else-ifcontains(i)). For every if... or else-if... statement, there must be a corresponding endif statement. If statements can be nested. For example:

ifstr
    .
    .
    .
else-ifint
        .
        .
        .
    else
        .
        .
        .
    endif
endif


A.3.4.2 For Statements

The ForListDo command enables you to create a loop for repeating a set of commands on the items of a list. Within the body of the loop, the notation $($) refers to the current list item. The notation $(#) refers to the position of the current item in the list, beginning with one. You cannot use a goto statement within a ForListDo loop to jump to a location outside the loop. Similarly, an if/endif block cannot straddle a loop boundary.

ForListDo List
    .
    .
    .
EndForListDo


A.3.4.3 Goto Statements

The goto command causes execution to continue at the specified label. The destination of the goto statement must be in the same section. It is illegal to use goto to jump from within a ForListDo loop to a location outside the loop. The following example illustrates the goto syntax:

goto configure
    .
    .
    .
configure = +
    .
    .
    .


A.3.5 Using DLLs and External Programs

The GUI INF script language supports the use of DLL functions from within an INF script. The syntax used to call a DLL function from a detect section differs from that used in install and shell sections. The DLL function prototype also differs depending on the type of section.


A.3.5.1 DLLs in Install and Shell Sections

For install and shell sections, the LoadLibrary, FreeLibrary, and LibraryProcedure commands are available to load and unload the library module, and to call a function within the library. The following shows the syntax for these commands:

LoadLibrary Syntax

The LoadLibrary command loads the specified library file, and returns a handle to the loaded module. If the library file cannot be located, the Setup program displays an error message and terminates. The syntax of the LoadLibrary command is:

LoadLibrary DiskName DLLPath LibHandle

DiskName
String to prompt the user to insert a disk. This string is used only if DLLPath indicates that the specified file is on a floppy disk (for example, a:\Setupdll.dll) and the file is not found on the disk that is present.
DLLPath
The full path of the DLL file.
LibHandle
Receives the library handle. This handle identifies the library module in the LibraryProcedure and FreeLibrary commands, and it can also be used to identify the library module in a detect section.

For example:

LoadLibrary "Setup Disk #1" a:\setupdll.dll LibHandle

FreeLibrary Syntax

The FreeLibrary command unmaps the specified library module, invalidating the handle. The syntax of the FreeLibrary command is:

FreeLibrary LibHandle

LibHandle
INF variable that identifies the loaded library module. This value is returned by the LoadLibrary command.

For example:

FreeLibrary $(LibHandle)

LibraryProcedure Syntax

The LibraryProcedure command calls an exported function in the specified DLL. The syntax of the LibraryProcedure command is:

LibraryProcedure Result LibHandle FunctionName [Args]*

Result
Receives the value of a text string returned by the DLL function.
LibHandle
INF variable that identifies the loaded library module. This value is returned by the LoadLibrary command.
FunctionName
Name of the DLL function to call (case-sensitive).
Args
Zero or more argument strings.

For example:

LibraryProcedure Result $(LibHandle) DLLFunc $(Arg0) $(Arg1)

The DLL function called by the LibraryProcedure command must provide a function whose prototype is shown below. Note that this prototype is different from that used for DLL functions called from a detect section.

BOOL PASCAL FunctionName(cArgs, lpszArgs[], *lpszTextOut)
DWORD cArgs;
LPSTR lpszArgs[];
LPSTR *lpszTextOut);

cArgs
The number of arguments in the lpszArgs array.
lpszArgs[]
An array of string arguments.
lpszTextOut
Receives a pointer to the result text. The DLL function must set this to point to a valid buffer (not NULL), although the buffer may be empty.

The function should return TRUE if successful or FALSE if an error occurs. The function is responsible for managing the memory allocated for the text buffer pointed to by lpszTextOut. If the function is successful, it can fill the lpszTextOut buffer with an arbitrary string, and the Setup program copies the text into the INF symbol table where it can be accessed using the Result variable. If an error occurs (that is, the function returns FALSE), the function should fill the lpszTextOut buffer with a descriptive error string. In this case, the Setup program’s default handling is to display the error string in a dialog box. This is treated as a noncritical error, with execution continuing and the value of the Result variable set to ERROR. This default error handling is overridden if the INF script has defined a FLibraryErrCtl variable with a value greater than zero. In this case, the error string is assigned to the Result variable, and the Setup program does not display the dialog.


A.3.5.2 DLLs in Detect Sections

In a detect section, the line to call a DLL function has the following format:

var = value ? LibHandle_or_Path FunctionName [Args]*

This is similar to other detect lines that use the var = value format. If the detect section is called using the read-syms command, value is assigned to var. But if the detect section is called using the detect command, the DLL function is called to get a value assigned to var.

LibHandle_or_Path
This can be a handle to a library module returned by a LoadLibrary command executed in a shell or install section. Alternatively, it can be a string containing the full or partial path of the DLL file.
FunctionName
Name of the DLL function to call (case-sensitive).
Args
Zero or more argument strings.

The DLL function called from a detect section must conform to the prototype shown below. The function’s return value indicates the actual number of bytes returned by the function in the lpszValue buffer. If the return value is greater than the size of the buffer, cbValue, the Setup program reallocates the buffer to the specified size and recalls the function.

UINT FunctionName(lpszArgs[], cArgs, lpszValue,
cbValue)
LPSTR lpszArgs[];
USHORT cArgs;
LPSTR lpszValue;
UINT cbValue);

lpszArgs[]
An array of string arguments.
cArgs
The number of arguments in the lpszArgs array.
lpszValue
Points to a buffer allocated by the Setup program, to receive the value assigned to the variable in the INF file.
cbValue
Size, in bytes, of the buffer pointed to by lpszValue.


A.3.5.3 Executing an External Program

The GUI INF script language provides two commands that enable you to execute another program from an INF script. From a shell or install section, you can use the RunProgram and StartDetachedProcess commands to start another program, and you can use the WaitOnEvent and SignalEvent commands to synchronize execution between the INF script and a program started by StartDetachedProcess.

RunProgram Command

The RunProgram command executes the specified program and stores the program’s exit code under the ExitCode variable. To the INF script, it appears that the program is executed synchronously, because execution of the INF script waits for the program to terminate. Internally, however, the Setup program executes the program asynchronously and then uses a loop to wait for the program’s termination while yielding periodically to handle messages. This enables the Setup program to be responsive to user interactions during the wait. The syntax of this command is:

RunProgram ExitCode DiskName Reserved ProgramPath [Args]*

ExitCode
Receives the exit code of the process, if the process is successfully started. Otherwise, receives the string, “ERROR”.
DiskName
String to prompt the user to insert a disk. This string is used only if ProgramPath indicates that the specified file is on a floppy disk (for example, a:\mysetup.exe) and the file is not found on the disk that is present.
Reserved
Ignored; should be ““.
Args
Zero or more argument strings.

For example:

RunProgram ExitCode "Setup Disk #1"  "" a:\setupprg.exe

StartDetachedProcess Command

The StartDetachedProcess command executes the specified program asynchronously, using the CreateProcess function of the Win32 API (with the DETACHED_PROCESS flag). Execution of the INF script continues without waiting for the process to terminate. The syntax of the command is:

StartDetachedProcess ExitCode DiskName Reserved ProgramPath [Args]*

ExitCode
Receives the exit code of the process, if the process is successfully started. Otherwise, receives the string “ERROR”.
DiskName
String to prompt the user to insert a disk. This string is used only if ProgramPath indicates that the specified file is on a floppy disk (for example, a:\mysetup.exe) and the file is not found on the disk that is present.
Reserved
Ignored; should be ““.
ProgramPath
A full or partial path specifying the file to be executed.
Args
Zero or more argument strings.

For example:

StartDetachedProcess Success "Setup Disk #1" "" a:\setupprg.exe

WaitOnEvent Command

The WaitOnEvent command opens a handle to the specified event object. It then waits for either the object’s state to be signaled or the timeout interval to elapse. This command blocks the execution of the INF script, but, internally, the Setup program uses a loop to wait for the event to be signaled while yielding periodically to handle messages. This enables the Setup program to be responsive to user interactions during the wait.

This command is useful in coordinating execution of the INF script with an external program started by the StartDetachedProcess command. If the first character of the EventName string is an asterisk (*), a variation of the WaitOnEvent operation is performed. In this case, the detached process can signal either the named event or an event named \\SETUP_FAILED. It signals the EventName event to indicate success, and signals the \\SETUP_FAILED event to indicate that the process failed and an error popup has already been presented to the user.

Note that the Setup program does not create the EventName event if it does not exist. This means that you must allow time for the detached process to create the event. The syntax of the command is:

WaitOnEvent Result EventName Timeout

Result
Receives one of the following strings:

“EventSet” - The EventName event was signaled.

“EventNotFound” - No event object exists with the specified name.

“EventTimeout” - The timeout interval elapsed before the event was signaled.

“EventFailed” - The \\SETUP_FAILED event was signaled.

EventName
Specifies the name of the event object to wait on. If the first character of the string is ‘*’, the name consists of the characters following the ‘*’.
Timeout
Specifies the timeout interval in milliseconds. If zero is specified, the wait is indefinite.
SignalEvent Command

The SignalEvent command opens a handle to the specified event object, and sets its state to signaled. The Setup program does not create the EventName event if it does not exist. The syntax of the command is:

SignalEvent Result EventName

Result
Receives one of the following strings:

“EventSignaled” - The event’s state was set to signaled.

“EventNotSet” - The event’s state could not be set to signaled.

“EventNotFound” - No event object exists with the specified name.

EventName
Specifies the name of the event object to be signaled.


A.3.6 Built-in Commands

The GUI INF script language supports a number of built-in commands. Some of these commands can only be called from within a shell section, others only from an install section, and still others from both shell and install sections.


A.3.6.1 Common Shell and Install Section Commands

The following commands can be specified in either a shell section or an install section of a GUI INF file.

Set inserts a new variable-value string pair into the symbol table. The syntax of the command is:

set var = value

var
A name that can be used as a variable in the shell or install section in which it is set.
value
A string or a list. If the string contains whitespace or commas, it must be enclosed in double quotes, “x”. A list is enclosed in curly braces ({ }) with the list items separated by commas.
Set-xxx Commands

There are several Set-xxx commands that are used to manipulate string values. These include string substitution and string arithmetic commands. They are generally self-explanatory and the syntax is given below.

The Set-subst command is like the set command, except that set-subst replaces escape sequences in the value string with their byte equivalents before storing the variable and value in the symbol table. The following escape sequences are handled: (\n, \r, \t, and \###, where ### is an octal number).

The syntax of the command is:

set-subst var = value

The set-add, set-sub, set-mul, set-div, and set-or commands use atol to convert value1 and value2 into integer values and then perform the indicated operation. Using the ltoa function, the result is converted to a string and is assigned to var. For the set-or command, the values are treated as ULONG.

Examples:

set-add var = value1, value2      ; value1 + value2
set-sub var = value1, value2      ; value1 - value2
set-mul var = value1, value2      ; value1 * value2
set-div var = value1, value2      ; value1 / value2
set-or var = value1, value2       ; value1 | value2

The set-hextodec command converts value from a hexadecimal string to the equivalent decimal string. The set-dectohex command converts value from a decimal string to the equivalent hexadecimal string. Both do so before storing the variable and value in the symbol table.

Examples:

set-hextodec var = value
set-dectohex var = value

Cursor Manipulation Commands

The StartWait command displays the mouse hourglass cursor, and the EndWait command displays the mouse arrow cursor.

There are no arguments. The syntax is:

StartWait
EndWait

GetDriveInPath Command

GetDriveInPath sets the value of DriveVar to be the drive letter and colon extracted from the path string in DrivePath. For example, if DrivePath is c:\windows, the value assigned to DriveVar is c:. If DrivePath does not contain a drive, the value is an empty string.

The syntax of the command is:

GetDriveInPath DriveVar DrivePath

Debug_Output Command

Debug_Output writes the string SETUP:DebugMessage to the kernel debugger. The syntax of the command is:

Debug_Output DebugMessage

Debug output is controlled by the value of the variable, DEBUGOUTPUTCONTROL. If this variable is 1, debug messages are displayed; if it is 0, debug messages are suppressed. There are two ways to set or clear DEBUGOUTPUTCONTROL.

GUI-mode Setup is controlled by initial.inf which contains a [DebugVars] section. In this section, the var STF_DISPLAYDEBUGOUTPUT is initialized to 0 as follows:

[DebugVars]
STF_DISPLAYDEBUGCONTROL = 0

To display debug output, change the 0 to 1. This causes debug messages to be displayed because in the shell section of initial.inf , the value of STF_DISPLAYDEBUGCONTROL is assigned to DEBUGOUTPUTCONTROL as follows:

read-syms DebugVars
set !DebugOutputControl = $(!STF_DISPLAYDEBUGOUTPUT)

An acceptable and more direct method is to set DEBUGOUTPUTCONTROL directly. Setup.inf contains a statement which sets the value of DEBUGOUTPUTCONTROL.

set !G:DebugOutputControl = 0

Turn on debug output by modifying the statement:

set !G:DebugOutputControl = 1

This will cause debug output to be displayed.

In maintenance setup mode, this is the only available method since setup.inf controls maintenance mode setup (initial.inf is not used) and setup.inf does not have a [DebugVars] section.

The use of the G prefix sets the value of DEBUGOUTPUTCONTROL in the global symbol table in case setup.inf is not the top level .inf file.

For more information on using the debugger, refer to Chapter 4, Debugging Windows NT Drivers.

Split-String Command

The Split-String command has three arguments: SourceString, SeparatorsChars, and DestList. It creates a list by making a string a token using one or more delimiter characters. The items in the list include the tokens and the delimiter characters. For example, the following command line:

Split-String "\windows\system32\setup.exe" "\." DestinationList

sets DestinationList to the following list:

{"\","windows","\","system32","\","setup",".","exe"

QueryListSize Command

The QueryListSize command sets the ListSize variable to the number of items in a List variable.

The syntax of the command is:

QueryListSize ListSize List

AddfileToDeleteList Command

The AddfileToDeleteList command adds the name of a file to a list of files to be deleted at the next boot. The filename is stored in a file, and registry entries are set to cause a service to perform the deletion the next time the system boots.

The syntax of the command is:

AddFileToDeleteList FullPath

Sleep Command

The Sleep command suspends execution of the INF script for the interval specified by the parameter Millisecs. The maximum interval allowed is one minute, that is, 60000 milliseconds. This command blocks execution of the INF script, but, internally, the Setup program uses a loop to wait for the interval to elapse, while yielding periodically to handle messages. This enables the Setup program to be responsive to user interactions during the wait.

The syntax of the command is:

Sleep Millisecs

FlushInf Command

The FlushInf command determines whether the Setup program has parsed the INF file specified by InfFileName in advance. If so, the information is discarded. This is necessary for Setup to interpret more than one INF file with the same base filename.

The syntax of the command is:

FlushInf InfFileName


A.3.6.2 Registry Commands

The GUI INF script language provides a set of built-in commands for manipulating the configuration registry. These commands can be used in the shell or install sections of an INF script. For additional information about the registry, refer to the Windows NT Software Development Kit.

The commands that manipulate the registry create an error variable, RegLastError, whose value is set to zero if the operation succeeds, or to a nonzero error code if it fails.

CreateRegKey Command

The CreateRegKey command creates and returns a handle to a new key in the registry under the specified key. If the key already exists, NewHandle contains an empty string, and RegLastError is set to a nonzero value. The syntax is:

CreateRegKey KeyHandle {NewKey} Reserved Access Options NewHandle

KeyHandle
Open handle to the registry key in which the new key is created. The handle must have KEY_WRITE or KEY_CREATE_SUB_KEY access.
NewKey
A list containing: {NewKeyName,Index,Class}
NewKeyName
Name of the new key
Index
Reserved item that should equal zero
Class
Class of the new key
Access
A decimal string specifying the mask that determines the access of NewHandle to the new key. For a list of predefined access constants, see the table at the end of this section.
Options
If “1”, the new key is volatile, and is not preserved when the system is restarted. If “0” or ““, the key is nonvolatile and will be preserved in the registry hives.
NewHandle
Receives an open handle to the new key.

The following example creates an “XYZ” key under the registry key identified by the SoftwareKey variable. If successful, NewHandle has KEY_SET_VALUE access to the new key.

CreateRegKey $(SoftwareKey) {"XYZ",0,GenericClass} "" +
              $(!REG_KEY_SET_VALUE) "" NewHandle

OpenRegKey Command

The OpenRegKey command returns an open handle to a specified registry key. If the key does not exist, NewHandle contains an empty string, and the RegLastError variable is set to a nonzero value. The syntax of the command is:

OpenRegKey KeyHandle Reserved KeyName Access NewHandle

KeyHandle
Open handle to the registry key in which the preferred key is located.
Reserved
Reserved; must be an empty string, ““.
KeyName
Name of the key to open.
Access
A decimal string specifying the mask that determines the access of NewHandle to the new key. Refer to the table of predefined constants.
NewHandle
Receives an open handle to the new key.

For example:

OpenRegKey $(SoftwareKey) "" "XYZ" $(!REG_KEY_SET_VALUE) NewHandle

FlushRegKey Command

The FlushRegKey command writes all attributes of the specified key to the registry hives. The syntax of the command is:

FlushRegKey KeyHandle

KeyHandle
Open handle to the registry key to flush.
CloseRegKey Command

The CloseRegKey command releases the handle of the specified key. Closing a key does not necessarily cause data to be immediately written to the registry hives. The syntax of the command is:

CloseRegKey KeyHandle

KeyHandle
Open handle to the registry key.
DeleteRegKey Command

The DeleteRegKey command deletes a subkey from a key in the registry. The subkey can have values under it, but cannot have any subkeys. The syntax of the command is:

DeleteRegKey KeyHandle SubKeyName

KeyHandle
Open handle to the registry key in which the key to be deleted is located.
SubKeyName
Name of the key to be deleted.
DeleteRegTree Command

The DeleteRegTree command deletes a key and all of its subkeys from a key in the registry. The syntax of the command is:

DeleteRegTree KeyHandle SubKeyName

KeyHandle
Open handle to the registry key in which the key to be deleted is located.
SubKeyName
Name of the key to be deleted.
EnumRegKey Command

The EnumRegKey command creates a list containing information about each subkey of the specified key. The syntax of the command is:

EnumRegKey KeyHandle KeyList

KeyHandle
Open handle to the registry key for which to enumerate keys. The handle must have KEY_READ or KEY_ENUMERATE_SUB_KEYS access.
KeyList
Receives a list containing information about each subkey. The list for each subkey is another list containing the following items: {KeyName,TitleIndex,Class}.
SetRegValue Command

The SetRegValue command stores a name/value pair in the registry under the specified key. If a value with the specified name already exists, the new value replaces the old value. The syntax of the command is:

SetRegValue KeyHandle NewValueList

KeyHandle
Open handle to the registry key in which the name/value pair is to be stored. The handle must have KEY_SET_VALUE access.
NewValueList
A list containing the following items: {ValueName,Index,ValueType,ValueData)
ValueName
Name of the new value
Index
Reserved item that should equal zero
ValueType
Type of data in ValueData. For a list of pre-defined constants for the standard registry data types, refer to the table at the end of this section.
ValueData
The data to be stored.
GetRegValue Command

The GetRegValue command retrieves the value associated with a specified value name under a specified key. The syntax of the command is:

GetRegValue KeyHandle ValueName ValueData

KeyHandle
Open handle to the registry key in which the name/value pair is stored. The handle must have KEY_QUERY_VALUE access.
ValueName
Name that identifies the required value.
ValueData
Receives a list containing the registry data associated with ValueName. The list contains the following items, which are described for the SetRegValue command: {ValueName,Index,ValueType,ValueData}
DeleteRegValue Command

The DeleteRegValue command deletes the name/value pair with a specified value name under a specified key. The syntax of the command is:

DeleteRegValue KeyHandle ValueName

KeyHandle
Open handle to the registry key in which the name/value pair is stored. The handle must have KEY_SET_VALUE access.
ValueName
Name that identifies the name/value pair to delete.
EnumRegValue Command

The EnumRegValue command creates a list containing information about each name/value pair of the specified key. The syntax of the command is:

EnumRegValue KeyHandle ValueList

KeyHandle
Open handle to the registry key in which the name/value pair is stored. The handle must have KEY_QUERY_VALUE access.
ValueList
A list containing an entry for each name/value pair in the key. The entry for each pair is a list containing the following information: {ValueName,Index,ValueType,ValueData}
ValueName
Name of the value
Index
Reserved item that should equal zero
ValueType
Type of data in ValueData. For a list of predefined constants for the standard registry data types, refer to the table at the end of this section.
ValueData
The stored data.
Global Registry Constants

The Windows NT Setup program defines a number of global variables that are useful when calling the registry functions. The values assigned to these variables are the constants that can be specified in the various registry operations. For example, these global keys include a set of constants to specify the required access when opening a registry key using the OpenRegKey and CreateRegKey commands (such as REG_KEY_READ whose value is KEY_READ, a constant defined in winnt.h). You would use the $(!GlobalKey) notation (for example, $(!REG_KEY_READ) ) to use one of these predefined keys. The following is a list of these global keys and their values:

Predefined registry handles

These variables can be used in any of the registry commands that require an open handle to a registry key.

"REG_H_LOCAL",                   HKEY_LOCAL_MACHINE
"REG_H_CLASSES",                 HKEY_CLASSES_ROOT
"REG_H_USERS",                   HKEY_USERS
"REG_H_CUSER",                   HKEY_CURRENT_USER

Registry key creation options
"REG_OPT_VOLATILE",              REG_OPTION_VOLATILE
"REG_OPT_NONVOL",                REG_OPTION_NON_VOLATILE

Registry value types
"REG_VT_NONE",                   REG_NONE
"REG_VT_BIN",                    REG_BINARY
"REG_VT_SZ",                     REG_SZ
"REG_VT_EXPAND_SZ",              REG_EXPAND_SZ
"REG_VT_MULTI_SZ",               REG_MULTI_SZ
"REG_VT_DWORD",                  REG_DWORD

Registry key access mask bit
"REG_KEY_QUERY_VALUE",           KEY_QUERY_VALUE
"REG_KEY_SET_VALUE",             KEY_SET_VALUE
"REG_KEY_CREATE_SUB_KEY",        KEY_CREATE_SUB_KEY
"REG_KEY_ENUMERATE_SUB_KEYS",    KEY_ENUMERATE_SUB_KEYS
"REG_KEY_NOTIFY",                KEY_NOTIFY
"REG_KEY_READ",                  KEY_READ
"REG_KEY_WRITE",                 KEY_WRITE
"REG_KEY_READWRITE",             KEY_READ | KEY_WRITE
"REG_KEY_EXECUTE",               KEY_EXECUTE
"REG_KEY_ALL_ACCESS",            KEY_ALL_ACCESS

Registry errors
"REG_ERROR_SUCCESS",             0L

The following global keys are used when creating a Services entry in the registry.

Service Types (Bit Mask)
"SERVICE_KERNEL_DRIVER",        SERVICE_KERNEL_DRIVER
"SERVICE_FILE_SYSTEM_DRIVER",   SERVICE_FILE_SYSTEM_DRIVER
"SERVICE_ADAPTER",              SERVICE_ADAPTER
"SERVICE_WIN32_OWN_PROCESS",    SERVICE_WIN32_OWN_PROCESS
"SERVICE_WIN32_SHARE_PROCESS",  SERVICE_WIN32_SHARE_PROCESS

Service Start Type
"SERVICE_BOOT_START",            SERVICE_BOOT_START
"SERVICE_SYSTEM_START",          SERVICE_SYSTEM_START
"SERVICE_AUTO_START",            SERVICE_AUTO_START
"SERVICE_DEMAND_START",          SERVICE_DEMAND_START
"SERVICE_DISABLED",              SERVICE_DISABLED

Error control type
"SERVICE_ERROR_IGNORE",          SERVICE_ERROR_IGNORE
"SERVICE_ERROR_NORMAL",          SERVICE_ERROR_NORMAL
"SERVICE_ERROR_SEVERE",          SERVICE_ERROR_SEVERE
"SERVICE_ERROR_CRITICAL",        SERVICE_ERROR_CRITICAL


A.3.6.3 Shell Section Commands

The following commands can only be called from a shell section.

Detect Command

The Detect command interprets the lines of the specified detect section, which must be in the same INF file as the calling shell section. For lines containing the ? operator, Detect assigns to the specified key the string returned by the specified DLL function. Lines that do not contain the ? operator are ignored.

The syntax of the command is:

Detect SectionName

SectionName
Identifies the detect section.
Exit Command

The Exit command causes the Setup program to exit, when called from a shell section. The Exit command can also be used in an install section, where it functions as a return to the shell section that called it, and does not terminate Setup. The syntax of the command is:

Exit

Install Command

The Install command calls the specified install section, which must be in the same INF file as the calling shell section. The Setup program executes the lines of the install section, with control returning to the shell section when the install section exits. .

The syntax of the command is:

Install SectionName

SectionName
Identifies the install section.
Read-Syms Command

The Read-Syms command interprets the lines of the specified detect section, which must be in the same INF file as the calling shell section. For lines of the form: key = value, the Setup program creates a variable named key in the symbol table of the calling shell section. The value string is stored as the value of this variable. The ? operator, if present on any of the lines, is ignored.

The syntax of the command is:

Read-Syms SectionName

SectionName
Identifies the detect section.
Return Command

The Return command causes the flow of execution to return from the called shell section back to the calling shell section. The syntax of the command is:

Return [ReturnValue[,]]*

ReturnValue
Zero or more comma-separated or space-separated string values to return to the shell section that called this shell section.
Shell Command

The Shell command calls the specified shell section, which can be in the same or in a different INF file. The calling shell section can pass multiple arguments to the shell section, and it can receive multiple return values from the shell section.

The syntax of the command is:

Shell inf_file_path section_name [Args]*

inf_file_path
Specifies the full or partial path of the INF file in which the section is located. An empty string (““) indicates the current file.
section_name
Identifies the shell section (name in square brackets).
Args
Zero or more comma- or whitespace-separated arguments.


A.3.6.4 Displaying Dialogs

The INF script language has built-in commands, dialog templates, and shell scripts that provide a mechanism for displaying simple message box dialogs. To display custom dialogs, create a DLL that displays the dialog and returns the user’s input.

The subroutn.inf file located in the Windows NT System32 directory contains a SetupMessage shell section that displays a message box dialog. Call SetupMessage from a driver’s oemsetup.inf file using the following syntax:

shell “subroutn.inf” SetupMessage LangID Template Message

LangID
Specifies the language in which messages are displayed, for example, ENG, SPN. You can use the STF_LANGUAGE variable, which is set by Setup to specify the language selected by the user or the default language on the system.
Template
Specifies the number of buttons, the default button, and the icon. Specify one of the following:
WARNING
Displays OK and Cancel buttons, and the ! icon. The Cancel button is the default.
STATUS
Displays an OK button and the Info icon.
NONFATAL
Displays an OK button and the ! icon.
FATAL
Displays an OK button and the Stop icon
Message
Specifies the text to display.

SetupMessage returns two values. The first return value indicates success or failure. It returns STATUS_SUCCESSFUL, STATUS_FAILURE, or STATUS_NOLANGUAGE if the LangID argument specifies an unsupported language. If the first return value is STATUS_SUCCESSFUL, the second return value indicates the button pressed (either OK or CANCEL).

For example, these lines from an INF script display the dialog shown below, and handle the return values:

shell "subroutn.inf" SetupMessage $(!STF_LANGUAGE) WARNING+
              "Looks like trouble. Do you want to continue?"
ifstr(i) $($R0) != STATUS_SUCCESSFUL
    goto HandleError
endif
ifstr(i) $($R1) == "OK"
    goto ContinueOperation
else-ifstr(i) $($R1) == "CANCEL"
    goto BailOut
endif

For additional customization of the message box dialog, you can use the ui start command as shown in the following example. Note that the string specified after ui start is required, but has no significance.

read-syms CustomMessageDlg      ; setup the message box variables
ui start "CustomMessageBox"
ifstr(i) $(DLGEVENT) == "ABORT" ; DLGEVENT indicates button pressed
    goto AbortProcedure
else-ifstr(i) $(DLGEVENT) == "RETRY"
    goto RetryProcedure
else-ifstr(i) $(DLGEVENT) == "IGNORE"
    goto IgnoreProcedure
else
    exit
endif

[CustomMessageDlg]
DlgType = "MessageBox"
    STF_MB_TITLE = "Message Box Dialog"
    STF_MB_TEXT  = "Message Text"
    STF_MB_TYPE  = 6    ; display Abort, Retry, and Ignore buttons
    STF_MB_ICON  = 5    ; ! icon
    STF_MB_DEF   = 2    ; default button is Retry

For the MessageBox dialog, the STF_MB_TITLE variable is optional, but all other variables in the following table must be set. Determine the button pressed by examining the DLGEVENT variable, which is set to one of the following strings: “ABORT”, “CANCEL”, “IGNORE”, “NO”, “OK”, “RETRY”, or “YES”.

DlgType = MessageBox

STF_MB_TITLE = Title
Specifies the title of the dialog box. Can be blank.
STF_MB_TEXT = Message
Specifies the message text.
STF_MB_TYPE = Type
Specifies the combination of buttons. Type is one of the following:

1 OK button

2 OK and Cancel buttons

3 Yes and No buttons

4 Yes, No, and Cancel buttons

5 Retry and Cancel buttons

6 Abort, Retry, and Ignore buttons

STF_MB_DEF
Specifies the default button (1, 2, or 3)
STF_MB_ICON = Icon
Specifies the icon to display. Icon is one of the following:

1 No icon

2 Info icon

3 Stop icon

4 ? icon

5 ! icon


A.3.6.5 Install Section Commands

These commands can only be called from an install section.

CreateDir Command

The CreateDir command creates the specified directory, if it does not exist. If the path specifies more than one nonexistent directory, all are created. The syntax of the command is:

CreateDir Path [V]

FullPath
Specifies the full or partial path of the directory to create.
V
If specified, the Setup program displays an error dialog and terminates if the directory does not exist and cannot be created.
RemoveDir Command

The RemoveDir command deletes the specified directory, if it exists and is empty. The syntax of the command is:

RemoveDir Path [V]

FullPath
Specifies the path of the directory to remove.
V
If specified, the Setup program displays an error dialog and terminates if the directory both exists and cannot be deleted.
Commands to Copy Files

The INF script language provides a set of install section commands that work in conjunction with the [Source Media Descriptions] section to copy files from a distribution disk, a local hard disk, or a network path. The following are the basic steps for using these commands:

  1. Create one or more Files sections containing information about the files to be copied.

  2. Use the AddSectionFilesToCopyList, AddSectionKeyFileToCopyList, or AddNthSectionFileToCopyList commands to extract information from a Files section and add it to the Setup program’s copy list. If necessary, use the ClearCopyList command to remove all copy list entries without copying the files.

  3. Use the CopyFilesInCopyList command. It copies the files specified in the copy list, and then clears the information in the copy list.

All three Add..ToCopyList commands specify the name of the Files section, and the source and destination directories of the files to be copied. The three commands provide different ways to extract files from the Files section. AddSectionFilesToCopyList adds all files in the section, AddSectionKeyFileToCopyList adds the file on the section line identified by a specified key, and AddNthSectionFileToCopyList adds the file on the section line specified by number, where the lines are numbered beginning with 1. The syntax for the CopyList commands is:

AddSectionFilesToCopyList SectionName SrcDir DestDir

AddSectionKeyFileToCopyList SectionName LineKey SrcDir DestDir

AddNthSectionFileToCopyList SectionName LineIndex SrcDir DestDir

CopyFilesInCopyList

ClearCopyList

A Files section line has one of two formats. The first format specifies another Files section from which to extract files.

[Key =] @(SectionName) [, @(LineKey)]

Key
An optional string that identifies the line.
SectionName
Identifies the Files section.
LineKey
Specifies a line in the Files section. If not specified, all files in the Files section are included.

The second format identifies a file and specifies how to copy it.

[Key =] DiskID, FileName, [Option ...]

Key
An optional string that identifies the line.
DiskID
An integer ID that identifies the distribution disk on which the source file is located. This ID must match a LineKey in the [Source Media Description] section. If the file is not being copied from a removable disk, the disk description and tagfile name on the [Source Media Description] section line are ignored.
FileName
Specifies the base file name and extension (must be 8.3 FAT format) of the source file.
Option
One or more of the options (in any order) described in the next table.

The CopyList mechanism for copying files provides a number of options that determine whether a file is copied, and, if so, how the file is copied. The default behavior is as follows:

  • The source file is copied with the same name to the destination directory.

  • If a file with the same name exists in the destination directory, it is overwritten.

  • Any attributes of the source file are copied to the destination file.

You can override these defaults by specifying an option on a Files section line or by setting a global variable (for example, STF_OVERWRITE or STF_UPGRADEONLY). The global STF_ variables specify a different default behavior, which can still be overridden by specifying an option on a Files section line.

Following are the options that can be specified on a Files section line. Note that all file names must use the 8.3 FAT format.

APPEND=DestFile
Appends the contents of the source file to the destination file specified by DestFile. Cannot be used with BACKUP or RENAME options.
BACKUP=SaveFile
If the destination file exists, renames it with name specified by SaveFile. If SaveFile = *, renames the destination file by appending the .bak extension. If a file with the backup name exists, the destination file is not backed up. Cannot be used with APPEND option.
!COPY
The file specified on this section line is not copied.
DATE=YYYY-MM-DD
Used with the OVERWRITE=OLDER option in an update situation to ensure that the destination file is overwritten only if its last write date is earlier than that specified by DATE. YYYY is >= 1980 and <= 2099, MM is in [01..12], and DD is in [01..31].
DECOMPRESS
Decompresses the source file before copying it. If STF_DECOMPRESS is defined to make decompression the default, you can use !DECOMPRESS to override it for a specific file.
DESTINATION=Path
Overrides the destination path specified in the Add..ToCopyList command line, or path specified by STF_DEST. Path must be full path with drive, for example, c:\windows\system32.
OVERWRITE=Option
Specifies when an existing destination file is overwritten. Option is one of the following:
ALWAYS
Always overwrites.
NEVER
Never overwrites. Same as !OVERWRITE.
OLDER
Overwrites if version of destination file is lower than version specified in VERSION option. If VERSION option not used, overwrites if date of destination file is older than date specified in DATE option.
VERIFYSOURCEOLDER
Compares last write times of source and destination files, and overwrites if destination is older.
UNPROTECTED
Overwrites unless destination file is read-only.
READONLY
Sets the read-only attribute of the destination file. If STF_READONLY is defined to make read-only the default, you can use !READONLY to override it for a specific file.
RENAME=NewName
Renames the destination file with the name specified by NewName. This option is useful for specifying the new name of a driver’s oemsetup.inf file. Cannot be used with APPEND options.
SETTIMESTAMP
Sets the create, last access, and last write times of copied files to those of the source file. Note that the FAT file system supports only last write times which are always copied.
SIZE=Size
Estimate of the file size, which Setup uses during copying to display a progress bar graph. Alternative is to use TIME option. If neither SIZE nor TIME is specified, progress is based on number of files copied.
TIME=CopyingTime
Estimate of the time to copy the file, which Setup uses during copying to display a progress bar graph.
UPGRADEONLY
Copies the file only if destination file exists. If STF_UPGRADEONLY is defined to make this the default, you can use !UPGRADEONLY to override it for a specific file.
VERSION=A,B,C,D
Used with the OVERWRITE=OLDER option in an update situation to ensure that the destination file is overwritten only if its version is lower than that specified by VERSION. A is the high-order and B the low-order 16 bits of the most significant portion, and C and D are the high- and low-order 16 bits of the least-significant portion of the file’s binary version number.
VITAL
If specified, Setup displays an error dialog and terminates if the file cannot be copied.
Default Copy Flags

Use the following variables to set the default copy flags. For example, the following INF file line sets the OVERWRITE mode:

set STF_OVERWRITE = “VERYIFYSOURCEOLDER”

The variables for default copy flags include:

STF_COPY = ““
Turns off copying. No files in the copy list are copied, except those that explicitly specify the COPY option.
STF_DATE = YYYY-MM-DD
Sets the default date to compare with if OVERWRITE=OLDER option is specified. Override by DATE option. Default is “1980-01-01”.
STF_DECOMPRESS = 1
Source files are decompressed before copying. Override by !DECOMPRESS.
STF_DEST = DestDir
Overrides the destination path specified on Add..ToCopyList command line. Must be full path including drive letter. Override by DESTINATION option.
STF_OVERWRITE = Option
Sets the default overwrite mode. One of the following is specified: ALWAYS, NEVER, OLDER, VERIFYSOURCEOLDER, UNPROTECTED
STF_READONLY = 1
Makes it the default to set the read-only attribute of destination files. Override by !READONLY.
STF_UPGRADEONLY = 1
Copies a source file only if the destination file exists. Override by !UPGRADEONLY.
STF_VERSION = A,B,C,D
Sets the default version to compare with if OVERWRITE=OLDER option is specified. Override by VERSION option.
STF_VITAL = 1
If a file cannot be copied, Setup displays an error dialog and terminates. Override by !VITAL.


Chapter 1 NT Drivers

This chapter contains the following information:
1.1 Kinds of Drivers in Windows NT
1.2 NT Driver Design Goals
1.2.1 Portability

1.2.2 Configurability

1.2.3 Always Preemptible and Always Interruptible

1.2.4 Multiprocessor-safe

1.2.5 Object-based

1.2.6 Packet-driven I/O with Reusable IRPs

1.2.7 Supporting Asynchronous I/O

Figure 1.1 illustrates the major components of the Microsoft® Windows NT® operating system environment.

Figure 1.1 Windows NT® Component Overview

As Figure 1.1 shows, this environment includes some components that run in user mode and others that run in kernel mode. File system, intermediate, and device drivers are shown at the lower left, included with the kernel-mode I/O Manager.

This chapter describes what Windows NT kernel-mode drivers are, summarizes the design goals for kernel-mode drivers, and explains how Windows NT kernel-mode drivers necessarily differ from other operating systems’ drivers in order to meet these design goals.

For more general information about the Windows NT operating system environment, see Inside Windows NT, published by Microsoft Press.


1.1 Kinds of Drivers in Windows NT

Within the Windows NT operating system, there are two basic kinds of drivers:

  • User-mode drivers, such as Win32 multimedia drivers, VDDs for MS-DOS® applications with application-dedicated devices, or another protected subsystem’s drivers

    User-mode drivers are subsystem-specific and are not discussed here. See the Multimedia Drivers and Virtual DOS Drivers documentation in the DDK for more information.

  • Kernel-mode drivers for logical, virtual, or physical devices

    These are called NT drivers, because they are part of the Windows NT executive: the underlying, “new technology” microkernel-based operating system that supports one or more protected subsystems. In the rest of this material, NT is frequently used to mean the Windows NT executive.

As Figure 1.1 shows, NT includes a number of kernel-mode components with well defined functionality isolated in each component. Those of most interest to NT device driver writers are the Kernel, I/O Manager, Hardware Abstraction Layer (HAL), Configuration Manager, Memory Manager, Executive Support, and Process Structure components. Additional components of interest to some NT driver writers include the Object Manager and Security Reference Monitor. Also of interest to NT file system driver writers is the Cache Manager, which is not shown in Figure 1.1.

Like NT itself, NT drivers are implemented as discrete, modular components with a well defined set of required functionality. All NT drivers have a set of system-defined standard driver routines and some number of internal routines as determined by the driver writer.

There are three basic types of NT drivers. Each type has a slightly different structure and quite different functionality:

  • Device drivers, such as a keyboard or disk driver that directly controls a physical device

    Device drivers are sometimes called lowest-level drivers, particularly when such a driver is the lowest driver in a chain of layered NT drivers.

  • Intermediate drivers, such as a virtual disk, mirror, or device-type-specific class driver, that depend on support from underlying device drivers

  • File system drivers (FSDs), such as the system-supplied FAT, HPFS, NTFS, or CDFS drivers, that also depend on support from underlying lower-level drivers

    While a particular NT file system driver might or might not get support from one or more intermediate drivers, every NT file system driver ultimately depends on support from one or more device drivers.

Windows NT network drivers also can be classified as one of these types of drivers. For example, an NT server or redirector is a specialized file system driver, a transport driver is a type of intermediate NT driver, and a physical netcard (sometimes called a media access controller) driver is an NT device driver. However, NT provides specialized interfaces and support for network drivers, such as NDIS 3.0 (Network Device Interface Specification, Version 3.0) for drivers of physical net cards.

While this documentation provides some useful overviews and background information for NT file system and network driver writers, it is primarily a design guide for writers of NT device and intermediate drivers. Network driver writers can consult the relevant Windows NT Device Driver Kit (DDK) documentation.


1.2 NT Driver Design Goals

NT drivers share many of the design goals of NT, in particular, those of the NT I/O Manager. These design goals include the following:

  • Portability from one platform to another

  • Configurability of hardware and software

  • Always preemptible and always interruptible

  • Multiprocessor-safe on multiprocessor platforms

  • Object-based

  • Packet-driven I/O with reusable I/O request packets (IRPs)

  • Support for asynchronous I/O

The following sections explain some of the implications of these design goals for NT driver writers.


1.2.1 Portability

Windows NT exports two system-specific runtime libraries, which contain functions that begin with the prefix Rtl. NT drivers can use the same kernel-mode Rtl Xxx routines that the executive components do, but NT drivers cannot call user-mode Rtl Xxx routines because NT drivers always run in kernel mode. NT file system drivers can also call the system-supplied file system runtime library’s FsRtl Xxx routines.

Each executive component exports a set of kernel-mode support routines that NT drivers and all other kernel-mode components call. If the underlying implementation of such a support routine changes over time, its callers remain portable because the interface to the defining component does not change.

The NT kernel does not support performing floating-point operations in kernel mode code. A kernel-mode driver cannot do floating-point operations without causing a system crash.

Most NT components are coded entirely in C, with only small pieces of the HAL and Kernel written in assembly language, so that Windows NT is readily portable across CISC- and RISC-based platforms. An NT driver should also be written in C so that it can be recompiled with an system-compatible C compiler, relinked, and run on different Windows NT platforms without recoding parts of the driver or replacing modules in the driver.

Driver writers should not use the features of any particular system-compatible C compiler or C support library if they cannot assume that the same features will be supported by other system-compatible compilers. In general, NT driver writers should write code conforming to the ANSI C standard but avoid writing code with dependencies on anything this standard describes as “implementation defined.”

In particular, NT driver writers must avoid the following:

  • Dependencies on data types whose size or layout can vary from one platform to another

  • Calling any standard C runtime library function that maintains state

  • Calling any other standard C runtime library function for which NT provides an alternative function

The DDK supplies a set of include files defining NT-specific data types and constants that drivers (and all other NT kernel-mode components) use to help maintain portability from one NT platform to another. Most NT drivers include the master DDK kernel-mode include file, ntddk.h, which pulls in both ntdef.h for these basic NT types and appropriate selections from a processor-architecture-specific include file when each driver is compiled with the corresponding compiler directive. Note that i386.h covers Intel 80386-based and subsequent x86-based Windows NT platforms, while the header files for RISC processors also can cover a family of microprocessors.

NT driver writers should isolate any platform-dependent definitions within #ifdef statements, so that each driver can be compiled and linked for the appropriate CISC-based or RISC-based Windows NT platform.

Certain NT drivers include other system-supplied header files to maintain portability across Windows NT platforms, in particular, SCSI and video miniport drivers. For more information about the header files for these types of NT drivers, see Appendix A and the Graphics Driver Design Guide, respectively.


1.2.2 Configurability

Because Windows NT is a portable operating system, designed to run on both CISC-based and RISC-based machines, devices and their NT drivers must be both hardware-configurable and software-configurable.

For example, a given disk device might be attached to either of two EISA buses in one Windows NT machine, but the same device might be limited to a single EISA bus in another Windows NT machine. Such a device is hardware-configurable. To remain portable, its driver must not contain hard-coded machine-dependent values.

The driver of such a disk device might provide hardware-level support to one or more NT file systems in any given machine, depending on how the user partitions the disk. In addition, an NT intermediate driver might support fault tolerance (mirrored partitions, stripe sets, and/or volume sets) for higher-level file system drivers in machines with sufficient mass storage capacity. In other words, the same physical disk driver could provide support to more than one type of higher-level driver in some Windows NT machines but not in others. Such a disk driver is software-configurable, as are all intermediate and file system drivers that ultimately depend on support from NT device drivers.

The NT HAL component, implemented as a dynamic-link library, is responsible for all hardware-level, platform-specific support needed by every other component in the system, including NT drivers. The HAL exports support routines that hide platform-specific hardware details about caches, I/O buses, interrupts, and so forth, to provide an interface between the platform’s hardware and the system’s software. For example, an NT device driver must call a HAL-supplied routine to map the bus-relative interrupt vector at which its device interrupts in a given machine to the system-assigned vector the driver will use.

The NT Configuration Manager component provides a database, called the registry, of information about the hardware, peripheral devices, and drivers in a given machine. NT drivers can use the registry to get information about both the hardware configuration of the machine and other drivers in the system. NT drivers also supply information about their devices and machine-specific configurations in the registry.


1.2.3 Always Preemptible and Always Interruptible

The NT Kernel component determines when a particular sequence of code is run according to one of the following prioritizing criteria:

  • The Kernel-defined runtime priority scheme for threads

    Every thread in the system has an associated priority attribute. In general, most threads in the system have variable priority attributes: they are always preemptible and are scheduled to run round-robin with all other threads currently at the same priority level. Some threads in the system have real-time priority attributes: these time-critical threads are preemptible by any thread with a higher real-time priority attribute, but otherwise run until they relinquish control.

    Whatever its priority attribute, any thread in the system can be preempted when hardware and certain kinds of software interrupts occur.

  • The Kernel-defined interrupt request level (IRQL) to which a particular interrupt vector is assigned on a given platform

    The NT Kernel also prioritizes hardware and software interrupts so that some kernel-mode code runs at higher IRQLs, thereby making it, including most NT drivers, have a higher scheduling priority than all threads in the system.

    The particular IRQL at which a piece of kernel-mode code executes determines its hardware priority. Such a piece of code is always interruptible: an interrupt with a higher IRQL value can occur at any time, thereby causing another piece of kernel-mode code with the system-assigned, higher IRQL to be run immediately on that processor. In other words, when a piece of code runs at a given IRQL, the Kernel masks off all interrupt vectors with a lesser or equal IRQL value on the processor.

In general, threads run at PASSIVE_LEVEL IRQL: no interrupt vectors are masked. Software interrupts are assigned relatively low IRQL values (APC_LEVEL, DISPATCH_LEVEL, or, for kernel debugging, WAKE_LEVEL). Device interrupts have higher IRQL values, and the Kernel reserves the highest IRQL values for system-critical interrupts such as the system clock or bus-error interrupts.

Some kernel-mode support routines also run at IRQL PASSIVE_LEVEL both because kernel-mode components can set up their own threads and because some kernel-mode support routines are implemented as pageable code.

Certain standard driver routines usually run at IRQL PASSIVE_LEVEL, as well. However, most NT driver routines run either at IRQL DISPATCH_LEVEL or, for a device driver, at device IRQL (also called DIRQL).

For performance reasons (avoiding context switches), very few NT drivers actually set up their own threads. Consequently, NT driver routines usually execute in the context of whatever thread happens to be current when the driver is called to get some work done: that is, in an arbitrary thread context.

In general, only a highest-level NT driver, such as a file system driver, is called in the context of a thread that is requesting the current I/O operation. An intermediate or lowest-level device driver can never assume that it is executing in the context of the thread that requested its current I/O operation.

While any kernel-mode routine that is run at higher than PASSIVE_LEVEL IRQL has a higher priority than all threads in the system, every routine in an NT driver is interruptible: any kernel-mode routine running at a particular IRQL retains control of the processor only if no interrupt with a higher IRQL value occurs while that routine is running.

Even an NT device driver’s interrupt service routine (ISR) can be interrupted by another routine (for example, by another driver’s ISR) that runs at higher IRQL. Unlike the drivers in some PC operating systems, an NT driver’s ISR is not the workhorse routine that does almost all I/O processing because an NT driver’s ISR does not necessarily retain control of the CPU it is currently running on until it returns.

Instead, an NT device driver must carry out most of its I/O operations at a lower IRQL than the DIRQL of its ISR. For good overall system performance, all kernel-mode routines that run at high IRQLs must relinquish control of the CPU very quickly. Such routines do only what must be done at high IRQL and generally queue a deferred procedure call (DPC) to complete any operations that can be done at a lower IRQL (DISPATCH_LEVEL).

For an introduction to the system-defined standard routines for NT drivers, see Chapter 2. For an overview of these routines, see Chapter 4.


1.2.4 Multiprocessor-safe

In any Windows NT multiprocessor platform, the following conditions hold:

  • All CPUs are identical, and either all have identical coprocessors or none has a coprocessor.

  • All CPUs share memory and have uniform access to memory.

  • In a symmetric platform, every CPU can access memory, take an interrupt, and access I/O control registers. In an asymmetric platform, one CPU takes all interrupts for a set of slave CPUs.

Windows NT is designed to run unchanged on uniprocessor and symmetric multiprocessor platforms, and NT drivers should be designed to do likewise.

To run safely on a symmetric multiprocessor platform, any operating system must solve this problem: how to guarantee that code executing on one processor does not simultaneously access and modify data that is being accessed and modified from another processor. For example, an NT device driver’s ISR that is handling a device interrupt on one processor must have exclusive access to critical, driver-defined data (or the device registers) in case its device interrupts simultaneously on another processor.

Furthermore, NT drivers’ I/O operations that are serialized in a uniprocessor machine can be overlapped in a symmetric multiprocessor machine. That is, a given driver’s routine that processes incoming I/O requests can be executing on one processor while another routine that communicates with the device executes concurrently on another processor. Whether they are running on a uniprocessor or multiprocessor machine, this situation requires NT drivers to synchronize access to any driver-defined data or system-provided resources that are shared among driver routines and to synchronize access to the physical device, if any.

The NT Kernel component exports a mechanism, called a spin lock, that is used to protect shared data (or device registers) from simultaneous access by one or more routines running concurrently on a symmetric multiprocessor platform. The Kernel enforces two policies regarding the use of spin locks:

  • One and only one routine can hold a particular spin lock at any given moment, and only the holder of a spin lock can access the data it protects. Another routine must acquire the spin lock in order to access the same data, and the spin lock cannot be acquired until the current holder releases it.

  • Like a hardware or software interrupt vector, the Kernel assigns each spin lock in the system an associated IRQL value. A kernel-mode routine can acquire a particular spin lock only when the routine runs at the spin lock’s IRQL.

These policies prevent an NT driver routine that usually runs at a lower IRQL but is currently holding a spin lock from being preempted by a higher priority driver routine that is trying to acquire the same spin lock, thereby causing a deadlock.

The IRQL assigned to a spin lock is generally that of the highest-IRQL routine that can acquire the spin lock. For example, an NT device driver’s ISR frequently shares a storage area with the driver’s DPC routine, which calls a driver-supplied critical-section routine to access the shared area. In this case, the spin lock protecting the shared area has an IRQL equal to the DIRQL at which the device interrupts. While the critical-section routine holds the spin lock and accesses the shared area at DIRQL, the ISR cannot be run in a uniprocessor machine because the device interrupt is masked off, as already mentioned in Section 1.2.3. While the critical-section routine holds the spin lock and accesses the shared data at DIRQL, the ISR still cannot acquire the spin lock in a symmetric multiprocessor machine.

Note that a set of kernel-mode threads can synchronize access to shared data or resources by waiting on one of the NT Kernel’s dispatcher objects: an event, mutex, semaphore, timer, or another thread. However, most NT drivers do not set up their own threads because they get better performance by avoiding context switches. Consequently, time-critical kernel-mode support routines and NT drivers must use the Kernel’s spin locks to synchronize access to shared data or resources whenever they run at IRQL DISPATCH_LEVEL or at DIRQL.

For more information about using spin locks and managing IRQLs, see Chapter 16. For more information about the Kernel’s dispatcher objects, see Chapter 3.


1.2.5 Object-based

Windows NT is an object-based system. Various components in the executive define one or more object types. Each NT component exports kernel-mode-only support routines that manipulate instances of its object types when these routines are called. No NT component is allowed to access any instance of another component’s object types directly. Each component must call the exported support routines in order to use another component’s objects.

Strict adherence to these conventions allows Windows NT to be both portable and flexible. For example, a future release of Windows NT could contain a wholly or partially recoded NT Kernel component that defines the same object types, possibly with entirely different internal structures, and exports a set of support routines with the same names and parameters as the existing set. Such a new version of the NT Kernel would have no effect on the portability of any other executive component in the existing system. In other words, the NT components do not practice back-door communication, and NT drivers also must eschew this practice in order to remain portable and configurable.

NT drivers and their devices are also object-based. To all other components in the system, including user-mode code, a device is represented as one of the NT I/O Manager’s file objects. Within the I/O system, each driver’s logical, virtual, and/or physical devices are represented as device objects. Within the I/O Manager, each NT driver’s load image is represented as a driver object. The I/O Manager defines the object types for file objects, device objects, and driver objects, as well as for adapter objects to represent system DMA controllers or busmaster DMA adapters and for controller objects to represent a physical controller for similar devices.

Like any other executive component, NT drivers use NT objects by calling kernel-mode support routines exported by the I/O Manager and other NT components. NT kernel-mode support routines generally have names that identify the specific object each manipulates and the operation each performs on that object. These support routine names have the form:
PrefixOperationObject
where
Prefix
Identifies the NT component that exports the routine and, usually, the component that defined the object type. Most prefixes have two letters.

Operation
Describes what is done to the object.

Object
Identifies the type of object.

For example, IoCreateDevice, which each NT driver calls one or more times when it initializes, creates a device object to represent a physical, logical, or virtual device.

For convenience, one NT component can export routines that call another component’s support routines. The I/O Manager, in particular, exports certain routines that make it easier to develop NT drivers. For example, IoConnectInterrupt, which device drivers call at initialization to register their ISRs, calls the NT Kernel’s support routines for interrupt objects.

The Glossary contains definitions for the set of NT objects most useful to NT driver writers. For detailed information about the support routines that are most useful to NT device and intermediate drivers, see the Kernel-mode Driver Reference.


1.2.6 Packet-driven I/O with Reusable IRPs

The I/O Manager’s chief job is to accept I/O requests (usually from user-mode applications), to create IRPs to represent them, to route the IRPs to the appropriate NT drivers, to track them until they are completed, and to return status to the original requestor of each I/O operation. The I/O Manager uses IRPs to communicate with NT drivers and to allow NT drivers to communicate with each other.

Note that some IRPs might be routed to more than one NT driver. For example, a request to open a file on a disk might go first to a file system driver, through an intermediate mirror driver, and ultimately to a physical disk driver, as implied in Sections 1.1 and 1.2.2.

Therefore, each IRP has a fixed part and one or more driver-specific I/O stack locations:

  • In the fixed part (or header), the I/O Manager maintains information about the original request, such as the caller’s parameters, the address of the device object on which a file is open, etc. The fixed part also contains an I/O status block, in which drivers set information about the status of the requested operation.

  • In the highest-level driver’s I/O stack location, the I/O Manager sets driver-specific parameters, such as the particular operation requested (represented as function codes) and context used by the corresponding driver to determine what it is supposed to be doing. In turn, each higher-level NT driver sets up the I/O stack location of the next-lower-level driver, if any.

As a given IRP is processed through each NT driver’s set of standard routines, each routine can access that driver’s I/O stack location in the IRP, thereby reusing the IRP at each stage of the driver’s operations. In addition, higher-level NT drivers can create (or reuse) IRPs to send requests down to lower-level drivers.

For an overview of the processing of IRPs through layered drivers, see Chapter 2. For device-type-specific information about IRPs, see the Kernel-mode Driver Reference.


1.2.7 Supporting Asynchronous I/O

The I/O Manager provides asynchronous I/O support so that the originator of an I/O request (usually a user-mode application, but sometimes another driver) can continue executing, rather than waiting for its I/O request to be completed. Providing asynchronous I/O support improves overall system I/O throughput, as well as the performance of any code that makes an I/O request.

As a consequence, NT drivers do not necessarily process I/O requests in the same order they were sent to the I/O Manager. The I/O Manager or a higher-level driver can reorder I/O requests as they are received or can split a large data transfer request into smaller transfer requests. Moreover, an NT driver can overlap I/O request processing, particularly in a symmetric multiprocessor platform, as mentioned in Section 1.2.4.

Note that an NT driver’s processing of a given I/O request is not necessarily serialized. That is, an NT driver does not process each IRP to completion before it starts processing the next request, as drivers naturally do in a single-tasking operating system written to support synchronous I/O in uniprocessor machines.

Instead, NT drivers respond to the current IRP as it is passed to the driver’s standard routines by carrying out whatever routine-specific operations are necessary to satisfy the current request. However, a lowest-level NT driver is required to help the I/O Manager and any higher-level drivers processing the same IRPs to track the status of each request by setting the I/O status block in the IRP.

NT drivers also can maintain state information about their current I/O operations in a special part of their device objects, called a device extension.

For an overview of IRP processing and of how device objects represent the physical and logical devices of NT drivers, see Chapter 2. For more detailed information about device objects and device extensions, see Chapter 3.


Chapter 2 Layered I/O, IRPs, and I/O Objects

This chapter contains the following information:
2.1 End-user I/O Requests and NT File Objects

2.1.1 Points to Consider about User I/O Requests

2.2 IRPs and Driver-specific I/O Stack Locations
2.2.1 Points to Consider about Processing IRPs
2.3 NT Driver Objects and Standard Driver Routines
2.3.1 NT Object Opacity

2.3.2 Driver Object Entry Points

2.3.3 Other Standard NT Driver Routines

2.3.4 Points to Consider about NT Standard Driver Routines

2.4 Device Configurations and Layered NT Drivers
2.4.1 Interactive Devices

2.4.1.1 Video Adapter Configurations and Driver Layers

2.4.1.2 Keyboard and Mouse Configurations and Driver Layers

2.4.2 Parallel and Serial Devices and Driver Layers

2.4.3 Sound Device and Driver Layers

2.4.4 Mass-storage Devices and Driver Layers

2.4.4.1 AT Disk Devices

2.4.4.2 Floppy Devices

2.4.4.3 SCSI Devices

2.4.5 Points to Consider about Adding NT Drivers

2.5 NT Objects with Device, Configuration, or Layer Dependencies
2.5.1 Video Driver’s Device Objects

2.5.2 Keyboard and Mouse Drivers’ Device Objects

2.5.3 Parallel and Serial Drivers’ Device Objects

2.5.4 Sound Driver’s Device Objects

2.5.5 AT Disk Driver’s Device and Controller Objects

2.5.6 Floppy Driver’s Device Objects

2.5.7 SCSI Drivers’ Device Objects

2.5.8 Points to Consider about NT Objects

Every operating system has an implicit or explicit I/O model for handling the flow of data to and from peripheral devices. The Windows NT® executive I/O model has the following general features:

  • The NT I/O Manager presents a consistent interface to all kernel-mode drivers, including device, intermediate, and file system drivers. All I/O requests to NT drivers are sent as I/O request packets (IRPs).

  • I/O operations are layered. That is, the I/O Manager exports system services, which user-mode protected subsystems call to carry out I/O operations on behalf of their applications and/or end users. The I/O Manager intercepts these calls, sets up one or more IRPs, and routes them through possibly layered NT drivers to physical devices.

  • The I/O Manager defines a set of standard routines, some optional, for NT driver writers to implement. All NT drivers follow a relatively consistent implementation model, given the differences among peripheral devices and the differing functionality required of physical, virtual, logical, and file system drivers.

  • Like NT itself, NT drivers are object-based. Drivers, their devices, and system hardware are represented as NT objects. The I/O Manager and other NT components export kernel-mode support routines that NT drivers can call to get work done by manipulating the appropriate objects.

This chapter introduces the NT I/O model, supplying an overview of how kernel-mode drivers fit into the system, of how NT drivers process IRPs, of the system-defined standard driver routines, of common device configurations and corresponding layered drivers, and of the NT objects that represent devices, drivers, and system I/O hardware.


2.1 End-user I/O Requests and NT File Objects

NT drivers are hidden from end users by a Windows NT protected subsystem that implements an already familiar programming interface, such as Windows® or POSIX. Devices are visible to user-mode code, which includes protected subsystems, only as named file objects controlled by the NT I/O Manager.

Figure 2.1 illustrates this relationship between an end user, a subsystem, and the NT I/O Manager.

Figure 2.1 NT File Objects Represent Files, Volumes, and Devices

A Windows NT protected subsystem, such as the Win32® subsystem, passes I/O requests to the appropriate kernel-mode driver through the I/O system services. The subsystem shown in Figure 2.1 depends on support from the NT display, video adapter, keyboard, and mouse device drivers.

A protected subsystem insulates its end users and applications from having to know anything about kernel-mode components, including NT drivers. In turn, the NT I/O Manager insulates protected subsystems from having to know anything about machine-specific device configurations or about NT drivers’ implementations.

The NT I/O Manager’s layered approach also insulates most NT drivers from having to know anything about:

  • Whether an I/O request originated in any particular protected subsystem, such as Win32 or POSIX

  • Whether a given protected subsystem has particular kinds of user-mode drivers

  • What any protected subsystem’s I/O model and interface to drivers is

The I/O Manager supplies NT drivers with a single I/O model, a set of kernel-mode support routines these drivers can use to carry out I/O operations, and a consistent interface between the originator of an I/O request and the NT drivers that must respond to it.

As shown in Figure 2.1, a subsystem and its native applications can access an NT driver’s device or a file on a mass-storage device only through file object handles supplied by the NT I/O Manager. A subsystem’s request to open such a file object and to obtain a handle for I/O to a device or a data file is made by calling the NT I/O system services to open a named file, which can have a subsystem-specific alias (symbolic link) to the kernel-mode name for the file object.

The NT I/O Manager, which exports these system services, is then responsible for locating or creating the file object that represents the device or data file and for locating the appropriate NT driver(s). Figure 2.2 shows an overview of what happens when a subsystem opens a file object representing a data file on behalf of an application.

Figure 2.2 Opening an NT File Object

  1. The subsystem calls an NT I/O system service to open a named file.

  2. The NT I/O Manager calls the Object Manager to look up the named file and to help it resolve any symbolic links for the file object. It also calls the Security Reference Monitor to check that the subsystem has the correct access rights to open that file object.

  3. If the volume is not yet mounted, the I/O Manager suspends the open request, calling one or more NT file systems until one of them recognizes the file object as something it has stored on one of the mass-storage devices the file system uses. When the file system has mounted the volume, the I/O Manager resumes the request.

  4. The I/O Manager allocates memory for and initializes an IRP for the open request. To NT drivers, an open is equivalent to a “create” request.

  5. The I/O Manager calls the file system driver, passing it the IRP. The file system driver accesses its I/O stack location in the IRP to determine what operation it must carry out, checks parameters, determines if the requested file is in cache, and, if not, sets up the next-lower driver’s I/O stack location in the IRP.

  6. Both drivers process the IRP and complete the requested I/O operation, calling kernel-mode support routines supplied by the I/O Manager and by other NT components (not shown in Figure 2.2).

  7. The drivers return the IRP to the I/O Manager with the I/O status block set in the IRP to indicate whether the requested operation succeeded or why it failed.

  8. The I/O Manager gets the I/O status from the IRP, so it can return status information through the protected subsystem to the original caller.

  9. The I/O Manager frees the completed IRP.

  10. The I/O Manager returns a handle for the file object to the subsystem if the open operation was successful. If there was an error, it returns appropriate status to the subsystem.

After a subsystem successfully opens a file object that represents a data file, a device, or a volume, the subsystem uses the returned file object handle to request that device I/O operations (usually read, write, or device I/O control requests) be carried out by calling the I/O system services. The I/O Manager routes these requests as IRPs sent to appropriate NT drivers.


2.1.1 Points to Consider about User I/O Requests

Keep the following points in mind when designing an NT driver:

  • NT drivers can be layered, and more than one NT driver can process a single I/O request (IRP).

  • NT drivers communicate the success or failure of a requested I/O operation in the I/O status block of the IRP. The I/O Manager communicates the success or failure of a requested I/O operation to a user-mode requestor.

  • NT drivers need not and should not be designed to provide application-specific support. A protected subsystem or its subsystem-specific, user-mode drivers supply this kind of support. There is one exception to this rule: an MS-DOS® application that relies on an application-dedicated device can require an NT driver to control the device and a closely coupled Win32 user-mode virtual device driver (VDD). See the Virtual DOS Drivers documentation in the DDK for more information about VDDs..


2.2 IRPs and Driver-specific I/O Stack Locations

Figure 2.2 shows an IRP with two I/O stack locations, but an IRP can have any number of I/O stack locations, depending on how many layered drivers will handle a given request.

Figure 2.3 illustrates in more detail how the drivers of Figure 2.2 use I/O support routines (IoXxx routines) to process the IRP for a read or write request. Figure 2.3 also shows more detail of an IRP’s I/O stack location for a lowest-level driver, such as a physical disk driver.

Figure 2.3 Processing IRPs in Layered NT Drivers

  1. The I/O Manager calls the file system driver (FSD) with the IRP it has allocated for the subsystem’s read/write request. The FSD accesses its I/O stack location in the IRP to determine what operation it should carry out.

  2. The FSD can break the original request into smaller requests (possibly for more than one device driver) by calling an I/O support routine (IoAllocateIrp) one or more times to allocate IRPs, which are returned to the FSD with zero-filled I/O stack location(s) for lower-level driver(s). At its discretion, the FSD can reuse the original IRP, rather than allocating additional IRPs as shown in Figure 2.3, by setting up the next-lower driver’s I/O stack location in the original IRP and passing it on to lower drivers.

  3. For each driver-allocated IRP, the FSD in Figure 2.3 calls an I/O support routine to register an FSD-supplied completion routine so it can determine whether lower drivers satisfied the request and free each driver-allocated IRP when lower drivers have completed it. The I/O Manager will call the FSD-supplied completion routine whether each driver-allocated IRP was completed successfully, with an error status, or cancelled. A higher-level NT driver is responsible for freeing any IRPs it allocates and sets up on its own behalf for lower-level drivers. The I/O Manager frees the IRPs that it allocates after all NT drivers have completed them.

    Next, the FSD calls an I/O support routine to access the next-lower-level driver’s I/O stack location in its FSD-allocated IRP in order to set up the request for the next-lower driver, which happens to be the lowest-level driver in Figure 2.3. The FSD then calls an I/O support routine to pass that IRP on to the next driver.

  4. When it is called with the IRP, the physical device driver checks its I/O stack location to determine what operation (indicated by the IRP_MJ_XXX function code) it should carry out on the target device, which is represented by the device object in its I/O stack location and passed with the IRP to the driver. This driver can assume that the I/O Manager has routed the IRP to an entry point that the driver defined for the IRP_MJ_XXX operation (here IRP_MJ_READ or IRP_MJ_WRITE) and that the higher-level driver has checked the validity of other parameters for the request.

    If there were no higher-level driver, such a device driver would check whether the input parameters for an IRP_MJ_XXX operation are valid. If they are, a device driver usually calls I/O support routines to tell the I/O Manager that a device operation is pending on the IRP and to either queue or pass the IRP on to another driver-supplied routine that accesses the target device (here, a physical or logical device: the disk or a partition on the disk).

  5. The I/O Manager determines whether the device driver is already busy processing another IRP for the target device, queues the IRP if it is, and returns. Otherwise, the I/O Manager routes the IRP to a driver-supplied routine that starts the I/O operation on its device. (At this stage, both drivers in Figure 2.3 and the I/O Manager return control.)

  6. When the device interrupts, the driver’s interrupt service routine (ISR) does only as much work as it must to stop the device from interrupting and to save necessary context about the operation. The ISR then calls an I/O support routine with the IRP to queue a driver-supplied DPC routine to complete the requested operation at a lower hardware priority than the ISR.

  7. When the driver’s DPC gets control, it uses the context (passed in the ISR’s call to IoRequestDpc) to complete the I/O operation. The DPC calls a support routine to dequeue the next IRP (if any) and to pass that IRP on to the driver-supplied routine that starts I/O operations on the device (see Step 5). The DPC then sets status about the just completed operation in the IRP’s I/O status block and returns it to the I/O Manager with IoCompleteRequest.

  8. The I/O Manager zeroes the lowest-level driver’s I/O stack location in the IRP and calls the file system’s registered completion routine (see Step 3) with the FSD-allocated IRP. This completion routine checks the I/O status block to determine whether to retry the request or to update any internal state maintained about the original request and to free its driver-allocated IRP. The file system can collect status information for all driver-allocated IRPs it sends to lower-level drivers in order to set I/O status and complete the original IRP. When it has completed the original IRP, the I/O Manager returns NT status to the original requestor (the subsystem’s native function) of the I/O operation.

Figure 2.3 also shows two I/O stack locations in the original IRP because it shows two NT drivers, a file system driver and a mass-storage device driver. The I/O Manager gives each driver in a chain of layered NT drivers an I/O stack location of its own in every IRP that it sets up. The driver-allocated IRPs in Figure 2.3 do not have a stack location for the FSD that created them. Any higher-level driver that allocates IRPs for lower-level drivers also determines how many I/O stack locations the new IRPs should have, according to the StackSize value of the next-lower driver’s device object.

As shown in Figure 2.3, each driver-specific I/O stack location in an IRP contains the following general information:

  • The major function code (IRP_MJ_XXX), indicating the basic operation the driver should carry out

  • For some major function codes handled by FSDs and higher-level SCSI drivers, a minor function code (IRP_MN_XXX), indicating which sub-case of the basic operation the FSD should carry out

  • A set of operation-specific arguments, such as the length and starting location of a buffer into which or from which the driver transfers data

  • A pointer to the driver-created device object, representing the target (physical, logical, or virtual) device for the requested operation

  • A pointer to the file object, representing an open file, device, directory, or volume

    An NT file system driver accesses the file object through its I/O stack location in IRPs. Other NT drivers usually ignore the file object.

The set of IRP major and minor function codes that a particular NT driver handles can be device-type-specific. However, NT device and intermediate drivers usually handle the following set of basic requests:

  • IRP_MJ_CREATE - open the target device object, indicating that it is present and available for I/O operations

  • IRP_MJ_READ - transfer data from the device

  • IRP_MJ_WRITE - transfer data to the device

  • IRP_MJ_DEVICE_CONTROL - set up (or reset) the device, according to a system-defined, device-type-specific I/O control code

  • IRP_MJ_CLOSE - close the target device object

For more information about the major function codes and device I/O control codes that NT drivers for particular kinds of devices are required to handle, see the Kernel-Mode Driver Reference.

In general, the I/O Manager sends IRPs with at least two I/O stack locations to mass-storage device drivers because an NT file system is layered over NT drivers for mass-storage devices. The I/O Manager sends IRPs with a single stack location to any physical device driver that has no driver layered above it.

However, the NT I/O Manager provides support for adding a new driver to any chain of existing drivers in the system. For example, an intermediate mirror driver that backs up data on a given disk partition might be inserted between a pair of drivers, such as those shown in Figure 2.3. When this new driver attaches itself to the device driver, the I/O Manager adjusts the number of I/O stack locations in all IRPs it sends to the file system, mirror, and disk device drivers. Every IRP that the file system in Figure 2.3 allocated would also contain another I/O stack location for such a new mirror driver.

Note that this support for adding new NT drivers to an existing chain implies certain restrictions on any particular NT driver’s access to the I/O stack locations in IRPs:

  • A higher-level driver in a chain of layered NT drivers can safely access only its own and the next-lower-level driver’s I/O stack locations in any IRP. Such a driver must set up the I/O stack location for the next-lower-level driver in IRPs. However, the designer of such a higher-level driver cannot predict when (or whether) a new driver will be added to the existing chain just below the designer’s driver.

    Every writer of a higher-level NT driver must assume that any subsequently added driver will handle the same IRP major function codes (IRP_MJ_XXX) as the displaced next-lower-level driver did.

  • The lowest-level driver in a chain of layered NT drivers can safely access only its own I/O stack location in any IRP. The designer of such a driver cannot predict when (or whether) a new driver will be added to the existing chain above the designer’s device driver.

    Every writer of a lowest-level NT driver must assume that the driver can continue to process IRPs using the information passed in its own I/O stack location, whatever the originating source of a given IRP and however many drivers are layered above it.

Like the file system driver shown in Figure 2.3, any new driver that is added to a chain of existing drivers can do all of the following:

  • Set its own completion routine into an IRP that checks the I/O status block to determine whether lower drivers completed the IRP successfully, cancelled the IRP, and/or completed it with an error. Such a driver’s completion routine can also update any IRP-specific state the driver might have saved, release any operation-specific resources the driver might have allocated, and so forth, before completing the IRP. Such a completion routine can even reregister itself and reuse an incoming IRP to send another request to the next-lower-level driver before allowing the IRP to complete.

  • Call I/O support routines to allocate new IRPs. However, NT intermediate drivers, which are chained somewhere between a highest-level driver (such as the file system in Figure 2.3) and a lowest-level (physical device) driver, cannot call IoMakeAssociatedIrp. Only the highest-level driver in a chain can create associated IRPs because the I/O Manager automatically completes associated IRPs, and the original IRP (also called a “master IRP”) when all its associated IRPs are completed, as long as the allocating driver does not register its completion routine for an associated IRP. NT intermediate drivers call other support routines to create IRPs that they send down to lower drivers.

  • Set up the next-lower-level driver’s I/O stack location in the IRPs it allocates and send requests to the next-lower-level driver.

  • Pass any incoming requests on to lower drivers by setting up the next-lower driver’s I/O stack location in each IRP and calling IoCallDriver.

For specific information about the support routines that intermediate and lowest-level NT drivers call and about device-type-specific requests these drivers must handle, see the Kernel-Mode Driver Reference.

As shown in Figure 2.3, an NT file system is a two-part driver:

  1. A file system driver (FSD), which executes in the context of a user-mode thread that calls an I/O system service

    The I/O Manager sends the corresponding IRP to the FSD. If the FSD sets up a completion routine for an IRP, its completion routine is not necessarily called in the original user-mode thread’s context.

  2. A set of file system threads, and possibly an FSP (file system process)

    An FSD can create a set of driver-dedicated system threads, but most FSDs use system worker threads in order to get work done without tying up user-mode threads that make I/O requests. Any FSD might set up its own process address space in which its driver-dedicated threads execute, but the system-supplied FSDs avoid this practice to conserve system memory.

NT file systems generally use system worker threads to set up and manage internal work queues of IRPs that they send to one or more lower-level drivers, possibly for different devices.

While the physical device driver shown in Figure 2.3 processes each IRP in stages through a set of discrete, driver-supplied routines, it does not use system threads as the file system does. A physical device driver does not need its own thread context unless setting up its device for I/O is such a protracted process that it has a noticable effect on system performance. Few NT device or intermediate drivers need to set up their own driver-dedicated or device-dedicated system threads, and those that do pay a performance penalty caused by context switches to their threads.

Most NT drivers, like the physical device driver in Figure 2.3, execute in an arbitrary thread context: that of whatever thread happens to be current when they are called to process an IRP. Consequently, NT drivers usually maintain state about their I/O operations and the devices they service in a driver-defined part of their device objects, called a device extension.

Each driver-created device object represents a physical, logical, or virtual device for which a particular NT driver carries out I/O requests. For guidelines about how different kinds of device drivers use device objects to represent their respective physical, logical, and virtual devices, see Section 2.5 later in this chapter. For detailed information about creating and setting up a device object, see Chapter 3.

As Figure 2.3 also shows, most NT drivers process each IRP in stages through a driver-supplied set of system-defined standard routines, but drivers at different levels in a chain necessarily have different standard routines. For example, only lowest-level NT drivers handle interrupts from a physical device, so only a lowest-level driver would have an ISR and a DPC that completes interrupt-driven I/O operations. On the other hand, a lowest-level driver cannot register a completion routine for a given IRP as higher-level drivers can, so only a higher-level NT driver would have one or more completion routines like the FSD in Figure 2.3. See Section 2.3 for a brief introduction to the system-defined routines that NT drivers must or can have. See Chapter 4 for an overview of these standard driver routines and subsequent chapters for routine-specific requirements.


2.2.1 Points to Consider about Processing IRPs

Keep the following points in mind when designing an NT driver:

  • A new NT driver must handle the same set of IRP_MJ_XXX as any system-supplied driver it replaces. The I/O Manager returns STATUS_INVALID_DEVICE_REQUEST for a given I/O request to a target device if its driver does not define an entry point for that IRP_MJ_XXX. A device driver also must handle the same I/O control codes for IRP_MJ_DEVICE_CONTROL requests as any system-supplied driver it replaces. In other words, a new NT device driver must not “break applications” by implementing less functionality than an existing driver for the same type of device.

  • A new NT intermediate driver inserted into a chain of existing drivers should recognize the same set of IRP_MJ_XXX as the driver it displaces. Such a driver can simply pass on IRPs for those requests that it does not process to the next-lower-level driver. However, a new NT intermediate driver must not “break the chain” for drivers above and below it by neglecting to define an entry point for an IRP_MJ_XXX request that the newly displaced, next-lower-level driver does handle.

  • A lowest-level device driver can access only its own I/O stack location in any IRP that it is sent. A higher-level driver can access only its own and the next-lower-level driver’s I/O stack locations in any IRP that it is sent.

  • Every NT driver communicates information to higher-level drivers (and ultimately, to user-mode applications via the I/O Manager) only in the I/O status blocks of IRPs because the I/O Manager zeros the corresponding I/O stack location as each driver in a chain completes an IRP. Any new NT driver that attempts to implement back-door communication with a particular higher (or lower) driver compromises its portability and its interoperability with other NT drivers from one Windows NT platform or version to the next.

  • A pair of NT drivers can define a set of device-specific (also called private) I/O control codes for IRP_MJ_INTERNAL_DEVICE_CONTROL requests that the higher of the pair can send down to the lower of the pair. However, such a pair of drivers must follow all of the preceding guidelines if they are to remain portable and interoperable with other NT drivers from one Windows NT platform or version to another. If you design a pair of NT drivers with such a private interface, consider the set of I/O control codes to be defined carefully. Make them as generally useful as possible and design your paired drivers to follow the preceding guidelines, so that you (or someone else) can reuse, replace, or displace either or both of your new drivers easily as they migrate from one Windows NT platform or version to another.


2.3 NT Driver Objects and Standard Driver Routines

Figure 2.4 illustrates a driver object, representing an NT driver, with the set of system-defined (or standard) routines that lowest-level and higher-level drivers can or must have.

Figure 2.4 also shows which of these standard routines are passed an IRP when they are called. On entry, every standard routine that is passed an IRP is also given a pointer to the target device object for the I/O request.

Figure 2.4 NT Driver Object

The I/O Manager defines the driver object type and uses driver objects to register and track information about the loaded images of NT drivers. Note the one-to-one correspondence between the Dispatch entry points in the driver object and the major function codes (IRP_MJ_XXX) that are passed in the I/O stack locations of IRPs.

As shown previously in Figure 2.3, the I/O Manager routes each IRP first to a driver-supplied Dispatch routine, and a device driver’s Dispatch routine usually calls an I/O support routine (IoStartPacket) to queue (or pass on) each IRP with valid arguments to the driver’s StartIo routine, which starts the requested I/O operation on a particular device. Higher-level NT drivers usually do not have StartIo routines, but they can.

When an NT driver is loaded, its DriverEntry routine is called with a pointer to the driver object. The DriverEntry routine sets one or more Dispatch entry points in the input driver object so that the I/O Manager can route IRPs to the appropriate driver-supplied Dispatch routine. The DriverEntry routine also sets the driver’s StartIo and Unload entry points, if any, in the driver object.

A DriverEntry or optional Reinitialize routine also can use a field in the driver object (not shown in Figure 2.4) to get information from and/or set information in the NT Configuration Manager’s registry database. For more information about how NT drivers use the registry, see Chapter 16.


2.3.1 NT Object Opacity

Like all NT objects, a driver object is opaque: only the defining NT component (here, the I/O Manager) “knows” an object type’s internal structure and can access all the data an object contains directly. The defining NT component usually exports support routines that NT drivers and other NT components can call to manipulate that component’s objects. For example, the NT Kernel exports support routines that the I/O Manager calls to initialize and connect interrupt objects when a lowest-level NT driver registers its interrupt service routine (ISR), shown in Figure 2.4 as DDInterruptService.

To maintain driver portability, consider this implementation guideline concerning NT objects

NT drivers must use the system-supplied support routines to manipulate NT objects. The defining NT component can change the internal structure of its object types at any time.

However, the I/O Manager exports no support routines to manipulate driver objects. Driver objects are used only by the I/O Manager to keep track of currently loaded NT drivers. Some fields within a driver object are opaque: “known” only to the I/O Manager and used only by the I/O Manager. Others are partially opaque: NT driver writers must know certain field names to define Dispatch, StartIo, and Unload entry points and to use the registry. Nevertheless, NT driver writers should neither attempt to use unpublished fields within a driver object nor make assumptions about the locations of any driver object fields that are named in this documentation. Otherwise, NT driver writers cannot maintain the portability of their drivers from one Windows NT platform to another.

Unlike most NT objects, which are wholly opaque outside the defining NT component, the driver object is partially opaque to NT drivers because each driver becomes part of the NT I/O system when the driver is loaded.


2.3.2 Driver Object Entry Points

Except for video and SCSI miniport drivers, as explained later in Section 2.4, each NT driver must define the following entry points in its driver object:

  • At least one Dispatch entry point in order to get IRPs requesting I/O operations

  • If the driver can be loaded and/or replaced dynamically, an Unload entry point in order to free any resources that the driver has allocated before the driver is unloaded

    NT drivers that cannot be replaced while the system is running, such as a keyboard driver, need not supply an Unload routine.

While an NT device driver is not required to define a StartIo entry point if it is designed to set up and manage its own queue(s) of IRPs, most NT device drivers define a StartIo entry point in their driver objects and rely on the I/O Manager to queue IRPs bound for their StartIo routines. In fact, few lowest-level system-supplied drivers are designed without a StartIo routine, even when they set up and maintain their own supplemental queues for IRPs.

Higher-level NT drivers can have a StartIo routine but, for better performance, seldom do. Instead, most NT file system drivers set up and maintain internal queues of IRPs. Other higher-level NT drivers either have internal queues for IRPs or simply pass IRPs on to lower drivers from their Dispatch routines after setting up the I/O stack location for the next-lower driver in each IRP, and, possibly, setting up the higher-level driver’s IoCompletion routine for a given IRP.

When an NT driver’s DriverEntry routine is called, it sets Dispatch, StartIo (if any), and Unload (if any) entry points directly in the driver object as follows:
DriverObject->MajorFunction[IRP_MJ_xxx] = DDDispatchXxx;
              :    :
DriverObject->MajorFunction[IRP_MJ_yyy] = DDDispatchYyy;
              :    :
DriverObject->DriverStartIo = DDStartIo;
DriverObject->DriverUnload = DDUnload;
              :    :

An NT driver can define several Dispatch entry points, but it can define only one StartIo entry point and one Unload entry point in its driver object. At the driver writer’s discretion, an NT driver writer can implement one or more Dispatch routines and define corresponding entry points in the driver object as any one of the following:

  • A single Dispatch entry point for all IRP_MJ_XXX the driver handles

  • A set of Dispatch entry points for nonintersecting subsets of all IRP_MJ_XXX the driver handles

  • A separate Dispatch entry point for each IRP_MJ_XXX the driver handles


2.3.3 Other Standard NT Driver Routines

As Figure 2.4 shows, NT drivers have other standard routines along with those for which they set entry points in their respective driver objects. Most standard driver routines and some of the configuration-dependent objects they use are defined by the I/O Manager. The ISR, SynchCritSection routine, and those shown in Figure 2.4 with names containing the word “custom” are defined by the NT Kernel.

With few exceptions (SCSI and video miniport drivers), each NT driver’s DriverEntry routine must also create one or more device objects representing the physical, logical, or virtual device(s) for which it carries out I/O requests. As Figure 2.4 shows, the I/O Manager maintains information about driver-created device objects in the corresponding driver object.

Most NT drivers use the device extension of each device object they create to maintain device-specific state about their I/O operations and to store pointers to any system resources that they must allocate in order to have other standard routines. For example, the DDCustomTimerDpc routine shown in Figure 2.4 requires the driver to supply storage for Kernel-defined timer and DPC objects.

As mentioned in Section 2.2, the set of standard driver routines for lowest-level drivers shown on the left in Figure 2.4 is necessarily different from the set for higher-level drivers. Some of the routines shown in Figure 2.4 are device-dependent or configuration-dependent requirements. Others are optional: a driver writer may choose to implement such a routine depending on the nature or configuration of the driver’s device(s), on the driver’s design, and on the driver’s position in a chain of layered NT drivers.

In addition to the Dispatch, StartIo, and Unload routines for which NT drivers set entry points in their respective driver objects, the system-defined standard NT driver routines include the following:
Standard Routine | NT Driver Level
InterruptService | lowest-level only

Drivers for physical devices that generate interrupts must have an ISR. The ISR must stop the device from interrupting. Then, it should do only what is necessary to save state and queue a DPC to complete interrupt-driven I/O operations at a lower hardware priority (IRQL) than that at which the ISR executes.

DpcForIsr

lowest-level only

Drivers that have an ISR also should have a DPC, either a DpcForIsr or one or more CustomDpc routines, to complete interrupt-driven I/O operations.

SynchCritSection

lowest-level only

Any device driver whose routines share data or device registers with its ISR must have one or more SynchCritSection routines to access the shared data or registers in a multiprocessor-safe manner.

Cancel

any level (highest driver in any chain)

Drivers in which IRPs might remain queued for an indefinite interval (so a user could cancel a previously submitted I/O request) must have one or more Cancel routines to complete user-cancelled I/O requests. Examples of NT drivers that should have Cancel routines are keyboard, mouse, parallel, serial, and sound device drivers (or drivers layered over them), and file system drivers.

AdapterControl

lowest-level only

Any driver whose device uses system DMA must have an AdapterControl routine in order to carry out transfer operations through a system DMA controller between its device and system physical memory. Any driver whose DMA device is a nonSCSI busmaster usually must have an AdapterControl routine.

ControllerControl

lowest-level only

A device driver that must synchronize operations through a physical controller, such as an “AT” disk controller, to similar devices can have a ControllerControl routine.

IoCompletion

intermediate or highest-level

Higher-level drivers that monitor on an IRP-specific basis how lower-level drivers carried out particular requests can have one or more IoCompletion routines. Higher-level drivers that allocate IRPs to send requests to lower drivers must have an IoCompletion routine. See Section 2.2 for a description of how higher-level drivers can use an IoCompletion routine.

IoTimer

any level

Drivers that need to be called periodically to determine if a device operation has timed out, to update some driver-defined variable (such as a counter), or for some other reason can have an IoTimer routine. An IoTimer routine is actually a DPC routine, associated with a device object, that the I/O Manager calls once per second. An NT driver can have an IoTimer routine for each device object that it creates.

CustomTimerDpc

any level

Drivers that need to be called periodically at finer-grained intervals than once per second or at variable intervals can have a CustomTimerDpc routine, rather than an IoTimer routine. NT drivers also can have one or more CustomTimerDpc routines in addition to their IoTimer routines.

CustomDpc

lowest-level only

Any driver that needs to finish an interrupt-driven I/O operation later at a lower hardware priority (IRQL) can have a CustomDpc routine. Few lowest-level drivers have CustomDpc routines to be queued from their ISRs unless their devices require more than one DpcForIsr routine to complete a varied set of interrupt-driven I/O operations.

Note that the CustomTimerDpc and IoTimer routines shown in Figure 2.4 are actually system-defined CustomDpc routines that execute after a system clock interrupt occurs.

Reinitialize

any level

Any driver that needs to intialize itself in stages can have a Reinitialize routine. A Reinitialize routine is called after the DriverEntry routine has returned control and other NT drivers have initialized themselves.

By convention, the system-supplied NT drivers prepend an identifying, driver- or device-specific prefix to the name of every standard routine except DriverEntry, shown in Figure 2.4 as “DD.” Following this convention makes it easier to debug and maintain NT drivers.


2.3.4 Points to Consider about NT Standard Driver Routines

Keep the following points in mind when designing an NT driver:

  • An NT driver must have at least one Dispatch routine and must define the Dispatch entry point in its driver object for each IRP_MJ_XXX the driver handles. An NT driver can have up to as many Dispatch routines as the IRP_MJ_XXX function codes the driver handles.

  • An NT driver must have an Unload routine and define one Unload entry point in its driver object if the driver can be replaced while the system is running. An Unload routine is responsible for releasing any system resources, such as NT objects or driver-allocated memory, that the driver is using before the driver itself is unloaded from the system.

  • An NT driver can have a StartIo routine and define one StartIo entry point in its driver object. Any lowest-level driver that does not have a StartIo routine must set up and manage internal queues of IRPs sent to its Dispatch routine(s) unless it can complete every IRP it gets within its Dispatch routine(s). Higher-level drivers can have a StartIo routine, but seldom do because higher-level NT drivers usually pass IRPs on to lower-level drivers directly from their Dispatch routines.

  • The only exceptions to the preceding requirements are NT SCSI and video miniport drivers. For general information about how SCSI miniport drivers fit into the system, see Sections 2.4.4.3 and 2.5.7 later in this chapter. See Appendix A for more information about requirements for SCSI miniport drivers. For general information about how video miniport drivers fit into the system, see Sections 2.4.1.1 and 2.5.1 later in this chapter. See the Graphics Driver Design Guide for more information about requirements for video miniport drivers.

  • Whether an NT driver has any other kind of standard routine depends on its functionality and on how that driver fits into the system (for example, whether it interoperates with system-supplied drivers). The remaining sections in this chapter show how various kinds of system-supplied NT drivers are configured in Windows NT machines, how they are layered, and how certain NT objects represent physical, logical, and virtual devices, system DMA controllers, and device controllers. Use these sections as a guide to developing new NT device and intermediate drivers or as a guide to replacing system-supplied drivers with new device drivers.

See also the following documentation:

  • Section 2.5 for how various kinds of NT device drivers use device objects to represent physical, logical, and virtual devices and for more information about NT objects that represent system DMA controllers or device controllers

  • Chapter 3 for detailed information about NT objects that drivers can use

  • Chapter 4 for an overview of the standard NT driver routines and Chapters 5-15 for routine-specific requirements

  • Chapter 16 for more information about how NT drivers manage the system-defined hardware priorities at which standard NT driver routines execute

  • Chapter 16 for more information about how NT drivers use the registry during driver initialization

  • The Glossary for a summary of terminology and acronyms relevant to NT driver writers


2.4 Device Configurations and Layered NT Drivers

For the most common kinds of PC devices, the Windows NT DDK supplies a sample set of fully functional, system NT drivers. Individual sample drivers can be used as models when developing new NT drivers for similar kinds of devices. However, the system’s NT drivers had an additional design requirement: to make it easy to develop new NT device drivers. Consequently, many of the system’s NT drivers have a layered architecture so that certain drivers can be reused to support new NT drivers for similar devices.

In most cases, the DDK-supplied reusable drivers are higher-level NT drivers that handle hardware-independent operations for a system-supplied lowest-level, device-specific driver. In some cases, such as the parallel port and SCSI port drivers, these reusable drivers provide support for higher-level, device-type-specific class drivers. Note that none of the system’s reusable drivers precludes the development of new intermediate NT drivers to be added to a chain of existing drivers, as already described in Section 2.2.

Whether a new (or replacement) NT device driver must be a lowest-level or higher-level driver depends partly on the hardware configuration of devices in a given Windows NT platform, and partly on how much support a new NT driver can get from existing system drivers.


2.4.1 Interactive Devices

NT drivers for interactive devices provide hardware I/O support for a Windows NT kernel-mode display driver. Interactive devices include video, keyboard, mouse devices, and other pointing devices such as trackballs or light pens.


2.4.1.1 Video Adapter Configurations and Driver Layers

Figure 2.5 illustrates a representative hardware configuration for the video device shown previously in Figure 2.1.

Figure 2.5 Video Hardware Configuration

Figure 2.6 illustrates the corresponding layered drivers for I/O operations on the video device shown in Figure 2.5.

Figure 2.6 Video Driver Layers

Applications and other subsystems request video operations by calling the system-supplied Win32 GDI routines in the Win32 subsystem. The Win32 GDI interface is device-independent, insulating applications from the differences between various video devices.

The Win32 subsystem routes graphics requests to the system-supplied graphics engine (GDI) through the NT system services. GDI processes requests in conjunction with hardware-specific display drivers, communicating through the DDI interface. The display driver has the primary responsibility for determining how applications write to the display screen. The display driver must have a companion video miniport driver. The display driver translates device-independent graphics commands from GDI into commands the video hardware needs to draw graphics on the screen. Graphics hardware vendors supply a display driver and a video miniport driver to support their video adapter.

For more information about requirements for NT display drivers and video miniport drivers, see the Graphics Driver Design Guide.

The system-supplied NT video port driver, implemented as a dynamic-link library, exports a set of support routines for use by one or more adapter-specific video miniport drivers. The port driver’s VideoPort Xxx routines manage all OS-dependent operations on behalf of miniport drivers. Video miniport drivers that call only the VideoPort Xxx routines can be portable to other Microsoft operating systems that provide a video port driver.

For more information about the VideoPort Xxx, see the Graphics Driver Reference.

Each NT video miniport driver is responsible for supporting certain device I/O control operations that the corresponding display driver need not handle. The I/O Manager sends IRPs to the NT video port driver for these device I/O control requests. The NT video port driver handles access checking, memory mapping, and synchronization when it passes I/O requests on to each miniport driver as system-defined video request packets (VRPs).

A Windows NT video adapter is an exclusive device: its miniport driver is dedicated to supporting a single kernel-mode display driver. For performance reasons, the NT video port driver gives the display driver a privileged handle with special access rights for the video device and calls the adapter-specific miniport driver to map device memory so its corresponding display driver can access the video frame buffer directly and, in some Windows NT platforms, the adapter registers as well. Other display drivers must “go through” the owning display driver.


2.4.1.2 Keyboard and Mouse Configurations and Driver Layers

Figure 2.7 shows two possible hardware configurations for the keyboard and mouse devices:

  • Each connected directly somewhere on the system bus

  • Both connected through a keyboard and auxiliary device controller

Figure 2.7 Keyboard and Mouse Hardware Configurations

Figure 2.8 illustrates the corresponding layered drivers for I/O operations on the devices shown in Figure 2.7.

Figure 2.8 Keyboard and Mouse Driver Layers

Note that NT drivers of keyboard and mouse devices, whatever the hardware configuration, can use the system’s higher-level NT keyboard class and mouse class drivers to handle hardware-independent operations. These are called class drivers because each supplies system-required but hardware-independent support for a particular class of device.

A corresponding port driver implements the device-specific support to carry out required I/O operations on each physical device. The system’s (i8042) keyboard and auxiliary device port driver for x86-based Windows NT platforms manages device-specific operations for both mouse and keyboard. In a hardware configuration where each device is separately connected, as shown in Figure 2.7, each system class driver can be layered over separate device-specific port drivers, or a single driver for each device could be implemented as a separate, monolithic (lowest-level) driver.


2.4.2 Parallel and Serial Devices and Driver Layers

Figure 2.9 illustrates a representative hardware configuration for parallel and serial devices.

Figure 2.9 Parallel and Serial Hardware Configurations

Both the parallel and serial devices are connected directly on the system bus.

Figure 2.10 shows the corresponding layered drivers for I/O operations on the devices shown in Figure 2.9.

Figure 2.10 Parallel and Serial Driver Layers

As mentioned in Section 2.1, a parallel driver traditionally writes data, which usually has been set up by a Windows NT subsystem-specific printer driver and spooled by the subsystem, out through a parallel port to a printer. The system’s parallel driver (parclass in Figure 2.10) handles printer output through a parallel port by calling down to the system parallel port driver. Other parallel class drivers can handle input from and output to other types of devices through a parallel port. The system parallel port driver provides all necessary port arbitration support.

The system’s serial driver shown in Figure 2.10 is both a lowest-level and highest-level NT driver.

When an end-user application opens a parallel or serial port, the user-mode subsystem calls an NT I/O system service to open the file object representing the appropriate port. If the call succeeds, that port becomes an exclusive device: dedicated for use by the application until it closes the port and the subsystem releases the file object handle.


2.4.3 Sound Device and Driver Layers

Figure 2.11 illustrates a representative hardware configuration for a multimedia sound device.

Figure 2.11 Sound Device Hardware Configuration

As Figure 2.11 shows, the sound device is connected to a system DMA controller channel. The NT HAL coordinates access to the system’s DMA controller or controllers and to appropriate DMA channels and/or registers for NT drivers of all slave DMA devices.

Figure 2.12 illustrates the corresponding driver for the sound device.

Figure 2.12 Sound Driver Layers

The system sound driver is both a highest-level and lowest-level NT driver that provides support for user-mode multimedia audio applications. The Multimedia component of the Win32 subsystem is layered between such an application and at least four system-supplied Win32 audio drivers: the wave-input, wave-output, midi-input, and midi-output drivers, which are implemented as dynamic-link libraries. Depending on its adapter, an NT sound driver also can provide support for a Win32 Aux driver that responds to applications’ volume-control requests and/or for a Win32 mixer driver that supports multimedia mixer applications.


2.4.4 Mass-storage Devices and Driver Layers

As already mentioned in Section 2.2, NT drivers for most kinds of mass-storage devices have at least one higher-level driver layered above them: an NT file system driver like the FSD shown previously in Figure 2.3. While NT file systems have much in common with all higher-level (intermediate or highest-level class) drivers, NT places additional requirements on and supplies additional support for file system drivers.

The following subsections illustrate representative hardware configurations for common mass-storage devices and the corresponding layered NT drivers, including which system-supplied file systems are layered above particular system-supplied device drivers, such as disk, floppy, CD-ROM, or tape drivers.

Note that NT also has a “last-resort” file system driver, called the RAW file system, that is layered above any driver for a mass-storage device that no other NT file system recognizes when a file object on that device is first opened.

The developer of a driver for a mass-storage, nonSCSI, adapter can choose either to write a full blown driver for that adapter, or to write a miniport driver based on the SCSI miniport model. Such an adapter-specific miniport driver must translate SCSI-specific input, such as the SRB, into an adapter-specific form to communicate with the mass-storage adapter. Note that such a miniport driver only calls SCSI port routines.


2.4.4.1 AT Disk Devices

Figure 2.13 illustrates a representative hardware configuration for an “AT” (WD1003-compatible) disk controller.

Figure 2.13 AT Disk Controller Hardware Configuration

Each “AT”-type disk controller is assumed to support at most two disk devices, which might have different geometries. Figure 2.14 illustrates the corresponding layered drivers for these disks.

Figure 2.14 AT Disk Driver Layers

The system “AT” disk driver is designed to manage up to six disk controllers, each with up to two disks attached. This driver supports any NT file system with a partition on one of its disks. Depending on how a particular end user partitions the disks in a Windows NT machine, more than one NT file system driver might be layered over the system “AT” disk driver, including any of the system-supplied FAT, NTFS, and HPFS FSDs.


2.4.4.2 Floppy Devices

Figure 2.15 illustrates a representative hardware configuration for a floppy controller.

Figure 2.15 Floppy Controller Hardware Configuration

The floppy controller in Figure 2.15 is connected to a system DMA controller channel, and its disk drives might be for different-sized media. As mentioned in Section 2.4.3, the NT HAL coordinates access to the system’s DMA controller(s) and to appropriate DMA channels and/or registers for slave DMA devices like the floppy controller in Figure 2.15.

Figure 2.16 illustrates the corresponding layered drivers for the floppy controller and drives.

Figure 2.16 Floppy Driver Layers

The system floppy controller driver can support up to four floppy drives, although only two floppy drives are shown in Figure 2.15.

The NT FAT file system handles files on unpartitionable removable media. While the system has other NT file systems for disk media, they do not support file I/O on diskettes.


2.4.4.3 SCSI Devices

Figure 2.17 illustrates a representative hardware configuration for a SCSI bus.

Figure 2.17 SCSI Bus Hardware Configuration

In Figure 2.17, the host bus adapter (HBA) for this bus is connected to a system DMA controller. However, Figure 2.17 shows only a partial set of the HBA’s target controllers (TIDs for “target identifier”) and LUs where SCSI devices can be connected on the bus. Figure 2.17 also does not show the second SCSI bus that an HBA can drive.

Figure 2.18 illustrates the corresponding layered drivers for a representative set of SCSI devices.

Figure 2.18 SCSI Device Driver Layers

The NT SCSI port driver, implemented as a dynamic-link library, exports a set of support routines for use by one or more HBA-specific miniport drivers, depending on how many SCSI HBAs a given Windows NT machine has. HBA-specific miniport drivers that call only the ScsiPort Xxx routines can link their binaries against either the NT SCSI port driver or the corresponding driver on other Microsoft operating systems that support Win32 applications. In other words, such HBA miniport drivers can be operating-system-independent because Microsoft operating systems will manage all OS-dependent requirements on their behalf.

The NT SCSI port driver also defines an interface to all NT SCSI (class) device drivers, including the system-supplied disk, tape, and CD-ROM class drivers. This port/class interface insulates every NT SCSI class driver from having to know anything about the HBAs driving buses on which their respective devices are connected. In other words, the NT SCSI port driver makes SCSI devices physically portable to any SCSI bus in a Windows NT system without changing the code in their corresponding class drivers.

Like the “AT” disk driver described in Section 2.4.4.1, the system-supplied SCSI disk class driver shown in Figure 2.18 supports any NT file system with a partition on one of its disk(s).

A subsystem-level utility can send system-defined device I/O control requests through the NT RAW file system down to the system SCSI tape class driver. As Figure 2.18 shows, NT tape class drivers have two parts:

  1. A generic tape class driver that performs functions common to all tape drivers, such as creating device objects and sending IRPs to the NT SCSI port driver. The tape class driver is supplied by the system and is OS-dependent.

  2. One or more device-specific tape miniclass drivers that are linked to the generic tape class driver. Tape miniclass drivers are portable across Microsoft operating systems that support Win32 applications. Each miniclass driver supports one more more tape devices.

    Driver writers for SCSI tape devices implement only a tape miniclass driver.

The NT CDFS (CD-ROM file system) is layered over the system SCSI CD-ROM class driver.

If a new type (toaster) of SCSI device is added to the system, its driver must be an NT SCSI class driver. Developers of SCSI class drivers use the NT SCSI class/port interface for their devices, whether they write a class driver for a new type of SCSI peripheral device, write a new class driver for a type of device that already has a system class driver, or replace an existing class driver. However, it is usually unnecessary to write a new SCSI class driver if a system class driver for the same type of device already exists.

The system-supplied NT SCSI drivers assume compliance with the ANSI SCSI-II standard; in particular, compliance with the common command set incorporated by this standard. The SCSI disk filter driver, tape miniclass driver, and CD-ROM filter driver, shown in Figure 2.18, indicate where new SCSI device drivers can be inserted into each chain if the following conditions hold for the device:

  • The device requires SCSI CDBs (command descriptor blocks) in a format different from the SCSI-II CDB format.

  • The device requires some other nonstandard interface.

  • The device has certain features that other devices of its type do not support.

For such a device, there are two alternatives:

  1. For most kinds of SCSI devices, a vendor-specific SFD can attach itself above the system-supplied class driver, as shown in Figure 2.18, to intercept requests for its device and to translate requests to and completion data from the device as necessary. For SCSI tape devices, a vendor-specific tape miniclass driver can link itself to the system-supplied tape class driver to set up CDBs for its device.

  2. Otherwise, a vendor-specific class driver can be added to the set of class drivers layered over the NT port driver like the toaster class driver shown in Figure 2.18. Note that implementing an NT SCSI class driver for a single device is far more work than writing an SFD or tape miniclass driver.

As Figure 2.18 shows, other value-added drivers can be inserted above any SCSI class or filter driver. For example a fault-tolerant disk driver might be inserted above the NT SCSI disk class driver (and filter driver, if any) to handle disk mirroring or duplexing. Such a value-added driver can ignore the NT class/port interface. However, such a higher-level driver must supply Dispatch entry points for each IRP_MJ_XXX that any NT device driver for the underlying peripheral device must handle, as already mentioned in Section 2.2.

See the Kernel-Mode Driver Reference for more information about the following:

  • The requests that SCSI class, SFD, and tape miniclass drivers for particular types of devices are required to handle

  • The ScsiPort Xxx routines that HBA-specific SCSI miniport drivers call

  • The standard functions for SCSI miniport drivers

  • The system-defined structures for the SCSI class/port and port/miniport interfaces

See also Appendix A for more information about requirements for SCSI class, filter, tape miniclass, and miniport drivers.


2.4.5 Points to Consider about Adding NT Drivers

Keep the following points in mind when designing an NT driver:

  • The NT SCSI and video port drivers cannot be replaced.

  • A replacement device driver (lowest-level driver) must implement the same functionality as the driver it replaces. For example, a replacement keyboard or mouse port driver must use the system-defined interface between itself and a system-supplied class driver that it reuses, and vice versa.

  • A new intermediate driver to be inserted between any pair of system-supplied drivers must interoperate with those drivers. In other words, such a new driver must comply with the requirements outlined in the Section 2.2.1 and Section 2.3.4.


2.5 NT Objects with Device, Configuration, or Layer Dependencies

This section introduces device, adapter, and controller objects by showing the correspondence between these objects and the representative hardware configurations and system drivers shown in Figures 2.5 through 2.18. Note that every driver in the following figures creates at least one device object, except for the video and SCSI miniport drivers. The NT-supplied port driver creates and manages all necessary NT objects on their behalf.

Chapter 3 describes device, adapter, and controller objects separately in some detail, along with all the other NT objects a driver might use or must use, such as interrupt objects for lowest-level NT drivers whose physical devices generate interrupts.

Device Objects and Device Extensions

Each NT driver (except for video and SCSI miniport drivers) must create at least one device object by calling an I/O support routine when the driver is loaded, as already mentioned in Section 2.3. Some NT drivers must create more than one device object: whatever a particular NT driver’s level in a chain of layered drivers, a separate device object represents each physical, logical, and/or virtual device for which the driver handles I/O requests. For the video and SCSI miniport drivers, the corresponding NT port driver creates the necessary device objects.

In other words, the driver object previously shown in Figure 2.4 represents a single NT driver that could have created more than one device object. When it is called, each standard driver routine that is given an IRP is also given a pointer to the target device object for the I/O request. Most NT drivers use the device extension of the target device object to maintain necessary device state information or driver-determined context data about the current I/O request they are processing.

Because most NT device and intermediate drivers execute in an arbitrary thread context (that of whatever thread happens to be current), a device extension is each driver’s primary place to maintain device state and all other driver-specific or device-specific data the driver needs. For example, any NT driver that implements a CustomTimerDpc or CustomDpc routine shown in Figure 2.4 usually provides storage for the required Kernel-defined timer, and/or DPC objects in a device extension.

Every NT driver that has an ISR must provide storage for a pointer to a set of Kernel-defined interrupt objects, and most NT device drivers store this pointer in a device extension. Each NT driver determines the size of the device extension when it creates a device object, and each driver defines the contents and structure of its own device extension(s).

Adapter Objects

Certain other NT objects that drivers use are also configuration-dependent, device-dependent, and/or design-dependent. For example, a lowest-level driver whose device is connected to a system DMA controller (as shown previously in Figures 2.11 and 2.15) must explicitly associate its device object with an adapter object that represents a DMA controller channel to carry out transfer operations. Such a driver provides storage, usually in a device extension, for a pointer to an adapter object and has an AdapterControl routine, as shown in Figure 2.4, that calls system-supplied adapter object support routines to accomplish DMA transfers.

Controller Objects

A lowest-level driver for a set of similar devices coordinated by a physical controller, such as the “AT” disk controller shown in Figure 2.13, can create a controller object and use it to synchronize I/O operations between the attached devices. Such a driver implements a ControllerControl routine, as shown in Figure 2.4, and calls the I/O Manager’s controller object support routines.


2.5.1 Video Driver’s Device Objects

Figure 2.19 illustrates the device object that represents the video adapter shown previously in Figure 2.5. The NT video port driver shown in Figure 2.6 creates this device object on behalf of a video miniport driver by calling an I/O support routine.

Figure 2.19 Video Device Object

When it is loaded, the NT video port driver creates a device object to represent the video adapter within the system. The NT video port driver also sets up any necessary platform-specific support, such as mapping the I/O space or device registers to system memory in some Windows NT platforms.

In Windows NT machines with more than one video adapter, the NT video port driver creates a separate device object to represent each adapter. To any protected subsystem and its display driver(s), these adapter-specific device objects appear to represent separate instantiations of the NT video port driver.

Each Win32 display driver acquires a handle for the corresponding video miniport driver’s device by opening the file object associated with the appropriate device object. After such a display driver obtains the handle, it calls the Win32 subsystem’s native functions to control the device. Its device I/O requests are sent as IRPs to the NT video port driver, which provides all necessary NT-dependent, kernel-mode support for both the display and miniport drivers.


2.5.2 Keyboard and Mouse Drivers’ Device Objects

Figure 2.20 illustrates the device objects that represent the keyboard and mouse devices shown previously in Figure 2.7. The keyboard and mouse drivers shown in Figure 2.8 create these device objects by calling an I/O support routine.

Figure 2.20 Keyboard and Mouse Device Objects

For the keyboard and mouse devices, both their respective port and class drivers create device objects. The port driver creates a device object to represent the physical port. Each class driver creates its own device object to represent the keyboard or mouse device as a target for I/O requests.

Each class driver calls an I/O support routine to get a pointer to the next-lower-level driver’s device object, so the class driver can chain itself above the port driver, and so each class driver can send I/O requests down to the port driver for the target device object representing its physical device.

As shown previously in Figure 2.8, each port driver is a lowest-level driver, so every port driver of a device that generates interrupts must set up interrupt object(s) and register an ISR.

Note that a dual-device port driver, like the (i8042) driver for the keyboard and auxiliary device controller shown in Figure 2.7, must set up device-specific interrupt objects when each device uses a different interrupt vector. The writer of such a driver can either implement separate ISRs for each device or implement a single ISR for both devices.


2.5.3 Parallel and Serial Drivers’ Device Objects

Figure 2.21 illustrates the device objects that represent the parallel and serial devices shown previously in Figure 2.9. The parallel and serial drivers shown in Figure 2.10 create these device objects by calling an I/O support routine.

Figure 2.21 Parallel and Serial Device Objects

Note that the parallel port and serial drivers each create a set of logical device objects: one for each parallel or serial port. Note also that neither driver creates a physical device object for the parallel or serial controller. The target of a parallel or serial I/O operation is always a particular port, so each port must be represented by an NT device object. Figure 2.21 happens to show three parallel and two serial ports, but the system-supplied parallel port and serial drivers can support any number of ports, subject to hardware limitations.

Each parallel class driver shown in Figure 2.10 also creates a device object to represent each target device for the I/O requests that it handles.


2.5.4 Sound Driver’s Device Objects

Figure 2.22 illustrates the device objects that represent the sound adapter shown previously in Figure 2.11. The sound driver shown in Figure 2.12 creates these device objects by calling an I/O support routine.

Figure 2.22 Sound Device Objects

Like the parallel and serial drivers described in Section 2.5.3, the system-supplied sound driver creates a set of logical device objects: one each for wave-input, wave-output, midi-input, and midi-output operations. If the driver also supports a Win32 Aux and/or mixer driver, it creates a device object to represent it.

An adapter object represents the system DMA controller channel to which the sound card is connected (as shown in Figure 2.11). The NT HAL creates as many adapter objects as the system needs to represent all DMA controller channels or nonSCSI-busmaster adapters in a given machine.

An adapter object is wholly opaque to drivers: the sound driver must use the system’s adapter support routines to accomplish its DMA transfers of (for example) wave data; it cannot access the adapter object by another means. Consequently, this driver has an AdapterControl routine, as shown previously in Figure 2.4.


2.5.5 AT Disk Driver’s Device and Controller Objects

Figure 2.23 illustrates the device objects that represent the disks and the controller object that represents the disk controller shown previously in Figure 2.13. The “AT” disk driver shown in Figure 2.14 creates these device objects and the controller object by calling I/O support routines.

Figure 2.23 AT Disk Device Objects and Controller Object

This driver creates a device object for each disk, plus some number of logical device objects representing disk partitions. Figure 2.23 happens to show the first disk with one partition and the second with two, but the number of partitions would vary from disk to disk, depending on how different users partition the disk(s) in their respective machines.

Note that this driver’s logical device objects representing disk partitions must be deleted and a new set created if the user repartitions a disk dynamically.

The system “AT” disk driver uses the controller object shown in Figure 2.23 to synchronize I/O operations between the attached disks, so it implements a ControllerControl routine, as shown previously in Figure 2.4.

A controller object is by no means the only mechanism an NT device driver can use to synchronize I/O operations among its physical, logical, or virtual devices. NT supplies drivers with many ways to synchronize I/O operations and to synchronize access to any data or hardware shared by a given driver’s routines. For example, the Kernel-defined event and semaphore objects shown in the next figure, and the spin lock associated with a driver’s interrupt object(s) are three such mechanisms.

While the “AT” disk driver is like the keyboard and auxiliary device port driver (see Section 2.5.2) in handling interrupts for two devices, the disk controller is actually the interrupting device. Consequently, this driver calls the I/O Manager’s interrupt support routine once to register its ISR.

Note that the port driver of a class/port pair is responsible for handling synchronization between (or among) dissimilar devices attached to a device controller, while the class driver(s) can ignore synchronization issues. For example, the keyboard and auxiliary device port driver (see Section 2.5.2) processes IRPs sent independently and at random by both the keyboard and mouse class drivers above it, so the keyboard and auxiliary device port driver must manage the synchronization of operations between the keyboard and mouse devices.


2.5.6 Floppy Driver’s Device Objects

Figure 2.24 illustrates the device objects that represent the floppy drives shown previously in Figure 2.15. The floppy driver shown in Figure 2.16 creates these device objects by calling an I/O support routine.

Figure 2.24 Floppy Device Objects

An adapter object represents the system DMA controller channel to which the floppy controller is connected, as already shown in Figure 2.15. Like the sound driver described in Section 2.5.4, this driver has an AdapterControl routine, as shown previously in Figure 2.4.

However, much of the system floppy controller driver’s work is done by a device-dedicated thread. Because programming the floppy controller takes a long time between interrupts (and some controller operations don’t even cause an interrupt), the system floppy controller driver sets up its own kernel-mode thread that is “blocked” (waiting on the semaphore object shown in Figure 2.24) until the floppy driver is called to carry out an I/O operation. Then, the floppy driver inserts incoming IRPs into an interlocked queue shared with its device-dedicated thread and sets the semaphore object to the Signaled state, thereby “unblocking” the floppy thread.

When it is run, the floppy thread removes an IRP from the queue, starts the requested operation on the device, and waits on the event object shown in Figure 2.24. The floppy driver handles interrupts from the controller, queueing a DpcForIsr routine that notifies the floppy thread (by setting the event to the Signaled state) to complete I/O processing and the IRP.

Unlike most NT drivers, the system floppy controller driver does not have a StartIo routine because it sets up an interlocked queue and manages its own queuing of IRPs. Using an interlocked queue prevents the floppy driver and thread from simultaneous attempts to insert and remove IRPs in the queue. The NT Executive Support component supplies routines for managing entries in an interlocked queue, and the driver allocates the storage for an executive spin lock, which is required to use an interlocked queue. Executive Support routines manage access to entries in such an interlocked queue with the spin lock as follows:

  • While a support routine holds the spin lock for the system floppy controller driver, the floppy thread cannot remove an IRP from the queue.

  • While another support routine holds the spin lock for the floppy thread, the floppy driver cannot insert an IRP into the queue.

Spin locks are opaque synchronization mechanisms, which NT drivers can use for operations that must be atomic or to protect driver-defined data, device registers, or driver-allocated resources from simultaneous access at raised IRQL by more than one driver routine, particularly in an SMP machine. Every lowest-level NT driver with an ISR also uses a spin lock that is associated with its interrupt object(s).

The NT Kernel supplies support routines for initializing spin locks, for acquiring and releasing spin locks, for synchronizing access to data protected by an interrupt spin lock (because the data is shared between the driver’s ISR and other driver routines), and for creating and using the event and semaphore objects shown in Figure 2.24. For more information about using spin locks, see also Chapter 16.

Note that only an NT thread can wait for a nonzero interval on Kernel-defined dispatcher objects, which include semaphore, event, mutex, and timer objects, as well as thread objects. However, every NT driver’s DriverEntry routine executes within a system thread context, so NT drivers can wait for nonzero intervals on dispatcher objects when they initialize themselves.

The system floppy controller driver’s thread allocates and uses the contiguous buffer shown in Figure 2.24 to contain data for format operations. The NT Memory Management component provides support routines for allocating contiguous and/or noncached memory, for explicitly locking down user buffers, and for mapping virtual and physical addresses.

However, the I/O Manager or an NT file system takes care of user buffer locking and address mappings for most NT mass-storage drivers. The I/O Manager buffers user data and takes care of address mappings for certain other NT device and higher-level drivers.

As Figure 2.24 shows, the system floppy controller driver is somewhat like the NT file system shown previously in Figure 2.3, because it uses a (device-dedicated) system thread. The NT Process Structure component supplies a support routine for creating device-dedicated system threads. However, NT file system drivers usually have worker-thread callback routines so they call routines provided by the Executive Support component to use system worker threads, rather than setting up driver-dedicated threads of their own.

Most NT device and intermediate drivers avoid setting up driver-dedicated threads or a device-dedicated thread like the system floppy controller driver. Most avoid using system worker threads, as well. Only drivers for devices that carry out long operations without notifying the driver should use a device-dedicated thread to wait for nonzero (and possibly indefinite) intervals on NT dispatcher objects.

Without a device-dedicated thread, the driver for such a device would have to stall, polling its device for device-state changes while executing in the context of some arbitrary thread, possibly the original user-mode calling thread that requested the I/O operation, a file system’s thread, or some other thread that happened to be current. Such a driver would waste the quantum allotted to the current thread, which could get no work done while the driver kept control. Such a driver would also waste many CPU cycles that could be better used by other NT kernel-mode components, including other drivers.

Thus, the system floppy controller driver improves overall system performance and I/O throughput at a cost to its own performance: context switches to the floppy thread. NT drivers for newer and faster devices can usually wait for device-state changes without any noticeable impact on system performance.

See Chapter 16 for more information about polling a device and for general guidelines about whether to implement a driver with device-dedicated thread(s).


2.5.7 SCSI Drivers’ Device Objects

Figure 2.25 illustrates a sample set of device objects that represent peripheral devices on the SCSI bus shown previously in Figure 2.17. The system-supplied SCSI port and class drivers and the toaster class driver shown in Figure 2.18 would create these device objects by calling an I/O support routine.

Figure 2.25 SCSI Device Objects

The NT SCSI port driver creates a device object representing the HBA within the system. In machines with more than one SCSI HBA, the NT port driver creates a separate device object for each HBA. To any NT SCSI class driver, these HBA-specific device objects appear to represent separate, HBA-specific, instantiations of the NT SCSI port driver.

When it loads, each NT SCSI class driver determines which SCSI bus has connected devices of its type by accessing the SCSI inquiry data collected by the NT SCSI port driver. Each SCSI class driver chains itself over one or more HBA-specific NT port driver instantiations, depending on which HBAs have devices of that class connected on their buses.

Windows NT defines a class/port interface between any NT SCSI class driver and the NT port driver that insulates class drivers from having to deal with HBA-specific hardware peculiarities. After a class driver locates its devices, that driver’s SCSI requests are sent as IRPs containing system-defined SCSI request blocks (SRBs) to the appropriate instantiation(s) of the NT port driver.

Each NT SCSI class driver shown previously in Figure 2.18 creates at least one device object representing its physical device connected to a corresponding logical unit on the SCSI bus.

Like the “AT” disk driver (see Figure 2.23), the SCSI disk class driver also creates a logical device object to represent each additional partition on its disks.

If a new SCSI filter driver is added to the system, it must create one or more device objects to be attached to the corresponding class driver’s device object(s).

As Figure 2.25 shows, adding a SCSI device of a new class to an NT machine requires its driver to create at least one device object to represent the physical device it controls. Possibly the new class driver must create more than one device object, depending on the nature of the (toaster) device. Such a driver must be implemented as an NT SCSI class driver.

For more information about the NT SCSI class/port interface and requirements for SCSI class and filter drivers, see Appendix A, and see also the Kernel-Mode Driver Reference.

The NT SCSI port driver also sets up any HBA-specific interrupt object(s), handles DMA, and, in effect, insulates each HBA-specific miniport driver from having to know anything about NT objects. SCSI miniport drivers interact only with their respective HBAs and with the OS-dependent port driver.


2.5.8 Points to Consider about NT Objects

Keep the following points in mind when designing an NT driver:

  • Except for video and SCSI miniport drivers, every NT driver must create a named device object to represent each physical, logical, or virtual device that could be a target for an I/O request.

    For example, a replacement parallel port or serial driver should create a logical device object for each port. By contrast, a new virtual disk driver that presented every hard disk in the machine as one big disk device could create a single device object to represent its disk, plus some number of device objects representing disk partitions for the file systems layered over such an intermediate driver.

  • For most NT device and intermediate drivers, the device extension of each device object is each driver’s primary (and frequently only) global data storage area. Many NT drivers maintain device state and all other driver-specific or device-specific data and resources a driver needs in the driver-defined device extension(s) of one or more driver-created device objects.

    The driver-specific I/O stack location in IRPs can be considered an operation-specific local storage area for some kinds of data.

  • NT drivers of slave DMA devices that use the system DMA contoller(s) must have an AdapterControl routine and call the system-supplied support routines that manipulate adapter objects in order to carry out DMA transfers.

    NT drivers of nonSCSI busmaster DMA devices usually must have an AdapterControl routine and must call the system-supplied support routines that manipulate adapter objects in order to carry out DMA transfers.

    The NT SCSI port driver sets up all necessary adapter objects for HBA-specific SCSI miniport drivers.

  • A driver of physical devices that are connected to a device controller may or may not represent the physical controller with a driver-created controller object and implement a ControllerControl routine to synchronize operations between attached devices. Generally, drivers use controller objects to synchronize operations to attached devices if the following criteria hold:

    • The controller does not carry out long operations without interrupting, so the driver does not need to create a device-dedicated thread or use system worker threads.

    • The devices connected to the controller are similar. That is, they are not devices with entirely different physical properties or operational functionality, such as the keyboard and mouse devices that can be connected to the keyboard and auxiliary device controller (see Figure 2.7).

    • The driver is designed to be monolithic: single-layered in relation to the device controller and attached physical devices, rather than being designed as a port driver (for the controller) with one or more class drivers (for attached devices) layered over the port driver.

  • Consider the synchronization of operations for the system floppy controller, SCSI disk, and “AT” disk drivers described in this section:

    • Floppy Driver

      This driver creates a device-dedicated thread because setting up its device controller for an I/O operation takes so much time (up to 375ms per command byte for up to 9 individually loaded bytes per I/O operation). This driver synchronizes all I/O operations for one or more floppy drives with its device-dedicated thread. The floppy driver queues IRPs to the floppy thread, which waits to complete all operations necessary to carry out each I/O request before starting the next IRP on the floppy controller. In effect, the floppy driver and thread synchronize operations between or among floppy drives by serializing IRPs in the driver’s interlocked queue and processing only one request to completion at a time.

    • SCSI Disk Driver

      This driver is required to be an NT SCSI class driver. It sends requests to its disks through the NT SCSI port driver to HBAs driving buses with connected disk devices. The NT SCSI port driver is responsible for synchronizing operations for all SCSI class drivers, while the HBAs driving the SCSI buses are responsible for synchronizing hardware operations among peripheral devices on their respective buses.

      The NT SCSI port driver creates a set of supplemental device queues to hold IRPs sent down by the higher-level NT SCSI class drivers, in order to manage the synchronization of requests to the SCSI peripheral devices on each HBA’s bus(es). The NT SCSI disk class driver simply sends each IRP down to the NT port driver, using the class/port interface, without having to synchronize operations among its target disks.

      Each NT SCSI class driver sets up an IoCompletion routine for IRPs when the driver needs to determine the completion status of any operation it sends down to the NT SCSI port driver. For example, when a class driver must split transfers to suit the limits of a given HBA, the class driver’s IoCompletion routine can track the amount of data actually transferred in each sub-request it makes and free any IRPs and SRBs the class driver allocated for subtransfer requests.

    • “AT” Disk Driver

      This driver is a monolithic (single-module) driver that uses a controller object to synchronize operations between attached disk devices. This driver programs the device controller and communicates directly only with the controller hardware in order to carry out I/O requests to the physical disks. However, its physical devices might have been managed by a disk class driver layered above a (controller) port driver, instead of by a monolithic driver.

  • A driver writer with any SCSI peripheral device (except tape) whose type is already handled by a system SCSI class driver has a choice about where to layer the driver:

    • A new SCSI filter driver can be inserted above the system class driver by attaching its device object(s) to the class driver’s. Note that such an SFD must attach itself to the class driver before any higher-level intermediate driver is loaded and before any higher-level file system driver mounts a volume on the physical device.

    • A new SCSI class driver can be layered over the NT port driver as another class driver that defines only the set of device objects necessary to represent its physical and/or logical devices. Unless the device requires the new driver to handle every I/O request differently than other SCSI devices of its type, writing an SFD takes much less time than writing such a new SCSI class driver.

  • NT drivers cannot and must not attempt to wait for a nonzero interval on a Kernel-defined dispatcher object in an arbitrary thread context. In other words, an NT driver can wait on a Kernel-defined event, semaphore, mutex, or timer object within its DriverEntry, Reinitialize, and certain Dispatch routines for inherently synchronous I/O operations. Otherwise, an NT driver cannot wait on a dispatcher object within most of its standard routines, which run at IRQL >= DISPATCH_LEVEL and in an arbitrary thread context. (NT drivers must use a spin lock to synchronize access to shared resources by driver routines that run at IRQL >= DISPATCH_LEVEL.)

    If necessary, an NT driver can create a device-dedicated thread, which can wait on a dispatcher object that the driver’s other routines (except an ISR or SynchCritSection routine) can set to the Signaled state and reset to the Not-Signaled state. As a general guideline, if you expect that your new device driver will often need to stall for longer than 50 microseconds while it waits for device-state changes during I/O operations, consider implementing a driver with a device-dedicated thread. If such a device driver is also a highest-level driver, consider using system worker threads and implementing one or more worker-thread callback routines.


Chapter 3 NT Objects and Support for Drivers

This chapter contains the following information:
3.1 NT Components and NT Drivers
3.2 Device Objects and Device Extensions
3.2.1 Defining Device Extensions

3.2.2 Creating a Device Object and Device Extension

3.2.3 Initializing Driver-specific Device Objects and Device Extensions

3.2.4 Setting Up Access to User Buffers

3.2.4.1 Using Buffered I/O

3.2.4.2 Using Direct I/O

3.2.4.3 Using Neither Direct Nor Buffered I/O

3.3 Adapter Objects and DMA
3.3.1 Map Registers

3.3.2 Getting an NT Adapter Object

3.3.3 Splitting Transfer Requests

3.3.4 Using System DMA

3.3.4.1 Packet-Based System DMA

3.3.4.2 Common-Buffer System DMA

3.3.5 Using Busmaster DMA

3.3.5.1 Packet-Based Busmaster DMA

3.3.5.2 Common-Buffer Busmaster DMA

3.4 Controller Objects
3.4.1 Creating a Controller Object with a Controller Extension

3.4.2 Allocating the Controller Object for I/O Operations

3.5 Interrupt Objects
3.5.1 Getting a System-assigned Interrupt Vector, DIRQL, and Processor Mask

3.5.2 Registering an ISR

3.6 DPC Objects
3.6.1 Registering and Queueing a DpcForIsr Routine

3.6.2 Registering and Queueing a CustomDpc Routine

3.7 Timer Objects with Associated DPCs
3.7.1 Registering and Enabling an IoTimer Routine

3.7.2 Registering and Queueing a CustomTimerDpc Routine

3.8 Device Queue Objects and Interlocked Queues
3.8.1 Setting up a Device Queue Object and Queueing IRPs

3.8.2 Setting up an Interlocked Queue and Queueing IRPs

3.9 Kernel Dispatcher Objects
3.9.1 Timer Objects

3.9.2 Event Objects

3.9.3 Semaphore Objects

3.9.4 Mutex Objects

All NT drivers use certain NT objects. For example, every NT driver except the SCSI and video miniport drivers, as explained in Chapter 2, must create and set up a named device object to represent each logical, virtual, and/or physical device for which it handles IRPs.

Only certain kinds of drivers are required to use particular NT objects, as also mentioned in Chapter 2. For example, whether a given driver uses an NT adapter object depends on whether its device uses DMA.

Use of other NT objects depends on the discretion of the driver designer.

This chapter describes the NT objects that NT drivers must and can use.


3.1 NT Components and NT Drivers

Figure 3.1 summarizes the relationship between a chain of layered NT drivers and the NT components whose support routines these drivers can call.

See also the Kernel-Mode Driver Reference for specifics about the support routines that are useful to NT device and intermediate drivers.

Figure 3.1 Executive Component Support for Drivers

The number of drivers in a chain of layered NT drivers depends somewhat on the nature of the device, as already described in Chapter 2. For example, system drivers for SCSI mass-storage devices form at least triple-driver chains, with the NT SCSI class and port drivers corresponding to the intermediate class and lowest-level physical device drivers shown in Figure 3.1, and with at least one NT file system driver layered over each class driver. Within NT, an HBA-specific SCSI miniport driver is considered part of the NT SCSI port driver.

In addition, the system-supplied fault tolerant disk driver, ftdisk, might be layered between the SCSI disk class driver and file system driver(s) if the user decides to enable partition mirroring or the creation of stripe or volume sets. The NT ftdisk driver is optional, but it corresponds to the logical/virtual intermediate device driver shown in Figure 3.1.

By contrast, the system sound and serial drivers described in Chapter 2 are not part of an NT driver chain. Each of these drivers corresponds to the lowest-level physical device driver shown in Figure 3.1. Each is an NT lowest-level device driver with respect to its set of standard driver routines. Each is also a highest-level NT driver with respect to its position relative to the I/O Manager and to user-mode protected subsystems.

Support Routines Manipulate NT Objects

NT lowest-level device drivers have different standard driver routines from higher-level drivers, so Figure 3.1 shows certain objects (interrupt, controller, and adapter) close to the hardware and to the lowest-level drivers that can use these objects. The NT HAL supplies platform-specific support to the I/O Manager, the Kernel, and to most NT device drivers. When a lowest-level device driver initializes, it calls HalGetInterruptVector to obtain platform-specific arguments (system vector, DIRQL, and processor mask) so the driver can call IoConnectInterrupt to set up interrupt object(s) and register its ISR. If its device uses DMA, such a driver supplies configuration-specific information, such as the interface type of the I/O bus to which its device is connected, and calls HalGetAdapter to obtain a pointer to the appropriate adapter object.

During initialization, all NT drivers can call support routines provided by the Configuration Manager to get (and supply) information in the registry. Drivers also can call routines supplied by the I/O Manager to set up symbolic links between their named device objects and the subsystem-specific names for devices to be stored in the registry. NT device drivers also can call Io Xxx and/or Rtl Xxx support routines to get and set configuration information in the registry. For more information about using the registry during driver initialization, see Chapter 16.

Like the I/O Manager, the NT Security Reference Monitor, Configuration Manager, Object Manager, Process Structure, Memory Manager, Executive Support, and Kernel components each define a set of opaque object types and/or data structures, as shown in Figure 3.1. NT drivers can use these objects and data structures only by calling the appropriate kernel-mode support routines.

As Figure 3.1 implies, NT device and intermediate driver writers can ignore the NT LPC (local procedure call) component. Only certain highest-level drivers call support routines supplied by the Security component. The I/O Manager or an NT file system driver performs any necessary interprocess communication and security access checks before an IRP is sent to lower-level NT drivers. The I/O Manager and Object Manager also perform symbolic link resolutions before sending IRPs to lower-level NT drivers.

As Figure 3.1 also shows, the I/O Manager provides support routines for manipulating certain objects and data structures that are defined by other NT components, including symbolic link objects, interrupt objects, DPC objects, timer objects and memory descriptor lists (MDLs). These Io Xxx support routines help to implement certain standard NT driver routines, and to map buffers, which can be associated with driver-customized IRPs sent to lower-level drivers. For most NT driver writers, the Io Xxx support routines are all their drivers need to do any of the following:

  • Register a standard ISR by calling IoConnectInterrupt and deregister the ISR when the driver unloads by calling IoDisconnectInterrupt

  • Register a standard DpcForIsr routine by calling IoInitializeDpcRequest and request a call to the driver’s DpcForIsr routine from its ISR by calling IoRequestDpc

  • Register a standard IoTimer routine by calling IoInitializeTimer and enable or disable once-per-second calls to the IoTimer routine by calling IoStartTimer or IoStopTimer, respectively

  • Break a too large buffer, already mapped by an MDL associated with an incoming IRP, into smaller mapped buffers by calling IoBuildPartialMdl in order to carry out DMA operations on a device with limited data transfer capabilities

Except for NT file system drivers, the I/O Manager also sets up an associated device queue object for each device object that NT drivers create when they call IoCreateDevice. It also provides I/O support routines that drivers call to have IRPs routed to their StartIo routines. The IoStartPacket and IoStartNextPacket routines call the Kernel’s device queue support routines on a driver’s behalf.

However, an NT driver can call many of the same Kernel support routines as the I/O Manager does if the driver designer finds doing this necessary. Depending on the driver’s design or on the device, an NT driver can do any of the following:

  • Set up a timer object and an associated DPC object that can be explicitly queued as a CustomTimerDpc routine

    For example, a driver might have a CustomTimerDpc, rather than an IoTimer routine, if its device requires variable time-out intervals or an interval that must be finer-grained than once per second.

  • Set up one or more DPC objects and explicitly queue driver-supplied CustomDpc routines from the ISR

    For example, a serial driver might queue a CustomDpc that cancels pending I/O requests when its ISR detects a device error.

  • Set up a timer object on which a device-dedicated thread or worker-thread callback routine can wait for a driver-specified interval

    Drivers that set up their own device-dedicated system threads, such as the floppy driver described in Chapter 2, also can have their threads wait on Kernel-defined event, semaphore, or, possibly, mutex objects, as can highest-level drivers’ worker-thread callback routines. NT drivers can set up a device-dedicated thread by calling PsCreateSystemThread and can release such a thread with PsTerminateSystemThread.

  • Set up additional device queue objects

    For example, the NT SCSI port driver described in Chapter 2 sets up a single device object to represent an HBA, and the I/O Manager routes IRPs through (or from) appropriate NT SCSI class drivers into the device queue associated with that HBA device object. However, the NT SCSI port driver sets up an additional device queue object for each logical unit on an HBA-specific SCSI bus that is claimed by a SCSI class driver. The NT SCSI port driver uses these additional device queues to sort incoming IRPs into LU-specific device queues.

Because every DriverEntry routine is called in the context of a system thread, a higher-level NT driver can also wait on a Kernel event object when it initializes, after the driver has chained itself above the next-lower-level driver by calling IoGetDeviceObjectPointer and getting a pointer to the lower driver’s device object or by calling IoAttachDevice to alias its device object to a lower driver’s device object. For example, a class driver’s DriverEntry routine might call IoBuildSynchronousFsdRequest or IoBuildDeviceIoControlRequest to set up an IRP with an associated event and, then, pass its driver-allocated IRP on to the underlying port driver with IoCallDriver. Such a class driver could wait on the event by calling KeWaitForSingleObject while the port driver below it gathers (or sets) device-state information that the class driver needs to complete its initialization.

The Executive Support component, shown in Figure 3.1 between the Kernel and Memory Manager components, provides certain support routines that require an initialized spin lock, for which the caller must supply the storage. Any Executive Support routine that contains the word “interlocked” requires a caller-supplied spin lock as an argument. The Executive Support component also provides routines for drivers that allocate system-space (pool) memory and for highest-level drivers, such as FSDs, that use system worker threads.

Whether an NT device driver uses the Memory Manager’s routines to manipulate MDLs depends somewhat on the nature of the device. Each driver determines whether it uses MDLs to access user buffers when it sets up its device object(s), as explained in Section 3.2, next.

Storage for NT Objects

Drivers that use the I/O Manager’s interrupt, DPC, and timer support routines can rely on the I/O Manager to provide storage for any necessary Kernel-defined objects. However, the NT Kernel does not allocate memory on behalf of callers to its support routines. Consequently, any NT component, including any NT driver, that directly calls the Ke Xxx routines must provide storage for the Kernel-defined objects it uses. For example, any NT driver that has a standard CustomTimerDpc routine must provide storage for the timer and DPC objects it needs to call the Kernel’s support routines that manipulate these objects.

Drivers that have an ISR must provide storage for an interrupt object pointer that is returned by IoConnectInterrupt. The I/O Manager provides storage for and initializes the driver’s interrupt objects (up to one per processor in an SMP machine). The Kernel actually defines the interrupt object type with an associated interrupt spin lock and supplies KeSynchronizeExecution, which device drivers call with the interrupt object pointer returned by IoConnectInterrupt and with a driver-supplied SynchCritSection routine to guarantee multiprocessor-safe access to data shared between another driver routine and the driver’s ISR.

An NT driver can also provide storage for, initialize, and use an executive spin lock, calling KeAcquireSpinLock and KeReleaseSpinLock to manage multiprocessor-safe access to data shared among driver routines other than an ISR. Driver routines that run at IRQL DISPATCH_LEVEL and share state can call KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel, which run faster than KeAcquireSpinLock and KeReleaseSpinLock. Note that a spin lock is not an NT object. A spin lock is, however, an opaque, Kernel-defined synchronization mechanism that uses memory. For guidelines on how to use spin locks and a summary of the default IRQLs at which standard driver routines are called, see Chapter 16.

NT drivers usually allocate storage for their Kernel-defined objects and spin locks (if any) in the device extensions of the device objects they create. Some drivers allocate storage in a controller extension if, like the “AT” disk driver described in Chapter 2, they have a ControllerControl routine. For more information about controller objects, see Section 3.4.

Most NT intermediate and lowest-level drivers use only a device or controller extension to maintain necessary device state and to provide storage for other driver-determined data, such as Kernel-defined objects, spin locks, interlocked queues, and other driver-defined data. However, NT drivers can use the Memory Manager’s support routines when they initialize to allocate contiguous or noncached internal buffers if their devices cannot be serviced adequately without using such buffers. For more information about using memory, see Chapter 16.


3.2 Device Objects and Device Extensions

An NT device object, like the driver object described in Chapter 2, is partially opaque to NT drivers. NT driver writers must know about certain field names and system-defined symbolic constants associated with device objects because their drivers must access these fields through the DeviceObject pointer returned by IoCreateDevice and passed to most standard NT driver routines.

Nevertheless, NT driver writers should be aware that the location of any field that is accessible through a returned DeviceObject pointer can change from one Windows NT® platform or version to another. In addition, NT driver writers should consider “unpublished” fields within an NT object inaccessible: that is, NT drivers should never access them. Any NT driver that has dependencies on NT object field locations or that accesses undocumented fields within an NT object also compromises its own future portability and interoperability with other NT drivers.

Figure 3.2 illustrates an NT device object, representing a physical, logical, or virtual device within the system.

Figure 3.2 Device Object

Figure 3.2 shows field names and constants that are particularly important for NT device and intermediate driver writers.


3.2.1 Defining Device Extensions

For most NT intermediate and physical device drivers, the device extension is the most important data structure associated with a device object. Its internal structure is driver-defined, used to maintain device state information, to provide storage for any Kernel-defined objects or other system resources, such as spin locks, the driver uses, and to hold any data the driver must have resident and in system space to carry out its I/O operations.

The I/O Manager allocates memory for the device extension from resident, system-space memory: that is, IoCreateDevice allocates memory for a device object from nonpaged pool.

Every standard driver routine that is given an IRP is also given a pointer to a device object that represents the target device for an I/O operation, as already mentioned in Chapter 2. Consequently, any driver routine given an IRP can access the corresponding DeviceExtension through this pointer to the appropriate device object. Usually, a DeviceObject pointer is also an input parameter to a lowest-level driver’s ISR.

Figure 3.2 shows a representative set of driver-defined data for the device extension of a lowest-level driver’s device object. A higher-level NT driver would not provide storage for an interrupt object pointer returned by IoConnectInterrupt and passed to KeSynchronizeExecution and IoDisconnectInterrupt. However, a higher-level driver would provide storage for the timer and DPC objects shown in Figure 3.2 if the driver has a CustomTimerDpc routine. A higher-level NT driver also might provide storage for an executive spin lock and interlocked work queue.

In addition to providing storage for an interrupt object pointer, an NT device driver must supply storage for an interrupt spin lock if its ISR handles interrupts for two or more devices on different vectors or if it has more than one ISR. For more information about registering an ISR, see Section 3.5 later in this chapter.

Most NT drivers find it convenient to store pointers to their device objects in their device extensions, as shown in Figure 3.2.

NT higher-level drivers almost always store pointers to the next-lower-level drivers’ device objects in their device extensions. A higher-level driver must pass a pointer to the next-lower driver’s device object to IoCallDriver, after it has set up the next-lower driver’s I/O stack location in an IRP, as explained in Chapter 2.

Note also that any higher-level NT driver that allocates IRPs for lower-level drivers must specify how many stack locations the new IRPs should have. In particular, if a higher-level driver calls IoMakeAssociatedIrp, IoAllocateIrp, or IoInitializeIrp, it must access the target device object of the next-lower-level driver to read its StackSize value, in order to supply the correct StackSize as an argument to these support routines.

While an NT higher-level driver can read data from the next-lower-level driver’s device object through the pointer returned by IoGetDeviceObjectPointer, such a driver must follow these implementation guidelines:

Never attempt to write data to the lower driver’s device object.

The only exceptions to the preceding guideline are NT file systems, which set and clear DO_VERIFY_VOLUME in the Flags of lower-level removable-media drivers’ device objects.

Never attempt to access the lower driver’s device extension for the following reasons:

  • There is no safe way to synchronize access to a single device extension between two NT drivers.

  • A pair of NT drivers that implement such a back-door communication scheme cannot be upgraded individually, cannot have an intermediate driver inserted between them without changing existing driver source, and cannot be recompiled and moved readily from one Windows NT platform to the next.

To preserve their interoperability with lower-level drivers from one Windows NT platform or version to the next, higher-level NT drivers either must reuse the IRPs given them or must create new IRPs, and they must use IoCallDriver to communicate requests to lower-level drivers.


3.2.2 Creating a Device Object and Device Extension

Each NT driver determines the size (StackSize in Figure 3.2) of the device extension when it calls IoCreateDevice to set up a device object.

For most NT drivers, this call is made only from the DriverEntry routine. For some NT drivers, such as disk drivers that must re-create logical device objects dynamically if the user repartitions a disk while the system is running, this call also can be made from a Dispatch routine.

However, every NT driver must call IoCreateDevice one or more times when it initializes to create a named device object to represent each physical, logical, or virtual device for which it handles I/O requests. Otherwise, the driver either will not be loaded if it creates no device object or it will not get IRPs for any target device for which it did not create a device object in its DriverEntry routine.

As shown in Figure 3.2, the caller also passes arguments that determine the following when IoCreateDevice sets up a device object:

  • A system-defined constant, indicating the DeviceType represented by the device object

    For a list of the system-defined FILE_DEVICE_XXX constants, see the DDK master kernel-mode header file, ntddk.h.

  • One or more ORed, system-defined constants indicating the DeviceCharacteristics for certain kinds of devices (floppy, CD-ROM, and WORM removable-media devices)

    Otherwise, the DeviceCharacteristics value passed to IoCreateDevice must be zero.

    NT drivers for removable-media devices also must OR the device object’s Flags with DO_VERIFY_VOLUME if they detect (or suspect) that the media has changed during I/O operations. For more information about how to handle removable media, see Chapter 16.

  • A Boolean (Exclusive) value that specifies whether a bit in the device object’s Flags should be set with DO_EXCLUSIVE, indicating the driver services an exclusive device

    For an explanation of exclusive devices, see the description of interactive device drivers in Chapter 2.

  • A pointer to the DriverObject, which was input to the driver’s DriverEntry routine, associating the driver object with the physical, logical, or virtual device for which the caller handles IRPs

  • A pointer to a zero-terminated Unicode string naming the device

    A DeviceName string must be provided when the driver creates a device object if the corresponding physical, logical, or virtual device must be made “visible” to user-mode callers as a file object, or if a higher-level driver must (or may) chain itself to the creating driver.

    User-mode callers (protected subsystems) cannot carry out device I/O operations for their applications or user-mode drivers without obtaining a handle for a named file object associated with the device object. Higher-level NT drivers cannot chain themselves to the driver of an unnamed device by calling IoGetDeviceObjectPointer or IoAttachDevice. However, an NT file system driver can chain itself to an unnamed mass-storage device object during a mount operation through a volume parameter block (VPB).

For every NT driver except FSDs, the I/O Manager also sets up an associated device queue object for every successful call to IoCreateDevice. The device queue object associated with a device object represents a queue of IRPs bound for a driver’s StartIo routine after the driver is loaded. NT drivers that manage their own internal IRP queues, such as the system floppy controller driver described in Chapter 2, do not use the device queues associated with their device objects.

NT drivers can create additional device queue objects like the NT SCSI port driver, as already mentioned in Section 3.1. For more information about Kernel-defined device queue objects, see Section 3.8 later in this chapter.

If the call to IoCreateDevice succeeds, the I/O Manager provides storage for the device object itself and for all other data structures associated with the device object, including the driver’s device extension, which it initializes with zeros.


3.2.3 Initializing Driver-specific Device Objects and Device Extensions

After IoCreateDevice returns, giving the caller a pointer to a DeviceObject that contains a pointer to the DeviceExtension, the driver can initialize any Kernel-defined objects and other system-defined data structures for which it has provided storage in the device extension.

For example, a lowest-level driver would call IoConnectInterrupt with the address of the PtrToInterruptObject(s) shown in Figure 3.2 to register its ISR, as well as Kernel-supplied support routines to initialize the DpcObject and TimerObject in order to set up a CustomTimerDpc routine. Such a driver would also call KeInitializeSpinLock, passing a pointer to the ExecutiveSpinLock, so the driver could call ExInterlockedInsertTailList and ExInterlockedRemoveHeadList to manage the queueing of IRPs in the interlocked work queue shown in Figure 3.2.

For general guidelines on initializing and using spin locks, see Chapter 16. For more information about interrupt, DPC, and timer objects, see Sections 3.5 and 3.7 later in this chapter. See also Sections 3.6 and 3.9 for other ways to use DPC and timer objects.

NT drivers also must set up certain fields in the device object(s) for their respective physical, logical, and/or virtual devices.

IoCreateDevice sets the StackSize field of a newly created device object to one. A lowest-level NT driver can ignore this field. After any higher-level driver has chained itself over another driver by successfully calling IoGetDeviceObjectPointer, the higher-level driver must set the StackSize field to that of the next-lower-level driver’s device object plus one. This ensures that IRPs sent to the higher-level driver will contain a driver-specific I/O stack location, plus the correct number of I/O stack locations for all lower-level drivers in the chain. If a higher-level driver calls IoAttachDevice or IoAttachDeviceToDeviceStack, those routines automatically set the StackSize field in the device object to the appropriate value.

IoCreateDevice sets the AlignmentRequirement field of a newly created device object to the processor’s data cache line size minus one. To ensure that I/O buffers are aligned correctly, lowest-level physical device drivers must do the following:

  1. Subtract one from the alignment requirement of the device.

  2. Compare the result of Step 1 with the current value of the device object’s AlignmentRequirement.

  3. If the device’s alignment requirement is greater, set AlignmentRequirement to the result of Step 1. Otherwise, leave the AlignmentRequirement value as set by IoCreateDevice.

After any higher-level driver has chained itself over another driver by calling IoGetDeviceObjectPointer, the higher-level driver must set the AlignmentRequirement field of its newly created device object to that of the next-lower-level driver’s device object. If a higher-level driver calls IoAttachDevice or IoAttachDeviceToDeviceStack, those routines automatically set the AlignmentRequirement field in the device object to that of the lower-driver’s device object.

IoGetDeviceObjectPointer returns pointers both to the lower driver’s device object and to the associated file object. Only an FSDs (or, possibly, other highest-level drivers) can use the returned file object pointer. An intermediate driver that calls IoGetDeviceObjectPointer should save this file object pointer so it can be dereferenced by calling ObDereferenceObject when the driver is unloaded.

After an FSD mounts the volume containing the file object that represents a lower driver’s device object, an NT intermediate driver cannot chain itself between the file system and the lower driver by calling IoAttachDevice.

If the caller creates more than one device object, the I/O Manager links its subsequently created device objects to the input DriverObject by maintaining NextDevice pointers in the device objects.

An intermediate or lowest-level NT driver also sets a bit in the device object’s Flags by ORing it either with DO_DIRECT_IO or with DO_BUFFERED_IO, as shown in Figure 3.2, in every device object it creates. Highest-level drivers of logical or virtual devices can avoid setting Flags for either buffered or direct I/O if the driver writer decides the additional work involved will pay off in better driver performance. An NT intermediate driver must set up the Flags field of its device object to match that of the next-lower driver’s device object.

Setting up a device object Flags field with DO_DIRECT_IO or DO_BUFFERED_IO determines how the I/O Manager passes access to user buffers in all data transfer requests subsequently sent to the driver.


3.2.4 Setting Up Access to User Buffers

Most NT device and all intermediate drivers set a bit in the DeviceObject->Flags, shown in Figure 3.2, by ORing the Flags either with DO_BUFFERED_IO or with DO_DIRECT_IO in each device object they create. NT drivers must use one of the following three ways to access user buffers for data transfers:

  • If an NT driver services an interactive (slow) device and/or usually transfers relatively small chunks of data at a time, it should set up its device object(s) requesting buffered I/O. Using buffered I/O for small, interactive transfers improves overall physical memory usage, because the NT Memory Manager need not lock down a full physical page for each transfer, as it does for NT drivers that request direct I/O.

    Generally, NT video, keyboard, mouse, serial, and parallel drivers request buffered I/O.

    Any NT intermediate driver layered above such a device driver must set up its DeviceObject->Flags to match that of the next-lower-level driver.

  • If an NT driver services a device that can transfer large chunks of data at a time, it should set up its device object(s) requesting direct I/O. Using direct I/O for large transfers improves a driver’s performance both by reducing its interrupt overhead and by eliminating the memory allocation and copying operations inherent in buffered I/O.

    Generally, NT mass-storage device drivers request direct I/O for transfer requests, including lowest-level drivers that use DMA or PIO, as well as any intermediate drivers chained above them.

    Even drivers that request direct I/O use buffered I/O to satisfy certain IRPs. In particular, the system-defined, device-type-specific I/O control codes for IRP_MJ_DEVICE_CONTROL requests that require data transfers are buffered, whether a driver has ORed its device objects’ Flags with DO_DIRECT_IO or not.

    Any NT intermediate driver layered above such a device driver must set up its DeviceObject->Flags to match that of the next-lower driver.

  • A driver writer can choose to set up device object(s) requesting neither direct nor buffered I/O if and only if the driver will always be called in the context of the original, user-mode thread that requests an I/O operation. Such a driver must be a highest-level NT driver.

The following subsections describe how ORing device objects’ Flags with DO_BUFFERED_IO, DO_DIRECT_IO, or neither affects data transfer requests sent to NT drivers, including how the data can be stored in physical memory and how a driver can access that memory.


3.2.4.1 Using Buffered I/O

Figure 3.3 illustrates how the I/O Manager sets up an IRP requesting a transfer operation for drivers that OR their device object(s)’ Flags with DO_BUFFERED_IO.

Figure 3.3 Buffered I/O for User Buffers

As Figure 3.3 shows, some range of user-space virtual addresses represents the current thread’s buffer, and that buffer’s contents might be stored somewhere within a range of page-based physical addresses.

Figure 3.3 also shows an overview of how drivers can use the SystemBuffer pointer in the IRP to transfer data for a read request, when a driver has ORed the device object’s Flags with DO_BUFFERED_IO:

  1. The I/O Manager services the current thread’s read request, for which the thread passes a range of user-space virtual addresses representing a buffer.

  2. The I/O Manager checks the user-supplied buffer for accessibility and calls ExAllocatePool to create a resident system-space buffer the size of the user-supplied buffer. It provides access to the newly allocated SystemBuffer in the IRP it sends to the driver.

    If Figure 3.3 showed a write request, the I/O Manager would copy data from the user buffer into the system buffer before it sent the IRP to the driver.

  3. For the read request shown in Figure 3.3, the driver reads data from the device into the system-space buffer.

  4. When the driver has called IoCompleteRequest with the IRP and the original thread is again current, the I/O Manager copies the read-in data from the system buffer into the user buffer. It also calls ExFreePool to release the system buffer and disposes of the IRP.

After the I/O Manager has created a system-space buffer for the driver, the requesting user-mode thread can be swapped out and its physical memory can be reused by another thread, possibly by a thread belonging to another process. However, the system-space virtual address range supplied in the IRP remains valid until the driver calls IoCompleteRequest with the IRP.

NT drivers for devices that do not transfer large amounts of data at a time, such as interactive devices, can use buffered I/O.

NT drivers that transfer large amounts of data at a time, in particular, drivers that do multipage transfers, should not attempt to use buffered I/O. As the system runs, nonpaged pool can become fragmented so that the I/O Manager cannot allocate large, contiguous system-space buffers to send in IRPs for such a driver.

Note that all NT drivers use buffered I/O for certain IRP_MJ_XXX. Even NT drivers that set up their device objects for direct I/O use buffered I/O for most requests except IRP_MJ_READ, IRP_MJ_WRITE, and, possibly, driver-defined IRP_MJ_INTERNAL_DEVICE_CONTROL requests that require large data transfers.


3.2.4.2 Using Direct I/O

Figure 3.4 illustrates how the I/O Manager sets up an IRP, requesting a transfer operation, for drivers that OR their device object(s)’ Flags with DO_DIRECT_IO.

Figure 3.4 Direct I/O on User Buffers

As Figure 3.4 shows, some range of user-space virtual addresses represents the current thread’s buffer, and that buffer’s contents might actually be stored on some number of physically discontiguous pages. An MDL is created to describe this buffer. As already mentioned in Section 3.1, an MDL is an opaque Memory-Manager-defined data structure that maps a particular virtual address range to one or more paged-based physical address ranges.

Figure 3.4 also shows an overview of how drivers can use the IRP’s MdlAddress to transfer data for a read request, when a driver has ORed the device object’s Flags with DO_DIRECT_IO:

  1. The I/O Manager services the current thread’s read request, for which the thread passes a range of user-space virtual addresses representing a buffer.

  2. The I/O Manager or FSD checks the user-supplied buffer for accessibility and calls MmProbeAndLockPages with an MDL, which specifies the range of virtual addresses for the user buffer. MmProbeAndLockPages also fills in the corresponding physical address range(s) in the MDL. As Figure 3.4 shows, an MDL for a virtual range can have several corresponding page-based physical address entries, and the virtual range for a buffer might begin and end at some byte offset from the start of the first and last pages described by an MDL. Note that an MDL is opaque to drivers.

    As Figure 3.4 also shows, the I/O Manager provides a pointer to this MDL (MdlAddress) in an IRP that requests a transfer operation. Until the I/O Manager or file system calls MmUnlockPages after the driver completes the IRP, the physical pages described in the MDL remain locked down and assigned to the buffer. However, the virtual addresses in such an MDL can become invisible (and invalid), even before the IRP is sent to the device driver or to any intermediate driver that might be layered above the device driver.

  3. If the device driver uses system or packet-based busmaster DMA, it calls MmGetMdlVirtualAddress with the IRP’s MdlAddress pointer to get an index for the MDL’s page-based entries. If the device uses PIO and the driver requires system (virtual) addresses, the driver calls MmGetSystemAddressForMdl with the IRP’s MdlAddress pointer to doubly map the user-space virtual addresses in the MDL to a system-space address range (AliasBuff in Figure 3.4).

  4. If the device driver uses system or packet-based busmaster DMA, it calls IoMapTransfer with the index returned by MmGetMdlVirtualAddress in order to read data from the device directly into physical memory when the driver’s AdapterControl routine has access to a DMA channel and/or map registers. If the device uses PIO, the driver uses the doubly mapped MDL’s system-space virtual address range to read data into memory.

When the driver completes the IRP by calling IoCompleteRequest, the I/O Manager or file system releases the MDL’s doubly mapped system-space range if the driver called MmGetSystemAddressForMdl. The I/O manager or file system unlocks the pages described in the MDL, and disposes of the MDL and IRP on the driver’s behalf. For better performance, NT drivers should avoid doubly mapping MDL physical addresses to system space, as described in Step 3, unless they must use virtual addresses. Releasing a doubly mapped system-space address range causes every processor in the machine to have its data cache flushed.

The current user thread’s buffer(s) and the thread itself are guaranteed to be resident in physical memory only while that thread is current. For the thread shown in Figure 3.4, its user buffer’s contents could be paged out to secondary storage while another process’s threads are run. When another process’s thread is run, the system physical memory for the requesting thread’s buffer can be overwritten unless the NT Memory Manager has locked down and preserved the corresponding physical pages that contain the original thread’s buffer.

However, the original thread’s virtual addresses for its buffer do not remain visible while another thread is current, even if the Memory Manager does preserve the buffer’s physical pages. Consequently, NT drivers cannot use a virtual address returned by MmGetMdlVirtualAddress to access memory. Callers of this routine must pass its results to IoMapTransfer (along with the IRP’s MdlAddress pointer) in order to transfer data using system or packet-based DMA.

NT drivers that use DMA also must use an adapter object, described later in Section 3.3. For more information on maintaining cache coherency during DMA and PIO transfers, see also Chapter 16.


3.2.4.3 Using Neither Direct Nor Buffered I/O

Setting up device objects for neither direct nor buffered I/O causes the I/O Manager to pass the original user-space virtual addresses in IRPs sent to the driver. Because such a driver must be executing in the context of the calling thread to access its buffer(s) safely, only highest-level NT drivers, such as FSDs, can set up their device objects without setting a bit in each device object’s Flags by ORing it with DO_DIRECT_IO or DO_BUFFERED_IO.

Note that an NT intermediate or lowest-level driver cannot always meet this condition. For example, if a requesting thread waits on the completion of an I/O request or if a higher-level driver (particularly a file system) is layered over the intermediate or lowest-level driver, such a lower-level driver’s routines are unlikely to be called in the context of the requesting thread.

When the I/O Manager sends an IRP with the current thread’s user-space virtual addresses for a buffer, a driver that did not OR its device object(s)’ Flags with DO_BUFFERED_IO or DO_DIRECT_IO must do the following:

  1. Check the validity of the user buffer’s address range and check whether the appropriate read or write access is permitted to the buffer. Such a driver must wrap its accesses to the buffer’s address range within a driver-supplied exception handler in case a user thread attempts to change the access rights for the buffer while the driver is accessing memory.

  2. Do one of the following:

    • Carry out its own double-buffering operations, as the I/O Manager does for drivers that use buffered I/O.

    • Create its own MDLs and lock down the buffer by calling the Memory Manager’s support routines, as the I/O Manager does for drivers that use direct I/O.

    • Perform all necessary operations on the user buffer directly in the context of the calling thread. Such a driver must wrap its access to the buffer within a driver-supplied exception handler in case a user thread changes either the access rights for the buffer or the data in the buffer while the driver is accessing memory.

In effect, such a driver must choose on a per-IRP basis whether to do buffered I/O, direct I/O, or I/O in the context of the calling thread, and it must handle any exceptions that might occur in a user-mode thread context. Such a driver must manage its own user buffer accesses, double-buffering operations, and memory mappings, as necessary, instead of letting the I/O Manager handle these operations for the driver.


3.3 Adapter Objects and DMA

An NT device driver must use a system-created adapter object if it sets up its device objects for direct I/O and its device uses DMA, as already mentioned in Section 3.2.4.2.

Two kinds of lowest-level NT drivers must use adapter objects:

  • Drivers whose devices use the system DMA controller (also called slave devices)

    Such a device is said to “use system DMA."

  • Drivers whose devices are busmaster adapters

    Such a device, which arbitrates with the system for use of the I/O bus, is said to “use busmaster DMA.”

When these kinds of device drivers initialize, they call the platform-specific NT HAL to create a set of adapter objects, representing DMA controller channels or ports and busmaster devices. Each HAL, like the NT SCSI port driver described in Chapter 2, is implemented as a dynamic-link library so that NT drivers can link themselves against each HAL (or SCSI miniport drivers against the NT SCSI port driver) and call the same support routines on every NT platform.

For any machine running Windows NT, the set of HAL-created adapter objects usually includes the following:

  • An adapter object for each system DMA controller channel or port to which a slave device is attached

  • An adapter object for each busmaster DMA device in the machine

Note that the NT SCSI port driver is responsible for creating and managing adapter objects for SCSI HBAs. An HBA-specific miniport driver supplies the necessary data for the NT SCSI port driver to create a corresponding adapter object when the miniport driver’s HwScsiGetConfigInfo routine executes. For more information about SCSI miniport drivers, see Appendix A.


3.3.1 Map Registers

An NT HAL must set up adapter objects that support DMA for a wide variety of DMA devices and types of I/O buses on different machines. For example, most ISA DMA controllers, slave devices, and busmaster devices do not have enough address lines to access the full four-gigabyte system physical address space of a 32-bit CPU. By contrast, EISA DMA devices generally have more than enough address lines to access the full system physical address space in 32-bit microprocessors. Consequently, every NT HAL provides mappings between the logical address ranges that DMA devices can access and system physical address ranges.

Each HAL uses map registers, associated with NT adapter objects, to represent its mappings of device-accessible logical address ranges to system physical address ranges. During DMA transfers, the HAL uses each map register to alias a device-accessible logical page to a page of physical memory in the CPU. In effect, map registers provide scatter/gather support for NT drivers that use DMA whether their devices have scatter/gather capabilities or not.

Figure 3.5 illustrates such a physical-to-logical address mapping for the driver of an ISA DMA device without scatter/gather capabilities.

Figure 3.5 Physical, Logical, and Virtual Address Mappings

As Figure 3.5 shows, three map registers are used to alias three paged ranges of data in system physical memory to three page-sized ranges of low-order logical addresses for an ISA DMA device. During DMA operations, the device would use logical addresses to access system memory. For a comparable EISA DMA device, three map registers would also be used for three page-sized ranges of data. However, the mapped logical address ranges would not necessarily be identical to the corresponding physical address ranges, so an EISA device would also use logical addresses to access system memory.

Note also the correspondence between a map register and a virtual-to-physical entry in the MDL shown in Figure 3.5:

  • Each map register and each virtual entry in an MDL maps at most a full physical page of data for a DMA transfer operation.

  • Each map register and each virtual entry in an MDL might map less than a full page of data. For example, the initial virtual entry in an MDL can map to an offset from the physical page boundary, as shown previously in Figure 3.4.

  • Each map register and each virtual entry in an MDL maps, at the very least, one byte.

In IRPs requesting a read or write operation, each virtual entry in the opaque-to-drivers MDL at Irp->MdlAddress represents a page boundary in the system physical memory for a user buffer. Similarly, each additional map register needed for a single DMA transfer represents a page boundary in the device-accessible logical address range aliased to system physical memory.

On every Windows NT platform, each NT adapter object has an associated set of one or more map registers located at a platform-specific (and opaque-to-drivers) base address. From an NT driver’s point of view, the MapRegisterBase shown in Figure 3.5 is a handle for a set of map registers that could be hardware registers in a chip, in a system DMA controller, or in a busmaster adapter, or could even be HAL-created virtual registers in system memory.

However, the number of map registers available with an adapter object can vary for different devices and Windows NT platforms. For example, the HAL can make more map registers available to drivers that use system DMA on some platforms than on others because the DMA controllers in different Windows NT platforms have different capabilities.


3.3.2 Getting an NT Adapter Object

When it initializes, an NT driver that uses system or busmaster DMA calls HalGetAdapter in order to get a pointer to a HAL-created adapter object and to determine the maximum number of map registers available for each transfer operation. Figure 3.6 illustrates such a call to HalGetAdapter.

Figure 3.6 Getting an Adapter Object

As Figure 3.6 shows, the driver supplies certain kinds of information to HalGetAdapter in a system-defined DEVICE_DESCRIPTION structure. The required data includes information about the features of the driver’s device, such as whether the device is a busmaster, whether it has scatter/gather capabilities, and how many bytes of data the device can transfer at a time (MaximumLength). Note that MaximumLength effectively specifies the maximum number of map registers the driver could use for each transfer operation, assuming the HAL could make an unlimited number of map registers available on every Windows NT platform.

The required device description data also includes certain platform-specific information, which the driver can obtain when it finds its adapter by calling HalGetBusData or HalGetBusDataByOffset or from the registry, such as the platform-specific and system-assigned number of the bus that a driver of a busmaster device controls. For more information about using the registry, see Chapter 16.

As Figure 3.6 also shows, the DEVICE_DESCRIPTION structure includes some fields that might be irrelevant to some DMA devices on any Windows NT platform. For example, an EISA or ISA DMA device would not use a particular DmaPort as a microchannel-type device would. Each NT driver should supply appropriate values for device description members that are relevant and should set the values for all other members to zero.

The driver of a slave device should not claim it supports scatter/gather unless the device is capable of waiting for the system DMA controller to be reprogrammed when a request must be broken up into two or more DMA operations.

All NT drivers must initialize the DeviceDescription area by calling RtlZeroMemory before they set relevant values in it.

As Figure 3.6 also shows, HalGetAdapter returns both a pointer to an adapter object and a platform-specific or device-specific value indicating how many map registers are available with the adapter object for each DMA transfer operation. As mentioned in Section 3.3.1, this value can vary from device to device and from platform to platform. Generally, the HAL assigns values according to the following criteria:

  • If possible, the HAL returns a value that is one more than the number of map registers needed to transfer MaximumLength bytes, as specified in the driver’s call to HalGetAdapter.

  • Otherwise, the HAL returns a lesser value that is as large as possible for the particular platform.

In other words, the NT HAL usually gives each driver enough map registers to maximize DMA throughput for its device, but the HAL can return a lesser value on some Windows NT platforms.

Any DMA device driver must provide storage for the adapter object pointer and NumberOfMapRegisters value returned by HalGetAdapter. This pointer is a required parameter to every Io Xxx and/or Hal Xxx used for DMA. Because many of these support routines must be called at IRQL DISPATCH_LEVEL, the driver-allocated storage must be resident. Most DMA device drivers provide the necessary storage in a device extension (see Section 3.2.1). However, the storage can be in a controller extension if the driver also uses a controller object (see Section 3.4) or in nonpaged pool, allocated by the driver. For more information about allocating nonpaged pool memory and about managing IRQLs, see Chapter 16.


3.3.3 Splitting Transfer Requests

Any NT driver might need to split up a transfer request and carry out more than one DMA transfer operation to satisfy a given IRP, depending on the following:

  • The NumberOfMapRegisters returned by HalGetAdapter on a given platform

  • The value of Length (bytes of data to be transferred) in the driver’s I/O stack location in the current IRP

  • The number of page boundaries in system physical memory for the buffer into which or from which the driver is to transfer data

  • Any device-specific constraints on the driver’s DMA operations

NT drivers can use the macro ADDRESS_AND_SIZE_TO_SPAN_PAGES to determine how many map registers are needed to transfer all the data requested in a given IRP, as follows:

  1. Call MmGetMdlVirtualAddress, passing a pointer to the MDL at Irp->MdlAddress, to get the starting virtual address for the buffer. Note that a driver must not attempt to access memory using this virtual address. The value returned by MmGetMdlVirtualAddress is an index into the MDL, not necessarily a valid address.

  2. Pass the returned index and the value of Length in the driver’s I/O stack location of the IRP to ADDRESS_AND_SIZE_TO_SPAN_PAGES.

If the value returned by ADDRESS_AND_SIZE_TO_SPAN_PAGES is greater than the NumberOfMapRegisters returned by HalGetAdapter, the driver cannot transfer all requested data for this IRP in a single DMA operation. Instead, it must do the following:

  • Split the buffer into pieces that are sized to suit the number of available map registers and any device-specific DMA constraints.

  • Carry out as many DMA operations as it takes to satisfy the current transfer request.

For example, suppose ADDRESS_AND_SIZE_TO_SPAN_PAGES indicates that twelve map registers are needed to satisfy a given transfer request but a driver has only five available map registers on this platform and no device-specific DMA constraints. Such a driver must carry out three (12/5 + 1 = 3) DMA transfer operations (and call IoMapTransfer at least three times) in order to transfer all the data requested by this IRP.

The system DMA device drivers use various techniques to split up a DMA transfer when there are not enough map registers to satisfy a given IRP with a single DMA transfer operation. For example, the SCSI class drivers are required to split up large transfer requests for the underlying NT SCSI port/miniport drivers. A system SCSI class driver allocates an additional IRP for each piece of a given transfer request, registers its IoCompletion routine with each IRP to track the status of the full transfer request and to free the driver-allocated IRP(s), and sends the IRP(s) on to the port driver with IoCallDriver, as explained in Chapter 2.

However, other class/port drivers can use this technique only if the class driver can determine how many map registers are available to the port driver on each Windows NT platform. In other words, the port driver must store this configuration information in the registry for the paired class driver, or such a pair of drivers must define a private interface, using internal device I/O control requests, to pass configuration information about the number of available map registers from the port driver to the class driver.

Other class/port drivers need not use this technique, and a monolithic DMA device driver must split up large transfer requests for itself. Such drivers usually split a large request into pieces and carry out a sequence of DMA operations in order to satisfy the current IRP.

The following sections, which describe how monolithic drivers of DMA devices use support routines to satisfy transfer requests, assumes the following:

  • The driver has a standard StartIo routine, rather than setting up and managing an internal queue of IRPs.

  • The driver has an internal routine to split those transfer requests for which an insufficient number of map registers is available and has no device-specific DMA constraints.

In other words, these sections describe the simplest possible technique for NT device drivers’ DMA operations, but individual NT drivers do not necessarily use exactly the same techniques. For any NT driver of a DMA device, the driver writer determines which driver routine(s) should split up large DMA transfer requests, depending on the driver model (class/port, monolithic, etc.), on the device’s features, and on any device-specific DMA constraints that driver must handle.


3.3.4 Using System DMA

NT drivers of slave DMA devices use one of the following types of system-provided DMA support:

  • Packet-based DMA if the driver need not use a system DMA controller’s autoinitialize mode

  • Common-buffer DMA if the driver does use the autoinitialize mode

The next two subsections explain each of these types of DMA support separately.


3.3.4.1 Packet-Based System DMA

NT drivers of slave devices that use packet-based DMA call the following general sequence of support routines as they process an IRP requesting a DMA transfer:

  1. KeFlushIoBuffers just before attempting to allocate the system DMA controller

    See Chapter 16 for more information about maintaining cache coherency during DMA with KeFlushIoBuffers and IoFlushAdapterBuffers.

  2. IoAllocateAdapterChannel when the driver is ready to program its device for DMA and needs the system DMA controller

  3. MmGetMdlVirtualAddress to get an index into the MDL, required as an parameter in the initial call to IoMapTransfer, and IoMapTransfer to program the system DMA controller for the transfer operation

    Note that a driver might need to call IoMapTransfer more than once to transfer all the requested data, as explained in Section 3.3.3.

  4. IoFlushAdapterBuffers just after each DMA transfer operation to/from the slave device

    If a driver must call IoMapTransfer more than once to transfer all the requested data, it must call IoFlushAdapterBuffers as many times as it calls IoMapTransfer.

  5. IoFreeAdapterChannel as soon as all the requested data has been transferred or the driver fails the IRP because of a device I/O error

The AdapterObject pointer returned by HalGetAdapter is a required parameter to each of these routines except KeFlushIoBuffers and MmGetMdlVirtualAddress, which require a pointer to the MDL at Irp->MdlAddress.

Individual NT drivers call this sequence of support routines at different points, depending on how each driver is implemented to service its device. For example, one driver’s StartIo routine might make the call to IoAllocateAdapterChannel, another driver might make this call from a routine that removes IRPs from a driver-created interlocked queue, and still another driver might make this call when its slave DMA device indicates it is ready to transfer data.

3.3.4.1.1 Allocating an Adapter Channel for Packet-Based DMA

A driver calls KeFlushIoBuffers and IoAllocateAdapterChannel after its Dispatch routine for IRP_MJ_READ and/or IRP_MJ_WRITE requests, or for any other request that requires a DMA transfer, has already checked the validity of the IRP’s parameters (if necessary), possibly queued the IRP to another driver routine for further processing, and the transfer request is the current IRP requiring a device I/O operation. Figure 3.7 illustrates such a call to IoAllocateAdapterChannel.

Figure 3.7 Allocating the System DMA Controller

The driver routine that calls IoAllocateAdapterChannel must be executing at IRQL DISPATCH_LEVEL when this call occurs. For more information about the IRQLs at which NT drivers’ standard routines execute, see Chapters 5 through 15. For more information about support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.

As Figure 3.7 shows, the driver must supply more than a pointer to the adapter object returned by HalGetAdapter when it calls IoAllocateAdapterChannel. Along with a pointer to the target device object, it must supply the entry point for its AdapterControl routine and a pointer to any driver-determined context information the AdapterControl routine will use.

IoAllocateAdapterChannel queues the driver’s AdapterControl routine, which executes when the system DMA controller is assigned to this driver and a set of map registers (described in Section 3.3.1) has been allocated for the driver’s DMA operation(s).

On entry, an AdapterControl routine is given the DeviceObject and Context pointers passed in the call to IoAllocateAdapterChannel, as well as a handle (MapRegisterBase) for the allocated map registers, as shown in Figure 3.7.

The AdapterControl routine also is given a pointer to the DeviceObject->CurrentIrp if the driver has a StartIo routine. If a driver manages its own queueing of IRPs (instead of having a StartIo routine), the driver should include a pointer to the current IRP as part of the context it passes when it calls IoAllocateAdapterChannel.

The AdapterControl routine usually does the following:

  1. Saves or initializes whatever context the driver maintains about DMA operations, such as saving the input MapRegisterBase handle the driver must pass to IoMapTransfer and IoFlushAdapterBuffers and, possibly, the Length of the requested transfer from its I/O stack location in the IRP

  2. Calls MmGetMdlVirtualAddress followed by IoMapTransfer (described in the next section)

  3. Sets up the slave device to start the transfer operation

  4. Returns the value KeepObject

As Figure 3.7 shows, an AdapterControl routine must return a system-defined value of type IO_ALLOCATION_ACTION. For NT drivers that use system DMA, the AdapterControl routine must return the value KeepObject. This allows the driver to retain "ownership” of the system DMA controller and allocated map registers until it has transferred all the requested data.

Note that an AdapterControl routine cannot wait for the slave device to carry out the DMA operation, so an AdapterControl routine must at least do the following:

  1. Save context information, particularly the MapRegisterBase handle, in the driver’s device extension, controller extension, or other driver-accessible resident storage area (nonpaged pool allocated by the driver).

  2. Return KeepObject.

Another driver routine (probably the DpcForIsr) must call IoFlushAdapterBuffers when each DMA transfer operation is complete. This routine also must call IoMapTransfer and IoFlushAdapterBuffers again if it is necessary to set up the DMA controller more than once to satisfy the current IRP’s transfer request.

When a driver has satisfied the current IRP’s request, it must call IoFreeAdapterChannel. This support routine should be called immediately following the last call to IoFlushAdapterBuffers for the current IRP so that other drivers and this driver can use the system DMA controller to satisfy other transfer requests expeditiously.

Note that the driver of a slave device with scatter/gather capabilities should also return KeepObject from its AdapterControl routine. Such a device must be capable of waiting while the system DMA controller is reprogrammed between DMA operations when the driver must split up a given DMA request. On some Windows NT platforms, these kinds of devices can transfer at most a page of data per DMA operation because the HAL can assign only a single map register to the driver of such a device.

3.3.4.1.2 Setting Up the System DMA Controller for Packet-Based DMA

When IoAllocateAdapterChannel transfers control to a driver’s AdapterControl routine, the driver “owns” the system DMA controller and a set of map registers. Then, the DMA controller must be set up for a transfer operation by calling the following routines:

  1. MmGetMdlVirtualAddress with the MDL at Irp->MdlAddress to get an index for the system physical address where the transfer should start

    The return value is a required parameter (CurrentVa) to IoMapTransfer.

  2. IoMapTransfer to set up the system DMA controller before the driver sets up its device for the transfer operation

Figure 3.8 illustrates such a call to IoMapTransfer.

Figure 3.8 Programming the System DMA Controller

As Figure 3.8 shows, the driver supplies the following parameters to IoMapTransfer:

  • The AdapterObject pointer returned by HalGetAdapter (see Section 3.3.2)

  • A pointer (Mdl) to the MDL at Irp->MdlAddress for the current IRP

  • The MapRegisterBase handle passed to the driver’s AdapterControl routine by IoAllocateAdapterChannel

  • The value (CurrentVa) returned by MmGetMdlVirtualAddress if this is the first call to IoMapTransfer for the IRP

    Otherwise, the driver should supply an updated CurrentVa value, indicating where in the buffer the next transfer operation should start. (How to calculate an updated CurrentVa is described later.)

  • A pointer to a variable (Length, shown in Figure 3.8 as IrpLengthNextTransfer)

    If the driver can transfer all the requested data with a single call to IoMapTransfer and has no device-specific constraints on its DMA operations, Length can be set to the value of Length in the driver’s I/O stack location of the IRP. At most, the length in bytes can be (PAGE_SIZE * the NumberOfMapRegisters returned by HalGetAdapter). Otherwise, the driver must split up the request, as explained in Section 3.3.3 and must update the value of Length in subsequent calls to IoMapTransfer for the current IRP.

  • A Boolean value (WriteToDevice), indicating the direction of the transfer operation (TRUE for a requested transfer from system memory to the device)

IoMapTransfer returns a logical address, which drivers that use system DMA must ignore. When IoMapTransfer returns control, the driver should set up its device for the DMA operation. When the device indicates that its current DMA operation has completed, the driver should call IoFlushAdapterBuffers, usually from the driver’s DpcForIsr routine.

Note that the DpcForIsr or another driver routine that completes a DMA operation calls IoFlushAdapterBuffers to ensure that any data cached in the system DMA controller is read into system memory or written out to the device. The same routine also must call IoMapTransfer again if it is necessary to reprogram the system DMA controller to transfer more data for the current IRP and IoFlushAdapterBuffers again following each transfer operation.

If a driver must call IoMapTransfer more than once for the current IRP, it supplies the same AdapterObject pointer, Mdl pointer, MapRegisterBase handle, and transfer direction in every call. However, the driver must update the CurrentVa and Length parameters before it makes the second and any subsequent calls to IoMapTransfer. To calculate an updated value for each of these parameters, use the following formulas:

  • CurrentVa = CurrentVa + (Length requested in the preceding call to IoMapTransfer)

  • Length = Minimum (remaining Length to be transferred, (PAGE_SIZE * NumberOfMapRegisters returned by HalGetAdapter))

The context information each driver maintains about its DMA transfers, such as the IrpLengthNextTransfer, IrpLengthDMAedSoFar, and MdlCurrentVa shown in Figure 3.8, depends on the driver writer.

When all the requested transfer is complete or the driver must return an error status for the IRP, the driver should call IoFreeAdapterChannel promptly to release the system DMA controller for other drivers and this driver to use.


3.3.4.2 Common-Buffer System DMA

A driver that uses a system DMA controller’s autoinitialize mode must allocate memory for a buffer into which or from which DMA transfers can be carried out. Such a driver must call HalAllocateCommonBuffer when it initializes in order to provide this buffer. Figure 3.9 illustrates such a driver’s call to HalAllocateCommonBuffer.

Figure 3.9 Allocating a Common Buffer for System DMA

As Figure 3.9 shows, the driver must pass a pointer to the adapter object that was returned by HalGetAdapter, along with the length in bytes requested for its buffer. To use memory economically, the input Length value for such a buffer either should be less than or equal to PAGE_SIZE or should be an integral multiple of PAGE_SIZE.

If the call succeeds, HalAllocateCommonBuffer returns two different types of pointers to the buffer:

  • The LogicalAddress of the buffer (BuffLogicalAddress in Figure 3.9), for which the driver must provide storage but which it should ignore thereafter

  • The virtual address of the buffer (BuffVirtualAddress in Figure 3.9), which the driver also must store so that it can build an MDL describing its buffer for DMA operations

If HalAllocateCommonBuffer returns a NULL pointer, the driver should free any system resources it has already claimed, call IoDeleteDevice to release any device objects it has already created, and so forth. The DriverEntry routine also should return STATUS_INSUFFICIENT_RESOURCES, because the driver must not be loaded when it cannot respond to data transfer requests.

Otherwise, the driver must call IoAllocateMdl with the VirtualAddress of the buffer returned by HalAllocateCommonBuffer and the Length of its buffer to allocate an MDL. It should then call MmBuildMdlForNonPagedPool with the pointer returned by IoAllocateMdl to map the virtual address range for its resident buffer to system physical memory.

NT drivers of slave devices that use a common buffer for DMA call the following general sequence of support routines as they process an IRP requesting a DMA transfer:

  1. At the driver writer’s discretion, RtlMoveMemory to copy data from a locked-down user buffer into the driver-allocated common buffer for a transfer to the device

  2. IoAllocateAdapterChannel when the driver is ready to program its device for DMA and needs the system DMA controller

  3. IoMapTransfer with the MDL, describing the driver-allocated common buffer, to set up the system DMA controller for the transfer operation

    Note that such a driver calls IoMapTransfer only once to set up the system DMA controller to use its common buffer. During a transfer, the driver can call HalReadDmaCounter to determine how many bytes remain to be transferred, and if necessary, call RtlMoveMemory to copy more data to or from a user buffer.

  4. IoFlushAdapterBuffers when the driver has completed its DMA transfer to/from the slave device

  5. IoFreeAdapterChannel as soon as all the requested data has been transferred or the driver fails the IRP because of a device I/O error

The AdapterObject pointer returned by HalGetAdapter is a required parameter to each of these support routines except RtlMoveMemory.

Individual NT drivers call this sequence of support routines at different points, depending on how each driver is implemented to service its device. For example, one driver’s StartIo routine might make the call to IoAllocateAdapterChannel, another driver might make this call from a routine that removes IRPs from a driver-created interlocked queue, and still another driver might make this call when its slave DMA device indicates it is ready to transfer data.

3.3.4.2.1 Allocating an Adapter Channel for Common-Buffer System DMA

A driver calls IoAllocateAdapterChannel after its Dispatch routine for IRP_MJ_READ and/or IRP_MJ_WRITE requests, or for any other request that requires a DMA transfer, has already checked the validity of the IRP’s parameters (if necessary), queued one or more IRPs to another driver routine for further processing, and possibly loaded its common buffer with data to be transferred. See Figure 3.7 for an illustration of a call to IoAllocateAdapterChannel.

The driver routine that calls IoAllocateAdapterChannel must be executing at IRQL DISPATCH_LEVEL when this call occurs. For more information about the IRQLs at which NT drivers’ standard routines execute, see Chapters 5 through 15. For more information about support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.

IoAllocateAdapterChannel queues the driver’s AdapterControl routine, which executes when the system DMA controller is assigned to this driver and a set of map registers (described in Section 3.3.1) has been allocated for the driver’s DMA operation.

On entry, an AdapterControl routine is given pointers to the device object and context passed in the call to IoAllocateAdapterChannel, as well as a handle for the allocated map register(s). The AdapterControl routine also is given a pointer to the DeviceObject->CurrentIrp if the driver has a StartIo routine. If the driver manages its own queueing of IRPs instead of having a StartIo routine, the driver should include a pointer to the current IRP as part of the context data it passes when it calls IoAllocateAdapterChannel.

The AdapterControl routine usually does the following:

  1. Saves or initializes whatever context the driver maintains about DMA operations, such as saving the MapRegisterBase handle the driver must pass to IoMapTransfer and IoFlushAdapterBuffers and, possibly, the Length of transfer requested from its I/O stack location in the IRP

  2. Sets up the slave device to start the transfer operation

  3. Returns the value KeepObject

For NT drivers that use a system DMA controller’s autoinitialize mode, the AdapterControl routine must return the value KeepObject. This allows the driver to retain “ownership” of the system DMA controller and allocated map register(s) until it has transferred all the data.

Note that an AdapterControl routine cannot wait for the slave device to carry out the DMA operation, so an AdapterControl routine must at least do the following:

  1. Save context information, particularly the MapRegisterBase handle, in the driver’s device extension, controller extension, or other driver-accessible resident storage area (nonpaged pool allocated by the driver).

  2. Return KeepObject.

Another driver routine (probably the DpcForIsr) must call IoFlushAdapterBuffers and IoFreeAdapterChannel when the DMA transfer operation is complete.

3.3.4.2.2 Setting Up the System DMA Controller for Common-Buffer DMA

When IoAllocateAdapterChannel transfers control to a driver’s AdapterControl routine, the driver “owns” the system DMA controller and a set of map registers. Then, the driver must call IoMapTransfer to set up the system DMA controller to use the driver-allocated common buffer before the driver sets up its device for the transfer operation. See Figure 3.8 for an illustration of calls to IoMapTransfer.

The driver supplies the following parameters to IoMapTransfer:

  • The AdapterObject pointer returned by HalGetAdapter (see Section 3.3.2)

  • A pointer to the MDL describing the driver-allocated common buffer (see Section 3.3.4.2)

  • The MapRegisterBase handle passed to the driver’s AdapterControl routine by IoAllocateAdapterChannel

  • A pointer to a variable (Length) indicating the size in bytes of the driver-allocated common buffer

  • A Boolean value, indicating the direction of the transfer operation (TRUE for a requested transfer from system memory to the device)

IoMapTransfer returns a logical address, which drivers that use system DMA must ignore. When IoMapTransfer returns control, the driver should set up its device for the DMA operation. Note that such a driver calls IoMapTransfer only once but continues to copy data between its common buffer and a locked-down user buffer until the requested transfer is done.

The driver can call HalReadDmaCounter to determine how many bytes currently remain to be transferred in the common buffer, so such a driver can continue to fill its common buffer with user data or copy data from its common buffer to the user buffer.

When all the requested transfer is complete or the driver must return an error status for the IRP, the driver calls IoFlushAdapterBuffers to ensure that any data cached in the system DMA controller is read into system memory or written out to the device. Then, the driver should call IoFreeAdapterChannel promptly to release the system DMA controller for other drivers and this driver to use.


3.3.5 Using Busmaster DMA

NT drivers of busmaster DMA devices can use the following kinds of system-supplied DMA support:

  • Packet-based DMA if the busmaster adapter is such that the driver can determine when a DMA transfer operation is done and/or when to begin another transfer operation for a given IRP

  • Common-buffer DMA (also called continuous DMA) if the busmaster adapter is such that the driver cannot determine readily when a transfer operation will begin or when a transfer is complete, or if a single buffer area is used continuously or repeatedly for DMA transfers

Depending on the nature of the busmaster adapter, some NT drivers use packet-based DMA exclusively, some use common-buffer DMA exclusively, and some use both. For example, the driver of a busmaster adapter that uses a mailbox scheme to communicate status information and commands might use a common buffer for the mailboxes shared between the driver and its adapter, together with packet-based DMA for data transfers.

Setting up a common buffer can tie up some (or all, depending on the size of the requested buffer) of the map registers associated with the adapter object that represents the busmaster adapter, so consider the following a design guideline:

Use common-buffer DMA economically.

Setting up common-buffer area(s) economically, such as in PAGE_SIZE chunks or in a single allocation, leaves more map registers available for such a driver’s packet-based DMA operations, if any. It also leaves more system memory free for other purposes, which yields better overall driver and system performance.

The following subsections explain each kind of busmaster DMA separately.


3.3.5.1 Packet-Based Busmaster DMA

To use packet-based DMA, NT drivers of busmaster DMA devices call the following general sequence of support routines as they process an IRP requesting a DMA transfer:

  1. KeFlushIoBuffers just before attempting to allocate map registers for a transfer request

    See Chapter 16 for more information about maintaining cache coherency during DMA with KeFlushIoBuffers and IoFlushAdapterBuffers.

  2. IoAllocateAdapterChannel when the driver is ready to program the busmaster adapter for DMA

  3. MmGetMdlVirtualAddress to get an index into the MDL, required as an initial parameter to IoMapTransfer, and IoMapTransfer to make the system physical memory that backs the IRP’s buffer device-accessible

    Note that any driver might need to carry out more than one transfer operation in order to satisfy the current IRP, as explained in Section 3.3.3. Drivers of devices that do not have scatter/gather capabilities can call IoMapTransfer once per transfer operation. Drivers of devices that have scatter/gather capabilities can call IoMapTransfer more than once to set up each transfer operation.

  4. IoFlushAdapterBuffers at the end of each DMA transfer operation to/from the target device, in order to determine whether all the requested data has been completely transferred

  5. IoFreeMapRegisters as soon as all DMA operations for the current IRP are done, either all the requested data has been completely transferred or the driver must fail the IRP because of a device or bus I/O error

The AdapterObject pointer returned by HalGetAdapter is a required parameter to IoAllocateAdapterChannel and IoFreeMapRegisters. However, drivers of busmaster devices pass a NULL AdapterObject pointer to IoMapTransfer and IoFlushAdapterBuffers. KeFlushIoBuffers and MmGetMdlVirtualAddress require a pointer to the MDL at Irp->MdlAddress.

Individual NT drivers call this sequence of support routines at different points, depending on how each driver is implemented to service its device. For example, one driver’s StartIo routine might make the call to IoAllocateAdapterChannel, while another driver might make this call from a routine that removes IRPs from a driver-created interlocked queue or device queue.

3.3.5.1.1 Allocating the Busmaster Adapter Object

A driver calls KeFlushIoBuffers and IoAllocateAdapterChannel after its Dispatch entry point for IRP_MJ_READ and/or IRP_MJ_WRITE requests, or for any other request that requires a DMA transfer, has already checked the validity of the IRP’s parameters (if necessary), possibly queued the IRP to another driver routine for further processing, and the transfer request is the current IRP requiring a device I/O operation. Figure 3.10 illustrates such a call to IoAllocateAdapterChannel.

Figure 3.10 Allocating an Adapter Object for Busmaster DMA

The driver routine that calls IoAllocateAdapterChannel must be executing at IRQL DISPATCH_LEVEL when the call occurs. For more information about the IRQLs at which NT drivers’ standard routines execute, see Chapters 5 through 15. For more information about support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.

As Figure 3.10 shows, the driver must supply more than the AdapterObject pointer returned by HalGetAdapter when it calls IoAllocateAdapterChannel. Along with a pointer to the target device object for the current IRP, it must supply the entry point for its AdapterControl routine and a pointer to any driver-determined context data the AdapterControl routine will use.

IoAllocateAdapterChannel queues the driver’s AdapterControl routine, which executes when the adapter object is free and a set of map registers (described in Section 3.3.1) has been allocated for the driver’s DMA operation(s) to or from the target device.

On entry, an AdapterControl routine is given the DeviceObject and Context pointers passed in the call to IoAllocateAdapterChannel, as well as a handle (MapRegisterBase) for the allocated map registers, as shown in Figure 3.10. The AdapterControl routine also is given a pointer to the DeviceObject->CurrentIrp if the driver has a StartIo routine. If a driver manages its own queueing of IRPs instead of having a StartIo routine, the driver can include a pointer to the current IRP as part of the context it passes when it calls IoAllocateAdapterChannel.

For the driver of a busmaster DMA device without scatter/gather capabilities, the AdapterControl routine usually does the following:

  1. Saves or initializes whatever context the driver maintains about DMA operations, such as saving the MapRegisterBase handle the driver must pass to IoMapTransfer and IoFlushAdapterBuffers, the Length in bytes of the requested transfer from its I/O stack location in the IRP, and so forth

  2. Calls MmGetMdlVirtualAddress followed by IoMapTransfer (described in the next subsection) to get the logical address its device can use to start the transfer operation

  3. Sets up the busmaster adapter to start the transfer operation

  4. Returns the value DeallocateObjectKeepRegisters

For the driver of a busmaster device with scatter/gather capabilities, the AdapterControl routine usually does the following:

  1. Saves or initializes whatever state the driver maintains about DMA operations, such as saving the MapRegisterBase handle the driver must pass to IoMapTransfer and IoFlushAdapterBuffers, the Length in bytes of the requested transfer from its I/O stack location in the IRP, and so forth

  2. Calls MmGetMdlVirtualAddress followed by IoMapTransfer (described in the next subsection) to get the logical address its device can use to start the transfer operation

    The AdapterControl routine calls IoMapTransfer repeatedly until it has used all the available map registers to build a scatter/gather list for the busmaster adapter.

  3. Sets up the busmaster adapter to start the transfer operation

  4. Returns the value DeallocateObjectKeepRegisters

As Figure 3.10 shows, an AdapterControl routine must return a system-defined value of type IO_ALLOCATION_ACTION. For NT drivers that use busmaster DMA, the AdapterControl routine must return the value DeallocateObjectKeepRegisters, which allows the driver to retain the allocated map registers for the target device object until it has transferred all the requested data for the current IRP.

Note that an AdapterControl routine cannot wait for the busmaster adapter to complete a DMA operation. Whether the busmaster adapter supports scatter/gather or not, the AdapterControl routine must at least do the following:

  1. Save necessary context information, particularly the MapRegisterBase handle in the driver’s device extension, controller extension, or other driver-accessible resident storage area (nonpaged pool, allocated by the driver).

  2. Return DeallocateObjectKeepRegisters.

Another driver routine (probably the DpcForIsr) must call IoFlushAdapterBuffers when each DMA transfer operation is done. This routine also must set up any additional DMA operations necessary to satisfy the current IRP.

When the driver has satisfied the current IRP’s transfer request or must fail the IRP due to a device or bus I/O error, it must call IoFreeMapRegisters. This call should occur immediately following the last call to IoFlushAdapterBuffers for the current IRP, so that the driver can service other DMA requests, possibly for other devices on the bus.

3.3.5.1.2 Setting Up a Transfer Operation

When IoAllocateAdapterChannel transfers control to a driver’s AdapterControl routine, it has allocated a set of map registers. However, system physical memory for the current IRP’s transfer request must be mapped to the busmaster adapter’s logical address range by calling the following routines:

  1. MmGetMdlVirtualAddress with the MDL at Irp->MdlAddress to get an index for the system physical address where the transfer should start

    The return value is a required parameter (CurrentVa) to IoMapTransfer.

  2. IoMapTransfer to map the system physical address ranges for the IRP’s buffer to the busmaster adapter’s logical address range before the driver sets up the adapter for the transfer operation

Figure 3.11 illustrates such a call to IoMapTransfer.

Figure 3.11 Setting Up a Logical Range for DMA

As Figure 3.11 shows, the driver supplies the following parameters to IoMapTransfer:

  • An AdapterObject pointer that is NULL

  • A pointer to the MDL at Irp->MdlAddress for the current IRP

  • The MapRegisterBase handle passed to the driver’s AdapterControl routine by IoAllocateAdapterChannel (see the preceding subsection)

  • The value returned by MmGetMdlVirtualAddress if this is the first call to IoMapTransfer for the current IRP

    Otherwise, the driver must supply an updated CurrentVa value, indicating the next physical-to-logical mapping to be done. (How to calculate an updated CurrentVa is described later.)

  • Access to a variable (Length, shown in Figure 3.11 as IrpLengthNextTransfer)

    If the driver has enough map registers to transfer all the requested data in a single DMA operation and has no device-specific constraints on its DMA operations, the Length can be set to the value of Length in the driver’s I/O stack location of the IRP. At most, the input length in bytes can be (PAGE_SIZE * the NumberOfMapRegisters returned by HalGetAdapter). Otherwise, the driver must split up the request, as explained in Section 3.3.3, and must update the value of Length in subsequent calls to IoMapTransfer for the current IRP. (How to calculate an updated Length value is also described later.)

  • A Boolean value (WriteToDevice), indicating the direction of the transfer operation (TRUE for a requested transfer from memory to the device)

IoMapTransfer returns a logical address at which the driver can program the busmaster adapter to begin the transfer operation.

If the driver must call IoMapTransfer more than once to satisfy the current IRP, it supplies the same NULL AdapterObject pointer, Mdl pointer, MapRegisterBase handle, and transfer direction in every call to IoMapTransfer. However, the driver must supply updated CurrentVa and Length values in its second and subsequent calls to IoMapTransfer. Use the following formulas to calculate these values:

  • CurrentVa = CurrentVa + (Length requested in preceding call to IoMapTransfer)

  • Length = Minimum (remaining Length to be transferred, (PAGE_SIZE * NumberOfMapRegisters returned by HalGetAdapter))

The context information each driver maintains about its DMA transfers, such as the IrpLengthNextTransfer, IrpLengthDMAedSoFar, and MdlCurrentVa shown in Figure 3.11, depends on the driver writer.

For drivers of devices with scatter/gather capabilities, the Length parameter to IoMapTransfer is both an input and output parameter. On return from IoMapTransfer, it indicates how many bytes of data the system has mapped. That is, the return value of Length, in combination with the returned logical address, indicates the range of logical addresses the busmaster adapter can use for this piece of the transfer in this DMA operation.

Since Length is overwritten by IoMapTransfer, follow this implementation guideline:

Never pass a pointer to the Length in the driver’s I/O stack location of an IRP as the Length parameter to IoMapTransfer if your device supports scatter/gather.

Doing this could destroy the value in the current IRP, making it impossible to determine whether the driver has transferred all the requested data.

At the end of each DMA operation, the driver must call IoFlushAdapterBuffers with a NULL AdapterObject pointer and the MapRegisterBase handle to be sure that all the data has been transferred (see Chapter 16), and to release the physical-to-logical mapping(s) for the current DMA operation. If the driver must set up additional DMA operations to satisfy the current IRP, it must call IoFlushAdapterBuffers after each transfer operation is complete.

When all the requested transfer is complete or the driver must return an error status for the IRP, the driver should call IoFreeMapRegisters immediately after its last call to IoFlushAdapterBuffers in order to get the best possible throughput for the busmaster adapter. The driver must pass the AdapterObject pointer that it passed in the preceding call to IoAllocateAdapterChannel to IoFreeMapRegisters.


3.3.5.2 Common-Buffer Busmaster DMA

To set up a common buffer for busmaster DMA, a busmaster DMA device driver’s DriverEntry routine must call HalAllocateCommonBuffer with the adapter object pointer returned by HalGetAdapter. An NT driver should allocate a common buffer only if it will use the buffer repeatedly for its DMA operations while the driver remains loaded. Figure 3.12 illustrates such a call to HalAllocateCommonBuffer.

Figure 3.12 Allocating a Common Buffer for Busmaster DMA

The requested size for the buffer, shown in Figure 3.12 as LengthForBuffer, determines how many map registers must be used to provide a virtual-to-logical mapping for the common buffer. At most, the number of map registers can be the value of (BYTES_TO_PAGES (LengthForBuffer)). This value cannot be greater than the NumberOfMapRegisters returned by HalGetAdapter. For more information about system-supplied macros, such as BYTES_TO_PAGES, that NT drivers can use, see the Kernel-Mode Driver Reference.

In addition, the caller must supply the following:

  • A Boolean that indicates whether caching should be enabled

  • A pointer to a driver-defined variable that will contain the device-accessible base Logical Address for the buffer (BuffLogicalAddress in Figure 3.12) on return from HalAllocateCommonBuffer

If the call succeeds, HalAllocateCommonBuffer returns a driver-accessible base virtual address for the buffer (BuffVirtualAddress in Figure 3.12), which the driver must save in its device extension, controller extension, or other driver-accessible resident storage area (nonpaged pool allocated by the driver).

HalAllocateCommonBuffer returns NULL if it cannot allocate memory for the buffer. If the returned base virtual address is NULL, such a driver either must use the system’s packet-based DMA support exclusively or the DriverEntry routine must fail initialization and return STATUS_INSUFFICIENT_RESOURCES.

Otherwise, the driver can use the allocated common buffer as a driver- and adapter-accessible storage area for DMA transfers.

When (or if) such a driver unloads, it must call HalFreeCommonBuffer to release each common buffer it has allocated.

For more information about requirements for DriverEntry and Unload routines, see Chapters 5 and 15, respectively.


3.4 Controller Objects

As its name suggests, an NT controller object usually represents a physical device controller with attached devices. An NT device driver can use a controller object to synchronize I/O operations through a physical controller to or from attached physical devices of the same type like the “AT” disk driver already described in Chapter 2. Drivers of devices with I/O channels and a set of logical device objects also might use a controller object to synchronize their I/O operations between or among the channels of such a device.

Note that such attached devices or channels must be represented by named device objects as the targets for I/O requests, while the device controller is represented by a controller object that has no name.

Because a controller object has neither a name nor an associated file object, it is invisible to user-mode protected subsystems, which cannot make device I/O requests without getting a handle for the file object that represents the target device object. A controller object is also invisible to higher-level drivers, which cannot attach their own device objects to a controller object. In other words, neither the I/O Manager nor a higher-level driver can set up an IRP requesting I/O on a device represented by a controller object.

For more information about creating device objects, see Section 3.2.

Synchronization and Overlapped I/O
Monolithic NT drivers of physical devices with features like the “AT” disk controller (see Chapter 2) are not required to use a controller object to synchronize their device I/O operations. For example, a driver writer could try something like the following synchronization technique instead of using a controller object:
Set up named device objects to represent the devices that are targets for I/O requests. Maintain state (perhaps a set of DeviceBusy flags) about which device object is the target of the current I/O operation in each device extension or in a single device extension. Carry out I/O for the currently busy device object and requeue incoming IRPs for other device objects until the current IRP is completed.

The preceding synchronization technique serializes IRP processing for all of the driver’s target device objects. Note that it also forces such a driver to complete the current IRP before its StartIo routine can begin processing the next IRP, thereby decreasing driver performance.

If a device is such that certain operations can be overlapped, using a controller object can increase a driver’s I/O throughput, because this synchronization technique allows the driver to determine whether it can overlap operations just before it sets up the physical device. For example, a disk controller might allow the driver to overlap seeks on one disk with read/write operations on another disk.

Moreover, using a controller object is a relatively easy way to synchronize I/O operations for more than one target device object through a single physical device, such as an “AT” disk controller. Using a controller object allows a monolithic driver to synchronize I/O across a set of named device objects without having to maintain state about every device and the device controller in one or more device extensions and without having to requeue IRPs.

However, some devices that are designed to overlap I/O operations, such as full-duplex serial controllers or busmaster adapters, generally do have NT drivers that set up internal queues for IRPs.

For more information about setting up and managing internal queues, see Section 3.8, and see also Chapter 7.


3.4.1 Creating a Controller Object with a Controller Extension

If an NT driver uses a controller object, it must call IoCreateController when the driver initializes. Figure 3.13 illustrates such a call from the DriverEntry routine.

Figure 3.13 Controller Object

Like a driver-created device object, every controller object has an associated controller extension. As Figure 3.13 show, the caller of IoCreateController determines the Size of the controller extension. Its structure and contents are driver-defined.

In addition to whatever device-specific state information the driver maintains about the physical controller (or device with channels), Figure 3.13 shows a representative set of driver-defined data for a controller extension.

The PtrToControllerObject returned by IoCreateController must be passed in the driver’s calls to IoAllocateController and IoFreeController, described in Section 3.4.2, next. The DriverEntry routine must store the returned controller object pointer in the device extension(s) of its driver-created device object(s) or in another driver-accessible resident storage area (nonpaged pool, allocated by the driver). If such a driver is unloaded, it also must pass the controller object pointer to IoDeleteController.

Most drivers that set up controller objects find it convenient to store a pointer to the current target device object or device extension in the controller extension. Usually, such a driver stores the controller object pointer in every one of its device extensions so that it can use the ControllerObject->ControllerExtension pointer to access driver-maintained, controller-specific state about I/O operations for every target device object.

If the physical controller represented by a controller object generates interrupts, a driver also can use the controller extension as storage for the PtrToInterruptObject(s) returned by IoConnectInterrupt. For more information about interrupt objects, see Section 3.5, later in this chapter.

IoCreateController allocates resident storage for the controller object and extension, which it initializes with zeros. If it cannot allocate the memory, IoCreateController returns a NULL pointer. If this occurs, the DriverEntry routine must fail the inititialization and should return STATUS_INSUFFICIENT_RESOURCES.


3.4.2 Allocating the Controller Object for I/O Operations

After a driver that uses a controller object has initialized, it is ready to process IRPs sent to its target device objects. Whenever the current IRP requires the driver to program the physical device represented by the controller object for an I/O operation, the driver calls IoAllocateController. Figure 3.14 illustrates such a call.

Figure 3.14 Allocating a Controller Object for I/O

As Figure 3.14 shows, a driver must supply more than the ControllerObject pointer that was returned by IoCreateController when it calls IoAllocateController. Along with this pointer, it must pass pointers to the device object representing the target of the current I/O request, to a driver-supplied ControllerControl routine, and to whatever Context its ControllerControl routine will need to set up the device for the requested I/O operation.

IoAllocateController queues the driver-supplied ControllerControl routine if the device represented by the controller object is already busy doing I/O for a target device object. Otherwise, the ControllerControl routine is called immediately with the input parameters shown in Figure 3.14. The input Context pointer to IoAllocateController is passed to the driver’s ControllerControl routine when it is run.

Consider the following design guidelines to determine the location of such a context area:
  • The driver-supplied context area should not be in the controller extension unless the driver processes each IRP to completion before starting another operation on the physical controller. Otherwise, a context area in the controller extension could be overwritten by other driver routines or on receipt of a new IRP.

  • Even if the driver overlaps a device I/O operation for another device object, a context area in the device extension of the target device object cannot be overwritten.

  • If another I/O request is made for a particular device object and the driver has a StartIo routine, a context area in its device extension also cannot be overwritten because the incoming IRP will be queued when the driver calls IoStartPacket and the same IRP will remain in the device queue until the driver calls IoStartNextPacket just before it completes the current IRP for that device object.

The I/O Manager passes a pointer to the DeviceObject->CurrentIrp to a ControllerControl routine if the driver has a StartIo routine. If, like the system-supplied floppy driver described in Chapter 2, a driver manages its own queuing of IRPs instead of having a StartIo routine, the I/O Manager cannot give the ControllerControl routine a pointer to the current IRP. When it calls IoAllocateController, such a driver should include the current IRP as part of the Context-accessible data it passes.

The driver routine that calls IoAllocateController must be executing at IRQL DISPATCH_LEVEL when the call occurs. A driver that makes this call from its StartIo routine is already running at DISPATCH_LEVEL. For more information about the IRQLs at which NT drivers’ standard routines execute, see Chapters 5 through 15 or the section on managing IRQLs in Chapter 16. For more information about support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.

The ControllerControl routine sets up the physical controller for the IRP’s requested operation.

As shown in Figure 3.14, the ControllerControl routine returns a value of type IO_ALLOCATION_ACTION, which can be either of the following system-defined values:

  • If the ControllerControl routine can start another operation on the physical controller, it should return DeallocateObject so the driver can overlap the next requested I/O operation.

    For example, if the ControllerControl routine can program a disk controller for a seek operation on one disk, complete that IRP, and return DeallocateObject, the ControllerControl routine can be called again to program the disk controller for a transfer operation on the other disk if any transfer requests currently are queued to the other disk.

  • If the current IRP requires further processing by other driver routines, the ControllerControl routine must return KeepObject.

    For example, if the driver programs a disk controller for a transfer operation but cannot complete the IRP until the transfer is complete, the ControllerControl routine must return KeepObject.

When a ControllerControl routine returns KeepObject, usually the driver’s ISR runs when the device interrupts, and its DpcForIsr routine completes the I/O operation and the current IRP for the target device object.

Whenever the ControllerControl routine returns KeepObject, the routine that completes the IRP must call IoFreeController. Such a driver routine should call IoFreeController as soon as possible so that its next device I/O operation can be set up promptly.


3.5 Interrupt Objects

Each NT driver of a physical device that generates interrupts must register its ISR when the driver initializes. The NT Kernel defines the interrupt object type to track information about each interrupt vector, its system-assigned DIRQL, the ISR to be called when the interrupt occurs, and so forth.

Every NT driver should be portable across all NT platforms, including SMP machines with several I/O buses of various types (ISA, EISA, PCI and so forth). Consequently, the following are configurable when NT device drivers and the system itself are loaded:

  • The set of processors on which interrupts can occur

    For any Windows NT platform, this determines how many interrupt objects must be set up to register an NT device driver’s ISR. In SMP machines, each device driver might require an interrupt object per processor. However, the system can perform load balancing by restricting any particular device interrupt to a subset of available processors.

  • The system-assigned interrupt vectors for every device on each I/O bus in the machine

    For Windows NT multibus machines, the bus-relative interrupt vectors of the devices on each I/O bus must be mapped to a set of system-assigned interrupt vectors.

  • The DIRQL assigned to each system interrupt vector

    For an NT device driver, the DIRQL value assigned to its mapped system vector determines the hardware priority at which that driver’s ISR executes.

For any Windows NT platform, the NT HAL can remap all bus-relative device interrupt vectors to a set of machine-specific interrupt vectors, each with a system-assigned DIRQL value. A platform-specific HAL provides hardware-level support to both the Kernel and the I/O Manager for the I/O buses, peripherals, and processors in each NT machine, as already shown in Figure 3.1. The HAL also provides platform-specific support for NT drivers that use interrupt objects, as explained in the next section.


3.5.1 Getting a System-assigned Interrupt Vector, DIRQL, and Processor Mask

When an NT device driver is loaded for initialization, the registry contains whatever hardware information the system can detect about the processors, I/O buses, and peripheral devices in the machine. An NT device driver can use the registry to determine the system-assigned number and, for some devices, the system-defined interface type of the bus to which its device is attached, along with the bus-relative vector and/or IRQL for the device itself. Otherwise, an NT device driver can call one of the HalGetBus Xxx routines to get bus-relative configuration information about its interrupt and use other support routines to claim hardware resources in the registry. For more information about how device drivers can get this hardware configuration information, see Chapter 16.

The HAL is responsible for remapping bus-relative device interrupt vectors to system interrupt vectors with system-assigned DIRQL values, so NT device drivers call a HAL-supplied support routine to get required parameters for setting up interrupt objects and registering their ISRs. Figure 3.15 illustrates a call to HalGetInterruptVector.

Figure 3.15 Getting a System Vector and IRQL

As shown in Figure 3.15, HalGetInterruptVector maps driver-supplied values for a device’s bus-relative interrupt vector and IRQL to DeviceSystemVector and SystemDirql values. See the section on the registry in Chapter 16 for more information about how device drivers can obtain the BusInterfaceType, BusNumber, BusRelativeVector, and BusRelativeIrql parameters to HalGetInterruptVector.

HalGetInterruptVector also returns a ProcessorMask (Affinity) that indicates the platform-specific set of processors on which any particular device can interrupt. NT device drivers must pass this value to the I/O Manager so it can set up a Kernel-defined interrupt object for each enabled processor when the driver registers its ISR, as explained in the next section.


3.5.2 Registering an ISR

An NT device driver must call IoConnectInterrupt when it initializes in order to register its ISR. Figure 3.16 illustrates this call.

Figure 3.16 Setting Up Interrupt Objects

As Figure 3.16 shows, when a device driver calls IoConnectInterrupt, the I/O Manager calls the Kernel to set up the driver’s interrupt object(s):

  1. Unless the ISR handles more than one interrupt vector for different devices, the DriverEntry routine registers the ISR with the following parameters:

    • A pointer to the driver’s storage area for the interrupt object pointer returned by IoConnectInterrupt

    • A NULL spin lock pointer, unless the driver itself has allocated storage for and already initialized an InterruptSpinLock with KeInitializeSpinLock

    • A pointer to the ServiceContext area the ISR will use when it is called and the entry point for the ISR

    • The (mapped) DeviceSystemVector obtained from HalGetInterruptVector

    • The SystemDirql assigned to that vector for this machine, also obtained from HalGetInterruptVector

    • The same IRQL value for the SynchronizeIrql parameter

    • The ProcessorMask, indicating the set of processors on which the device can interrupt in this machine, also obtained from HalGetInterruptVector

    • Whether the interrupt mode is LevelSensitive or Latched

    • Whether the device can share the vector

    • Whether to save the floating-point registers when the device interrupts

  2. The I/O Manager allocates sufficient resident memory for as many interrupt objects as the input ProcessorMask indicates, which could be as many interrupt objects as processors in SMP machines or could be a lesser number, depending on the ProcessorMask. The I/O Manager also provides storage for an interrupt spin lock and initializes it if the driver passes a NULL pointer to IoConnectInterrupt (see Step 1).

  3. The I/O Manager calls KeInitializeInterrupt for each interrupt object.

  4. The I/O Manager also calls KeConnectInterrupt for each initialized interrupt object to connect it to a particular processor on which the device can interrupt in the machine. This call actually sets the given DeviceSystemVector in the Kernel’s IDT for a particular processor, as hinted in Figure 3.16.

  5. When all necessary interrupt object(s) have been initialized and connected on the processor(s), IoConnectInterrupt returns a pointer to the set of interrupt objects. NT driver routines must pass the returned PointerToInterruptObject(s) in their calls to KeSynchronizeExecution.

  6. As Figure 3.16 shows, when a device interrupt occurs on a given processor, the driver’s ISR is run on that processor at DIRQL and given a pointer to the ServiceContext that was set up when the driver called IoConnectInterrupt.

If an NT driver must handle interrupts from more than one device, each with a different interrupt vector, it can have a single multivector ISR for all its devices, or it can have more than one ISR, up to one for each vector. Such a driver’s DriverEntry routine must register the ISR as follows:

  1. The DriverEntry routine must call KeInitializeSpinLock with a pointer to driver-provided storage for an InterruptSpinLock that must be in resident memory (in a device extension, controller extension, or nonpaged pool allocated by the driver).

  2. For each vector an ISR handles, the DriverEntry routine must call HalGetInterruptVector and IoConnectInterrupt as described in Step 1, except for the following parameters:

    • Such a driver must pass a pointer to its storage for an initialized InterruptSpinLock, rather than a NULL spin lock pointer.

    • Such a driver must specify a SynchronizeIrql value that is the highest SystemDirql assigned to any device for which its ISR(s) handle interrupts.

The ISR(s) of such a driver must run at the highest DIRQL assigned to the DeviceSystemVectors the driver handles, as must any driver-supplied SynchCritSection routine that accesses device registers (or data) shared with an ISR.

Because an ISR runs at a relatively high IRQL, it must return control as quickly as possible. Consequently, an NT driver’s ISR should do as little I/O processing as it can, usually the following:

  1. Determine whether its device actually generated the interrupt and return FALSE as soon as possible if it did not.

  2. Otherwise, stop the device from generating interrupts, save whatever context is necessary about the operation that caused the interrupt, and queue a DPC to complete the interrupt-driven I/O operation at a lower IRQL.

    For more information about DPC objects, see Section 3.6, next.

For more information about using an interrupt spin lock, KeInitializeSpinLock, and KeSynchronizeExecution, see the section on spin locks in Chapter 16. For more information about ISRs and SynchCritSection routines, see also Chapters 4, 8, and 10.


3.6 DPC Objects

Like the interrupt object type described in the preceding section, the DPC object type is defined by the NT Kernel. A DPC object represents a deferred procedure call: a caller-supplied routine that is run later at a lower hardware priority (IRQL).

Any NT device driver that registers an ISR must use one or more DPC objects to register its DpcForIsr and/or CustomDpc routine(s). A driver-supplied DpcForIsr or CustomDpc routine is responsible for completing interrupt-driven I/O operations so the ISR can return control as quickly as possible.

Some system device drivers have only a single DpcForIsr routine, and such a driver uses a single DPC object. At the driver writer’s discretion, an NT device driver can have one or more CustomDpc routines, each using a DPC object, in addition to or as a substitute for a DpcForIsr routine.

The NT I/O Manager provides support for registering a DpcForIsr and for queueing this routine from the ISR, as explained in the next section. The Kernel provides support for registering a CustomDpc and for queueing this routine from the ISR, as explained in Section 3.6.2.


3.6.1 Registering and Queueing a DpcForIsr Routine

An NT device driver can register its DpcForIsr by calling IoInitializeDpcRequest when the driver initializes. After the driver is loaded and handling interrupt-driven I/O requests, the ISR calls IoRequestDpc just before it returns control to have the DpcForIsr routine queued for execution. Figure 3.17 illustrates calls to these routines.

Figure 3.17 Using a DPC Object for a DpcForIsr Routine

As Figure 3.17 shows, calling IoInitializeDpcRequest associates a Kernel DPC object with a driver-supplied DpcForIsr routine and a driver-created device object. The I/O Manager allocates memory for the DPC object and calls KeInitializeDpc on the driver’s behalf.

When the ISR is called to handle a device interrupt at DIRQL, it should return control to the system as soon as possible for better overall system and driver performance. Usually, an ISR merely stops the device from generating more interrupts, gathers whatever context information the DpcForIsr routine needs to complete the operation that caused the interrupt, calls IoRequestDpc, and returns.

As Figure 3.17 shows, the ISR passes a pointer to the device object, representing the target device for which the operation was carried out, a pointer to the DeviceObject->CurrentIrp, and a pointer to a driver-determined Context for the operation to IoRequestDpc. The I/O Manager calls KeInsertQueueDpc on the driver’s behalf, and the corresponding DPC object is queued until IRQL falls below DISPATCH_LEVEL on a processor. Then, the Kernel dequeues the DPC object and the driver’s DpcForIsr is run on the processor at IRQL DISPATCH_LEVEL.

On entry, the DpcForIsr is given a pointer to the DPC object and the DeviceObject, current Irp, and Context pointers passed in the ISR’s call to IoRequestDpc. The Context-accessible area must be in resident memory. Unless the driver overlaps I/O for the target device, such a context area is usually in the DeviceObject->DeviceExtension, but it can be in a controller extension if the driver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver. The DpcForIsr is responsible for doing whatever is necessary to complete the I/O requested in the current IRP.

The ISR and DpcForIsr can be run concurrently in symmetric multiprocessor machines, so NT device driver writers should follow these guidelines:
  • The ISR must call IoRequestDpc just before it returns control. Otherwise, the DpcForIsr might be run on another processor before the ISR has finished setting up the Context-accessible area for the DpcForIsr.

  • The ISR could be called again if the device interrupts while or before the DpcForIsr is run. When a driver uses the device extension (see Section 3.2) to maintain context about its device I/O operations, the DpcForIsr should never call IoStartNextPacket for the input DeviceObject (nor dequeue an IRP for the input DeviceObject, if the driver manages its own IRP queueing) until just before it calls IoCompleteRequest with the current Irp. Otherwise, the driver’s StartIo or queue-management routine(s) might start a device I/O operation that overwrote the shared context area before the DpcForIsr could complete the current operation.

  • The DpcForIsr and any other driver routine that shares a context area with the ISR must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine in order to access any context area shared with the ISR in a multiprocessor-safe manner.

Even in a uniprocessor machine, the ISR could be called again if the device interrupts while or before the DpcForIsr is run. If this occurs, the DpcForIsr routine is run only once. In other words, there is no one-to-one correspondence between an ISR’s calls to IoRequestDpc and instantiations of the DpcForIsr routine if an NT driver overlaps I/O operations for its target device objects.

For more information about the functionality required of a DpcForIsr routine, see Chapter 9.


3.6.2 Registering and Queueing a CustomDpc Routine

An NT device driver can register a CustomDpc routine by calling KeInitializeDpc when the driver initializes. Just before it returns control, the ISR can call KeInsertQueueDpc to have the CustomDpc routine queued for execution. Figure 3.18 illustrates calls to these routines.

Figure 3.18 Using a DPC Object for a CustomDpc Routine

As Figure 3.18 shows, a driver that has a CustomDpc routine must provide the storage for a DPC object. Because the driver must pass a pointer to the DPC object from its ISR, the storage must be in resident system-space memory. Most NT device drivers with CustomDpc routines provide storage for their DPC objects in the device extension of a driver-created device object (see Section 3.2), but the storage can be in a controller extension if the driver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.

When the DriverEntry routine calls KeInitializeDpc, it must pass the entry point for its CustomDpc routine and pointers to the driver-allocated storage for the DPC object and to the driver-defined DeferredContext area, which is passed to the CustomDpc routine when it is called. Because it must be accessible at raised IRQL, the DeferredContext-accessible area also must be in resident memory.

Note that a CustomDpc routine is not associated with a given device object as the DpcForIsr routine is when the driver calls IoInitializeDpcRequest, as described in Section 3.6.1. Nevertheless, the DeferredContext area for a CustomDpc routine almost always includes pointers to the target device object and current IRP, because a CustomDpc routine has the same functional requirements as a DpcForIsr routine: to complete an interrupt-driven I/O operation at a lower IRQL than the ISR.

As Figure 3.18 shows, the ISR passes pointers to the DPC object and to two additional parameters, which are caller-determined, to KeInsertQueueDpc. If all processors in the machine currently have code running at IRQL greater than or equal to DISPATCH_LEVEL, the DPC object is queued until the IRQL falls below DISPATCH_LEVEL on a processor. Then, the Kernel dequeues the DPC object and the driver’s CustomDpc routine is run on the processor at IRQL DISPATCH_LEVEL.

Only a single instantiation of a given DPC object can be queued at any given moment. That is, if an ISR calls KeInsertQueueDpc more than once with the same Dpc pointer before the driver’s CustomDpc routine is run, the CustomDpc routine runs only once after IRQL falls below DISPATCH_LEVEL on a processor.

A CustomDpc routine is responsible for doing whatever is necessary to complete the I/O operation(s) that caused the interrupt(s). For more information about the functionality required of a CustomDpc routine, see Chapter 9.

Note that the ISR and CustomDpc can be run concurrently in a symmetric multiprocessor machine, so NT driver writers who implement CustomDpc routines should follow the guidelines set out in Section 3.6.1.


3.7 Timer Objects with Associated DPCs

Any NT driver can have one or more IoTimer or CustomTimerDpc routines to be called periodically for whatever purpose the driver writer decides. Each of these types of standard driver routines is actually a DPC (see Section 3.6), queued for execution after some number of clock interrupts have been handled by the system.

The NT Kernel defines the timer object type, and the I/O Manager sets up a timer object that can be associated with each driver-created device object to provide support for drivers’ IoTimer routines. At the driver writer’s discretion, an NT driver can have a separate IoTimer routine for each device object that the driver creates. For more information about creating device objects, see Section 3.2.

An IoTimer routine is called once per second after the driver enables the timer until the driver disables the timer. The I/O Manager provides support routines for registering an IoTimer routine with a given device object and for enabling and disabling such a timer.

As an alternative, an NT driver can have one or more CustomTimerDpc routines that can be called at finer-grained, driver-determined intervals. The Kernel provides support routines for registering a CustomTimerDpc routine and for queueing the CustomTimerDpc to be called after a given timer interval has expired.

The following subsection explains how to set up an IoTimer routine and enable the timer. Section 3.7.2 explains how to register a CustomTimerDpc routine and how to queue this routine to be called after setting a timer interval.


3.7.1 Registering and Enabling an IoTimer Routine

Any NT driver can register an IoTimer routine after it creates one or more device objects by calling IoInitializeTimer when the driver initializes. After the driver has initialized, it can enable the timer by calling IoStartTimer. Figure 3.19 illustrates these calls.

Figure 3.19 Using a Timer Object for an IoTimer Routine

As Figure 3.19 shows, the driver calls IoInitializeTimer with the entry point of its IoTimer routine and pointers to a driver-created device object and TimerContext area in which the driver maintains whatever context its IoTimer routine uses. The I/O Manager associates the device object with a Kernel timer object, which the I/O Manager sets up to time out every second.

After the driver calls IoStartTimer, its IoTimer routine is called once per second until the driver calls IoStopTimer. An NT driver can re-enable calls to its IoTimer routine with IoStartTimer.

On entry, the IoTimer routine is given DeviceObject and Context pointers to the associated device object and TimerContext area that was set up when the driver called IoInitializeTimer.

Because an IoTimer routine is run at IRQL DISPATCH_LEVEL, its TimerContext area must be in resident, system-space memory. Most NT drivers that have IoTimer routines use the DeviceObject->DeviceExtension of the associated device object as such a Context-accessible area, but it can be in a controller extension if the driver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.

Driver writers who implement an IoTimer routine should follow these guidelines concerning the TimerContext area:
  • If the IoTimer routine shares its TimerContext area with the driver’s ISR, it must call KeSynchronizeExecution with a SynchCritSection routine in order to access the TimerContext area in a multiprocessor-safe manner.

    For more information about SynchCritSection routines, see Chapter 10. See also the section about using spin locks in Chapter 16 for more information about KeSynchronizeExecution.

  • If the IoTimer routine does not share its TimerContext area with an ISR but does share it with another driver routine, the driver must protect this context area with an initialized executive spin lock in order to access the TimerContext in a multiprocessor-safe manner.

    For more information about initializing and using executive spin locks, see Chapter 16.

For more information about the functionality required of an IoTimer routine, see Chapter 14.


3.7.2 Registering and Queueing a CustomTimerDpc Routine

Any NT driver can register a CustomTimerDpc routine when the driver is loaded by calling the following routines:

  1. KeInitializeDpc to set up its CustomTimerDpc routine

  2. KeInitializeTimer or KeInitializeTimerEx to set up a timer object

After the driver has initialized, it can call KeSetTimer or KeSetTimerEx to set up the timer object with an interval and with the DPC object in order to have its CustomTimerDpc called when the given interval expires. Figure 3.20 illustrates these calls.

Figure 3.20 Using Timer and DPC Objects for a CustomTimerDpc Routine

As Figure 3.20 shows, the driver must supply storage both for a DPC object and a timer object if it has a CustomTimerDpc routine. Most NT drivers that have CustomTimerDpc routines provide the storage for these objects in the device extension of a driver-created device object (see Section 3.2) or in other resident memory allocated by the driver.

The DriverEntry routine must first initialize the DPC object by calling KeInitializeDpc and then initialize the timer object by calling KeInitializeTimer or KeInitializeTimerEx. For more information about calls to KeInitializeDpc, see Section 3.6.2, earlier in this chapter.

As Figure 3.20 shows, the driver must pass a pointer to its storage for the timer object when it calls KeInitializeTimer or KeInitializeTimerEx.

After the driver has initialized, it can queue the CustomTimerDpc to be called after a driver-determined interval by calling KeSetTimer with Dpc and Timer object pointers, and with a given DueTime expressed in units of 100 nanoseconds, as shown in Figure 3.20. A positive value for DueTime specifies an absolute time at which the CustomTimerDpc routine should be called. A negative value for DueTime specifies a relative expiration time. Absolute expiration times track any changes in system time; relative expiration times are not affected by system time changes.

To invoke a CustomTimerDpc routine repeatedly, set the timer using KeSetTimerEx and specify a recurring interval in the Period parameter. KeSetTimerEx is just like KeSetTimer except for this additional parameter.

As shown in Figure 3.20, the call to KeSetTimer or KeSetTimerEx queues the timer object for the given interval:

  1. When the given DueTime expires, the timer object is dequeued and set to the Signaled state.

  2. If every processor in the machine is currently running code at an IRQL greater than or equal to DISPATCH_LEVEL, the DPC object associated with the timer object is put in a DPC queue. Otherwise, the CustomTimerDpc routine is called.

  3. If the DPC object was queued when the DueTime interval expired, the CustomTimerDpc routine is called as soon the IRQL on any processor in the machine falls below DISPATCH_LEVEL.

    The CustomTimerDpc routine, like all DPCs, is run at IRQL DISPATCH_LEVEL.

A Kernel timer object interval is approximately ten milliseconds, so a driver with a CustomTimerDpc has more control over the intervals at which its CustomTimerDpc routine is run than an IoTimer routine.

Like a DPC object, only one instantiation of a given timer object can be queued at any given moment. Calling KeSetTimer or KeSetTimerEx again with the same Timer object pointer cancels the queued timer object and resets it.

For more information about CustomTimerDpc routines, see also Chapter 14.

A driver writer who implements a CustomTimerDpc must follow the same guidelines concerning the DeferredContext area as for the TimerContext area of an IoTimer routine, as already described in Section 3.7.1.


3.8 Device Queue Objects and Interlocked Queues

Except for NT file system drivers, the I/O Manager associates a device queue object with each device object that an NT driver creates, as already shown in Figure 3.2.

Most NT device drivers call the I/O Manager’s support routines to use the associated device queue that holds IRPs whenever device I/O requests for a target device object come in faster than the driver can process them to completion. With this technique, IRPs are queued to a driver-supplied StartIo routine.

For good performance, most NT intermediate drivers simply pass IRPs on to lower drivers as fast as they come in, so intermediate drivers almost never use the device queues associated with their respective device objects.

However, NT driver writers can design their drivers to manage internal queues of IRPs by explicitly setting up one or more device queue objects or interlocked queues, particularly if the driver controls a device that overlaps I/O operations. For such a device, the driver designer might find it difficult to manage concurrent processing of two or more IRPs for the same target device object using only a single queue.

An NT driver designer also might decide to set up supplemental queues for IRPs in the driver of a device controller for a set of heterogeneous physical devices.

For example, the NT SCSI port driver uses device queue objects for internal queues, as already mentioned in Section 3.1. This driver both has a StartIo routine and sets up device queue objects as supplemental queues: that is, in addition to the device queue associated with the device object it creates to represent an HBA. The NT SCSI port driver uses its supplemental device queues to hold IRPs bound for particular logical units on the HBA-controlled SCSI bus(es).

The system floppy controller driver is an example of a driver that has no StartIo routine and uses an interlocked queue, as already mentioned in Chapter 2. This driver sets up a doubly linked interlocked queue into which and from which the driver and its device-dedicated thread insert and remove IRPs.

The NT Kernel defines the device queue object type. The Executive Support component provides routines for inserting and removing IRPs in interlocked queues. The next subsection explains how to set up and use a device queue object. Section 3.8.2 explains how to set up and use interlocked queues.

For more information about the I/O Manager’s built-in support for IRP queueing in the device queue associated with a device object, see also Chapter 4.


3.8.1 Setting up a Device Queue Object and Queueing IRPs

An NT driver can set up a device queue object by calling KeInitializeDeviceQueue when the driver initializes. After the driver is loaded, it inserts IRPs into this queue by calling KeInsertDeviceQueue or KeInsertByKeyDeviceQueue. Figure 3.21 illustrates these calls.

Figure 3.21 Using a Device Queue Object

As Figure 3.21 shows, the driver must provide the storage for a device queue object, which must be resident. Drivers that set up a device queue object usually provide the necessary storage in the device extension of a driver-created device object (see Section 3.2), but it can be in a controller extension if the driver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.

The DriverEntry routine of such a driver must call KeInitializeDeviceQueue, passing a pointer to the driver-provided storage for the device queue object.

After the driver is loaded, it can insert an IRP into its device queue by calling KeInsertDeviceQueue, which places the IRP at the tail of the queue, or KeInsertByKeyDeviceQueue, which places the IRP into the queue according to a driver-determined SortKey value, as shown in Figure 3.21.

Each of these support routines returns a Boolean value indicating whether the IRP was inserted into the queue. Each of these calls also sets the state of the device queue object to Busy if the queue is currently empty. However, an IRP passed to a KeInsert..DeviceQueue routine is not inserted into the queue on the state transition from Not-Busy to Busy: that is, each of these support routines returns FALSE when it sets the state of an empty device queue object to Busy.

Consequently, NT driver writers who set up supplemental device queues should follow this implementation guideline:

When an IRP is sent to the driver and a call to KeInsert..DeviceQueue returns FALSE, the caller must pass the IRP on for further processing to another driver routine.

However, the call to KeInsert..DeviceQueue with such an IRP changes the state of the device queue object to Busy, so the next IRP to come in is inserted in the queue unless the driver calls KeRemove..DeviceQueue first.

When the device queue object’s state is set to Busy, the driver can dequeue an IRP for further processing or reset the state to Not-Busy by calling one of the following support routines:

  • KeRemoveDeviceQueue to remove the IRP at the head of the queue

  • KeRemoveByKeyDeviceQueue to remove an IRP chosen according to a driver-determined SortKey value

  • KeRemoveEntryDeviceQueue to remove a particular IRP in the queue or to determine whether a particular IRP is in the queue

    KeRemoveEntryDeviceQueue returns a Boolean indicating whether the given IRP was in the device queue.

An attempt to remove an entry from a device queue that is empty but whose state is set to Busy causes the state transition to Not-Busy.

A device queue object is protected by a built-in executive spin lock (not shown in Figure 3.21), so a driver that uses a device queue object can insert IRPs into the queue and remove them in a multiprocessor-safe manner from any driver routine running at less than or equal to IRQL DISPATCH_LEVEL. This IRQL restriction precludes calling any Ke..DeviceQueue routine from an NT device driver’s ISR and SynchCritSection routines, which run at DIRQL.

For more information about managing IRQLs and using executive spin locks, see Chapter 16. For support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.


3.8.2 Setting up an Interlocked Queue and Queueing IRPs

NT drivers with device-dedicated threads or drivers that use executive worker threads, such as most system FSDs, are the most likely types of NT drivers to manage their own internal queueing of IRPs in an interlocked queue. Usually, these drivers set up a doubly linked interlocked queue because the driver can requeue IRPs for retry operations in a doubly linked interlocked queue. An NT driver cannot requeue IRPs for retries if it sets up a singly linked interlocked queue.

Such a driver must set up its interlocked queue when it initializes. Figure 3.22 illustrates a doubly linked interlocked queue, the support routines the DriverEntry routine must call to set up such a queue, and a set of ExInterlocked Xxx routines an NT driver can call to insert IRPs into and remove IRPs from the queue.

Figure 3.22 Using an Interlocked Queue

As Figure 3.22 shows, a driver must provide the storage for the queue itself and for the following in order to set up a doubly linked interlocked queue:

  • An ExecutiveSpinLock that the DriverEntry routine must initialize by calling KeInitializeSpinLock

  • The ListHead for the queue that the DriverEntry routine must initialize by calling InitializeListHead, which is one of the NT kernel-mode runtime library routines, even though this routine does not have the Rtl prefix

Most NT drivers that use interlocked queues provide the necessary storage in the device extension of a driver-created device object, but such a queue and executive spin lock can be in a controller extension if the driver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.

A queue with a ListHead of type LIST_ENTRY, as shown in Figure 3.22, is a doubly linked list. One with a ListHead of type SINGLE_LIST_ENTRY is a singly linked list. The DriverEntry routine initializes the ListHead for a singly linked interlocked queue by setting it to NULL.

After such a driver is loaded, it can insert an IRP into its queue by calling either of the following support routines if the ListHead is of type LIST_ENTRY, as shown in Figure 3.22:

  • ExInterlockedInsertTailList to place the IRP at the end of the queue

  • ExInterlockedInsertHeadList to place the IRP at the front of the queue, which drivers usually call only when they must retry a particular request

The driver must pass pointers to the IRP (ListEntry), as well the ListHead and ExecutiveSpinLock (Lock) pointers that were initialized by the DriverEntry routine, to each of these ExInterlockedInsert..List routines. It must pass pointers only to the ListHead and Lock when the driver dequeues an IRP by calling ExInterlockedRemoveHeadList.

A driver that never retries operations can use ExInterlockedPushEntryList and ExInterlockedPopEntryList to manage its queueing of IRPs internally in a singly linked interlocked queue. Any driver that uses such an interlocked queue also must provide resident storage for the queue, for a ListHead of type SINGLE_LIST_ENTRY, and for an ExecutiveSpinLock, as shown in Figure 3.22, and must set up its queue in a similar manner when the driver initializes.

Because its interlocked queue is protected by the executive spin lock, the driver can insert IRPs into its queue and remove them in a multiprocessor-safe manner from any driver routine running at less than or equal to IRQL DISPATCH_LEVEL.

For more information about managing IRQLs and using spin locks, see Chapter 16. For support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.


3.9 Kernel Dispatcher Objects

As mentioned in Section 3.7, the NT Kernel defines the object type for timer objects, which can be associated with driver-supplied CustomTimerDpc routines. A timer object is one of a set of Kernel-defined object types called dispatcher objects.

The NT Kernel also defines the types for other dispatcher objects, such as events, semaphores, and mutexes. The Kernel-defined thread object, representing a thread of execution within the system, is also a dispatcher object.

Dispatcher objects can be used as synchronization mechanisms within a nonarbitrary thread context while running at IRQL PASSIVE_LEVEL.

Dispatcher Objects State and Thread Priorities

Every Kernel-defined dispatcher object type has a state that is either set to Signaled or set to Not-Signaled.

A group of threads can synchronize their operations by having one or more threads call KeWaitForSingleObject, KeWaitForMutexObject, or KeWaitForMultipleObjects with dispatcher object(s) and waiting until another routine or thread sets the dispatcher object(s) to the Signaled state.

When a thread calls the KeWaitForSingleObject with a dispatcher object or KeWaitForMutexObject with a mutex, the thread is put into a wait state until the dispatcher object is set to the Signaled state. When a thread calls KeWaitForMultipleObjects with one or more dispatcher objects, the thread is put into a wait state until one or all of the dispatcher objects are set to the Signaled state.

Whenever a dispatcher object is set to the Signaled state, the Kernel changes the state of any thread waiting on that object to ready: the thread will be scheduled to run according to its current runtime priority and the current availability of processor(s) for any thread with that priority. Synchronization events and synchronization timers are exceptions to this rule; when a synchronization event or timer is signaled, only one waiting thread is set to the ready state.

Some NT drivers create their own driver- or device-dedicated system threads and set their thread’s base priority to the lowest real-time priority value. Other highest-level drivers, particularly NT file system drivers, use system worker threads with a base priority that is usually set to the highest variable priority value. The Kernel schedules a thread with the lowest real-time priority to run ahead of every thread with a variable priority, which includes almost every user-mode thread in the system.

Most standard NT driver routines are run in an arbitrary thread context, ahead of all threads that are currently in the ready state.

Threads, whatever their respective runtime priorities, are run at IRQL PASSIVE_LEVEL. Many standard NT driver routines are run at raised IRQL: that is, at a hardware priority greater than PASSIVE_LEVEL, such as IRQL DISPATCH_LEVEL or DIRQL.

Waiting on Dispatcher Objects in NT Drivers

For most NT device and intermediate drivers, the Kernel’s dispatcher objects can be used for synchronization only in the following circumstances:

  • The DriverEntry, Reinitialize, Unload, or certain Dispatch routines are run in the context of a system thread, so an NT driver can wait on a dispatcher object within these routines.

  • The Dispatch routines of a highest-level NT driver also are run in a nonarbitrary thread context, usually that of a user-mode thread requesting an I/O operation.

  • Lower-level drivers’ Dispatch routines that initiate inherently synchronous I/O operations, such as create, flush, shutdown, close, and some device I/O control operations, also can wait on a dispatcher object for the completion of the requested operation.

However, lower-level drivers’ Dispatch routines cannot wait on a Kernel-defined dispatcher object for the completion of an inherently asynchronous I/O operation.

NT miniport driver writers should consider the following a design guideline:

For portability, miniport drivers should not use kernel dispatcher objects. Miniport drivers should call only ScsiPort Xxx routines.

NT driver writers should consider the following a design guideline:

An NT driver cannot and must not attempt to wait on a dispatcher object for the completion of a transfer operation to or from a paging device.

In other words, NT driver Dispatch routines for read/write requests generally cannot wait on a dispatcher object. A Dispatch routine for any device I/O control request with an I/O control code whose transfer-type value is other than METHOD_BUFFERED also cannot wait on a dispatcher object. For more information about device-type-specific requests that NT drivers must support and I/O control codes, see the Kernel-Mode Driver Reference.

Every other standard NT driver routine is run in an arbitrary thread context: that of whatever thread happens to be current when the driver routine is called to process an IRP or to handle a device interrupt. Moreover, most standard driver routines are run at raised IRQL, either at DISPATCH_LEVEL, or for device drivers, at DIRQL.

NT driver designers should always keep in mind the following fact:

It is a fatal error to wait on a Kernel dispatcher object for a nonzero interval at raised IRQL.

Driver Dispatch routines that initiate asynchronous I/O operations to a device and all driver routines that run at raised IRQL cannot use dispatcher objects to synchronize their processing of IRPs.

For a summary of the IRQLs at which standard NT driver routines run, see Chapter 16.

Few NT device and intermediate drivers set up driver-created system threads. Few highest-level NT device drivers use system worker threads, as NT FSDs usually do. Consequently, most NT device and intermediate drivers can use a dispatcher object only while the driver is initializing, unloading, or in a Dispatch routine for a synchronous I/O request.

For example, the DriverEntry routines of the system’s NT SCSI class drivers set up an event object so they can do the following:

  1. Call IoBuildSynchronousFsdRequest with the pointer to the driver-supplied storage for an initialized event object.

  2. Set up the IRP that this routine creates and send it with IoCallDriver down to the NT SCSI port driver to request configuration information.

  3. Call KeWaitForSingleObject to wait on the event until the port driver returns the IRP with IoCompleteRequest, which sets the associated event to the Signaled state indicating that the requested configuration information or an error status is available for the class driver.

The following subsections describe the timer, event, semaphore, and mutex dispatcher objects for driver writers who either design drivers with device-dedicated threads and/or worker-thread callback routines, or who design drivers that must wait on one or more dispatcher objects in their DriverEntry, Reinitialize, Unload, or Dispatch routines for synchronous I/O operations.

Any driver that waits on a dispatcher object in its DriverEntry, Reinitialize, or Unload routine runs in a system thread context. In effect, any driver that waits on a dispatcher object in its Dispatch routine for a synchronous I/O operation steals cycles from an arbitrary thread’s quantum.

For simplicity, the following subsections illustrate calls to KeWaitForSingleObject. For more information about KeWaitForMutexObject and KeWaitForMultipleObjects, see the Kernel-Mode Driver Reference.


3.9.1 Timer Objects

Any NT driver can set up a timer object with KeInitializeTimer or KeInitializeTimerEx that it can use within a nonarbitrary thread context to time out operations within the driver’s other routines or perform a periodic operation.

A timer can be a notification timer or a synchronization timer. When a notification timer is signaled, all waiting threads have their wait satisfied and the state of the timer remains signaled until it is explicitly reset. When a synchronization timer expires, its state is set to Signaled until a single waiting thread is released and then the timer is reset to the Not-Signaled state. KeInitializeTimer always creates notification timers. KeInitializeTimerEx accepts a Type parameter which can be NotificationTimer or SynchronizationTimer. Both notification and synchronization timers can optionally have an associated CustomTimerDpc routine.

A timer can expire just once or it can be set to expire repeatedly at a given interval. KeSetTimer always sets a timer that will expire just once. KeSetTimerEx accepts an optional Period parameter for specifying a recurring interval for the timer.

Figure 3.23 illustrates using a notification timer to set up a time-out interval for an operation and then wait while other driver routines process an I/O request.

Figure 3.23 Waiting on a Timer Object

As Figure 3.23 shows, a driver must provide storage for the timer object, which must be initialized in the DriverEntry or Reinitialize routine by calling KeInitializeTimer with a pointer to this storage.

Within the context of a particular thread, such as a driver-created thread or a thread requesting a synchronous I/O operation, the driver can wait on its timer object as shown in Figure 3.23:

  1. The thread calls KeSetTimer with a pointer to the timer object and a given DueTime, expressed in units of 100 nanoseconds. A positive value for DueTime specifies an absolute time at which the timer object should be removed from the Kernel’s timer queue and set to the Signaled state. A negative value for DueTime specifies an interval relative to the current system time.

    Note that the thread (or driver routine running in a system thread) passes a NULL pointer for the DPC object previously shown in Figure 3.20 when it calls KeSetTimer if it waits on the timer object instead of queueing a CustomTimerDpc routine.

  2. The thread calls KeWaitForSingleObject with a pointer to the timer object, which puts the thread into a wait state while the timer object is in the Kernel’s timer queue.

  3. The given DueTime expires.

  4. The Kernel dequeues the timer object, sets it to the Signaled state, and changes the thread’s state from waiting to ready.

  5. The Kernel dispatches the thread for execution as soon as a processor is available: that is, no other thread with a higher priority is currently in the ready state and there are no kernel-mode routines to be run at raised IRQL (greater than PASSIVE_LEVEL).

NT driver routines that run at raised IRQL can time out requests by using a timer object with an associated DPC object, as already described in Section 3.6, to queue a driver-supplied CustomTimerDpc routine. Only driver routines that run within a nonarbitrary thread context can wait for a nonzero interval on a timer object, as shown in Figure 3.23.

Like every other thread, a driver-created thread is represented by a Kernel thread object, which is also a dispatcher object. Consequently, a driver need not have its driver-created thread use a timer object to voluntarily put itself into a wait state for a given interval. Instead, the thread can call KeDelayExecutionThread with a caller-supplied interval. For more information about this technique, see the section on polling a device in Chapter 16. See also the Kernel-Mode Driver Reference for the specifics of calling KeDelayExecutionThread.

NT drivers’ DriverEntry, Reinitialize, and Unload routines also run in a system thread context, so NT drivers can call KeWaitForSingleObject with a driver-initialized timer object or KeDelayExecutionThread while they are initializing or unloading. A device driver can call KeStallExecutionProcessor for a very short interval (preferably something less than 50 microseconds) if it must wait for the device to update state during its initialization.

However, higher-level NT drivers generally use another synchronization mechanism in their DriverEntry and/or Reinitialize routines instead of using a timer object. Higher-level NT drivers should always be designed to layer themselves over any lower-level driver of a particular type or types of device. Therefore, a higher-level driver tends to become slow-to-load if it waits on a timer object or calls KeDelayExecutionThread because such a driver must wait for an interval long enough to accommodate the slowest possible device supporting it. Note also that a “safe” but minimum interval for such a wait is very difficult to determine.

For more information about the FILE_DEVICE_XXX that NT drivers set in their device objects, see Section 3.2, and see also the Kernel-Mode Driver Reference.

If the system time changes before a timer expires, relative timers are not affected but the system adjusts absolute timers. A relative timer always expires after the specified number of time units elapse, regardless of the absolute system time. An absolute timer expires at a specific system time, so a change in the system time changes the wait duration of an absolute timer.


3.9.2 Event Objects

As mentioned in Section 3.9, some of the system-supplied drivers use an event object in their DriverEntry routines to wait while the next-lower driver processes an IRP set up by the waiting driver. NT drivers that have driver-created threads or driver Dispatch routines that wait on the completion of a synchronous I/O request also can use an event object to synchronize operations between their threads and/or other driver routines.

Any NT driver that uses an event object must call KeInitializeEvent or IoCreate Xxx Event before it waits on, sets, clears, or resets the event. Figure 3.24 illustrates how a driver with a thread can use an event object for synchronization.

Figure 3.24 Waiting on an Event Object

As Figure 3.24 shows, such a driver must provide the storage for the event object, which must be resident. The driver can use the device extension of a driver-created device object (see Section 3.2), the controller extension if it uses a controller object (see Section 3.4), or nonpaged pool allocated by the driver.

When the DriverEntry or Reinitialize routine calls KeInitializeEvent, it must pass a pointer to the driver’s resident storage for the event object. In addition, the caller must specify the initial State (Signaled or Not-Signaled) for the event object, as shown in Figure 3.24. The caller also must specify the Type for its event object, which can be either of the following:

  • SynchronizationEvent

    When such an event is set to the Signaled state, a single thread that is waiting on the event becomes eligible for execution and the event’s state is automatically reset to Not-Signaled.

    This type of event is sometimes called an autoclearing event, because its Signaled state is automatically reset each time a wait is satisfied.

  • NotificationEvent

    When such an event is set to the Signaled state, all threads that were waiting on the event become eligible for execution and the event remains in the Signaled state until an explicit reset to Not-Signaled occurs: that is, there is a call to KeClearEvent or KeResetEvent with the given Event pointer.

Very few NT device or intermediate drivers have a single driver-dedicated thread, let alone a set of threads that might synchronize their operations by waiting on an event that protects a shared resource.

Most NT drivers that use event objects to wait for the completion of an I/O operation set the input Type to NotificationEvent when they call KeInitializeEvent. An event object set up for IRPs that an NT driver creates with IoBuildSynchronousFsdRequest or IoBuildDeviceIoControlRequest is almost always initialized as a NotificationEvent because the caller will wait on the event for notification that its request has been satisfied by one or more lower-level drivers.

After the driver has initialized, its thread, if any, and other routines can synchronize their operations on the event. For example, a driver with a thread that manages the queueing of IRPs, such as the system floppy controller driver, might synchronize IRP processing on an event, as shown in Figure 3.24:

  1. The thread, which has dequeued an IRP for processing on the device, calls KeWaitForSingleObject with a pointer to the driver-supplied storage for the initialized event object.

  2. Other driver routines carry out device I/O operations necessary to satisfy the IRP, and, when these operations are complete, the driver’s DpcForIsr routine calls KeSetEvent with a pointer to the event object, a driver-determined priority boost for the thread (Increment, as shown in Figure 3.24), and a Boolean Wait set to FALSE. Calling KeSetEvent sets the event object to the Signaled state, thereby changing the waiting thread’s state to ready.

  3. The Kernel dispatches the thread for execution as soon as a processor is available: that is, no other thread with a higher priority is currently in the ready state and there are no kernel-mode routines to be run at raised IRQL (greater than PASSIVE_LEVEL).

    The thread now can complete the IRP if the DpcForIsr has not called IoCompleteRequest with the IRP already, and can dequeue another IRP to be processed on the device.

Calling KeSetEvent with the Wait parameter set to TRUE indicates the caller’s intention to immediately call a KeWait..Object(s) support routine on return from KeSetEvent.

NT driver writers should consider the following guidelines for setting the Wait parameter to KeSetEvent:

A pageable thread or pageable driver routine that runs at IRQL PASSIVE_LEVEL should never call KeSetEvent with the Wait parameter set to TRUE. Such a call causes a fatal page fault if the caller happens to be paged out between the calls to KeSetEvent and KeWait..Object(s).

Any standard driver routine that runs at an IRQL greater than PASSIVE_LEVEL cannot wait for a nonzero interval on any dispatcher object(s) without bringing down the system (see Section 3.9). However, such a routine can call KeSetEvent while running at an IRQL less than or equal to DISPATCH_LEVEL.

For a summary of the IRQLs at which standard NT driver routines run, see Chapter 16. For support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.

KeResetEvent returns the previous state of a given Event: whether it was set to Signaled or not when the call to KeResetEvent occurred. KeClearEvent simply sets the state of the given Event to Not-Signaled.

Consider the following a guideline for when to call the preceding support routines:

For better performance, every NT driver should call KeClearEvent unless the caller needs the information returned by KeResetEvent to determine what to do next.


3.9.3 Semaphore Objects

Any NT driver can use a semaphore object to synchronize operations between its driver-created thread(s) and other driver routines. For example, a driver-dedicated thread might put itself into a wait state when there are no outstanding I/O requests for the driver, and the driver’s Dispatch routines might set the semaphore to the Signaled state just after they queue an IRP.

The Dispatch routines of highest-level NT drivers, which are run in the context of the thread requesting an I/O operation, might use a semaphore to protect a resource shared among the Dispatch routines. Lower-level driver Dispatch routines for synchronous I/O operations also might use a semaphore to protect a resource shared among that subset of Dispatch routines or with a driver-created thread.

Any NT driver that uses a semaphore object must call KeInitializeSemaphore before it waits on or releases the semaphore. Figure 3.25 illustrates how a driver with a thread can use a semaphore object.

Figure 3.25 Waiting on a Semaphore Object

As Figure 3.25 shows, such a driver must provide the storage for the semaphore object, which should be resident. The driver can use the device extension of a driver-created device object (see Section 3.2), the controller extension if it uses a controller object (see Section 3.4), or nonpaged pool allocated by the driver.

When the DriverEntry or Reinitialize routine calls KeInitializeSemaphore, it must pass a pointer to the driver’s resident storage for the semaphore object. In addition, the caller must specify a Count for the semaphore object, as shown in Figure 3.25, that determines its initial state (nonzero for Signaled).

The caller also must specify a Limit for the semaphore, which can be either of the following:

  • Limit = 1

    When such a semaphore is set to the Signaled state, a single thread waiting on the semaphore becomes eligible for execution and can access whatever resource is protected by the semaphore.

    This type of semaphore is also called a binary semaphore because a thread either does or does not have exclusive access to the semaphore-protected resource.

  • Limit > 1

    When such a semaphore is set to the Signaled state, some number of threads waiting on the semaphore object become eligible for execution and can access whatever resource is protected by the semaphore.

    This type of semaphore is called a counting semaphore because the routine that sets the semaphore to the Signaled state also specifies how many waiting threads can have their states changed from waiting to ready, which can be the Limit set when the semaphore was initialized or some number less than this preset Limit.

Very few NT device or intermediate drivers have a single driver-created thread, let alone a set of threads that might wait on a semaphore. Few system-supplied drivers use semaphore objects, and, of those that do, even fewer use a binary semaphore. A binary semaphore, while seeming to have similar functionality to a mutex object, does not provide the built-in protection agains deadlocks that a mutex object has for system threads running in SMP machines. For more information about mutex objects, see Section 3.9.4, next.

After a driver with an initialized semaphore is loaded, it can synchronize operations on the semaphore that protects a shared resource. For example, a driver with a device-dedicated thread that manages the queueing of IRPs, such as the system floppy controller driver, might synchronize IRP queueing on a semaphore, as shown in Figure 3.25:

  1. The thread calls KeWaitForSingleObject with a pointer to the the driver-supplied storage for the initialized semaphore object to put itself into a wait state.

  2. IRPs begin to come in that require device I/O operations. The driver’s Dispatch routines insert each such IRP into an interlocked queue (see Section 3.8.2) under spin-lock control and call KeReleaseSemaphore with a pointer to the semaphore object, a driver-determined priority boost for the thread (Increment, as shown in Figure 3.25), an Adjustment of one that is added to the semaphore’s Count as each IRP is queued, and a Boolean Wait set to FALSE. A nonzero semaphore Count sets the semaphore object to the Signaled state, thereby changing the waiting thread’s state to ready.

  3. The Kernel dispatches the thread for execution as soon as a processor is available: that is, no other thread with a higher priority is currently in the ready state and there are no kernel-mode routines to be run at raised IRQL (greater than PASSIVE_LEVEL).

    The thread removes an IRP from the interlocked queue under spin-lock control, passes it on to other driver routines for further processing, and calls KeWaitForSingleObject again. If the semaphore is still set to the Signaled state (that is, its Count remains nonzero, indicating that more IRPs are in the driver’s interlocked queue), the Kernel again changes the thread’s state from waiting to ready.

    By using a counting semaphore in this manner, such a driver thread “knows” there is an IRP to be removed from the interlocked queue whenever that thread is run.

Calling KeReleaseSemaphore with the Wait parameter set to TRUE indicates the caller’s intention to immediately call a KeWait..Object(s) support routine on return from KeReleaseSemaphore.

NT driver writers should consider the following guidelines for setting the Wait parameter to KeReleaseSemaphore:

A pageable thread or pageable driver routine that runs at IRQL PASSIVE_LEVEL should never call KeReleaseSemaphore with the Wait parameter set to TRUE. Such a call causes a fatal page fault if the caller happens to be paged out between the calls to KeReleaseSemaphore and KeWait..Object(s).

Any standard driver routine that runs at an IRQL greater than PASSIVE_LEVEL cannot wait for a nonzero interval on any dispatcher object(s) without bringing down the system (see Section 3.9). However, such a routine can call KeReleaseSemaphore while running at an IRQL less than or equal to DISPATCH_LEVEL.

For a summary of the IRQLs at which standard NT driver routines run, see Chapter 16. For support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.


3.9.4 Mutex Objects

As its name suggests, a mutex object is a synchronization mechanism designed to ensure mutually exclusive access to a single resource shared among a set of kernel-mode threads. Only highest-level drivers, such as FSDs that use executive worker threads, are likely to use a mutex object.

Possibly, a highest-level driver with driver-created threads and/or worker-thread callback routines might use a mutex object. However, any driver with pageable thread(s) or worker-thread callbacks must manage the acquisitions of, waits on, and releases of its mutex object(s) very carefully.

As already mentioned in the preceding section, NT mutex objects have built-in features that provide system (kernel-mode only) threads mutually exclusive, deadlock-free access to shared resources in SMP machines. Every NT mutex object has two basic built-in features that provide this protection for resources shared between or among system threads:

  • Ownership

    The NT Kernel assigns ownership of a given mutex to a single thread at a time, which has the following effects:

    • Raises the owning thread’s runtime priority to the lowest real-time priority if that thread’s priority is not already in the real-time range

    • Prevents the owning thread’s process from leaving the balance set: that is, from being paged out to secondary storage

    • Prevents the delivery of normal kernel-mode asynchronous procedure calls (APCs): that is, from being preempted by an APC unless the Kernel issues an APC_LEVEL software interrupt to run a special kernel APC, such as the I/O Manager’s IRP completion routine that returns results to the original requestor of an I/O operation

    A thread can acquire ownership of a mutex object that it already owns (recursive ownership), but a recursively acquired mutex object is not set to the Signaled state until the thread releases its ownership completely. Such a thread must explicitly releases the mutex as many times as it acquired ownership before another thread can acquire the mutex.

    The Kernel never permits a thread that owns a mutex to cause a transition to user mode without first releasing the mutex and setting it to the Signaled state. If any FSD- or driver-created thread that owns a mutex attempts to return control to the I/O Manager before releasing ownership of the mutex, the Kernel brings down the system.

  • Level

    If a driver thread acquires more than one mutex, the driver should be designed such that the Level of a given mutex object determines whether such a thread can acquire ownership of the mutex at any particular time. In such a case, the driver should enforce the following policy:

    • A thread should acquire mutexes in ascending order of assigned Level. The driver can assign a nonzero Level when it initializes the mutex.

    Thus, the owner of a mutex must not attempt to acquire another mutex with a lower Level since this other mutex might already be owned by another thread that is trying to acquire the first thread’s higher Level mutex. If a driver does not enforce this policy, the scenario that results would cause deadlocks since neither thread could acquire ownership of the other thread’s currently held mutex.

Any NT driver that uses a mutex object must call KeInitializeMutex once before it waits on or releases its mutex object. Figure 3.26 illustrates how two system threads might use a mutex object.

Figure 3.26 Waiting on a Mutex Object

As Figure 3.26 shows, such a driver must provide the storage for the mutex object. Because drivers can wait on and release mutexes only within a thread context, a driver that creates its own system threads can provide storage for its mutex objects in paged pool, allocated by the driver.

If such a highest-level driver uses system worker threads or waits on the mutex in its Dispatch routines, it must provide resident storage in the device extension of a driver-created device object (see Section 3.2), in the controller extension if it uses a controller object (see Section 3.4), or in nonpaged pool allocated by the driver.

When the DriverEntry or Reinitialize routine calls KeInitializeMutex it must pass a pointer to the driver’s storage for the mutex object, which the Kernel initializes to the Signaled state. In addition, the caller can specify a nonzero Level for the mutex object, as shown in Figure 3.26 to control the order of acquisition if the caller uses more than one mutex.

After such a highest-level driver has initialized, it can manage mutually exclusive access to a shared resource as shown in Figure 3.26. For example, a driver’s Dispatch routines for inherently synchronous operations and thread(s) might use a mutex to protect a driver-created queue for IRPs.

Because KeInitializeMutex always sets the initial state of a mutex object to Signaled, as Figure 3.26 shows:

  1. A Dispatch routine’s initial call to KeWaitForSingleObject with the Mutex pointer puts the current thread immediately into the ready state, gives the thread ownership of the mutex, and resets the mutex state to Not-Signaled. As soon as the Dispatch routine resumes running, it can safely insert an IRP into the mutex-protected queue.

  2. When a second thread (another Dispatch routine, driver-supplied worker-thread callback routine, or driver-created thread) calls KeWaitForSingleObject with the Mutex pointer, the second thread is put into the wait state.

  3. When the Dispatch routine finishes queueing the IRP as described in Step 1, it calls KeReleaseMutex with the Mutex pointer and a Boolean Wait value, which indicates whether it intends to call KeWaitForSingleObject (or KeWaitForMutexObject) with the Mutex as soon as KeReleaseMutex returns control.

  4. Assuming the Dispatch routine released its ownership of the mutex in Step 3 (Wait set to FALSE), the mutex is set to the Signaled state by KeReleaseMutex. The mutex currently has no owner, so the Kernel determines whether another thread is waiting on that mutex. If so, the Kernel makes the second thread (see Step 2) the mutex owner, possibly boosts the thread’s priority to the lowest real-time priority value, and changes its state to ready.

  5. The Kernel dispatches the second thread for execution as soon as a processor is available: that is, no other thread with a higher priority is currently in the ready state and there are no kernel-mode routines to be run at raised IRQL (greater than PASSIVE_LEVEL). The second thread (a Dispatch routine queueing an IRP, or the driver’s worker-thread callback routine or driver-created thread dequeueing an IRP) can now safely access the mutex-protected queue of IRPs until it calls KeReleaseMutex.

If a thread acquires ownership of a mutex object recursively, that thread must explicitly call KeReleaseMutex as many times as it waited on the mutex in order to set the mutex object to the Signaled state. For example, if a thread calls KeWaitForSingleObject and then KeWaitForMutexObject with the same Mutex pointer, it must call KeReleaseMutex twice when it acquires the mutex in order to set that mutex object to the Signaled state.

Calling KeReleaseMutex with the Wait parameter set to TRUE indicates the caller’s intention to immediately call a KeWait..Object(s) support routine on return from KeReleaseMutex.

NT driver writers should consider the following guidelines for setting the Wait parameter to KeReleaseMutex:

A pageable thread or pageable driver routine that runs at IRQL PASSIVE_LEVEL should never call KeReleaseMutex with the Wait parameter set to TRUE. Such a call causes a fatal page fault if the caller happens to be paged out between the calls to KeReleaseMutex and KeWait..Object(s).

Any standard driver routine that runs at an IRQL greater than PASSIVE_LEVEL cannot wait for a nonzero interval on any dispatcher object(s) without bringing down the system (see Section 3.9,). However, such a routine can call KeReleaseMutex if it owns the mutex while running at an IRQL less than or equal to DISPATCH_LEVEL.

For a summary of the IRQLs at which standard NT driver routines run, see Chapter 16. For support-routine-specific IRQL requirements, see the Kernel-Mode Driver Reference.


Chapter 4 Basic Driver Structure

This chapter contains the following information:
4.1 Standard NT Driver Routines

4.2 Device Drivers’ Staged IRP Processing

4.3 Intermediate Drivers’ Staged IRP Processing

4.4 Designing and Developing an NT Driver
4.4.1 Naming Device Objects

4.4.2 Choosing Names for NT Driver Routines

4.4.3 Starting Design

4.4.4 Starting Development

The basic structure of any Windows NT® kernel-mode driver consists of a set of required standard driver routines, plus some number of optional standard routines and internal routines, as determined by the type of driver and the driver’s designer. The common set of standard routines allows these drivers to process IRPs by calling NT support routines.

This chapter presents an overview of the standard routines for NT drivers, so that driver writers can determine the following:

  • The set of standard driver routines they must implement to process IRPs

  • The set of standard driver routines they can implement, depending on the nature of the underlying device or the functionality required of each driver

  • How, in general, to begin the design and development of a new NT driver

For specific information about any of the support routines mentioned in this chapter, see the Kernel-mode Driver Reference. For specific information about the required functionality of each standard routine, see Chapters 5 through 15.


4.1 Standard NT Driver Routines

All NT driver writers must implement certain system-defined standard routines and can implement others, along with any internal routines that the driver designer chooses, as mentioned in Chapter 2.

Figure 4.1 shows the NT driver object, representing a driver’s load image, and the set of standard routines for NT drivers.

Figure 4.1 Standard NT Driver Routines

As Figure 4.1 shows, all NT drivers must have a basic set of standard routines in order to process IRPs. Otherwise, the set of standard routines that can or must be implemented depends on whether the driver controls a physical device or is layered over such a device driver, as well as on the nature of the underlying physical device.

Every NT driver must have the following types of routines, which are defined by the NT I/O Manager:

DriverEntry

    NTSTATUS
    (*PDRIVER_INITIALIZE) (
        IN PDRIVER_OBJECT DriverObject,
        IN PUNICODE_STRING RegistryPath
        );

Every NT driver must have an initialization routine, which the I/O Manager calls automatically if this routine is explicitly named DriverEntry. For more information about a DriverEntry routine’s functional requirements, see Chapter 5.

Dispatch

    NTSTATUS
    (*PDRIVER_DISPATCH) (
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
        );

Every NT driver must have at least one Dispatch routine, but any driver can have a separate Dispatch routine for each IRP major function code (IRP_MJ_XXX) it handles. For more information about Dispatch routines’ functional requirements, see Chapter 6. For device-type-specific information about which major function codes NT drivers must handle, see the Kernel-mode Driver Reference.

StartIo (or Queue-management)

    VOID
    (*PDRIVER_STARTIO) (
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
        );

If an NT device driver cannot complete all possible I/O requests in its Dispatch routines, it either must have a StartIo routine or must set up one or more driver-created internal queues and manage its own queueing of IRPs. A higher-level NT driver might have a StartIo routine, and it can have driver-created internal queues of IRPs. For more information about the StartIo routine’s functional requirements and about managing queues of IRPs, see Chapter 7.

Depending on an NT driver’s level in a chain of layered drivers, if any, and on the nature of the underlying physical device, an NT driver can (or must) have the following types of routines, which are defined by the NT I/O Manager or Kernel:

Reinitialize

    VOID
    (*PDRIVER_REINITIALIZE) (
        IN PDRIVER_OBJECT DriverObject,
        IN PVOID Context,
        IN ULONG Count
        );

In addition to its DriverEntry routine, an NT driver can have a Reinitialize routine to be called one or more times later in the system boot process after its DriverEntry routine returns control. For more information about an optional Reinitialize routine’s functionality, see Chapter 5.

InterruptService (ISR)

    BOOLEAN
    (*PKSERVICE_ROUTINE) (
        IN PKINTERRUPT Interrupt,
        IN PVOID ServiceContext // usually points to device object
        );

Any driver of a physical device that generates interrupts must have an ISR. Such a driver is always the lowest-level driver in any chain of layered NT drivers. For more information about an ISR’s functional requirements, see Chapter 8.

DpcForIsr or CustomDpc

    VOID
    (*PIO_DPC_ROUTINE) (
        IN PKDPC Dpc,
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp,
        IN PVOID Context
        );

    VOID
    (*PKDEFERRED_ROUTINE) (
        IN PKDPC Dpc,
        IN PVOID DeferredContext,
        IN PVOID SystemArgument1,
        IN PVOID SystemArgument2
        );

Any driver that has an ISR must have a DpcForIsr or CustomDpc routine, and can have additional CustomDpc routines. For more information about the DpcForIsr routine’s functional requirements and about CustomDpc routines, see Chapter 9.

SynchCritSection

    BOOLEAN
    (*PKSYNCHRONIZE_ROUTINE) (
        IN PVOID SynchronizeContext
        );

Any lowest-level device driver with data or device registers shared between its ISR and other driver routines must have one or more SynchCritSection routines. For more information about a SynchCritSection routine’s functional requirements, see Chapter 10.

AdapterControl and/or ControllerControl

    IO_ALLOCATION_ACTION
    (*PDRIVER_CONTROL) (
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp,
        IN PVOID MapRegisterBase,
        IN PVOID Context
        );

Any driver of a device that uses system DMA or packet-based busmaster DMA must have an AdapterControl routine. Any device driver that must synchronize operations through a physical controller to similar devices (or device channels) can have a ControllerControl routine. For more information about these routines’ functional requirements, see Chapter 11.

Cancel

    VOID
    (*PDRIVER_CANCEL) (
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
        );

Keyboard, mouse, serial, parallel, and sound device drivers, as well as file system drivers, have Cancel routines. Any driver in which IRPs might remain queued for an indefinite interval (so that a user could cause a previously submitted I/O request to be cancelled) must have one or more Cancel routines. Usually, the highest NT driver in a chain has a Cancel routine. For more information about a Cancel routine’s functional requirements, see Chapter 12.

IoCompletion

    NTSTATUS
    (*PIO_COMPLETION_ROUTINE) (
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp,
        IN PVOID Context
        );

Any higher-level NT driver that allocates IRPs to send to lower drivers must have at least one IoCompletion routine to free all IRPs that the driver allocates. However, any higher-level driver can have one or more IoCompletion routines. Other driver routines can set up an IoCompletion routine to be called when all lower-level drivers have finished processing a given IRP. For a general discussion of how higher-level drivers can use IoCompletion routines, see Chapter 2. See also Section 4.3 for an overview of how a representative NT intermediate driver’s IoCompletion routine processes IRPs. For more information about an IoCompletion routine’s functional requirements, see Chapter 13.

IoTimer and/or CustomTimerDpc

    VOID
    (*PIO_TIMER_ROUTINE) (
        IN PDEVICE_OBJECT DeviceObject,
        IN PVOID Context
        );

    VOID
    (*PKDEFERRED_ROUTINE) (
        IN PKDPC Dpc,
        IN PVOID DeferredContext,
        IN PVOID SystemArgument1,        // reserved for system use
        IN PVOID SystemArgument2            // reserved for system use
        );

To monitor whether an I/O operation has timed out or for some other purpose determined by the driver designer, any driver can have IoTimer and/or CustomTimerDpc routines. An IoTimer routine is called once per second when the driver has enabled the timer. A CustomTimerDpc routine can be called at finer-grained intervals or at variable intervals. For more information about timer routines’ functional requirements, see Chapter 14.

Unload

    VOID
    (*PDRIVER_UNLOAD) (
        IN PDRIVER_OBJECT DriverObject
        );

An NT driver must have an Unload routine if it can be unloaded while the system continues to run. For more information about an Unload routine’s functional requirements, see Chapter 15.

As the preceding declarations for standard NT driver routines show, the current IRP and target device object are input parameters to many standard routines defined by the I/O Manager. Every NT driver processes each IRP in stages through its set of standard routines. Sections 4.2 and 4.3 describe the general path for an IRP through a representative lowest-level physical device driver and a representative intermediate driver, respectively.


4.2 Device Drivers’ Staged IRP Processing

As shown in Figure 4.1, lowest-level NT drivers have certain standard routines that higher-level NT drivers need not have. The set of standard routines for NT device drivers also varies according to the following criteria:

  • The nature of the device each controls

  • Whether a particular driver sets up its device objects for direct or buffered I/O, as described in Chapter 3

  • The design of any particular device driver

Figure 4.2 illustrates the path an IRP takes through the standard routines of an NT mass-storage device driver with the following characteristics:

  • The device generates interrupts at the end of each I/O operation, so this driver has ISR and DpcForIsr routines.

  • The driver has a StartIo routine, rather than setting up internal queues for IRPs and managing its own queueing.

  • The driver uses system DMA, so it sets its device objects’ Flags for direct I/O, as described in Chapter 3, and has an AdapterControl routine.

Figure 4.2 IRP Path through NT Device Driver Routines (DMA)

As Figure 4.2 shows, an IRP is sent first to the driver’s Dispatch routine for the given major function code (IRP_MJ_XXX), in this case DDDispatchReadWrite. An NT driver is never sent an IRP with an unknown function code, because each driver sets its Dispatch routine(s) in the driver object on an IRP_MJ_XXX-specific basis when the driver initializes.

Calling IoGetCurrentIrpStackLocation

A Dispatch routine that handles more than one IRP_MJ_XXX, handles an IRP_MJ_XXX with minor subfunctions (IRP_MN_XXX), or handles device I/O control requests (IRP_MJ_DEVICE_CONTROL and/or IRP_MJ_INTERNAL_DEVICE_CONTROL), and every other driver routine that processes each IRP must call IoGetCurrentIrpStackLocation in order to determine what to do and what parameters to use.

The IRP shown in Figure 4.2 requests a data transfer operation (IRP_MJ_READ or IRP_MJ_WRITE), and this driver’s I/O stack location is the lowest in the IRP, with an indefinite number of higher-level drivers’ I/O stack locations shown shaded. For simplicity, calls to IoGetCurrentIrpStackLocation from the DispatchReadWrite, StartIo, AdapterControl, and DpcForIsr routines are not shown in Figure 4.2.

Calling IoMarkIrpPending and IoStartPacket

Assuming the parameters for the read/write request are valid, the Dispatch routine would call IoMarkIrpPending to indicate that the IRP is not yet completed, and IoStartPacket to queue or pass the IRP on to the driver’s StartIo routine for further processing. The Dispatch routine would also return the NTSTATUS value STATUS_PENDING.

Figure 4.3 illustrates such a call to IoStartPacket.

Figure 4.3 Calling IoStartPacket

If the driver is currently busy processing another IRP on the device, IoStartPacket will insert the IRP into the device queue associated with the device object. If the driver is not busy and the device queue is empty, its StartIo routine will be called immediately with the input IRP.

Note that the driver of a mass-storage device would not supply a Cancel routine when it calls IoStartPacket both because a file system layered over such a driver handles the cancelation of file I/O requests and because mass-storage device drivers process IRPs so quickly. Usually, the highest-level driver in a chain of layered NT drivers handles the cancelation of IRPs.

The device driver shown in Figure 4.2 might supply a Key value to impose a driver-determined order on IRPs in the device queue when it calls IoStartPacket.

Calling IoAllocateAdapterChannel and IoMapTransfer

Assuming the StartIo routine shown in Figure 4.2 finds that the transfer request can be done by a single DMA operation, the StartIo routine calls IoAllocateAdapterChannel with the entry point of the driver’s AdapterControl routine and the IRP.

When the system DMA controller is available, the AdapterControl routine is called to set up the transfer operation, as shown in Figure 4.2. The AdapterControl routine calls IoMapTransfer with a pointer to the buffer, described in the MDL at Irp->MdlAddress, to set up the system DMA controller. Then, the driver programs its device for the DMA operation and returns. (For more detailed information about using DMA and adapter objects, see Chapter 3.)

Calling IoRequestDpc from the Driver’s ISR

When the device interrupts to indicate its transfer operation is complete, the driver’s ISR stops the device from generating interrupts and calls IoRequestDpc, as shown in Figure 4.2.

This call queues the driver’s DpcForIsr routine to complete as much of the transfer operation as possible at a lower hardware priority (IRQL).

Calling IoStartNextPacket and IoCompleteRequest

When the DpcForIsr routine has done its processing for the transfer, it calls IoStartNextPacket promptly so the driver’s StartIo routine will be called with the next IRP in the device queue, if any are queued. The DpcForIsr also sets the just completed IRP’s I/O status block and then calls IoCompleteRequest with the IRP.

Figure 4.4 illustrates this driver’s calls to IoStartNextPacket and IoCompleteRequest.

Figure 4.4 Calling IoStartNextPacket and IoCompleteRequest

Contrary to what Figure 4.4 implies, most NT device drivers call IoStartNextPacket before they call IoCompleteRequest.

NT drivers should call IoStartNextPacket or IoStartNextPacketByKey to begin the next requested I/O operation as soon as possible, preferably before they call IoCompleteRequest and certainly before they return control. If no IRPs are currently in the device queue, IoStartNextPacket merely returns to the caller.

Setting the I/O Status Block in an IRP

Every NT device driver must set the I/O status block in the IRP before calling IoCompleteRequest, in order to supply information to any interested higher-level drivers and, ultimately, to the original requestor of the I/O operation. Any higher-level driver layered above the device driver in Figure 4.4 might have set up an IoCompletion routine that reads the I/O status block set by the device driver. However, higher-level NT drivers usually do not modify the I/O status block in an IRP that has been completed by a device driver, unless such a higher-level driver is retrying the IRP and, therefore, reinitializes the I/O status block.

Every higher-level NT driver that completes an IRP without sending it on to the next lower driver also must set the I/O status block in that IRP before it calls IoCompleteRequest. For good overall I/O throughput, a higher-level driver should check the parameters in its own I/O stack location of a given IRP and, if it determines that they are invalid, should set the I/O status block and complete the request itself, rather than passing an invalid request on to lower drivers in the chain.

The I/O status block is defined as follows:
typedef struct _IO_STATUS_BLOCK {
    NTSTATUS Status;
    ULONG Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

Assuming the transfer operation in Figure 4.4 was successful, the DpcForIsr shown in Figure 4.2 would set STATUS_SUCCESS in Status and the number of bytes transferred in Information for the IRP’s I/O status block.

Note that certain standard NT driver routines also return NTSTATUS-type values, as shown by the declarations in Section 4.1. For more information about NTSTATUS constants like STATUS_SUCCESS, see Chapter 16.

Supplying a PriorityBoost in Calls to IoCompleteRequest

If an NT device driver can complete an IRP in its Dispatch routine, it calls IoCompleteRequest with a PriorityBoost of IO_NO_INCREMENT because that driver can assume that the original requestor did not wait on its I/O operation.

Otherwise, such a device driver supplies a system-defined and device-type-specific value that boosts the requestor’s runtime priority to compensate for the time the requestor waited on its device I/O request. (See ntddk.h for specifics.)

Higher-level NT drivers apply the same PriorityBoost as their respective underlying device drivers when they call IoCompleteRequest with an IRP.

Effect of Calling IoCompleteRequest

When a driver calls IoCompleteRequest with a given IRP, the I/O Manager fills that driver’s I/O stack location with zeros before calling the next higher-level driver, if any, that has set up its IoCompletion routine to be called for the IRP.

A higher-level driver’s IoCompletion routine can check only the IRP’s I/O status block to determine how all lower drivers handled a given request.

The caller of IoCompleteRequest must not attempt to access the just completed IRP. Such an attempt is a programming error that causes a system crash.


4.3 Intermediate Drivers’ Staged IRP Processing

As shown in Figure 4.1, higher-level NT drivers have a different set of standard routines than lowest-level device drivers, with an overlapping subset of standard routines common to both types of drivers.

The set of routines for intermediate and highest-level NT drivers also varies, according to the following criteria:

  • The nature of the underlying physical device

  • Whether the underlying device driver(s) set up device objects for direct or buffered I/O

  • The design of any particular higher-level driver

Figure 4.5 illustrates the path an IRP might take through the standard routines of an NT driver layered somewhere over the device driver described Section 4.2.

The intermediate mirror driver shown in Figure 4.5 has the following characteristics:

  • The driver is layered over more than one physical device, and, possibly over more than one device driver.

  • The driver sometimes allocates additional IRPs for lower-level drivers, depending on the requested operation in the input IRP.

  • The driver has at least one NT file system driver layered above it, which also might be layered above other intermediate drivers at a higher level than this one.

Figure 4.5 IRP Path through NT Intermediate Driver Routines

As Figure 4.5 shows, an IRP is sent first to the intermediate driver’s Dispatch routine for the given major function code (IRP_MJ_XXX). This IRP also requests a data transfer operation (IRP_MJ_WRITE), and the intermediate driver’s I/O stack location is shown in the middle, with an indefinite number of I/O stack locations for higher- and lower-level drivers shown shaded.

Allocating IRPs

This driver mirrors write requests to another physical device but sends read requests alternately to the driver(s) of devices with mirrored partitions. For write requests, it creates IRPs for both the original device and for a second device on which the data is mirrored, assuming the parameters in the input IRP are valid.

Figure 4.5 happens to show a call to IoAllocateIrp but higher-level NT drivers can call other support routines to allocate IRPs for requests they send to lower-level drivers. For more information about allocating IRPs, see Chapter 6.

When the Dispatch routine calls IoAllocateIrp, it specifies the number of I/O stack locations needed in each new IRP. The driver must specify a stack location for each lower driver in the chain, getting the appropriate value from the device objects of each driver just below the mirror driver. At the driver writer’s discretion, it can add one to this value when it calls IoAllocateIrp to get a stack location of its own in each IRP it allocates, as the driver in Figure 4.5 does.

This intermediate driver’s Dispatch routine calls IoGetCurrentIrpStackLocation (not shown) with the original IRP to determine what it should be doing and to check parameters, if possible.

It calls IoSetNextIrpStackLocation because it allocated its own stack location in each newly created IRP and IoGetCurrentIrpStackLocation to create a context for itself that it uses later in the IoCompletion routine.

Then, it calls IoGetNextIrpStackLocation with each newly created IRP so that it can set up the next lower-level drivers’ I/O stack locations in the IRPs it allocated. The mirror driver’s Dispatch routine copies the IRP function codes and parameters (pointer to the transfer buffer, length in bytes to be transferred for IRP_MJ_WRITE) into the I/O stack locations for the next-lower drivers. These drivers, in turn, will set up the I/O stack locations for the drivers just below themselves, if any.

Calling IoSetCompletionRoutine and IoCallDriver

The Dispatch routine in Figure 4.5 calls IoSetCompletionRoutine with each IRP it allocated.

A higher-level NT driver can request that its IoCompletion routine be called when lower drivers complete an IRP with one, some, or any of STATUS_SUCCESS, STATUS_CANCELLED, or an error status. Because the driver in Figure 4.5 must dispose of the IRPs it allocated, this driver sets its IoCompletion routine to be called when lower drivers complete its IRPs, whatever their completion status values.

Because the driver in Figure 4.5 mirrors in parallel, it passes both IRPs that it allocated on to the next-lower-level drivers by calling IoCallDriver twice, once for each target device object representing a mirrored partition.

Processing IRPs in the Driver’s IoCompletion Routine

When either set of lower-level drivers completes the requested operation, the intermediate mirror driver’s IoCompletion routine is called. This routine must determine whether the other set of lower drivers has also completed the requested operation, so the mirror driver maintains a count in its own I/O stack location of the original IRP.

Assuming that the I/O status block indicates that one set of lower drivers has completed the DupIRP1 shown in Figure 4.5 successfully, the mirror driver’s IoCompletion routine decrements its count but cannot complete the original IRP until it decrements the count to zero. If the decremented count is not yet zero, the IoCompletion routine calls IoFreeIrp with the first-returned IRP (DupIRP1 in Figure 4.5) that the driver allocated and returns STATUS_MORE_PROCESSING_REQUIRED.

When the mirror driver’s IoCompletion routine is called again with the DupIRP2 shown in Figure 4.5, the IoCompletion routine decrements the count in the original IRP and determines that both sets of lower-level drivers have carried out the requested operations.

Assuming the I/O status block in DupIRP2 also is set with STATUS_SUCCESS, the IoCompletion routine copies the returned IRP’s I/O status block into the original IRP and frees the second IRP it created. It calls IoCompleteRequest with the original IRP, and returns STATUS_MORE_PROCESSING_REQUIRED to forestall the I/O Manager’s completion processing of DupIRP2.

If either set of lower-level drivers does not complete the mirror driver’s IRPs successfully, the mirror driver’s IoCompletion routine would log an error and attempt appropriate mirrored-data recovery. For more information about logging errors, see Chapter 16.


4.4 Designing and Developing an NT Driver

The following subsections offer general advice about getting the design and development process started.


4.4.1 Naming Device Objects

NT drivers’ device objects have names for two basic reasons:

  • So that higher-level NT drivers can layer themselves over lower-level NT drivers

    The DriverEntry routine of each higher-level NT driver must pass a pointer to the name of the next-lower-level driver’s device object when it calls IoGetDeviceObjectPointer or IoAttachDevice. NT drivers at every level should name their device objects to support the addition of value-added, higher-level NT drivers.

  • So that user-mode applications can carry out I/O operations

    Any driver of a physical, logical, or virtual device to which user-mode code can direct I/O requests must name the device object it creates. For example, when an application or end user calls a Win32® function to connect to a printer port, such as LPT1, the Win32 subsystem translates the application I/O request into a call to one of the NT I/O Manager’s system services with the name of a file object that represents the printer port.

    Such a named file object has an associated symbolic link object in the Win32 namespace, \??, that aliases the Win32 name to the name of the appropriate device object in the NT namespace for device objects. The NT Object Manager and I/O Manager parse the input file object name and chase the symbolic link to identify the target device for the I/O operation. Then, the I/O Manager sets up an IRP for the system parallel class driver.

The system uses a simple naming scheme to identify the NT devices in every machine. In general, each device object that can be the target of an I/O request or can be connected to by a higher-level NT driver has a name of the form:

\Device\GenericDeviceTypeDigit
where

GenericDeviceType indicates the kind of device, such as Harddisk, Serial, Tape, etc.

and
The Digit suffix indicates the zero-based number of this device, depending on how many other devices of the same type have already been named.

Every NT driver must follow these naming conventions for device objects.

Note that each GenericDeviceType guarantees that all device objects representing a particular type of device have the same name, so NT higher-level drivers can readily find device objects representing the set of underlying physical devices over which such a higher-level driver can layer itself. Note also that the Digit suffix guarantees that each device object of that type has a unique name in every Windows NT machine. For more information about how to assign an appropriate Digit suffix to a named device object, see also the section on using the registry in Chapter 16.

The DriverEntry routine is responsible for setting up a unique-to-the-machine Unicode string to name each device object that an NT driver creates, except for any device object that cannot be a target for an I/O request or that cannot be connected to by a higher-level driver.


4.4.2 Choosing Names for NT Driver Routines

Except for the DriverEntry routine, the names used for the standard driver routines in this documentation were chosen to describe the functionality of each type of standard routine, and, in some cases, to provide hints about which types of NT objects a driver with particular standard routines must use. You can name most standard driver routines anything you like in your driver.

However, any NT driver is easier to develop, debug, and test when the driver writer uses a naming scheme that distinguishes that driver’s routines from those of other drivers. An NT driver is also easier to debug and test if the driver writer uses names that distinguish between the driver’s standard routines and its internal routines.

Consequently, most system-supplied NT drivers have routine names that follow these guidelines:

  • Prepend a driver-specific prefix to each routine, preferably one prefix for all standard routines and another for all internal routines.

  • Create a name for each routine that indicates its functionality and, when debugging and testing, where in the driver a given IRP is being processed.

For example, the system keyboard class driver prepends KeyboardClass to its standard driver routines, and Kbd to its internal routines. Other system drivers use a single identifying prefix on every routine, but prepend the lower-case letter “p” (for private) to this prefix for their internal routines.

Each routine in the system keyboard class driver also has a name that indicates the routine’s functionality. This driver’s Dispatch routines are named according to the IRP_MJ_XXX that each routine handles: KeyboardClassOpenClose, KeyboardClassRead, KeyboardClassDeviceControl, and so forth. Other standard routines in this driver have names such as KeyboardClassStartIo and KeyboardClassCancel to aid the driver’s developer in tracing the path of IRPs through the driver.

The keyboard class driver’s naming conventions also aid system test engineers in evaluating the performance of this driver, in finding out whether any particular driver routine is a performance bottleneck, and in finding the exact location of any bug if problems occur in this driver during regression testing.


4.4.3 Starting Design

The following suggests a general set of design criteria that any NT driver writer can use to start designing a driver.

Which I/O Requests?

Before writing any code, see the Kernel-mode Driver Reference to determine which IRP major function codes your driver must handle:

  • If you are designing a device driver, your driver must handle the same set of IRP_MJ_XXX and device I/O control codes as every other NT driver for the same type of peripheral device.

  • If you are designing an intermediate NT driver, identify the underlying physical device or devices your driver will be layered over, because a higher-level driver must have Dispatch entry points for most IRP_MJ_XXX that the drivers under it must handle. A higher-level NT driver must set up the next-lower driver’s I/O stack location in IRPs and pass them on to lower drivers with IoCallDriver when the parameters in its own I/O stack location of each such IRP are valid. Consequently, any intermediate NT driver must set Dispatch entry points in its driver object for those IRP_MJ_XXX to be passed on.

How Many Dispatch Routines?

When you have identified the IRP_MJ_XXX your driver must handle, you can determine the upper limit on the number of Dispatch routines your driver might have. You can also start considering whether to combine particular IRP_MJ_XXX into convenient subsets to be handled by particular Dispatch routines.

For example, most lowest-level and intermediate NT drivers need do little or no device or IoCompletion processing for an IRP_MJ_CREATE (equivalent to an “open target device” request for these types of drivers) or IRP_MJ_CLOSE request. Except for underlying disk devices and NT drivers with pageable-image sections, IRPs containing IRP_MJ_CREATE usually just establish the existence of the target device object for higher-level drivers and for user-mode protected subsystems via the I/O Manager’s system services. For lower-level drivers, IRPs containing IRP_MJ_CLOSE usually indicate that a user-mode subsystem on behalf of an application has closed the file object handle for the device to which it was sending I/O requests.

For create/close requests, many NT device and intermediate drivers simply set the IRP’s I/O status block with STATUS_SUCCESS in the Status member and zero in the Information member and, then, call IoCompleteRequest with the IRP from their Dispatch routines. Consequently, many lowest-level and intermediate NT drivers have a combined Dispatch routine for create/close requests.

However, a serial device driver usually resets its device for a create request; it can lock down a pageable-image section when it processes create requests and unlock its pageable-image section when it processes close requests. Disk device drivers are called with create requests only when a higher-level driver calls IoGetDeviceObjectPointer or IoAttachDevice; because any disk driver might control the device that holds the system page file, they are not called with close requests. On the other hand, NT file system drivers do considerably more processing for create/close requests.

If you are designing a higher-level NT driver, consider the set of IRP major function codes your driver must handle and determine the set of requests for which you might need to implement an IoCompletion routine.

As a general rule, a higher-level NT driver need not have an IoCompletion routine for each kind of request. It must have an IoCompletion routine to dispose of the IRPs the driver allocates, if any. Otherwise, it needs IoCompletion routines only for those IRP_MJ_XXX that could require further processing in the higher-level driver, depending on how lower drivers handle a given IRP_MJ_XXX request.

How Many Device Objects?

Next, consider how many named device objects your driver must create:

  • A device driver must create a named device object for each physical or logical device that could be the target of an I/O request. Some lowest-level NT drivers must create an indeterminate number of device objects, as mentioned in Chapter 2. For example, a disk driver must create a device object for each physical disk and additional device objects for partitions on its disks.

  • A higher-level driver must create a named device object to represent its own virtual device, so that still-higher-level drivers can connect their device objects to this driver’s device object(s). In addition, a higher-level driver usually creates a set of virtual or logical device objects that correspond to the named device objects created by the next-lower driver(s).

You can develop your driver in stages, as outlined in Section 4.4.4, so an under-development driver need not create every device object that the fully developed driver will have. Nevertheless, determining how many device objects the driver must eventually create helps to identify any synchronization problems the driver writer must solve.

Determining the number of necessary or possible device objects also can help a driver writer in defining the driver-determined contents and structure of the device extension(s) for the driver’s device object(s). For example, if the device extension will contain context shared between a device driver’s ISR and its other routines, consider setting up the device extension to isolate all the shared context in a part of the device extension. The driver’s nonISR routines can then access other areas in the device extension without having to call KeSynchronizeExecution with the SynchCritSection routine, discussed in Chapter 10.

As another example, if one nonISR routine shares an area with another, the driver’s device extension would likely contain an executive spin lock, used to protect that area from simultaneous access by both routines. For more information about excecutive spin locks, see Chapter 16.

StartIo Routine or Queue Management in Driver Thread Context?

If you are writing a driver for a physical device, determine whether setting up the device for I/O requires the driver to wait for longer than around 50 microseconds for device state changes:

  • If so, your driver should have a device-dedicated thread or, in a file system driver, possibly one or more worker-thread callback routines. Such a driver must manage its own internal queues of IRPs, as the system floppy controller driver mentioned in Chapter 2 does.

  • Otherwise, your driver can have a StartIo routine and call IoStartPacket from its Dispatch routines and IoStartNextPacket or IoStartNextPacketByKey from its DpcForIsr (or CustomDpc) routine, as described in Section 4.2, thereby relying on the I/O Manager to manage the queueing of IRPs for your driver.


4.4.4 Starting Development

It is easier to develop any driver that is coded in stages, so here is a “cookbook” for getting a skeleton NT driver up and running quickly.

Getting a Skeleton Driver to Load

  1. Write a DriverEntry routine that calls IoCreateDevice to create a single named device object (see Chapter 3 for how to set up a device object).

  2. Next, write a skeleton Dispatch routine for IRP_MJ_CREATE requests (see Section 4.4.3 for a description of the minimum work a DispatchCreate routine must do), and revise the DriverEntry routine to set the DispatchCreate entry point in the driver object (see Chapter 2 for how to set an entry point in the driver object).

    For any request, a higher-level NT driver’s DriverEntry routine must establish a connection to a next-lower-level driver (see Chapter 5), and its Dispatch routine must set up the next-lower driver’s I/O stack location in IRPs.

  3. Compile and link your driver.

  4. Test this skeleton driver by doing the following:

    • Set up a symbolic link between a Windows NT logical device name (such as LPT1 or drive E:) and the target device object’s name, in the registry. Use regedt32 to add a new symbolic link (of type REG_SZ) between a Win32-visible device name and the target device object’s name in the registry key \HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control
      \Session Manager\DOS Devices      .

      You must reboot the machine for this to take effect, but the newly created symbolic link is persistent across system boots thereafter in your machine. Later, the retail driver should set up these symbolic links with the corresponding protected subsystem(s)’ device names in the registry. For more information about how to set up these symbolic links, see Chapter 16.

    • Write a simple test program to call the Win32 function CreateFile with the Win32 name for the appropriate file object to be sure your driver was loaded and that the NT I/O Manager can send IRPs to your driver’s DispatchCreate routine.

Supporting an Additional Request to More Target Device Objects

  1. Revise the DriverEntry routine to create other named device objects, if any, that the driver will need and to set up a Dispatch entry point for IRP_MJ_CLOSE requests.

    Note that the skeleton Dispatch routine for IRP_MJ_CREATE requests can usually handle IRP_MJ_CLOSE requests for an NT device or intermediate driver, so whether you write a separate DispatchClose routine is entirely up to you.

  2. Compile and link your revised driver.

  3. Test the driver as described in the preceding subsection to be sure that all the driver’s new device objects can be opened and closed with calls to the Win32 CreateFile and CloseHandle functions.

Supporting Device I/O Requests

  1. Add another Dispatch routine for an IRP_MJ_XXX that requires more processing than a create/close request.

    For example, write a DispatchRead routine that reads data from the device into memory or that intercepts read requests for a set of lower drivers, that is, handles IRP_MJ_READ requests.

  2. Revise the DriverEntry routine to set the DispatchRead entry point in the driver object and to set up any NT objects that handling read requests will require. Write any additional driver routines necessary to process a read request.

    • A lowest-level driver would need to have at least skeleton StartIo, ISR, and DpcForIsr routines, would probably need to have a SynchCritSection routine, and would need to have a skeleton AdapterControl routine if the device uses DMA.

      The DriverEntry routine must set the StartIo entry point in the driver object, and set up the driver’s other new routines. The DriverEntry routine must register the ISR (interrupt object) and would need to set up an adapter object for DMA. It also must initialize the physical device. Note that the ISR could be called immediately following a successful return from IoConnectInterrupt, so the device-initialization code might need to disable interrupts at the device before the DriverEntry routine registers the ISR (and re-enable interrupts at the device later).

    • A higher-level driver could have a skeleton IoCompletion routine that, at least, checks the I/O status block for STATUS_SUCCESS and calls IoCompleteRequest with the IRP.

      The DriverEntry routine must set the DispatchRead entry point in the driver object. The DispatchRead routine must call IoSetCompletionRoutine to set the driver’s new IoCompletion routine in an IRP.

  3. Revise the internal structure of the device extension, as necessary:

    • For such a lowest-level driver, the device extension should provide storage for an interrupt object pointer, for any context area to be shared between the ISR and StartIo or DpcForIsr routines, plus storage for adapter object information if the device uses DMA.

    • For such a higher-level driver, the device extension must provide storage for the next-lower-level driver’s device object pointer. Otherwise, its contents and structure are driver-defined.

  4. Compile and link your revised driver.

  5. Test this set of routines by calling the Win32 CreateFile, ReadFile, and CloseHandle functions, and by using the NT kernel debugger to trace the path of IRPs through the driver’s new standard routines (see Section 4.2 or 4.3).

Continue to add functionality to your driver by coding new Dispatch routines and, in higher-level drivers, possibly new IoCompletion routines to handle the set of required IRP_MJ_XXX. Revise the DriverEntry routine and device extension (or other driver-allocated storage) as required. Flesh out existing routines in the driver as you add new Dispatch routines, such as adding error logging. Test the driver at each development stage, and use the debugger to trace IRPs through your driver to discover any bugs.

For more information about logging I/O errors, see Chapter 16. For more information about debugging NT drivers, see the Programmer’s Guide.


Chapter 5 DriverEntry and Reinitialize Routines

This chapter contains the following information:
5.1 DriverEntry Routine Requirements

5.1.1 Every DriverEntry Routine’s Responsibilites

5.1.2 DriverEntry’s Additional Responsibilites

5.2 DriverEntry Routine Functionality
5.2.1 Device Driver’s Initialization

5.2.2 Higher-Level Driver’s Initialization

5.3 Reinitialize Routine Requirements and Functionality

The DriverEntry routine is defined by the I/O Manager as follows:

NTSTATUS
(*PDRIVER_INITIALIZE) (
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );

The Reinitialize routine is defined by the I/O Manager as follows:

VOID
(*PDRIVER_REINITIALIZE) (
    IN PDRIVER_OBJECT DriverObject,
    IN PVOID Context,
    IN ULONG Count
    );

This chapter summarizes the required functionality of NT drivers’ standard DriverEntry routines. It also summarizes the required functionality of an optional Reinitialize routine.

For specific information about any of the support routines mentioned in this chapter, see the Kernel-mode Driver Reference.


5.1 DriverEntry Routine Requirements

Each NT driver must have a standard initialization routine in order to be loaded. The NT I/O Manager defines the prototype, as shown previously, for driver initialization routines.

Naming an NT Driver’s Initialization Routine

Each NT driver should have an initialization routine named DriverEntry. Otherwise, the driver writer must define the name of an NT driver’s initialization routine for the linker so that the driver’s transfer address is linked into the OS loader.

Explicitly naming an NT driver’s initialization routine DriverEntry creates this linkage automatically.

Linking an NT Driver to Libraries

Never link an NT driver with the C runtime library, thereby bloating the driver’s load image with a set of functions specific to the Win32® programming environment. NT drivers cannot call user-mode C runtime routines from kernel mode.

NT drivers use the system-supplied support routines declared in ntddk.h, which includes the NT Rtl Xxx (for Runtime library) functions and ntdef.h with the set of kernel-mode C runtime routines that NT drivers can call.

The system-supplied video and SCSI port drivers are implemented as dynamic-link libraries. Kernel-mode SCSI and video miniport drivers link themselves to the NT video or SCSI port driver and call the routines exported by the appropriate port driver. For more information about SCSI and video miniport drivers, see Appendix A and the Graphics Driver Design Guide, respectively.

Discardable and Pageable NT Driver Code

The DriverEntry routine and any other driver routines that run only during initialization should be discarded after initialization to conserve system-space memory.

The following driver code must be resident while an NT driver is processing I/O requests:

  • Any code that is run at IRQL >= DISPATCH_LEVEL

    For a summary of the default IRQLs at which standard NT driver routines run, see Chapter 16.

  • Any code that might be in the system page-fault path, which includes any intermediate or device driver code that handles I/O requests for an underlying mass-storage device

Otherwise, NT driver code can be made pageable to conserve system memory. An NT driver of a device that is not used constantly while the system is running can set up a pageable code section that is locked in system memory only when that driver is called to process I/O requests, as can any NT driver layered over such a device driver. However, a device driver’s pageable code section cannot contain certain standard routines.

For more information about discardable and pageable NT driver code, see the Programmer’s Guide.


5.1.1 Every DriverEntry Routine’s Responsibilites

Every DriverEntry routine is run in the context of a system thread at IRQL PASSIVE_LEVEL.

In general, a DriverEntry routine is responsible for exporting the driver’s other entry points, for initializing the NT objects the driver uses, and for setting up any other system resources that the driver uses. For more information about NT objects, see Chapter 3.

A DriverEntry routine can use the Windows NT® registry to get some of the information it needs to initialize the driver or its device(s). The DriverEntry routine must set information in the registry for other NT drivers and/or protected subsystems to use. For more information about using the registry, see Chapter 16.

In particular, every DriverEntry routine must do the following:

  • Fill in the driver object entry points for the driver’s Dispatch, StartIo (if any), and Unload (if any) routines.

    See Chapter 2 for how to do this. For more information about Dispatch, StartIo, and Unload routines, see Chapters 6, 7, and 15, respectively.

  • Create a named device object to represent each physical, logical, or virtual device for which the driver handles I/O requests.

    See Chapter 3 for how to set up a device object.

  • Claim any necessary hardware resources or find appropriate lower-level drivers in the registry, and set up any necessary symbolic links between the names of the driver’s device objects and the Win32® subsystem names for the same devices. The I/O Manager provides support for setting up these symbolic links.

    See Chapter 16 for more information about using the registry and setting up symbolic links.

  • Register the driver’s other entry points and set up the NT objects necessary for any other standard routines the driver has.

    For routine-specific requirements, see Chapters 7-9, 11, and 14.

  • Create and/or initialize any other NT objects, types, or resources the driver uses, such as the following:

    • If the driver has a device-dedicated thread or waits on any Kernel-defined dispatcher objects, the DriverEntry routine must initialize the dispatcher object(s) that its thread or the driver waits on by calling the appropriate KeInitialize Xxx support routine with a pointer to the event, semaphore, mutex, and/or timer object(s) for which the driver provides the storage.

      Because it executes in a system-thread context, the DriverEntry routine itself can wait for a nonzero interval on a dispatcher object, which must be initialized before the wait begins. For example, class drivers that call down to a corresponding port driver during initialization usually wait on an event object they associate with an IRP, which they allocate with IoBuildSynchronousFsdRequest and pass with IoCallDriver to the port driver for the physical device.

      See Chapter 3 for more information about waiting on dispatcher objects.

    • If the driver uses any executive spin lock(s) or provides the storage for an interrupt spin lock, the DriverEntry routine must call KeInitializeSpinLock with each such spin lock before passing it to any other support routine.

      See Chapter 16 for guidelines on how to use spin locks, and Chapter 8 for more information about whether a device driver must supply storage for an interrupt spin lock.

  • Return NTSTATUS indicating whether the driver can accept and successfully process I/O requests for at least one named device object that the driver created.
Return from DriverEntry

As shown by its declaration, a DriverEntry routine returns an NTSTATUS-type value. The DriverEntry routine should postpone any call to IoRegisterDriverReinitialization until just before it returns STATUS_SUCCESS. It must not make this call unless it will return STATUS_SUCCESS.

If a DriverEntry routine returns something other than STATUS_SUCCESS to indicate that it can successfully process I/O requests for at least one named device object that it created, that driver does not remain loaded.

A DriverEntry routine that will fail initialization must free any NT objects, system resources, and registry resources it has already set up before it returns control. It should reset the driver’s Dispatch entry points in the driver object for IRP_MJ_FLUSH_BUFFERS and/or IRP_MJ_SHUTDOWN to NULL if the driver supports these requests. If it already called IoRegisterShutdownNotification, the DriverEntry routine must call IoUnregisterShutdownNotification before it fails initialization.

If a driver will fail initialization, the DriverEntry routine also should log an error or have IoReportResourceUsage log a hardware resource conflict error on a device driver’s behalf before DriverEntry returns control. For more information about logging I/O and resource-conflict errors, see Chapter 16.


5.1.2 DriverEntry’s Additional Responsibilites

Depending on the position of a particular driver in a chain of layered NT drivers, on the nature of the underlying device, and on the design of the driver, its DriverEntry routine also can be responsible for doing following:

  • In a lowest-level NT driver, the DriverEntry routine initializes the physical device.

  • In a higher-level NT driver, the DriverEntry routine layers the driver over the next-lower-level driver(s), using one of the following techniques:

    • To establish a connection between paired class/port drivers, the class driver calls IoGetDeviceObjectPointer with the name of the port driver’s (primary) device object(s).

      For example, a SCSI class driver calls IoGetDeviceObjectPointer repeatedly, passing an updated Unicode name string \Device\ScsiPort Digit where Digit is a count enumerating the SCSI HBAs in the machine. Then, the class driver uses the returned device object pointers to send get-configuration requests to the NT SCSI port driver, in order to determine which HBAs control a bus with attached devices of the class driver’s type. (For more information about initializing SCSI drivers, see also Appendix A.)

      A successful call to IoGetDeviceObjectPointer returns pointers to both the lower driver’s device object and the file object associated with that device object. A higher-level driver must save the returned device object pointer so it can pass this device object pointer to IoCallDriver when it passes IRPs on for processing by lower drivers.

      Few higher-level NT drivers except FSDs have any use for the returned pointer to the file object. However, a higher-level NT driver should save this returned pointer to the file object if it might break its connection to the lower driver later, for example when being unloaded. To break such a connection, higher-level drivers call ObDereferenceObject to release the file object pointer returned by IoGetDeviceObjectPointer.

      For more information about driver Unload routines, see Chapter 15.

    • To establish a connection between an NT intermediate or highest-level driver and a lower-level driver, the DriverEntry routine creates a device object and calls IoAttachDevice with a pointer to its newly created device object and a pointer to the name of the lower driver’s device object.

      Calling IoAttachDevice successfully allows a higher-level NT driver to intercept requests bound for the lower driver’s device by aliasing the target device object to its own device object. Note that the device object created by the higher-level driver is not required to have a name, because the lower driver’s device object is still the named target for I/O requests.

  • Since the system itself can boot from an NT driver’s floppy or CD-ROM device, the DriverEntry routine of such a driver must create a symbolic link between each possible boot device (the driver’s named device object that represents a floppy or CD-ROM drive) and the corresponding ARC device by calling IoAssignArcName. For more information about the ARC firmware and the corresponding functionality in x86-based Windows NT platforms, see Chapter 16. For disk device drivers, the system sets up these symbolic links automatically.

  • If an NT device or intermediate driver has a device-dedicated thread, its DriverEntry routine can call PsCreateSystemThread with the entry point for the driver’s thread.

    If a highest-level NT driver uses executive worker threads, as many NT file system drivers do, it must have a callback routine of type WORKER_THREAD_ROUTINE, which takes a single input PVOID Parameter.

  • If a driver has an optional Reinitialize routine, its DriverEntry routine calls IoRegisterDriverReinitialization once if and only if the DriverEntry routine will return STATUS_SUCCESS. The driver can call IoRegisterDriverReinitialization as many times as the Reinitialize routine should be run. Usually, a driver with a Reinitialize routine is a higher-level NT driver. For more information about Reinitialize routines, see Section 5.3.
Providing Storage for System Resources

An NT driver must provide storage, usually in the device extension of a device object, for any Kernel-defined objects and executive spin locks it uses. NT drivers also must provide storage for pointers to certain objects obtained from the HAL or I/O Manager, as explained in Chapter 3.

Most device and intermediate drivers provide storage in their device or controller extensions for every NT object and system resource they use to process I/O requests. Their DriverEntry routines initialize certain resources, such as any Kernel-defined object stored in the device (or controller) extension, that the driver uses thereafter.

However, an NT driver designer might decide to allocate additional system-space memory for the driver’s needs, such as for long-term I/O buffers or a zone buffer. If so, the DriverEntry routine can call one (or more) of the following routines:

  • ExAllocatePool for paged or nonpaged system-space memory

  • MmAllocateNonCachedMemory or MmAllocateContiguousMemory for cache-aligned nonpaged system-space memory (used for I/O buffers)

  • HalAllocateCommonBuffer for drivers of devices that use continuous DMA

For more information about using memory, see Chapter 16. For more information about using common buffers for DMA, see the section on adapter objects in Chapter 3.


5.2 DriverEntry Routine Functionality

This section summarizes how a representative NT device driver’s DriverEntry routine works.

The particulars of how any NT device driver uses the registry and initializes its device(s) are device-dependent, as well as dependent on the driver’s designer. The particulars of how any higher-level NT driver uses the registry are somewhat device-type-dependent, as well as dependent on the driver’s designer.

Every DriverEntry routine is called with two input pointers, as shown in the declaration. The input DriverObject pointer allows the DriverEntry routine to set appropriate entry points for its Dispatch, StartIo, and/or Unload routines in the driver object.

Using the Configuration Registry

The input DriverObject pointer also gives the driver access to DriverObject->HardwareDatabase, which points to a counted Unicode string that specified a path to the registry \Registry\Machine\Hardware tree. The RegistryPath input to the DriverEntry routine points to a counted Unicode string that specifies a path to the driver’s registry key \Registry\Machine\System\CurrentControlSet\Services\DriverName.

Every NT driver can get information from the registry in order to initialize successfully. For example, higher-level NT drivers that layer themselves over drivers for a particular kind of underlying device can get information about how many devices of that type are present in any particular Windows NT machine.

The DriverEntry routines of NT device drivers must successfully claim the hardware resources they need to support I/O on their devices in the registry before they attempt to initialize any physical device. If an NT device driver cannot establish a claim in the registry on the I/O ports, device memory ranges, interrupt, and/or DMA channel/port it needs to perform device I/O, that driver cannot remain loaded.

In general, an NT device driver whose support is necessary for the system to boot is likely to use the registry in a slightly different manner than a driver that can be loaded and unloaded on demand. The driver of a device that must be accessible for the system itself to load is more likely to get support from what the system’s hardware-detection code stored in the \Registry\Machine\Hardware\Description tree. The DriverEntry routine of a load-on-demand driver is likely to depend more on user-supplied value entries in the driver’s registry key \Registry\Machine\System\CurrentControlSet\Services\DriverName and/or in its Parameters subkey(s) that were set up when the driver was installed in the system.

For more information about how NT drivers use the registry, see Chapter 16. For more information about the installation of NT drivers, see the Programmer’s Guide.


5.2.1 Device Driver’s Initialization

The DriverEntry routine of an NT device driver should initialize in the following stages:

  1. Allocate memory to hold the hardware configuration information necessary to communicate with its device(s).

  2. Gather hardware configuration information for the physical device(s) from the registry, from the I/O bus(es) in the machine, and/or by interrogating the device on the bus; otherwise, the driver must supply a set of default hardware configuration values for its device(s).

  3. Set the driver’s Dispatch, StartIo (if any), and Unload (if any) entry points in the given driver object.

  4. Attempt to claim the hardware resources that the driver uses to carry out device I/O operations, such as device memory, bus-relative interrupt vector, and so forth, in the registry.

  5. If the driver can claim the hardware resources it needs in the registry, initialize the physical device(s) and set up the NT objects and any other resources, such as spin locks, that the driver will use to process device I/O requests.

  6. If the driver succeeds in initializing its device(s) and setting up all the resources it needs to process IRPs to its device(s), free the memory it allocated to hold configuration information, or possibly call IoRegisterDriverReinitialization passing a Context pointer to the configuration information, and return STATUS_SUCCESS.

    Otherwise, free any objects and system resources it allocated, including the memory it used to hold configuration information, and return an appropriate NTSTATUS error.

Every NT device driver writer should consider the following guidelines when implementing a DriverEntry routine:

  • Never attempt to initialize a physical device without claiming the necessary bus-relative I/O ports, memory ranges, interrupt, and/or DMA channel/port hardware resources in the registry first.

    Every NT device driver must call IoAssignResources, IoReportResourceUsage, or HalAssignSlotResources and attempt to claim the bus-relative hardware resources for a device before the driver attempts to initialize that device. If there is no conflicting claim on the given hardware resources, the driver’s claims are written in the \Registry\Machine\Hardware\ResourceMap tree.

    Claiming hardware resources in the registry prevents resource conflicts between NT drivers and between devices in the machine. In other words, a successful claim on a set of hardware resources prevents a subsequently loaded driver from “stealing” an already loaded driver’s hardware resources and from resetting that driver’s already initialized device.

  • Hardware resource specifications written under the registry \ResourceMap tree are the bus-relative “physical” range, interrupt, and/or DMA values returned by IoQueryDeviceDescription, HalGetBusData, HalGetBusDataByOffset, or similar driver-supplied bus-relative values. They are not the mapped system values returned by HalTranslateBusAddress and/or MmMapIoSpace that a driver can use to communicate with its device, nor the mapped vector/IRQL returned by HalGetInterruptVector that the driver uses to register its ISR with IoConnectInterrupt.

  • An NT device driver’s ISR should be capable of determining whether it has been called with a spurious interrupt even during the driver’s initialization. On return from the DriverEntry’s call to IoConnectInterrupt, the ISR can be called immediately if interrupts are enabled on the device or if the driver shares a vector or IRQL with other devices.

  • Because the DriverEntry routine runs in a system thread context at IRQL PASSIVE_LEVEL, any memory allocated with ExAllocatePool for use exclusively during initialization can be from paged pool, as long as the driver does not control the device that holds the system page file. Such a memory allocation must be released with ExFreePool before DriverEntry returns control unless the driver passes a pointer to this memory in a call to IoRegisterDriverReinitialization, making the driver’s Reinitialize routine responsible for freeing the memory allocation.

  • If the driver has a Reinitialize routine, its DriverEntry routine must not call IoRegisterDriverReinitialization unless it will return STATUS_SUCCESS.


5.2.2 Higher-Level Driver’s Initialization

The DriverEntry routine of a higher-level NT driver should initialize itself in the following stages:

  1. Allocate memory for and gather whatever configuration information it needs from the registry.

    For example, a higher-level driver might search the registry for the named device objects created by already loaded device drivers that represent a particular type of device so the higher-level driver can layer itself over appropriate device drivers or the drivers layered above them.

  2. Set the driver’s Dispatch, StartIo (if any), and Unload (if any) entry points in the given driver object.

  3. Set up the NT objects and any other system resources, such as spin locks, that the driver will use to process I/O requests, and connect or attach its own device object(s) to the next-lower driver’s device object(s) with IoGetDeviceObjectPointer or IoAttachDevice.

  4. Set up value entries in its own keys in the registry, such as the named device object(s) this driver created, so that still higher-level drivers can layer themselves over this driver.

  5. If the driver successfully layered itself over one or more underlying device drivers, free the memory it allocated to hold configuration information from the registry, or possibly call IoRegisterDriverReinitialization passing a Context pointer to the configuration information, and return STATUS_SUCCESS.

    Otherwise, free any objects and system resources it allocated, including the memory it used to hold configuration information, and return an appropriate NTSTATUS error.

Every writer of a higher-level NT driver should consider the following guidelines when implementing a DriverEntry routine:

  • If the driver calls IoGetDeviceObjectPointer successfully, it must save the returned pointer to the next-lower driver’s device object; this pointer is a required parameter to IoCallDriver.

  • A higher-level NT driver should save the file object pointer returned by IoGetDeviceObjectPointer. Such a higher-level driver must call ObDereferenceObject if the underlying driver has an Unload routine, described in Chapter 15, and is later unloaded.

  • If the driver calls IoAttachDevice successfully, it must save the pointer to the next-lower driver’s device object; this pointer is a required parameter to IoCallDriver.

  • To call IoAttachDevice, the driver must create a device object of its own first. If subsequent call(s) to IoAttachDevice do not succeed, the driver must call IoDeleteDevice with the device object(s) that it created.

  • Because the DriverEntry routine runs in a system thread context at IRQL PASSIVE_LEVEL, any memory allocated with ExAllocatePool for use exclusively during initialization can be from paged pool, provided that the underlying device driver does not control the device that holds the system page file. Such a memory allocation must be freed with ExFreePool before DriverEntry returns control unless the driver passes a pointer to this memory in a call to IoRegisterDriverReinitialization, making the driver’s Reinitialize routine responsible for freeing the memory allocation.

  • If the driver has a Reinitialize routine, its DriverEntry routine must not call IoRegisterDriverReinitialization unless it will return STATUS_SUCCESS.


5.3 Reinitialize Routine Requirements and Functionality

Like the DriverEntry routine, a Reinitialize routine is run in a system thread context at IRQL PASSIVE_LEVEL.

If an NT driver cannot fully initialize itself in its DriverEntry routine, it can supply a Reinitialize routine to be called some time after its DriverEntry routine returns control.

For example, the NT fault-tolerant disk driver, ftdisk, is loaded very early in the boot process in case its support is needed to boot the system from a mirrored partition. When the ftdisk driver’s DriverEntry routine is called, it can layer the ftdisk driver only over driver(s) for the device from which the system image is loaded. Consequently, the ftdisk driver has a Reinitialize routine to be called later in the boot process after all other lowest-level mass storage device drivers (and any drivers layered above them but below the ftdisk driver) have initialized and been loaded. This driver’s Reinitialize routine layers the ftdisk driver over the remaining drivers of devices that have mirror sets, stripe sets, or volume sets by attaching ftdisk-created device objects to the lower drivers’ device objects, as mentioned in Section 5.2.2.

If an NT driver has a Reinitialize routine, its DriverEntry routine causes the Reinitialize routine to be run later by calling IoRegisterDriverReinitialization once. The DriverEntry routine is responsible for setting up any context data to be passed to the Reinitialize routine before it calls IoRegisterDriverReinitialization.

If its Reinitialize routine uses the registry, the data at Context might include the RegistryPath pointer that was passed to the DriverEntry routine because this pointer is not an input parameter to the Reinitialize routine, as shown by its declaration. For more information about using RegistryPath, see Chapter 16.

The DriverEntry routine can call IoRegisterDriverReinitialization only once. If the Reinitialize routine should be run again after any other drivers’ Reinitialize routines have returned control, the Reinitialize routine also can call IoRegisterDriverReinitialization as many times as the driver’s Reinitialize routine should be run. The Count passed to the Reinitialize routine indicates how many times this routine has been called, including the current call.

Note also that a driver’s Reinitialize routine should not be registered unless its DriverEntry routine will return STATUS_SUCCESS, indicating that the driver has successfully set up at least one named device object representing an initialized physical device or a logical/virtual device for which it can process I/O requests.

Usually, a driver with a Reinitialize routine is a higher-level NT driver that has already layered itself over at least one underlying device driver in its DriverEntry routine. Such a higher-level driver has already created at least one device object, but might need to create others to layer itself over additional chains of NT drivers for an underlying device of the same type. Consequently, such a driver’s Reinitialize routine initializes in the same basic stages, already described in Section 5.2.2, and has the same basic requirements as its DriverEntry routine, except that it does not return an NTSTATUS value.

If an NT device driver has a Reinitialize routine, it initializes in the same basic stages, already described in Section 5.2.1, and it also has the same basic requirements as its DriverEntry routine.

Note that if a device driver postpones making claims on some hardware resources in the registry to its Reinitialize routine, many other NT device drivers might load in the interval between when the driver returned control from DriverEntry and when its Reinitialize routine is called. These other device drivers will then have already claimed their respective hardware resources in the registry, so such a device driver’s Reinitialize routine might have difficulty claiming all hardware resources that the driver needs to initialize additional physical devices.


Chapter 6 Dispatch Routines

This chapter contains the following information:
6.1 Dispatch Routine Requirements

6.1.1 Commonly Implemented Dispatch Routines

6.1.2 Conditionally Implemented Dispatch Routines

6.2 Basic Dispatch Routine Functionality
6.2.1 When to Check the I/O Stack Location

6.2.2 How to Complete an IRP in a Dispatch Routine

6.2.3 When to Complete an IRP in the Dispatch Routine

6.2.4 How to Pass IRPs with Valid Parameters on from a Dispatch Routine

6.2.5 Allocating IRPs for Lower-Level Drivers

6.2.6 Handling Asynchronous Transfer Requests

6.3 Designing Dispatch Routines
6.3.1 DispatchCreate and DispatchClose Functionality

6.3.1.1 Basic DispatchCreateClose Routines

6.3.1.2 Points to Consider in Implementing DispatchCreateClose

6.3.2 DispatchCleanup Functionality

6.3.3 DispatchRead and/or DispatchWrite Functionality

6.3.3.1 DispatchReadWrite Using Buffered I/O

6.3.3.2 DispatchReadWrite Using Direct I/O

6.3.3.3 DispatchReadWrite in Higher-Level Drivers

6.3.3.4 Points to Consider in Implementing DispatchReadWrite

6.3.4 Dispatch(Internal)DeviceControl Functionality

6.3.4.1 DispatchDeviceControl in Lowest-Level Drivers

6.3.4.2 DispatchDeviceControl in Higher-Level Drivers

6.3.4.3 Dispatch(Internal)DeviceControl in Class/Port Drivers

6.3.4.4 Points to Consider in Implementing Dispatch(Internal)DeviceControl

The Dispatch routine is defined by the I/O Manager as follows:

NTSTATUS
(*PDRIVER_DISPATCH) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

This chapter summarizes the required functionality of NT drivers’ standard Dispatch routines and some guidelines for the most commonly implemented Dispatch routines.


6.1 Dispatch Routine Requirements

Processing any IRP begins in the Dispatch routine that the DriverEntry routine set up to handle the given major function code (IRP_MJ_XXX), which is set in the driver’s I/O stack location of each incoming IRP.

Most NT drivers’ Dispatch routines are called in an arbitrary thread context at IRQL PASSIVE_LEVEL, with the following exceptions:

  • Any highest-level NT driver’s Dispatch routines are called in the context of the thread that originated the I/O request, which is commonly a user-mode application thread.

    In other words, the Dispatch routines of NT file system drivers and other highest-level NT drivers are called in a nonarbitrary thread context at IRQL PASSIVE_LEVEL.

  • The DispatchRead and/or DispatchWrite routines of mass-storage device drivers and of NT intermediate drivers layered above them, can be called at IRQL APC_LEVEL and in an arbitrary thread context.

    The DispatchRead and/or DispatchWrite and any other routine that also processes read and/or write requests in such a device or intermediate driver must be resident at all times. These NT driver routines can neither be pageable nor be part of a driver’s pageable-image section.

    For more information about pageable and pageable-image driver routines, see the Programmer’s Guide.

The driver writer determines how many Dispatch routines any NT driver has. An NT driver can have as many Dispatch routines as the IRP_MJ_XXX function codes the driver handles, as already mentioned in Chapter 4.


6.1.1 Commonly Implemented Dispatch Routines

In general, most NT drivers must handle some or all of the following requests:

  • IRP_MJ_CREATE indicates either that a user-mode protected subsystem, possibly on behalf of an application or subsystem-specific driver, has requested a handle for the file object associated with the target device object, or that a higher-level NT driver is connecting or attaching its device object to the target device object.

  • IRP_MJ_CLEANUP indicates that the handle for the file object, representing the target device object, is being closed, so any IRPs currently queued to the target device for the given file object, found in the driver’s I/O stack location of the cleanup IRP, should be cancelled.

  • IRP_MJ_CLOSE indicates that the handle of the file object that represents the target device object or a pointer to the target device object has been closed and that there are no outstanding references to the file object pointer.

  • IRP_MJ_READ indicates an I/O request to transfer data from the underlying physical device to the system.

  • IRP_MJ_WRITE indicates an I/O request to transfer data from the system to the underlying physical device.

  • IRP_MJ_DEVICE_CONTROL indicates a request with a system-defined, device-type-specific I/O control code specifying a particular device operation is being sent to a device driver.

    For more information about the system-defined (sometimes called public) I/O control codes that NT device drivers must support, see the Kernel-mode Driver Reference. Note that higher-level NT drivers also must support IRP_MJ_DEVICE_CONTROL to pass these IRPs on to their underlying device drivers.

  • IRP_MJ_INTERNAL_DEVICE_CONTROL indicates a request sent to the device driver, in most cases from a closely coupled higher-level driver, usually with a privately defined, driver-specific and device-type-specific or device-specific I/O control code requesting a device-type-specific or device-specific operation.

    Only certain kinds of NT drivers are required to handle system-defined internal device I/O control requests, including certain SCSI drivers, keyboard or mouse device drivers, and parallel drivers that interoperate with system-supplied drivers.

The Dispatch routine entry points that any NT driver must set in its driver object vary according to the type and functionality of the underlying physical device. For device-type-specific information about IRP major function codes that NT device drivers must handle, see the Kernel-Mode Driver Reference. For information about SCSI and video miniport drivers, see Appendix A and the Kernel-Mode Graphics Driver Guide, respectively. For more information about other types of SCSI drivers, see also Appendix A.


6.1.2 Conditionally Implemented Dispatch Routines

Other requests that NT device and intermediate drivers might handle include the following:

  • IRP_MJ_QUERY_INFORMATION and IRP_MJ_SET_INFORMATION for certain highest-level NT drivers

    Such a request indicates that a user-mode application, component, or driver has requested information about the length of the file object (representing the driver’s device object) for which the user-mode requestor has a handle, or that the user-mode requestor is attempting to set an end-of-file on that file object.

    NT parallel class and serial device drivers handle these requests by setting the FILE_STANDARD_INFORMATION or FILE_POSITION_INFORMATION length or position to zero. Other highest-level NT device drivers should support these requests, particularly if a user-mode application or user-mode driver might call C runtime functions to manipulate the file object. NT file system drivers must support these requests more fully than these highest-level NT device drivers.

  • IRP_MJ_FLUSH_BUFFERS for any NT driver that caches data in a device or buffers data internally in driver-allocated memory

    Receipt of this request indicates that the driver should write its buffered data or flush the cached data out to the device, or should discard buffered or cached data that was read from the device.

    For example, the system keyboard and mouse class drivers, which have internal ring buffers for input data from their devices, support the flush request. Drivers of mass-storage devices and drivers layered above them also support this request.

  • IRP_MJ_SHUTDOWN for any driver that should be called before the system itself is shut down

    If the underlying device is not a mass-storage device, an NT driver can set a DispatchShutdown entry point in the driver object when it initializes at the discretion of the driver designer. For such a driver, its DriverEntry routine must call IoRegisterShutdownNotification with pointer(s) to the device object(s) for which its DispatchShutdown routine will do whatever driver-determined cleanup is necessary before the system is shut down.

NT drivers for mass-storage devices and intermediate drivers layered over them can rely on a highest-level NT file system driver to send them shutdown IRPs when the system is about to shut down. That is, the FSD is responsible for making sure that any cached file data is written out to peripheral devices, calling underlying drivers to flush data from their device caches or buffers (if any), and so forth before the system is shut down.

The driver of a mass-storage device that caches data internally must set the entry points in the driver object for its DispatchShutdown and DispatchFlush routines when it initializes. If such a driver buffers data in memory but its device has no internal cache, it also must set DispatchShutdown and DispatchFlush entry points in its driver object when it initializes.

Any intermediate driver layered above a driver that handles IRP_MJ_FLUSH and IRP_MJ_SHUTDOWN requests also must set its DispatchShutdown and DispatchFlush entry points in its driver object when it initializes.


6.2 Basic Dispatch Routine Functionality

The required functionality of a particular Dispatch routine varies, depending on the IRP_MJ_XXX it handles, on the individual driver’s level in a chain of NT drivers, and on the type of underlying physical device.

Basically, most Dispatch routines process incoming IRPs as follows:

  1. Check the driver’s I/O stack location in the IRP to determine what to do and check the parameters, if any, for validity.

  2. Satisfy the request and complete the IRP if possible; otherwise, pass it on for further processing by lower-level drivers or by other device driver routines.

Whether an NT driver must check its I/O stack location to determine what to do and to check parameters depends on the given IRP_MJ_XXX, as well as on whether that driver set up a separate Dispatch routine for each IRP_MJ_XXX that the driver handles.

Whether an NT driver must pass on an IRP for further processing depends on the validity of the parameters, if any, as well as on the IRP_MJ_XXX and on the driver’s level, if any, in a chain of layered drivers.


6.2.1 When to Check the I/O Stack Location

An NT driver’s Dispatch routine must check its own I/O stack location of the IRP to determine what to do if any of the following conditions hold:

  • The Dispatch routine handles more than one IRP_MJ_XXX.

  • The Dispatch routine must handle a set of minor function codes for certain major function codes: that is, IRP_MN_XXX, as the NT SCSI port driver and NT file system drivers do.

  • The Dispatch routine of a device driver or of a closely coupled higher-level driver handles IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL requests, which have an associated set of device-type-specific, or possibly privately defined and device-specific, I/O control codes.

To determine which operation is requested and what parameters to use, if the particular IRP_MJ_XXX has any, the Dispatch routine calls IoGetCurrentIrpStackLocation to get a pointer to its own I/O stack location in the input IRP.

Higher-level NT drivers’ Dispatch routines always call IoGetCurrentIrpStackLocation and also call IoGetNextIrpStackLocation to get a pointer to the next-lower driver’s I/O stack location in the IRPs that they set up for the next-lower driver, as mentioned in Chapter 4.

The Dispatch(Internal)DeviceControl routine of a device driver, or possibly of its closely coupled class driver(s), must determine which I/O control code is set in the driver’s I/O stack location at Parameters.DeviceIoControl.IoControlCode for each request. Such a driver’s Dispatch(Internal)DeviceControl routine frequently features a switch statement, as described later in Section 6.3.4.

In most cases, the Dispatch(Internal)DeviceControl routine of a higher-level NT driver simply passes an IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL request on to the next-lower driver, after setting up its stack location in the IRP. However, NT SCSI class drivers must check for certain IOCTL_SCSI_XXX control codes in order to set up the SCSI port driver’s I/O stack location correctly before they pass on these requests. For more information about these requirements for SCSI class drivers, see Appendix A.


6.2.2 How to Complete an IRP in a Dispatch Routine

If an input IRP can be completed immediately, a Dispatch routine does the following:

  1. Sets the Status and Information members of the IRP’s I/O status block with appropriate values, in general:

    • The Dispatch routine sets Status either to STATUS_SUCCESS or to an appropriate error STATUS_XXX, which can be the value returned by a call to a support routine or, for certain synchronous requests, by a lower driver.

      If a lower-level driver returns STATUS_PENDING, a higher-level driver should not call IoCompleteRequest with an IRP unless it calls IoMarkIrpPending with that IRP first. However, a higher-level driver’s Dispatch routine is unlikely to complete any IRP for which lower driver(s) return STATUS_PENDING.

    • It sets Information to the number of bytes successfully transferred if a request to transfer data, such as a read or write request, was satisfied.

    • It sets Information to a value that varies according to the specific request for other IRPs that it completes with STATUS_SUCCESS.

    • It sets Information to a value that varies according to the specific request for IRPs that it completes with a warning STATUS_XXX. For example, it would set Information to the number of bytes transferred for such a warning as STATUS_BUFFER_OVERFLOW.

    • Usually, it sets Information to zero for requests that it completes with an error STATUS_XXX.

  2. Calls IoCompleteRequest with the IRP and the PriorityBoost IO_NO_INCREMENT.

  3. Returns the appropriate STATUS_XXX that it already set in the I/O status block. Note that a call to IoCompleteRequest makes the given IRP inaccessible by the caller, so the return value from a Dispatch routine cannot be set from the I/O status block of an already completed IRP.

    NT driver writers should follow this implementation guideline for calling IoCompleteRequest with an IRP:

    Always release any spin lock(s) the driver is holding before calling IoCompleteRequest.

It takes an indeterminate amount of time to complete an IRP, particularly in a chain of layered NT drivers. Moreover, a deadlock can occur if a higher-level driver’s IoCompletion routine sends an IRP back down to a lower driver that is holding a spin lock.


6.2.3 When to Complete an IRP in the Dispatch Routine

Usually, NT drivers do not complete IRPs in their Dispatch routines unless the parameters for the given request are invalid or, in a device driver, unless the particular IRP_MJ_XXX requires no device I/O operation(s).

A higher-level NT driver should complete an IRP in its Dispatch routine(s) or pass it on for processing by lower drivers according to the following guidelines:

  • If the Dispatch routine determines that any parameters in its own I/O stack location are invalid, it should complete that IRP immediately with an appropriate error STATUS_XXX, such as STATUS_INVALID_PARAMETER.

  • If the IRP contains the function code IRP_MJ_CLEANUP, the DispatchCleanup routine must complete every IRP currently queued to the target device object for the file object that is specified in the driver’s I/O stack location of the cleanup IRP, and complete the cleanup IRP as described later in Section 6.3.2.

    A cleanup request indicates that an application is being terminated or has closed a file handle for the file object that represents the driver’s device object. When the DispatchCleanup routine returns, usually the driver’s DispatchClose routine is called next.

  • Otherwise, a higher-level driver can satisfy the request only by passing it on to the next-lower driver(s).

Note that every NT driver in a chain of layered drivers can check the validity of parameters in its own I/O stack location of each IRP input to the driver’s Dispatch routines. In such a chain, each driver’s Dispatch routine can complete only those IRPs for which it can determine the validity of parameters in its own I/O stack location. Completing IRPs with invalid parameters in the Dispatch routine of the highest possible driver improves I/O throughput for any chain of NT drivers and for the system overall.

A lowest-level NT driver should complete an IRP in its Dispatch routine(s) according to the following guidelines:

  • If the Dispatch routine determines that any parameters in its own I/O stack location are invalid, it should complete that IRP immediatedly with an appropriate error STATUS_XXX.

    Usually, any higher-level driver has already checked the parameters for a requested operation, but device drivers can perform their own “sanity checks” on parameters as well. However, device drivers do not check the parameters of IRP_MJ_INTERNAL_DEVICE_CONTROL requests because a closely coupled higher-level driver is responsible for setting up the parameters for these requests.

  • If the IRP contains the function code IRP_MJ_CLEANUP, the DispatchCleanup routine must complete every IRP currently queued to the target device object for the given file object in the driver’s I/O stack location of the cleanup IRP and then complete the cleanup IRP as described later in Section 6.3.2.

    A cleanup request indicates that an application is being terminated or has closed a file handle for the file object that represents the driver’s device object. When the DispatchCleanup routine returns, usually the driver’s DispatchClose routine is called next. For more information about handling cancelable IRPs, see also Chapter 12.

  • If the given request requires no device I/O operation, the Dispatch routine should satisfy the given request and complete the IRP.

    For example, a driver might save the current mode of its device in the device extension, particularly if it seldom changes device modes after initialization. Its DispatchDeviceControl routine could then satisfy a request that queried the current device mode by returning this stored information.

Otherwise, the Dispatch routine must call IoMarkIrpPending with the IRP, queue the IRP to other driver routines for further processing, and return STATUS_PENDING.


6.2.4 How to Pass IRPs with Valid Parameters on from a Dispatch Routine

When a Dispatch routine has checked its own I/O stack location in an input IRP and determined that any parameters are valid, it must pass the IRP on for further processing by lower-level drivers or by other device driver routines if it cannot satisfy and complete the given request in the Dispatch routine itself.

    A higher-level NT driver should pass such a request on to a next-lower driver as follows:

    1. Call IoGetCurrentIrpStackLocation with the input IRP, if the Dispatch routine has not already done so, to get a pointer for its own I/O stack location in the IRP.

    In most circumstances, a higher-level driver’s Dispatch routine has already made this call. However, a Dispatch routine devoted exclusively to handling a particular IRP_MJ_XXX with parameters that the driver cannot validate or with no parameters might call IoGetCurrentIrpStackLocation.

    2. Call IoGetNextIrpStackLocation if the driver will pass the input IRP on to the next lower-level driver. If it allocates additional IRP(s) for one or more lower drivers, the Dispatch routine makes either of the following sets of calls with each of the IRPs it allocates:

    • IoGetNextIrpStackLocation to get a pointer to the next-lower driver’s I/O stack location if the driver allocated no stack location of its own in the new IRP

    • IoSetNextIrpStackLocation, followed by IoGetCurrentIrpStackLocation, to get a pointer to its own stack location in a new IRP, where it can set up whatever context its IoCompletion routine needs; then, IoGetNextIrpStackLocation to get a pointer to the next-lower driver’s I/O stack location

    3. Set up the next-lower driver(s)’ I/O stack location, usually by copying the contents of its own I/O stack location in the original IRP to the next-lower driver’s. However, the Dispatch routine can modify some of the parameters in the next-lower driver’s I/O stack location for certain requests.

    For example, a higher-level driver might modify the parameters for a large transfer request when the underlying device has a known limit in transfer capacity, and reuse the IRP to send partial-transfer requests to the underlying device driver.

    4. Call IoSetCompletionRoutine with each IRP that the Dispatch routine allocated so the driver’s IoCompletion routine releases each such IRP when lower drivers have completed it.

    The Dispatch routine also can call IoSetCompletionRoutine with an input IRP in which it has set up the next-lower driver’s I/O stack location. Then, its IoCompletion routine can check on how lower drivers completed the request, reuse the IRP for partial transfers, update whatever state the driver maintains if it tracks IRPs, possibly retry a request returned with an error, and so forth.

    For more information about IoCompletion routines, see Chapter 13.

    5. Call IoCallDriver with each IRP to be processed by lower driver(s).

    6. Return an appropriate NTSTATUS value, such as:

    • Usually, STATUS_PENDING if the input IRP is an asynchronous request, such as IRP_MJ_READ or IRP_MJ_WRITE

    • Frequently, the result of the call to IoCallDriver if the input IRP is a synchronous request, such as IRP_MJ_CREATE or IRP_MJ_DEVICE_CONTROL

    An NT device driver passes any IRP that it cannot complete in its Dispatch routine on to other driver routines as follows:

    1. Call IoMarkIrpPending with the input IRP.

    2. Call IoStartPacket to pass on or queue the IRP to the driver’s StartIo routine, unless the driver manages its own internal IRP queueing. See Chapter 7 for more information about driver-managed queues.

    If the driver does not have a StartIo routine but handles cancelable IRPs, it must call IoSetCancelRoutine with the entry point for a Cancel routine and the IRP before queueing the IRP for further processing. See Chapter 12 for more information about Cancel routines.

    3. Return STATUS_PENDING.


6.2.5 Allocating IRPs for Lower-Level Drivers

An NT intermediate-level driver cannot allocate IRPs for lower-level drivers by calling IoMakeAssociatedIrp. Only highest-level NT drivers can call IoMakeAssociatedIrp to allocate IRPs and set them up for a chain of lower drivers. The I/O Manager automatically completes the original IRP when all its associated IRPs have been completed, as long as the driver does not call IoSetCompletionRoutine with the original IRP or with any of the associated IRPs it allocates.

If an intermediate driver creates IRPs for lower drivers, it should call IoAllocateIrp, IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, or IoBuildAsynchronousFsdRequest. However, IoBuildSynchronousFsdRequest can be called only in the following circumstances:

  • By a driver-created thread to build IRPs for read or write requests, because such a thread can wait in a nonarbitrary thread context (its own) on a dispatcher object, such as a driver-initialized Event passed to IoBuildSynchronousFsdRequest

  • In the system thread context during initialization or while unloading

  • To build IRPs for inherently synchronous operations, such as create, flush, shutdown, close, and device control requests

    However, a driver is more likely to call IoBuildDeviceIoControlRequest to allocate device control IRPs than IoBuildSynchronousFsdRequest.


6.2.6 Handling Asynchronous Transfer Requests

Except for highest-level drivers, all NT drivers handle IRP_MJ_READ and IRP_MJ_WRITE requests asynchronously. Even a highest-level NT driver cannot wait in its Dispatch routine for lower-level drivers to finish processing an asynchronous read or write request. It must pass such a request on to lower drivers and return STATUS_PENDING from its DispatchRead and/or DispatchWrite routine(s).

Similarly, an NT device driver must pass such a transfer request on to other driver routines that process device I/O requests and return STATUS_PENDING from its DispatchRead and/or DispatchWrite routine(s) or from its DispatchReadWrite routine.

As already mentioned in Section 6.2.4, a higher-level NT driver sometimes must set up partial-transfer IRPs and pass them on to lower drivers. Such a higher-level driver can complete the original read/write IRP only when its partial-transfer requests have been completed by the lower driver(s).

For example, an NT SCSI class driver’s DispatchReadWrite routine is required to split large transfer requests that exceed the underlying HBA’s transfer capabilites into a set of partial-transfer requests. The class driver must set up the parameters in its partial-transfer IRPs so that the NT SCSI port/miniport drivers can satisfy each partial-transfer request in a single DMA operation.

Other NT device drivers that use DMA or PIO also might need to split up large transfer requests for themselves.

For more information about using DMA and PIO, see Chapters 3 and 16. For more information about NT SCSI class driver requirements, see also Appendix A.


6.3 Designing Dispatch Routines

This section describes some of the most commonly implemented Dispatch routines in NT device and intermediate drivers more fully, including the following:

  • DispatchCreate and DispatchClose

  • DispatchCleanup

  • DispatchRead and DispatchWrite

  • DispatchDeviceControl and DispatchInternalDeviceControl

The following summarizes design and implementation considerations for these Dispatch routines.


6.3.1 DispatchCreate and DispatchClose Functionality

A create request can originate either in a user-mode subsystem’s attempt to get a handle for the file object representing the device (possibly on behalf of an application or subsystem-level driver) or in a higher-level driver’s call to IoGetDeviceObjectPointer or IoAttachDevice.

The reciprocal close request can originate in a user-mode subsystem’s close of the file object handle associated with the driver’s device object or in a higher-level driver’s call to IoDetachDevice.

Each of these requests is inherently synchronous.

An NT driver’s Dispatch routine(s) for IRP_MJ_CREATE and IRP_MJ_CLOSE requests might do nothing more than complete the input IRP with STATUS_SUCCESS, as already mentioned in Chapter 4.

Another NT driver’s Dispatch routine(s) for IRP_MJ_CREATE and IRP_MJ_CLOSE requests might do more work, depending on the underlying device driver or on the underlying device. Consider the following scenarios:

  • On receipt of a create request, a class driver might initialize an internal queue and send an IRP_MJ_INTERNAL_DEVICE_CONTROL request down to the corresponding port driver requesting device configuration information or exclusive access to a controller port.

  • On receipt of a close request, a highest-level driver might need to delay until it has completed device I/O processing for all outstanding IRPs currently queued to the target device object.

  • On receipt of a create request, an NT driver of an infrequently used device might call MmLockPagableImageSection to make resident some of the driver routines that process other IRP_MJ_XXX requests. On receipt of a reciprocal close request, the driver might call MmUnlockPagableImageSection to conserve system memory by having its pageable-image section paged out when all file object handles for such a driver’s device object(s) are closed.

    For more information about discardable, pageable, and pageable-image driver code, see the Programmer’s Guide. For more information about the preceding support routines, see the Kernel-mode Driver Reference.

Although many NT drivers have a single Dispatch routine for create and close requests, any NT driver writer can implement separate DispatchCreate and DispatchClose routines. Some NT drivers handle IRP_MJ_CLOSE requests only for symmetry because, after their device objects have been opened by a protected subsystem or higher-level NT driver, the lower-level drivers’ device objects are not closed until the system itself is shut down.

In general, a DispatchClose routine is not called if it is implemented in any lower-level NT driver for an underlying physical device that must be available for the system to continue running.

For example, keyboard and mouse drivers set up device objects representing physical devices that must be functional while the system is running, so these drivers might have minimalist DispatchClose routines for symmetry or combined DispatchCreateClose routines.

As another example, some of the the system disk drivers have no DispatchClose routine, but these drivers usually have DispatchFlush and DispatchShutdown routines to complete any outstanding file I/O operations before the system is shut down.


6.3.1.1 Basic DispatchCreateClose Routines

Many NT drivers merely need to establish their existence on receipt of a create request and merely need to acknowledge the receipt of a close request, particularly lower-level drivers in a chain of layered NT drivers.

For example, a port driver of a device controller with one or more closely coupled class drivers that call IoGetDeviceObjectPointer might have a minimalist DispatchCreateClose routine. Such a port driver’s DispatchCreateClose routine might do nothing more than complete the IRP as follows:
    :	:
{
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

This minimalist DispatchCreateClose routine sets the Information member of the I/O status block to zero indicating the file object is opened for a create request; Information has no meaning for a close request. It sets the Status member with and returns STATUS_SUCCESS, which indicates that it is ready to accept I/O requests for the target device object from the higher-level class driver that caused the create IRP to be sent to the port driver’s DispatchCreateClose routine when that class driver called IoGetDeviceObjectPointer.

This minimalist DispatchCreateClose routine completes the create IRP without boosting the priority of the originator of the IRP (IO_NO_INCREMENT) because the originator of the request is assumed to wait for an indeterminate but very small interval on the completion of this request.

How much work a DispatchCreateClose routine does depends partly on the nature of the driver’s device or the underlying device and partly on the design of the driver(s). At the discretion of the driver designer, an NT driver can handle these requests in separate DispatchCreate and DispatchClose routines, particularly when the driver performs quite different operations for each request.

For a create request to open a file object that represents a logical or physical device, a highest-level NT driver should do the following:

  1. Call IoGetCurrentIrpStackLocation to get a pointer to its I/O stack location in the IRP.

  2. Check the FileObject.FileName in the I/O stack location and complete the IRP with STATUS_SUCCESS if the Unicode string at FileName has a zero length; otherwise, complete the IRP with STATUS_INVALID_PARAMETER.

Following the preceding steps ensures that no attempt to open a pseudofile on a device can cause problems later. For example, this prevents attempts to open a nonexistent \\device\parallel0\temp.dat.


6.3.1.2 Points to Consider in Implementing DispatchCreateClose

Keep the following points in mind when implementing a DispatchCreateClose routine:

  • At a minimum, these Dispatch routines must do the following:

    1. Set the Status field of the input IRP’s I/O status block with an appropriate NTSTATUS, usually STATUS_SUCCESS.

    2. Set the Information field of the input IRP’s I/O status block to zero.

    3. Call IoCompleteRequest with the IRP and a PriorityBoost of IO_NO_INCREMENT.

    4. Return the NTSTATUS that it set in the Status field of the IRP’s I/O status block.

  • In a highest-level or intermediate NT driver, this routine might need to do additional work to process a create and/or close request, depending on the nature of its device or of the underlying device, and on the design of the driver.

  • For a create request to open a file object that represents a logical or physical device, a highest-level driver should check the FileObject.FileName in the I/O stack location and complete the IRP with STATUS_SUCCESS if the Unicode string at FileName has a zero length. Otherwise, it should complete the IRP with STATUS_INVALID_PARAMETER.

  • The DispatchCreateClose routine(s) of lowest-level NT drivers are called only during the system initialization process when the next-higher-level driver calls IoGetDeviceObjectPointer, IoAttachDevice, or IoDetachDevice. Such a lowest-level driver in a chain of layered drivers frequently does only the minimum required processing of a create or close request.


6.3.2 DispatchCleanup Functionality

Any NT driver in which IRPs can be held for an indefinite interval should have a DispatchCleanup routine. If IRPs might be queued when a user-mode handle for a file object that represents the underlying physical or logical device is closed, the highest-level driver holding IRPs bound for that device must have a DispatchCleanup routine.

An NT driver’s DispatchCleanup routine is called to cancel all IRPs that are currently queued to the target device object for the file object specified in the driver’s I/O stack location of the cleanup IRP.

In general, a DispatchCleanup routine must do the following:

  • Reset the driver’s Cancel entry point, if any, to NULL in every IRP currently in the device queue (or in the driver’s internal queue of IRPs) for the target device object and given file object with IoSetCancelRoutine.

  • Cancel every IRP currently in the queue for the target device object and the file object specified in the driver’s I/O stack location of the cleanup IRP.

  • Complete the input cleanup IRP and return STATUS_SUCCESS.

The DispatchCleanup routine must set up the I/O status block as follows in each IRP queued to the target device object for the given file object, before it completes each IRP to be cancelled:
Status | set to STATUS_CANCELLED
Information | set to zero

The DispatchCleanup routine specifies IO_NO_INCREMENT when it calls IoCompleteRequest with each cancelled IRP. After it has cancelled all queued IRPs for the given file object, the DispatchCleanup routine completes the cleanup request itself with STATUS_SUCCESS and a PriorityBoost of IO_NO_INCREMENT.

If a driver manages its own internal queue of IRPs, rather than using the device queue associated with its device object, the driver should set up an interlocked queue that is protected with an executive spin lock. Otherwise, another driver routine (running on another processor in an SMP machine) might dequeue an IRP that should be cancelled by the DispatchCleanup routine. To remove an IRP from such a queue and cancel it, the DispatchCleanup routine calls an ExInterlocked..List support routine with a driver-supplied executive spin lock.

Any area in the device extension of the target device object that is shared between the DispatchCleanup routine and another driver routine also must be protected by a spin lock.

Any NT driver must synchronize its access to cancelable IRPs with the I/O Manager and with its own Cancel routine(s), if any.

Consider the following guidelines for managing spin lock usage within a DispatchCleanup routine that must synchronize with one or more Cancel routines:

  • If the DispatchCleanup routine must acquire both the system cancel spin lock and one or more of the driver’s own spin locks, it should call IoAcquireCancelSpinLock before it acquires its own spin lock(s).

    Such a DispatchCleanup routine must release any nested spin lock acquisitions in inverse order: that is, it must release its own spin lock(s) before it calls IoReleaseCancelSpinLock.

  • If a driver uses the I/O-manager-supplied device queue in the device object, then the DispatchCleanup routine must call IoAcquireCancelSpinLock before it calls IoSetCancelRoutine to reset the Cancel entry point in an IRP to NULL.

    If a driver manages its own queue(s) of IRPs, then the DispatchCleanup routine does not need to acquire the cancel spin lock before calling IoSetCancelRoutine.

  • The DispatchCleanup routine must release every spin lock it is holding before it calls IoCompleteRequest with an IRP, including each IRP that it cancels.

For more information about Cancel routines, see also Chapter 12.

Keep the following points in mind when implementing a DispatchCleanup routine:

  • This routine must walk the queue of IRPs for the target device object and cancel every outstanding request for the given file object before it completes the input cleanup IRP, which is sent when the handle for the file object that represents the device in user mode is closed.

  • The queue should be protected by a spin lock to prevent IRPs that should be cancelled from being dequeued by any other driver routine. The device queue associated with a device object comes with a spin lock. If a driver sets up its own queue for IRPs, it should set up an interlocked queue.

  • This routine must acquire the system cancel spin lock with IoAcquireCancelSpinLock before it calls IoSetCancelRoutine to reset its Cancel entry point to NULL in each queued IRP if the driver has a Cancel routine. The exception to this rule is if the driver manages its own queue(s) of IRPs and synchronizes access to them with driver-supplied spin locks; in that case, the driver does not need to acquire the cancel spin lock before calling IoSetCancelRoutine.

  • This routine must set each queued IRP’s I/O status block with Status set to STATUS_CANCELLED and Information set to zero before it completes each such IRP.

  • This routine must release all spin locks it is holding before calling IoCompleteRequest with an IRP.

  • This routine should set the PriorityBoost to IO_NO_INCREMENT when it completes each cancelled IRP and the cleanup IRP.


6.3.3 DispatchRead and/or DispatchWrite Functionality

Every driver of a device from which data can be transferred to the system must have a DispatchRead routine and must set its entry point in the driver object during initialization. Every driver of a device to which data can be transferred from the system must have a DispatchWrite routine and must set its entry point in the driver object during initialization. Any device driver that transfers data in both directions can have a combined DispatchReadWrite routine, as can any higher-level driver layered above it.

Any higher-level driver layered above such a device driver must set its Dispatch entry point(s) in its driver object for IRP_MJ_READ and/or IRP_MJ_WRITE requests that are handled by the underlying device driver.

As already mentioned in Section 6.2.6, lower-level NT drivers handle IRP_MJ_READ and IRP_MJ_WRITE requests asynchonously. Therefore, DispatchRead and/or DispatchWrite routines must pass these requests on for further processing, provided that any such request has valid parameters in that driver’s I/O stack location of the IRP.

Whether an NT driver sets up its device objects for buffered or direct I/O affects how it handles transfer requests, as already described in Chapter 3. In particular, a device driver that uses direct I/O to do DMA operations might need to split up large transfer requests into a sequence of smaller transfer operations in order to satisfy an IRP_MJ_READ or IRP_MJ_WRITE request.

The following subsections discuss some of the design and implementation considerations for DispatchReadWrite routines in device drivers that use buffered I/O and direct I/O, as well as in higher-level drivers layered above them. Section 6.3.3.4 summarizes points to consider in implementing a DispatchRead, DispatchWrite, or combined DispatchReadWrite routine.


6.3.3.1 DispatchReadWrite Using Buffered I/O

Any NT device driver that sets up its device objects for buffered I/O satisfies a read request by returning data transferred from its device into a locked down system-space buffer at Irp->AssociatedIrp.SystemBuffer. It satisfies a write request by transferring data from the same buffer out to its device.

Consequently, the DispatchReadWrite routine of such a device driver usually does the following on receipt of a transfer request:

  1. Calls IoGetCurrentIrpStackLocation and determines the direction of the transfer request if necessary.

  2. Checks the the validity of the parameters for the request.

    • For a read request, such a driver usually checks the driver’s IoStackLocation->Parameters.Read.Length value to determine whether the buffer is large enough to contain some discrete amount of data transferred from its device.

      For example, the system keyboard class driver processes read requests that come only from the Win32® user input thread. This driver defines a structure, KEYBOARD_INPUT_DATA, in which to store keystrokes from the device and, at any given moment, holds some number of these structures in an internal ring buffer in order to satisfy read requests as they come in.

    • For a write request, such a driver usually checks the value at Parameters.Write.Length, and checks the data at Irp->AssociatedIrp.SystemBuffer for validity if necessary: that is, if its device accepts only structured data packets containing members with defined value ranges.

  3. If any parameters are invalid, the DispatchReadWrite routine completes the IRP immediately, as already described in Section 6.2.2. Otherwise, the DispatchReadWrite passes the IRP on for further processing by other driver routines, as described in Section 6.2.4.

NT device drivers that use buffered I/O usually must satisfy a transfer request with some discrete amount of data that the originator of the request can use or set up. Such a driver is likely to define a structure for data coming in from or being sent to its device and is likely to buffer structured data internally as the system keyboard class driver does.

Drivers that buffer data internally should support IRP_MJ_FLUSH_BUFFERS requests, and can also support IRP_MJ_SHUTDOWN requests (see Section 6.1.2).

It is usually the responsibility of the highest-level driver in a chain to check the input IRP’s parameters before passing a read/write request on to lower drivers. Consequently, many lower-level NT drivers can assume that their I/O stack locations in a read/write IRP have valid parameters. The lowest-level driver in a chain is required to check the validity of the parameters in its I/O stack location if its device places constraints on data transfers known only to that device driver.


6.3.3.2 DispatchReadWrite Using Direct I/O

Any NT device driver that sets up its device objects for direct I/O satisfies a read request by returning data transferred from its device to system physical memory, which is described by the MDL at Irp->MdlAddress. It satisfies a write request by transferring data from system physical memory out to its device.

As already mentioned in Section 6.2.6, lower-level NT drivers must handle read/write requests asynchronously. Every NT device driver’s DispatchReadWrite routine must pass IRP_MJ_READ and IRP_MJ_WRITE IRPs with valid parameters on to other driver routines, as already described in Section 6.2.4.

For all read/write IRPs sent to lower-level NT drivers, the paged physical memory described by the MDL at Irp->MdlAddress has already been probed for the correct access rights to carry out the requested transfer and has already been locked down by the highest-level NT driver in the chain or by the I/O Manager. Any intermediate or lowest-level NT driver that sets up its device objects for direct I/O should not call MmProbeAndLockPages because this has already been done. Such a lowest-level driver calls MmGetSystemAddressForMdl to get mapped system virtual addresses if its device uses PIO to transfer data.

Any intermediate or lowest-level device driver’s DispatchReadWrite routine should perform sanity checks on the parameters in its I/O stack location of read/write IRPs if it cannot trust a higher-level driver to pass down only IRPs with valid parameters. If such a driver’s DispatchReadWrite routine finds a parameter error in an incoming transfer IRP, it should complete the IRP with an appropriate error STATUS_XXX value as already described in Section 6.2.2. Otherwise, such an intermediate driver’s DispatchReadWrite routine must pass the request on for further processing, according to the guidelines in Section 6.3.3.3.

Any device driver’s DispatchReadWrite routine must call IoMarkIrpPending with the transfer request, pass the IRP on for further processing by other driver routines, and return STATUS_PENDING, as described in Section 6.2.4. Note that a device driver’s DispatchReadWrite routine can control the order in which IRPs are queued to its device for faster I/O throughput by calling IoStartPacket with a driver-determined Key value. Another routine in the driver dequeues the IRP later, determines whether the requested length must be split into partial-transfer operations, and programs the device to transfer data.

In general, an NT device driver that must split up large transfer requests to suit the limitations of its device should postpone these operations until just before setting up the device for a given transfer request. Such a device driver’s DispatchReadWrite routine should not check the I/O stack location of incoming IRPs for any device-specific transfer constraints, nor attempt to calculate partial-transfer ranges, when the driver can postpone these checks until just before its StartIo (or other driver routine) programs the device for a transfer operation.


6.3.3.3 DispatchReadWrite in Higher-Level Drivers

NT device drivers’ DispatchReadWrite routines queue transfer IRPs with valid parameters on to other driver routines for further processing on the device and return STATUS_PENDING.

Except for NT file systems, a higher-level driver layered above such a device driver usually does not have any internal driver queues for IRPs. Such a driver’s DispatchReadWrite routine can pass transfer IRPs with valid parameters on to lower drivers possibly after setting up its IoCompletion routine, as already described in Section 6.2.4.

However, an NT SCSI class driver’s DispatchReadWrite routine is responsible for splitting up large transfer requests, if necessary, before it sends an IRP with the major function code IRP_MJ_READ or IRP_MJ_WRITE to the SCSI port/miniport driver pair. For more information about requirements for SCSI class drivers and about how to split up a large transfer request, see Appendix A, and see also the section on adapter objects in Chapter 3.

If a higher-level driver allocates one or more IRPs, which it sets up for the next-lower driver(s) in its DispatchReadWrite routine, to request some number of partial transfers, the DispatchReadWrite routine must call IoSetCompletionRoutine with each driver-allocated IRP. Such a driver must register its IoCompletion routine to track how much data is transferred in each partial-transfer operation so that the IoCompletion routine can release all driver-allocated IRPs and, eventually, complete the original request.

If the underlying driver controls a removable-media device, any IRPs allocated by the higher-level driver must have a thread context. To set up such a thread context, the allocating driver must set the Irp->Tail.Overlay.Thread in each newly allocated IRP from the same value in the incoming transfer IRP.

If the underlying device driver returns an IRP for a partial transfer with an error, the IoCompletion routine can either retry the partial-transfer request or complete the original IRP with its I/O status block set with the returned error, after freeing any IRPs and memory the higher-level driver has allocated.

If a higher-level NT driver’s DispatchReadWrite routine allocates memory for partial-transfer operations and its allocation will be accessed by the driver’s IoCompletion routine (or by the underlying device driver), the DispatchReadWrite routine must allocate that memory from nonpaged pool.


6.3.3.4 Points to Consider in Implementing DispatchReadWrite

Keep the following points in mind when implementing a DispatchRead, DispatchWrite, or DispatchReadWrite routine:

  • It is the responsiblity of the highest-level driver in a chain of layered drivers to check the parameters of incoming read/write IRPs for validity before setting up the next-lower-level driver’s I/O stack location in an IRP.

  • Intermediate and lowest-level drivers generally can rely on the highest-level driver in their chain to pass down transfer requests with valid parameters. However, any driver can perform sanity checks on the parameters in its I/O stack location of an IRP, and each device driver should check the parameters for conditions that might violate any restrictions imposed by its device.

  • If a DispatchReadWrite routine completes an IRP with an error, it should set the I/O stack location Status member with an appropriate NTSTATUS-type value, set the Information member to zero, and call IoCompleteRequest with the IRP and a PriorityBoost of IO_NO_INCREMENT.

  • If a driver uses buffered I/O, it might need to define a structure to contain data to be transferred and might need to buffer some number of these structures internally.

  • If a driver uses direct I/O, it might need to check whether the MDL at Irp->MdlAddress describes a buffer containing too much data (or too many page breaks) for the underlying device to handle in a single transfer operation. If so, the driver must split up the original transfer request into a sequence of smaller transfer operations.

    A closely coupled class driver might split up such a request in its DispatchReadWrite routine for its underlying port driver. NT SCSI class drivers, particularly for mass-storage devices, are required to do this. For more information about requirements for SCSI drivers, see Appendix A.

  • A device driver’s DispatchReadWrite routine should postpone splitting a large transfer request into partial transfers until another driver routine dequeues the IRP to set up the device for the transfer.

  • If a device driver queues a read/write IRP for further processing by its own routines, it must call IoMarkIrpPending before it queues the IRP. The DispatchReadWrite routine also must return control with STATUS_PENDING in these circumstances.

  • If the DispatchReadWrite routine passes an IRP on to lower drivers, it must set up the I/O stack location for the next-lower driver in the IRP. Whether the higher-level driver also sets an IoCompletion routine in the IRP before passing it on with IoCallDriver depends on the design of the driver and of those layered under it.

    However, a higher-level driver must call IoSetCompletionRoutine before it calls IoCallDriver if it allocates any resources, such as IRPs or memory. Its IoCompletion routine must free any driver-allocated resources when lower drivers have completed the request but before the IoCompletion routine calls IoCompleteRequest with the original IRP.

  • If a higher-level driver allocates IRPs for lower drivers that might include an underlying removable-media device driver, the allocating driver must establish the thread context in each IRP it allocates.


6.3.4 Dispatch(Internal)DeviceControl Functionality

For every common type of peripheral device, the system defines a set of I/O control codes for IRP_MJ_DEVICE_CONTROL requests that new drivers for each type of device must support. In most cases, these public I/O control codes for a given type of device are not exported to user-mode applications.

Some of these system-defined I/O control codes are used by higher-level NT drivers that create IRPs with IoBuildDeviceIoControlRequest for the underlying device driver. Others are used by Win32 drivers or particular Win32 components to communicate with an underlying device driver by calling the Win32 function DeviceIoControl. In turn, this Win32 function calls an NT system service, and the I/O Manager sets up an IRP with the major function code IRP_MJ_DEVICE_CONTROL and the given I/O control code in the I/O stack location at Parameters.DeviceIoControl.IoControlCode. Then, the I/O Manager calls the DispatchDeviceControl routine of the highest-level driver in the chain with the IRP.

For certain system-supplied drivers designed to interoperate with and support new NT drivers, Windows NT® also defines a set of I/O control codes for IRP_MJ_INTERNAL_DEVICE_CONTROL requests. In most cases, these public I/O control codes allow add-on higher-level NT drivers to interoperate with an underlying device driver.

For example, the IOCTL_SCSI_GET_INQUIRY_DATA and IOCTL_SCSI_GET_CAPABILITIES codes support the initialization and configuration of NT SCSI class drivers. SCSI class drivers send these device control requests down to the NT SCSI port driver to find SCSI peripheral devices of each class driver’s type and, for those SCSI buses with a connected peripheral of the class driver’s type, to collect relevant information about the underlying SCSI miniport’s HBA, such as its maximum transfer capacity and maximum number of page breaks per DMA transfer operation.

As another example, the system parallel port driver supports a set of public I/O control codes that add-on parallel class drivers set up in IRP_MJ_INTERNAL_DEVICE_CONTROL requests to communicate with their devices through a parallel port. The system parallel port driver can be replaced, as long as such a new NT parallel port driver continues to support the same set of internal device control requests for use by existing and new NT parallel class drivers.

Almost all system-defined I/O control codes are defined to use buffered I/O, because this type of request seldom requires the transfer of large amounts of data. That is, even drivers that set up their device objects for direct I/O are sent IRPs for device control requests with data to be transferred in(to) the buffer at Irp->AssociatedIrp.SystemBuffer, except for certain types of highest-level device drivers with closely coupled Win32 multimedia drivers.

NT driver writers can define a set of private I/O control codes that their written-together drivers can use to communicate with each other. New public I/O control codes can be added to the system only with the cooperation of Microsoft Corporation, because public I/O control codes are built into Windows NT itself.

For specific information about the set of public I/O control codes that different kinds of NT drivers must support and about defining private I/O control codes, see the Kernel-mode Driver Reference.


6.3.4.1 DispatchDeviceControl in Lowest-Level Drivers

An IRP_MJ_DEVICE_CONTROL request for a lowest-level NT driver either requires the driver to change the state of its device or to provide information about the state of its device. Because most kinds of NT drivers are required to handle a number of I/O control codes, their DispatchDeviceControl routines usually contain a switch statement somewhat like the following:
     :      :
switch(irpSp->Parameters.DeviceIoControl.IoControlCode) {
    case IOCTL_DeviceType_XXX:
    case IOCTL_DeviceType_YYY:
        if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
           (sizeof(IOCTL_XXXYYY_STRUCTURE))) {
             status = STATUS_BUFFER_TOO_SMALL;
             break;
        } else {
             IoMarkIrpPending(Irp);
      :       : // pass IRP on for further processing
    case ...
      :      :

As this code fragment shows, a DispatchDeviceControl routine also checks parameters, sometimes on each I/O control code that the driver must support, sometimes on groups of these IOCTL_XXX.

Consider these implementation guidelines for device drivers’ DispatchDeviceControl routines:

  • As for any IRP and driver Dispatch routine, DispatchDeviceControl must check the parameters for validity and complete IRPs with parameter errors immediately, as described in Section 6.2.3.

  • Switching first on any I/O control codes for which the DispatchDeviceControl routine can satisfy and complete the IRP improves performance because the driver can return control faster.

    For better performance, every NT device driver should satisfy any device control request that it can, without queueing the IRP to other driver routines, in its DispatchDeviceControl routine.

  • Switching later on I/O control codes that specify infrequently requested operations also can improve the driver’s performance in processing IRP_MJ_DEVICE_CONTROL_REQUESTS.

  • Grouping cases of I/O control codes as possible when testing for valid parameters is economical both in term of driver performance/size and in code maintenance. As the preceding code fragment suggests, I/O control codes that use a common structure are natural candidates for such a case group.

For those IRPs that the DispatchDeviceControl routine can complete, it should call IoCompleteRequest with a PriorityBoost of IO_NO_INCREMENT. The DispatchDeviceControl routine must call IoMarkIrpPending with each request that it queues for further processing on the device and it must return STATUS_PENDING.


6.3.4.2 DispatchDeviceControl in Higher-Level Drivers

Usually, the DispatchDeviceControl routine of a higher-level NT driver simply sets up the I/O stack location in the IRP for the next-lower-level driver and passes it on with IoCallDriver. Such a DispatchDeviceControl routine seldom checks the validity of parameters in the input IRP because the underlying device driver is assumed to have better information about how to handle each device-type-specific I/O control request on its own device.

A possible exception to this general rule is the DispatchDeviceControl routine in the class driver of a class/port driver pair. For more information about handling device control requests in paired class/port drivers, see Section 6.3.4.3.

Any new higher-level NT driver that is not closely associated with a particular device driver should simply set up the I/O stack location for the next-lower-level driver and pass the IRP_MJ_DEVICE_CONTROL request on for further processing.

A device control request is usually handled synchronously. That is, a higher-level driver’s DispatchDeviceControl routine can frequently return control to the system as follows:

        :     :
    return IoCallDriver(DeviceObject->NextDeviceObject, Irp);

However, a higher-level driver cannot use the preceding technique if a lower driver might return STATUS_PENDING for such a request. In these circumstances, the higher-level driver should set its IoCompletion routine in the IRP. When its IoCompletion routine is called with the IRP, it can check the I/O status block to determine whether the IRP is still pending. If so, the IoCompletion routine might retry the request or, possibly, call IoMarkIrpPending with the IRP before it calls IoCompleteRequest and returns STATUS_PENDING. A higher-level driver must not complete an IRP with STATUS_PENDING unless it has called IoMarkIrpPending with that IRP first.

If the underlying device driver must process much data transferred from the device before it completes the request, then a higher-level driver might handle such a device control request asynchronously. That is, the higher-level driver might set its IoCompletion routine in the IRP, pass the IRP on to lower drivers, and return control from its own DispatchDeviceControl routine.

Almost all system-defined I/O control codes require the underlying device driver to transfer very modest amounts of data, usually far less than a PAGE_SIZE amount. As a general rule, higher-level NT drivers should handle these requests synchronously, as shown in the preceding code fragment, because the lower drivers return control so quickly. That is, the overhead of calling the higher-level driver’s IoCompletion routine does not compensate for whatever additional IRP processing that driver can get done in such a short interval.

A higher-level NT driver that allocates IRPs with IoBuildDeviceIoControlRequest for an underlying device driver can handle these device control requests synchronously. Such a higher-level driver can wait on an optional Event, passed to IoBuildDeviceIoControlRequest and associated with the driver-allocated IRP. Such a driver must supply storage for the event object and must initialize its Event with KeInitializeEvent before calling IoBuildDeviceIoControlRequest with that Event pointer.


6.3.4.3 Dispatch(Internal)DeviceControl in Class/Port Drivers

The higher-level driver of a class/port pair can sometimes complete IRPs in its DispatchDeviceControl routine. For example, if a class driver gathers and stores information about the features of the underlying device during initialization, which might be sought in a subsequent IRP_MJ_DEVICE_CONTROL request, that class driver might satisfy such a request without passing it on to the underlying device driver to save processing time. Depending on the designer, a class driver also might check the IRP’s parameters for its paired port driver so that it could send down only requests with valid parameters.

Closely coupled class/port drivers also can define a set of driver- and/or device-specific I/O control codes that the class driver uses to set up IRPs for IRP_MJ_INTERNAL_DEVICE_CONTROL requests to the port driver.

For example, the system keyboard and mouse class drivers send system-defined internal device control requests to enable/disable keyboard/mouse interrupts to the underlying port driver(s) from each class driver’s DispatchCreateClose routine. These system class drivers handle IRP_MJ_DEVICE_CONTROL requests by setting up IRP_MJ_INTERNAL_DEVICE_CONTROL requests for an underlying port driver. Any new keyboard or mouse port driver that interoperates with these system class drivers also must support these public internal device control requests.

The system parallel class/port driver model has similar features. New parallel class drivers can get support from the system parallel port driver by setting up IRPs for IRP_MJ_INTERNAL_DEVICE_CONTROL requests with public IOCTL_PARALLEL_PORT_XXX control codes. NT driver writers can replace the system parallel port driver, but such a new driver also must support this set of public internal device control requests.

For more information about these public internal device control requests and about how to define private I/O control codes for new class/port drivers, see the Kernel-mode Driver Reference.

For a closely coupled pair of port/class drivers, the class driver might handle the processing of certain device control requests without passing them on to the port driver. The designer of a new class/port driver pair can implement the class driver’s DispatchDeviceControl routine to do either of the following:

  • Check the validity of the parameters in its own I/O stack location in the IRP, set the I/O status block if it finds any parameter errors, and call IoCompleteRequest with the IRP and a PriorityBoost of IO_NO_INCREMENT; otherwise, call IoGetNextIrpStackLocation, copy its own I/O stack location into the port driver’s, and pass the IRP on with IoCallDriver.

  • Or, do nothing more than set up the port driver’s I/O stack location in the IRP without checking parameters and pass it on to the port driver for processing.

NT SCSI class drivers have special requirements for handling device control requests. For more information about these requirements, see Appendix A.


6.3.4.4 Points to Consider in Implementing Dispatch(Internal)DeviceControl

Keep the following points in mind when implementing a new DispatchDeviceControl or DispatchInternalDeviceControl routine:

  • At a minimum, a higher-level driver must copy the parameters for an IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL request from its own I/O stack location in the IRP to the next-lower-level driver’s I/O stack location. Then, it must call IoCallDriver with a pointer to the next-lower driver’s device object and the IRP.

    Such a higher-level driver should propagate the status value returned by IoCallDriver or set in the returned IRP’s I/O status block when it returns control for a request that lower drivers handle synchronously.

  • The underlying device driver must process device control requests unless it has a closely coupled class driver that completes a subset of these requests on its behalf. A device driver’s DispatchDeviceControl routine usually begins processing these requests by switching on the Parameters.DeviceIoControl.IoControlCode in its I/O stack location of each IRP.

  • A device driver should check the parameters passed in with the request and fail the IRP with an appropriate error if any parameter is invalid. The most common check on the validity of parameters to these requests has the form:

        if (Irp->Parameters.DeviceIoControl.InputBufferLength <
                (sizeof(IOCTL_SPECIFIC_STRUCTURE))) {
        status = STATUS_XXX;

    or

        if (Irp->Parameters.DeviceIoControl.OutputBufferLength <
                (sizeof(IOCTL_SPECIFIC_STRUCTURE))) {
        status = STATUS_XXX;

    where the status value set is one of STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER.

  • Every device driver’s DispatchDeviceControl or DispatchInternalDeviceControl routine must handle the receipt of an unrecognized I/O control code by setting the I/O status block with an appropriate NTSTATUS value, setting its Information field to zero, and completing the IRP with a PriorityBoost of IO_NO_INCREMENT.

  • The particular I/O control codes a device driver handles must include any device-type-specific, system-defined I/O control codes for the same type of device. See the Kernel-mode Driver Reference for more information about the system requirements for each type of device and the corresponding (SDK) header files, each beginning with the prefix ntdd, for declarations of the system-defined structures for these I/O control codes.

  • At the discretion of the driver designer, the class driver of a closely coupled class/port driver pair can process and complete a subset of device control requests without passing them on to the underlying port driver. However, such a class driver must pass on all valid device control requests that require a change of state for the device and those that require the return of volatile information about the device, such as its current baud rate, volume, or video mode.


Chapter 7 StartIo and Queue Management Routines

This chapter contains the following information:
7.1 Queueing IRPs
7.2 StartIo Routine Requirements
7.2.1 StartIo Routines in NT Device Drivers

7.2.2 StartIo Routine Functionality

7.2.2.1 Device Driver’s StartIo Routine Using Buffered I/O

7.2.2.2 Device Driver’s StartIo Routine Using Direct I/O

7.2.2.3 Handling Device Control Requests in StartIo

7.2.3 Programming the Device for I/O Operations

7.2.4 StartIo Routines in Higher-Level NT Drivers

7.2.5 Points to Consider in Implementing StartIo

7.3 Requirements for Using Interlocked Queues or Device Queues
7.3.1 Storage Requirements for Driver-Managed Queues

7.3.2 Setting Up Driver-Managed Queues

7.3.3 Queueing IRPs in Driver-Managed Queues

7.3.4 Managing Interlocked Queues with a Driver-Created Thread

7.3.5 Managing Supplemental Device Queues

The StartIo routine is defined by the I/O Manager as follows:

VOID
(*PDRIVER_STARTIO) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
     );

This chapter summarizes the required functionality of NT drivers’ standard StartIo routines. For NT driver designers who need to set up internal queues for IRPs in their drivers, it also summarizes the support Windows NT® provides for doing this and discusses some issues in managing driver-created internal queues.


7.1 Queueing IRPs

Because the NT I/O Manager supports asynchronous I/O within a multitasking and multithreaded system, I/O requests to a device can come in faster than its driver can process them to completion, particularly in Windows NT multiprocessor machines. Consequently, IRPs bound to any particular device must be queued in the driver when its device is already busy processing another IRP.

In other words, lowest-level NT drivers either must have a StartIo routine or must hold IRPs queued for further processing by their other driver routines whenever IRPs come in faster than such a device driver can satisfy them.

Only an NT device driver that can satisfy and complete every possible IRP in its Dispatch routines needs no StartIo routine and/or no driver-managed queue(s) for IRPs.

Higher-level NT drivers almost never have StartIo routines. Most NT intermediate drivers also do not have internal queues because such a driver can usually pass IRPs with valid parameters on from its Dispatch routines and do whatever postprocessing is required for any IRP in its IoCompletion routine(s).

This section describes, in general, some of the design considerations for determining whether to implement a StartIo routine with or without internal, driver-managed queues for IRPs.

StartIo Routines in NT Drivers

Most NT device drivers have StartIo routines because most PC peripheral devices are capable of handling only one device I/O operation at a time. For such a device driver with a StartIo routine, the NT I/O Manager provides IoStartPacket and IoStartNextPacket to manage the queueing of IRPs in the driver, as shown in Chapter 4.

For more information about StartIo routines in device drivers, see Section 7.2.

Higher-level NT drivers usually do not have StartIo routines. For more information about the tradeoffs of implementing StartIo routines in higher-level NT drivers, see Section 7.2.4.

Internal Queues for IRPs in NT Drivers

If an underlying device can support more than one concurrent I/O operation, an NT device driver must set up internal request queues and manage its own queueing of IRPs. For example, the system serial driver maintains separate queues for read, write, purge, and wait operations on its device(s) because it supports full-duplex serial device(s).

A higher-level NT driver that sends requests to some number of underlying device drivers also might maintain internal queues of IRPs. For example, NT file system drivers almost always have internal queues for IRPs.

For more information about managing internal queues, see Section 7.3.

Internal Queues with StartIo Routines in NT Drivers

An NT driver that manages its own internal queues can have a StartIo routine at the discretion of the driver designer. Most NT device drivers either have a StartIo routine or manage their own queueing of IRPs.

An exception to this is the NT SCSI port driver, which both has a StartIo routine and manages internal queues of IRPs. IRPs are queued to its StartIo routine in the device queue associated with the driver-created device object that represents a SCSI HBA. This driver also sets up separate device queue objects for and manages the queueing of IRPs to each target device (corresponding to a SCSI logical unit) on any HBA-driven SCSI bus in the machine.

The NT SCSI port driver uses its supplemental device queues to hold IRPs sent down from the NT SCSI class drivers in LU-specific queues whenever any device on a SCSI bus is particularly busy. In effect, this driver’s supplemental, LU-specific device queues allow the NT SCSI port driver to serialize operations for heterogeneous SCSI devices through an HBA while keeping each device on that HBA’s SCSI bus(es) as busy as possible.

Internal Queues and Driver Threads

NT drivers with device-dedicated threads and highest-level NT drivers that use executive worker threads, including most NT file system drivers, usually set up an interlocked queue for IRPs in the device extensions of their device objects. Such a queue is shared by the driver thread or driver-supplied worker thread callback and by other driver routines that process IRPs.

Such a driver’s Dispatch routines insert IRPs into the interlocked queue and a driver-created thread or the driver’s worker-thread callback removes them by calling the ExInterlocked..List support routines.

For example, the system floppy controller driver, as mentioned in Chapter 2, uses such an interlocked queue. Its device-dedicated thread handles the same processing of IRPs that is done by other NT device drivers’ StartIo routines and some of the same processing of IRPs that is done by other NT device drivers’ DpcForIsr routines.


7.2 StartIo Routine Requirements

A StartIo routine is run in an arbitrary thread context at IRQL DISPATCH_LEVEL.

Running at IRQL DISPATCH_LEVEL restricts the set of support routines a StartIo routine can call. For more information about managing IRQLs, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-mode Driver Reference.


7.2.1 StartIo Routines in NT Device Drivers

As its name suggests, a StartIo routine in an NT device driver is responsible for starting an I/O operation on the physical device.

When an NT device driver’s StartIo routine is called, it can assume that the target device represented by the input device object is not busy. Either one of that device driver’s Dispatch routines has just called IoStartPacket and the IRP was not inserted into the device queue associated with the target device object, or the driver’s DpcForIsr routine is completing another request and has just called IoStartNextPacket.

Before the StartIo routine in a highest-level NT device driver is called, that driver’s Dispatch routine must have probed and locked down the user buffer, if necessary, to set up valid mapped buffer addresses in the IRP queued to its StartIo routine. Such a highest-level device driver that sets up its device objects for direct I/O (or for neither buffered nor direct I/O) cannot defer locking down a user buffer to the driver’s StartIo routine because every StartIo routine is called in an arbitrary thread context at IRQL DISPATCH_LEVEL.

In other words, NT driver designers should keep in mind the following fact:

Any buffer memory to be accessed by an NT driver’s StartIo routine must be locked down or allocated from resident, system-space memory and must be accessible in an arbitrary thread context.

NT drivers that set up their device objects for buffered I/O can rely on the I/O Manager to pass valid buffers in all IRPs sent to such a driver. Lower-level NT drivers that set up device objects for direct I/O can rely on the highest-level NT driver in their chain to pass valid buffers in all IRPs sent through any intermediate drivers to the underlying device driver.

In general, any NT device driver’s StartIo routine is responsible for calling IoGetCurrentIrpStackLocation with the input IRP and then doing whatever request-specific processing is necessary to start the I/O operation on its device, which can include the following:

  • Setting up or updating any state information about the current request that the driver maintains in the device extension of the target device object or elsewhere in nonpaged pool allocated by the driver

    For example, if a device driver maintains an InterruptExpected Boolean about the current transfer operation, its StartIo routine might set this variable to TRUE. If the driver maintains a time-out counter for the current operation, its StartIo routine might set up this value, or the StartIo routine might queue the driver’s CustomTimerDpc routine.

    Note that if this state information or any other resource is shared with other driver routines, the state area or resource must be protected by a spin lock. If the StartIo routine shares state or resources with other nonISR routines, it must acquire the spin lock that protects the state or resource before accessing it. If the StartIo routine shares state with the driver’s ISR, StartIo must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine that sets up the state.

    For more information about CustomTimerDpc routines, see Chapter 14. For more information about the ISR and SynchCritSection routines, see Chapters 8 and 10, respectively. For more information about using spin locks, see Chapter 16.

  • Assigning a sequence number to the IRP in case the driver must log a device I/O error while processing the IRP

    For more information about logging I/O errors, see Chapter 16.

  • If necessary, translating the parameters in the driver’s I/O stack location into device-specific values

    For example, a disk driver might need to calculate the starting sector and/or byte offset to the physical disk address for a transfer operation, and whether the requested length of the transfer will cross a particular sector boundary or exceed the transfer capacity of its physical device.

  • If the driver controls a removable-media device, checking for media changes before programming the device for I/O and notifying its overlying file system if the media has changed

    For more information about handling removable media, see Chapter 16.

  • If the device uses DMA, checking whether the requested Length (number of bytes to be transferred, found in the driver’s I/O stack location of the IRP) should be split into partial-transfer operations, as explained in Chapter 3, assuming a closely coupled higher-level driver does not pre-split large transfers for the device driver

    The StartIo routine of such a device driver also can be responsible for calling KeFlushIoBuffers and, if the driver uses packet-based DMA, for calling IoAllocateAdapterChannel with the driver’s AdapterControl routine.

    For more information about AdapterControl routines, see Chapter 11. For more information about maintaining cache coherency during DMA operations, see also Chapter 16.

  • If the device uses PIO, mapping the base virtual address of the buffer, described in the IRP at Irp->MdlAddress, to a system-space address with MmGetSystemAddressForMdl, as explained in Chapter 3

    For read requests, the device driver’s StartIo routine can be responsible for calling KeFlushIoBuffers before PIO operations begin. For more information about maintaining cache coherency during PIO, see also Chapter 16.

  • If the driver uses a controller object, calling IoAllocateController with its ControllerControl routine

    For more information about ControllerControl routines, see Chapter 11.

  • If the driver handles cancelable IRPs, checking whether the input IRP has already been cancelled

  • If an input IRP can be cancelled before it is processed to completion, the StartIo routine must call IoSetCancelRoutine with the IRP and the entry point of the driver’s Cancel routine. The StartIo routine must acquire the cancel spin lock for its call to IoSetCancelRoutine.

    For more information about Cancel routines, see Chapter 12.


7.2.2 StartIo Routine Functionality

IRPs coming in to an NT device driver’s Dispatch routines do not necessarily require the driver to do I/O processing on its device. Usually, each NT driver’s Dispatch routine(s) completes a subset of required requests, as well as those IRPs that have parameter errors in the driver’s I/O stack location, as described in Chapter 6.

However, every NT device driver with a StartIo routine is likely to call IoStartPacket from its Dispatch routines for IRP_MJ_READ and/or IRP_MJ_WRITE requests, and usually for a subset of the I/O control codes it supports for IRP_MJ_DEVICE_CONTROL requests.

A StartIo routine is the second stage in satisfying a device I/O request. When a Dispatch routine calls IoStartPacket with such an IRP, the I/O Manager either passes the IRP directly to the StartIo routine for further processing or inserts the packet into the device queue that was associated with the target device object when it was created.

As a general rule, a device driver that uses buffered I/O has a simpler StartIo routine than one that uses direct I/O. NT device drivers that use buffered I/O transfer small amounts of data for each transfer request, while those that use direct I/O (whether DMA or PIO) transfer large amounts of data to or from locked-down buffers that can span physical page boundaries in system memory.

Higher-level NT drivers layered above NT device drivers usually set up their device objects to match those of their respective device drivers. However, a highest-level NT driver, particularly NT file system drivers, can set up device objects for neither direct nor buffered I/O. For more information about setting up device objects for buffered and direct I/O, see Chapter 3.


7.2.2.1 Device Driver’s StartIo Routine Using Buffered I/O

Assuming its DispatchRead, DispatchWrite, or DispatchDeviceControl routine determined that a request was valid and called IoStartPacket, a device driver’s StartIo routine is called to process the packet immediately if the device is not already busy. If the packet is queued by IoStartPacket, eventually a call to IoStartNextPacket from the driver’s DpcForIsr or CustomDpc routine causes the StartIo routine to be called with the dequeued IRP.

The StartIo routine calls IoGetCurrentIrpStackLocation and determines which operation must be performed to satisfy the request. It preprocesses the IRP in any way necessary before programming the physical device to carry out the I/O request.

The StartIo routine must call a device driver’s SynchCritSection routine to perform the necessary device programming if access to the physical device (and/or the device extension) must be synchronized with that driver’s ISR. For more information, see Section 7.2.3.

An NT device driver that uses buffered I/O either transfers data into (reads) or from (writes) a system-space buffer, allocated by the I/O Manager, that the driver finds in each IRP at Irp->AssociatedIrp.SystemBuffer.


7.2.2.2 Device Driver’s StartIo Routine Using Direct I/O

Assuming its DispatchRead, DispatchWrite, or DispatchDeviceControl routine determined that a request was valid and called IoStartPacket, a device driver’s StartIo routine is called to process the packet next. If the packet is queued by IoStartPacket, eventually a call to IoStartNextPacket from the driver’s DpcForIsr or CustomDpc routine causes the StartIo routine to be called with the dequeued IRP.

The StartIo routine calls IoGetCurrentIrpStackLocation and determines which operation must be performed to satisfy the request. It preprocesses the IRP in any way necessary, such as splitting up a large DMA transfer request into partial-transfer ranges and saving state about the Length of an incoming transfer request that must be split, before programming the physical device to carry out the I/O request.

The StartIo routine must use a driver-supplied SynchCritSection routine to perform the necessary programming if access to the physical device (and/or the device extension) must be synchronized with the driver’s ISR. For more information, see Section 7.2.3.

Any NT device driver that uses direct I/O either reads data into or writes data from a locked-down buffer, described by a memory descriptor list (MDL), that the driver finds in the IRP at Irp->MdlAddress. Such a driver commonly uses buffered I/O for device control requests. For more information, see Section 7.2.2.3.

As a type defined by, used exclusively by, and changeable by the NT Memory Manager, all MDLs are opaque to NT drivers. NT driver writers should consider the following an implementation guideline:

NT drivers cannot use the address mappings contained in any MDL, including an MDL at Irp->MdlAddress, directly.

Instead, NT device drivers that use PIO remap user-space buffers by calling MmGetSystemAddressForMdl with the Irp->MdlAddress. NT device drivers that use DMA also pass Irp->MdlAddress to support routines during their transfer operations to have the buffer addresses remapped to logical ranges for their devices.

Unless a closely coupled higher-level driver pre-splits large DMA transfer requests for the underlying device driver, an NT device driver’s StartIo routine must split up each transfer request that is larger than its device can manage in a single transfer operation. NT device drivers that use system DMA are required to split transfer requests that are too large for the system DMA controller or for their device(s) to handle in a single transfer operation.

If the device is a slave DMA device, its driver must synchronize transfers through a system DMA controller with a driver-allocated adapter object, representing the DMA channel, and a driver-supplied AdapterControl routine. The driver of a busmaster DMA device also must use a driver-allocated adapter object to synchronize its transfers and must supply an AdapterControl routine if it uses the system’s packet-based DMA support.

Depending on an NT device driver’s design, it might synchronize transfer and device control operations on a physical device with a controller object and supply a ControllerControl routine.

For more information about DMA transfers, adapter objects, and controller objects, see Chapter 3. For more information about driver-supplied AdapterControl and ControllerControl routines, see also Chapter 11.


7.2.2.3 Handling Device Control Requests in StartIo

In general, only a subset of device I/O control requests are passed on from the DispatchDevice(Internal)Control routine for further processing by a device driver’s StartIo routine. That is, any device driver’s StartIo routine must handle only valid device control requests that require device state changes or return volatile information about the current state of the device.

Each new NT device driver must support the same set of public I/O control codes as all other NT drivers for the same kind of device. The system defines public, device-type-specific I/O control codes for IRP_MJ_DEVICE_CONTROL requests as buffered requests.

Consequently, NT device drivers make data transfers to or from a system-space buffer that each driver finds in the IRP at Irp->AssociatedIrp.SystemBuffer for device control requests. Even NT drivers that set up their device objects for direct I/O use buffered I/O to satisfy device control requests with public I/O control codes.

The definition of each I/O control code determines whether data transferred for that request is buffered. Any privately defined I/O control codes for driver-specific IRP_MJ_INTERNAL_DEVICE_CONTROL requests between paired NT drivers can define a code with method buffered, method direct, or method neither. As a general rule, any privately defined I/O control code should be defined with method neither if a closely coupled higher-level NT driver must allocate a buffer for that request.

For more information about the I/O control codes that a new driver for any common type of PC device must support and about defining private I/O control codes, see the Kernel-mode Driver Reference.


7.2.3 Programming the Device for I/O Operations

Usually, the StartIo routine in an NT device driver must synchronize access to any memory or device registers it shares with the driver’s ISR by calling KeSynchronizeExecution with a driver-supplied SynchCritSection routine.

In other words, a device driver’s StartIo routine generally causes the SynchCritSection routine to actually program the physical device for I/O at DIRQL. For more information about SynchCritSection routines, see Chapter 10.

The StartIo routine must do any preprocessing necessary for the request, such as calculating an initial partial-transfer range and saving any state information about the original request for other driver routines, before it calls KeSynchronizeExecution with a SynchCritSection routine that programs the device.

If a device driver uses DMA, its StartIo routine usually calls IoAllocateAdapterChannel with a driver-supplied AdapterControl routine. In these circumstances, the StartIo routine postpones the responsibility for programming the physical device to the AdapterControl routine. It, in turn, can call KeSynchronizeExecution to have a driver-supplied SynchCritSection routine program the device for a DMA transfer.

For more information about how driver routines share resources with an ISR or share resources among routines other than the ISR in a multiprocessor-safe way, see the section on using spin locks in Chapter 16. For more information about using DMA, see Chapter 3 or, for AdapterControl routines, see Chapter 11.


7.2.4 StartIo Routines in Higher-Level NT Drivers

Any higher-level NT driver can have a StartIo routine at the driver writer’s discretion. However, such a driver is unlikely to be interoperable with existing lower-level drivers and is likely to exhibit poor performance characteristics.

A StartIo routine in a higher-level NT driver has the following effects:

  • Incoming IRPs can be queued by calling IoStartPacket from the driver’s Dispatch routine(s) and IoStartNextPacket from its IoCompletion routine(s), thereby causing IRPs to be processed one at a time through the StartIo routine.

  • The driver’s I/O throughput could become appreciably slower during periods of heavy I/O demand, because its StartIo routine can become a bottleneck.

  • Such a driver’s StartIo routine calls IoCallDriver with each IRP at IRQL DISPATCH_LEVEL, thereby causing all lower-level drivers’ Dispatch routines also to run at IRQL DISPATCH_LEVEL. This restricts the set of support routines that lower drivers can call in their Dispatch routines. Because most NT driver writers assume their drivers’ Dispatch routines run at IRQL < DISPATCH_LEVEL, such a higher-level driver is unlikely to be interoperable with many existing lower-level drivers.

  • Such a StartIo routine reduces overall system throughput because it and the Dispatch routines of all lower-level drivers in its chain are run at raised IRQL.

    For more information about the IRQLs at which standard driver routines are run, see Chapter 16. For specific information about any particular standard routine, see the pertinent chapter among Chapters 5 through 15.

None of the system’s higher-level NT drivers has a StartIo routine, because it can slow IRP processing for the driver itself, for all drivers above and below it, and for the system overall.

Most higher-level NT drivers simply send IRPs to the underlying device driver(s) from their Dispatch routines, as described in Chapter 6, and do any necessary clean-up processing in their IoCompletion routines. For more information about IoCompletion routines, see Chapter 13.

However, higher-level NT drivers can set up internal queues for IRPs that request particular kinds of operations or set up internal queues to hold IRPs bound for a set of heterogeneous underlying devices like the NT SCSI port driver, as mentioned in Section 7.1.


7.2.5 Points to Consider in Implementing StartIo

Keep the following points in mind when implementing a StartIo routine:

  • A StartIo routine must synchronize its access to a physical device and to any shared state information or resources that the driver maintains in the device extension with the driver’s other routines that access the same device, memory location, or resources.

    If the StartIo routine shares the device or state with the ISR, it must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine to program the device or to access the shared state. For more information about SynchCritSection routines, see Chapter 10.

    If the StartIo routine shares state or resources with routines other than the ISR, it must protect the shared state or resources with a driver-initialized executive spin lock for which the driver provides the storage. For more information about using executive spin locks, see Chapter 16.

  • If a monolithic NT device driver sets up a controller object in its DriverEntry routine, its StartIo routine can use the controller object to synchronize operations through a shared physical device with attached (similar) devices.

    For more information about controller objects, see Chapter 3. For more information about such a driver’s ControllerControl routine, see Chapter 11.

  • Unless a closely coupled higher-level driver pre-splits large DMA transfer requests for its underlying device driver, a DMA device driver’s StartIo routine must split up large transfer requests into partial-transfer ranges and the driver must carry out a sequence of partial-transfer device operations. Each partial transfer must be sized to suit the capabilities of the hardware: either the capabilities of the driver’s device or, for a slave DMA device, the capabilities of the system DMA controller, whichever has stricter constraints.

    For more information about using system or busmaster DMA, see Chapter 3.

  • The StartIo routine of a driver that uses DMA must synchronize transfers using an adapter object.

    For more information about adapter objects, see Chapter 3.

  • A StartIo routine is run at IRQL DISPATCH_LEVEL, which restricts the set of support routines it can call.

    For example, a StartIo routine can neither access nor allocate pageable memory, and it cannot wait on a dispatcher object. On the other hand, a StartIo routine can acquire and release a driver-allocated executive spin lock with KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel, which run faster than KeAcquireSpinLock and KeReleaseSpinLock.

    For more information about managing IRQLs and using spin locks, see Chapter 16.

  • If the driver holds IRPs in a cancelable state, its StartIo routine must check whether the input IRP has already been cancelled before it begins any processing for that request on its device. For more information about managing cancelable IRPs, see also Chapter 12.


7.3 Requirements for Using Interlocked Queues or Device Queues

A lowest-level NT driver either must have a StartIo routine or must set up and manage its own internal queue(s) of IRPs. A higher-level NT driver can have its own internal queues of IRPs at the discretion of the driver designer.

Depending on the functionality required and the nature of the underlying device, an NT driver writer might implement a StartIo routine and set up one or more driver-managed supplemental queues for IRPs.

The system provides support for the following kinds of internal queues of IRPs that NT drivers can use:

  • A doubly-linked interlocked queue into which the driver inserts IRPs by calling ExInterlockedInsertTailList or ExInterlockedInsertHeadList, and from which a device-dedicated thread, worker-thread callback, or another driver routine removes IRPs by calling ExInterlockedRemoveHeadList

  • A device queue associated with a device object into which the driver can insert IRPs by calling KeInsertDeviceQueue or KeInsertByKeyDeviceQueue, and from which it can remove IRPs by calling KeRemoveDeviceQueue, KeRemoveByKeyDeviceQueue, or KeRemoveEntryDeviceQueue

    If the driver has a StartIo routine, it can queue and dequeue IRPs from the device queue associated with its device object(s) by calling IoStartNextPacket or IoStartNextPacketByKey, as mentioned in Section 7.2.1.

An interlocked queue is simpler to manage, particularly for drivers with device-dedicated threads or worker-thread callbacks. On the other hand, supplemental device queues give an NT driver more control over whether a given IRP is placed in the queue and over the ordering of entries in the queue.

An NT driver should not use a singly-linked interlocked queue for IRPs.


7.3.1 Storage Requirements for Driver-Managed Queues

Any NT driver that uses either an interlocked or supplemental device queue must provide storage for the necessary NT objects and resources.

If it uses either kind of queue, an NT driver must provide storage for the following:

  • For a doubly-linked interlocked queue, storage both for an executive spin lock, required as a parameter to the ExInterlocked..List routines, and for the queue header of type LIST_ENTRY

    For more information about using spin locks, see Chapter 16.

  • For a device queue, storage for a Kernel-defined device queue object

    While each device queue object has an associated executive spin lock and a queue header, storage for these resources is allocated automatically when the driver provides storage for its device queue object.

NT drivers usually provide this storage in the device extensions of their device objects, but they can call ExAllocatePool with the PoolType parameter set to NonPagedPool to allocate storage or can provide storage in a controller extension if they use controller objects.

For more information about allocating pool memory, see Chapter 16. For more information about controller objects, see Chapter 3.


7.3.2 Setting Up Driver-Managed Queues

The DriverEntry routine must set up each interlocked queue and/or each supplemental device queue that an NT driver has, as follows:

  • For drivers that use a doubly-linked interlocked queue, the DriverEntry routine must initialize the queue header by calling InitializeListHead, which is an NT runtime library routine. It also must initialize the executive spin lock by calling KeInitializeSpinLock.

  • For drivers that use a supplemental device queue, the DriverEntry routine must call KeInitializeDeviceQueue to initialize the device queue object.

If a new NT driver sets up an internal queue for an indefinite (but limited at any given moment) number of fixed-size, driver-structured entries, a zone buffer can be useful for allocating and freeing memory for such entries. For more information about using zones, see Chapter 16.

For more information about setting up and using an interlocked queue or a device queue object, see also Chapter 3. For specific information about calling the Ex...Zone, ExInterlocked..List, Ke..SpinLock, and Ke..DeviceQueue routines, see the Kernel-mode Driver Reference.


7.3.3 Queueing IRPs in Driver-Managed Queues

IRPs can be inserted into or removed from either kind of queue by calling the appropriate ExInterlocked..List or Ke..DeviceQueue support routine with the corresponding IRP-specific argument:
&Irp->Tail.Overlay.ListEntry
or
&Irp->Tail.Overlay.DeviceQueueEntry

NT drivers also can set up internal queues or buffers for driver-defined data structures, as mentioned in Chapter 6.


7.3.4 Managing Interlocked Queues with a Driver-Created Thread

Like the system floppy controller driver mentioned in Chapter 2, an NT driver with a device-dedicated thread, rather than a StartIo routine, usually manages its own queueing of IRPs in an interlocked queue. Such a driver’s thread pulls IRPs from its interlocked queue when there is work to be done on the device.

In general, such a driver must manage synchronization with its thread to any resources shared between the thread and other driver routines. Such a driver also must have some way to notify its driver-created thread that IRPs are queued. Usually, such a thread waits on a dispatcher object, stored in the device extension, until the driver’s Dispatch routines set the dispatcher object to the Signaled state after inserting an IRP into the interlocked queue.

When such a device driver’s Dispatch routines are called, each checks the parameters in the I/O stack location of the input IRP and, if they are valid, queues the request for further processing. For each IRP queued to a driver-dedicated thread, the Dispatch routine should set up whatever context its thread needs to process that IRP before it calls ExInterlockedInsert..List. The driver’s I/O stack location in each IRP gives the driver’s thread access to the device extension of the target device object, where the driver can share context information with its thread, as the thread removes each IRP from the queue.

Any driver-created thread runs at IRQL PASSIVE_LEVEL and at a base runtime priority previously set when the driver called PsCreateSystemThread. Such a thread’s call to ExInterlockedRemoveHeadList temporarily raises the IRQL to DISPATCH_LEVEL on the current processor while the IRP is being removed from the driver’s internal queue, and the original IRQL is restored to PASSIVE_LEVEL on return from this call.

Any driver thread (or driver-supplied worker-thread callback) must manage the IRQLs at which it runs carefully. For example, consider the following:
  • Because system threads generally run at IRQL PASSIVE_LEVEL, it is possible for a driver thread to wait on Kernel-defined dispatcher objects.

    For example, a device-dedicated thread might wait on an event for other driver routines to satisfy and complete some number of partial-transfer IRPs that the thread sets up with IoBuildSynchronousFsdRequest.

  • On the other hand, such a device-dedicated thread must raise IRQL on the current processor before it calls certain support routines.

    For example, if a driver uses DMA, its device-dedicated thread must nest its calls to IoAllocateAdapterChannel and IoFreeAdapterChannel between calls to KeRaiseIrql and KeLowerIrql because the Io..Adapter Xxx routines and other support routines for DMA operations must be called at IRQL DISPATCH_LEVEL.

    Note that, as mentioned in Section 7.2, StartIo routines are run at DISPATCH_LEVEL, so NT drivers that use DMA need not make calls to the the Ke..Irql routines from their StartIo routines.

  • A driver-created thread can access pageable memory because it runs in a nonarbitrary thread context (its own) at IRQL PASSIVE_LEVEL, but many other standard NT driver routines run at raised IRQL.

    Consequently, if a driver-created thread allocates memory that can be accessed by other driver routines, it must allocate the memory from nonpaged pool. For example, if a device-dedicated thread allocates any buffer that will be accessed later by the driver’s ISR or SynchCritSection, AdapterControl, ControllerControl, DpcForIsr, CustomDpc, IoTimer, CustomTimerDpc, or, in a higher-level driver, IoCompletion routine, the thread-allocated memory cannot be pageable.

  • Like a StartIo routine, a driver thread must synchronize its access to a physical device and to any shared state information or resources that the driver maintains in the device extension with the driver’s other routines that access the same device, memory location, or resources.

    If the thread shares the device or state with the ISR, it must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine to program the device or to access the shared state. For more information about SynchCritSection routines, see Chapter 10.

    If the thread shares state or resources with routines other than the ISR, the driver must protect the shared state or resources with a driver-initialized executive spin lock for which the driver provides the storage. For more information about using spin locks, see Chapter 16.

For more information about managing IRQLs, and about the design tradeoffs of a using a driver thread for a slow device, see Chapter 16. For specific information about the required IRQL(s) for calling any particular support routine, see the Kernel-mode Driver Reference.


7.3.5 Managing Supplemental Device Queues

The I/O Manager usually (except for FSDs) creates an associated device queue object when an NT driver calls IoCreateDevice. It also provides IoStartPacket and IoStartNextPacket, which drivers can call to have the I/O Manager insert IRPs into the associated device queue or call their StartIo routines with an IRP, as desribed in Section 7.2.

Consequently, very few NT driver designers find it necessary (or particularly useful) to set up their own device queue objects for IRPs. Likely candidates are drivers, such as the NT SCSI port driver, that must coordinate incoming IRPs from some number of closely coupled class drivers for heterogeneous devices that are serviced through a single controller or bus adapter.

In other words, the designer of an NT driver for a disk array controller is more likely to use a driver-created controller object than to set up supplemental device queue object(s), while the designer of an NT driver for an add-on bus adapter and of a set of class drivers is slightly more likely to use supplemental device queues.

Support for Supplemental Device Queues

The KeInsert..DeviceQueue routines return a Boolean value indicating whether the given IRP was inserted into the device queue. As mentioned in in Chapter 3, KeInsertDeviceQueue either inserts an entry at the tail of the device queue and sets the state of the device queue object to Busy, or it simply sets the state to Busy and returns FALSE to indicate that the IRP was not queued. A driver that sets up a supplemental device queue can impose a driver-determined order on the processing of IRPs by calling KeInsertByKeyDeviceQueue, which also returns a Boolean value indicating whether the given IRP was inserted into the device queue.

Consider the following an implementation guideline for NT drivers that call KeInsertDeviceQueue or KeInsertByKeyDeviceQueue directly:

If the return value is FALSE, the caller must pass the IRP on for further processing by another driver routine immediately.

A call to KeInsertDeviceQueue or KeInsertByKeyDeviceQueue sets the state of the device queue object to Busy, even if the queue is currently empty and the input IRP is not inserted into the queue. A reciprocal call to KeRemove..DeviceQueue resets the state of an empty device queue object to Not-Busy.

Such a driver can remove an IRP from its supplemental device queue (or reset the device queue state to Not-Busy) by calling KeRemoveDeviceQueue. The driver also can remove a particular entry or determine whether it is currently queued by calling KeRemoveEntryDeviceQueue.

Using Supplemental Device Queues with a StartIo Routine

By calling IoStartPacket and IoStartNextPacket, an NT device driver’s Dispatch and DpcForIsr (or CustomDpc) routines synchronize calls to its StartIo routine using the device queue that the I/O Manager created when the driver created its own device object. For a port driver with a StartIo routine, IoStartPacket and IoStartNextPacket insert and remove IRPs in the device queue for the port driver’s shared device controller/adapter. If such a port driver also sets up supplemental device queues to hold requests coming in from closely coupled higher-level class drivers, it must “sort” incoming IRPs into its supplemental device queues, usually in its StartIo routine.

However, the closely coupled class drivers of such a port driver must identify their devices in the IRPs they send down so that the port driver can attempt to insert each IRP into the appropriate driver-managed device queue before attempting to program its shared controller/adapter to carry out the request. Thus, such a port driver can process incoming requests for all devices on a first-come, first-served basis until a call to KeInsertDeviceQueue puts an IRP into a particular class driver’s device queue.

However, such a port driver’s StartIo routine must determine which class driver sent any incoming request. When each class driver called IoGetDeviceObjectPointer to layer itself over the port driver, each class driver was given a unique file object pointer for its “open” of the port driver’s device object. Each class driver can set this file object pointer in the port driver’s I/O stack location to identify itself when it sets up IRPs for the port driver.

By using its own device queue for all IRPs to be processed through its StartIo routine, such an underlying port driver serializes operations through the shared device (or bus) controller/adapter to all attached devices. By sometimes holding IRPs for each supported device in a separate device queue, this port driver inhibits the processing of IRPs for an already busy device while increasing I/O throughput for every other device doing I/O through its shared hardware.

At the call to IoStartPacket from such a port driver’s Dispatch routine, the I/O Manager either calls that driver’s StartIo routine immediately or puts the IRP into the device queue associated with the device object for the port driver’s shared controller/adapter.

Managing Supplemental Device Queues in StartIo

Such a port driver with supplemental device queues would have a StartIo routine with the following features:

{
    PIO_STACK_LOCATION irpSp =
                    IoGetCurrentIrpStackLocation(Irp);
    PDEVICE_EXTENSION deviceExtension =
                    DeviceObject->DeviceExtension;
    PFILE_OBJECT whichClass;
    PATTACHED_DEVICE_STATE attachedDevice;
        :     :
//
// Switch on MajorFunction code in IRP.
//
    switch (irpSp->MajorFunction) {
        case IRP_MJ_READ:
        case IRP_MJ_WRITE:
//
// Get which device this request is for.
//
            whichClass = irpSp->FileObject;
            attachedDevice = GetAttachedDeviceState(
                                    deviceExtension,
                                    whichClass);
//
// Increment the following to get a unique value
// to give the error log routine, if necessary.
//

            attachedDevice->SequenceNumber++;
                :     :
        case IRP_MJ_DEVICE_CONTROL:
//
// Get which device this request is for.
//
            whichClass = irpSp->FileObject;
            attachedDevice = GetAttachedDeviceState(
                                    deviceExtension,
                                    whichClass);
        :     :
    } // end switch
}

Note that this code fragment checks state information, based on the file object pointer that each class driver sets in the underlying port driver’s I/O stack location of the IRP, in order to determine the current state of the (attached) target device for any given request. The device object pointer input to its StartIo routine gives the port driver access to its own device object, representing the device controller/adapter. It does not give this port driver access to the class driver’s device object for the target device. Such a port driver must maintain its own state information about each of the heterogeneous devices that it services through the shared device controller/adapter.

NT driver writers who design class/port drivers with supplemental device queues in the port driver should keep in mind the following facts:
  • An NT driver cannot get a pointer to the device object of any driver layered above itself easily.

    By design, the I/O Manager does not provide a support routine for getting such a pointer. Moreover, the order in which NT drivers are loaded makes it impossible for lower drivers to get pointers for higher-level drivers’ device objects, which have not yet been created when any lower-level driver is initializing itself.

  • An NT driver cannot use a pointer to the device object of any driver layered above itself.

    There is no way to synchronize access to a single device object (and its device extension) between two drivers in a multiprocessor-safe manner. Neither driver can make any assumptions about what I/O processing the other driver is currently doing.

Even for closely coupled class/port drivers, each class driver should use the pointer to the port driver’s device object(s) only to pass on IRPs with IoCallDriver. The underlying port driver must maintain its own state, probably in the port driver’s device extension, about requests that it processes for any closely coupled class driver(s)’ device(s).

Managing Supplemental Device Queues Across Driver Routines

Any NT port driver that queues IRPs in supplemental device queues for a closely coupled set of class drivers also must handle the following situation efficiently:

  1. Its Dispatch routines have inserted IRPs for a particular device in the driver-created device queue for that device.

  2. IRPs for other devices continue to come in, to be queued to the driver’s StartIo routine with IoStartPacket, and to be processed through the shared device controller.

  3. The device controller does not become idle, but each IRP held in the driver-created device queue also must be queued to the driver’s StartIo routine as soon as possible.

Consequently, such a port driver’s DpcForIsr must attempt to transfer an IRP from the driver’s internal device queue for a particular device into the device queue for the shared adapter/controller whenever the port driver completes an IRP, as follows:

  1. The DpcForIsr routine calls IoStartNextPacket to have the StartIo routine begin processing the next IRP queued to the shared device controller.

  2. The DpcForIsr routine calls KeRemoveDeviceQueue to dequeue the next IRP (if any) that it is holding in its internal device queue for the device on whose behalf it is about to complete an IRP.

  3. If KeRemoveDeviceQueue returns a nonNULL pointer, the DpcForIsr routine calls IoStartPacket with the just dequeued IRP to have it queued to the shared device controller/adapter. Otherwise, the call to KeRemoveDeviceQueue simply resets the state of the device queue object to Not-Busy, and the DpcForIsr omits the call to IoStartPacket.

  4. Then, the DpcForIsr calls IoCompleteRequest with the input IRP for which the port driver has just completed I/O processing, either by setting the I/O status block with an error or by satisfying the I/O request.

Note that the preceding sequence implies that such a DpcForIsr routine also must call the internal GetAttachedDeviceState routine shown in the preceding StartIo code fragment. That is, the DpcForIsr must determine the device for which it is completing the current (input) IRP in order to manage internal queueing of IRPs efficiently.

If such a port driver attempted to wait until its shared controller/adapter was idle before dequeueing IRPs held in its supplemental device queues, the driver might starve a device for which there was heavy I/O demand while it promptly serviced every other device for which the current I/O demand was actually much lighter.


Chapter 8 Interrupt Service Routine

This chapter contains the following information:
8.1 ISR Requirements

8.1.1 ISR Performance

8.1.2 Additional Required Driver Routines

8.1.3 Setting Up an ISR

8.2 Basic ISR Functionality

8.3 ISR Functionality for Overlapped I/O Operations

The interrupt service routine is defined by the Kernel as follows:

BOOLEAN
(*PKSERVICE_ROUTINE) (
    IN PKINTERRUPT Interrupt,
    IN PVOID ServiceContext
    );

This documentation summarizes the required functionality of an NT device driver’s standard interrupt service routine and the requirements for setting up an ISR.


8.1 ISR Requirements

Every NT driver of a physical device that generates interrupts must have an ISR.

An ISR is run at DIRQL, in particular at the SynchronizeIrql level that was specified when the device driver registered its ISR with IoConnectInterrupt. When an NT device driver’s ISR runs, every interrupt with an equivalent or lower IRQL value is masked off on the current processor.

However, another device with a higher system-assigned DIRQL can interrupt or a high-IRQL system interrupt can occur at any time in a Windows NT® machine.

NT device driver writers should keep in mind the following fact:

An NT device driver’s ISR is interruptible.

Because an NT driver’s ISR runs at a relatively high, system-assigned DIRQL, thereby masking off interrupts with an equivalent or lower IRQL on the current processor, an ISR should return control as quickly as possible.

Running an ISR at DIRQL restricts the set of support routines the ISR can call. For more information about managing IRQLs, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-mode Driver Reference.


8.1.1 ISR Performance

Windows NT is quite different from some operating systems with respect to driver ISRs. NT device drivers actually get better performance if their ISRs return control as quickly as possible, rather than attempting to retain control of a CPU and doing as much I/O processing as possible in their ISRs, particularly in SMP machines.

Instead, the ISR should stop its device from interrupting and save any necessary state information about or context for the operation that caused the interrupt in resident memory, usually in the device extension. Then, it should queue the driver’s DpcForIsr routine or a CustomDpc routine to complete the operation at a lower IRQL, usually at IRQL DISPATCH_LEVEL.

The ISR returns a Boolean, indicating whether the driver’s device generated the interrupt, as shown by the declaration. For drivers of devices that share an interrupt vector or DIRQL, each ISR should return FALSE as soon as it determines that its device was not the source of an interrupt.


8.1.2 Additional Required Driver Routines

Every NT driver that has an ISR also must have a DpcForIsr or CustomDpc routine. At the driver writer’s discretion, any NT device driver can have additional CustomDpc routines, used to complete particular kinds of interrupt-driven I/O operations.

If any driver routine shares data, device registers, or context information with the driver’s ISR, that driver also must have one or more SynchCritSection routines.

Note that in a Windows NT uniprocessor machine, the ISR must return before the DpcForIsr or a CustomDpc routine can execute. However, it is possible for the ISR and DpcForIsr (or a CustomDpc) to run concurrently in an SMP machine.

For more information about DpcForIsr and CustomDpc routines, see Chapter 9. For more information about SynchCritSection routines, see Chapter 10.


8.1.3 Setting Up an ISR

An NT device driver’s DriverEntry routine must register the driver’s ISR(s) by calling IoConnectInterrupt when the driver is loaded.

An NT device driver must provide resident storage for at least one interrupt object pointer if it has an ISR. Usually, this pointer is stored in the device extension of the device object that represents the physical device that generates interrupts. The interrupt object pointer can be stored in a controller extension if the driver creates a controller object, or it can be stored in nonpaged pool allocated by the driver.

If an NT driver has a single ISR that handles interrupts for more than one device on different vectors or has more than one ISR, it must provide storage for an interrupt spin lock to be associated with every set of interrupt objects for all its devices. Its interrupt spin lock must be initialized with KeInitializeSpinLock before the driver registers its ISR(s) by calling IoConnectInterrupt. Such a driver also must provide storage for as many interrupt object pointers as the IRQs it handles.

For more information about registering ISRs and interrupt objects, see Chapter 3.


8.2 Basic ISR Functionality

On entry, an ISR is given pointers to the driver’s interrupt object and a ServiceContext pointer to whatever area the DriverEntry routine set up when it called IoConnectInterrupt. Most NT device drivers set the ServiceContext pointer to their device objects that represent physical devices that generate interrupts or to such a device object’s device extension. Such a driver uses the device extension to set up state information for the driver’s DpcForIsr routine, which usually does almost all of the I/O processing to satisfy each request that caused the device to interrupt.

In NT device drivers that do not overlap device I/O operations, the ISR is responsible for the following:

  1. Determine whether the interrupt is spurious. If so, return FALSE immediately so the ISR of the device that interrupted will be called promptly. Otherwise, continue interrupt processing.

  2. Stop the device from interrupting.

  3. Gather whatever context information the DpcForIsr (or CustomDpc) routine will need to complete I/O processing for the current operation.

  4. Store this context in an area accessible to the DpcForIsr or CustomDpc routine, usually in the device extension of the target device object for which processing the current I/O request caused the interrupt.

  5. If the driver has a DpcForIsr routine, call IoRequestDpc with pointers to the current IRP, the target device object, and the saved context. IoRequestDpc queues the DpcForIsr routine to be run as soon as IRQL falls below DISPATCH_LEVEL on a processor.

    If the driver has a CustomDpc routine, call KeInsertQueueDpc with a pointer to the DPC object (associated with the CustomDpc routine) and pointer(s) to any saved context the CustomDpc routine will need to complete the operation. Usually, the ISR also passes pointers to the current IRP and target device object. The CustomDpc routine is run as soon as IRQL falls below DISPATCH_LEVEL on a processor.

  6. Return TRUE to indicate that its device generated the interrupt.

In general, an ISR does no actual I/O processing to satisfy an IRP. Instead, it stops its device from interrupting, sets up necessary state information, and queues the driver’s DpcForIsr or CustomDpc to do whatever I/O processing is necessary to satisfy the current request that caused the device to interrupt.

NT device driver writers should consider the following an implementation guideline:

An ISR must run at DIRQL for the shortest possible interval.

Following the preceding guideline increases I/O throughput for every device in the machine because running at DIRQL masks off all interrupts to which the system has assigned a lesser or equal IRQL value.

The SynchronizeIrql of the driver’s interrupt object(s), specified when the driver called IoConnectInterrupt, determines the DIRQL at which a device driver’s ISR and SynchCritSection routines are run.

When an NT device driver’s StartIo (or other) routine calls KeSynchronizeExecution with the driver’s SynchCritSection routine, the caller also passes the pointer to the interrupt object(s) associated with the ISR. Consequently, interrupts from the device are masked off on the processor running the SynchCritSection routine. Meanwhile, KeSynchronizeExecution holds the spin lock associated with the interrupt object(s) so that the ISR cannot access the device registers or shared state in the device extension from another processor until the driver’s SynchCritSection routine returns control.

For more information about StartIo and SynchCritSection routines, see Chapters 7 and 10, respectively. For more information about how the caller of KeSynchronizeExecution uses an interrupt spin lock, see also Chapter 16.


8.3 ISR Functionality for Overlapped I/O Operations

While the ISR and DpcForIsr (or CustomDpc) can run concurrently in Windows NT SMP machines, only one instantiation of the DPC object that represents a DpcForIsr or CustomDpc routine can be queued for execution at any given moment.

If the same DPC object is queued more than once by an ISR before the DpcForIsr (or CustomDpc) routine is run, the corresponding DpcForIsr or CustomDpc routine is called only once. If the DPC object is queued while the DpcForIsr (or CustomDpc) is running, two instantiations of this routine can run concurrently in Windows NT SMP machines.

Consequently, any NT driver that overlaps I/O operations on its device(s) must have DpcForIsr and/or CustomDpc routines that can complete more than one IRP when they are called. Such a driver’s ISR has the same basic requirements as a device driver that does not overlap I/O operations, as already described in Section 8.2.

However, such a device driver’s ISR must set up additional state for the DpcForIsr and/or CustomDpc routines, such as a count of how many outstanding requests the DPC routine is required to complete and whatever context the DPC routine needs to complete each outstanding IRP. Note that such a driver’s ISR must take care not to overwrite the saved context for a request that has not yet been completed if the ISR is called to handle another interrupt before the DPC is run.

Because such a driver’s DPC routine(s) would share this state with the ISR, its DPC routine(s) must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine to access the shared state on behalf of each DPC routine.

For more information about DpcForIsr and CustomDpc routines, see Chapter 9. For more information about SynchCritSection routines and about the interaction of the ISR and DPCs queued from the ISR, see also Chapters 10 and 16, respectively.


Chapter 9 DpcForIsr Routine and CustomDpc Routines

This chapter contains the following information:
9.1 DpcForIsr and CustomDpc Routine Requirements

9.1.1 DpcForIsr versus CustomDpc Routines

9.1.2 Basic DpcForIsr Requirements

9.1.3 Basic CustomDpc Routine Requirements

9.1.4 Additional Required Driver Routines

9.1.5 Setting Up DpcForIsr and CustomDpc Routines

9.2 DpcForIsr and CustomDpc Functionality
9.2.1 Using Buffered I/O and Satisfying Device Control IRPs

9.2.2 Using Direct I/O

9.2.3 Points to Consider in Implementing a DpcForIsr or CustomDpc Routine

The DpcForIsr routine is defined by the I/O Manager as follows:

VOID
(*PIO_DPC_ROUTINE) (
    IN PKDPC Dpc,
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    );

The CustomDpc routine is defined by the Kernel as follows:

VOID
(*PKDEFERRED_ROUTINE) (
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    );

This chapter summarizes the required functionality of an NT device driver’s standard DpcForIsr routine and/or CustomDpc routines.


9.1 DpcForIsr and CustomDpc Routine Requirements

If an NT driver has an ISR, it also must have a DpcForIsr or CustomDpc routine to complete its processing of interrupt-driven I/O operations.

A DpcForIsr or CustomDpc routine is run in an arbitrary thread context at IRQL DISPATCH_LEVEL.

Running at IRQL DISPATCH_LEVEL restricts the set of support routines a DpcForIsr or CustomDpc routine can call.

For more information about managing IRQLs in NT drivers, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-mode Driver Reference.


9.1.1 DpcForIsr versus CustomDpc Routines

Depending on an NT driver’s design, it can have a single DpcForIsr to complete all interrupt-driven I/O operations, have both a DpcForIsr and a set of operation-specific CustomDpc routines to be queued from its ISR, or have a set of one or more CustomDpc routines.

Most NT device drivers have a single DpcForIsr routine to complete I/O processing for each IRP that requires one or more operations on their respective devices. Using a single DpcForIsr to complete per-request, interrupt-driven I/O operations on a device that does one operation at a time is relatively easy. Such a driver’s ISR need only call IoRequestDpc for each interrupt-driven I/O operation.

Using a single DpcForIsr to complete overlapped, interrupt-driven I/O operations on a device that can do concurrent operations is possible with careful design but can be relatively difficult. In addition to or instead of queueing a DpcForIsr, an NT device driver’s ISR can queue a set of operation-specific, driver-supplied CustomDpc routines by calling KeInsertQueueDpc at the discretion of the driver designer.

For example, consider some of the design challenges for an NT serial driver. As the driver of a full-duplex device, an NT serial driver cannot rely on a one-to-one correspondence between the order in which IRPs are queued to a StartIo routine and the sequence of interrupts from its device in a multitasking, multiprocessor system. Furthermore, such a driver must handle timing out requests and asynchronous user-generated requests to cancel previously requested operations, to purge buffered data, and so forth.

Consequently, an NT serial driver might maintain internal queues for the read, write, purge, and wait operations that user-mode comm applications can request. It also could maintain reference counts or use some other tracking mechanism, such as a set of flags, for the IRPs in its internal queues. Its ISR would call KeInsertQueueDpc with any of a number of driver-allocated and initialized DPC objects, each associated with a driver-supplied CustomDpc routine.

Whether any NT device driver has a single DpcForIsr routine and/or a set of CustomDpc routines depends on the nature of its underlying device, the set of I/O requests it must support, and on the driver designer.


9.1.2 Basic DpcForIsr Requirements

On entry, the DpcForIsr is given pointers to the current IRP, target device object, and context information, as shown by the declaration, which were passed in the ISR’s call to IoRequestDpc.

A DpcForIsr routine is responsible, in general, for the following:

  • Completing the I/O processing requested by the IRP for the target device object, using the context that was set up by the ISR

  • Seeing that the next IRP is processed as soon as possible, usually by calling IoStartNextPacket or IoStartNextPacketByKey so the driver’s StartIo routine will start the next requested I/O operation on the target device

    If an NT device driver manages its own queueing, its DpcForIsr routine must have some way of notifying the driver to dequeue the next IRP and begin device I/O processing for the next request.

  • Setting the I/O status block in the input IRP and calling IoCompleteRequest with this just completed request

For more detailed information about the functionality of DpcForIsr routines, see also Section 9.2


9.1.3 Basic CustomDpc Routine Requirements

On entry, any context information passed to a CustomDpc routine is driver-determined. That is, the ISR can pass pointers to any relevant context it has set up at SystemArgument1 and/or SystemArgument2 when it calls KeInsertQueueDpc with the Dpc object pointer representing the driver’s CustomDpc routine. Note that a driver must specify a DeferredContext pointer for its CustomDpc routine when it initializes its DPC object, so such a driver’s ISR also can set up context for the CustomDpc routine at DeferredContext.

Frequently, an ISR passes pointers to the device extension of the target device object for each interrupt-driven I/O operation and a pointer to the current IRP to its CustomDpc routines. Each CustomDpc routine is responsible for completing some task, associated with an interrupt-driven I/O operation, of the driver designer’s choice.

If an NT device driver has no DpcForIsr routine, its CustomDpc routine(s) are responsible for carrying out the same basic operations as a DpcForIsr routine. That is, the driver’s CustomDpc routine(s) are responsible, in general, for the following:

  • Completing the I/O processing requested by the current IRP for the target device object that caused the interrupt on the device, using context set up by the ISR

  • Seeing that the next IRP is processed as soon as possible, possibly by calling IoStartNextPacket or IoStartNextPacketByKey so the driver’s StartIo routine will start the next requested I/O operation on the target device

    If an NT device driver manages its own queueing, a CustomDpc routine must have some way of notifying the driver to dequeue the next IRP and begin device I/O processing for the next request.

  • Setting the I/O status block in the current IRP and calling IoCompleteRequest with this just completed request

For more information about the functionality of CustomDpc routines, see also Section 9.2.


9.1.4 Additional Required Driver Routines

An NT device driver’s ISR saves context information for its DpcForIsr and/or CustomDpc routines. Sometimes the DpcForIsr or CustomDpc routine is responsible for retrying operations, setting up another DMA transfer operation to satisfy a given request, or otherwise accessing the same device registers or context area as the ISR does.

When any NT driver routine shares context information or device registers with an ISR, that routine must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine in order to access the shared context safely.

Unless an NT device driver serializes its IRP processing by completing each request before it starts device I/O operations for the next IRP, the driver must have at least one SynchCritSection routine. While it is almost impossible for the ISR and another routine, such as its DpcForIsr or CustomDpc, to access shared state concurrently if a driver serializes IRP processing, handling IRPs in this manner prevents such a driver from achieving the best possible I/O throughput for its device.

Note that an NT driver that serializes its IRP processing can run no faster in Windows NT® SMP machines than in uniprocessor machines.


9.1.5 Setting Up DpcForIsr and CustomDpc Routines

The DriverEntry routine must register any DpcForIsr and initialize each CustomDpc routine an NT device driver has as follows:

  • Call IoInitializeDpcRequest with the entry point for the DpcForIsr and a pointer to the driver-created device object representing the physical device that generates interrupts.

  • Call KeInitializeDpc with a pointer to the driver’s storage for the DPC object, the entry point for the CustomDpc routine, and a DeferredContext pointer to a context area, which is passed to the CustomDpc routine when it is run.

If an NT driver has a CustomDpc routine, it must provide resident storage for a Kernel-defined DPC object. Usually, such a driver stores the DPC object in the device extension of the device object that it creates to represent its physical device that generates interrupts. Such a driver’s ISR is likely to access this device extension to save context for the CustomDpc routine, and the ISR must pass the Dpc object pointer to KeInsertQueueDpc when it queues the CustomDpc routine.

However, an NT device driver can provide storage for its DPC objects and context for interrupt-driven I/O operations in driver-allocated nonpaged pool or in the controller extension if the driver sets up a controller object.

Note that a CustomDpc routine differs from a CustomTimerDpc routine in how each is queued, the former with KeInsertQueueDpc and the latter with KeSetTimer or KeSetTimerEx. That is, for a CustomTimerDpc routine, the driver also must allocate storage for and initialize a Kernel-defined timer object, and it cannot pass the SystemArgument1 and SystemArgument2 pointers to its CustomTimerDpc routine with KeSetTimer or KeSetTimerEx.

For more information about DPC objects, see Chapter 3. For more information about CustomTimerDpc routines, see Chapter 14.


9.2 DpcForIsr and CustomDpc Functionality

In addition to the basic responsibilities of these routines, already mentioned in Sections 9.1.2 and 9.1.3, every DpcForIsr or CustomDpc routine is responsible for using ISR-provided context to complete one or more interrupt-driven I/O operations.

In other words, besides ensuring that the next device I/O operation is started promptly and completing the current IRP, the work done by any DpcForIsr or CustomDpc routine depends on the driver’s design and/or on the nature of its device.

For example, the DpcForIsr or CustomDpc routine also can do any of the following in order to complete an interrupt-driven I/O operation:

  • Retry an operation that has timed out or failed.

  • Call IoAllocateErrorLogEntry, set up an error log packet to report a device I/O error, and call IoWriteErrorLogEntry.

    For more information about handling I/O errors, see Chapter 16.

  • If the driver uses buffered I/O or the current IRP requests a device control operation, transfer data read in from the device to the system buffer at Irp->AssociatedIrp.SystemBuffer before completing the current IRP.

  • If the driver uses direct I/O, save state about a just completed partial-transfer operation, calculate the next partial-transfer range, and use a driver-supplied SynchCritSection routine to program the device for the next partial-transfer operation.

    Even a driver that uses buffered I/O might have to split up a transfer request if its device has very limited transfer capabilities.

  • If the driver uses packet-based DMA, call IoFlushAdapterBuffers after each device transfer operation, and call IoFreeAdapterChannel or IoFreeMapRegisters when a sequence of partial transfers is done and the full transfer request is satisfied.

    If a requested transfer is only partly satisfied by a single DMA operation, the DpcForIsr is usually responsible for setting up one or more DMA operations until the current IRP’s requested Length (number of bytes) has been fully transferred.

    For more information about using DMA, see Chapter 3. See also Chapter 11 for more information about AdapterControl routines and Chapter 16 for more information about maintaining cache coherency during DMA.

  • If the driver uses PIO, call KeFlushIoBuffers at the end of each device transfer operation if the current IRP requests a read.

    If a requested transfer is only partly satisfied by a single PIO operation, the DpcForIsr (or CustomDpc) is usually responsible for setting up one or more transfer operations until the current IRP’s requested Length (number of bytes) has been fully transferred.

    For more information about using PIO, see Chapter 3. See also Chapter 16 for more information about maintaining processor cache coherency during PIO reads.

  • If the driver has a ControllerControl routine, call IoFreeController when a requested operation is complete.

    For more information about controller objects, see Chapter 3. For more information about ControllerControl routines, see Chapter 11.

Note that an NT device driver’s DpcForIsr (or CustomDpc) routine usually does most of the driver’s device I/O processing to satisfy IRPs. This routine also shares some of the responsibility for queueing IRPs to the device with the driver’s Dispatch routines.

NT device driver writers should consider the following a general design guideline

Any DpcForIsr or CustomDpc routine should call IoStartNextPacket as soon as it can safely make this call: that is, without possibly causing a resource conflict or race condition with the driver’s StartIo routine or with any other routine the StartIo routine causes to run.

If a driver manages its own queueing of IRPs, its DpcForIsr or CustomDpc routine should notify the driver as soon as it is safe to dequeue the next IRP and to set up the device for the next request.

A DpcForIsr or CustomDpc routine must call IoStartNextPacket, or otherwise notify the appropriate driver routine when device I/O processing for the next request can be started, before the DpcForIsr or CustomDpc returns control. Depending on the driver and its device, this can occur well before the DpcForIsr or CustomDpc routine completes the current IRP with IoCompleteRequest, or it can occur immediately before this routine completes the current IRP and returns control.


9.2.1 Using Buffered I/O and Satisfying Device Control IRPs

As already mentioned in Chapter 7, NT device drivers that use buffered I/O usually have simpler StartIo routines than those that use direct I/O because the former transfer relatively small amounts of data per interrupt into or from a system-space buffer allocated by the I/O Manager. Most device drivers that use buffered I/O also have simpler DpcForIsr routines than those that use direct I/O.

Even drivers that set up their device objects for direct I/O use buffered I/O for IRP_MJ_DEVICE_CONTROL requests when the I/O control codes are defined with method buffered.

Using buffered I/O, drivers transfer data to or from a system-space buffer, allocated by the I/O Manager, at Irp->AssociatedIrp.SystemBuffer.

The DpcForIsr or CustomDpc routine of any NT device driver usually completes the I/O processing only for a subset of public I/O control codes that the driver must support. In particular, the DPC routine completes operations for device control requests with the following characteristics:

  • Requests that change the state of the physical device

  • Requests that require the return of inherently volatile information about the physical device

For more information about the system-defined and device-type-specific I/O control codes an NT device driver must support, see the Kernel-mode Driver Reference.


9.2.2 Using Direct I/O

Any device driver that transfers data using direct I/O might have to split up a large transfer request into several transfer operations to suit the capabilities of its device or, for a slave DMA device, of the system DMA controller. Consequently, such a driver’s DpcForIsr routine might have to reprogram the device for more than one transfer operation in order to satisfy a given read/write request and complete that IRP.

Because a DpcForIsr or CustomDpc routine runs at IRQL DISPATCH_LEVEL, it must call KeSynchronizeExecution with a SynchCritSection routine to update any state, such as the set of device registers, that is shared with the driver’s ISR.

NT device drivers that use direct I/O can be requested to transfer large amounts of data. The locked-down buffer described by the MDL in IRPs passed to such a driver’s DispatchRead and/or DispatchWrite routine(s) can be larger than the system-defined PAGE_SIZE. Moreover, the MDL at Irp->MdlAddress can describe a buffer backed by several discontiguous physical pages in system memory.

In addition, the device through which such a driver transfers the requested data can have transfer limitations of its own. For example, the system “AT” disk driver, described in Chapter 2, must split up any transfer request for more than 256 sectors due to the disk controller’s limitations.

When such a device driver’s StartIo, AdapterControl, or ControllerControl routine is called with a request to transfer more data than the physical device can handle, it can set up its device to transfer only as much data as that device allows. After the driver’s ISR has handled the interrupt(s) for a maximum device transfer, the DpcForIsr (or CustomDpc) must set up the device again, possibly more than once, to transfer the remaining data necessary to satisfy the IRP.


9.2.3 Points to Consider in Implementing a DpcForIsr or CustomDpc Routine

Keep the following points in mind when implementing a DpcForIsr or CustomDpc routine:

  • A DpcForIsr or CustomDpc routine must synchronize its access to a physical device and to any shared state information or resources that the driver maintains in the device extension with the driver’s other routines that access the same device or memory location.

    If the DpcForIsr or CustomDpc shares the device or state with the ISR, it must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine to program the device or to access the shared state.

    If the DpcForIsr or CustomDpc shares state or resources, such as an interlocked queue or a timer object, with routines other than the ISR, it must protect the shared state or resources with a driver-initialized executive spin lock for which the driver provides resident storage.

  • DpcForIsr or CustomDpc routines run at IRQL DISPATCH_LEVEL, which restricts the set of support routines they can call.

    For example, a DpcForIsr or CustomDpc routine can neither access nor allocate pageable memory, and it cannot wait on a dispatcher object. On the other hand, a DpcForIsr or CustomDpc routine can acquire and release a driver’s executive spin lock with KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel, which run faster than KeAcquireSpinLock and KeReleaseSpinLock.

  • A DpcForIsr or CustomDpc routine is responsible for starting the next I/O operation on the device.

    For device drivers that use direct I/O, this responsibility can include using a SynchCritSection routine to program the device to transfer more data in order to satisfy the current IRP before the driver calls IoStartNextPacket.

  • If a device driver sets up a controller object in its DriverEntry routine to synchronize I/O operations through the controller to attached devices, its DpcForIsr or CustomDpc routine is responsible for releasing the controller object with IoFreeController before it completes the current IRP and returns control.

  • If the driver uses DMA and its AdapterControl routine returns KeepObject or DeallocateObjectKeepRegisters (thereby retaining the system DMA controller channel or busmaster adapter for additional transfer operations), the driver’s DpcForIsr or CustomDpc routine is responsible for releasing the adapter object or map registers with IoFreeAdapterChannel or IoFreeMapRegisters before it completes the current IRP and returns control.

  • A DpcForIsr or CustomDpc routine is generally responsible for logging any device errors that occurred during the processing of a given request, retrying the current request if necessary (and also possible), and for setting the I/O status block and calling IoCompleteRequest with the current IRP.

  • The DpcForIsr or CustomDpc of a driver that overlaps operations on its device cannot rely on a one-to-one correspondence between requests input to the StartIo routine and the ISR’s calls to IoRequestDpc or KeInsertQueueDpc. In other words, such a driver’s DpcForIsr or CustomDpc cannot necessarily use the input pointers to the IRP and ISR-supplied context, nor the CurrentIrp pointer in the target device object, to complete only that IRP.

    At any given moment, the same DPC object cannot be queued twice. If the ISR of such a driver calls IoRequestDpc or KeInsertQueueDpc more than once before the corresponding DpcForIsr or CustomDpc executes, only one instantiation of that DPC routine is run as soon as the IRQL on a processor falls below DISPATCH_LEVEL. On the other hand, if the ISR calls IoRequestDpc or KeInsertQueueDpc while the corresponding DpcForIsr or CustomDpc is running on another processor, two instantiations of that DPC routine can run concurrently.

    Therefore, any NT driver that overlaps interrupt-driven I/O operations on its device must have the following functionality:

    • A DpcForIsr or CustomDpc that can complete some driver-maintained count of outstanding requests when it is called

    • An ISR that never overwrites its saved context for an interrupt-driven I/O operation until the DpcForIsr or CustomDpc has consumed that context information and completed the IRP for which the context was saved

    • A SynchCritSection routine that accesses the ISR’s context area on behalf of the DpcForIsr or CustomDpc routine

For more information about SynchCritSection routines, see Chapter 10. For more information about ControllerControl and AdapterControl routines, see Chapter 11. For more information about any particular support routine mentioned in this section, see the Kernel-mode Driver Reference.


Chapter 10 SynchCritSection Routines

This chapter contains the following information:
10.1 SynchCritSection Routine Requirements

10.1.1 Using a SynchCritSection Routine

10.1.2 Running a SynchCritSection Routine

10.1.3 SynchCritSection Routine Performance Requirements

10.2 SynchCritSection Routine Functionality
10.2.1 Programming the Device for an I/O Operation

10.2.2 Maintaining State About Interrupt-Driven I/O Operations

The SynchCritSection routine is defined by the Kernel as follows:

BOOLEAN
(*PKSYNCHRONIZE_ROUTINE) (
    IN PVOID SynchronizeContext
    );

This chapter summarizes the required functionality of an NT device driver’s SynchCritSection routine(s).


10.1 SynchCritSection Routine Requirements

Any NT device driver with routines that share state, resources, or device ports/registers with its ISR must have a SynchCritSection routine. Most NT device drivers have at least two SynchCritSection routines, and many have more.

A SynchCritSection routine, like an ISR, is run at the SynchronizeIrql level specified when the driver registered its ISR with IoConnectInterrupt.

Running at DIRQL restricts the set of support routines a SynchCritSection routine can call.

For more information about managing IRQLs in NT drivers, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-mode Driver Reference.


10.1.1 Using a SynchCritSection Routine

As its name suggests, a SynchCritSection routine is a critical section of code written to access a shared area when accesses to that area must be synchronized with possible accesses by an ISR.

For NT device drivers, the StartIo and DpcForIsr or CustomDpc routines frequently must access some of the same memory or device ports/registers as the driver’s ISR. Depending on such a driver’s device or its design, Dispatch, AdapterControl, ControllerControl, or driver-supplied timer routines also might access driver-maintained state and/or device ports/registers shared with its ISR.

If any nonISR routine attempted to access such a shared area directly, the device could interrupt while that routine was programming the device or updating state information. In other words, the ISR might change the state or device registers out from under the Dispatch, StartIo, DpcForIsr, CustomDpc, AdapterControl, ControllerControl, IoTimer or CustomTimerDpc routine whenever a device interrupt occurred.


10.1.2 Running a SynchCritSection Routine

Every nonISR routine in an NT device driver must synchronize its access to areas shared with its ISR by calling KeSynchronizeExecution with a pointer to the interrupt object(s) associated with the ISR and with a driver-supplied SynchCritSection routine.

Making this call protects the shared data or device ports/registers with the spin lock that is associated with the driver’s interrupt object(s). Holding the spin lock masks device interrupts of lower or equivalent IRQL on the SynchCritSection routine’s processor, and prevents the shared area from being simultaneously accessed from the ISR in Windows NT® SMP machines.


10.1.3 SynchCritSection Routine Performance Requirements

Like an ISR, a SynchCritSection routine must execute as quickly as possible, doing only what is necessary to set up device registers or update context data, before returning control to the routine that called KeSynchronizeExecution.

NT device driver writers should consider the following a design and implementation guideline

Because it is run at DIRQL, any SynchCritSection routine must return control as quickly as possible.

Because KeSynchronizeExecution holds a device driver’s interrupt spin lock while its SynchCritSection routine runs, the driver’s ISR cannot get any work done until the SynchCritSection routine returns control.

Each NT device driver should be designed to minimize the time it spends running at DIRQL for each IRP it processes on the device.

On return from KeSynchronizeExecution, the caller’s IRQL is restored.


10.2 SynchCritSection Routine Functionality

NT device drivers use their SynchCritSection routines for either of two basic purposes:

  • To program a physical device, shared with the ISR, for an I/O operation

  • To access state information that is shared with the ISR from other driver routines


10.2.1 Programming the Device for an I/O Operation

For any incoming IRP, NT device drivers do as much I/O processing as possible at IRQL PASSIVE_LEVEL in their Dispatch routines (or, possibly driver threads) or at IRQL DISPATCH_LEVEL, as in their StartIo routines, before they program their devices for an I/O operation.

In addition, a device driver’s StartIo routine must postpone its call to KeSynchronizeExecution with a SynchCritSection routine that programs the device to the following standard driver routines:

  • If an NT driver sets up a controller object to represent a device controller with attached devices, its StartIo routine cannot program the device controller, which might already be in use for an I/O operation on another attached device, until the physical controller is not busy.

    1. Such a driver’s StartIo routine must call IoAllocateController with its ControllerControl routine to synchronize operations through the physical controller.

    2. Only then can its ControllerControl routine call KeSynchronizeExecution with a SynchCritSection routine that programs the physical controller for the requested operation to/from the target device.

    If a transfer operation through such a driver’s controller does not fully satisfy a particular IRP, the driver’s DpcForIsr routine also must call KeSynchronizeExecution with the same SynchCritSection routine to reprogram the physical controller for each additional transfer necessary to satisfy the current IRP.

    For more information about controller objects, see Chapter 3. For more information about ControllerControl routines, see also Chapter 11.

  • If an NT driver sets up an adapter object for system or packet-based busmaster DMA, its StartIo routine (or possibly, driver-created thread or ControllerControl routine) cannot program a slave device until the system DMA controller, which might be busy transferring data for another slave device, is available or until the system has allocated the DMA map registers for such a driver’s busmaster adapter.

    1. Such a driver’s StartIo routine must call IoAllocateAdapterChannel with its AdapterControl routine in order to synchronize operations through the system DMA controller or the driver’s busmaster DMA adapter.

    2. Only then can its AdapterControl routine call KeSynchronizeExecution with a SynchCritSection routine that programs the driver’s device for a DMA transfer.

    If a DMA transfer operation on such a driver’s device/adapter does not fully satisfy a particular IRP, the driver’s DpcForIsr routine also must call KeSynchronizeExecution with the same SynchCritSection routine to reprogram the device/adapter for each additional DMA transfer necessary to satisfy the current IRP.

    For more information about adapter objects and how to stage DMA processing, see Chapter 3. For more information about AdapterControl routines, see also Chapter 11.

    NT device driver writers should consider the following general guidelines for designing, implementing, and calling SynchCritSection routines that program the device for I/O operations

  • A device driver should do all preprocessing for an IRP, set up any necessary state for other driver routines that also process device I/O requests, and acquire all necessary resources, such as a access to a device controller, system DMA channel, or set of DMA map registers, before it calls KeSynchronizeExecution to have a driver-supplied SynchCritSection routine program the device for an I/O operation.

  • A SynchCritSection routine that programs the device for I/O operations must return control as quickly as possible. For this reason, such a SynchCritSection routine should do only what is necessary to set up the device for I/O.

  • A device driver can have a set of SynchCritSection routines to program the device, if necessary. That is, the driver of a device for which setting up a read request differs markedly from setting up certain device control requests might have separate SynchCritSection routines to program its device for each type of request.

  • An NT device driver should not have a single, large, general-purpose SynchCritSection routine with a switch statement or many nested if..then..else statements to determine what operations it will carry out to program the device for an I/O operation and/or what state information to update. On the other hand, a driver also should avoid having a set of many SynchCritSection routines, each responsible for programming only a single device register.

Every SynchCritSection routine must return control as quickly as possible, because running any SynchCritSection routine prevents the driver’s ISR from getting any work done if it is running concurrently.


10.2.2 Maintaining State About Interrupt-Driven I/O Operations

Drivers whose DpcForIsr, CustomDpc, ControllerControl, AdapterControl, IoTimer, or CustomTimerDpc routines share state in the device extension with the ISR also use various techniques to make their SynchCritSection routines run as quickly as possible.

For example, device drivers might use some of techniques described in this scenario to minimize the execution intervals of their SynchCritSection routines. Consider a device driver that maintains a timer counter in its device extension to check whether a device I/O operation has timed out and that does not overlap I/O operations on its device. Several of the driver’s routines might access such a timer counter, using a SynchCritSection routine, as follows:

  • Its StartIo routine initializes the timer counter to a driver-determined time-out value for the current request. The driver adds a second to its device-time-out value, in case its IoTimer routine has just returned control.

  • Its ISR must set this timer counter to minus one when it is called to handle an interrupt from the device for the requested operation.

  • Its IoTimer routine is called once per second to read the value of this timer counter and determine whether the ISR has already reset it to minus one. If not, the IoTimer routine decrements this counter by calling KeSynchronizeExecution with a SynchCritSection_1 routine.

    If the counter goes to zero, indicating that the request timed out on the device, the SynchCritSection_1 routine calls a SynchCritSection_2 routine to program a device reset. If the counter is set to minus one, the IoTimer routine simply returns.

    For more information about IoTimer routines, see Chapter 14.

  • If the driver’s DpcForIsr must have the device reprogrammed to begin another partial-transfer operation, it must reinitialize the timer counter as the StartIo routine did.

    The DpcForIsr also must call KeSynchronizeExecution with the SynchCritSection_2 routine, or possibly a SynchCritSection_3 routine, to program the device for another transfer operation.

Note that such a driver has more than one SynchCritSection routine, each with discrete but specific responsibilities, one to maintain its timer counter, another (or others) to program the device. Thus, each SynchCritSection routine can return control as quickly as possible because each does a single discrete task and nothing else.

Note also that such a driver has a single SynchCritSection_1 routine that maintains the state to the timer counter, along with the driver’s ISR. Thus, there is no contention for access to the timer counter among several SynchCritSection routines and the ISR.

NT device driver writers should consider the following general guidelines for designing and implementing SynchCritSection routines that maintain state

  • If a device driver maintains state about its interrupt-driven I/O operations in the device (or controller) extension, place all this state information in a subrange of the device extension. This allows other nonISR routines that share resources with each other to access “their” area of the device extension without possible contention from the ISR and SynchCritSection routines.

    For example, a driver-managed interlocked queue cannot be accessed by an ISR or SynchCritSection routine because neither can call an ExInterlocked..List routine from DIRQL when other driver routines synchronize their accesses to the queue from IRQL DISPATCH_LEVEL. Such a queue header and its guardian spin lock are readily accessible to other driver routines if its ISR and SynchCritSection routines always find the state they modify within a defined subrange of the device extension that does not include any resources accessed by other driver routines.

    On the other hand, if the ISR or SynchCritSection routines access state variables scattered throughout the device extension, conflicting attempts to access the area that contains the driver’s interlocked queue or other shared resources are much more likely.

  • Give each SynchCritSection routine that maintains state information responsibility for a discrete set of state variables. That is, avoid writing SynchCritSection routines that maintain overlapping state information.

    This prevents contention, and possibly race conditions, between SynchCritSection routines (and the ISR) trying to access the same state concurrently.

    This also ensures that each SyncCritSection routine returns control as quickly as possible because one SynchCritSection routine never has to wait for another that updates some of the same state information to return control.

  • An NT device driver should avoid having a single, large, general-purpose SynchCritSection routine that does more testing of conditions to determine what to do than actually doing useful work. On the other hand, a driver also should avoid having many SynchCritSection routines that never execute a conditional statement because each updates only a single byte of state information.

Every SynchCritSection routine must return control as quickly as possible, because running any SynchCritSection routine prevents the driver’s ISR from getting any work done if it is running concurrently.


Chapter 11 AdapterControl and ControllerControl Routines

This chapter contains the following information:
11.1 Driver Control Routines

11.1.1 Designing Device Drivers with DriverControl Routines

11.1.2 Returns from DriverControl Routines

11.1.3 Storage Requirements

11.1.4 Setting Up AdapterControl and ControllerControl Routines

11.2 AdapterControl Routine Requirements

11.3 ControllerControl Routine Requirements

The AdapterControl and ControllerControl routines are defined by the I/O Manager as follows:

IO_ALLOCATION_ACTION
(*PDRIVER_CONTROL) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID MapRegisterBase,
    IN PVOID Context
    );

This chapter summarizes the required functionality of an NT device driver’s standard AdapterControl and/or ControllerControl routines.

For more information about support for system and busmaster DMA device drivers and about adapter objects, see Chapter 3. For more information about controller objects, see also Chapter 3.


11.1 Driver Control Routines

AdapterControl and ControllerControl routines are run in an arbitrary thread context at IRQL DISPATCH_LEVEL.

Running at IRQL DISPATCH_LEVEL restricts the set of support routines an AdapterControl or ControllerControl routine can call.

For more information about managing IRQLs in NT drivers, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-mode Driver Reference.


11.1.1 Designing Device Drivers with DriverControl Routines

Most NT drivers of DMA devices must have an AdapterControl routine:

  • Every NT driver of a DMA device that is connected to a system DMA controller must have an AdapterControl routine.

  • Any NT driver of a busmaster DMA device must have an AdapterControl routine unless that driver’s designer decides to use the system’s common-buffer DMA support exclusively.

At the driver writer’s discretion, an NT device driver can have a ControllerControl routine in order to synchronize its operations to similar devices through a single physical controller or adapter.

The I/O Manager defines a single type (DRIVER_CONTROL) for AdapterControl and ControllerControl routines because each is called indirectly, usually from the StartIo routine, when a driver needs to synchronize access to a physical device that might already be busy:

  • When a driver calls IoAllocateAdapterChannel, its AdapterControl routine is run immediately if the system DMA controller or busmaster adapter is available for a DMA operation. Otherwise, the AdapterControl routine is queued until the DMA controller or busmaster adapter is free.

  • When a driver calls IoAllocateController, its ControllerControl routine is run immediately if the hardware represented by the controller object is available for an I/O operation. Otherwise, the ControllerControl routine is queued until the controller is free.

For a ControllerControl routine, the input PVOID MapRegisterBase, shown in the DRIVER_CONTROL declaration, is a system-reserved value.


11.1.2 Returns from DriverControl Routines

An AdapterControl or ControllerControl routine must return a value of type IO_ALLOCATION_ACTION, defined as follows:
typedef enum _IO_ALLOCATION_ACTION {
    KeepObject,
    DeallocateObject,
    DeallocateObjectKeepRegisters
} IO_ALLOCATION_ACTION, *PIO_ALLOCATION_ACTION;

The value DeallocateObjectKeepRegisters is meaningful only for a driver that uses packet-based busmaster DMA.

If a ControllerControl routine completes an IRP or if it can set up an operation, such as a disk seek, for a target device object (disk) that could be overlapped with an operation for another device object, it should return DeallocateObject. Otherwise, it should return KeepObject.


11.1.3 Storage Requirements

If it has an AdapterControl routine, an NT device driver must provide resident storage for the following:

  • Context information to be used in its DMA operations

  • An AdapterObject pointer returned by HalGetAdapter

  • A ULONG-type variable to hold the system-determined maximum NumberOfMapRegisters available for any given DMA transfer request

The driver can provide the necessary storage in a device extension, in a controller extension, or in nonpaged pool allocated by the driver.

If it has a ControllerControl routine, an NT device driver must provide resident storage for the following:

  • A ControllerObject pointer returned by IoCreateController

Any NT device driver can provide the necessary storage in a device extension or in nonpaged pool allocated by the driver. Usually, NT drivers that use controller objects store the ControllerObject pointer in the device extension of each device object that represents a physical or logical device controlled by the hardware represented by the controller object.

The driver writer determines the size and internal structure of the ControllerObject->ControllerExtension.

A controller object, which cannot be given a name, cannot be the target of an I/O request. The hardware it represents usually controls a set of homogeneous devices that are the actual targets of I/O requests.


11.1.4 Setting Up AdapterControl and ControllerControl Routines

NT device drivers must set up associated NT objects for their AdapterControl and/or ControllerControl routines during initialization.

For an AdapterControl routine, the DriverEntry routine must do the following:

  1. Set up the adapter object for the device’s DMA capabilities by filling in a DEVICE_DESCRIPTION structure and calling HalGetAdapter.

  2. Save the AdapterObject pointer and NumberOfMapRegisters returned by HalGetAdapter.

    The platform-specific maximum NumberOfMapRegisters returned by HalGetAdapter or the transfer capabilities of the driver’s device, whichever is more restrictive, determines whether the driver must split up a given transfer request and carry out more than one DMA operation on its device to satisfy that IRP.

The returned AdapterObject pointer, the entry point of the driver’s AdapterControl routine, the DeviceObject pointer representing the target device for the current IRP, a Context pointer to an area already set up for the AdapterControl routine, and a NumberOfMapRegisters value, which can be less than the maximum possible number for smaller transfer requests, must be passed in calls to IoAllocateAdapterChannel. Usually, a device driver’s StartIo (or possibly ControllerControl) routine sets up the area at Context before it calls IoAllocateAdapterChannel.

For a ControllerControl routine, the DriverEntry routine must do the following:

  1. Call IoCreateController to set up the controller object, specifying the driver-determined Size for the controller extension, which the system allocates from nonpaged pool and initializes with zeros.

  2. Save the ControllerObject pointer returned by IoCreateController, usually in the device extension(s) of each device object representing a physical or logical device that is controlled by the hardware represented by the controller object.

  3. Set up and/or initialize the driver-determined contents of the ControllerObject->ControllerExtension.

The returned ControllerObject pointer, the entry point of the driver’s ControllerControl routine, the DeviceObject pointer representing the target device for the current IRP, and a Context pointer to an area already set up for the ControllerControl routine must be passed in the driver’s calls to IoAllocateController. Usually, a device driver’s StartIo routine sets up the area at Context before it calls IoAllocateController.


11.2 AdapterControl Routine Requirements

At a minimum, an AdapterControl routine must do the following:

  1. Save the input MapRegisterBase value, which the driver must pass to IoFlushAdapterBuffers when each DMA transfer operation is complete, and any other context information that the driver needs to carry out one or more DMA transfer operations for the current IRP.

  2. Return the appropriate IO_ALLOCATION_ACTION value:

    • KeepObject if the device is a slave so the driver uses system DMA.

    • DeallocateObjectKeepRegisters if the device is a busmaster so the driver uses packet-based busmaster DMA.

Depending on the driver’s design, its AdapterControl routine also can do the following before it returns control:

  1. Determine the starting location for the transfer on its device.

  2. Calculate the size of the transfer possible, given any limitations of its device due to the starting location of the transfer.

    In general, it is the responsibility of the routine that calls IoAllocateAdapterChannel to determine whether a given transfer request must be split up into partial transfers due to any platform-specific limitations on the NumberOfMapRegisters available for each DMA transfer operation, as mentioned in Section 11.1.4.

  3. Set up any driver-maintained state about each tranfer request in the device (or controller) extension.

    For example, an AdapterControl routine might call KeSetTimer with the entry point for a CustomTimerDpc routine that times out DMA transfer operations for the driver. For more information about CustomTimerDpc routines, see Chapter 14.

  4. Call MmGetMdlVirtualAddress with the MDL at Irp->MdlAddress to get an index for the start of the transfer, suitable for passing to IoMapTransfer.

  5. Call IoMapTransfer to set up the system DMA controller or to obtain a physical-to-logical address mapping for a busmaster device.

  6. Program the driver’s device for a transfer operation by calling KeSynchronizeExecution with a SynchCritSection routine, described in Chapter 10.

If a given transfer request requires the driver to perform a sequence of partial-transfer operations to satisfy the current IRP, usually the driver’s DpcForIsr routine is responsible for reprogramming the device for subsequent transfer operations. An AdapterControl routine is called only once for each incoming transfer IRP.

The driver routine that completes the current transfer IRP, usually the DpcForIsr, also is responsible for releasing the system DMA controller or busmaster adapter by calling IoFreeAdapterChannel or IoFreeMapRegisters, respectively. This driver routine should make the appropriate call as soon as possible when its last partial-transfer operation is done so that NT drivers of slave DMA devices can allocate the system DMA controller or a busmaster driver can begin processing the next transfer IRP promptly.


11.3 ControllerControl Routine Requirements

As its name implies, a ControllerControl routine is associated with an NT controller object. When the ControllerControl routine executes, the hardware represented by the controller object is free and the controller extension generally is not being accessed by another driver routine unless the controller extension contains context that is shared with the driver’s ISR.

Usually, a ControllerControl routine does at least the following:

  1. Updates or initializes whatever context the driver maintains in the device extension of the target device object and in the controller extension

    If the driver uses DMA, its ControllerControl routine usually is responsible for determining whether a given transfer request must be split up into partial transfers due to any system- or device-imposed limitations on the size of each DMA transfer. In these circumstances, the ControllerControl routine also is responsible for calling IoAllocateAdapterChannel if the driver has an AdapterControl routine.

    If the driver uses PIO, its ControllerControl routine also is responsible for splitting large transfer requests, if its hardware requires it, into partial-transfer ranges and for calling MmGetSystemAddressForMdl with the MDL at Irp->MdlAddress.

  2. Programs its hardware for the requested I/O operation

    If the device or controller extension can be accessed from the ISR, the ControllerControl routine must call KeSynchronizeExecution with a SynchCritSection routine, described in Chapter 10, to program the hardware or to set up context shared with the ISR.

If the driver has a Cancel routine, its ControllerControl routine also must check the Irp->Cancel field to determine whether the current IRP should be cancelled, and do either of the following:

  • If Irp->Cancel is set to TRUE, the ControllerControl routine must do the following:

    1. Set STATUS_CANCELLED for Status and zero for Information in the I/O status block of the IRP.

    2. Call IoFreeController to release the controller object so the next device operation can be started promptly.

    3. Call IoStartNextPacket or dequeue the next IRP if the driver manages its own queueing.

    4. Complete the cancelled IRP with IoCompleteRequest and return control.

  • Otherwise, the ControllerControl routine must do the following:

    1. Call IoSetCancelRoutine to reset the Cancel routine entry point for the IRP to NULL. Acquire the cancel spin lock for this call if the driver uses the I/O-manager-supplied device queue in the device object.

    2. Call KeSynchronizeExecution with a driver-supplied SynchCritSection routine to program the hardware for the requested I/O operation.

For more information about Cancel routines and handling cancelable IRPs, see Chapter 12.

For most interrupt-driven I/O operations except overlapped operations on different devices attached to the physical controller/adapter, a ControllerControl routine should return KeepObject because the DpcForIsr or CustomDpc routine completes the operation and the IRP.

As soon as the I/O operation(s) to satisfy the current request are done, the routine that will complete the IRP should call IoFreeController and IoStartNextPacket so that the next request can be processed as quickly as possible.

If the ControllerControl routine itself completes an IRP or if it can set up an operation, such as a disk seek, for one target device object (disk) that could be overlapped with an operation for another device object, the ControllerControl routine should return DeallocateObject.


Chapter 12 Cancel Routines

This chapter contains the following information:
12.1 Cancel Routine Requirements

12.1.1 Cancel Routines in NT Drivers

12.1.2 Cancelable IRPs in Higher-Level Drivers without Cancel Routines

12.2 Handling Cancelable IRPs

12.3 Cancel Routine Functionality

12.4 Points to Consider In Handling Cancelable IRPs

The Cancel routine is defined by the I/O Manager as follows:

VOID
(*PDRIVER_CANCEL) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

This chapter summarizes the required functionality of an NT driver’s standard Cancel routine(s) and guidelines for managing cancelable IRPs in other driver routines.

Two members in the IRP contain key information concerning cancellation:

  • Irp->Cancel indicates whether an IRP is being cancelled or should be cancelled.

  • Irp->CancelRoutine indicates whether an IRP is cancelable. If this member contains a pointer to a cancel routine, then the IRP is cancelable. If this member is NULL, then the IRP is not cancelable. If this member is NULL, but Irp->Cancel is set, that indicates that the cancel routine is running and the IRP is in the process of being cancelled.


12.1 Cancel Routine Requirements

A Cancel routine is called to complete a given IRP for a given target device object after setting the IRP’s I/O status block with STATUS_CANCELLED for Status and zero for Information.

A Cancel routine is always called with the system cancel spin lock held on its behalf. Until it calls IoReleaseCancelSpinLock, this routine runs at DISPATCH_LEVEL IRQL, and it is run in an arbitrary thread context.

Running at IRQL DISPATCH_LEVEL and in an arbitrary thread context restricts the set of support routines a Cancel routine can call.

For more information about managing IRQLs in NT drivers, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-Mode Driver Reference.


12.1.1 Cancel Routines in NT Drivers

Any NT driver in which IRPs can be held pending for an indefinite interval must have one or more Cancel routines.

For example, suppose a user-mode thread makes an I/O request, which is queued by a highest-level device driver’s Dispatch routine, and the requesting thread is terminated while the IRP is queued. IRPs queued on behalf of a terminated thread should be cancelled. Consequently, such a driver must set a driver-supplied Cancel routine in each IRP that it queues.

The number of Cancel routines any NT driver has depends on the driver’s design. In general, an NT driver should have a Cancel routine for each stage in its I/O processing at which an IRP might be held pending for an indefinite interval, which is also called held in a cancelable state.

NT driver writers should consider the following design guidelines:

  • The highest-level driver in a chain of layered NT drivers must have at least one Cancel routine if it queues IRPs or otherwise holds IRPs in a cancelable state. It can have more than one Cancel routine at the driver writer’s discretion.

  • Lower-level NT drivers in which IRPs can be held in a cancelable state for relatively long intervals also should have one or more Cancel routines.

  • If a driver manages its own internal queues of IRPs, it should have a separate Cancel routine for each of its queues.

Certain highest-level NT drivers for interactive devices, such as keyboard, mouse, sound, parallel class and serial drivers, must have Cancel routines. Certain lower-level drivers, such as a parallel port driver that holds IRPs queued for some number of higher-level class drivers for relatively long intervals, also should have Cancel routines.

NT intermediate drivers that are layered over mass-storage device drivers and mass-storage device drivers are unlikely to have Cancel routines. In other words, it is the responsibility of an NT file system driver to handle the cancelation of file I/O requests, while the IRPs input to lower-level mass-storage drivers are usually processed to completion too quickly to be cancelable.


12.1.2 Cancelable IRPs in Higher-Level Drivers without Cancel Routines

The designer of a higher-level NT driver can make no assumptions about whether or how existing lower-level NT drivers handle cancelable IRPs. As soon as any higher-level NT driver calls IoCallDriver with an IRP, it no longer “owns” that IRP and it can neither determine nor control whatever processing lower-level drivers are doing to satisfy that request.

However, any higher-level NT driver can set its IoCompletion routine in an IRP by calling IoSetCompletionRoutine before it calls IoCallDriver. Such a higher-level driver can determine whether any pending IRP is cancelled in a lower driver by calling IoSetCompletionRoutine with the InvokeOnCancel parameter set to TRUE before it passes the IRP on to lower drivers.

A higher-level NT driver can call IoCancelIrp with any pending IRP that the driver allocates. However, making this call does not guarantee that the given driver-allocated IRP will be completed with its I/O status block set to STATUS_CANCELLED. Such a higher-level driver that calls IoCancelIrp can check whether the given IRP was cancelled only in its IoCompletion routine so it must call IoSetCompletionRoutine with the InvokeOnCancel parameter set to TRUE before passing the IRP on to lower drivers. For more information about IoCompletion routines, see Chapter 13.

A higher-level NT driver should not call IoCancelIrp with an IRP that it did not allocate.


12.2 Handling Cancelable IRPs

If an NT driver handles cancelable IRPs, it is responsible for setting the appropriate Cancel routine in each IRP that will be put into a cancelable state as it is processed in stages by the driver’s routines.

The following summarizes guidelines for driver routines that handle cancelable IRPs.

Handling Cancelable IRPs in Driver Routines That Pass IRPs On

If a device driver has a StartIo routine, its Dispatch routines must specify a driver-supplied Cancel routine’s entry point when they call IoStartPacket.

Otherwise, any driver’s Dispatch routine(s) must do the following before queueing an IRP for further processing by other driver routines:

1. Call IoAcquireCancelSpinLock.

  1. Call IoSetCancelRoutine with the input IRP and the entry point for a driver-supplied Cancel routine.

  2. Call IoReleaseCancelSpinLock.

In a similar manner, any routine in such a driver that passes an IRP on for further processing by other routines in the same driver should set up a Cancel routine if that request could be held in a cancelable state before another driver routine begins processing it.

Drivers that manage their own queue(s) of IRPs, rather than using the I/O-manager-supplied device queue, do not need to acquire the cancel spin lock when calling IoSetCancelRoutine.

Handling Cancelable IRPs In Driver Routines That Process Requests

A driver’s StartIo routine, if any, and/or any other routine that dequeues or is called with an IRP that was held in a cancelable state must do the following:

  1. Call IoAcquireCancelSpinLock.

  2. Check the Irp->Cancel field to determine whether to cancel the IRP or to begin processing for the request.

    • If Irp->Cancel is set to TRUE, call IoReleaseCancelSpinLock and return control.

    • Otherwise, do the following:

      1. Call IoSetCancelRoutine with a NULL CancelRoutine pointer to remove the IRP from the cancelable state.

      2. Call IoReleaseCancelSpinLock.

      3. Start the requested device I/O operation for the target device object or, from a higher-level driver, pass the request on to lower drivers.

Any other driver routine that dequeues an IRP or is called with an IRP that might have been held pending for an indefinite interval should do likewise. That is, such a routine must test whether the IRP should be cancelled or remove the IRP from the cancelable state before it begins I/O processing for that IRP.

Drivers that manage their own queue(s) of IRPs, rather than using the I/O-manager-supplied device queue, do not need to acquire the cancel spin lock when calling IoSetCancelRoutine.

In any driver that handles cancelable IRPs, every driver routine that processes an IRP before the underlying device has been programmed for the requested I/O operation should check the cancelable state of all incoming IRPs. That is, a highest-level NT device driver with both StartIo and ControllerControl routines should process incoming IRPs in both these driver routines as already described.

Using the System Cancel Spin Lock

Consider the following implementation guidelines for driver routines that change the state of cancelable IRPs, including all routines that might complete an IRP with STATUS_CANCELLED:

  • In drivers that use the I/O-manager-supplied device queue, any driver routine besides the Cancel routine that changes the cancelable state of an IRP must first call IoAcquireCancelSpinLock to acquire the system cancel spin lock.

    This ensures that only the caller can change the cancelable state of that IRP for the following reasons:

    • It prevents the I/O Manager from calling the driver’s Cancel routine with that IRP.

    • It prevents any other driver routine, such as its DispatchCleanup routine, from simultaneously attempting to change the cancelable state of that IRP.

  • In drivers that manage their own queue(s) of IRPs and synchronize queue access using driver-supplied spin locks, the driver routines do not need to acquire the cancel spin lock when calling IoSetCancelRoutine.

  • Any driver routine that calls IoAcquireCancelSpinLock must call IoReleaseCancelSpinLock as quickly as possible.

  • Never call IoCompleteRequest with an IRP while holding a spin lock.

    Attempting to complete an IRP while holding a spin lock can cause deadlocks.


12.3 Cancel Routine Functionality

The I/O Manager calls a driver-supplied Cancel routine with an input IRP to be cancelled and a DeviceObject pointer representing the target for the request.

Such an IRP might be one that the driver’s DispatchReadWrite routine has queued just as the current Win32 application is being closed by the user. Such an IRP also might be one that a higher-level driver explicitly cancelled, depending on the nature of the underlying device.

When its Cancel routine is called, the input IRP might already be the CurrentIrp in the target device object or might already be in the device queue associated with the target device object if the driver has a StartIo routine. If the driver has no StartIo routine, such a request might be in a driver-managed internal queue of IRPs when its Cancel routine is called. In either case, the I/O Manager resets the Cancel entry point in the incoming IRP to NULL before it calls the Cancel routine with that IRP.

NT driver writers should consider the following implementation guidelines for Cancel routines:

  • Because a Cancel routine is always called with the system cancel spin lock held, this routine must not call IoAcquireCancelSpinLock unless it calls IoReleaseCancelSpinLock first.

  • A Cancel routine cannot be holding the system cancel spin lock when it returns control. That is, every Cancel routine must call IoReleaseCancelSpinLock at least once before it returns control.

  • If it calls IoAcquireCancelSpinLock, a Cancel routine must make the reciprocal call to IoReleaseCancelSpinLock as quickly as possible.

  • Never call IoCompleteRequest with an IRP while holding a spin lock.

    Attempting to complete an IRP while holding a spin lock can cause deadlocks.

Cancel Routines in Drivers with StartIo Routines

The I/O Manager maintains the CurrentIrp field in a device object only if IRPs are queued in the associated device queue object. That is, the driver has a StartIo routine.

In such a driver, a Cancel routine usually does the following:

  1. Tests whether the pointer for the input IRP matches the target device object’s CurrentIrp address

    If these pointers are equivalent, the Cancel routine calls ReleaseCancelSpinLock, sets the IRP’s I/O status block with STATUS_CANCELLED for Status and zero for Information, calls IoStartNextPacket, calls IoCompleteRequest with the IRP, and returns control.

  2. Otherwise, the Cancel routine checks whether the input IRP is in the device queue associated with the target device object by calling KeRemoveEntryDeviceQueue with the IRP’s Tail.Overlay.DeviceQueueEntry pointer

    • If the IRP is in the device queue, calling KeRemoveEntryDeviceQueue removes it from the queue. The Cancel routine calls IoReleaseCancelSpinLock, sets the IRP’s I/O status block with STATUS_CANCELLED for Status and zero for Information, calls IoStartNextPacket, calls IoCompleteRequest with the cancelled IRP, and returns control.

    • If the IRP is not in the device queue, the Cancel routine calls IoReleaseCancelSpinLock and returns control.

Such a driver’s Cancel routine calls KeRemoveEntryDeviceQueue to test whether the IRP is in the device queue because this support routine either removes the given IRP from the device queue or does nothing except return FALSE, indicating that the given entry was not queued. A Cancel routine cannot assume that the input IRP is at any particular position in the device queue, so it cannot call KeRemoveDeviceQueue or KeRemoveByKeyDeviceQueue to compare the pointers to the returned IRP and input IRP.

NT drivers with Cancel routines can handle IRP_MJ_CLEANUP requests as well. For more information about DispatchCleanup routines, see Chapter 6.

Cancel Routines in Drivers without StartIo Routines

The I/O Manager maintains the CurrentIrp field in a device object only if IRPs are queued in the associated device queue object.

If an NT driver manages its own internal queues of IRPs, its Cancel routines can be called with an input IRP that is neither the CurrentIrp for the input target device object nor an IRP in the driver’s internal queue. Such a driver must maintain its own state about which IRP is currently being processed and should have a Cancel routine for each of its queues. Such a driver’s internal queue should be an interlocked queue because its internal queue must be protected by an executive spin lock.

When such a driver’s Cancel routine is called, it usually does the following:

  1. Calls IoReleaseCancelSpinLock

  2. Acquires the spin lock that protects its interlocked queue and walks the queue to find an IRP with Irp->Cancel set to TRUE

    • If it finds such an IRP in the interlocked queue, dequeues it thereby releasing the spin lock protecting the queue, sets the IRP’s I/O status block with STATUS_CANCELLED for Status and zero for Information, starts the next queued IRP, calls IoCompleteRequest with the cancelled IRP, and returns control

    • Otherwise, releases any spin locks it is holding and returns control

      Such a driver usually assumes that I/O processing for the input IRP has already begun if it is not queued.

NT drivers with Cancel routines can handle IRP_MJ_CLEANUP requests as well. For more information about DispatchCleanup routines, see Chapter 6.


12.4 Points to Consider In Handling Cancelable IRPs

Keep the following points in mind when implementing a Cancel routine and handling cancelable IRPs:

  • A Cancel routine is called with the cancel spin lock already held, so it must call IoReleaseCancelSpinLock before it returns control. It must not call IoAcquireCancelSpinLock unless it calls IoReleaseCancelSpinLock first, and it must make a reciprocal call to IoReleaseCancelSpinLock for each call it makes to IoAcquireCancelSpinLock.

  • Unless a driver manages its own internal queue(s) of IRPs, its Cancel routine is called with an incoming IRP that could be either of the following:

    • The CurrentIrp in the input target device object

    • An entry in the device queue associated with the target device object

  • Unless a driver manages its own internal queues of IRPs, its Cancel routine should call KeRemoveEntryDeviceQueue with the input IRP to test whether it is an entry in the device queue associated with the target device object. Such a driver’s Cancel routine cannot call KeRemoveDeviceQueue or KeRemoveByKeyDeviceQueue because it cannot assume that the given IRP is at any particular position in the device queue.

  • If a driver does manage its own internal queue of IRPs and has a Cancel routine, the queue must be protected by an executive spin lock. Such a queue should be an interlocked queue so the driver can use the ExInterlocked..List routines.

  • If a Cancel routine is called with an IRP for which the driver has already started I/O processing and the request will be completed soon, the Cancel routine should release the system cancel spin lock and return control.

  • Provided that the current state of the input IRP is pending, a Cancel routine must do the following:

    1. Set the input IRP’s I/O status block with STATUS_CANCELLED for Status and zero for Information.

    2. Release any spin locks it is holding, including the system cancel spin lock.

    3. Call IoCompleteRequest with the given IRP.

  • Any driver routine that passes IRPs on for further processing by other driver routines when an IRP might be held in a cancelable state must call IoSetCancelRoutine to set its entry point for the Cancel routine in the IRP. Only then can that driver routine call any support routine that causes the IRP to be held in a cancelable state, such as IoStartPacket, IoAllocateController, or an ExInterlockedInsert..List routine.

  • Any driver routine that subsequently processes cancelable IRPs must check whether an IRP has already been cancelled before it begins operations to satisfy the request. Such a routine must call IoSetCancelRoutine to reset its entry point for the Cancel routine to NULL in the . Only then can that routine begin its I/O processing for the input IRP.

    Note that such a routine might have to reset the entry point for a Cancel routine in an IRP if it, too, passes IRPs on for further processing by other driver routines and those IRPs might be held in a cancelable state.

  • Any higher-level driver that holds IRPs in a cancelable state must reset its Cancel entry point to NULL before it passes an IRP on to the next-lower driver with IoCallDriver.

  • Any higher-level driver can call IoCancelIrp with an IRP that it has allocated and passed on for further processing by lower-level driver(s). However, such a driver cannot assume that the given IRP will be completed with STATUS_CANCELLED by lower driver(s).

  • A driver can (or must, depending on its design) maintain additional state information in its device extension to track the cancelable status of IRPs. If this state is shared by driver routines running at IRQL <= DISPATCH_LEVEL, the shared data can be protected with a driver-allocated and initialized executive spin lock.

    Such a driver should manage its acquisitions and releases of the system cancel spin lock and its own executive spin lock(s) carefully, and it should be designed to hold the system cancel spin lock for the shortest possible interval(s).

  • If a device driver maintains state information about cancelable IRPs that various driver routines share with its ISR, these other routines must synchronize access to the shared state with the ISR. Only a driver-supplied SynchCritSection routine can access such shared-with-the-ISR state in a multiprocessor-safe way.

For more information about using spin locks, see Chapter 16. See Chapters 8 and 10, respectively, for more information about ISRs and SynchCritSection routines.


Chapter 13 IoCompletion Routines

This chapter contains the following information:
13.1 Using IoCompletion Routines

13.1.1 Setting Up an IoCompletion Routine

13.1.2 Calling IoSetCompletionRoutine

13.1.3 Allocating IRPs for Lower Drivers

13.2 IoCompletion Routine Required Functionality

The IoCompletion routine is defined by the I/O Manager as follows:

NTSTATUS
(*PIO_COMPLETION_ROUTINE) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    );

This chapter summarizes the required functionality of an NT driver’s standard IoCompletion routine(s) and guidelines for setting up IRPs to be processed by IoCompletion routines.


13.1 Using IoCompletion Routines

At the discretion of the driver designer, any higher-level NT driver can have as many IoCompletion routines as the driver needs to complete IRPs for various kinds of operations.

An IoCompletion routine is run at IRQL <= DISPATCH_LEVEL and in an arbitrary thread context. A driver writer cannot make assumptions about the IRQL at which an IoCompletion routine will be called.

Consider the following a design guideline for IoCompletion routines

Assume that every IoCompletion routine always runs at IRQL DISPATCH_LEVEL.

Running at IRQL DISPATCH_LEVEL restricts the set of support routines an IoCompletion routine can call.

For more information about managing IRQLs in NT drivers, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-mode Driver Reference.


13.1.1 Setting Up an IoCompletion Routine

Every higher-level NT driver’s Dispatch routines are generally responsible for the following:

  1. Checking the validity of parameters in their own I/O stack location of incoming IRPs and failing those requests with invalid parameters

  2. Sending IRPs with valid parameters and those for which the Dispatch routine cannot determine the validity on to lower drivers with IoCallDriver

    For more detailed information about higher-level drivers’ Dispatch routines, see Chapter 6.

Such a Dispatch routine can call IoSetCompletionRoutine with the entry point of a driver-supplied IoCompletion routine and with any IRP that it will pass on to lower drivers with IoCallDriver.

Such a Dispatch routine can specify whether the given IoCompletion routine will be called when lower drivers complete the given IRP with any one, two, or all of the following NTSTATUS values when it calls IoSetCompletionRoutine:

  • STATUS_SUCCESS indicating that all lower-level drivers completed the requested operation successfully

  • STATUS_CANCELLED indicating that the IRP was cancelled in a lower driver

  • STATUS_XXX indicating that a lower driver encountered an error and failed the IRP

The purpose of an IoCompletion routine is to monitor what lower-level drivers did with a given IRP and/or to do any necessary cleanup processing for an IRP that the higher-level driver has passed on for processing by lower drivers.

For more information about NTSTATUS and I/O errors, see Chapter 16.

For specific information about any of the support routines mentioned in this chapter, see the Kernel-mode Driver Reference.


13.1.2 Calling IoSetCompletionRoutine

The most common uses for higher-level drivers’ IoCompletion routines are the following:

  • To dispose of IRPs that a higher-level driver has allocated with IoAllocateIrp or IoBuildAsynchronousFsdRequest to send requests to lower drivers

    Any higher-level driver that allocates an IRP with either of these support routines must set its IoCompletion routine in such an IRP. The IoCompletion routine must call IoFreeIrp to dispose of these driver-allocated IRPs.

  • To reuse an incoming IRP to request that lower drivers complete some number of operations, such as partial transfers, until the original request can be satisfied and completed by the IoCompletion routine

  • To retry a request that a lower driver completed with an error

    Highest-level NT drivers, such as file systems, are more likely to have IoCompletion routines that attempt to retry requests for which lower-level drivers have returned an error status than are intermediate drivers, except possibly class drivers layered above a closely coupled port driver. Nevertheless, any NT intermediate driver can retry requests from its IoCompletion routine(s) at the discretion of the driver designer.

In other words, an NT highest-level or intermediate driver’s DispatchReadWrite routine usually determines whether a given IRP requires the driver to set up an IoCompletion routine. At the discretion of the driver designer, such a driver’s DispatchDeviceControl or other Dispatch routine(s) also can set up an IoCompletion routine for any given IRP that is passed on to lower drivers.

For driver-allocated and reused IRPs, the Dispatch routine must call IoSetCompletionRoutine with the following Boolean parameters:

  • InvokeOnSuccess set to TRUE

  • InvokeOnError set to TRUE

  • InvokeOnCancel set to TRUE if any lower driver in the chain might handle cancelable IRPs

    Usually, InvokeOnCancel is set to TRUE, whether an IRP might be returned with STATUS_CANCELLED or not, to ensure that the IoCompletion routine frees each driver-allocated IRP or checks the completion status of each reuse of an IRP.

    Consider the following implementation guidelines for calling IoSetCompletionRoutine in higher-level drivers’ Dispatch routines

  • Any Dispatch routine that allocates IRPs for lower drivers with IoAllocateIrp or IoBuildAsynchronousFsdRequest must set an IoCompletion routine in each driver-allocated IRP.

    • The Dispatch routine must set up state about the original IRP and the IRP(s) it allocated for the IoCompletion routine to use. At a minimum, the IoCompletion routine needs access to the original IRP and a count of how many additional IRPs were allocated.

    • The Dispatch routine should call IoSetCompletion Routine with all InvokeOnXxx parameters set to TRUE for the IRP(s) it allocates.

    • If it allocates an I/O stack location of its own in an IRP, the Dispatch routine must call IoSetNextIrpStackLocation before it calls IoGetCurrentIrpStackLocation to set up context in its own I/O stack location for the IoCompletion routine, as mentioned in Chapter 6.

    • The Dispatch routine must call IoMarkIrpPending with the original IRP, but not with any driver-allocated IRPs because the IoCompletion routine will free them.

    • If the Dispatch routine is allocating IRPs for partial transfers and the underlying device driver might control a removable-media device, the Dispatch routine must set up the thread context in its newly allocated IRPs from the value at Tail.Overlay.Thread in the original IRP.

      Such an underlying removable-media device driver might call IoSetHardErrorOrVerifyDevice, which references the pointer at Irp->Tail.Overlay.Thread, with a driver-allocated IRP. If the underlying device driver calls this support routine, the file system driver can send a popup to the appropriate user thread that prompts the user to cancel, retry, or fail the operation that the device driver could not satisfy.

      For more information about handling removable media, see Chapter 16.

    • The Dispatch routine must return STATUS_PENDING after it has sent all driver-allocated IRPs on to lower drivers.

    The driver’s IoCompletion routine should free all driver-allocated IRPs with IoFreeIrp before it calls IoCompleteRequest with the original IRP. When it completes the original IRP, the IoCompletion routine must free all driver-allocated IRPs before it returns control.

  • Any Dispatch routine must call IoSetCompletionRoutine with an IRP to be reused for a sequence of operations or if the IoCompletion routine can retry requests.

    • The Dispatch routine must set up state about the original IRP for the IoCompletion routine to use.

      For example, a DispatchReadWrite routine must save the relevant transfer parameters of an input IRP for the IoCompletion routine before the DispatchReadWrite routine sets up a partial tranfer for the next-lower driver in that IRP, particularly if the DispatchReadWrite routine modifies any parameters that the IoCompletion routine needs to determine when the original request has been satisfied.

      If the IoCompletion routine can retry the request, the Dispatch routine must set up a driver-determined upper limit on how many retries its IoCompletion routine should attempt before it completes the original IRP with an error.

    • For a to-be-reused IRP, the Dispatch routine should call IoSetCompletion Routine with all InvokeOnXxx parameters set to TRUE.

    • For an asynchronous request, the Dispatch routine of any intermediate driver must call IoMarkIrpPending with the original IRP.

    • For an asynchronous request, the Dispatch routine of any intermediate driver must return STATUS_PENDING after it has sent the IRP on to lower drivers.

    The driver’s IoCompletion routine must maintain state about each reuse of an IRP until the original request is satisfied (or failed) and then call IoCompleteRequest with the IRP. The IoCompletion routine also might be responsible for reinitializing the common state area, or notifying another driver routine to dequeue the next IRP to be processed.

    An IoCompletion routine that retries IRPs must maintain the retry count to it can determine when to fail an IRP if the requested operation cannot be completed successfully.

  • Any Dispatch routine that allocates per-IRP resources for a type of request that it passes on to lower drivers must call IoSetCompletionRoutine with any IRP of that type.

    For example, if the Dispatch routine allocates an MDL with IoAllocateMdl and calls IoBuildPartialMdl for a partial-transfer IRP it allocates, the IoCompletion routine must release the MDL with IoFreeMdl. If it allocates resources to maintain state about the original IRP, it must free those resources, preferably before it calls IoCompleteRequest with the original IRP and definitely before it returns control.

    In general, an IoCompletion routine should free any per-IRP resources such a Dispatch routine allocated before the corresponding IRP is freed or completed. Otherwise, the driver must maintain state about the resources to be freed before its IoCompletion routine returns control from completing the original request.

  • At the discretion of the driver writer, a Dispatch routine can call IoSetCompletionRoutine with any other IRP that it passes on to lower drivers.


13.1.3 Allocating IRPs for Lower Drivers

An NT highest-level or intermediate driver’s DispatchRead and/or DispatchWrite routine is most likely to set an IoCompletion routine in an IRP because transfer requests must be handled asynchronously by lower-level drivers, as discussed in Chapter 6. To allocate an IRP for an asynchronous request, which will be processed in an arbitrary thread context by lower drivers, such a DispatchReadWrite routine can call one of the following support routines:

  • IoAllocateIrp, which allows the caller to allocate an I/O stack location for itself in the IRP and initializes all I/O stack locations in the IRP with zeros

    The Dispatch routine must set up the next-lower driver’s I/O stack location in the newly allocated IRP, usually by copying (possibly modified) information from its own stack location in the original IRP. If a higher-level driver allocates an I/O stack location of its own in such an IRP, the Dispatch routine can set up per-request context information there for the IoCompletion routine to use.

  • IoBuildAsynchronousFsdRequest, which sets up the next-lower driver’s I/O stack location for the caller, according to caller-specified parameters

    Higher-level drivers can call this routine to allocate IRPs for IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_FLUSH_BUFFERS, and IRP_MJ_SHUTDOWN requests.

    When an IoCompletion routine is called with such an IRP, it can check the I/O status block, and if necessary (or possible) set up the next-lower driver’s I/O stack location in the IRP again and retry the request or reuse it. However, the IoCompletion routine has no local context storage for itself in such an IRP, so the driver must maintain context about the original request elsewhere in resident memory.

  • IoMakeAssociatedIrp, which allows a highest-level driver to allocate an I/O stack location for itself in the IRP, but callers very seldom set an IoCompletion routine in such an IRP

    NT intermediate drivers cannot call IoMakeAssociatedIrp to create IRPs for lower drivers. Any highest-level driver that calls IoMakeAssociatedIrp to create IRP(s) for lower drivers can return control to the NT I/O Manager after sending its associated IRP(s) on and calling IoMarkIrpPending with the master (original) IRP. Such a driver can rely on the I/O Manager to complete the master IRP when all associated IRPs have been completed by lower drivers.

    If such a highest-level driver calls IoSetCompletionRoutine for an associated IRPs it creates, the I/O Manager does not complete the master IRP if the driver returns STATUS_MORE_PROCESSING_REQUIRED from its IoCompletion routine. In these circumstances, the driver’s IoCompletion routine must explicitly complete the master IRP with IoCompleteRequest.

A higher-level driver’s DispatchDeviceControl routine can allocate an IRP with IoBuildDeviceIoControlRequest. It can set up an IoCompletion routine for such an IRP but is unlikely to do this. If the driver allocates resident storage for and initializes an event object, its DispatchDeviceControl routine can wait on an event when it sends on driver-allocated IRPs for inherently synchronous device control requests, as mentioned in Chapter 6.

Usually, a higher-level driver would not set its IoCompletion routine in an IRP allocated with IoBuildSynchronousFsdRequest for the same reason. For more information about restrictions on calls to this support routine from Dispatch routines, see also Chapter 6.

Each higher-level NT driver sets up any driver-allocated (and reused) IRPs for lower drivers in such a way that it is immaterial to the underlying device driver whether a given request comes from an NT intermediate driver or originates from any other source, such as an NT file system or user-mode application.


13.2 IoCompletion Routine Required Functionality

On entry, an IoCompletion routine is called with DeviceObject, Irp, and Context pointers, as shown by the declaration. The Dispatch routine that called IoSetCompletionRoutine can pass a Context pointer to whatever driver-determined context it set up for the IoCompletion routine to use in processing the given IRP. Note that such a context area cannot be pageable because the IoCompletion routine can be called at IRQL DISPATCH_LEVEL.

When it is called, an IoCompletion routine is responsible for doing whatever additional IRP processing the driver writer chooses and any necessary cleanup operations for the request, as determined by how the Dispatch routine set up the request.

Consider the following implementation guidelines for IoCompletion routines

  • If the input IRP was allocated by the Dispatch routine with IoAllocateIrp or IoBuildAsynchronousFsdRequest, the IoCompletion routine must call IoFreeIrp to release that IRP, preferably before it completes the original IRP.

    • The IoCompletion routine must release any per-IRP resources the Dispatch routine allocated for such a driver-allocated IRP, preferably before it frees the corresponding IRP.

      For example, if the Dispatch routine allocated an MDL for a partial-transfer IRP that the IoCompletion routine is processing, the IoCompletion routine must call IoFreeMdl to free that MDL before it returns control from completing the original request.

    • If the IoCompletion routine cannot complete the original IRP with STATUS_SUCCESS, it must set the I/O status block in the original IRP to the value returned in the driver-allocated IRP that caused the IoCompletion routine to fail the original request.

    • If the IoCompletion routine will complete the original request with STATUS_PENDING, it must call IoMarkIrpPending with the original IRP before it calls IoCompleteRequest.

    • If the IoCompletion routine must fail the original IRP with an error STATUS_XXX, it can log an error. However, it is the responsibility of the underlying device driver to log any device I/O errors that occur, so IoCompletion routines usually do not log errors.

      For more information about logging I/O errors, see Chapter 16.

    • When it has processed such a driver-allocated IRP and freed it, the IoCompletion routine must return control with STATUS_MORE_PROCESSING_REQUIRED.

      Returning STATUS_MORE_PROCESSING_REQUIRED from the IoCompletion routine forestalls the I/O Manager’s completion processing for a driver-allocated and freed IRP.

  • If the IoCompletion routine reuses an incoming IRP to send one or more requests to lower drivers, it should update whatever context the IoCompletion routine maintains about each reuse (or retry) of the IRP before it sets up the next-lower driver’s I/O stack location again, calls IoSetCompletionRoutine with its own entry point, and calls IoCallDriver with the IRP.

    • The IoCompletion routine should not call IoMarkIrpPending at each reuse or retry of the IRP.

      The Dispatch routine already marked the original IRP as pending. Until all drivers in the chain complete the original IRP with IoCompleteRequest, it remains pending.

    • Before it retries a request, the IoCompletion routine should reset the I/O status block with STATUS_SUCCESS for Status and zero for Information, possibly after saving the returned error information.

      For each retry, the IoCompletion routine usually decrements a count set up by the Dispatch routine because the IoCompletion routine must fail such an IRP with IoCompleteRequest when some limited number of retries have failed.

    • The IoCompletion routine must return STATUS_MORE_PROCESSING_REQUIRED after it calls IoSetCompletionRoutine and IoCallDriver with an IRP that is being reused or retried.

      Returning STATUS_MORE_PROCESSING_REQUIRED from the IoCompletion routine forestalls the I/O Manager’s completion processing of a reused or retried IRP.

    • If the IoCompletion routine cannot complete the original IRP with STATUS_SUCCESS, it must leave the I/O status block as returned by lower drivers for the reuse or retry operation that causes the IoCompletion routine to fail the IRP.

    • If the IoCompletion routine will complete the original request with STATUS_PENDING, it must call IoMarkIrpPending with the original IRP before it calls IoCompleteRequest.

    • If the IoCompletion routine must fail the original IRP with an error STATUS_XXX, it can log an error. However, it is the responsibility of the underlying device driver to log any device I/O errors that occur, so IoCompletion routines usually do not log errors.

      For more information about logging I/O errors, see Chapter 16.

  • The IoCompletion routine must release any resources the Dispatch routine allocated for processing the original IRP, preferably before the IoCompletion routine calls IoCompleteRequest with the original IRP and definitely before the IoCompletion routine returns control from completing the original IRP.

If any still-higher-level driver has set its IoCompletion routine in the original IRP, that driver’s IoCompletion routine is not called until all lower-level drivers have called IoCompleteRequest with the original IRP.


Chapter 14 IoTimer and CustomTimerDpc Routines

This chapter contains the following information:
14.1 Timer Routine Requirements

14.1.1 IoTimer Routine Requirements

14.1.1.1 Setting Up an IoTimer Routine

14.1.1.2 Setting Up Context for an IoTimer Routine

14.1.2 CustomTimerDpc Routine Requirements

14.1.2.1 Setting Up a CustomTimerDpc Routine

14.1.2.2 Setting Up Context for a CustomTimerDpc Routine

14.2 Using Timer Routines
14.2.1 Using an IoTimer Routine

14.2.2 Using CustomTimerDpc Routines

The IoTimer routine is defined by the I/O Manager as follows:

VOID
(*PIO_TIMER_ROUTINE) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PVOID Context
    );

The CustomTimerDpc routine is defined by the Kernel as follows:

VOID
(*PKDEFERRED_ROUTINE) (
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,    // reserved for system use
    IN PVOID SystemArgument2     // reserved for system use
    );

This chapter summarizes the requirements for NT drivers’ standard IoTimer and/or CustomTimerDpc routines and guidelines for using these routines.

For more information about timer objects, with and without associated DPC routines, see also Chapter 3.


14.1 Timer Routine Requirements

Any NT driver can have as many IoTimer routines as the device objects it creates and any number of CustomTimerDpc routines.

NT drivers can use these routines to monitor whether an I/O operation timed out, to restart device operations that have timed out, to log errors when an operation has timed out or a device error has occurred, or for any other purpose the driver designer chooses.

An IoTimer or CustomTimerDpc routine runs at IRQL DISPATCH_LEVEL, and it is run in an arbitrary thread context.

Running at IRQL DISPATCH_LEVEL restricts the set of support routines an IoTimer or CustomTimerDpc routine can call.

For more information about managing IRQLs in NT drivers, see Chapter 16. For specific information about the IRQL(s) at which any particular support routine can be called, see the Kernel-Mode Driver Reference.


14.1.1 IoTimer Routine Requirements

Every IoTimer routine is associated with a driver-created device object. Consequently, an NT driver can have a single IoTimer routine for all its device objects, a separate IoTimer routine for each of its device objects, or some number of IoTimer routines associated with nonintersecting subsets of the driver’s device objects, at the discretion of the driver designer.

An IoTimer routine is called approximately once per second after the driver calls IoStartTimer with the DeviceObject pointer to an associated device object unless the driver disables the timer for that device object with a call to IoStopTimer using the same DeviceObject pointer. The driver can re-enable the timer with another call to IoStartTimer, so that its IoTimer routine again will be called automatically at one-second intervals for a given device object.

Frequently, an NT device driver calls IoStartTimer with the DeviceObject pointer in its I/O stack location of the current IRP.

Do not call IoStopTimer from within a driver’s IoTimer routine.


14.1.1.1 Setting Up an IoTimer Routine

The DriverEntry routine must set up each IoTimer routine as follows:

  1. Call IoCreateDevice first to set up the device object(s) with which the IoTimer routine will be associated.

  2. Call IoInitializeTimer with the entry point for the IoTimer routine, a Context pointer to any driver-determined context area the IoTimer routine will use, and a DeviceObject pointer for the target device on which an operation could time out.

Calling IoInitializeTimer registers the driver’s IoTimer routine for the given device object.

For more information about creating device objects and about the timer object associated with an IoTimer routine, see Chapter 3. See also the Kernel-Mode Driver Reference for details about IoInitializeTimer.


14.1.1.2 Setting Up Context for an IoTimer Routine

The Context passed to IoInitializeTimer points to a context area where other driver routines and the IoTimer routine can maintain state about timed operations. This Context pointer is passed to the IoTimer routine whenever it is called, as shown by the declaration.

Such a context area must be in resident memory allocated by the driver. Usually, this context area is in the associated device object’s device extension. It can be in a controller extension if the driver uses a controller object or in nonpaged pool allocated by the driver. The contents of the context area are driver-determined.

If it shares a context area with the driver’s ISR, the IoTimer routine must call KeSynchronizeExecution with a driver-supplied SynchCritSection routine in order to access the shared context safely.

If the IoTimer routine shares a context area only with nonISR driver routine(s), the shared area at Context must be protected by an executive spin lock.

For more information about SynchCritSection routines, see Chapter 10. For more information about using executive and interrupt spin locks, see Chapter 16.


14.1.2 CustomTimerDpc Routine Requirements

The granularity of a Kernel-defined timer object interval is around 10 milliseconds, depending on the system clock rate. Consequently, a CustomTimerDpc can be called at driver-determined variable intervals and at finer-grained intervals than an IoTimer routine.

A CustomTimerDpc routine is called once after the expiration time arrives that was specified when the driver called KeSetTimer. If the driver set the timer with KeSetTimerEx and specified a Period, the CustomTimerDpc routine is called repeatedly at the specified interval.

A driver can call KeCancelTimer to disable an already set timer object and cancel a call to its CustomTimerDpc routine, provided its call to KeCancelTimer occurs before the expiration of the most recent interval it passed to KeSetTimer or KeSetTimerEx.

For more information about managing calls to KeSetTimer, KeSetTimerEx, and KeCancelTimer, see also Section 14.2.2.


14.1.2.1 Setting Up a CustomTimerDpc Routine

At a minimum, any NT driver that has CustomTimerDpc routines must provide resident storage for at least one timer object (of type KTIMER) and resident storage for some number, determined by how many DPC routines that driver has, of DPC objects (of type KDPC).

With very few exceptions, NT drivers that have CustomTimerDpc routines should provide storage for both a timer object and a DPC object for each CustomTimerDpc routine the driver has.

The DriverEntry routine must set up each CustomTimerDpc routine as follows:

  1. Call KeInitializeDpc to set up the DPC object with the CustomTimerDpc routine and a DeferredContext pointer to the driver-determined context area.

  2. Call KeInitializeTimer or KeInitializeTimerEx to set up the timer object to be associated with the CustomTimerDpc routine.

In other words, setting up a CustomTimerDpc routine is exactly like setting up a CustomDpc routine with an additional step to initialize the timer object. A CustomTimerDpc routine is not associated with a particular timer object until the driver calls KeSetTimer or KeSetTimerEx with the Dpc pointer to the corresponding DPC object and Timer pointer to that timer object.

Note also that a driver cannot pass additional context pointers to its CustomTimerDpc routine in calls to KeSetTimer or KeSetTimerEx, while a device driver’s ISR can pass two SystemArgument pointers when it queues a CustomDpc routine with KeInsertQueueDpc.

For more information about CustomDpc routines, see Chapter 9. For more information about setting up timer objects and DPCs, see also Chapter 3. For specific information about any support routine mentioned in this chapter, see the Kernel-Mode Driver Reference.


14.1.2.2 Setting Up Context for a CustomTimerDpc Routine

The DeferredContext passed to KeInitializeDpc points to a context area where other driver routines and the CustomTimerDpc routine can maintain state. This DeferredContext pointer is passed to the DPC routine whenever it is called, as shown by the declaration.

A CustomTimerDpc has no particular associations with a driver-created device object. However, a driver can associate a CustomTimerDpc routine with a driver-created device object by passing a DeferredContext pointer to the device object when it calls KeInitializeDpc.

Such a context area must be in resident memory allocated by the driver. Usually, this context area is in a device extension. It can be in a controller extension if the driver uses a controller object or in nonpaged pool allocated by the driver. The contents of the context area are driver-determined.

If a CustomTimerDpc routine shares context information with the driver’s ISR, the CustomTimerDpc routine must call KeSynchronizeExecution with a SynchCritSection routine in order to access the shared context safely.

If the CustomTimerDpc shares the context information with other nonISR driver routine(s), the area at DeferredContext must be protected by an executive spin lock.

For more information about SynchCritSection routines, see Chapter 10. For more information about using executive and interrupt spin locks, see Chapter 16.


14.2 Using Timer Routines

An NT driver writer can use an IoTimer or CustomTimerDpc routine for any purpose determined by that driver’s design, by the underlying device, or by the kinds of operations that driver must handle.

In general, a CustomTimerDpc routine should be used to time operations that require variable intervals or finer-grained intervals than once per second, while an IoTimer routine is easier to use if it serves the intended purpose.


14.2.1 Using an IoTimer Routine

Any NT driver can have an IoTimer routine for any purpose the driver writer chooses. While the timer for the associated device object is enabled, the IoTimer routine is called approximately once per second.

The I/O Manager uses Kernel-defined timer object(s) to make calls to NT drivers’ IoTimer routines. Consequently, the intervals at which any particular IoTimer routine is called ultimately depends on resolution of the system clock, so driver writers should not assume that an IoTimer routine will be called precisely on a one-second boundary.

Perhaps the most common use for an IoTimer routine is to time-out device I/O operations for the current IRP. Consider the following scenario for using an IoTimer routine for this purpose as a running timer within an NT device driver:

  1. The DriverEntry routine initializes a timer counter in the device extension to minus one, indicating no current device I/O operations, and calls IoStartTimer just before it returns STATUS_SUCCESS.

    Each time the IoTimer routine is called, it checks whether the timer counter is minus one, and, if so, returns control.

  2. The driver’s StartIo routine initializes the timer counter in the device extension to an upper limit, plus an additional second in case the IoTimer routine has just been run, before it calls KeSynchronizeExecution with a SynchCritSection_1 routine to program the physical device for the operation requested by the current IRP.

  3. Each time the IoTimer routine is called, it checks whether the timer counter has been reset by the ISR to minus one, and, if so, returns control. If not, the IoTimer routine calls KeSynchronizeExecution with a SynchCritSection_2 routine that adjusts the timer counter by some driver-determined number of seconds.

  4. The SynchCritSection_2 routine returns TRUE to the IoTimer routine as long as the current request has not yet timed out. If the timer counter goes to zero, the SynchCritSection_2 routine resets the timer counter to a driver-determined reset-time-out value, sets a reset-expected flag for itself (and for the DpcForIsr) in its context area, attempts to reset the device, and returns TRUE.

    Note that the SynchCritSection_2 routine will be called again if its reset operation also times out on the device, when it returns FALSE. If its reset succeeds, the DpcForIsr routine determines that the device has been reset from the reset-expected flag and retries the request, repeating the actions of the StartIo routine as described in Step 2.

  5. If the SynchCritSection_2 routine returns FALSE, the IoTimer routine assumes the physical device is in an unknown state because an attempt to reset it has already failed. In these circumstances, the IoTimer routine queues a CustomDpc routine and returns. This CustomDpc routine logs a device I/O error, calls IoStartNextPacket, fails the current IRP, and returns.

If this device driver’s ISR resets the shared timer counter as described in Step 3 to minus one, the driver’s DpcForIsr routine completes the interrupt-driven I/O processing of the current IRP. That is, this device I/O operation has not timed out, so the IoTimer routine makes no attempt to change the state of the timer counter.

Note that the preceding SynchCritSection_2 routine simply decrements the timer counter that this driver maintains in the device extension under most circumstances. Only if the current I/O operation has timed out, indicated when the timer counter goes to zero, does this routine attempt to reset the device. Only if an attempt to reset the device has already failed does the SynchCritSection_2 routine return FALSE to the IoTimer routine.

Consequently, both the preceding IoTimer routine and its helper SynchCritSection_2 routine take very little time to execute under normal circumstances. By using an IoTimer routine in this manner, such a device driver provides insurance that each valid device I/O request can be retried, if necessary, and that such an IRP will be failed by a CustomDpc routine only if an uncorrectable hardware failure prevents it from being satisfied. Moreover, the driver provides this functionality at very little cost in execution time.

However, the simplicity of the preceding scenario depends on a device that does only one operation at a time and on a driver that does not normally overlap I/O operations. A driver that carried out overlapped device I/O operations, or a higher-level driver that used an IoTimer routine to time out a set of driver-allocated IRPs sent to more than one chain of lower drivers, would have more complex time-out scenarios to manage.


14.2.2 Using CustomTimerDpc Routines

As already mentioned in Section 14.1.2, an NT driver must call KeSetTimer or KeSetTimerEx with pointers to its timer and DPC objects and with an expiration time to have the driver’s CustomTimerDpc routine queued for execution when the given DueTime arrives.

The expiration time is specified in units of 100 nanoseconds, with negative values indicating an interval relative to the current system time. When DueTime arrives, the CustomTimerDpc routine can be called.

The elapsed time until a driver’s CustomTimerDpc routine begins execution can be somewhat longer than the DueTime interval specified when the driver called KeSetTimer or KeSetTimerEx. The NT Kernel queues the given Dpc at the expiration of the timer interval.

If the system time changes before a timer expires, relative timers are not affected but the system adjusts absolute timers. A relative timer always expires after the specified number of time units elapses, regardless of the absolute system time. An absolute timer expires at a specific system time, so a change in the system time changes the wait duration of an absolute timer.

A driver can associate a CustomTimerDpc routine with a timer in the call to KeSetTimer and the routine will be called after the timer expires. To set a recurring timer use KeSetTimerEx which accepts the same parameters as KeSetTimer plus an additional Period parameter.

If a driver routine calls KeSetTimer or KeSetTimerEx with a Dpc pointer for its CustomTimerDpc routine, a subsequent call to KeCancelTimer with the same Timer pointer has one of the following effects:

  • If the most recently specified interval passed to KeSetTimer or KeSetTimerEx has not yet expired, the timer object and the DPC object representing the CustomTimerDpc routine are dequeued and disabled. In other words, the call to KeCancelTimer cancels the call to the driver’s CustomTimerDpc routine.

  • If the most recently specified interval passed to KeSetTimer or KeSetTimerEx is just about to expire when KeCancelTimer is called, the CustomTimerDpc might or might not be called, depending on whether a system clock interrupt causes the timer object to be removed from the timer queue before KeCancelTimer executes.

Calling KeSetTimer or KeSetTimerEx with the same Timer and Dpc pointers before the previously specified interval expires has all of the following effects:

  • The new call to KeSetTimer or KeSetTimerEx implicitly cancels the previously specified DueTime for the already queued timer object.

  • The new DueTime is set in the timer object.

  • For KeSetTimer, the CustomTimerDpc routine is called only once, after the most recently specified DueTime has expired. For a periodic timer set with KeSetTimerEx, one instance of the timer (and thus one execution of the CustomTimerDpc routine) is deleted.

Note that using the same timer object for different purposes can cause race conditions or serious driver errors. For example, if the same Timer pointer is used both to set up a call to a CustomTimerDpc routine and to set up waits in a driver-dedicated thread, such a thread would cancel calls to the CustomTimerDpc routine at random whenever that thread called KeSetTimer (or KeSetTimerEx or KeCancelTimer) with the common Timer pointer and the timer object was already queued for a CustomTimerDpc call.

For drivers that have CustomTimerDpc routines and wait on timer objects in a nonarbitrary thread context:

  • Never use a thread-context-sensitive timer object in a nonarbitrary thread context and vice versa.

  • Each CustomTimerDpc should have its own associated timer object. Each set of driver threads and/or driver routines that are called in a nonarbitrary thread context should have its own set of “waitable” timer objects.

A driver writer who uses a CustomTimerDpc routine should exercise care in choosing the interval(s) the driver passes in calls to KeSetTimer or KeSetTimerEx. In addition, the driver writer must consider all possible effects of a call to KeCancelTimer with the same timer object from any driver routine that makes this call, particularly in Windows NT® SMP platforms.

NT driver writers should always keep in mind the following fact about their CustomTimerDpc and/or CustomDpc routines

Only one instantiation of a DPC object representing a particular DPC routine can be queued for execution at any given moment.

If two driver routines call KeSetTimer or KeSetTimerEx to run the same CustomTimerDpc routine and the interval specified by the first caller has not expired, that CustomTimerDpc routine is run only after the interval specified by the second caller expires. In these circumstances, the CustomTimerDpc does none of the work for which the first routine called KeSetTimer or KeSetTimerEx.

For drivers that have CustomTimerDpc routines and use periodic timers:

A driver cannot deallocate a periodic timer from a DPC routine. Drivers can deallocate nonperiodic timers from a DPC routine.

Consider the following a design guideline for NT drivers that have both CustomDpc and CustomTimerDpc routines

To prevent race conditions, never pass the same Dpc pointer to KeSetTimer or KeSetTimerEx and KeInsertQueueDpc.

In other words, if the StartIo routine calls KeSetTimer or KeSetTimerEx to run a CustomTimerDpc and the ISR calls KeInsertQueueDpc simultaneously from another processor with the same Dpc pointer, that DPC routine will be run when IRQL on a processor falls below DISPATCH_LEVEL or the timer interval expires, whichever comes first. Whichever does come first, some essential work for the StartIo or ISR would simply be dropped by such a for-common-use DPC routine.

In addition, such a DPC used by two standard driver routines with very different functionality would necessarily have poorer performance characteristics than separate CustomTimerDpc and CustomDpc routines. Such a DPC would have to determine which operations to carry out to suit the reason(s) it was queued from the StartIo routine or ISR, and testing conditions in the DPC would use additional CPU cycles.


Chapter 15 Unload Routine

This chapter contains the following information:
15.1 Unload Routine Requirements

15.1.1 Releasing Driver-Allocated Resources

15.1.2 Releasing Device and Controller Objects

15.2 Unload Routine Functionality

The Unload routine is defined by the I/O Manager as follows:

VOID
(*PDRIVER_UNLOAD) (
    IN PDRIVER_OBJECT DriverObject
    );

This chapter summarizes the required functionality of an NT driver’s standard Unload routine.

For specific information about any of the support routines mentioned in this chapter, see the Kernel-Mode Driver Reference.


15.1 Unload Routine Requirements

Any NT driver that can be replaced, or unloaded and reloaded, while Windows NT® is running must have an Unload routine.

NT drivers for video, keyboard, or mouse devices, and for mass-storage devices that can hold the system page file currently do not have Unload routines because these types of devices must be available while the current system is running. Higher-level NT drivers layered above these device drivers also do not have Unload routines.

Like every DriverEntry routine, an Unload routine runs at IRQL PASSIVE_LEVEL and is called in the context of a system thread.

In general, the process of unloading a driver is a synchronous operation.


15.1.1 Releasing Driver-Allocated Resources

An Unload routine is responsible for freeing all the system objects and resources the driver is using before the driver itself is unloaded.

In other words, the Unload routine must call the following support routines:

  • IoDisconnectInterrupt if the driver has registered an ISR with IoConnectInterrupt

  • IoDeleteSymbolicLink if the DriverEntry or Reinitialize routine called IoCreateSymbolicLink, and IoDeassignArcName if the driver called IoAssignArcName

  • ExFreePool if the DriverEntry or any other driver routine called ExAllocatePool and the memory has not yet been released

  • MmUnmapIoSpace if the DriverEntry routine called MmMapIoSpace

  • MmFreeNonCachedMemory if the DriverEntry routine called MmAllocateNonCachedMemory

  • MmFreeContiguousMemory if the DriverEntry routine called MmAllocateContiguousMemory

  • HalFreeCommonBuffer if the DriverEntry routine called HalAllocateCommonBuffer

  • IoAssignResources or IoReportResourceUsage if the DriverEntry routine called one of these support routines or HalAssignSlotResources to claim hardware resources in the configuration registry for itself and/or for its physical devices individually

    Consider the following a general design guideline for Unload routines

    Every Unload routine must ensure that no other driver routine is currently using or might shortly be using any driver-allocated resource before the Unload routine frees that resource.

For example, an Unload routine must call IoStopTimer if the driver’s IoTimer routine is currently enabled for a particular device object. It must ensure that no thread is waiting on any of the driver’s dispatcher objects and/or that its timer object(s) are not queued for calls to its CustomTimerDpc routine(s) before it frees the storage for its dispatcher objects. It must call KeRemoveQueueDpc if it has a CustomDpc routine that the ISR might have queued, and so on.

Any device driver must call IoDisconnectInterrupt with the interrupt object(s) pointer if it called IoConnectInterrupt in its DriverEntry routine. Its Unload routine must call IoDisconnectInterrupt before it deletes the storage for this pointer.

Any higher-level driver that called IoGetDeviceObjectPointer in its DriverEntry or Reinitialize routine must call ObDereferenceObject with the pointer to the file object associated with the lower driver’s device object. Its Unload routine must call ObDereferenceObject before it deletes the storage for this pointer.

Any higher-level driver that called IoAttachDevice in its DriverEntry or Reinitialize routine must call IoDetachDevice with the pointer to the lower driver’s device object. Its Unload routine must call IoDetachDevice before it deletes the storage for this pointer.

If the driver allocated nonpaged pool as storage for any Kernel-defined object(s) it uses, the Unload routine must explicitly call ExFreePool to free the storage and the object(s).

If the driver called PsCreateSystemThread, the Unload routine also must cause the driver-created thread to be run so that the thread itself can call PsTerminateSystemThread before the driver is unloaded from the system. A driver-created system thread cannot be released by calling ZwClose with the ThreadHandle returned by PsCreateSystemThread.

For more information about symbolic links, about claiming resources in the registry, about releasing registry resources, and about allocating memory, see also Chapter 16. For specific information about any particular support routine, see the Kernel-Mode Driver Reference.


15.1.2 Releasing Device and Controller Objects

Any NT driver must release its references to external resources, such as pointers to other drivers’ objects and/or to interrupt objects, that it stored in a device or controller extension before it deletes the corresponding device or controller object.

After it has freed all resources associated with a device or controller object, the Unload routine must call IoDeleteDevice for each device object that the driver created with successful calls to IoCreateDevice, and it must call IoDeleteController if the DriverEntry routine called IoCreateController.

Any Kernel-defined object for which the driver provides storage in a device extension is automatically freed when the Unload routine calls IoDeleteDevice with the corresponding device object. In general, any object that the DriverEntry routine set up by calling KeInitialize Xxx can be freed by a call to IoDeleteDevice if the driver provided storage for that object in its device extension. For example, if a driver has a CustomTimerDpc routine and has provided storage for the necessary DPC and timer objects in its device extension, the call to IoDeleteDevice releases these system resources.

In a similar manner, any Kernel-defined object for which the driver provides storage in a controller extension is automatically freed when the Unload routine calls IoDeleteController with the corresponding controller object.


15.2 Unload Routine Functionality

The input parameter to an Unload routine is a DriverObject pointer, as shown by the declaration, to the driver object that the NT I/O Manager uses to represent that driver’s load image.

Every NT driver’s calls to IoCreateDevice require the DriverObject pointer that was input to its DriverEntry routine. Each successful call to IoCreateDevice associates the newly created device object with the given driver object.

Consequently, an NT driver’s Unload routine can walk the chain of device objects associated with the input DriverObject to release the resources and objects that driver was using.

Consider the following implementation guidelines for the Unload routines of NT drivers

  • An NT device driver must disable interrupts from its device if it is possible to do so before its Unload routine releases the system objects and resources the DriverEntry routine set up for processing I/O requests and before its Unload routine frees any hardware resources that the DriverEntry routine claimed for its physical device in the registry.

  • Any NT driver’s Unload routine must ensure that no other driver routine is currently using or might shortly be using a particular resource before it releases that resource.

While the NT I/O Manager does not continue to send IRPs to any driver after its Unload routine has been called, disabling interrupts from the device should be the first thing done by any device driver’s Unload routine if possible. Otherwise, its ISR might be called to handle a device interrupt while the Unload routine is executing and releasing resources in the device extension that the ISR needs to handle the interrupt. Note that even if its ISR runs successfully in these circumstances, the DpcForIsr or CustomDpc routine that the ISR queues, and possibly other driver routines that run at IRQL >= DISPATCH_LEVEL, will execute before the Unload routine regains control, thereby increasing the likelihood that the Unload routine has deleted a resource that another driver routine references. For more information about managing IRQLs in NT drivers, see Chapter 16.

Any Unload routine must release the resources an NT driver is using in stages. However, the specifics of how any particular NT driver uses the registry, sets up system objects and resources in its device extension(s), in any controller extension, or in driver-allocated nonpaged pool varies from driver to driver.

A DriverEntry and Unload routine can sometimes share internal driver routines that set up necessary parameters to allocate and release resources. For example, a driver that sets up symbolic links between Win32®-visible names and the corresponding NT device object names for its devices might have a common routine to construct Unicode string paths to the registry keys in the \Registry..\DeviceMap tree where it stores configuration information about these aliases.

In general, an NT driver’s Unload routine releases all driver-allocated resources in the following stages:

  1. First, disable interrupts on any physical device(s), if possible, and then call IoDisconnectInterrupt as soon as interrupts are disabled.

  2. Ensure that no other driver routine can reference the resources that the Unload routine intends to release, such as calling IoStopTimer one or more times to disable the driver’s IoTimer routine(s) for all associated device objects, and so forth.

  3. Release certain system objects and resources that the DriverEntry routine set up in the device extension of the device object(s) or in the controller extension of the controller object, if any.

    • In particular, a device driver’s Unload routine must call IoDisconnectInterrupt before it calls IoDeleteDevice or IoDeleteController to free the interrupt object(s) pointer stored in the corresponding device/controller extension by its DriverEntry routine.

    • In particular, a higher-level driver’s Unload routine must call ObDereferenceObject with the pointer to the next-lower driver’s file object if it called IoGetDeviceObjectPointer and stored this pointer in a device or controller extension before it calls IoDeleteDevice or IoDeleteController with its own device or controller object.

    • In particular, a higher-level driver’s Unload routine must call IoDetachDevice with the pointer to the lower driver’s device object if it called IoAttachDevice and stored this pointer in a device or controller extension before it calls IoDeleteDevice or IoDeleteController with its own device or controller object.

  4. Free any hardware resources that the DriverEntry routine claimed for the driver’s physical device(s), if any, in the registry under the \Registry\Machine\Hardware\ResourceMap tree.

    For more information about releasing hardware resource claims in the registry, see Chapter 16. For specific information about IoAssignResources and IoReportResourceUsage, see the Kernel-Mode Driver Reference.

  5. Deassign any symbolic links that the driver created, such as links between the NT name for its device objects and the Win32-visible names for the same devices and any other symbolic links set up with IoCreateSymbolicLink, IoCreateUnprotectedSymbolicLink and/or IoAssignArcName in the DriverEntry routine.

    The Unload routine should remove any names for its devices that the DriverEntry routine stored in the registry under the \Registry..\DeviceMap tree, as well.

  6. Delete each device object with IoDeleteDevice and any controller object with IoDeleteController after freeing, as necessary, any pointers to external objects or other resources that were stored in the device and controller extensions.

    If the DriverEntry routine called IoGetConfigurationInformation to increment the count for a particular type of device, the Unload routine also must call IoGetConfigurationInformation and decrements the count for its devices in the I/O Manager’s global configuration information structure as it deletes the corresponding device objects.

In effect, an Unload routine must undo whatever the corresponding DriverEntry and Reinitialize, if any, routine(s) did to initialize its physical device(s), if any. An Unload routine must undo whatever the corresponding DriverEntry and Reinitialize, if any, routine(s) did to allocate the resources the driver needed to remain loaded and process IRPs.

Before it returns control, an Unload routine also is responsible for freeing any other driver-allocated resources that have not yet been freed by other driver routines.


Chapter 16 Common Driver Design Issues

This chapter contains the following information:
16.1 Managing Hardware Priorities
16.2 Using Spin Locks
16.2.1 Providing Storage for Spin Locks and Protected Data

16.2.2 Initializing Spin Locks

16.2.3 Calling Support Routines That Use Spin Locks

16.2.4 Releasing Spin Locks Promptly

16.2.5 Preventing Errors or Deadlocks While Using Spin Locks

16.3 Polling a Device

16.4 Managing Memory Usage
16.4.1 Using System Memory

16.4.1.1 NT Driver Access to User-Space Memory

16.4.1.2 Setting up MDLs for Partial Transfer Requests

16.4.1.3 Allocating System-Space Memory

16.4.1.4 Remapping Bus-Relative Memory-Space Addresses to Logical Addresses

16.4.2 Using the Kernel Stack

16.4.3 Using Zone Buffers

16.5 Maintaining Cache Coherency for DMA and PIO
16.5.1 Flushing Cached Data during DMA Operations

16.5.2 Flushing Cached Data during PIO Operations

16.6 Error Logging and NTSTATUS Values
16.6.1 Calling IoAllocateErrorLogEntry

16.6.2 Filling in an Error Log Packet

16.6.3 Setting NTSTATUS Values in Error Log Packets

16.6.4 Calling IoWriteErrorLogEntry

16.6.5 Defining New IO_ERR_XXX

16.6.6 Defining Private NTSTATUS Constants

16.7 Handling Removable Media

16.8 Using the Registry
16.8.1 Registry Paths Supplied to NT Drivers

16.8.1.1 RegistryPath, DriverName, and Device Object Names

16.8.1.2 Load-control Sets for NT Drivers

16.8.1.3 HardwareDatabase and the System DeviceMap

16.8.2 Getting Device Hardware Configuration Information

16.8.2.1 Calling IoQueryDeviceDescription

16.8.2.2 Calling HalGetBusData or HalGetBusDataByOffset

16.8.3 Claiming Hardware Resources

16.8.3.1 Calling IoReportResourceUsage

16.8.3.2 Calling IoAssignResources or HalAssignSlotResources

16.8.4 Setting Up Driver-specific, User-visible Error Logging

16.8.5 Using the RegistryPath Parameters

16.9 Setting Up Symbolic Links
16.9.1 Making a Named Device Object Visible to User-mode Applications

16.9.2 Creating Symbolic Links between Devices and ARC Names

This chapter contains some information of general interest to all NT driver writers, including the following:

  • A summary of the default hardware priorities (IRQLs) at which standard NT driver routines run and some guidelines for calling support routines at appropriate IRQLs

  • General guidelines about using spin locks to synchronize access to data or resources shared by driver routines

  • General guidelines about allocating system-space memory, using the kernel stack, and using zone buffers

  • How NT drivers should handle I/O errors and how NTSTATUS values are defined

  • How NT drivers use the registry to initialize themselves in a manner that keeps them portable and configurable across Windows NT® platforms

  • How to create a symbolic link between a driver’s named device object and a Win32®-visible name (such as LPT1) for the same device

This chapter also discusses design issues that are device-type-specific or design-specific, including the following:

  • For NT device driver designers, whether to have the driver poll the device or set up a thread that waits on a Kernel-defined dispatcher object, such as an event or semaphore

  • For NT DMA or PIO device driver designers, how to maintain cache coherency and data integrity during transfer operations

  • For NT removable-media device driver designers, how to handle user-induced errors, such as supplying the wrong media or removing the media on which a file is open

  • For NT floppy and CD-ROM device driver designers, how to set up a symbolic link between the driver’s name for a device object that can represent a boot-from device and the corresponding ARC name for the device


16.1 Managing Hardware Priorities

The IRQL at which a particular NT device or intermediate driver routine is run determines which kernel-mode support routines it can call. For example, some support routines require that the caller be running at DISPATCH_LEVEL IRQL. Others cannot be called safely if the caller is running at raised IRQL: that is, at any IRQL higher than PASSIVE_LEVEL.

Figure 16.1 illustrates the default IRQLs at which the most commonly implemented standard NT driver routines are called and the Kernel-defined ordering of low-to-high IRQL values.

Figure 16.1 Default IRQLs for NT Driver Routines

As Figure 16.1 shows, lowest-level NT drivers process IRPs while running at one of three IRQLs:

  • PASSIVE_LEVEL, with no interrupts masked off on the processor, in the driver’s Dispatch routine(s)

    DriverEntry, Reinitialize, and Unload routines also are run at PASSIVE_LEVEL, as are any driver-created system threads.

  • DISPATCH_LEVEL, with DISPATCH_LEVEL and APC_LEVEL interrupts masked off on the processor, in the StartIo routine

    AdapterControl, ControllerControl, IoTimer, Cancel (while it holds the cancel spin lock), and CustomTimerDpc routines also are run at DISPATCH_LEVEL, as are DpcForIsr and CustomDpc routines.

  • Device IRQL (DIRQL), with all interrupts at less than or equal to the SynchronizeIrql of the driver’s interrupt object(s) masked off on the processor, in the ISR and SynchCritSection routines

Most higher-level NT drivers process IRPs while running at either of two IRQLs:

  • PASSIVE_LEVEL, with no interrupts masked off on the processor, in the driver’s Dispatch routine(s)

    DriverEntry, Reinitialize, and Unload routines also are run at PASSIVE_LEVEL, as are any driver-created system threads or worker-thread callback routines or file system drivers.

  • DISPATCH_LEVEL, with DISPATCH_LEVEL and APC_LEVEL interrupts masked off on the processor, in the driver’s IoCompletion routine(s)

    IoTimer, Cancel, and CustomTimerDpc routines also are run at DISPATCH_LEVEL.

In some circumstances, intermediate and lowest-level drivers of mass-storage devices are called at IRQL APC_LEVEL. In particular, this can occur at a page fault for which an NT file system driver sends an IRP_MJ_READ request to lower drivers.

Most standard driver routines are run at an IRQL that allows them simply to call the appropriate support routines. For example, a device driver must call IoAllocateAdapter or IoAllocateController while running at DISPATCH_LEVEL IRQL. Since most NT device drivers call these routines from a StartIo routine, usually they are running at DISPATCH_LEVEL already.

Note that a device driver that has no StartIo routine because it sets up and manages its own queue(s) of IRPs is not necessarily running at DISPATCH_LEVEL IRQL when it should call IoAllocateAdapter (or IoAllocateController). Such a driver must nest its call to IoAllocateAdapter between calls to KeRaiseIrql and KeLowerIrql so that it runs at the required IRQL when it calls IoAllocateAdapter and restores the original IRQL when the calling routine regains control.

Consider the following facts in order to call support routines at appropriate IRQLs and to manage hardware priorities successfully in NT drivers:
  • Calling KeRaiseIrql with an input NewIrql value that is less than the current IRQL causes a fatal error. Calling KeLowerIrql except to restore the original IRQL (that is, after a call to KeRaiseIrql) also causes a fatal error.

  • While running at raised IRQL, calling KeWaitForSingleObject or KeWaitForMultipleObjects with Kernel-defined dispatcher object(s) to wait for a nonzero interval causes a fatal error. Only driver routines that run in a nonarbitrary thread context at IRQL PASSIVE_LEVEL, such as driver-created threads, the DriverEntry and Reinitialize routines, or Dispatch routines for inherently synchronous I/O operations, such as most device I/O control requests, can safely wait on events, semaphores, mutexes, or timers for a nonzero interval.

  • Even while running at IRQL PASSIVE_LEVEL, pageable driver code must not call KeSetEvent, KeReleaseSemaphore, or KeReleaseMutex with the input Wait parameter set to TRUE. Such a call can cause a fatal page fault.

  • Any routine that is running at greater than IRQL APC_LEVEL can neither allocate memory from paged pool nor access memory in paged pool safely. If a routine running at IRQL greater than APC_LEVEL causes a page fault, it is a fatal error.

  • A driver must be running at IRQL DISPATCH_LEVEL when it calls KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel. A driver can be running at IRQL <= DISPATCH_LEVEL when it calls KeAcquireSpinLock but it must release that spin lock by calling KeReleaseSpinLock. In other words, it is a programming error to release a spin lock acquired with KeAcquireSpinLock by calling KeReleaseSpinLockFromDpcLevel.

  • Calling a support routine, such as an ExInterlocked Xxx, that uses a spin lock raises IRQL on the current processor either to DISPATCH_LEVEL or to DIRQL if the caller is not already running at raised IRQL.

  • Driver code that runs at raised IRQL should execute as quickly as possible. The higher the IRQL at which a routine runs, the more important it is for good overall performance to tune that routine to execute as quickly as possible. For example, any driver that calls KeRaiseIrql should make the reciprocal call to KeLowerIrql as soon as it can.

For routine-specific information about IRQL requirements for calling NT support routines, see the Kernel-Mode Driver Reference. For more information about using spin locks, see Section 16.2.


16.2 Using Spin Locks

Spin locks are Kernel-defined, kernel-mode-only synchronization mechanisms, exported as an opaque type: KSPIN_LOCK. A spin lock can be used to protect shared data or resources from simultaneous access by routines that can execute concurrently and at raised IRQL in Windows NT SMP machines.

Many NT components use spin locks, including NT drivers. Any kind of NT driver might use one or more executive spin locks. For example, most NT file systems use an interlocked work queue in the FSD’s device extension to store IRPs that are processed both by the file system’s worker-thread callback routines and by the FSD. An interlocked work queue is protected by an executive spin lock, used to resolve contention among the FSD trying to insert IRPs into the queue and any threads simultaneously trying to remove IRPs. As another example, the system floppy controller driver uses two executive spin locks. One executive spin lock protects an interlocked work queue shared with this driver’s device-dedicated thread; the other protects a timer object shared by three driver routines.

Every NT driver that has an ISR uses an interrupt spin lock to protect any data or hardware shared between its ISR and its SynchCritSection routine(s) that are usually called from a driver’s StartIo and DpcForIsr routines. An interrupt spin lock is associated with the set of interrupt objects created when such a driver calls IoConnectInterrupt, as described in Chapter 3.

All designers of NT drivers that use a spin lock should follow these guidelines:
  • Provide the storage for any data or resource protected by a spin lock and for the corresponding spin lock in resident system-space memory (nonpaged pool, as shown in Figure 16.3). A driver must provide the storage for any executive spin locks it uses. However, a device driver need not provide the storage for an interrupt spin lock unless it has a multivector ISR or has more than one ISR, as described in Chapter 3.

  • Call KeInitializeSpinLock to initialize each spin lock for which the driver provides storage before using it to synchronize access to the shared data or resource it protects.

  • Call every support routine that uses a spin lock at an appropriate IRQL, generally at <= DISPATCH_LEVEL for executive spin locks or at <= DIRQL for an interrupt spin lock associated with the driver’s interrupt object(s).

  • Implement routines to execute as quickly as possible while they hold a spin lock. No routine should hold a spin lock for longer than 25 microseconds.

  • Never implement routines that do any of the following while holding a spin lock:

    • Cause hardware exceptions or raise software exceptions

    • Attempt to access pageable memory

    • Make a recursive call that would cause a deadlock or could cause a spin lock to be held for longer than 25 microseconds

    • Attempt to acquire another spin lock if doing so might cause a deadlock

    • Call an external routine that violates any of the preceding rules

The following subsections explain these guidelines in more detail.


16.2.1 Providing Storage for Spin Locks and Protected Data

When an NT driver is loaded, its DriverEntry routine must allocate resident storage for any spin-lock-protected data or resources and for corresponding spin locks in one of the following places:

  • The device extension of a device object that the driver sets up by calling IoCreateDevice

  • The controller extension of a controller object that the driver sets up by calling IoCreateController

  • Nonpaged, system-space memory that the driver obtains by calling ExAllocatePool

Attempting to access pageable data while holding a spin lock causes a fatal page fault if that page is not present. Referencing a spin lock that is invalid (because it was stored in pageable memory and is currently paged-out) also causes a fatal page fault.

An NT driver must provide the storage for each of the following kinds of executive spin lock it might use:

  • Any spin lock used to synchronize access to driver-defined data by nonISR driver routines that call KeAcquireSpinLock and KeReleaseSpinLock or KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel

  • Any spin lock used to synchronize access to a driver-allocated resource by calling a resource-specific set of ExInterlocked Xxx routines

While a driver can make calls to the ExInterlocked..List and/or ExInterlocked..Zone routines from its ISR or SynchCritSection routines, it cannot call KeAcquireSpinLock and KeReleaseSpinLock or KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel while running at IRQL greater than DISPATCH_LEVEL. Consequently, any driver that reuses a spin lock in calls to the Ke..SpinLock and ExInterlocked Xxx routines must make every call while running at IRQL <= DISPATCH_LEVEL.

Note that a driver can pass the same spin lock to ExInterlockedInsertHeadList as it does to ExInterlockedAllocateFromZone. For more information about how spin lock usage affects performance, see Section 16.2.4. For more information about using zone buffers, see Section 16.4.3.

In addition to the storage for its executive spin locks, an NT device driver must provide the storage for another spin lock to be associated with its interrupt object(s) if it has a multivector ISR or more than one ISR.


16.2.2 Initializing Spin Locks

Any NT driver must call KeInitializeSpinLock to initialize each executive spin lock it uses, before calling any support routine that requires access to a caller-supplied executive spin lock, such as the following routines:

  • KeAcquireSpinLock and, subsequently, KeReleaseSpinLock

  • KeAcquireSpinLockAtDpcLevel and, subsequently, KeReleaseSpinLockFromDpcLevel

  • An ExInterlocked Xxx routine

A lowest-level NT driver must call KeInitializeSpinLock to initialize an interrupt spin lock for which it provides storage before it calls IoConnectInterrupt and, subsequently, KeSynchronizeExecution to synchronize access to data and resources shared between its ISR (or ISRs) and SynchCritSection routines.


16.2.3 Calling Support Routines That Use Spin Locks

Calling KeAcquireSpinLock sets the IRQL on the current processor to DISPATCH_LEVEL until a corresponding KeReleaseSpinLock call restores the previous IRQL. Consequently, drivers must be executing at IRQL <= DISPATCH_LEVEL when they call the KeAcquireSpinLock.

Callers of KeAcquireSpinLockAtDpcLevel and KeReleaseSpinLockFromDpcLevel run faster because they are already running at IRQL DISPATCH_LEVEL so these support routines need not reset IRQL on the current processor. Consequently, it is a fatal error on most Windows NT platforms to call KeAcquireSpinLockAtDpcLevel while running at IRQL less than DISPATCH_LEVEL. It is also an error to release a spin lock that was acquired with KeAcquireSpinLock by calling KeReleaseSpinLockFromDpcLevel because the caller’s original IRQL is not restored.

Routines that hold an executive spin lock, such as the ExInterlocked Xxx, usually execute at IRQL DISPATCH_LEVEL until they release the spin lock and return control to the caller. However, it is possible for an NT driver’s ISR and SynchCritSection routines to call certain ExInterlocked Xxx, such as the ExInterlocked..List routines, as long as the spin lock passed to this set of ExInterlocked Xxx is used exclusively by the ISR and SynchCritSection routines.

Each routine that holds an interrupt spin lock executes at the DIRQL of an associated set of interrupt objects. Therefore, a driver must not call KeAcquireSpinLock and KeReleaseSpinLock nor any other routine that uses an executive spin lock from its ISR or SynchCritSection routine(s). Such a call is an error that can cause a system deadlock, requiring the user to reboot his or her machine. Note that if a driver’s ISR or SynchCritSection routine calls an ExInterlocked..List routine, such a driver cannot reuse the spin lock it passes to the ExInterlocked..List routines in calls to the Ke..SpinLock or Ke..SpinLock..DpcLevel support routines.

If a driver has a multivector ISR or more than one ISR, its routines can call KeSynchronizeExecution while executing at any IRQL up to the SynchronizeIrql value specified for the associated interrupt objects when they were connected. For more information about interrupt objects, see Chapter 3.

For more information about device drivers’ SynchCritSection routines passed to KeSynchronizeExecution, see Chapters 4 and 10, and Section 16.2.4. For more information about managing hardware priorities in drivers, see Section 16.1, and see the Kernel-Mode Driver Reference for support-routine-specific IRQL requirements.


16.2.4 Releasing Spin Locks Promptly

An NT driver writer who minimizes the time that the driver holds spin locks can significantly improve both the performance of the driver and of the system overall. For example, consider Figure 16.2, which shows how an interrupt spin lock protects device-specific data that must be shared between an ISR and the StartIo and DpcForIsr routines in an SMP machine.

Figure 16.2 Using an Interrupt Spin Lock

  1. While the driver’s ISR runs at DIRQL on one processor, its StartIo routine runs at DISPATCH_LEVEL on a second processor. The Kernel interrupt handler holds the InterruptSpinLock for the driver’s ISR, which accesses protected, device-specific data, such as state or pointers to device registers (SynchronizeContext), in the driver’s device extension. The StartIo routine, which is ready to access SynchronizeContext, calls KeSynchronizeExecution, passing a pointer to the associated interrupt object(s), the shared SynchronizeContext, and the driver’s SynchCritSection routine (AccessDevice in Figure 16.2).

    Until the ISR returns, thereby releasing the driver’s InterruptSpinLock, KeSynchronizeExecution spins on the second processor, preventing AccessDevice from touching SynchronizeContext. However, KeSynchronizeExecution also raises IRQL on the second processor to the SynchronizeIrql of the interrupt object(s), thereby preventing another device interrupt from occurring on that processor so AccessDevice can be run at DIRQL as soon as the ISR returns. However, higher DIRQL interrupts for other devices, clock interrupts, and power-fail interrupts can still occur on either processor, as shown in Figure 16.1.

  2. When the ISR queues the driver’s DpcForIsr and returns, AccessDevice runs on the second processor at the SynchronizeIrql of the associated interrupt object(s) and accesses SynchronizeContext. Meanwhile, the DpcForIsr is run on another processor at DISPATCH_LEVEL IRQL. The DpcForIsr also is ready to access SynchronizeContext, so it calls KeSynchronizeExecution with the same parameters as the StartIo routine did in Step 1.

    Note that when KeSynchronizeExecution acquires the spin lock and runs AccessDevice on behalf of the StartIo routine, the driver-supplied synchronization routine AccessDevice is given exclusive access to SynchronizeContext. Because AccessDevice runs at the SynchronizeIrql, the driver’s ISR cannot acquire the spin lock and access the same area until the spin lock is released, even if the device interrupts on another processor while AccessDevice is executing.

  3. AccessDevice returns and the spin lock is released, so the StartIo routine resumes running at DISPATCH_LEVEL on the second processor. KeSynchronizeExecution now runs AccessDevice on the third processor, so it can access SynchronizeContext on behalf of the DpcForIsr. However, if a device interrupt had occurred before the DpcForIsr called KeSynchronizeExecution in Step 2, the ISR might run on another processor before KeSynchronizeExecution could acquire the spin lock and run AccessDevice on the third processor.

As Figure 16.2 shows, while a routine running on one processor holds a spin lock, every other routine trying to acquire that spin lock gets no work done. Each routine trying to acquire an already held spin lock simply spins on its current processor until the holder releases the spin lock. When a spin lock is released, one and only one routine can acquire it so every other routine currently trying to acquire the same spin lock will continue to spin.

As explained in Section 16.2.3, the holder of any spin lock runs at raised IRQL: either at DISPATCH_LEVEL for an executive spin lock, or at a DIRQL for an interrupt spin lock. Callers of KeAcquireSpinLock run at DISPATCH_LEVEL until they call KeReleaseSpinLock. Callers of KeSynchronizeExecution automatically raise IRQL on the current processor to the SynchronizeIrql of the interrupt object(s) until the caller-supplied SynchCritSection routine exits and KeSynchronizeExecution returns control.

NT driver writers should always keep in mind the following fact about using spin locks:

All code that runs at a lower IRQL cannot get any work done on the set of processors occupied by a spin-lock holder and by other routines trying to acquire the same spin lock.

Consequently, NT driver writers who minimize the time their drivers hold spin locks get significantly better performance from their drivers. They also contribute significantly to better overall system performance.

As Figure 16.2 shows, the Kernel interrupt handler executes routines running at the same IRQL in a multiprocessor machine on a first-come, first-served basis, and the Kernel also does the following:

  • When a driver routine calls KeSynchronizeExecution, the Kernel causes the driver’s SynchCritSection routine to run on the same processor from which the call to KeSynchronizeExecution occurred (see Steps 1 and 3).

  • When a driver’s ISR queues its DpcForIsr, the Kernel causes the DPC to run on the first available processor on which IRQL falls below DISPATCH_LEVEL. This is not necessarily the same processor from which the IoRequestDpc call occurred (see Step 2).

Note that a given driver’s interrupt-driven I/O operations would tend to be serialized in a uniprocessor machine, but that the same operations can be truly asynchronous in an SMP machine. As Figure 16.2 shows, a driver’s ISR could run on CPU4 in an SMP machine before its DpcForIsr begins processing an IRP for which the ISR has already handled a device interrupt on CPU1.

In other words, NT device driver writers should not assume that an interrupt spin lock can protect operation-specific data, saved by the ISR when it runs on one processor, from being overwritten by the ISR when a device interrupt occurs on another processor prior to the execution of the DpcForIsr or CustomDpc routine.

Although a device driver writer could try to serialize all interrupt-driven I/O operations in order to preserve data collected by the ISR, such a driver would not run much faster in an SMP machine than it did in a uniprocessor machine. To get the best possible driver performance while remaining portable across Windows NT uniprocessor and multiprocessor platforms, NT drivers should use some other technique to save operation-specific data obtained by the ISR for subsequent processing by the DpcForIsr.

For example, an ISR can save operation-specific data in the IRP it passes to the DpcForIsr. A refinement of this technique is to implement a DpcForIsr that consults an ISR-augmented count, processes the count’s number of IRPs using ISR-supplied data, and resets the count to zero before returning. Of course, such a count must be protected by the driver’s interrupt spin lock because its ISR and a SynchCritSection would maintain its value dynamically.


16.2.5 Preventing Errors or Deadlocks While Using Spin Locks

When an NT driver routine holds a spin lock, it cannot cause a hardware exception or raise a software exception without bringing down the system. In other words, an NT driver’s ISR and any SynchCritSection routine that the driver supplies in a call to KeSynchronizeExecution must not cause a fault or trap, such as a page fault or an arithmetic exception, and cannot raise a software exception. A routine that calls KeAcquireSpinLock also cannot cause a hardware exception or raise a software exception until it has released its executive spin lock and is no longer running at IRQL DISPATCH_LEVEL.

Pageable Data and Support Routines

NT drivers must not call routines that access pageable data while holding a spin lock. Drivers can call certain support routines that access pageable data if and only if their calls occur while executing at an IRQL strictly less than DISPATCH_LEVEL. Note that this IRQL restriction precludes calling such a support routine while holding a spin lock.

Recursion

Attempting to acquire a spin lock recursively is guaranteed to cause a deadlock: the holding instantiation of a recursive routine cannot release the spin lock while a second instantiation spins, trying to acquire the same spin lock.

NT driver writers who implement a recursive routine should follow these guidelines concerning spin lock usage:
  • The recursive routine must not call itself while holding a spin lock, or must not attempt to acquire the same spin lock on subsequent calls.

  • While it holds a spin lock, another driver routine must not call the recursive routine if recursion might cause a deadlock or could cause the caller to hold the spin lock for longer than 25 microseconds.

For more information about recursive driver routines, see also Section 16.4.2.

Nested Spin Lock Acquisitions

Attempting to acquire a second spin lock while holding another spin lock also can cause deadlocks or poor driver performance.

NT driver writers should follow these implementation guidelines in drivers holding a spin lock:
  • The driver must not call a support routine that uses a spin lock unless a deadlock cannot occur.

  • Even if a deadlock cannot occur, the driver should not call a support routine that uses a spin lock unless alternate coding techniques cannot provide comparable driver performance and functionality.

  • If a driver makes nested calls to acquire spin locks, it must release those spin locks in inverse order: that is, if a driver acquires spin lock A before acquiring spin lock B, it must release spin lock B before it releases spin lock A.

In general, NT driver writers should avoid using nested spin locks to protect overlapping subsets or discrete sets of shared data and resources. Consider what can happen if a driver uses two executive spin locks to protect discrete resources, such as a pair of timer objects that might be set individually and collectively by various driver routines. Such a driver would deadlock intermittently in an SMP machine, whenever either of two routines, each holding one spin lock, tried to acquire the other spin lock.

Even if a deadlock-proof driver can be designed to use nested spin locks, it could be difficult to implement successfully. Nested spin locks are very difficult to debug and to test adequately in a Windows NT SMP machine. Moreover, using nested spin locks can degrade both driver and system performance severely.


16.3 Polling a Device

An NT device driver should avoid polling its device unless it is absolutely necessary and should never use a whole timeslice polling. Polling a device is an expensive operation that makes any operating system compute-bound within the polling driver. If a device driver does much polling, it interferes with I/O operations on other devices and can make the system slow and unresponsive to users.

Recently developed devices, which are as technologically advanced as the processors on which Windows NT is designed to run, seldom require a driver that polls its device to be sure the device is ready to start an I/O operation or that an operation has been completed.

Nevertheless, some devices still in use were designed to work with old processors, which had narrow data buses, slow clock rates, and single-user, single-tasking operating systems that did synchronous I/O. Such devices might require polling or some other means of waiting for the device to update its registers, particularly for Windows NT, which is designed to do asynchronous I/O on new processors with wide data buses and fast clock rates.

An inexperienced driver programmer might think that an NT driver could solve such a slow-device problem by coding a simple loop that increments a counter, thereby “wasting” a minimum interval while its device updates registers. However, such an NT driver is unlikely to be portable across Windows NT platforms. The loop counter maximum would require customization for each Windows NT platform. Furthermore, when such a driver is recompiled with a good optimizing compiler, the compiler could remove the driver’s counter variable and the loop(s) where it is incremented.

Follow this implementation guideline if the device is such that its driver must stall while the hardware updates state:

An NT driver can call KeStallExecutionProcessor before it reads the device register(s). Such a driver should minimize the interval it stalls and should, in general, specify a stall interval no longer than 50 microseconds.

The granularity of a KeStallExecutionProcessor interval is 1 microsecond.

The designer of an NT driver for a device that frequently requires more than 50 microseconds to update state should consider setting up a device-dedicated thread.

Driver Threads

The driver of a slow device or a device that is seldom used like the floppy controller, can solve many “waiting” problems by creating a device-dedicated system thread or, for a file system driver, by using system worker threads and supplying worker-thread callback routines, as most NT FSDs do. A thread can call KeDelayExecutionThread to wait for an interval that could be a full timeslice or longer.

The granularity of a KeDelayExecutionThread interval is around 10 milliseconds. Because KeDelayExecutionThread is a timer-driven routine, the granularity of its interval is slightly faster or slower than 10 milliseconds, depending on the platform. However, such a call is portable because the delta time specified is constant.

If such an NT device driver has its own thread context or is running in a system-thread context, the device-dedicated thread or highest-level driver’s worker-thread callback routine can synchronize operations on a Kernel-defined dispatcher object such as an event or semaphore in a shared communication region of the driver’s device extension. While its device is not in use, a device-dedicated thread can wait on a shared dispatcher object, for example, by calling KeWaitForSingleObject with a semaphore. Until such a device driver is called to carry out an I/O operation and sets the semaphore to the Signaled state, its waiting thread uses no CPU time.

Setting the Base Priority of a Driver-Created Thread

An NT driver should set the base priority of any driver- or device-dedicated thread that it creates with PsCreateSystemThread to the lowest real-time priority level in order to give its thread(s) a relatively high priority while avoiding runtime priority inversions in SMP machines.


16.4 Managing Memory Usage

Many NT drivers use only the memory allocated for their device objects’ device extensions as their global storage areas and their I/O stack locations in IRPs as operation-specific local storage areas. However, an NT driver can allocate additional system-space memory for its needs and can use the kernel stack to pass small amounts of data in calls to internal driver routines.


16.4.1 Using System Memory

Figure 16.3 illustrates the Windows NT virtual memory spaces and their relationship to system physical memory.

Figure 16.3 Windows NT Virtual Memory Spaces and Physical Memory

As Figure 16.3 shows, virtual memory is backed by paged physical memory, and a virtual address range can be backed by discontiguous pages in the CPU. User-space virtual memory and system-space memory allocated from paged pool are always pageable. That is, any noncurrent process and its data can be paged out to secondary storage, usually on a disk.

The hyperspace area shown in Figure 16.3 is a dedicated range of system-space addresses, used by the NT Memory Manager to map the current process’s virtual address space to a set of physical pages in the CPU. Note that any noncurrent process’s virtual addresses are not visible, so its memory space is inaccessible.


16.4.1.1 NT Driver Access to User-Space Memory

NT drivers cannot allocate user-space virtual memory because they run in kernel mode. An NT driver also cannot access memory through user-mode virtual addresses unless it is running in the context of the user-mode thread that caused the driver’s current I/O operation and it is using that thread’s virtual addresses.

Only highest-level NT drivers, such as FSDs, can be sure their Dispatch routines will be called in the context of such a user-mode thread. A highest-level driver can call MmProbeAndLockPages to lock down a user buffer before setting up an IRP for lower drivers.

NT device and intermediate drivers that set up their device objects for buffered or direct I/O, as described in the section on device objects in Chapter 3, can rely on the I/O Manager or a highest-level driver to pass valid access to locked-down user buffers or to system-space buffers in IRPs.


16.4.1.2 Setting up MDLs for Partial Transfer Requests

If a transfer request is too large for the underlying device driver to handle, a higher-level driver can call IoBuildPartialMdl and set up a sequence of partial-transfer IRPs for the underlying device driver(s).

If a highest-level driver cannot lock down the entire user buffer with MmProbeAndLockPages in a machine with limited memory, the original request also must be split into partial transfers. For such a large transfer request, a highest-level driver can do the following:

  1. Call IoBuildSynchronousFsdRequest to allocate a partial-transfer IRP and lock down a portion, which is usually a multiple of PAGE_SIZE or sized to suit the underlying device’s transfer capacity, of the user buffer.

  2. Call IoCallDriver with the partial-transfer IRP and KeWaitForSingleObject to wait on the event object that the driver set up to be associated with its partial-transfer IRP if lower driver(s) return STATUS_PENDING.

  3. When it regains control, repeat Steps 1 and 2 until all the data has been transferred, and, then, complete the original IRP.

A highest-level device driver that must handle very large transfer requests can use the preceding technique and simply call itself with the partial-transfer IRPs it allocates. As an alternative, such a highest-level device driver can do the following:

  1. Call IoAllocateMdl to allocate an MDL describing a portion of the user buffer.

  2. Call MmProbeAndLockPages to lock down that portion of the user buffer.

  3. Transfer the data for that portion of the buffer.

  4. Call MmUnlockPages and do either of the following:

    • If the MDL that the driver allocated in Step 1 is large enough for the next piece of the transfer, call MmPrepareMdlForReuse and, then, repeat Steps 2 through 4.

    • Otherwise, call IoFreeMdl and repeat Steps 1 through 4.

  5. Call MmUnlockPages and IoFreeMdl when all the data has been transferred.


16.4.1.3 Allocating System-Space Memory

The system-space virtual memory shown in Figure 16.3 consists of a limited amount of paged pool and an even more limited amount of nonpaged pool.

Nonpaged pool is guaranteed to be resident at all times. Consequently, it can be accessed safely while running at any IRQL.

For NT drivers, paged pool can be allocated and accessed only under the following condition:

The routine using the corresponding paged-pool virtual addresses must be running at IRQL <= APC_LEVEL. As mentioned in Section 16.1, if a page fault occurs while running at IRQL > APC_LEVEL, it is a fatal error.

Except during their initialization or, possibly, while unloading, NT device and intermediate drivers seldom allocate memory from paged pool because these types of drivers so frequently run at IRQL greater than APC_LEVEL. Any pageable storage that such a driver allocates cannot be accessed safely except by driver-created threads or by the DriverEntry, Reinitialize (if any), and Unload (if any) routines, which can use paged pool allocations to contain data, objects, and resources needed only during initialization or while unloading.

Since several standard NT driver routines run at an IRQL higher than APC_LEVEL, memory allocated from paged pool is inaccessible to most of an NT intermediate or device driver’s routines. For example, higher-level drivers’ IoCompletion routines execute in an arbitrary thread context, usually at DISPATCH_LEVEL IRQL. Such a driver should never allocate pageable storage for data to be accessed from an IoCompletion routine. For more information about the IRQLs at which standard driver routines execute, see Section 16.1.

For specific information about any of the support routines mentioned in this section, see also the Kernel-Mode Driver Reference.

Allocating Driver Buffer Space

For an I/O buffer space, an NT driver can call MmAllocateNonCachedMemory, MmAllocateContiguousMemory, HalAllocateCommonBuffer if the driver’s device uses busmaster DMA or a system DMA controller’s autoinitialize mode, or ExAllocatePool.

Nonpaged pool tends to become fragmented as the system runs, so an NT driver’s DriverEntry routine should call these routines to set up any long-term I/O buffers the driver needs. Each of these routines, except possibly ExAllocatePool, allocates memory that is aligned on a processor-specific boundary (determined by the processor’s data-cache-line size) to prevent cache and coherency problems.

NT drivers should allocate their internal I/O buffers, if any, as economically as possible because nonpaged pool memory is a limited system resource. In general, a DriverEntry routine should avoid calling these support routines repeatedly to request allocations of less than PAGE_SIZE.

In particular, driver writers should consider the following facts in order to allocate I/O buffer memory economically:
  • Each call to MmAllocateNonCachedMemory ties up at least a full page of nonpaged system-space memory, whatever the size of the requested allocation. For requests less than a page, any remainder bytes on the page are wasted: inaccessible by the driver that called MmAllocateNonCachedMemory and unusable by other kernel-mode code.

  • Each call to MmAllocateContiguousMemory allocates up to a page if the specified number of bytes is less than or equal to a page. For requests greater than a page, any remainder bytes on the last-allocated page are wasted: inaccessible to the driver that called MmAllocateContiguousMemory and unusable by other kernel-mode code.

  • Each call to HalAllocateCommonBuffer uses at least one adapter object map register, which maps at least one byte and at most one page. For more information about map registers and using common buffers, see the section on adapter objects in Chapter 3.
Allocating Memory with ExAllocatePool or ExAllocatePoolWithTag

NT drivers also can call ExAllocatePool or ExAllocatePoolWithTag, specifying one of the following system-defined values for the PoolType parameter:

  • NonPagedPoolCacheAligned for a permanent I/O buffer the driver uses, such as a SCSI class driver’s buffer for request-sense data

    Note that such a driver could call MmAllocateNonCachedMemory or MmAllocateContiguousMemory instead.

  • NonPagedPoolCacheAlignedMustS for a temporary, but critically important, I/O buffer, such as a buffer containing initialization data for a physical device that must be present for the system to boot

  • NonPagedPool for any objects or resources not stored in a device extension or controller extension that the driver might access while running at IRQL > APC_LEVEL

    For this PoolType value, ExAllocatePool or ExAllocatePoolWithTag allocates exactly the amount of memory requested if the specified NumberOfBytes is less than or equal to PAGE_SIZE. Otherwise, any remainder bytes on the last-allocated page are wasted: inaccessible to the caller and unusable by other kernel-mode code.

    For example, on an x86, an allocation request of 5K returns two 4K pages. The last 3K of the second page is unavailable to the caller or another caller. To avoid wasting nonpaged pool, a driver writer should design the driver to use a multipage allocation efficiently, such as making two allocations,, one for PAGE_SIZE and the other for 1K to allocate a total of 5K.

  • NonPagedPoolMustSucceed for a temporary, but critically important, storage area, that the driver will release as soon as possible, such as memory that a driver needs to correct an error condition because it would corrupt the system otherwise

  • PagedPoolCacheAligned for a file system driver’s I/O buffer that it locks down and passes in an IRP requesting a DMA transfer by an underlying mass-storage device driver

  • PagedPool for a temporary buffer, used by the DriverEntry or Reinitialize routine to contain objects, data, or resources necessary for initialization, if the buffer will be released before the caller returns, or for a storage area that will be accessed only by one or more driver-created threads

    An NT driver’s Unload routine also can allocate memory from paged pool if the buffer will be released before the Unload routine returns control.

Because the must-succeed pool is a very limited system resource, allocations must be released by calling ExFreePool as soon as possible. Most NT drivers should not call ExAllocatePool or ExAllocatePoolWithTag with the PoolType values NonPagedPoolMustSucceed or NonPagedPoolCacheAlignedMustS, unless the system cannot continue to run if the driver’s allocation request does not succeed. For these PoolType specifications, ExAllocatePool brings down the system if it cannot allocate the requested memory.

For all other PoolType specifications, ExAllocatePool or ExAllocatePoolWithTag returns a NULL pointer if it cannot allocate the requested NumberOfBytes. NT drivers should always check the pointer returned by ExAllocatePool or ExAllocatePoolWithTag. If its value is NULL, the DriverEntry routine (or any other driver routine that returns NTSTATUS) should return STATUS_INSUFFICIENT_RESOURCES or handle the error condition if possible. For more information about handling I/O errors, see Section 16.6.

For the CacheAligned PoolType specifications, ExAllocatePool or ExAllocatePoolWithTag allocates memory that is aligned on a processor-specific boundary (determined by the processor’s data-cache-line size) to prevent cache and coherency problems.


16.4.1.4 Remapping Bus-Relative Memory-Space Addresses to Logical Addresses

To remap a memory-space physical address, such as that of the device registers, returned by HalTranslateBusAddress to a virtual address in nonpaged pool, a DriverEntry routine can call MmMapIoSpace.

If such a driver fails to initialize or is subsequently unloaded, it must release this mapping with MmUnmapIoSpace. It also must free the memory it allocated.


16.4.2 Using the Kernel Stack

While an NT driver can pass data to its internal routines, the NT kernel-mode stack size is around two pages. Consequently, NT drivers cannot pass large amounts of data on the kernel stack.

NT driver writers should take care not to run out of kernel-mode stack space by following these design guidelines:
  • Avoid making deeply nested calls from one internal driver routine to another if each routine passes data on the kernel stack.

  • Take care to limit the number of recursive calls that can occur if you design a driver with a recursive routine.

In other words, the call-tree structure of an NT driver should be relatively flat. While nonpaged pool is also a limited system resource, it is better for an NT driver to allocate a system-space buffer, as described in Section 16.4.1.3, than to run out of kernel stack space.

The NT kernel-mode stack is in cached memory. Consequently, NT drivers cannot transfer data on the stack using DMA.

NT driver writers should avoid DMA data alignment and/or data integrity problems by following this design guideline:

Never attempt to transfer data on the kernel stack using DMA.

Drivers of devices that use DMA can buffer data to be transferred, if necessary, either by calling ExAllocatePool or ExAllocatePoolWithTag for a NonPagedPoolCacheAligned-type buffer or, for some drivers, by using common-buffer DMA, as described in the section on adapter objects in Chapter 3.


16.4.3 Using Zone Buffers

NT drivers that set up zone buffers for fast allocation of fixed-size blocks (also called entries) should use that memory economically.

NT driver writers who use the Ex..Zone routines should follow these design guidelines:
  • Set up a modestly sized zone buffer. That is, make the zone buffer large enough for the driver to handle an average I/O demand for its device in a typical Windows NT machine, rather than setting up a fixed-size zone buffer large enough to handle a worst-case I/O-demand scenario on a high-end Windows NT platform.

    When it initializes, the driver can call MmQuerySystemSize and MmIsThisAnNtAsSystem to get an estimate for sizing a zone buffer. MmQuerySystemSize returns a system-defined enumerated type, indicating whether the machine has a small, medium, or large amount of available memory. MmIsThisAnNtAsSystem returns TRUE if the current machine is a Windows NT server, which is likely both to have more memory available and to require a larger zone buffer to handle the I/O demand on the underlying driver(s)’ device(s).

  • If the driver allocates and frees zone buffer entries using the ExInterlocked..Zone routines, it must provide storage for an excutive spin lock and initialize the spin lock in its DriverEntry routine, as mentioned in Section 16.2.

  • The driver should always call ExIsFullZone immediately preceding a call to ExInterlockedAllocateFromZone. Otherwise, the driver might tie up a processor for an extended interval by trying to acquire the spin lock that protects a zone with no free blocks.

  • If the driver’s call to ExAllocateFromZone returns a NULL pointer or its call to ExIsFullZone returns TRUE, the driver should call ExAllocatePool or ExAllocatePoolWithTag to obtain additional memory to use as zone entries. The driver can use this memory temporarily until some zone entries have been released by calls to ExFreeToZone or ExInterlockedFreeToZone. Then, the driver can call ExFreePool to release its temporary pool allocation since its zone buffer has free blocks again.

  • The driver should not call ExExtendZone or ExInterlockedExtendZone, except possibly under the following circumstances:

    • Its originally allocated zone buffer has no free blocks.

    • It has already called ExAllocatePool or ExAllocatePoolWithTag more than once, but the driver is still using all the additional memory it has allocated from pool for zone entries.

    Such a condition indicates a very heavy I/O demand for the driver’s device, so extending its zone buffer might be worthwhile. That is, the performance gain from allocating additional zone blocks might allow the driver to process requests fast enough to compensate for its additional “permanent” demand on nonpaged pool, especially if extending the zone allows the driver to release some of the temporary memory it allocated with ExAllocatePool or ExAllocatePoolWithTag.

Note that an NT driver’s successful call to ExExtendZone or ExInterlockedExtendZone allocates nonpaged pool that remains allocated to the driver until the system is rebooted. Consequently, any NT driver that calls ExExtendZone or ExInterlockedExtendZone whenever its zone buffer becomes full (that is, all entries are currently allocated), can eventually run out of nonpaged pool. Such a driver also can put the system into a low-memory state such that all I/O throughput becomes very slow, including the “memory-hogging” driver’s.

For routine-specific information about the Ex..Zone routines, see the Kernel-Mode Driver Reference.


16.5 Maintaining Cache Coherency for DMA and PIO

In Windows NT machines, data can be cached in one or more processor caches and/or in the system DMA controller’s cache when a driver is transferring data between system memory and its device. NT drivers that use DMA or PIO to service read/write IRPs or any device I/O control request that requires a DMA or PIO data transfer operation should ensure the integrity of possibly cached data during transfer operations as explained in the following subsections.


16.5.1 Flushing Cached Data during DMA Operations

In some Windows NT platforms, the processor and system DMA controller (or busmaster DMA adapters) exhibit cache coherency anomalies.

To maintain data integrity during DMA operations, NT device drivers must follow these guidelines:
  1. Call KeFlushIoBuffers before beginning a transfer operation to maintain consistency between data that might be cached in the processor and the data in memory.

    If an NT driver calls HalAllocateCommonBuffer with the CacheEnabled parameter set to TRUE, the driver must call KeFlushIoBuffers before beginning a transfer operation to/from its buffer.

  2. Call IoFlushAdapterBuffers at the end of each device transfer operation to be sure any remainder bytes in the system DMA controller’s buffers have been written into memory or to the slave device.

    Or, call IoFlushAdapterBuffers at the end of each transfer operation for a given IRP to be sure all data has been read into system memory or written out to a busmaster DMA device.

Figure 16.4 shows why it is important to flush the processor cache before a read or write operation using DMA if the host processor and DMA controller do not automatically maintain cache coherency.

Figure 16.4 Read and Write Operations Using DMA

When an asynchronous DMA read or write operation occurs, it accesses data in memory, not in the processor cache. Unless this cache has been flushed by calling KeFlushIoBuffers just before a read, the data transferred into system memory by a DMA operation could be overwritten with stale data if the processor cache is flushed later. Unless the processor cache has been flushed by calling KeFlushIoBuffers just before a write, the data in this cache might be more up-to-date than the copy in memory.

KeFlushIoBuffers does nothing if the processor and DMA controller can be relied on to maintain cache coherency, so calls to this support routine have almost no overhead in such a Windows NT platform.

As also shown in Figure 16.4, DMA controllers, which are represented by NT adapter objects, can have internal buffers. Such a DMA controller can transfer cached data in fixed-size chunks, usually eight or more bytes at a time. Moreover, these DMA controllers can wait until their internal buffers are full before each transfer operation.

Consider the case of any NT device driver that uses slave DMA to read data in variable-sized chunks or in fixed-size chunks that are not an integral multiple of a system DMA controller’s cache size. Unless such a driver calls IoFlushAdapterBuffers at the end of each device transfer, it cannot be sure when every byte the driver requested actually will be transferred.

The driver of a busmaster DMA device also should call IoFlushAdapterBuffers at the end of each transfer operation for a given IRP to be sure that all data has been transferred into system memory or out to the device.

IoFlushAdapterBuffers returns a Boolean, indicating whether the requested flush operation was successful. NT drivers can use this value to determine how to set the I/O status block when completing an IRP for any DMA read or write operation.


16.5.2 Flushing Cached Data during PIO Operations

In some Windows NT platforms, the instruction and data caches in the processor exhibit cache coherency anomalies during PIO read operations.

To maintain data integrity during their read operations, NT drivers that use PIO must follow this guideline:

Call KeFlushIoBuffers at the end of each read operation.

For example, a driver making a PIO transfer from its device to system memory should call KeFlushIoBuffers at the end of each device transfer operation. As another example, a driver that reads a sequence of device registers into system memory should call KeFlushIoBuffers at the end of the sequence. Otherwise, such a driver might attempt to access data that is still in the processor’s data cache, rather than in system memory, in some Windows NT platforms.

KeFlushIoBuffers does nothing if the processor can be relied on to maintain cache coherency, so calls to this support routine have almost no overhead in such a Windows NT platform.


16.6 Error Logging and NTSTATUS Values

Windows NT is designed to be both more robust and more “user-friendly” than some PC operating systems with respect to runtime errors. That is, the system is designed to do the following:

  • To continue running without allowing one component (or thread) to corrupt other components’ code or data when an error occurs

  • To continue running without sending a flood of cryptic error messages to end users whenever errors occur

Granted that some I/O errors are user-induced. For example, a request to read data from a file on removable media when the user has supplied the wrong diskette, tape, or CD-ROM causes a user-induced I/O error. Such an error can be corrected readily when the user is prompted to supply the appropriate media, as discussed in Section 16.7.

Other I/O errors cannot be readily corrected by the majority of end users. For these kinds of I/O errors, Windows NT continues to run without forcing the user to become aware of errors that the user cannot fix immediately. Instead, it provides a system error-logging thread that formats and stores information about I/O errors as entries in a file.

The Win32 event viewer, which reads and displays this error log file, allows Windows NT users, system administrators, or support technicians to monitor the condition of the hardware in a given machine and replace failing hardware as necessary, to tune the configuration of devices in the machine for better performance, and to debug hardware problems if they occur.


16.6.1 Calling IoAllocateErrorLogEntry

When an NT driver discovers an I/O error in processing an IRP, it should call IoAllocateErrorLogEntry as follows:
size = sizeof(IO_ERROR_LOG_PACKET) + (n * sizeof(ULONG))
                                   + sizeof(InsertionStrings);
                    // where n depends on how much
                    // DumpData the driver will supply
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
    deviceExtension->DeviceObject,
                            // target device for current operation
    size);

Note that an error log packet is limited in size, and the system-defined limit includes any dump data and insertion strings that the driver supplies with a packet. NT drivers can call IoAllocateErrorLogEntry with a specified EntrySize value that is, at most, ERROR_LOG_MAXIMUM_SIZE.

IoAllocateErrorLogEntry returns a pointer to an error log packet. If the returned pointer is NULL, the driver need not log an error. It should simply continue running and assume that, if the same error occurs again, it can be logged at that point.


16.6.2 Filling in an Error Log Packet

The error log packet is defined as follows:
typedef struct _IO_ERROR_LOG_PACKET {
    UCHAR MajorFunctionCode;
    UCHAR RetryCount;
    USHORT DumpDataSize;
    USHORT NumberOfStrings;
    USHORT StringOffset;
    USHORT EventCategory;
    NTSTATUS ErrorCode;
    ULONG UniqueErrorValue;
    NTSTATUS FinalStatus;
    ULONG SequenceNumber;
    ULONG IoControlCode;
    LARGE_INTEGER DeviceOffset;
    ULONG DumpData[1];
} IO_ERROR_LOG_PACKET, *PIO_ERROR_LOG_PACKET;

The driver should fill in an error log packet with the following data:

MajorFunctionCode
Indicates the IRP_MJ_XXX from the driver’s I/O stack location in the current IRP.
RetryCount
Indicates how many times the driver has retried the operation and encountered this error.

RetryCount is a zero-based value. That is, the driver should set it to zero when an error is first encountered for the current IRP.

DumpDataSize
Indicates the size in bytes required for any DumpData the driver will set in the packet.

The specified value should be an integral multiple of sizeof(ULONG).

NumberOfStrings
Indicates how many insertion strings the driver will supply with this packet. NT drivers set this value to zero for errors that need no insertion strings.

The NT error-logging thread can use these driver-supplied, zero-terminated Unicode strings to fill in messages written into the Win32 event log, where it can be examined using the Win32 event viewer. The I/O Manager assumes that the initial insertion string, if any, is either the name of the driver or of the device on which the error occurred.

Insertion strings provided by a driver should be language independent. Drivers that log errors and use insertion strings should use strings read from the registry or use names which are language independent or are the same in any language, for example, file names.

In most cases, NT device and intermediate drivers can simply log I/O errors without having to supply insertion strings for a higher-level event-logging component and/or without having to set up a driver-specific event-logging component. Of the system-supplied NT drivers, only network device drivers currently supply insertion strings in error log packets.

StringOffset
Indicates the offset, immediately following the DumpData, at which any driver-supplied insertion string data begins.

If a driver supplies this data, each string must be a zero-terminated Unicode string.

EventCategory
For drivers that install themselves in the registry as event-logging components, this is a driver-defined value that was specified in the driver’s message file for categories.

Most NT device and intermediate drivers set this value to zero. For more information about setting up an event-logging component, see Section 16.8.4.

ErrorCode
Indicates the type of error.

This is a system-defined or driver-defined constant, as described in Section 16.6.5.

UniqueErrorValue
Indicates where the error was detected in the driver.
FinalStatus
Indicates the value set in the I/O status block of the IRP when it was completed or the STATUS_XXX returned by a support routine the driver called.
SequenceNumber
Indicates a driver-assigned sequence number for the current IRP, which should be constant for the life of a given request.
IoControlCode
Indicates the I/O control code from the driver’s I/O stack location in the current IRP if the MajorFunctionCode is IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL. Otherwise, this value should be zero.

For more information about I/O control codes and device I/O control requests, see the Kernel-Mode Driver Reference.

DeviceOffset
Indicates the offset into the device where the error occurred.
DumpData
Can be used to store driver-specific data, such as register values or whatever else the driver writer decides would be useful for identifying the cause of the error.

Any driver-supplied insertion strings must be supplied immediately following the dump data, starting at StringOffset.


16.6.3 Setting NTSTATUS Values in Error Log Packets

Both the ErrorCode and FinalStatus members of the error log packet are of type NTSTATUS. Figure 16.5 shows the layout for NTSTATUS values.

Figure 16.5 NTSTATUS layout

NT drivers must set an NTSTATUS-type IO_ERR_XXX value for ErrorCode and a STATUS_XXX value for FinalStatus in each error log packet.

The system supplies a set of public IO_ERR_XXX constants for setting the ErrorCode in an error log packet. For example, NT drivers can use the following system-defined constants:
IO_ERR_RETRY_SUCCEEDED
IO_ERR_INSUFFICIENT_RESOURCES
IO_ERR_CONFIGURATION_ERROR
IO_ERR_INCORRECT_IRQL
IO_ERR_INVALID_IOBASE
IO_ERR_DRIVER_ERROR
IO_ERR_PARITY
    :     :
IO_ERR_OVERRUN_ERROR
IO_ERR_TIMEOUT
IO_ERR_CONTROLLER_ERROR
IO_ERR_INTERNAL_ERROR

The system also supplies a set of public STATUS_XXX values that NT drivers can use to set FinalStatus in error log packets and can return from their standard driver routines for NTSTATUS. For example, NT drivers can return the following system-defined constants for NTSTATUS from their standard driver routines, can set any of the following in the I/O status block of IRPs, and can set some of the following for FinalStatus in error log packets:
STATUS_SUCCESS
STATUS_DEVICE_CONFIGURATION_ERROR
STATUS_DRIVER_INTERNAL_ERROR
STATUS_INVALID_DEVICE_STATE
STATUS_IO_DEVICE_ERROR
    :     :
STATUS_DEVICE_BUSY
STATUS_DEVICE_DOES_NOT_EXIST
STATUS_ADAPTER_HARDWARE_ERROR

Public IO_ERR_XXX and STATUS_XXX constants are system resources, and each such STATUS_XXX is mapped to a corresponding Win32 constant by the system. Because they are built into Windows NT, public IO_ERR_XXX and STATUS_XXX constants can be added to the system only with the cooperation of Microsoft Corporation.


16.6.4 Calling IoWriteErrorLogEntry

When an NT driver has filled in the data for the packet returned by IoAllocateErrorLogEntry, it must call IoWriteErrorLogEntry so the NT error-logging thread will write its entry in the error log file.

Whenever possible, an NT driver should log an error, and fail the IRP if necessary, but continue to run when the driver encounters unusual or unexpected I/O errors.

A retail NT driver should never call KeBugCheckEx (nor KeBugCheck) to bring down the system. However, KeBugCheckEx can be used for debugging under-development drivers.


16.6.5 Defining New IO_ERR_XXX

NT driver writers can create driver-specific private IO_ERR_XXX codes of type NTSTATUS within the driver’s xxxlog.mc resource file.

The NTSTATUS Facility field, shown in Figure 16.5, for such an error must be set to FACILITY_IO_ERROR_CODE. The driver writer must supply a Sev value and a unique Code value for each new IO_ERR_XXX. Such a driver also must set the C bit in its new IO_ERR_XXX.

The Sev field shown in Figure 16.5 indicates the severity code, which must be one of the following system-defined values:

STATUS_SEVERITY_SUCCESS
Indicates STATUS_SUCCESS was set for FinalStatus, and a value such as IO_ERR_RETRY_SUCCEEDED for an ErrorCode value in error log packets.

While STATUS_PENDING also belongs to the STATUS_SEVERITY_SUCCESS category, NT drivers would not log an error on a pending IRP.

STATUS_SEVERITY_INFORMATIONAL
Indicates an informational NTSTATUS value, such as STATUS_SERIAL_MORE_WRITES.
STATUS_SEVERITY_WARNING
Indicates a warning NTSTATUS value, such as STATUS_DEVICE_PAPER_EMPTY.
STATUS_SEVERITY_ERROR
Indicates an error NTSTATUS value, such as STATUS_INSUFFICIENT_RESOURCES for a FinalStatus value or IO_ERR_CONFIGURATION_ERROR for an ErrorCode value in error log packets.

Most public IO_ERR_XXX constants belong to the STATUS_SEVERITY_ERROR category.

If the designer of an NT driver decides to make a set of driver-defined errors visible to system administrators or end users through the Win32 event viewer, such a driver must set itself up as an error-logging component in the registry, as described in Section 16.8.4.


16.6.6 Defining Private NTSTATUS Constants

The writer of a new pair (class/port or video display/miniport) of NT drivers also can define driver-specific STATUS_XXX values to communicate information about privately defined IRP_MJ_INTERNAL_DEVICE_CONTROL requests from the lower to the higher driver of the pair.

Such a class driver must map any private STATUS_XXX value to a system-defined NTSTATUS value when it completes an IRP if an existing higher-level driver’s IoCompletion routine might be called for that IRP. For paired display and NT video miniport drivers, the video port driver does the mapping between public STATUS_XXX values and the Win32-defined constants returned by video miniport drivers. For more information about video miniport drivers, see the Graphics Driver Design Guide.

NT driver writers who define private STATUS_XXX values for a set of IRP_MJ_INTERNAL_DEVICE_CONTROL requests should set the Facility field, shown in Figure 16.5, to an appropriate driver-defined constant that identifies the type of device.

NT driver writers also must set the customer code flag, labelled C in Figure 16.5, when they define private NTSTATUS values for add-on drivers, and must set the Sev field with an appropriate value, as described previously in Section 16.6.5


16.7 Handling Removable Media

NT file systems and removable-media device drivers share the responsibility for ensuring that the correct media is mounted when a file is opened on a removable-media device and that the correct media remains mounted during operations that access the media. Any NT intermediate driver layered between such a file system and removable-media device driver also shares this responsiblity.

Responding to FS-Initiated Check-Verify Requests

At its discretion, the file system can send an IRP to the device driver’s Dispatch entry point for IRP_MJ_DEVICE_CONTROL requests with Parameters.DeviceIoControl.IoControlCode in the I/O stack location set to the following:

IOCTL_XXX_CHECK_VERIFY
where XXX is the type of device, such as DISK, TAPE, or CDROM.

The type DISK includes both unpartitionable (floppy) and partitionable removable-media devices.

If the underlying device driver determines that the media has not changed, the driver should complete the IRP, returning the IoStatus block with the following values:
Status | set to STATUS_SUCCESS
Information | set to zero

In addition, if the device type is DISK or CDROM and the caller specified an output buffer, the driver returns the media change count in the buffer at
Irp->AssociatedSystemBuffer and sets IoStatus.Information to sizeof(ULONG). By returning this count the driver gives the caller an opportunity to determine whether the media has changed from its perspective.

If the underlying device driver determines that the media has changed, it should do the following:

  • If the volume is mounted (the VPB_MOUNTED flag is set in the VPB):

    1. Set the Flags in the DeviceObject by ORing Flags with DO_VERIFY_VOLUME.

    2. Set the IoStatus block in the IRP to the following:

      Status STATUS_VERIFY_REQUIRED

      Information zero

    3. Call IoCompleteRequest with the input IRP.

  • If the volume is not mounted, the driver must not set the DO_VERIFY_VOLUME bit. The driver should set IoStatus.Status to STATUS_IO_DEVICE_ERROR, set IoStatus.Information to zero, and call IoCompleteRequest with the IRP.
Notifying the File System of Possible Media Changes

An NT removable-media device driver must ensure that the media is not changed for the device represented by the DeviceObject (input to every driver routine that is sent an IRP) whenever the driver processes an IRP that requests a transfer to/from the media or a device I/O control operation that affects the media. The best possible time to check for changed media is just after the transition from a no-media-present state to a media-present state if the physical device always notifies the driver about these state changes.

If its physical device indicates that the state of the media might have changed before the driver begins an I/O operation or during an operation, the driver must do the following:

  1. Ensure that the volume is mounted by checking the VPB_MOUNTED flag in the VPB. (If the volume is not mounted, the driver must not set the DO_VERIFY_VOLUME bit. The driver should set IoStatus.Status to STATUS_VERIFY_REQUIRED, set IoStatus.Information to zero, and call IoCompleteRequest with the IRP.)

  2. Set the Flags in the DeviceObject by ORing Flags with DO_VERIFY_VOLUME.

  3. Set the IoStatus block in the IRP to the following:

    Status STATUS_VERIFY_REQUIRED

    Information zero

  4. Before completing any IRP with an IoStatus block in which the Status field is not set to STATUS_SUCCESS, the driver must call IoIsErrorUserInduced, which returns a Boolean TRUE for any of the following Status values:

    STATUS_VERIFY_REQUIRED
    STATUS_NO_MEDIA_IN_DEVICE
    STATUS_WRONG_VOLUME
    STATUS_UNRECOGNIZED_MEDIA
    STATUS_MEDIA_WRITE_PROTECTED
    STATUS_IO_TIMEOUT
    STATUS_DEVICE_NOT_READY

    If IoIsErrorUserInduced returns TRUE, the driver must call IoSetHardErrorOrVerifyDevice so the FSD can send a popup to the user, who can then supply the correct media, decide to retry the original request, or decide to cancel the requested operation.

Checking the DeviceObject->Flags

For each IRP requesting an I/O operation to/from removable media, a removable-media device driver must determine whether DO_VERIFY_VOLUME is already set in its DeviceObject->Flags. If this value is set, the driver must do the following:

  • For IRP_MJ_READ, IRP_MJ_WRITE, and certain IRP_MJ_DEVICE_CONTROL requests, check whether the I/O stack location Flags is set with SL_OVERRIDE_VERIFY_VOLUME. If it is, continue the requested operation.

    Device control request that return information about the logical structure of the underlying media have SL_OVERRIDE_VERIFY_VOLUME set in the I/O stack location Flags when an IFS mounts or remounts a removable-media volume.

  • Otherwise, the driver must refuse to carry out I/O requests for the corresponding drive, device, or partition while DO_VERIFY_VOLUME is set in its DeviceObject->Flags. A removable media driver must fail IRPs sent to the corresponding device as described in the preceding subsection, repeating both Steps 2 and 3 for each IRP until the FSD clears DO_VERIFY_VOLUME in the removable-media driver’s DeviceObject->Flags.

Otherwise, if a removable-media device driver neglects to fail IRPs when DO_VERIFY_VOLUME is set and SL_OVERRIDE_VERIFY_VOLUME is not set for the preceding transfer requests, the file system can neither maintain the integrity of cached file data nor cause the user to be prompted to remount the media that holds an open file.

Setting up IRPs in Intermediate Drivers

Any intermediate driver layered between an NT file system driver and a removable-media device driver must set up the next-lower-level driver’s I/O stack location in IRPs. From incoming IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests, such an intermediate driver must copy its own I/O stack location Flags into the next-lower-level driver’s I/O stack location when it sets up the I/O stack location for the lower driver.

If such an intermediate driver allocates new IRPs for lower-level removable-media drivers, it must set up the IRPs it allocates as follows:

  • For transfer requests, it must set up the thread context in each driver-allocated IRP from the value at Tail.Overlay.Thread in the original IRP.

  • For IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests, it must copy the I/O stack location Flags from the original IRP to each driver-allocated IRP.

Otherwise, the file system can neither maintain the integrity of cached file data nor cause the user to be prompted to remount the media that holds an open file.


16.8 Using the Registry

Some of the information in the Windows NT registry is persistent across system boots after it has been set up, particularly information about things like an end user’s graphics display preferences, application-specific environment settings, and so forth.

However, NT reconstructs some of the registry’s configuration information every time the system is loaded so that new drivers, hardware components, and peripheral devices can be added to machines running Windows NT.

In many RISC-based platforms, the ARC firmware provides an execution environment for the system boot loader and gathers configuration information about the hardware components in the machine. In x86-based platforms, a “hardware recognizer” component called NtDetect provides comparable functionality.

These NT boot components “poke” the hardware and/or ROM BIOS in the machine to collect all hardware information that they can about the following:

  • The number and type of microprocessors and coprocessors, if any, in the machine

  • The firmware caches

  • The I/O buses present and the type of interface, such as internal, ISA, EISA, MCA, PCI, etc. for each I/O bus

  • The devices detectable on each I/O bus

  • The disk, floppy, or CD-ROM from which the operating system is to be loaded

  • The monitor on which strings are displayed early in the system boot process and the particular video adapter that controls it at that time

  • The keyboard and pointing devices

  • The parallel and serial ports

  • The network card(s)

The ARC or NtDetect component collects all the hardware information it can find into a tree structure, and the OS loader writes this information into the registry for NT device drivers to use and to supplement when they, in turn, are loaded.

Higher-level NT drivers, which are loaded after device drivers, can consult the registry to get information about already loaded lower drivers and the underlying devices. Still higher-level user-mode drivers at the protected-subsystem level also can use some of the registry information supplied by NT device drivers.

Naturally, NT drivers for different types of peripheral devices use the registry to get and set somewhat device-type-specific information. Every NT driver can get any driver-specific or user-supplied machine-specific parameters that are necessary for the driver to initialize itself. Each NT driver’s parameters are stored in the registry.

Some higher-level NT drivers, such as the NT SCSI class drivers, actually do not get hardware information from the registry when they initialize themselves. Instead, SCSI class drivers get any HBA-specific configuration information from the NT SCSI port driver. As a “lowest-level” NT driver, the NT SCSI port driver does use the registry both to get and to set hardware information about each SCSI HBA in the machine.

In other words, NT device drivers or the NT video and SCSI port drivers on behalf of their respective miniport device drivers can get system-supplied hardware information from the registry, and they must set device-specific hardware configuration information in the registry. Higher-level NT drivers can use the registry information supplied by device drivers, by other lower-level drivers, or by the user as deemed necessary or useful by each driver writer.

An NT device driver also can get I/O bus-specific device configuration information by calling HAL-supplied support routines.

Every NT device driver must claim the bus-relative hardware resources, such as I/O port and memory ranges, interrupt vector/IRQL, and/or DMA channel/port, if any, that its device uses, in the registry when the driver initializes. Claiming a device’s hardware resources in the registry prevents subsequently loaded drivers from accessing an already initialized device adapter/controller and from claiming the same resources as an already loaded driver.

Because NT drivers vary in how they use the registry, this section contains an overview, rather than a device-type-specific or driver-level-specific discussion, of the following topics:

  • What registry information is made available to NT drivers when they initialize themselves

  • How a typical NT device driver uses information supplied in the registry and/or supplements the hardware configuration information in the registry when the driver is loaded

  • How higher-level NT drivers can use system-supplied and device-driver-supplied configuration information contained in the registry


16.8.1 Registry Paths Supplied to NT Drivers

When its DriverEntry routine is called, each NT driver is given a pointer to a Unicode string specifying a path to a driver-specific key in the registry, along with a pointer to the driver object. Every driver object also contains a pointer to such a string specifying a path to another key in the registry. Figure 16.6 illustrates the registry paths given to NT drivers when they initialize.

Figure 16.6 Registry Paths for NT Drivers

As Figure 16.6 shows, the HardwareDatabase pointer in the driver object and the RegistryPath pointer input to the DriverEntry routine each identify particular keys in the registry. A DriverEntry routine is given read access to the Unicode strings at RegistryPath and HardwareDatabase, but it must call support routines to manipulate registry keys and the configuration information they contain.

Under each key in the registry is either a set of named values with assigned value data or additional subkeys (eventually) with a set of values. The combination of a named value and its corresponding data is called a value entry.

NT provides the following sets of support routines that NT drivers call, depending on how a particular driver gets hardware configuration information and/or uses the registry:

  • The I/O Manager supplies the following routines that NT device drivers can call to get hardware configuration information from and to claim bus-relative and device-specific hardware resources in the registry:

    • IoQueryDeviceDescription to retrieve hardware configuration information collected by the ARC or NtDetect component and written into the registry by the OS loader

    • IoReportResourceUsage to either claim specified hardware resources for one or more devices in the registry or return whether a resource conflict exists

    • IoAssignResources to claim a set of configurable (preferred and alternative) hardware resources for a device in the registry and to return information about which resources it claimed for the caller

  • The HAL supplies the following routines that device drivers can call to get bus-type-specific hardware configuration information or to claim hardware resources in the registry:

    • HalGetBusData to return bus-relative configuration information for the device, if any, at a particular location or slot on an I/O bus, which is equivalent to calling HalGetBusDataByOffset with a zero Offset

    • HalGetBusDataByOffset to return bus-relative configuration information, starting at a given offset, for the device, if any, at a particular slot; for a specified nonzero Offset, on a dynamically configurable I/O bus with a published, standard interface

    • HalAssignSlotResources to claim hardware resources for the driver of a device at a given slot on a dynamically configurable I/O bus

  • The system supplies a number of kernel-mode runtime library routines (Rtl..Registry Xxx) that can be used to query the value entries of keys with names known to the driver, to supply default value entries for “missing” keys, and to modify the value entries of keys.

    Drivers that get or set information under the registry keys \Registry\Machine\System\CurrentControlSet\Services\DriverName and/or \Registry\Machine\Hardware\DeviceMap can use the Rtl..Registry Xxx routines. Note that the caller either must “know” the DriverName value or create a subkey under a key with a known name to call these Rtl..Registry Xxx routines.

  • The NT Configuration Manager supplies a set of Zw..Key routines that drivers can use to open a registry key with a name known to the driver, to enumerate the immediate subkeys of an open key and get their respective value entries, which always includes the the name of the subkey, to query the value entries for a particular key, or to modify the value entries of keys in the registry.

Some NT device drivers also call IoCreateSymbolicLink to create symbolic links between user- or administrator-supplied names for the driver’s device(s) and the corresponding NT device object names. For more information about how to use the Zw..Key routines in conjunction with IoCreateSymbolicLink, see Section 16.9.


16.8.1.1 RegistryPath, DriverName, and Device Object Names

As shown in Figure 16.6, RegistryPath points to the driver-specific key \Registry\Machine\System\CurrentControlSet\Services\DriverName. The value entry of the key DriverName is a Unicode string that identifies the driver, such as “Serial.”

\DriverName\Parameters

One or more subkeys under each DriverName key include the driver-specific Parameters with value entries needed to load the driver, and can also include one or more subkeys for device-specific Parameters needed to initialize each device a particular NT driver controls.

If an NT device driver needs hardware configuration information that the ARC or NtDetect component cannot “find,” that the driver cannot get from its device, or that the HAL cannot get from the bus, this information should be supplied under the ..\DriverName registry key, so that the driver can get this hardware configuration information from its Parameters key(s) when it is loaded.

An NT driver loses its RegistryPath pointer when its DriverEntry routine returns. If a driver-supplied Reinitialize routine uses this path, an NT driver should save the RegistryPath pointer that is input to its DriverEntry routine.

Note that the DriverName value along the RegistryPath string is not necessarily the generic name for a type of physical device like “Serial.” NT drivers generally cannot use this string to create names for their device object(s).

Calling IoGetConfigurationInformation to Determine Device Object Names

Names for device objects are also Unicode strings but have the form \Device\GenericDeviceTypeDigit, such as \Device\Harddisk1\Partition2, as also discussed in Chapters 4 and 5. However, NT drivers for certain kinds of devices can call IoGetConfigurationInformation to determine the Digit value each driver should append to the name of each device object that it creates to represent a physical device.

Some higher-level NT drivers also might call IoGetConfigurationInformation to determine how many device objects for a particular kind of physical device have been created by lowest-level drivers.

IoGetConfigurationInformation returns a CONFIGURATION_INFORMATION-type pointer to the I/O Manager’s structure. NT device drivers set the following about certain types of physical devices in this structure as these drivers are being loaded:

ULONG DiskCount;                       // Harddisks already named
ULONG FloppyCount;                     // Floppy drives already named
ULONG CdRomCount;                      // CdRom drives already named
ULONG TapeCount;                       // Tape drives already named
ULONG ScsiPortCount;                   // ScsiPort (HBAs) already named
ULONG SerialCount;                     // Serial ports already named
ULONG ParallelCount;                   // Parallel ports already named
BOOLEAN AtDiskPrimaryAddressClaimed;   // 0x1f0 - 0x1ff
BOOLEAN AtDiskSecondaryAddressClaimed; // 0x170 - 0x17f

Drivers of the preceding kinds of physical devices can use the current value in this global system structure to name their device objects with the correct Digit suffix. For example, if the current value of DiskCount is one, a disk device driver would name its driver-created device objects for two physical disks \Device\Harddisk1 and \Device\Harddisk2. Note that the configuration information counts are one-based while device object Digit suffixes are zero-based values. Such a driver must increment the DiskCount value in the configuration information structure for each device object that it creates to represent a physical disk.

The last two members of the configuration information structure indicate whether either of the bus-relative I/O address spaces used by “AT” disk (WD1003-compatible) controllers is being claimed by the driver. A subsequently loaded disk driver for a device with an “AT-compatible” mode might use this information.

A higher-level NT SCSI class driver can call IoGetConfigurationInformation to determine how many HBAs are present in a particular machine, as well as how many Harddisk, Floppy, CdRom, Tape, Serial, or Parallel devices have already been named. Since SCSI class and filter drivers are loaded after the NT SCSI port and HBA-specific miniport drivers, ScsiPortCount indicates the total number of HBAs in the machine. A SCSI class driver can use the value of ScsiPortCount as a loop-control variable to determine which HBAs drive buses with attached peripheral devices of the class driver’s type. For more information about initializing SCSI drivers, see also Appendix A.


16.8.1.2 Load-control Sets for NT Drivers

As shown in Figure 16.6, RegistryPath points to a subkey of \Registry\Machine\System\CurrentControlSet\Services\DriverName.

CurrentControlSet is a symbolic link to a particular ControlSet nnn in the registry. Each ControlSet nnn is a named key with subkeys and value entries that specify the load order for a set of NT drivers.

Every ControlSet nnn has the following two subkeys:

  • Control, which holds miscellaneous machine-specific value entries, such as the size of nonpaged pool, the ServiceGroupOrder that specifies the load order for each group of layered NT drivers, and the GroupOrderList that controls the load order of drivers within a group. For example, an excerpt of the Control subkey might be:

    Control
        :
    GroupOrderList
            :
        SCSI CDROM Class:REG_BINARY:02 00 00 00 01 ...
        SCSI Class:REG_BINARY:03 00 00 00 01 ...
        SCSI miniport:REG_BINARY:24 00 00 00 00 ...
            :
    :
    ServiceGroupOrder
        List:REG_MULTI_SZ:System Bus Extender
                          SCSI miniport
                          port
                          Primary disk
                          SCSI class
                          SCSI CDROM class...
                              :

    This ServiceGroupOrder example indicates that HBA-specific SCSI miniport drivers (each linked to the NT SCSI port driver, which is implemented as a dynamic-link library) should be loaded before other drivers in the Primary disk group (such as add-on disk array, “AT” disk, and/or ABIOS disk drivers), followed by the SCSI class drivers, and so on.

    The GroupOrderList defines the load order of drivers within each group. The first number in the list defines how many tags are used; in the above example, there are 03 tags for SCSI Class. The rest of the list defines the order in which drivers with those tags are loaded. The tags are not necessarily listed in increasing numerical order. The tag assignment for a driver is defined in its entry in the Services key.

  • Services, which holds driver-specific information about how and when during system initialization a given driver is loaded and which, if any, lower-level drivers must be loaded first. For example, the Services subkeys pertaining to the SCSI class group might have:

    Services
    :
    Disk
        DependOnGroup:REG_MULTI_SZ:SCSI miniport
        ErrorControl:REG_DWORD:0  // = SERVICE_ERROR_IGNORE
        Group:REG_SZ:SCSI class
        Start:REG_DWORD:0     // = SERVICE_BOOT_START
        Tag:REG_DWORD:0x2
        Type:REG_DWORD:0x1    // = SERVICE_KERNEL_DRIVER
    :
    Scsiscan
        DependOnGroup:REG_MULTI_SZ:SCSI miniport
        ErrorControl:REG_DWORD:0   // = SERVICE_ERROR_IGNORE
        Group:REG_SZ:SCSI class
        Start:REG_DWORD:0x1   // = SERVICE_SYSTEM_START
        Tag:REG_DWORD:0x3
        Type:REG_DWORD:0x1    // = SERVICE_KERNEL_DRIVER
    :

    This Services example indicates:

    • These drivers are NT drivers (Type = SERVICE_KERNEL_DRIVER), rather than user-mode subsystem-specific drivers.

    • Whether each driver must be loaded by the NT boot loader because the system itself might be loaded from the SCSI class driver’s physical device (Start = SERVICE_BOOT_START) or during the next phase of system initialization (Start = SERVICE_SYSTEM_START). For more information about writing NT drivers for boot-from floppy and CD-ROM devices, see also Section 16.9.2.

    • These drivers belong to the SCSI class Group in the ServiceGroupOrder of the Control section for this ControlSet nnn.

    • What order these drivers will load within the SCSI class group (Tag). The position of a driver’s Tag value in the CurrentControlSet\Control\GroupOrderList entry determines when that driver loads relative to other drivers in the group. A driver that has no Tag value in this list is loaded last within its group.

    • If these drivers cannot be loaded, the system will skip them. (ErrorControl = SERVICE_ERROR_IGNORE). Other drivers have an ErrorControl value of SERVICE_ERROR_NORMAL, which means that the system will continue the boot process, possibly trying the LastKnownGood ControlSet mmm, if those drivers fail to initialize and cannot be loaded.

    • These drivers cannot be loaded (DependOnGroup) unless at least one driver from the SCSI miniport Group in the ServiceGroupOrder section has already been loaded.

A new driver can be loaded in NT machines by adding appropriate subkeys with value entries for the driver to the CurrentControlSet in the registry.

For example, a new SCSI miniport driver that enables “WD1003 emulation” on its HBA should be loaded before any other SCSI miniport drivers so that it can claim an AtDisk..AddressSpace in the I/O Manager’s configuration information structure, described in Section 16.8.1.1. On the other hand, a SCSI miniport driver for an HBA that does not support a BIOS should be loaded following all miniport drivers of HBAs that do.

Using Regedt32 to Load a New NT Driver

To create a driver-specific key in the registry for an under-development driver and to make it loadable, the driver writer can use the Windows NT registry editor, regedt32, to modify and add appropriate keys and their value entries in the registry. For example, the driver writer would modify the CurrentControlSet\Services key to get the under-development driver to load and add driver-specific keys, such as the DriverName\Parameters with appropriate value entries or subkeys with value entries.

However, every NT driver writer must decide whether to rely on a system administrator or end user to “install” the retail driver in the registry with regedt32, to use a setup utility, to have the driver set itself up in the registry, or to use some combination of these methods. For more information about these alternatives, see Sections 16.8.5 and 16.9.1 and the Programmer’s Guide.

Note that the ServiceGroupOrder key is reset to its system-supplied value on a system upgrade. Drivers that modify this key may need to be reinstalled after a system upgrade.

LastKnownGood Control Set

The registry always contains additional load-control specifications, including a backup specification called LastKnownGood, in case modifications to the CurrentControlSet make it impossible to load the system.

Using the \Regisry\Machine\System Tree in Higher-Level NT Drivers

Note that the registry has other subkeys under the path \Registry\Machine\System, shown in Figure 16.6, that contain configuration information that is useful to higher-level NT drivers.

For example, the system-supplied ftdisk driver uses the subkey \Registry\Machine\System\Disk to find disk devices and logical partitions so it can set up device objects for its own mirror, stripe, stripe-with-parity, and volume sets, as mentioned in Chapter 5.


16.8.1.3 HardwareDatabase and the System DeviceMap

Higher-level NT drivers also can use the pointer at DriverObject->HardwareDatabase, which identifies the key \Registry\Machine\Hardware as shown in Figure 16.6, to get information from or to set information in a driver-created subkey under \Registry\Machine\Hardware\DeviceMap.

All driver-created subkeys of DeviceMap are volatile: that is, they are recreated every time the system boots. Any NT driver can supply driver-determined value entries in its DeviceMap subkey. Thus, an NT driver can set up a subkey under the DeviceMap containing any information that the driver writer chooses.

Usually, such a subkey supplies information to be consumed by a higher-level driver or that is useful to a system administrator or end user. For example, the NT video port driver sets up aliases between its device objects’ names and the type of video adapter each represents. Such an alias might appear in the registry as something like the following:
\Device\Video0:REG_SZ:\REGISTRY\Machine\System\ControlSet001
    \Services\Vga\Device0

The system serial driver supplies a mapping between the generic NT names of its device objects and the corresponding Win32 names for serial ports. Such a mapping might appear in the registry as the following value entry:
Serial0:REG_SZ:COM1

While the NT I/O Manager’s generic naming scheme for device objects, as mentioned in Section 16.8.1.1, is designed to create a set of unique names for every device object that can be the target of an I/O request in any particular machine, end users and/or user-mode applications running within a protected subsystem generally use subsystem-specific device names to request I/O operations. For more information about setting up symbolic links between device object names and user-mode names for the same devices, see Section 16.9.1.

NT device drivers can get information from and must set information in the registry along the HardwareDatabase registry path. Device drivers get hardware configuration information, collected by the ARC or NtDetect component, under the key \Registry\Machine\Hardware\Description. They must set configuration information about the hardware resources claimed for their device(s) under the key \Registry\Machine\Hardware\ResourceMap, as explained in Sections 16.8.2 and 16.8.3, respectively.


16.8.2 Getting Device Hardware Configuration Information

When an NT device driver is loaded, the OS loader has already set up the registry with any machine-specific information the ARC or NtDetect component could find. Depending on the I/O buses in the machine, this information can include the bus-relative interrupt vector for a particular peripheral connected on a particular bus, the bus-relative physical memory range or the bus-relative range of I/O ports that a particular device can use, or the bus-relative DMA controller channel/port to which a particular device is connected.

Each NT device driver must have this kind of hardware configuration information to initialize successfully. For example, a device driver must pass the bus-relative interrupt vector for its device to HalGetInterruptVector, as described in the section on interrupt objects in Chapter 3, to get a mapped system vector, system-assigned DIRQL, and processor affinity mask, required as parameters to IoConnectInterrupt. Otherwise, the driver cannot be loaded because it cannot register its ISR(s) to handle interrupts from its device(s).

If a driver needs hardware configuration information that the ARC or NtDetect component cannot supply, it can get some of the required information from its device or by calling HalGetBusData, particularly for an EISA or MCA device, or HalGetBusDataByOffset, particularly for a PCI device. Calls to these support routines can return more hardware configuration data than is available in the registry ..\Hardware\Description tree since they are called later in the system boot process. For more information about getting hardware configuration information with these routines, see Section 16.8.2.2. For specifics, see the Kernel-Mode Driver Reference.

Otherwise, the driver must must use the RegistryPath pointer to get its DriverName\Parameters, as mentioned in Section 16.8.1.1. An NT driver can always supplement the system-, bus-, or device-supplied hardware information with “user-supplied” Parameters under its registry key \Registry\Machine\System\CurrentControlSet\Services\DriverName . Frequently, these Parameters are set up in the registry when the user installs a new driver. For more information about using Parameters, see also Section 16.8.5. For more information about installing Windows NT drivers, see the Programmer’s Guide.

Every NT device driver must claim any hardware resources it needs for its device(s) in the \Registry\Machine\Hardware\ResourceMap tree, as described in Section 16.8.3. Any NT driver that cannot claim sufficient hardware resources for at least one device should release any system resources it is using, such a driver-created device object(s). Such a driver must not be loaded.


16.8.2.1 Calling IoQueryDeviceDescription

NT device drivers can call IoQueryDeviceDescription to obtain the system-detected and system-supplied hardware configuration information about their devices from the registry.

Supplying a ConfigCallback Routine

A device driver that calls IoQueryDeviceDescription must provide a ConfigCallback routine, defined as follows:

NTSTATUS
    (*PIO_QUERY_DEVICE_ROUTINE) (
        IN PVOID Context,                     //driver-determined
        IN PUNICODE_STRING PathName,
        IN INTERFACE_TYPE BusType,            //system-defined enum
        IN ULONG BusNumber,                   //system-assigned count
        IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
        IN CONFIGURATION_TYPE ControllerType, //system-defined enum
        IN ULONG ControllerNumber,
        IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
        IN CONFIGURATION_TYPE PeripheralType, //system-defined enum
        IN ULONG PeripheralNumber,
        IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
        );

The Context pointer passed to a ConfigCallback routine accesses a driver-determined storage area. However, the kernel-mode stack is limited in size: somewhat less than two pages are available for data storage by the DriverEntry and ConfigCallback routines and the amount of information returned cannot be predetermined. NT drivers usually call ExAllocatePool or ExAllocatePoolWithTag to allocate a paged pool buffer for the configuration information passed between their DriverEntry and ConfigCallback routines. The DriverEntry routine can free such a buffer when it has consumed and processed (as described in Section 16.8.3) the information supplied by its ConfigCallback routine.

Note the order of the bus, controller, and peripheral parameters to a ConfigCallback routine. The corresponding parameters to IoQueryDeviceDescription are declared to be optional, but the DriverEntry routine must supply appropriate values for at least one of the following sets of parameters when it calls IoQueryDeviceDescription:

  • BusType and BusNumber

  • ControllerType and ControllerNumber

  • PeripheralType and PeripheralNumber

Depending on the information requested when the DriverEntry routine calls IoQueryDeviceDescription, the PathName passed to the ConfigCallback routine points to a registry key that contains successively more device-specific and detailed information. For example, if a driver requests information only about a particular I/O bus in the machine, the PathName passed to the ConfigCallback routine would point to the key containing bus information. If the driver also requests information about both a ControllerType and a PeripheralType, the PathName parameter to the ConfigCallback routine would point to the registry key containing peripheral information.

Making Calls to IoQueryDeviceDescription

Figure 16.7 illustrates a device driver’s call to IoQueryDeviceDescription.

Figure 16.7 Getting Hardware Configuration Information from the Registry

Driver-supplied pointers for Context and the ConfigCallback routine are required parameters to IoQueryDeviceDescription. All other parameters are optional, so the caller can specify one or more of the bus, controller, and peripheral parameter sets, possibly in separate calls to IoQueryDeviceDescription with separate ConfigCallback routines. NT drivers usually need device-type-specific hardware information about a given ControllerType and/or PeripheralType.

System-defined enumerated values for the BusType parameter indicate the types of interfaces that I/O buses in Windows NT machines provide. The driver for a device that must be connected on an ISA bus would set BusType to the system-defined INTERFACE_TYPE value Isa when it called IoQueryDeviceDescription.

A device driver could also search for its devices on every I/O bus in the machine. Any driver can set BusType to successive values by using the system-defined value MaximumInterfaceType as a loop-control variable for calling IoQueryDeviceDescription, particularly if a driver constrains its query by specifying values for the ControllerType and/or PeripheralType parameters.

IoQueryDeviceDescription returns STATUS_OBJECT_NAME_NOT_FOUND if a particular type of bus is not available in the machine.

NT device drivers can use the following system-defined CONFIGURATION_TYPE values for the ControllerType and PeripheralType parameters to IoQueryDeviceDescription:
ControllerType Values: | PeripheralType Values:
DiskController | DiskPeripheral
TapeController | FloppyDiskPeripheral
CdromController | TapePeripheral
WormController | ModemPeripheral
SerialController | MonitorPeripheral
NetworkController | PrinterPeripheral
DisplayController | PointerPeripheral
ParallelController | KeyboardPeripheral
PointerController | TerminalPeripheral
KeyboardController | OtherPeripheral
AudioController | LinePeripheral
OtherController | NetworkPeripheral

Most of these defined type names are self-explanatory. However, the PeripheralType value MonitorPeripheral indicates a video monitor for which there is a corresponding video adapter. The value TerminalPeripheral indicates a so-called “dumb terminal” that displays data usually sent through a serial port. The value LinePeripheral indicates a peripheral device connected to the machine by a controller line.

Depending on which optional parameters the DriverEntry routine passes to IoQueryDeviceDescription, its ConfigCallback routine is given values for the corresponding parameters when it is called. A ConfigCallback routine can save these values (such as system-assigned bus numbers) in a temporary buffer, usually allocated from paged pool as described in Section 16.4.1.3 and released from the DriverEntry routine, or on the stack for subsequent calls to HAL routines.

IoQueryDeviceXxx Pointers Input to ConfigCallback

Depending on which optional parameters the DriverEntry routine passes to IoQueryDeviceDescription, a ConfigCallback routine is also given one or more nonNULL pointers to BusInformation, ControllerInformation, and PeripheralInformation, as shown in Figure 16.7. Each of these parameters is actually an array of pointers whose elements select ARC- or NtDetect-supplied information in the \Registry\Machine\Hardware\Description tree.

NT device drivers can use any of the following system-defined values to get to the kind(s) of bus, controller, or peripheral information they need from the registry:

  • IoQueryDeviceIdentifier points to any “hardware” name that the ARC component found for the device or that the x86-based OS loader created for the device, which the system translates into a Unicode string when this name is written into the registry.

  • IoQueryDeviceConfigurationData points to any bus-relative hardware configuration information that the ARC or NtDetect component found about the driver’s device.

  • IoQueryDeviceComponentInformation points to any information that the ARC or NtDetect component found about a device’s subcomponents.

As shown in Figure 16.7, NT device drivers usually get information from the registry of type CM_FULL_RESOURCE_DESCRIPTOR, for which they use the ControllerInformation[IoQueryDeviceConfigurationData] and/or PeripheralInformation[IoQueryDeviceConfigurationData] pointers.

Processing Configuration Information in ConfigCallback

As Figure 16.7 also shows, the IoQueryDevice Xxx pointers are valid only within the ConfigCallback routine. Moreover, NT drivers can read the value entries of subkeys under the key \Registry\Machine\Hardware\Description but cannot modify them. It is a ConfigCallback routine’s responsibility to copy pertinent system-supplied data it finds to a driver-allocated buffer (or driver-structured area on the stack) so the DriverEntry routine can use and, if necessary, supplement this information when the ConfigCallback routine returns control.

The CM_FULL_RESOURCE_DESCRIPTOR-type information shown in Figure 16.7 includes all controller- or peripheral-specific information that can be detected about one or more of the following:

  • An inclusive, but bus-relative, range of physical Port addresses

    The DriverEntry (or ConfigCallback) routine calls HalTranslateBusAddress to get a mapped system value that the driver uses (or remaps by calling MmMapIoSpace if HalTranslateBusAddress indicates that the Port range is in memory, rather than I/O space) to access its device ports thereafter.

  • An inclusive range of physical Memory addresses used by the device

    The DriverEntry (or ConfigCallback) routine calls HalTranslateBusAddress to get a mapped range, which it passes to MmMapIoSpace to get a remapped system range that the driver uses to access its device-dedicated memory thereafter.

  • The bus-relative Interrupt vector for its device and the IRQL for that vector

    The DriverEntry or ConfigCallback routine calls HalGetInterruptVector to get a mapped system vector, DIRQL, and processor affinity mask that the DriverEntry routine can pass to IoConnectInterrupt when it registers the driver’s ISR.

  • The bus-relative physical Dma channel or port (on MicroChannel-type buses) to which its device is connected

    The DriverEntry or ConfigCallback routine fills in a DEVICE_DESCRIPTION structure using this information, and calls HalGetAdapter so its device can do DMA transfer operations thereafter. For more information about devices that use DMA, see the section on adapter objects in Chapter 3.

Note that it is possible for a ConfigCallback routine to call HalTranslateBusAddress, HalGetInterruptVector, or HalGetAdapter, as long as it passes the values returned by these HAL routines to the DriverEntry routine. Note also that an NT device driver is required to call IoReportResourceUsage with the same values returned by IoGetDeviceDescription before it accesses the device, as explained in Section 16.8.3, so a ConfigCallback routine must not return control before it saves the information, if any, that it obtained from the registry for the DriverEntry routine.

The DeviceSpecificData, shown in Figure 16.7 at the tail of each partial resource descriptor in a partial resource list, is generally a device-type-specific structure. The system defines DeviceSpecificData structures for many kinds of peripheral devices. See ntddk.h for details.

On Return from ConfigCallback

Depending on the platform and/or type of bus, an NT driver might get only some of the machine-specific hardware information that it needs by calling IoQueryDeviceDescription. Depending on the device, some NT drivers also interrogate their devices to supplement system-supplied hardware configuration information.

While it supplies as much hardware configuration information as possible to each device driver, the system also requires each device driver to supply hardware configuration information about itself (and/or each of its devices) in the registry, as one of the following:

  • A CM_RESOURCE_LIST, containing one or more CM_RESOURCE_DESCRIPTORs, which can include DeviceSpecificData entries, that IoReportResourceUsage successfully claimed in the registry for the caller

  • An IO_RESOURCE_REQUIREMENTS_LIST of preferred and alternative (or fixed) hardware resources, from which IoAssignResources returns a CM_RESOURCE_LIST describing those it claimed in the registry for the caller

  • A pointer to a variable, at which HalAssignSlotResources returns the address of a buffered CM_RESOURCE_LIST, describing the hardware resources it claimed in the registry for a device on a dynamically configurable I/O bus with a published standard interface, such as PCI
NT device driver writers should consider the following an implementation guideline:

Every NT device driver must claim all the hardware resources in the machine that the driver and its device(s) need to carry out I/O operations before the driver actually initializes its device(s).

For more information about claiming these resources in the registry, see Section 16.8.3.


16.8.2.2 Calling HalGetBusData or HalGetBusDataByOffset

NT device drivers can call HalGetBusDataByOffset or HalGetBusData to get hardware configuration information, particularly for devices on EISA, MCA, or PCI buses.

Figure 16.8 illustrates a device driver’s call to HalGetBusData(ByOffset).

Figure 16.8 Getting Hardware Configuration Information from an I/O Bus

As Figure 16.8 shows, HalGetBusDataByOffset returns bus-type-specific configuration information for a particular slot (or location, depending on the input BusDataType value) on a particular I/O bus to a driver-allocated buffer.

An NT device driver can allocate memory for such a buffer from paged pool, as described in Section 16.4.1.3. The Length value passed to HalGetBusDataByOffset, which specifies the maximum amount of configuration data requested, usually is the NumberOfBytes that was passed in the driver’s call to ExAllocatePool or ExAllocatePoolWithTag.

HalGetBusDataByOffset returns a value indicating the number of bytes of configuration information it wrote at the given Buffer. The structure of returned bus-relative configuration information depends on the input BusDataType. For more specific information about these system-defined structures for a particular types of I/O buses and about HalGetBusDataByOffset and HalGetBusData, see the Kernel-Mode Driver Reference.

HalGetBusDataByOffset takes an Offset parameter that specifies the offset within a dynamically configurable bus’s configuration structure from which subsequent bytes should be returned in the driver-allocated buffer. For such an I/O bus, the structure of its configuration information is defined by the standard for that type of bus. Drivers of devices on such a bus also might call HalSetBusData or HalSetBusDataByOffset.

Drivers of devices on other types of buses that are not dynamically configurable also can call HalGetBusDataByOffset if they pass an Offset of zero. A call to HalGetBusData is equivalent to a call to HalGetBusDataByOffset with a zero Offset.

As Figure 16.8 suggests, a device driver can call HalGetBusDataByOffset more than once for a particular I/O bus to investigate the devices in each slot. A driver must call HalGetBusData to investigate each possible location for the BusDataType Cmos.

If there is more than one bus of a given BusDataType in the machine, an NT device driver also can increment the input BusNumber and call HalGetBusData or HalGetBusDataByOffset until the driver has looped through all slots on all I/O buses of that type in the machine.

Note that HalGetBusData or HalGetBusDataByOffset also might get configuration information about the number of I/O buses of a particular type in a machine from the registry, as suggested by Figure 16.8.


16.8.3 Claiming Hardware Resources

When a device driver’s ConfigCallback routine returns, its DriverEntry routine can use the configuration information collected from its subkeys under \Registry\Machine\Hardware\Description, from calls to HalGetBusData or HalGetBusDataByOffset, and/or from its Parameters subkey(s) under \Registry\Machine\System\CurrentControlSet\Services\DriverName in calls to one or more of HalTranslateBusAddress, HalGetInterruptVector, and HalGetAdapter. The driver can use the values returned by these HAL support routines as parameters to “higher-level” NT routines such as MmMapIoSpace, IoConnectInterrupt, HalAllocateCommonBuffer, and, after initialization IoAllocateAdapterChannel.

When an NT device driver has set up any NT objects that it needs, such as device objects, but before the driver attempts to access its device(s) registers or I/O ports, the DriverEntry routine must call one of the following support routines one or more times to write configuration information about the hardware resources the driver and its device(s) will be using into the registry:

  • IoReportResourceUsage, which returns information about whether the input hardware resources have already been claimed by a previously loaded driver so NT device drivers can determine whether it is possible to initialize their devices.

    Usually, device drivers that call IoQueryDeviceDescription also call IoReportResourceUsage, possibly after supplementing the returned hardware configuration information in a buffered CM_RESOURCE_LIST that includes DeviceSpecificData.

  • IoAssignResources with a buffered, driver-constructed IO_RESOURCE_REQUIREMENTS_LIST that specifies set(s) of hardware resources the driver (for all its devices) or a particular device can use, from which IoAssignResources returns a buffered CM_RESOURCE_LIST describing the set of resources it claimed in the registry.

    Usually, device drivers that call HalGetBusData also call IoAssignResources. Such a driver also might call IoReportResourceUsage with the CM_RESOURCE_LIST returned by IoAssignResources to which the driver has appended its DeviceSpecificData before calling IoReportResourceUsage.

  • HalAssignSlotResources to build, on behalf of the caller, an IO_RESOURCE_REQUIREMENTS_LIST that specifies sets of hardware resources the driver of a device at a particular slot on a dynamically configurable bus with a published, standard interface, such as PCI, can use and to return a buffered CM_RESOURCE_LIST describing the set of configuration resources it claimed in the registry for the device.

    Device drivers that call HalGetBusDataByOffset or HalGetBusData with an input BusDataType for such a dynamically configurable I/O bus can call HalAssignSlotResources. Such a driver also might call HalSetBusDataByOffset or HalSetBusData to configure its device on the bus.

A successful call to IoReportResourceUsage, IoAssignResources, or HalAssignSlotResources claims a set of hardware resources for the caller in the \Registry\Machine\Hardware\ResourceMap tree. Making such a call prevents device drivers that load later from trying to use the hardware resources already claimed by a loaded driver.

NT device driver writers should consider the following fact about calls to IoReportResourceUsage, IoAssignResources, or HalAssignSlotResources:

A subsequent call to any of the preceding support routines to claim resources for the same device(s) overwrites the existing resource claims for the driver or for the particular device in the registry ResourceMap.

Any NT device driver can call one or more of these routines more than once to claim resources for itself or for a particular device. If it does, the caller cannot attempt to claim a single resource at a time, assuming that its previously claimed resources remain claimed. Instead, such a caller must augment the list of claimed resources for the driver or for any particular device at each call to these support routines.


16.8.3.1 Calling IoReportResourceUsage

Figure 16.9 illustrates a device driver’s call to IoReportResourceUsage.

Figure 16.9 Claiming Hardware Resources with IoReportResourceUsage

As Figure 16.9 shows, each resource list that an NT driver writes into the registry under \Registry\Machine\Hardware\ResourceMap is a variable-sized array of CM_RESOURCE_DESCRIPTOR elements, containing the same information in the same form as shown in Figure 16.7.

In other words, values supplied for the driver or for each of its devices when it calls IoReportResourceUsage must specify the same CM_PARTIAL_RESOURCE_DESCRIPTOR Port, Interrupt, Memory, and Dma “hardware” values obtained from the driver’s call(s) to IoQueryDeviceDescription or by some other means.

A device driver frequently must supplement the information returned by IoQueryDeviceDescription, particularly with DeviceSpecificData, for each of its devices. For example, a floppy controller driver would fill in DeviceSpecificData information about the maximum density and mount density for each of the floppy drives it controlled, while a serial device driver would fill in information about the maximum baud clock for each of its serial ports. The system defines DeviceSpecificData structures for many kinds of peripheral devices, but an NT driver writer can define a new structure for any new device driver.

Making Calls to IoReportResourceUsage

When it calls IoReportResourceUsage, an NT device driver must supply the OverrideConflict and ConflictDetected parameters shown in Figure 16.9. The driver also must supply nonNULL and nonzero values for at least one of the following sets of parameters:

  • DriverObject, DriverList, and DriverListSize

  • DeviceObject, DeviceList, and DeviceListSize

In general, the resource descriptors at DriverList specify those hardware resources that the driver uses in common for all its devices. The resource descriptors at DeviceList specify those used for a particular device. An NT driver must call IoReportResourceUsage more than once if the physical devices it controls each use device-dedicated hardware resources or if these physical devices do not have identical values for DeviceSpecificData.

While the DriverClassName parameter to IoReportResourceUsage can be NULL, such a Unicode string determines whether the driver’s resource lists are written under a registry key that is named according to the type of device, such as \Registry\Machine\Hardware\ResourceMap\Video, or under the default key (...\Other). If a named key already exists for a new driver’s type of device, the driver writer should supply the appropriate Unicode string at DriverClassName when that driver calls IoReportResourceUsage. In addition, the driver writer should have the driver store its \Registry\Machine\Hardware\DeviceMap information, if any, under the key with the same name.

The DriverClassName parameter is also passed in calls to IoAssignResources and HalAssignSlotResources.

When an NT driver for an entirely new kind of peripheral device supplies a unique string at DriverClassName, IoReportResourceUsage (as well as IoAssignResources or HalAssignSlotResources) creates a new key with that name in the registry. The designer of such a driver should create a generic device-type name, and should use the same name for the driver’s key, if it creates one, under \Registry\Machine\Hardware\DeviceMap.

Handling Resource Conflicts

The Boolean value returned to the driver at ConflictDetected indicates whether it has attempted to claim a hardware resource that another device driver already claimed. When such a hardware resource conflict occurs, the driver that “found” the conflict should log an error, as described in Section 16.6.

If its device can be reconfigured dynamically, the driver that finds a resource conflict can rebuild the offending resource list and call IoReportResourceUsage again to overwrite its “conflicted” value entries under \Registry\Machine\Hardware\ResourceMap. However, for the driver of such a device, calling HalAssignSlotResources or IoAssignResources, as described in Section 16.8.3.2, is more efficient that calling IoReportResourceUsage repeatedly to loop through a set of alternative resource lists.

The OverrideConflict parameter to IoReportResourceUsage is a value of type BOOLEAN, which should be set to FALSE. If IoReportResourceUsage detects a conflict and OverrideConflict was set to FALSE, this support routine logs an error showing the exact cause of the conflict (memory locations or interrupt) and which driver was not loaded due to the resource conflict. IoReportResourceUsage does not write such a caller’s resources into the registry, but a system administrator or user then can determine why a particular device is not working by using the Win32 event viewer to examine the entries in the NT error log file.

When OverrideConflict is set to TRUE in a call to IoReportResourceUsage, the driver’s resource lists are written into the registry under the \Registry\Machine\Hardware\ResourceMap key even if another driver has already claimed a resource in this driver’s resource lists. If such a conflict exists, the driver that loads later does not override the other driver’s claim on the hardware resource itself, nor does IoReportResourceUsage log a resource-conflict error.

Releasing Resources

If IoReportResourceUsage returns TRUE at ConflictDetected and a driver cannot carry out I/O operations without the specified hardware resources, the DriverEntry routine must release every system resource it has set up (free memory it has allocated, disconnect its interrupt objects, delete its device objects, and so forth), because that driver will be unloaded when its DriverEntry routine returns control, before the driver’s Reinitialize routine, if any, is called.

When an NT driver’s Unload routine is called, it must call IoReportResourceUsage one or more times with the DriverList and/or DeviceList CM_RESOURCE_LIST in which the Count is set to zero or call IoAssignResources with appropriate parameters, as described in Section 16.8.3.2. Such a call removes the driver’s resource list from the registry, releasing the resource(s) it claimed in its DriverEntry routine. For more information about the Unload routine, see Chapters 4 and 15.


16.8.3.2 Calling IoAssignResources or HalAssignSlotResources

Figure 16.10 illustrates a device driver’s call to IoAssignResources.

Figure 16.10 Claiming Hardware Resources with IoAssignResources

As Figure 16.10 shows, the driver allocates a buffer, usually from paged pool as described in Section 16.4.1.3 but possibly on the stack at RequestedResources and fills it with an IO_RESOURCE_REQUIREMENTS_LIST structure containing sets of preferred and, possibly, alternative hardware resources for its device(s).

Making Calls to IoAssignResources

If an NT driver is claiming hardware resources in common for all its devices, it passes the DriverObject pointer input to the DriverEntry routine when it calls IoAssignResources. If a driver claims device-specific resources for each of its devices, it passes a NULL DriverObject pointer but the DeviceObject pointer representing a particular device in each call to claim device-specific resources.

The AllocatedResources pointer input to IoAssignResources is the address of a variable in which IoAssignResources returns the address of a buffer that it allocates for the caller. On return from a successful call to IoAssignResources, this buffer contains a CM_RESOURCE_LIST describing the set of hardware resources claimed in the registry for the caller. A driver can use these values in subsequent calls to HalTranslateBusAddress, HalGetInterruptVector, and/or HalGetAdapter.

An NT driver that calls HalAssignSlotResources also calls IoAssignResources indirectly. That is, HalAssignSlotResources allocates and sets up RequestedResources and calls IoAssignResources on behalf of its caller.

Note that the IO_RESOURCE_REQUIREMENTS_LIST structure does not have any DeviceSpecificData member as the resource descriptor(s) in the CM_RESOURCE_LIST returned by IoAssignResources or HalAssignSlotResources do. On return from a successful call to either of these routines, these DeviceSpecificData member(s) are empty. The caller can add appropriate DeviceSpecificData configuration information to the CM_RESOURCE_LIST and call IoReportResourceUsage, as described in the preceding section, to add this hardware configuration information to the registry.

As for IoReportResourceUsage, the DriverClassName parameter to IoAssignResources or HalAssignSlotResources is optional, but passing a NULL DriverClassName causes the caller’s resource claims to be written under the default key ..\Other. For more information about supplying an appropriate Unicode string at DriverClassName, see Section 16.8.3.1.

Handling Resource Conflicts and Releasing Resources

If IoAssignResources or HalAssignSlotResources does not return STATUS_SUCCESS and a driver cannot carry out I/O operations without the specified hardware resources, the DriverEntry routine must release every system resource it has set up (free memory it has allocated, disconnect its interrupt objects, delete its device objects, and so forth), because that driver will be unloaded when its DriverEntry routine returns control, before the driver’s Reinitialize routine, if any, is called.

When an NT driver’s Unload routine is called, it must call IoAssignResources with a NULL value for RequestedResources or call IoReportResourceUsage with appropriate parameters, as described in the preceding section. Such a call removes the driver’s resource list from the registry, releasing the resource(s) it claimed in its DriverEntry routine. For more information about the Unload routine, see Chapters 4 and 15.


16.8.4 Setting Up Driver-specific, User-visible Error Logging

Any NT driver that logs driver-defined I/O errors, as described in Section 16.6, can set itself up as an error-logging component of the system, which is also called an event-logging component within the Win32 subsystem programming environment.

Such a driver must create a new subkey in the registry under the key CurrentControlSet\Services\EventLog\System in order to have its driver-supplied message file made available to the Win32 event viewer or to any (subsystem-specific) application that reads the system error log file and displays Win32 events to users or system administrators. Note that a comparable application within another protected subsystem might use another, subsystem-specific term for Win32 “events.”

Such a driver should name its new subkey with a unique DriverName that is identical to the name of the driver’s executable. The driver-created subkey must include a value entry for the driver-supplied message file, which can have any unique name chosen by the driver writer. The value entry of a driver-supplied message file is a full pathname for the file.

Any NT device driver writer who sets up the value entries for a new DriverName subkey of the \EventLog\System key also should weigh the tradeoffs of supplying information that is truly useful to the customer who buys the device against the resources that setting up a Win32 event-logging component consumes.

An NT driver writer who sets up an event-logging driver should make its messages meaningful to the system administrator or end user who will use the Win32 event viewer (or some other application) to diagnose problems that have occurred while the system is running. The system error-logging mechanisms are not intended to be, nor should they be used for, debugging under-development NT drivers. Instead, they should provide useful information to an NT driver writer’s retail customers.

For more information about the Windows NT event-logging mechanisms and how to set up Source, which corresponds to the NT DriverName, subkeys and message files, see the documentation provided with the Win32 SDK.


16.8.5 Using the RegistryPath Parameters

NT drivers for some types of devices must rely entirely on the value entries under \..\Machine\System\CurrentControlSet\Services\DriverName\Parameters at RegistryPath (see Figure 16.6) when they are loaded because their calls to IoQueryDeviceDescription (see Section 16.8.2.1) do not provide sufficient hardware configuration information, because HalGetBusDataByOffset or HalGetBusData cannot return sufficient information (particularly for ISA-type buses), or because the device itself does not supply everything the driver needs to initialize itself and to claim hardware resources in the registry, as described in Section 16.8.3.

Relying on a system administrator or end user to set up necessary subkeys and/or value entries under \DriverName\Parameters can present the driver writer with challenges, such as the following:

  • A system administrator or user who sets up value entries for one or more subkeys under the driver’s Parameters key can supply an entirely arbitrary subkey name from the driver writer’s point of view.

  • User-supplied value entries under Parameters subkeys can specify a user-assigned name for the corresponding device, so the driver must create a symbolic link between the generic NT name of its device object and the corresponding user-assigned name, as described in Section 16.9.

  • Any driver that has no limit on the number of devices of a particular type it can support must create an undefined number of named device objects and, possibly, symbolic links for user-assigned names in any particular machine.

Such a driver can use the registry when it initializes to determine how many devices it must support, find any user-supplied Parameters subkey value entries (possibly under user-chosen names for the subkeys) for particular devices, supply default hardware configuration value entries if necessary, and to find any user-assigned Win32-visible name(s) for its device(s).

To do this, an NT driver can call the following general sequence of support routines:

  1. Call ZwOpenKey to get a handle for the Parameters subkey, using the RegistryPath pointer, shown in Figure 16.6.

  2. Call ZwEnumerateKey with the handle to determine how many logical (such as parallel or serial ports) or physical devices the driver supports in the machine. This routine can be called repeatedly until it returns STATUS_NO_MORE_ENTRIES, thereby determining a count of subkeys and returning KEY_BASIC_INFORMATION about each subkey. The returned information includes the name of the subkey so the driver can supply the required key name parameter to RtlQueryRegistryValues.

  3. With each named subkey, call RtlQueryRegistryValues to find any user-supplied hardware configuration values for the device and to find any user-assigned Win32 name for the device. If a subkey has no value entry except its name, the driver can supply default hardware configuration value entries for that subkey.

  4. Call ZwClose with the handle, obtained in Step 1, when the driver has consumed the information available in the Parameters subkeys.

  5. After the driver has successfully claimed hardware resources and initialized its physical device(s), the driver must set up a symbolic link between any user-assigned Win32 subsystem name or driver-determined default name for each Win32-visible device and the corresponding NT device object name, as described in Section 16.9.

  6. After the driver has created any necessary symbolic links, call ZwOpenKey to get a handle for the device-type specific DriverClassName subkey (or ..\Other) under the DeviceMap (see Section 16.8.3) and call RtlWriteRegistryValue to set up aliased names under the driver’s DeviceMap subkey(s).

  7. Call ZwClose to release the handle(s) of the DeviceMap\DriverClassName subkey(s).

Note that driver-supplied configuration information under the key \Registry\Machine\Hardware\DeviceMap, shown in Figure 16.6, about the NT names of driver-created device objects can be useful both to higher-level drivers and to anyone who uses regedt32 to modify or create Win32-visible names for add-on devices in a Windows NT machine.

Any NT driver that has an Unload routine is responsible for releasing the symbolic links it has created as described in Section 16.9 and for “erasing” the aliased names it wrote into the registry (see Step 6), as well as for freeing any resources it claimed in the registry, as described in Section 16.8.3. For more information about the Unload routine, see also Chapters 4 and 15.

Note also that NT drivers of floppy and CD-ROM devices from which the system itself can be loaded must set up a symbolic link between the system-defined ARC name for the device and the NT device object name. For more information about this requirement for NT floppy and CD-ROM device drivers, see also Section 16.9.2.


16.9 Setting Up Symbolic Links

The user-visible name for a particular device can be subsystem-specific, as mentioned in Section 16.8.1.3. For example, an MS-DOS® or Win32 application’s print job is likely to be directed to a logical device named LPT1, rather than to the NT device object name for the parallel port to which a printer is attached.

A subkey of the CurrentControlSet\Control key, \Session Manager\DOS Devices, contains subkeys with value entries for the Win32 subsystem’s logical device names. During system initialization, the Win32 Session Manager sets up symbolic link objects in the NT object namespace \?? (formerly \DosDevices), using the registry’s \Session Manager\DOS Devices subkeys. Each symbolic link object creates an alias between a Win32-visible name and the corresponding NT device object name, so that applications and/or end users can use the Win32-visible name to send I/O requests to the underlying device.

Windows NT supplies a default set of Win32 logical names for standard MS-DOS and Windows® devices like LPT1, but the default set cannot include every possible device that might be present in or added to a Windows NT machine. Consequently, NT driver writers must set up symbolic link objects in the NT object namespace \??, in order to make their devices available to Win32 applications and end users for I/O requests. Section 16.9.1, explains how the writer of a new NT device driver can set up the necessary symbolic link(s).

As mentioned in Section 16.8.1.2, NT drivers of floppy and CD-ROM devices from which the system itself can be loaded must set up a symbolic link between the ARC- or NtDetect-created “hardware” name for the device and the corresponding device object name. Section 16.9.2 explains how the writer of a new floppy or CD-ROM device driver can set up the necessary symbolic link(s).


16.9.1 Making a Named Device Object Visible to User-mode Applications

To make a new device accessible from the Win32 subsystem, there must be a symbolic link object in the NT object namespace \??. Such a symbolic link object maps the NT generic name for a driver’s device object to the corresponding Win32 logical name for the same device. There are two alternative techniques to set up such a symbolic link:

  • Use regedt32 (or a setup utility) to add a value entry under CurrentControlSet\Control\Session Manager\DOS equating the NT device object name to the Win32 logical name. For example:

        FaxDev:REG_SZ:\Device\Fax0

  • Set up the symbolic link when the driver initializes by calling IoCreateSymbolicLink.

When such a symbolic link object has been created in \??, a Win32 application or subsystem-level driver can get a handle for the file object that represents the \Device\Fax0 device object using \\ .\FaxDev as a parameter.

Note that a system administrator or end user must be very knowledgeable about using regedt32 and about the DOS Devices key in the registry in order to use the first technique of “installing” a new Win32 device in the registry.

Using regedt32 creates a nonvolatile subkey under \Session Manager\DOS Devices in the registry. As mentioned in Section 16.9, the Session Manager creates a symbolic link object in the \?? object namespace for each subkey. To the Session Manager, the creation of these symbolic link objects requires only that it process the value entries of the DOS Devices subkeys. Consequently, it can create a symbolic link for a device that is not actually available for user-mode I/O requests. For example, if a device driver fails its initialization or is unloaded dynamically, the Session Manager does not remove the corresponding DOS Devices subkey in the registry nor the corresponding symbolic link object in the \?? namespace.

For developers of new NT drivers, relying on the first technique for setting up a new device and retail driver can be very costly in terms of customer support.

Consequently, most NT driver writer are likely to prefer the second technique for making a new device visible to Win32 applications and end users. For example, the driver of a new fax device might do the following in order to make its device accessible to I/O requests from Win32 applications:

  1. The driver forms a full path name string, including the \?? as the root of the path, for the user-visible name, such as FaxDev, and another full path name string for the corresponding device object name, such as \Device\Fax0. Each path name string must be a driver-buffered, zero-terminated Unicode string.

  2. The driver calls IoCreateSymbolicLink with the strings that it set up in Step 1. (A parallel or serial driver should call IoCreateUnprotectedSymbolicLink, rather than IoCreateSymbolicLink.)

While using the first technique to make a driver’s device visible within the Win32 subsystem environment is a simple way to get an under-development NT driver loaded so it can be debugged and tested, retail drivers should create symbolic links between the NT names for their device objects and the corresponding Win32 logical device names in the NT object namespace \??.

Any NT driver that has an Unload routine is responsible for releasing the symbolic link objects it has created. Such a driver’s Unload routine must call IoDeleteSymbolicLink for each symbolic link that the DriverEntry routine set up.


16.9.2 Creating Symbolic Links between Devices and ARC Names

Drivers of floppy and CD-ROM devices from which the system itself can be loaded must be identified by the ARC or NtDetect components during the earliest phase of the system boot process.

For such a boot-from floppy or CD-ROM device in a given machine, a hardware device name, representing a vector to the device, is passed to the system boot loader by the ARC component. To ensure driver portablity across NT platforms, the OS loader sets up the ARC names in x86-based NT machines.

When the driver of a floppy or CD-ROM device is loaded, it must call IoAssignArcName to create a symbolic link between the ARC name for the device and the name that the driver assigned when it created a device object to represent a possible boot-from device.

For example, when such a CD-ROM driver creates a device object for a CD-ROM drive, it should include code somewhat like the following:
{
    CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
    CCHAR arcNameBuffer[MAXIMUM_FILENAME_LENGTH];
    STRING ntNameString;
    STRING arcNameString;
    UNICODE_STRING ntUnicodeString;
    UNICODE_STRING arcUnicodeString;
            :      :
    RtlInitString(&ntNameString, ntNameBuffer);
    (VOID)RtlAnsiStringToUnicodeString(
                                        &ntUnicodeString,
                                        &ntNameString,
                                        TRUE);
            :     :
    // Call IoCreateDevice with ntUnicodeString
            :     :
    //
    // An ARC name looks something like
    // \ArcName\scsi(0)cdrom(0)fdisk(0)
    //
    RtlInitString(&arcNameString, arcNameBuffer);
    status = RtlAnsiStringToUnicodeString(
                                        &arcUnicodeString,
                                        &arcNameString,
                                        TRUE);
    if (!NT_SUCCESS(status)) {
        RtlFreeUnicodeString(&ntUnicodeString);
        goto CreateDeviceObjectsExit;
    }
    IoAssignArcName(&arcUnicodeString,  &ntUnicodeString);
    RtlFreeUnicodeString(&arcUnicodeString);
            :     :

If such a driver has an Unload routine, it must call IoDeassignArcName to delete each such symbolic link that the DriverEntry routine set up.


Appendix A SCSI Drivers

This appendix contains the following information:
A.1 Designing a SCSI Class Driver

A.1.1 Supporting I/O Requests

A.1.2 Supporting SCSI-Specific Functionality

A.1.3 SCSI Class Driver’s DriverEntry Routine

A.1.3.1 SCSI Class Driver’s GetInquiryData Routine

A.1.3.2 SCSI Class Driver’s ClaimDevice Routine

A.1.3.3 SCSI Class Driver’s GetCapabilities Routine

A.1.3.4 Setting up Device Extensions

A.1.4 SCSI Class Driver’s Dispatch Routines

A.1.4.1 Handling Requests to SCSI Peripherals

A.1.4.2 Handling SCSI Pass-Through Requests

A.1.4.3 Queueing SCSI Requests

A.1.4.4 SCSI Class Driver’s BuildSrb Routine

A.1.4.5 SCSI Class Driver’s SplitTransferRequest Routine

A.1.5 SCSI Class Driver’s IoCompletion Routines

A.1.5.1 SCSI Class Driver’s ReleaseQueue Routine

A.1.5.2 SCSI Class Driver’s InterpretRequestSense Routine

A.1.5.3 SCSI Class Driver’s RetryRequest Routine

A.2 Designing a SCSI Filter Driver
A.2.1 Supporting I/O Requests

A.2.2 Supporting SCSI-Specific Functionality

A.2.3 SCSI Filter Driver’s DriverEntry Routine

A.2.4 SCSI Filter Driver’s Dispatch Routines

A.2.5 SCSI Filter Driver’s IoCompletion Routines

A.3 Designing a SCSI Tape Miniclass Driver
A.3.1 Using the Tape Class Driver

A.3.2 Supplying Required and Optional Miniclass Routines

A.3.3 Storing Miniclass Context in Optional Extensions

A.3.4 Processing Tape Device Control Requests

A.4 Designing a SCSI Miniport Driver
A.4.1 Required and Optional SCSI Miniport Routines

A.4.2 SCSI Miniport Driver’s DriverEntry Routine

A.4.2.1 Calling ScsiPortInitialize

A.4.2.2 Return from DriverEntry

A.4.3 SCSI Miniport Driver’s HwScsiFindAdapter Routine

A.4.3.1 Calling ScsiPort Xxx from HwScsiFindAdapter

A.4.3.2 Port-Driver-Supplied ConfigInfo for HwScsiFindAdapter

A.4.3.3 Setting up ConfigInfo in HwScsiFindAdapter

A.4.3.4 Setting Again and Returning from HwScsiFindAdapter

A.4.3.5 Return from HwScsiFindAdapter to ScsiPortInitialize

A.4.4 SCSI Miniport Driver’s HwScsiInitialize Routine

A.4.5 SCSI Miniport Driver’s HwScsiStartIo Routine

A.4.5.1 Modifying SRBs

A.4.5.2 Handling SRB_FUNCTION_EXECUTE_SCSI

A.4.5.3 Handling SRB_FUNCTION_ABORT_COMMAND

A.4.5.4 Handling SRB_FUNCTION_RESET_BUS

A.4.5.5 Handling SRB_FUNCTION_FLUSH and SRB_FUNCTION_SHUTDOWN

A.4.5.6 Handling SRB_FUNCTION_IO_CONTROL

A.4.5.7 Handling Unsupported SRB_FUNCTION_XXX

A.4.5.8 Return from HwScsiStartIo

A.4.5.9 Other SRB_FUNCTION_XXX Requests

A.4.6 SCSI Miniport Driver’s HwScsiDmaStarted Routine

A.4.7 SCSI Miniport Driver’s HwScsiResetBus Routine

A.4.8 SCSI Miniport Driver’s HwScsiInterrupt Routine

A.4.8.1 Deferring Interrupt-Driven I/O from HwScsiInterrupt

A.4.8.2 SCSI Miniport Driver’s HwScsiEnableInterruptsCallback Routine

A.4.8.3 SCSI Miniport Driver’s HwScsiDisableInterruptsCallback Routine

A.4.9 SCSI Miniport Driver’s HwScsiTimer Routine

A.4.10 SCSI Miniport Driver’s HwScsiAdapterState Routine

A.4.11 Error Handling in SCSI Miniport Drivers

This appendix contains guidelines for designing the following kinds of Windows NT® kernel-mode SCSI drivers:

  • A class driver for a new type of peripheral device

  • A filter driver for a peripheral device of a type that already has a class driver

  • An OS-independent tape miniclass driver for a vendor-specific tape device

  • An OS-independent miniport driver for a vendor-specific HBA

  • A miniport driver for a nonSCSI mass-storage adapter
I/O Request Handling in Layered SCSI Drivers

Windows NT class and filter drivers for SCSI peripheral devices act as an interface between any intermediate or highest-level drivers layered above the class or filter driver and the system-supplied SCSI port driver. That is, SCSI class drivers translate the standard IRPs they get into IRPs with system-defined SCSI request blocks (SRBs) before sending each IRP on to the system port driver. Each SRB contains a SCSI-II command descriptor block (CDB), as well as members that identify the target device for the requested operation.

In turn, the system-supplied, SCSI port driver passes SRBs with CDBs on to OS-independent, HBA-specific miniport drivers. The port driver acts as an interface between all SCSI class drivers and all HBA-specific miniport drivers.

In addition, the Windows NT SCSI port driver can act as an interface between a SCSI mass-storage class driver and a miniport that controls a nonSCSI mass-storage device of the same type. For example, rather than writing a standard NT driver for a new disk-array controller, a driver writer can save considerable design, development, and debugging effort by writing a pseudoSCSI miniport driver that links itself to the system SCSI port driver and uses the interface it provides. This technique requires such a miniport to translate incoming SCSI commands into device-specific commands and to support higher-level NT drivers as if it were a SCSI HBA miniport. On the other hand, the system-supplied port and class drivers would handle much necessary work on such a pseudoSCSI miniport’s behalf, including registry accesses during initialization, all resource and object allocations, synchronization, presizing requested transfers to suit the capabilities of the miniport’s device, and retrying requests.

For more detailed information about SRBs, see the Kernel-Mode Driver Reference. For device-type-specific information about CDBs, consult the ANSI X3.131-199X SCSI-II standard.

System Header Files

The system-supplied SCSI drivers include the header file scsi.h, which contains SCSI-II-compliant definitions for CDBs and other structures used by most SCSI-II-compliant drivers. This header file includes srb.h, which defines the interfaces provided by the system SCSI port driver.

System-supplied class and filter drivers also can include the header file class.h so they can call the ScsiClassXxx routines provided by class.c. The Windows NT DDK provides class.h and class.c as sample source code. However, class.c should not be considered a system-supplied library for all future SCSI class drivers; both class.h and class.c are subject to change without notice.

OS-independent SCSI miniport drivers, which can be designed to run both with Microsoft® Windows NT and with future x86-only Microsoft Windows® systems, include the system-supplied header files miniport.h and scsi.h, which includes srb.h.


A.1 Designing a SCSI Class Driver

To the I/O Manager and any higher-level drivers layered above a SCSI class driver, most SCSI class drivers are standard kernel-mode intermediate drivers. Such a class driver must supply a set of Dispatch routines to which the I/O Manager and/or still higher-level drivers, such as FSDs, can send IRPs for appropriate I/O operations.

In addition, every class driver must have a DriverEntry routine and one or more IoCompletion routines. A SCSI class driver also can have any other standard higher-level driver routine, such as an Unload routine, as necessary.

For more information about standard kernel-mode driver routines, see Chapter 4 .


A.1.1 Supporting I/O Requests

A new SCSI class driver is required to support the same set of IRP_MJ_XXX requests as a nonSCSI driver for the same type of device. For specific information about which requests drivers of the most common types of devices must handle, see the Kernel-Mode Driver Reference.

The designer of a class driver for an entirely new type of SCSI device must determine an appropriate set of requests for the driver to support, depending on the nature of the device. The set of requests to be supported generally includes at least the following:

  • IRP_MJ_CREATE and, for some device types or for symmetry, IRP_MJ_CLOSE

  • IRP_MJ_DEVICE_CONTROL

  • IRP_MJ_READ, IRP_MJ_WRITE, or both


A.1.2 Supporting SCSI-Specific Functionality

To the system SCSI port driver, a SCSI class driver is a higher-level driver with built-in, SCSI-specific functionality. In general, every SCSI class driver is responsible for the following:

  • Connecting itself to the port driver so that requests can be routed to the correct target(s)

  • Creating device objects to represent its devices on each SCSI bus in the machine

  • Initializing the appropriate controllers (TIDs) and devices (LUs) on the SCSI bus(es)

  • Interpreting system I/O requests (IRPs)

  • Mapping IRPs to SCSI class/port interface requests (SRBs with SCSI-II CDBs)

  • Establishing time-out values for requests

  • Limiting the size of data transfers to suit the limits of the underlying HBA

  • Handling error conditions that are not already handled by the port driver or the HBA miniport driver, such as check-condition status or bus resets

The following sections describe how class drivers provide this SCSI-specific functionality in the context of each standard routine that class drivers commonly have.


A.1.3 SCSI Class Driver’s DriverEntry Routine

Like any other Windows NT kernel-mode higher-level driver, the DriverEntry routine of a SCSI class driver must do the following:

  1. Define its Dispatch entry points in the input driver object.

  2. Layer itself over the SCSI port driver by calling IoGetDeviceObjectPointer.

  3. Locate its device(s), if any are present in the machine.

  4. Create a device object to represent each physical, logical, and/or virtual device for which it handles IRPs.

The class driver’s initial call to IoGetDeviceObjectPointer returns a pointer to a generic device object created by the system port driver, rather than a pointer to a device object that represents a particular HBA. The class driver uses this generic device object pointer to find its device(s) on each bus driven by one or more HBAs in the machine.


A.1.3.1 SCSI Class Driver’s GetInquiryData Routine

To find its device(s), the class driver either calls a GetInquiryData routine or implements the same functionality in its DriverEntry routine. A GetInquiryData routine allocates an INQUIRY_DATA_SIZE buffer for SCSI_ADAPTER_BUS_INFO-type data.

Next, it allocates an IRP with IoBuildDeviceIoControlRequest, sets the port driver’s I/O stack location up with the I/O control code IOCTL_SCSI_GET_INQUIRY_DATA, and passes the IRP on to the SCSI port driver with IoCallDriver.

The class driver uses the InquiryDataOffset in the SCSI_ADAPTER_BUS_INFO buffer to inspect the SCSI inquiry data and find device(s) of its type.

Like any other kernel-mode driver, a SCSI class driver must set up a device object to represent each physical, logical, and/or virtual device for which it handles I/O requests. The class driver also must establish a claim on each of the physical devices for which it creates a device object with IoCreateDevice.

Subsequently loaded drivers for the same type of device can determine whether a particular SCSI peripheral has already been claimed by inspecting the inquiry data for each HBA and SCSI bus in the machine. A class driver must not attempt to claim an already claimed device.

For more information about the SCSI_ADAPTER_BUS_INFO structure and IOCTLs supported by the Windows NT SCSI port driver, see the Kernel-Mode Driver Reference.


A.1.3.2 SCSI Class Driver’s ClaimDevice Routine

To claim a SCSI device, a class driver either calls a ClaimDevice routine or implements the same functionality in its DriverEntry routine. A ClaimDevice routine sets up an SRB for the target device (designated by PathId, TargetId, and Lun in the SRB) with the Function value SRB_FUNCTION_CLAIM_DEVICE.

The ClaimDevice routine allocates an IRP with IoBuildDeviceIoControlRequest, setting up the port driver’s I/O stack location with the I/O control code IOCTL_SCSI_EXECUTE_NONE and a pointer to the SRB at Parameters.Scsi.Srb. ClaimDevice also must set up an event object so it can wait on the completion of the IRP. Then, it sends the IRP on to the port driver with IoCallDriver.

On return from a successful call to the system port driver, the ClaimDevice routine checks the SRB’s DataBuffer for a nonNULL pointer to the port driver’s device object for the HBA.

The class driver must store such a returned pointer in the device extension of its own device object that represents the newly claimed device. The class driver must use this HBA-specific device object pointer in all subsequent requests that the class driver sends down to the port-miniport driver pair.

A ClaimDevice routine can serve double duty as a routine to be called from a class driver’s Unload routine by also sending SRBs with the Function value SRB_FUNCTION_RELEASE_DEVICE. If the class driver can be unloaded, its Unload or ClaimDevice routine must dereference the pointer to the HBA-specific device object by calling ObDereferenceObject.


A.1.3.3 SCSI Class Driver’s GetCapabilities Routine

For data transfer operations, SCSI class drivers need configuration information about each HBA driving a bus to which their devices are attached. Consequently, a class driver either calls a GetCapabilities routine or implements the same functionality in its DriverEntry routine.

A GetCapabilities routine builds and sets up an IRP_MJ_DEVICE_CONTROL request with the I/O control code IOCTL_SCSI_GET_CAPABILITIES for the port driver. The class driver inspects the returned IO_SCSI_CAPABILITIES-type information to determine the following:

  • The maximum number of bytes a particular HBA can transfer in a single operation

  • If the HBA can transfer buffered data backed by discontiguous physical pages, how many discontiguities per buffer it can manage per transfer operation

  • The HBA’s alignment requirements for transfers so the class driver can properly set the AlignmentRequirement field in its device objects.

    Applications that send IOCTL_SCSI_PASS_THROUGH requests also might use this field.

    For more information about setting up AlignmentRequirement in device objects, see Chapter 3.

  • Whether the HBA uses PIO and requires the use of system-space virtual addresses mapped to physical memory for data buffers

  • Whether the HBA supports SCSI-II tagged queueing and/or per-logical-unit internal queues (MultipleRequestsPerLu)

  • Whether the HBA supports asynchronous event notifications

  • Whether the HBA scans down

The class driver should store this information in the device extension so its Dispatch routines can ensure that all requests sent to the port and miniport drivers conform to the size, alignment, and PIO requirements of the underlying HBA. For more information about class driver Dispatch routines, see Section A.1.4. For more information about setting up device extensions, see Section A.1.3.4.

For more information about general requirements for DriverEntry routines, see Chapter 5.


A.1.3.4 Setting up Device Extensions

In the device extension of each device object, the class driver provides storage for whatever driver-determined data it uses to manage I/O requests for the device, such as the pointer to the port driver’s HBA-specific device object, a back pointer to its own device object, and so forth.

Most SCSI class drivers also provide storage for the following SCSI-specific information:

  • A device-type-specific time-out value

    The class driver can pass the time-out value in SRBs it sends to the port driver, which optionally times requests on behalf of each class driver. The port driver returns an SRB with its SrbStatus member set to SRB_STATUS_TIMEOUT if the interval between when the port driver sends the request to the miniport driver and when the request completes exceeds the specified time-out value.

  • A pointer to the class driver’s error-handling routine

    See Section A.1.5 for more information about error-handling in SCSI class drivers.

  • A count that the driver maintains of SCSI protocol errors on the device

  • The PathId value

    This identifies the SCSI bus on which the device is attached. It is a required value in SRBs.

  • The TargetId value

    This identifies the TID of the controller or device. It is a required value in SRBs.

  • The Lun value

    This identifies the logical unit number of the device. It is a required value in SRBs.

  • A pointer to a driver-allocated buffer for SCSI request-sense data

    A class driver must allocate memory for returned request-sense data from cache-aligned, nonpaged pool. For more information about allocating memory for driver buffers, see Chapter 16.

  • A driver-determined default value for SrbFlags that the class driver sets in SRBs

  • A pointer to a zone header if the driver sets up a zone buffer for the SRBs it allocates

    If the driver calls the ExInterlocked..Zone routines for its SRBs, it also must provide storage for and initialize a spin lock that protects the zone’s free list. For more information about using zones and spin locks, see Chapter 16.

  • A spin lock used to guarantee multiprocessor-safe access to a count of driver-allocated IRPs for partial transfer requests

  • A pointer to the IO_SCSI_CAPABILITIES-type data that the port driver collected from the miniport driver of the HBA

    How class drivers get and use this data was described in Section A.1.3.3.

Note that a SCSI class driver’s original call to IoGetDeviceObjectPointer creates a reference to the handle of the file object associated with the port driver’s generic device object. The class driver must call ObDereferenceObject to release this reference if the class driver is unloaded.

A Windows NT SCSI class driver cannot send requests through the system-supplied port driver to its device without using the port driver’s HBA-specific device object pointer that was stored in the device extension by the ClaimDevice routine.


A.1.4 SCSI Class Driver’s Dispatch Routines

Class driver Dispatch routines for IRP_MJ_CREATE and IRP_MJ_CLOSE requests usually have no SCSI-specific requirements. Most SCSI class drivers are intermediate drivers; their Dispatch routines just return STATUS_SUCCESS to indicate that a given device object exists so that higher-level drivers and, indirectly, user-mode applications can open the device for I/O and close the device afterward.

A SCSI class driver must have a Dispatch routine for IRP_MJ_SHUTDOWN requests and possibly for IRP_MJ_FLUSH_BUFFERS requests if its device caches data internally, if its device might be attached to a bus driven by an HBA that caches data internally, or if a file system is layered above the class driver. To maintain data integrity, such a cache should be flushed to the device before the system is shut down.

See also Chapter 6 for more information about general requirements for Dispatch routines.


A.1.4.1 Handling Requests to SCSI Peripherals

For all requests that require the port driver to execute a request on the SCSI bus, the class driver must set up an IRP with an SRB containing a CDB. Consequently, most class drivers have one or more internal routines to build SRBs. For more information about BuildSrb routines, see Section A.1.4.4.

SCSI class drivers also pass on IRP_MJ_INTERNAL_DEVICE_CONTROL requests to the Windows NT SCSI port driver. Such a request can originate from a SCSI filter driver, described in Section A.2. As for IOCTL_SCSI_PASS_THROUGH requests described in Section A.1.4.2, the class driver is responsible for setting the PathId, TargetId, and Lun members in the SRB. The class driver is also responsible for setting the MinorFunction code to IRP_MN_SCSI_CLASS in the port driver’s I/O stack location before passing the IRP on to the port driver with IoCallDriver.

Every SCSI class driver is responsible for splitting up transfer (IRP_MJ_READ and/or IRP_MJ_WRITE) requests that exceed the underlying HBA’s capabilities. Consequently, most class drivers also call an internal SplitTransferRequest routine, described in Section A.1.4.5, or implement the same functionality in their Dispatch routine(s) for read and write requests.


A.1.4.2 Handling SCSI Pass-Through Requests

A class driver does not necessarily set up an SRB when it sets up an IRP_MJ_DEVICE_CONTROL request for an I/O control code supported by the port driver, such as IOCTL_SCSI_GET_CAPABILITIES, IOCTL_SCSI_PASS_THROUGH, or IOCTL_SCSI_PASS_THROUGH_DIRECT.

The class driver’s DispatchDeviceControl routine is responsible for the following on receipt of a IOCTL_SCSI_PASS_THROUGH or IOCTL_SCSI_PASS_THROUGH_DIRECT request:

  • Checking that the length of the user buffer at Parameters.DeviceIoControl.InputBufferLength is at least sizeof(SCSI_PASS_THROUGH) or sizeof(SCSI_PASS_THROUGH_DIRECT), respectively, and for failing the IRP with STATUS_INVALID_PARAMETER if is not

  • Ensuring that certain fields in the SCSI_PASS_THROUGH or SCSI_PASS_THROUGH_DIRECT structure contain correct values by setting the pointer to the structure itself to Irp->AssociatedIrp.SystemBuffer, and setting its PathId, TargetId, and Lun members to the corresponding values from the driver’s device extension

  • Setting up the port driver’s I/O stack location as usual and also setting the MinorFunction field to IRP_MN_SCSI_CLASS, which marks the request as having been processed by a SCSI class driver

If the port driver’s I/O stack location for an IOCTL_SCSI_PASS_THROUGH or IOCTL_SCSI_PASS_THROUGH_DIRECT request does not have its MinorFunction field set with IRP_MN_SCSI_CLASS, the port driver assumes the request came directly from an application and that no class driver exists for the target device type. It is an application error to send such a request directly to the port driver for a device that has been claimed by a SCSI class driver.


A.1.4.3 Queueing SCSI Requests

While it is possible for a SCSI class driver to set up internal queues for IRPs, it is seldom necessary to do so and likely to degrade the driver’s performance as well because the SCSI port driver already maintains driver-created, LU-specific device queues for IRPs. Whether a particular HBA supports SCSI-II tagged queueing or not, SCSI class drivers can send every request to their devices as each IRP comes in and rely on the system-supplied port driver or the HBA to handle queued requests expeditiously.

When certain SCSI errors occur, the system port driver freezes the appropriate LU-specific queue and notifies the class driver. For more information about handling errors and releasing frozen request queues, see Sections A.1.5.1, A.1.5.2, and A.1.5.3.

If an HBA supports tagged queueing, as indicated in the returned IO_SCSI_CAPABILITIES data, the class driver uses the QueueAction member of the SRBs it creates to control how its requests are queued.


A.1.4.4 SCSI Class Driver’s BuildSrb Routine

Like all higher-level kernel-mode drivers, a SCSI class driver must set up the IRP’s I/O stack location for the next-lower-level driver. In IRPs that the class driver sets up with SRBs for the system-supplied port driver, the port driver’s I/O stack location is set with the following:

  • IRP_MJ_SCSI is the major function code.

  • Parameters.Scsi.Srb contains a pointer to the SRB.

Each class driver is responsible for allocating memory for SRBs as well as for setting them up with CDBs for the underlying port-miniport drivers. The class driver can either call ExAllocatePool for nonpaged memory or can set up a resident zone buffer for its SRBs. For more information about using nonpaged pool and zones, see Chapter 16.

Whether it allocates memory from pool or from a driver-created zone buffer, every SCSI class driver is responsible for freeing the memory it allocates for SRBs. SCSI class drivers’ IoCompletion routines, described in Section A.1.5, usually free the memory allocated for SRBs.

A class driver’s BuildSrb routine must set appropriate values in the SRB members, including the length of the CDB it has set up to communicate with its device. For requests that return request-sense information and/or that the driver might need to retry, it sets an IoCompletion routine in the IRP. For read or write requests, it ORs the SrbFlags with the appropriate transfer direction, SRB_FLAGS_DATA_IN or SRB_FLAGS_DATA_OUT, respectively.

A BuildSrb routine might split the responsibility for setting up an SRB with a pair of SendSrbSynchronous and SendSrbAsynchronous routines. That is, the BuildSrb routine could set up the SRB members that are commonly set up for all requests, while the SendSrbXxx routines each set SRB values pertinent only to each type of SCSI request. When an IRP is passed down to the port driver from a SendSrbAsynchronous routine, the IRP must be set up with a driver-supplied IoCompletion routine.

After the class driver has loaded, it sets up most SRBs with the Function member set to SRB_FUNCTION_EXECUTE_SCSI, indicating a device I/O request to be sent over the bus.

For more information about the system-defined SRB members and their values, see also the Kernel-Mode Driver Reference.

Setting Up SRBs for Request Sense

A class driver can request that the port driver perform a SCSI-II request-sense operation when the target controller returns a check condition. To do this, the class driver sets up the SenseInfoBuffer pointer and SenseInfoBufferLength in the SRB, so the port driver can return the request-sense information if a check condition occurs. The port driver indicates that it returned request-sense information by setting the SrbStatus member with SRB_STATUS_AUTOSENSE_VALID when it returns the IRP. For more information about InterpretRequestSense routines, see Section A.1.5.2.

Retries

SCSI class drivers are responsible for retrying requests that fail due to target controller errors, bus resets, or request time-outs. Consequently, many class drivers maintain a retry count in their own I/O stack location of the IRP. Such a class driver’s BuildSrb routine also might initialize the retry limit for a given request before it sets up its IoCompletion routine and sends the IRP to the port driver. For more information about RetryRequest routines, see Section A.1.5.3.


A.1.4.5 SCSI Class Driver’s SplitTransferRequest Routine

The IO_SCSI_CAPABILITIES-type data returned to the DriverEntry routine indicates the transfer capabilities of a given HBA to the class driver. In particular, this data indicates the HBA’s MaximumTransferLength in bytes and the HBA’s MaximumPhysicalPages: that is, how many discontiguites in the physical memory backing a system buffer the HBA can manage.

Most class drivers store a pointer to this configuration data in the device extension of each device object because SCSI class drivers are responsible for splitting up all transfer requests that exceed the HBA’s capability to transfer data. In other words, a class driver’s DispatchReadWrite routine must determine whether each IRP requests a transfer that is more than the HBA can handle in a single transfer operation.

For example, such a DispatchReadWrite routine could have code similar to the following:

maxTransferLength =
    deviceExtension->PortCapabilities->MaximumTransferLength;
maxPhysicalPages =
    deviceExtension->PortCapabilities->MaximumPhysicalPages;
currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
transferLengthRequested = currentIrpStack->Parameters.Read.Length;
    :        :
//
// Calculate number of pages in this transfer
//
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
                    MmGetMdlVirtualAddress(Irp->MdlAddress),
                                    transferLengthRequested);
//
// Check whether requested length is greater than maximum
// HBA can transfer in a single operation
//
if (transferLengthRequested > maxTransferLength ||
                    transferPages > maxPhysicalPages) {
    transferPages = maxPhysicalPages - 1;
    if (maxTransferLength > transferPages << PAGE_SHIFT) {
        maxTransferLength = transferPages << PAGE_SHIFT;
    }
    IoMarkIrpPending(Irp);
    SplitTransferRequest(DeviceObject,
                            Irp,
                            maxTransferLength);
    return STATUS_PENDING;
}
    :        :

Note that such a driver’s DispatchReadWrite routine calls IoMarkIrpPending and returns STATUS_PENDING immediately after a call to its SplitTransferRequest routine with the original IRP.

To carry out the original transfer request, the driver’s SplitTransferRequest routine creates one or more IRPs to handle sub-buffers that are sized to suit the HBA’s capabilities. For each such IRP, the SplitTransferRequest routine sets up an SRB, usually by calling a BuildSrb routine (see Section A.1.4.4), and sets up its IoCompletion routine before sending the IRP on to the port driver with IoCallDriver.

To track each piece of the transfer, SplitTransferRequest registers an IoCompletion routine for each driver-allocated IRP it sends down to the port driver. The IoCompletion routine maintains a count of completed partial transfer requests in the original IRP and protects the count with a spin lock, as already mentioned in Section A.1.3.4.

Such an IoCompletion routine must free any IRPs and/or SRBs the driver has allocated and must complete the original IRP when all requested data has been transferred or when the class driver has exhausted retries of the IRP and must fail it due device transfer errors.


A.1.5 SCSI Class Driver’s IoCompletion Routines

As mentioned in Section A.1.4.4, SCSI class drivers are responsible for freeing the memory they allocate for SRBs. Like any other higher-level kernel-mode driver, they are also responsible for releasing any IRPs that they allocate, such as an IRP to split up a transfer request as described in the preceding section.

A class driver’s IoCompletion routine is ultimately responsible for ensuring that the I/O status block is set and for completing the original IRP. Note that completing an IRP can include translating an error returned in the SRB’s ScsiStatus member or SrbSenseInfoBuffer into an NTSTATUS-type value and/or logging an error, as described in Chapter 16.

SCSI class drivers must have one or more IoCompletion routines, unless a particular driver waits on the completion of every IRP it sends to the port driver, retries requests as necessary, and releases memory for SRBs from a Dispatch or BuildSrb routine. Note that handling every IRP as such a synchronous request would degrade the performance of any class driver. Furthermore, SCSI class drivers for devices that might hold the system page file must handle all transfer requests asynchronously. Such a class driver must have an IoCompletion routine for read/write requests.

When certain kinds of errors occur in processing a request, the system port driver freezes its internal queue for the target LU. Consequently, class drivers usually have internal routines to change the status of the queue for their device I/O requests. For more information about class drivers’ ReleaseQueue routines, see Section A.1.5.1.

If the driver’s BuildSrb routine requested that the port driver return request-sense information for a request, its IoCompletion routine either calls an InterpretRequestSense routine or implements the same functionality inline. For more information about InterpretRequestSense routines, see Section A.1.5.2.

As mentioned in Section A.1.4.4, SCSI class drivers are responsible for retrying requests that fail due to target controller errors, bus resets, or request time-outs. When the port driver returns a particular request with its SrbStatus set to indicate such an error, the class driver can call a RetryRequest routine from its IoCompletion routine or, possibly, from the InterpretRequestSense routine. For more information about RetryRequest routines, see Section A.1.5.3.

For more information about general requirements for IoCompletion routines, see Chapter 13.


A.1.5.1 SCSI Class Driver’s ReleaseQueue Routine

Unless a SCSI class driver ORs the SrbFlags for a given request with SRB_FLAGS_NO_QUEUE_FREEZE, the system port driver freezes a queue for a given logical unit after any of the following:

  • A bus reset occurred while the logical unit was executing a request.

  • The logical unit returned SCSISTAT_CHECK_CONDITION or SCSISTAT_COMMAND_TERMINATED, which the class driver can find in the SRB’s ScsiStatus member.

  • A request was timed out.

  • A request was terminated by a bus message command such as SCSIMESS_ABORT.

The port driver indicates that an LU-specific queue has been frozen by returning a request with SRB_STATUS_QUEUE_FROZEN in the SrbStatus member. New requests from the class driver can be inserted into the queue, but no requests are sent to the logical unit except autosense requests.

Freezing the queue under these conditions gives each SCSI class driver an opportunity to analyze an error before other queued jobs are executed. For example, a class driver might need to reinitialize the TID after the controller has been reset, or queued jobs might need to be cancelled if the media has changed. To reinitialize a TID, the driver can send a request with the SrbFlags ORed with SRB_FLAGS_BYPASS_FROZEN_QUEUE.

A ReleaseQueue routine allocates and sets up an IRP and an SRB to either release or flush a frozen queue. The Function member of the SRB must be set to SRB_FUNCTION_RELEASE_QUEUE or SRB_FUNCTION_FLUSH_QUEUE, which both releases a frozen queue and cancels all currently queued requests for the target logical unit. The port driver completes all requests in a flushed queue with their SrbStatus members set to SRB_STATUS_FLUSHED.

A ReleaseQueue routine should allocate memory for an SRB by calling ExAllocatePool with the memory type NonPagedPoolMustSucceed because failing to release a frozen queue makes the device inaccessible.

Note that a class driver’s ReleaseQueue routine is called asynchronously, generally from its IoCompletion routine. A class driver’s IoCompletion routine cannot call ReleaseQueue to flush a queue that is not frozen. However, it can call ReleaseQueue to release an unfrozen queue, and the port driver simply ignores such a request.


A.1.5.2 SCSI Class Driver’s InterpretRequestSense Routine

An InterpretRequestSense routine interprets the data returned in the SrbSenseInfoBuffer, determines whether the request should be retried, and, if not, maps the error to an NTSTATUS value for the IRP’s I/O status block.

The system port driver indicates whether request-sense information is available by setting the SrbStatus member with SRB_STATUS_AUTOSENSE_VALID or SRB_STATUS_REQUEST_SENSE_FAILED.

If no request-sense information is available, InterpretRequestSense should check the SrbStatus value to determine whether to retry a given request or to determine an appropriate mapping to an NTSTATUS value.

The InterpretRequestSense routine might call a driver-supplied error-logging routine, as well. Whenever a SCSI class driver logs an I/O error, it should include the PathId, TargetId, Lun, and SrbStatus values from the SRB, and, if possible, pertinent request-sense information as part of the error log entry’s DumpData.

For more information about logging I/O errors, see Chapter 16.


A.1.5.3 SCSI Class Driver’s RetryRequest Routine

The port-miniport driver pair is responsible for retrying requests if certain SCSI errors occur, including bus parity errors, selection time-outs, and target controller busy errors. If their retry attempts fail, the Windows NT SCSI port driver completes the request with an appropriate error and logs the I/O error, as well.

SCSI class drivers should never attempt to retry a request that the port driver has already failed due to any of the preceding errors.

SCSI class drivers are required to retry requests that fail due to target controller errors, SCSI bus resets, or time-outs. In general, a RetryRequest routine can resubmit such an IRP to the port driver and direct that the SRB be placed at the head of the port driver’s LU-specific queue.

In particular, a RetryRequest routine should do the following:

  1. Ensure that a partial transfer request has the correct values set for the starting address and length.

  2. Zero the SrbStatus and ScsiStatus members of the SRB.

  3. Set up the SrbFlags member, as necessary for the device.

  4. Set up the I/O stack location for the port driver in the IRP as already described in Sections A.1.4 through A.1.4.5.

  5. Call IoSetCompletionRoutine for the IRP, because the driver’s IoCompletion routine might need to retry the request more than once, or to call the driver’s InterpretRequestSense or ReleaseQueue routine.

  6. Pass the request on to the port driver with IoCallDriver.


A.2 Designing a SCSI Filter Driver

If a Windows NT SCSI class driver already exists for a particular type of device, it might be unnecessary to write a driver for a new device of the same type. Each system-supplied SCSI class driver was designed to support SCSI-II-compliant peripheral devices of a given type and was tested against a number of vendors’ devices. Thus, any system-supplied SCSI class driver might provide all the support another SCSI-II-compliant device of its type needs.

If an existing SCSI class driver does not fully support a new device of its type, a new driver must be written as either of the following:

  • A new SCSI class driver for the device, which, in most cases, is also unnecessary

  • Or, an SFD layered over an existing system-supplied class driver

Unless a new device requires that every request be handled in a device-specific manner, writing an SFD takes far less development time than writing a new SCSI class driver.

An SFD supplies device-specific support for requests that require special handling, such as translating data sent to or returned from the device in a nonstandard format, or programming the device in response to an IRP_MJ_DEVICE_CONTROL request. Such an SFD can let the appropriate system SCSI class driver process all I/O requests that require no special handling.


A.2.1 Supporting I/O Requests

Like any SCSI class driver, an SFD is a standard higher-level driver with the following requirements common to all higher-level kernel-mode drivers:

  • It must supply a set of Dispatch routines to which the I/O Manager and/or still higher-level drivers can send IRPs for appropriate I/O operations. An SFD must support the same set of IRP_MJ_XXX as the SCSI class driver for its type of device.

  • For IRP_MJ_DEVICE_CONTROL requests, it must support as many of the class-driver-supported I/O control codes as its physical device can handle, and, if possible, emulate support for any remaining I/O control codes in the driver.

  • It must have a DriverEntry routine and can have any other standard higher-level driver routine, such as Unload or IoCompletion routines, as necessary.

If its device has special features, an SFD can support a set of driver-defined I/O control codes in addition to the system-required set of device-type-specific I/O control codes for IRP_MJ_DEVICE_CONTROL requests.

For more information about required device-type-specific requests, including the IRP_MJ_DEVICE_CONTROL request, and about defining driver-specific I/O control codes, see the Kernel-Mode Driver Reference.


A.2.2 Supporting SCSI-Specific Functionality

Depending on the nature of its device, an SFD also might (or must) be responsible for the following SCSI-specific functionality:

  • Translating data from or into a device-specific format before or after sending a transfer request to lower driver(s) if the device processes data in a nonstandard format

  • Setting up IRPs with SRBs for port-driver-supported I/O control requests, for driver-defined I/O control requests, or for pass-through requests, as necessary for its device and sending those IRPs through the class driver to the port driver

  • Establishing time-out values for requests

  • Supplying one or more IoCompletion routines and, like the underlying SCSI class driver, handling certain SCSI error conditions and retries for device-specific requests that require special handling

In general, an SFD has the same responsibilities as a SCSI class driver for those requests that require device-specific handling. For a discussion of the functionality required of SCSI class drivers, see Section A.1.

The following subsections describe how SFDs provide this SCSI-specific functionality in the context of each standard routine that a SCSI filter driver commonly has.


A.2.3 SCSI Filter Driver’s DriverEntry Routine

Like any other kernel-mode driver, the DriverEntry routine of an SFD must define its Dispatch (and Unload, if any) entry points in the input driver object. Then, the SFD must create one or more device objects with IoCreateDevice, layer itself over the appropriate SCSI class driver with IoAttachDevice, and set up one or more requests to find its device(s), if any, on a SCSI bus in the machine.

SFDs for certain kinds of devices might call IoGetConfigurationInformation to determine how many devices of their respective types are already represented by named device objects. The count of named device objects in the I/O Manager’s CONFIGURATION_INFORMATION-type structure places an upper bound on the number of device objects such an SFD might need to create.

If this system-defined structure does not have a count for the driver’s type of device, an SFD can examine the registry for devices of its type. For more information about using the I/O Manager’s global configuration information structure and/or the registry, see Chapter 16.

Note that a configuration information count or the registry keys for a given type of device also could include device objects for nonSCSI devices and/or other hardware vendors’ SCSI devices of the filter driver’s type.

An SFD’s DriverEntry routine must determine whether any particular device object(s) created by another driver actually represent a SCSI device that the driver supports.

After the SFD has layered itself over the SCSI class driver (or over another driver for its type of device), the DriverEntry routine must allocate a buffer and set up a request for SCSI-II inquiry data about the underlying device(s). On a successful return from this request, the SFD must inspect the buffered inquiry data for the vendor name and, possibly, model designation to determine whether the device is one the driver supports. If so, the SFD should set up the device extension in its device object. For more information about GetInquiryData routines, see Section A.1.3.1.

Note that every driver must call IoAttachDevice with a driver-created device object. Therefore, an SFD must create a device object before it can determine whether the underlying device is one that it supports. Its DriverEntry routine must call IoDetachDevice and, then, IoDeleteDevice to release any device objects it creates for unsupported devices. The DriverEntry routine also must free the memory it allocated for SCSI-II inquiry data before it returns control.

Assuming it finds a device it supports, the SFD must save the pointer, returned from IoAttachDevice, to the SCSI class driver’s device object in its own device extension so the SFD can send IRPs on to the class driver. If the SFD sends device-specific requests through the class driver, it might need to set up SCSI-specific data in the device extension of its device object(s). In other words, such an SFD’s device extension should include any SCSI-specific data necessary to manage its device-specific requests.

For more information about setting up a higher-level driver’s device extension with SCSI-specific data, see Section A.1.3.4. For more information about the general requirements for DriverEntry routines, see Chapter 5.


A.2.4 SCSI Filter Driver’s Dispatch Routines

Like any other higher-level kernel-mode driver, a SCSI filter driver must have one or more Dispatch routines to handle every IRP_MJ_XXX request for which the underlying SCSI class driver supplies a Dispatch entry point. Depending on the nature of its device, an SFD’s Dispatch entry point for any given request might do either of the following:

  • For a request that requires no special handling, set up the I/O stack location in the IRP for the next-lower-level class driver, possibly call IoSetCompletionRoutine to set up its IoCompletion routine for the IRP, and pass the IRP on for further processing by lower drivers with IoCallDriver

  • Or, set up a new IRP with an SRB and CDB for its device, call IoSetCompletionRoutine so the SRB (and the IRP if the driver calls IoAllocateIrp or IoBuildAsynchronousFsdRequest) can be freed, and pass the IRP on with IoCallDriver

    An SFD is most likely to set up new IRPs with the major function code IRP_MJ_INTERNAL_DEVICE_CONTROL.

For requests that require no special handling, an SFD’s Dispatch routine usually calls IoGetNextIrpStackLocation with an input IRP, sets up the class driver’s I/O stack location and, then, calls IoCallDriver with pointers to the class driver’s device object and the IRP. Note that an SFD seldom sets its IoCompletion routine in IRPs that require no special handling both because a call to the IoCompletion routine is unnecessary and because it degrades I/O throughput for the SFD’s device(s).

For requests that do require special handling, the SFD can do the following:

  1. Create a new IRP with IoBuildDeviceIoControlRequest, IoAllocateIrp, IoBuildSynchronousFsdRequest, or IoBuildAsynchronousFsdRequest, usually specifying an I/O stack location for itself.

  2. Check the returned IRP pointer for NULL and return STATUS_INSUFFICIENT_RESOURCES if an IRP could not be allocated.

  3. If the driver-created IRP includes an I/O stack location for the SFD, call IoSetNextIrpStackLocation to set up the IRP stack location pointer. Then, call IoGetCurrentIrpStackLocation to get a pointer to its own I/O stack location in the driver-created IRP and set up it up with state to be used by its own IoCompletion routine.

  4. Call IoGetNextIrpStackLocation to get a pointer to the class driver’s I/O stack location in the driver-created IRP and set it up with the major function code IRP_MJ_SCSI and an SRB (see Sections A.1.4 through A.1.4.5).

  5. Translate data to be transferred to the device into a device-specific, nonstandard format if necessary.

  6. Call IoSetCompletionRoutine if the driver allocated any memory, such as memory for an SRB, SCSI request-sense buffer, MDL, and/or IRP with a call to IoAllocateIrp or IoBuildAsynchronousFsdRequest, or if the driver must translate data transferred from the device in a device-specific, nonstandard format.

  7. Pass the driver-created IRP to (and through) the class driver with IoCallDriver.

Like a SCSI class driver, an SFD might have SCSI-specific BuildSrb or SplitTransferRequest routines to be called from the driver’s Dispatch routine(s), or might implement the same functionality inline.

For more information about BuildSrb and SplitTransferRequest routines, see Sections A.1.4.4 and A.1.4.5. For more information about general requirements for Dispatch routines, see Chapter 6.


A.2.5 SCSI Filter Driver’s IoCompletion Routines

A SCSI filter driver’s IoCompletion routine is called when lower (class and port) drivers have called IoCompleteRequest. The SFD’s IoCompletion routine should return STATUS_MORE_PROCESSING_REQUIRED to prevent completion processing of a driver-allocated IRP. If the SFD will reuse the original IRP before the SFD completes it, the SFD’s IoCompletion routine also should return STATUS_MORE_PROCESSING_REQUIRED.

Like any other higher-level driver, an SFD’s IoCompletion routine is responsible for calling IoFreeIrp to release any IRP the driver’s Dispatch routine(s) created with IoAllocateIrp or IoBuildAsynchronousFsdRequest.

Depending on its device, an SFD might supply an IoCompletion routine for IRPs it sends to the class driver from its Dispatch routines. In particular, a device that retrieves and processes data in a nonstandard format might require the SFD to have a TranslateDataIn routine called from its IoCompletion routine for transfer requests from such a device to system memory.

Note that such a TranslateDataIn routine would be called at IRQL DISPATCH_LEVEL and in an arbitrary thread context. Therefore, the buffer into which the driver returns data either must be located in nonpaged pool or must be locked down and accessible using mapped, nonpaged system-space virtual addresses. For more information about accessing user-supplied buffers at raised IRQL safely, see Chapter 16.

In general, a SCSI filter driver should supply an IoCompletion routine with the same functionality as a class driver’s IoCompletion routine for IRPs that the filter driver sets up with SRBs and CDBs. Consequently, a SCSI filter driver might have any or all of the ReleaseQueue, InterpretRequestSense, or RetryRequest routines that can be called from a SCSI class driver’s IoCompletion routine(s).

For more information about InterpretRequestSense, RetryRequest, and ReleaseQueue routines, see Sections A.1.5.1, A.1.5.2, and A.1.5.3. For more information about general requirements for IoCompletion routines, see Chapter 13.


A.3 Designing a SCSI Tape Miniclass Driver

Windows NT provides a generic tape class driver that handles OS-specific and device-independent tape tasks. To support a new tape device or family of tape devices a driver writer creates a device-specific tape miniclass driver that calls the system-supplied tape class driver.

If a tape miniclass driver calls only routines in the tape class driver, the miniclass driver can be portable across Microsoft operating systems that support Win32® applications.

A tape miniclass driver includes the minitape.h header file and links with the tape class driver DLL.

This section describes the support provided by the OS-specific tape class driver and provides design guidelines for writing a new tape miniclass driver. See the Kernel-Mode Driver Reference for details on the routines and data structures in the tape class and tape miniclass drivers. See Part I for a description of the SCSI device driver layers.


A.3.1 Using the Tape Class Driver

The system-supplied tape class driver implements device-independent, OS-specific tape support and exports support routines to the device-specific tape miniclass drivers.

The tape class driver:

  • Performs a large part of miniclass driver initialization, including allocating and initializing OS resources for the miniclass driver and the devices it supports.

  • Exports memory allocation and initialization routines.

  • Recognizes the maximum transfer size for HBAs and splits transfer requests when necessary.

  • Handles IRP_MJ_CREATE, IRP_MJ_READ, and IRP_MJ_WRITE requests.

  • Handles IOCTL_TAPE_CHECK_VERIFY, IOCTL_TAPE_FIND_NEW_DEVICES, and IOCTL_TAPE_REMOVE_DEVICE requests.

  • Performs driver-independent preprocessing for IRP_MJ_DEVICE_CONTROL requests and dispatches to the appropriate device-specific routines in the tape miniclass drivers.

  • Communicates with other drivers in the system to deliver the CDBs to the tape devices.

  • Translates between NT status codes and tape status codes, provides device-independent tape-specific error handing, and calls device-specific error handling routines.

  • Allocates driver context areas for miniclass drivers (minitape extension and command extension).

The tape class driver is provided as a kernel-mode DLL.

See the Kernel-Mode Driver Reference for descriptions of the TapeClass Xxx routines that can be called by a tape miniclass driver.


A.3.2 Supplying Required and Optional Miniclass Routines

A tape miniclass driver must have the following routines:

  • DriverEntry initializes the miniclass driver.

    This routine must be named DriverEntry for the OS to load the miniclass driver automatically.

    The miniclass DriverEntry routine sets driver-specific constants and entry points in the TAPE_INIT_DATA structure and calls TapeClassInitialize in the tape class driver.

  • TapeMiniVerifyInquiry determines whether the miniclass driver supports a particular tape device.

  • TapeMiniProcessCommand routines, such as TapeMiniGetPosition, implement device-specific processing for tape device control requests.

    These routines set up the SRBs with CDBs for each device control request. There is typically one routine for each command. The TapeMiniProcessCommand routines are called by the tape class driver. They return the SRBs and CDBs to the tape class driver and the tape class driver sends them to the tape device.

A tape miniclass driver can have the following optional routines:

  • TapeMiniExtensionInit initializes the optional device extension.

    See Section A.3.3 for information on device extensions.

  • TapeMiniTapeError supplements the error handling of the tape class driver.

    For most devices, the tape class driver returns an appropriate status value to the application when an error occurs. For some devices, returning the appropriate status requires device-specific information from the tape miniclass driver. For example, the miniclass driver for 4mm DAT tape drives can determine that, in certain situations, a TAPE_STATUS_BUS_ERROR status is actually due to no media in the drive. The 4mm DAT miniclass driver’s TapeMiniTapeError routine identifies these situations and changes the status that is returned to the application to TAPE_ERROR_NO_MEDIA.

The DriverEntry routine must use that name exactly. The other TapeMiniXxx routines can be named as the driver writer chooses, as long as the addresses of the routines are loaded into the appropriate members of the TAPE_INIT_DATA structure. To aid in debugging, a miniclass driver should prefix the TapeMiniXxx routines with some characters that identify its driver and should ensure the rest of the characters in the name reflect what the routine does.

See also the description of tape miniclass routines in the Kernel-Mode Driver Reference.


A.3.3 Storing Miniclass Context in Optional Extensions

A tape miniclass driver can use two optional extensions to store context:

  1. A driver-specific minitape extension

    When used, this extension typically stores SCSI inquiry data for devices. A driver that supports several devices can store details about each device, rather than repeatedly querying the devices.

    A miniclass driver specifies the size of the minitape extension in the TAPE_INIT_DATA structure in its DriverEntry routine. The tape class driver allocates the requested storage on behalf of the miniclass driver. The miniclass driver initializes the optional extension with a TapeMiniExtensionInit routine.

  2. A command-specific command extension

    When used, this extension stores command-specific context.

    A miniclass driver specifies the size of the command extension in TAPE_INIT_DATA in its DriverEntry routine. The tape class driver allocates the requested storage each time it sends a command to the miniclass driver. The miniclass driver initializes the command extension on the first call to process a given command; that is, when the CallNumber parameter to the TapeMiniProcessCommand routine equals zero.


A.3.4 Processing Tape Device Control Requests

Processing a single tape device control request can require multiple SRBs and thus multiple calls between the tape class driver and a TapeMiniProcessCommand routine. For example, to partition a tape the miniclass driver must first rewind the tape and then partition it.

When a TapeMiniProcessCommand routine is called by the tape class driver to process a request, the miniclass driver uses the return status to indicate whether the request is complete or whether the miniclass driver should be called back. The tape class driver maintains a zero-based count of the number of times a TapeMiniProcessCommand routine has been called for a given request and passes that count to the miniclass driver in the CallNumber parameter.

After building an SRB for a command, the TapeMiniProcessCommand routine usually returns to the tape class driver with the status TAPE_STATUS_SEND_SRB_AND_CALLBACK. This status directs the class driver to send the SRB to the device. If the operation is successful, the class driver increments the CallNumber and calls the TapeMiniProcessCommand routine again. If the SRB fails, the class driver returns to the calling application or to the TapeMiniProcessCommand routine, depending on the value of RetryFlags.

A TapeMiniProcessCommand routine uses the TAPE_STATUS_CHECK_TEST_UNIT_READY return status to direct the tape class driver to create an SRB for the test unit ready SCSI command and execute the command. This return status moves some work from the tape miniclass driver to the tape class driver.

A TapeMiniProcessCommand routine uses the TAPE_STATUS_CALLBACK return status to increment the CallNumber without sending an SRB to the device. This streamlines case statements that support several devices. For example, assume most of the tape devices supported by a miniclass driver require three SRBs to process a certain request. One device, however, only requires the first and third SRBs. The miniclass driver can use the same code to process the request for all its devices. For the unique device, the tape miniclass driver returns TAPE_STATUS_CALLBACK at the second step in command processing (when CallNumber equals one).

When a TapeMiniProcessCommand routine finishes processing a request, it returns to the tape class driver with a TAPE_STATUS_XXX that indicates the success or failure of the request.

For portability, the tape class/miniclass model insulates miniclass drivers from processing IOCTL codes. A miniclass driver implements the routines required in the TAPE_INIT_DATA structure, some of which equate to IOCTLs, but the miniclass driver does not operate on IOCTLs directly.


A.4 Designing a SCSI Miniport Driver

Windows NT SCSI miniport drivers are HBA-specific but OS-independent. That is, each miniport driver links itself with the system-supplied SCSI port driver, which is a dynamic-link library (DLL), and calls only the port driver’s ScsiPort Xxx routines to communicate with the system and its HBA. In the future, such SCSI miniport drivers will run on other Microsoft operating systems that support Win32 applications and also export the ScsiPort Xxx routines.

Note that any SCSI miniport driver that calls routines other than the ScsiPort Xxx cannot run in both Microsoft OS environments. To remain portable across Microsoft Windows systems, including Windows NT, SCSI miniport drivers must call only the system-supplied ScsiPort Xxx.


A.4.1 Required and Optional SCSI Miniport Routines

Every SCSI miniport driver must have at least the following system-defined routines:

  • DriverEntry to initialize the miniport driver

  • HwScsiFindAdapter to determine how (or whether) driver-supported HBA(s) are configured in the machine

  • HwScsiInitialize to initialize supported HBA(s)

  • HwScsiStartIo to start operations on its HBA(s) for incoming requests

  • HwScsiResetBus to handle bus-reset requests.

Depending on each HBA and the driver designer, SCSI miniport drivers also have some or all of the following system-defined routines:

  • HwScsiInterrupt to handle HBA-generated interrupts, which is optional if and only if the HBA does not generate interrupts so the miniport manages all I/O operations on its HBA by polling

    However, using polling exclusively has an adverse effect on a miniport’s performance and on its HBA’s I/O throughput. Such a miniport driver should also have a HwScsiTimer routine.

  • HwScsiDisableInterruptsCallback and HwScsiEnableInterruptsCallback to handle deferred I/O processing if interrupt-driven I/O operations take a long time

  • HwScsiTimer to time operations that require long delays on the HBA, or for any other purpose determined by the driver designer

    A miniport should have a HwScsiTimer routine if it has no HwScsiInterrupt routine so it can use the HwScsiTimer routine for efficient polling of its HBA.

  • HwScsiDmaStarted, which is required if the HBA uses a system DMA controller, to set up an HBA transfer after the system DMA controller has been programmed by the port driver

  • HwScsiAdapterState, which is optional if and only if the HBA has no BIOS or x86-real-mode driver and/or will never run in x86-only Microsoft Windows systems

Each of the preceding miniport driver routines, except DriverEntry, has a name chosen to describe its functionality. Except for DriverEntry, which is a required name for every miniport’s initial entry point, names for miniport driver routines can be anything the driver writer chooses.

The following sections describe the requirements for and functionality of each of these miniport driver routines. See the Kernel-Mode Driver Reference for their formal definitions and descriptions of their parameters.

Section A.4.11 describes error-handling requirements for SCSI miniport drivers.


A.4.2 SCSI Miniport Driver’s DriverEntry Routine

A DriverEntry routine is the initial entry point for most Windows NT kernel-mode drivers and for every SCSI miniport driver. A miniport’s DriverEntry routine is called with two input arguments of type PVOID and must do the following:

  1. Initialize a HW_INITIALIZATION_DATA structure on the stack with zeros.

  2. Set the HwInitializationDataSize member to sizeof(HW_INITIALIZATION_DATA).

  3. Set driver-specific and HBA-specific values in the HW_INITIALIZATION_DATA members, including the miniport driver’s entry points. The following entry points must be set:

    • HwScsiFindAdapter

    • HwScsiInitialize

    • HwScsiStartIo

    • HwScsiResetBus

    The following entry points can be set to a driver-supplied routine or must be set to NULL:

    • HwScsiInterrupt (NULL if the miniport uses polling exclusively)

    • HwScsiDmaStarted (NULL if the HBA uses PIO or is a busmaster)

    • HwScsiAdapterState (NULL if the miniport runs only on Windows NT platforms or if it is designed to also run on x86-only Windows platforms but the HBA has neither a BIOS nor x86-real-mode driver)

  4. Set up any driver-determined context data that the miniport’s HwScsiFindAdapter routine will use.

  5. Call ScsiPortInitialize with the pointers that were input to the DriverEntry routine, the address of the filled-in HW_INITIALIZATION_DATA, and the address of the context data, if any.


A.4.2.1 Calling ScsiPortInitialize

If its HBA can be connected on more than one type of I/O bus, a miniport driver needs to call ScsiPortInitialize for each bus type and must have a HwScsiFindAdapter routine for each bus type. Such a miniport driver must modify the AdapterInterfaceType and HwScsiFindAdapter members in the HW_INITIALIZATION_DATA after each call to ScsiPortInitialize, possibly modify the miniport-supplied context data for the new bus type, and call ScsiPortInitialize for each type of bus on which a supported HBA might be connected.

Before it calls the miniport’s HwScsiFindAdapter routine, ScsiPortInitialize does all of the following:

  • Checks the validity of the HW_INITIALIZATION_DATA

  • Collects and stores pertinent information in the device extension of a device object that it creates to represent the HBA

  • Allocates memory for and initializes with zeros a device extension of the requested size in which the miniport driver can store driver-determined information about the HBA

  • Allocates memory for a PORT_CONFIGURATION_INFORMATION-type buffer

  • Fills in as much configuration information about an adapter on a particular I/O bus as it can from the registry

Then, ScsiPortInitialize calls the miniport driver’s HwScsiFindAdapter routine, described in Section A.4.3.

If the miniport’s DriverEntry routine sets a particular AdapterInterfaceType value in the HW_INITIALIZATION_DATA but there is no bus of that type in the machine, the port driver returns an OS-specific status value indicating that such an HBA does not exist in the current machine. It does not call the driver-supplied HwScsiFindAdapter routine for that bus type.

A miniport driver does not remain loaded if the machine has no I/O buses of the type(s) specified by the miniport’s DriverEntry routine.

Note that ScsiPortInitialize also is responsible for the following before it returns control to the miniport’s DriverEntry routine:

  • Setting up all necessary system objects

  • Getting configuration information from and setting configuration information in the registry

  • Allocating system resources on behalf of the miniport driver, including memory for the miniport-specified DeviceExtensionSize, SpecificLuExtensionSize, and/or SrbExtensionSize in which the miniport driver can maintain HBA-specific state, per-logical-unit state, and/or per-request state, respectively

Each SCSI miniport driver defines the internal structure and contents of its device extension, logical unit extensions (if any), and SRB extensions (if any). A pointer to the HBA-specific device extension is an input argument to every system-defined miniport driver routine except DriverEntry. Many ScsiPort Xxx routines require this pointer as an argument.

ScsiPortInitialize can be called only from a miniport driver’s DriverEntry routine. For more information about the HW_INITIALIZATION_DATA structure and ScsiPortInitialize, see the Kernel-Mode Driver Reference.


A.4.2.2 Return from DriverEntry

When ScsiPortInitialize returns control, the DriverEntry routine propagates the return value of ScsiPortInitialize when DriverEntry itself returns control.

If a miniport driver calls ScsiPortInitialize more than once, its DriverEntry routine must propagate the lowest return value. A miniport driver writer cannot make any assumptions about the values returned by ScsiPortInitialize.


A.4.3 SCSI Miniport Driver’s HwScsiFindAdapter Routine

The OS-specific port driver fills in as much PORT_CONFIGURATION_INFORMATION as it can from the miniport’s HW_INITIALIZATION_DATA specification and from the registry before calling the given HwScsiFindAdapter routine with a pointer to the configuration information buffer.

In general, a HwScsiFindAdapter routine is responsible for using the supplied configuration information and/or for calling ScsiPort Xxx to collect sufficient configuration information to determine whether it supports an HBA on the I/O bus identified by the SystemIoBusNumber in the configuration information supplied by the port driver. If so, HwScsiFindAdapter is responsible for filling in any remaining configuration information for the supported HBA in the input ConfigInfo buffer, for setting up the miniport’s device extension with driver-determined state about that adapter, and for setting the Again parameter to an appropriate value before it returns control.


A.4.3.1 Calling ScsiPortXxx from HwScsiFindAdapter

Certain ScsiPort Xxx routines can be called only from the miniport driver’s HwScsiFindAdapter routine(s), in particular, the following:

  • ScsiPortGetBusData to get BUS_DATA_TYPE-specific configuration information, such as bus-relative device memory (access) ranges, interrupt vector or IRQL, DMA channel or port, etc.

  • ScsiPortValidateRange to verify that a miniport-supplied bus-relative access range has not already been claimed in the registry by another driver for its device

  • ScsiPortGetDeviceBase to map the (bus-relative) “physical” address range for an HBA to a system-assigned logical address range that the driver can use to communicate with the HBA by calling the ScsiPortRead Xxx and ScsiPortWrite Xxx routines with the mapped logical range addresses

  • ScsiPortFreeDeviceBase to release such a mapped range if HwScsiFindAdapter does not find an HBA it can support on a given I/O bus, as indicated by the PORT_CONFIGURATION_INFORMATION SystemIoBusNumber value

  • ScsiPortGetUncachedExtension to allocate a DMA buffer shared between the system and a busmaster HBA

For more information about the PORT_CONFIGURATION_INFORMATION structure and these ScsiPort Xxx routines, see also the Kernel-Mode Driver Reference.


A.4.3.2 Port-Driver-Supplied ConfigInfo for HwScsiFindAdapter

The system port driver always sets up the following PORT_CONFIGURATION_INFORMATION before it calls a miniport’s HwScsiFindAdapter routine:

  • Length to sizeof(PORT_CONFIGURATION_INFORMATION)

  • AdapterInterfaceType to the miniport’s HW_INITIALIZATION_DATA specification

  • InterruptMode to Latched

    If the miniport has no HwScsiInterrupt routine and, therefore, set the HwInterrupt entry point to NULL in the HW_INITIALIZATION_DATA, this member is irrelevant.

  • AtdiskPrimaryClaimed to TRUE if a previously loaded driver is using the I/O port range 0x1F0 to 0x1FF

  • AtdiskSecondaryClaimed to TRUE if a previously loaded driver is using the I/O port range 0x170 to 0x17F

  • NumberOfAccessRanges to the miniport’s HW_INITIALIZATION_DATA specification

The port driver also attempts to fill in the following members with values from the registry:

  • SystemIoBusNumber set to the system-assigned value for the I/O bus

    The miniport’s HwScsiFindAdapter routine can be called for each bus of the given AdapterInterfaceType with an updated value for SystemIoBusNumber, or this can be set to a value from the registry if the system detects an HBA on a particular bus of the given AdapterInterfaceType.

  • AccessRanges elements, set with the bus-relative RangeStart address and RangeLength, as well as whether each range is a RangeInMemory

    RangeInMemory set to FALSE indicates a range of ports in I/O space, rather than a memory-space range.

    The port driver either supplies all the information in an ACCESS_RANGE-type element or it sets all members of the element to (default) zero. Usually, the port driver supplies additional configuration information if it provides nonzero values for an access range.

    A miniport driver must map the bus-relative access range values, if any, supplied by the port driver with ScsiPortGetDeviceBase and use the mapped logical address values to determine whether the corresponding adapter is an HBA the driver supports. Never map and use a miniport-supplied range to access an adapter on the bus if the port driver supplies filled-in access range elements in the port configuration information it passes to the HwScsiFindAdapter routine. Using miniport-supplied addresses when the port driver has supplied range configuration information can reset an already configured adapter, making it disfunctional, or even cause the system to fail the boot process.

    For more information about using mapped logical access ranges, see also Section A.4.3.3.

  • BusInterruptLevel or BusInterruptVector

    This member is irrelevant if the miniport has no HwScsiInterrupt routine.

  • DmaChannel or DmaPort if the HBA uses a system DMA controller

    If the HBA is a busmaster or uses PIO, these members of PORT_CONFIGURATION_INFORMATION are irrelevant.

  • InitiatorBusId

    If the input InitiatorBusId[0] has the value zero, the miniport can assign a default value if its HBA does not require the use of particular value(s) determined by querying the HBA. Otherwise, the miniport should use any nonzero value assigned by the port driver if possible. Every miniport must update InitiatorBusId specifications to match what its HBA uses, if necessary querying the HBA to determine the appropriate values(s).

    A miniport driver must set an entry for each SCSI bus supported by the HBA, as indicated by the value of NumberOfBuses.


A.4.3.3 Setting up ConfigInfo in HwScsiFindAdapter

A HwScsiFindAdapter routine usually calls ScsiPortGetBusData and examines the returned bus-type-specific configuration information, such as POS data, EISA configuration data or PCI configuration data, for a supported HBA. If the input PORT_CONFIGURATION_INFORMATION AccessRanges elements have been filled in from the registry, the HwScsiFindAdapter routine should map the bus-relative access ranges with ScsiPortGetDeviceBase and use the returned logical range addresses to communicate with the HBA. If access range addresses are supplied by the port driver, HwScsiFindAdapter must not scan for adapters at other locations on that I/O bus.

If the port driver supplies access range values, HwScsiFindAdapter should not use the HwContext parameter. Nonzero access range values are usually accompanied by additional configuration information, which can indicate a plug-and-play environment. In such an environment, HwScsiFindAdapter is being called after the miniport’s DriverEntry routine has exited and thus the HwContext pointer is no longer valid. Legacy drivers that receive zero access range values from the port driver and that do their own scanning for supported HBAs can use the HwContext pointer.

If the PORT_CONFIGURATION_INFORMATION buffer input to the HwScsiFindAdapter routine has no configuration information from the registry, HwScsiFindAdapter can use values returned by ScsiPortGetBusData or, if necessary, set driver-defined default values for AccessRanges elements, InterruptLevel or BusInterruptVector, DmaChannel or DmaPort if the HBA uses system DMA, and InitiatorBusId.

The port driver is responsible for doing bus-level configuration programming, particularly of dynamically configurable I/O buses with published standard interfaces, such as PCI and PCMCIA. For an HBA on such an I/O bus, the miniport driver can simply accept the interrupt configuration information provided as values reserved for system use. For an HBA on other types of I/O buses, a miniport should program its HBA to use any supplied interrupt value if its HBA supports programmable interrupt configuration. If no interrupt configuration is supplied, as indicated either by the value zero or the value SP_UNINITIALIZED_VALUE, the miniport should either query its HBA if the HBA supports interrupt selection using jumpers or should supply a nonzero default interrupt configuration unless the HBA does not use interrupts. The interrupt configuration value zero indicates that the miniport controls its HBA in a polled mode.

The HwScsiFindAdapter routine must call ScsiPortValidateRange to check whether such driver-supplied access ranges can be used safely before it accesses an adapter on the bus. If ScsiPortValidateRange returns TRUE, the miniport can call ScsiPortGetDeviceBase to map the range and use the returned logical addresses in calls to ScsiPortRead Xxx and/or ScsiPortWrite Xxx to determine whether it supports an HBA on the I/O bus. If ScsiPortValidateRange returns FALSE, the miniport driver must not attempt to map and use those bus-relative access range values.

When the HwScsiFindAdapter routine finds an HBA it can support, this routine must fill in pertinent members, as appropriate for its HBA and the given AdapterInterfaceType, in the input PORT_CONFIGURATION_INFORMATION buffer. Every miniport driver must fill in the AccessRanges information, converting each AccessRanges element’s bus-relative RangeStart value with ScsiPortConvertUlongToPhysicalAddress before setting the bus-relative base address for a range in the ConfigInfo buffer.

For a supported HBA, HwScsiFindAdapter also should save the mapped logical range addresses returned by ScsiPortGetDeviceBase in the miniport’s device extension. Every miniport driver must call the ScsiPortRead Xxx and ScsiPortWrite Xxx with these mapped system addresses to communicate with its HBA(s).

For each successfully validated and mapped range in I/O space, the miniport driver calls the ScsiPortRead/WritePort Xxx routines to communicate with its HBA. For each such range in memory space, the miniport driver calls the ScsiPortRead/WriteRegister Xxx.

For an “AT-compatible” HBA, the HwScsiFindAdapter routine should check the input Atdisk..Claimed members and attempt to claim an “AT” range by resetting the value to TRUE.


A.4.3.4 Setting Again and Returning from HwScsiFindAdapter

For a supported and successfully configured HBA, the HwScsiFindAdapter routine returns SP_RETURN_FOUND.

The HwScsiFindAdapter routine should set Again to TRUE to indicate it should be called again with exactly the same ConfigInfo but a new DeviceExtension if another of its HBAs might be connected on the same I/O bus.

If HwScsiFindAdapter cannot find an HBA it supports, it should set Again to FALSE and return SP_RETURN_NOT_FOUND. If the HwScsiFindAdapter routine finds a supported HBA but the input PORT_CONFIGURATION_INFORMATION has inconsistent configuration data (such as an invalid DmaChannel or BusInterruptLevel on an EISA bus), it should set Again to FALSE and return SP_RETURN_BAD_CONFIG. For an HBA on a PCI bus, HwScsiFindAdapter can accept the interrupt configuration information supplied by the system.

Note that setting Again to FALSE and returning control with SP_RETURN_NOT_FOUND or SP_RETURN_BAD_CONFIG indicates that a given I/O bus, identified by the SystemIoBusNumber in the input ConfigInfo buffer, has no HBA that the miniport driver can support. It does not prevent the system port driver from calling HwScsiFindAdapter again with updated PORT_CONFIGURATION_INFORMATION to scan another I/O bus for HBA(s) if the machine has additional I/O buses of the same AdapterInterfaceType.


A.4.3.5 Return from HwScsiFindAdapter to ScsiPortInitialize

When a miniport driver’s HwScsiFindAdapter routine returns control, ScsiPortInitialize returns to the DriverEntry routine if the call(s) to HwScsiFindAdapter indicated that the miniport driver could not support an HBA. Otherwise, ScsiPortInitialize claims resources in the registry, sets up necessary system resources, such as NT interrupt and adapter objects, on behalf of the miniport driver. Then, it calls the miniport’s HwScsiInitialize routine, described in Section A.4.4.

In addition to the values it sets in the PORT_CONFIGURATION_INFORMATION buffer, ScsiPortInitialize also checks the registry for user-set values that disable any or all of the following:

  • Synchronous transfers on the HBA

    The port driver ORs the default SrbFlags that it maintains for the HBA with SRB_FLAGS_DISABLE_SYNCH_TRANSFER.

  • Bus-disconnect operations on the HBA

    The port driver ORs the default SrbFlags with SRB_FLAGS_DISABLE_DISCONNECT.

  • Tagged queueing

    The port driver sets the TaggedQueuing Boolean that it maintains for the HBA to FALSE.

  • Internal queueing of requests on the HBA

    The port driver sets the MultipleRequestPerLu Boolean that it maintains about the HBA to FALSE.

If any of the immediately preceding “disable” values are in the registry, it overrides whatever the HwScsiFindAdapter routine sets in the PORT_CONFIGURATION_INFORMATION buffer. Note that temporarily disabling synchronous transfers, bus-disconnect operations, tagged queueing, and HBA internal request queueing can simplify using a debugger to trace request handling in a miniport driver that is under development.

Note also that the Windows NT port driver uses the PORT_CONFIGURATION_INFORMATION values provided by a miniport’s HwScsiFindAdapter routine and/or in the registry to fill in the IO_SCSI_CAPABILITIES data for use by SCSI class drivers, as described in Section A.1.3.3.


A.4.4 SCSI Miniport Driver’s HwScsiInitialize Routine

For each supported HBA found by the miniport driver, its HwScsiInitialize routine is called to set up the HBA’s registers and initial protocol state, if any.

If the HwScsiInitialize routine enables interrupts on the HBA, the miniport’s HwScsiInterrupt routine will be called to handle any interrupts the device generates during the initialization.

If initializing the HBA causes a bus reset, the HwScsiInitialize routine must call ScsiPortNotification with the NotificationType value ResetDetected.

When the HwScsiInitialize routine returns control, ScsiPortInitialize sends SCSIOP_INQUIRY requests to the miniport driver, collecting the SCSI-II INQUIRY data about the devices on each bus and storing it for subsequent examination by Windows NT SCSI class drivers, as described in Section A.1.3.1. Each request to scan a SCSI bus for the INQUIRY data is sent to the miniport’s HwScsiStartIo routine, described in Section A.4.5.


A.4.5 SCSI Miniport Driver’s HwScsiStartIo Routine

As its name suggests, a HwScsiStartIo routine is the entry point for incoming requests to peripheral devices on the HBA-driven bus(es). HwScsiStartIo is called with pointers to a SCSI request block (SRB) and to the miniport’s device extension for per-HBA state.

If the miniport’s DriverEntry routine also requested that memory be allocated for logical unit extensions (see Section A.4.2.1), the HwScsiStartIo routine calls ScsiPortGetLogicalUnit with the input device extension pointer and the PathId, TargetId, and Lun values from the input SRB.

If the DriverEntry routine requested that memory be allocated for SRB extensions, the SrbExtension member in each SRB contains a pointer to the miniport’s per-request storage area. Note that a miniport driver must request that memory be allocated for SrbExtensions if it maintains per-request state information. It cannot use an SRB for this purpose.


A.4.5.1 Modifying SRBs

As mentioned in the preceding section, a SCSI miniport’s DriverEntry routine must request that the port driver allocate memory for SRB extensions if the miniport maintains per-request state information.

Otherwise, a SCSI miniport driver can write values into SRBs only for the following purposes and only in the following members:

  • To return required status about each operation in the SrbStatus and ScsiStatus members

  • If an underrun occurs, to update the DataTransferLength member

  • If it supports auto request sense and transfers request-sense information, to update the SenseInfoBufferLength

  • If SRB_FLAGS_UNSPECIFIED_DIRECTION is set in the SrbFlags member and the HBA is a slave DMA device, to reset the SRB_FLAGS_DATA_IN or SRB_FLAGS_DATA_OUT, as appropriate to the current transfer request


A.4.5.2 Handling SRB_FUNCTION_EXECUTE_SCSI

After the Windows NT higher-level SCSI class drivers have loaded, most of the SRBs sent to the HwScsiStartIo routine have the Function member set to SRB_FUNCTION_EXECUTE_SCSI.

On receipt of an SRB_FUNCTION_EXECUTE_SCSI request, a miniport’s HwScsiStartIo routine does the following:

  • Gets and/or sets up whatever context the miniport driver maintains in its device, logical unit, and/or SRB extensions

    For example, a miniport driver might set up a logical unit extension with pointers to the SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value, and a driver-defined value (or CDB SCSIOP_XXX value) indicating the operation to be carried out on the HBA.

  • Calls an internal routine to program the HBA, as partially directed by the SRB Flags, for the requested operation

    For a device I/O operation, such an internal routine generally selects the target device and sends the CDB over the bus to the target logical unit.

If the miniport driver uses system DMA, it must call ScsiPortIoMapTransfer before programming the HBA to transfer data. ScsiPortIoMapTransfer sets up the system DMA controller and calls the miniport’s HwScsiDmaStarted routine, described later in Section A.4.6.

All system-defined, required device I/O control requests sent to Windows NT SCSI class drivers are mapped to SRBs with the Function member set to SRB_FUNCTION_EXECUTE_SCSI.


A.4.5.3 Handling SRB_FUNCTION_ABORT_COMMAND

A HwScsiStartIo routine is also responsible for handling incoming SRBs with the Function member set to SRB_FUNCTION_ABORT_COMMAND.

For an abort request, the miniport’s HwScsiStartIo routine should verify that the given SRB has not been aborted already by calling ScsiPortGetSrb for the target logical unit and comparing the returned pointer to the current SRB’s NextSrb value. If they are unequal, the current SRB has already been aborted, and the miniport’s HwScsiStartIo routine should do the following:

  1. Set the input SRB’s ScsiStatus to SRB_STATUS_ABORT_FAILED.

  2. Call ScsiPortNotification with the NotificationType RequestComplete and with the input SRB.

  3. Call ScsiPortNotification again with the NotificationType NextRequest, or with NextLuRequest if the HBA supports tagged queueing or multiple requests per logical unit.

Otherwise, the HwScsiStartIo routine does the following:

  1. Sets up context for the request in its device, logical unit, and/or SRB extensions

  2. Programs the HBA to abort the given NextSrb request


A.4.5.4 Handling SRB_FUNCTION_RESET_BUS

The system port driver always sends its own reset-bus requests directly to the miniport’s HwScsiResetBus routine, described in Section A.4.7.

However, it is possible for the HwScsiStartIo routine to be called with an SRB in which the Function member is set to SRB_FUNCTION_RESET_BUS if a Windows NT SCSI class driver requests this operation. The HwScsiStartIo routine can simply call the HwScsiResetBus routine to satisfy an incoming bus-reset request.


A.4.5.5 Handling SRB_FUNCTION_FLUSH and SRB_FUNCTION_SHUTDOWN

If the HBA caches data internally, as indicated when the HwScsiFindAdapter routine set up the PORT_CONFIGURATION_INFORMATION, the HwScsiStartIo routine must support the SRB_FUNCTION_FLUSH and SRB_FUNCTION_SHUTDOWN requests as follows:

  • An SRB with its Function member set to SRB_FUNCTION_FLUSH tells the miniport driver to transfer data cached in the HBA, usually to a disk. The miniport must hold on to the flush request until all cached data has been transferred and, then, complete the flush request.

    Such a flush request might originate when an application closes a file or the application itself is terminated.

  • An SRB with its Function member set to SRB_FUNCTION_SHUTDOWN tells the miniport driver to complete transferring data, including flushing all cached data, out to the target device. The miniport driver must hold on to the shutdown request until no data remains in the HBA’s internal cache for the target logical unit and, then, complete the shutdown request.

    Note that a miniport can be called with more than one shutdown request, possibly for the same LU or with several shutdown requests for different LUs, before the system itself is actually shut down.


A.4.5.6 Handling SRB_FUNCTION_IO_CONTROL

Whether a miniport driver handles SRB_FUNCTION_IO_CONTROL requests depends on whether the HBA is to provide dedicated support for a user-mode application. Supporting this request allows a set of driver-defined (a.k.a. “private”) I/O control requests to be sent directly to the miniport driver. For SRBs with the Function member set to SRB_FUNCTION_IO_CONTROL, the DataBuffer member contains a pointer to a system-defined SRB_IO_CONTROL structure containing the driver-defined and application-specified ControlCode.

All system-defined, required device I/O control requests sent to Windows NT SCSI class drivers are mapped to SRBs with the Function member set to SRB_FUNCTION_EXECUTE_SCSI, not to SRB_FUNCTION_IO_CONTROL.


A.4.5.7 Handling Unsupported SRB_FUNCTION_XXX

Every HwScsiStartIo routine must handle the receipt of an unsupported SRB_FUNCTION_XXX as follows:

  1. Set the input SRB’s SrbStatus to SRB_STATUS_INVALID_REQUEST.

  2. Call ScsiPortNotification with the NotificationType RequestComplete and with the input SRB.

  3. Call ScsiPortNotification again with the NotificationType NextRequest, or with NextLuRequest if the HBA supports tagged queueing or multiple requests per logical unit.

  4. Return control.


A.4.5.8 Return from HwScsiStartIo

Every HwScsiStartIo routine must return TRUE, indicating that the input request was processed.

If the HwScsiStartIo routine cannot carry out a requested operation when it is called, HwScsiStartIo should do the following:

  1. Set the input SRB’s SrbStatus to SRB_STATUS_BUSY.

  2. Call ScsiPortNotification with the NotificationType RequestComplete and with the input SRB.

  3. Call ScsiPortNotification with the NotificationType NextRequest if the driver can accept a request to a different target logical unit than the one in the just completed SRB.

  4. Return TRUE.

The port driver resubmits any request returned with the SrbStatus value SRB_STATUS_BUSY to the miniport’s HwScsiStartIo routine later.

Eventually, every miniport driver must call ScsiPortNotification twice for each SRB input to its HwScsiStartIo routine: first, to complete the request (NotificationType RequestComplete) and, second, to tell the port driver to call the HwScsiStartIo routine again with the next SRB (NotificationType NextRequest or NextLuRequest).

The HwScsiStartIo routine of a miniport driver that manages its HBA exclusively by polling calls ScsiPortNotification with the NotificationType RequestTimerCall and a pointer to its HwScsiTimer routine. For more information about the HwScsiTimer routine, see Section A.4.9.


A.4.5.9 Other SRB_FUNCTION_XXX Requests

The following SRB Function values are defined for use in future versions of Windows NT:

  • SRB_FUNCTION_RECEIVE_EVENT

  • SRB_FUNCTION_RELEASE_RECOVERY

  • SRB_FUNCTION_RESET_DEVICE

  • SRB_FUNCTION_TERMINATE_IO

  • SRB_FUNCTION_REMOVE_DEVICE

The Windows NT SCSI port driver processes requests with the following SRB Function values without calling any SCSI miniport driver:

  • SRB_FUNCTION_CLAIM_DEVICE (see Section A.1.3.2)

  • SRB_FUNCTION_RELEASE_QUEUE (see Section A.1.5.1)

  • SRB_FUNCTION_FLUSH_QUEUE (see Section A.1.5.1)

  • SRB_FUNCTION_ATTACH_DEVICE (see Section A.2.3)

  • SRB_FUNCTION_RELEASE_DEVICE (see Section A.2.3)

Miniport driver designers can assume that their miniports will never be sent an SRB with any of the immediately preceding Function values. The Windows NT port driver handles these requests from SCSI class and filter drivers to protect higher-level SCSI drivers from having to access any HBA-specific (or miniport-specific) state information in order to find their devices or to cancel queued requests. This ensures that Windows NT SCSI class and filter drivers have no dependencies on any particular model of HBA.

For more information about the SCSI_REQUEST_BLOCK structure and the set of system-defined SRB_FUNCTION_XXX values, see also the Kernel-Mode Driver Reference.


A.4.6 SCSI Miniport Driver’s HwScsiDmaStarted Routine

A miniport driver of an HBA that uses the system DMA controller must have a HwScsiDmaStarted routine.

For a data transfer operation, such a miniport driver must call ScsiPortIoMapTransfer, passing in the pointers to its device extension for per-HBA data and to the SRB requesting the transfer, along with a range of logical addresses for the buffer from which or into which the data will be transferred.

Note that the logical address range passed to ScsiPortIoMapTransfer either must be the mapped values for the input SRB’s DataBuffer and DataTransferLength or a proper subset of this range. For most transfer requests, a miniport driver writer can assume that all data specified in the input SRB can be transferred in a single DMA operation.

In particular, a miniport driver might have to carry out more than one slave DMA transfer operation to satisfy a given SRB only if the HBA provides application-dedicated support and the application sends large transfer requests directly to the miniport driver. Otherwise, it is the responsibility of the Windows NT SCSI class drivers to split up large transfer requests into a set of partial transfer requests, each sized to suit the capabilities of the HBA (see Section A.1.4.5).

ScsiPortIoMapTransfer calls the miniport’s HwScsiDmaStarted routine when the system DMA controller is ready to transfer data between system memory and the HBA. HwScsiDmaStarted must set up the HBA for the transfer operation.

When a transfer operation is complete, the miniport driver must call ScsiPortFlushDma before it calls ScsiPortNotification with the SRB and/or calls ScsiPortIoMapTransfer to set up the DMA controller again for a subrange in an application-supplied buffer if the HBA is dedicated to the support of a user-mode application.

ScsiPortFlushDma flushes any remaining data cached in the DMA controller. Note that ScsiPortFlushDma also can be called to cancel a system DMA transfer, even if the miniport’s HwScsiDmaStarted routine has not yet been called.

For more information about ScsiPortIoMapTransfer and ScsiPortFlushDma, see the Kernel-Mode Driver Reference.


A.4.7 SCSI Miniport Driver’s HwScsiResetBus Routine

Every miniport driver must have a HwScsiResetBus routine, which is called with a pointer to the miniport’s device extension for HBA state and the PathId of the bus to be reset. If an attempted bus-reset operation fails or times out, the miniport driver should call ScsiPortLogError and, then, program the HBA for a hard reset.

A bus-reset operation might require the miniport driver to clean up state it maintains in the device extension and/or logical unit extensions for devices on the bus. HwScsiResetBus must complete any outstanding requests by calling ScsiPortCompleteRequest with the SrbStatus value SRB_STATUS_BUS_RESET or, for individual SRBs, ScsiPortNotification with this status value.

After completing the bus-reset request and any outstanding requests, the miniport must call ScsiPortNotification (if it has not already done so) with the NotificationType NextRequest, or NextLuRequest if the HBA supports tagged queueing or multiple requests per logical unit.

Note that the OS-specific port driver manages SCSI bus-reset delays on behalf of miniport drivers.


A.4.8 SCSI Miniport Driver’s HwScsiInterrupt Routine

On entry, a HwScsiInterrupt routine should determine if its HBA actually generated an interrupt. HwScsiInterrupt must return FALSE as soon as possible if it detects a spurious interrupt so the ISR for the device that actually generated the interrupt can be called quickly.

Otherwise, a miniport’s HwScsiInterrupt routine is generally responsible for completing the I/O operation that caused the interrupt. Depending on the HBA and the design of the miniport, a HwScsiInterrupt routine does some or all of the following:

  • Dismisses the interrupt on the HBA (required)

  • Notifies (by calling ScsiPortNotification or ScsiPortCompleteRequest) the port driver if the HBA indicates that certain SCSI error conditions occurred during the operation and possibly logging the error.

    For more information about logging errors, see Section A.4.11.

  • Completes the requested operation that caused the interrupt, such as calling ScsiPortIoMapTransfer (see Section A.4.6) if the interrupt came in from a previously selected target TID and LU indicating a readiness to transfer data

When the HwScsiInterrupt routine (or an internal miniport routine) completes an SRB, it calls ScsiPortNotification twice:

  1. First, with the NotificationType RequestComplete and the just satisfied SRB

  2. Next, with the NotificationType NextRequest, or with NextLuRequest if the HBA supports tagged queueing or multiple requests per logical unit

For better overall system performance, a miniport’s HwScsiInterrupt routine should do only the minimum necessary to process I/O requests. That is, the miniport should be designed to return control from the HwScsiInterrupt routine as quickly as possible. A HwScsiInterrupt routine must not call ScsiPortStallExecution with large intervals, thereby tying up a processor and preventing other drivers from servicing their device interrupts.


A.4.8.1 Deferring Interrupt-Driven I/O from HwScsiInterrupt

If interrupt-driven I/O operations take a long time to complete, a miniport driver should have a pair of HwScsiEnableInterruptsCallback and HwScsiDisableInterruptsCallback routines.

For example, if a miniport must stall for longer than 50 microseconds doing PIO, its HwScsiInterrupt routine should not retain control of the CPU for the full polling interval in order to complete a requested operation. Instead, its HwScsiInterrupt routine should do the following:

  1. Disable interrupts from the HBA.

  2. Set up the device extension with any context necessary to complete the operation.

  3. Call ScsiPortNotification with a pointer to the device extension, the NotificationType CallEnableInterrupts, and the miniport’s HwScsiEnableInterruptsCallback routine, described in Section A.4.8.2.

  4. Return control.

The Windows NT ScsiPortNotification calls the HwScsiEnableInterruptsCallback routine as a DPC routine. For more information about DPCs, see Chapter 3.

If a miniport’s HwScsiInterrupt routine cannot disable interrupts on the HBA but its interrupt-driven transfers can take more than 50 microseconds in the HwScsiInterrupt routine, the driver writer should tune the miniport by limiting the size of the transfers that it accepts. Otherwise, the mouse pointer will appear “jumpy” and/or serial and parallel throughput will degrade noticeably every time the miniport is transferring data concurrently.

Such a miniport’s HwScsiFindAdapter routine should reset the MaximumTransferLength value in the PORT_CONFIGURATION_INFORMATION to a value that allows the miniport to carry out interrupt-driven transfers without noticeably affecting the performance of other system drivers.

Such a miniport driver also might call ScsiPortNotification with a miniport-supplied HwScsiTimer routine. For more information about HwScsiTimer routines, which are synchronized with HwScsiInterrupt routines, see Section A.4.9.


A.4.8.2 SCSI Miniport Driver’s HwScsiEnableInterruptsCallback Routine

A HwScsiEnableInterruptsCallback routine finishes processing an interrupt-driven I/O operation without inhibiting I/O operations for other devices in the machine.

When the HwScsiEnableInterruptsCallback routine gets control, all system device interrupts are enabled except from the HBA because the HwScsiInterrupt routine disabled interrupts on the HBA before it called ScsiPortNotification. Thus, the miniport’s HwScsiInterrupt routine cannot be called and cannot disturb the context data it set up about the current operation while the HwScsiEnableInterruptsCallback routine is running.

A HwScsiEnableInterruptsCallback routine should do the following:

  1. Use the context that was set up for the operation in the input device extension to complete processing of the request that caused the interrupt.

  2. Call ScsiPortNotification with the NotificationType RequestComplete and the just satisfied SRB.

  3. Call ScsiPortNotification with the NotificationType NextRequest, or with NextLuRequest if the HBA supports tagged queueing or multiple requests per logical unit.

  4. Call ScsiPortNotification with a pointer to the device extension, the NotificationType CallDisableInterrupts, and the miniport’s HwScsiDisableInterruptsCallback routine, described in Section A.4.8.3.

  5. Return control.

The Windows NT ScsiPortNotification calls the HwScsiDisableInterruptsCallback routine with a subset of the system device interrupts disabled. No device interrupt with a system-assigned hardware priority (IRQL) less than or equal to the HBA’s can occur.

For more information about IRQLs, see Chapter 16.


A.4.8.3 SCSI Miniport Driver’s HwScsiDisableInterruptsCallback Routine

A HwScsiDisableInterruptsCallback routine should do the following:

  1. Reenable interrupts from the HBA.

  2. Return control.

Note that this routine must execute as quickly as possible to avoid tying up I/O operations for other devices in the machine. Consequently, a HwScsiDisableInterruptsCallback must do no more than reenable interrupts on the HBA before it returns control.


A.4.9 SCSI Miniport Driver’s HwScsiTimer Routine

A miniport driver that does not have a HwScsiInterrupt routine because it manages all HBA I/O operations by polling should have a HwScsiTimer routine. However, miniport drivers with HwScsiInterrupt routines frequently have HwScsiTimer routines as well.

While a miniport driver can call ScsiPortStallExecution to wait for a state change on the HBA, miniport drivers should never call this routine to wait for longer than one millisecond except, possibly, for an operation performed only when a miniport is initializing. ScsiPortStallExecution ties up the processor for the given interval, preventing other code in the system from doing useful work.

Instead of calling ScsiPortStallExecution with large input intervals and wasting many CPU cycles, a miniport driver should have a HwScsiTimer routine. One or more HwScsiTimer routines are particularly useful if the HBA does not generate a completion interrupt for every operation or if any commonly requested operation, such as a bus reset, takes longer than a millisecond.

After the HBA has been programmed for such an operation, the miniport driver calls ScsiPortNotification with the NotificationType RequestTimerCall, a pointer to its HBA-specific device extension containing context about the operation, its HwScsiTimer entry point, and a driver-determined interval.

ScsiPortNotification synchronizes calls to the HwScsiTimer routine with those to the HwScsiInterrupt routine so that it cannot execute concurrently while the HwScsiTimer routine is running.

HwScsiTimer is called once for each such call to ScsiPortNotification, which can be called from the HwScsiTimer routine itself. However, any call to ScsiPortNotification with the NotificationType RequestTimerCall overrides a preceding call for which the specified interval has not expired. That is, there is only one outstanding request to call a miniport’s HwScsiTimer routine at any given moment.

The interval passed in to ScsiPortNotification is in microseconds, and the minimum overhead for each call to a HwScsiTimer routine is approximately ten microseconds. An input interval of zero cancels the preceding request to call the HwScsiTimer routine, provided it has not been called or dispatched for execution on another processor in a Windows NT SMP machine.


A.4.10 SCSI Miniport Driver’s HwScsiAdapterState Routine

Windows NT miniport drivers should set this entry point to NULL in the HW_INITIALIZATION_DATA (see Section A.4.1) only if either of the following conditions hold:

  • The miniport drives an HBA to be connected on an I/O bus commonly found only in high-end, RISC-based platforms. That is, an x86-based platform running an x86-only Microsoft Windows system would not have an I/O bus of a type to support the HBA.

  • The miniport drives an HBA that could be found in an x86-based platform running an x86-only Windows system, but the HBA has neither a BIOS nor an x86-only real-mode driver.

Otherwise, a miniport driver must have a HwScsiAdapterState routine in order to be portable across Windows NT and x86-only Microsoft Windows systems.

A HwScsiAdapterState routine is responsible for saving and restoring the state of its HBA, as requested by the x86-only system during transitions between x86 real and protected processor mode.

For more information about HwScsiAdapterState routines, see also the Kernel-Mode Driver Reference.


A.4.11 Error Handling in SCSI Miniport Drivers

Every SCSI miniport driver is responsible for handling and for notifying the system port driver of the following kinds of SCSI errors, which should be set in the SrbStatus member before the driver completes the SRB it was processing when the error occurred:

  • SRB_STATUS_ERROR (if the HBA returns a nonspecific bus error)

  • SRB_STATUS_PARITY_ERROR

  • SRB_STATUS_UNEXPECTED_BUS_FREE

  • SRB_STATUS_SELECTION_TIMEOUT

  • SRB_STATUS_COMMAND_TIMEOUT

  • SRB_STATUS_MESSAGE_REJECTED

  • SRB_STATUS_NO_DEVICE

  • SRB_STATUS_NO_HBA

  • SRB_STATUS_DATA_OVERRUN (also returned for underruns)

  • SRB_STATUS_PHASE_SEQUENCE_FAILURE

  • SRB_STATUS_BUSY (TID busy)

For a data underrun, the miniport driver must update the SRB’s DataTransferLength to indicate how much data actually was transferred.

In addition, the miniport driver should log some of the preceding errors by passing the SRB to ScsiPortLogError, according to the following guidelines:

  • Log an error at the discretion of the driver writer for SRB_STATUS_ERROR.

  • Always log an error for SRB_STATUS_PARITY_ERROR.

  • Always log an error for SRB_STATUS_UNEXPECTED_BUS_FREE.

  • Always log an error for SRB_STATUS_SELECTION_TIMEOUT.

  • Always log an error for SRB_STATUS_COMMAND_TIMEOUT.

  • Log an error for SRB_STATUS_DATA_OVERRUN whenever an overrun occurs, but not when an underrun occurs.

  • Always log an error for SRB_STATUS_PHASE_SEQUENCE_FAILURE.

  • Always log an error for SRB_STATUS_BUSY for hardware errors.

To log an error, a miniport driver calls ScsiPortLogError with one of the following system-defined error or warning codes:

  • SP_BUS_PARITY_ERROR maps to SRB_STATUS_PARITY_ERROR.

  • SP_UNEXPECTED_DISCONNECT (by the target LU)

  • SP_INVALID_RESELECTION maps to SRB_STATUS_PHASE_SEQUENCE_FAILURE or SRB_STATUS_ERROR.

  • SP_BUS_TIME_OUT maps to SRB_STATUS_SELECTION_TIMEOUT.

  • SP_REQUEST_TIMEOUT maps to SRB_STATUS_COMMAND_TIMEOUT.

  • SP_PROTOCOL_ERROR maps to SRB_STATUS_PHASE_SELECTION_FAILURE, SRB_STATUS_UNEXPECTED_BUS_FREE, or SRB_STATUS_DATA_OVERRUN for an overrun condition.

  • SP_INTERNAL_ADAPTER_ERROR maps to SRB_STATUS_ERROR.

  • SP_IRQ_NOT_RESPONDING (warning that the miniport has detected that the HBA is no longer generating interrupts)

  • SP_BAD_FW_ERROR (“FW” stands for “firmware”)

  • SP_BAD_FW_WARNING

ScsiPortLogError allocates an error-log packet, sets it up, and logs the I/O error in the Windows NT event log on behalf of the miniport driver. System administrators or users can monitor the condition of an HBA by examining the system event log and, if necessary, reconfigure, repair, or replace the HBA before it fails.


Kernel-mode Glossary

This glossary contains terms and acronynms of interest to Windows NT® kernel-mode driver writers. It does not attempt to define terms of interest to Win32® user-mode driver writers.

For brevity, this glossary uses the term “NT” as an adjective to mean “kernel-mode” or “microkernel” and as a noun to mean the system kernel-mode components, collectively, of Windows NT.


A

access right

A permission granted to a process to manipulate a particular object in a particular way (by calling a system service). Different NT object types support different access rights, which are stored in an object’s ACL.

access violation

An attempt to execute a memory operation that is not allowed by the underlying page protection. See also probe and SEH.

There are four basic kinds of actions that can cause access violations:

· Attempting an invalid operation, such as writing to a read-only page

· Attempting to access memory beyond the limit of the current program’s address space (a.k.a. “length violation”)

· Attempting to access a page to which the system forbids access

For example, code is not allowed to run in the low-order 64K of the NT user-mode address space in order to simplify the detection of NULL pointer references.

· Attempting to access a page that is currently resident but dedicated to the use of an NT component

For example, user-mode code is not allowed access a page that the Kernel is using.

Note that this term pertains to memory operations. It has nothing to do with the Security Manager’s checking of user-mode access rights to objects.

ACE

(1) Access control entry

An ACE is an individual entry in an ACL. An ACE contains an SID and describes the access rights that a particular user or group of users has to a system resource. The set of all ACEs on the object are used to determine whether an access request to the object is granted. See also security descriptor.

(2) Advanced computing environment

A consortium of hardware and software companies formed to promote an open computing environment.

ACL

Access control list

An ordered list of ACEs.

adapter object

A kernel-mode-only object type, defined by the I/O Manager and supported by the HAL component. An adapter object represents a hardware bus adapter or DMA controller channel. Adapter objects “connect” different kinds of devices on the bus or DMA controller, each device (or kind of device) with its own driver.

affinity

(1) A programmer-defined attribute of a process and/or thread on a multiprocessor platform:

· For a process, its affinity is the programmer-determined set of processors on which its threads are permitted to run.

· For a thread, its default affinity is identical to that of the process to which it belongs; otherwise, its affinity must be a proper subset of the process’s.

(2) The set of processors on which a particular interrupt is enabled in a given machine.

alert

A Boolean that provides a way to break into a thread’s execution at a point where either of the following conditions is met:

· The thread is in an alertable wait state (as specified when the wait service was called).

· The thread polls the alerted flag.

APC

Asynchronous procedure call

An APC is a Kernel-defined control object representing a procedure that is called asynchronously. APCs are thread-context dependent; that is, they are queued to a particular thread for execution.

There are three different kinds of APCs in NT:

User APCs are used by certain asynchronous NT system services to allow user-mode applications or protected subsystems to synchronize the execution of a thread with the completion of an operation or the occurrence of an event such as a timer’s expiration. User APCs are, by default, disabled. That is, they are queued to the user-mode thread, but they are not executed except at well-defined points in the program. Specifically, they can only be executed when an application or protected subsystem has called a wait service and has enabled alerts to occur, or if it has called the test-alert service.

Kernel APCs are normal kernel-mode APCs. They are much like a normal user APC except that they are executable by default. That is, they are enabled except when the thread is already executing a Kernel APC. (Note that a special Kernel APC always preempts these.)

Special Kernel APCs cannot be blocked except by running at a raised IRQL. They are executed at APC_LEVEL IRQL (see IDT), in kernel mode. These types of APCs are used by the system to force a thread to execute a procedure in the thread’s context. An example of this is I/O completion: the I/O Manager needs to get back into the context of the original requestor of the I/O operation so that it can copy buffers, and so forth. In order to do this, the I/O Manager must be able to access the virtual address space of the thread/process, and the most efficient way to complete the operation is to be in the calling thread’s context.

arbitrary thread context

See thread object.

ARC

(1) Advanced RISC computing

Refers to a RISC-based computer architecture standard, associated with the ACE consortium.

(2) Windows NT runs on top of ARC-compliant machines, including both RISC- and CISC-based platforms that supply the following components to the OS loader:

· In ACE-compliant platforms, such as most MIPS machines, a set of machine firmware that supports bootstrap loading and execution as an “abstracted” set of ARC routines and ARC devices.

· In x86-based platforms, a hardware recognizer (Ntdetect) that finds devices necessary to bootstrap the system by querying the ROM BIOS and builds a hardware database that emulates the RISC-based ARC firmware.

· In both kinds of platforms, a HAL that interfaces between the hardware and the system. See also HAL.

Device drivers for certain types of devices create an alias between the names of their device objects and the corresponding ARC device name by calling IoAssignArcName.

associated IRP

One of a set of IRPs, created by the highest-level driver in a chain of layered drivers, that specifies some part of an I/O request to be sent on to a lower-level driver. When all associated IRPs in the set have been processed, the I/O Manager completes the original request. See also IRP.

asynchronous I/O

A model for I/O in which the operations carried out to satisfy I/O requests do not necessarily occur in sequence. The application that originally made the request can continue executing (rather than waiting for its I/O to complete), the I/O Manager or a higher-level driver can reorder I/O requests as they are received, and a lowest-level driver can start an I/O operation on a device before it has completed the preceding request, particularly in a multiprocessor machine.


B

backing store

A mass-storage medium, such as a disk, that serves as backup “memory” for paging when physical memory becomes full. See also paging file.

balance set

The set of processes currently in the system, in particular, processes whose threads are eligible for execution. See also dispatch state and working set.

Any process is either part of the balance set (“included”) or not (“excluded”). A kernel-mode process must remain in the balance set as long as any of its threads owns a mutex object. Each thread owning one or more mutexes continues to run until it releases its last mutex, when the thread is suspended. The process is not removed from the balance set until every thread has released all mutexes.

At any given moment, the balance set depends on the availability of physical memory (pages) to back the virtual address space associated with each active process. If a physical memory shortage occurs, the NT Memory Manager first trims the working set of each active process to its minimum, then (if necessary) removes processes from the balance set.

based section

A section allocated at the same virtual address for each process that has a view of the section. See also section and view.

BCB

Buffer control block

An opaque Cache Manager structure, used to maintain state as a file system pins and releases data (for example, its volume structure) in the cache.

big-endian

Refers to a processor memory architecture in which the byte layout is as follows:

· Byte N is the most significant (and, in conventional layout diagrams, the “leftmost”) byte of:

· A word composed of bytes N and (N + 1).

· A double word composed of bytes N, (N + 1), (N + 2), and (N + 3).

· A K-byte memory entity composed of bytes N, (N + 1),...,(N + K - 1).

The address of the preceding word, double word, or K-byte entity is its most significant byte, N.

Some RISC machines can be configured for either big-endian or little-endian byte addressing. For a big-endian configuration, the most significant bit of a 16-bit short value is the “leftmost” bit at byte N, while the least significant bit is the “rightmost” bit of byte (N + 1). See also little-endian.

The terms “big-endian” and “little-endian” are derived from Jonathan Swift’s Gulliver’s Travels.


C

Cache Manager

An NT component that provides file caching support for NT file system drivers.

CAM

Common access method

An interface definition for SCSI devices.

CCB

Context control block

An internal NT file system structure, in which a file system maintains per file object state for an open instance of a file. See also FCB and DCB.

CDB

Command descriptor block

A structure, defined by the SCSI-II standard, used to communicate requests to a target device on a SCSI bus.

CDFS

CD-ROM file system

CD-ROM

Compact disk, read-only memory

chase

To follow a linked list, queue, or other software-defined path. For example, when a file system encounters a symbolic link file within a path specification, it “chases the symbolic link” to redirect operations to the target file. See also symbolic link.

checked build

A version of the system that has been compiled with debug symbols and built with special support for debugging under-development components, such as new drivers. See also free build.

child process

When a new NT-native process is created by calling a system service, the caller must specify a “parent” process from which the new process inherits its token, quota, and base runtime priority for threads. The new process can optionally inherit any or all of the following from the specified parent process:

· A copy of the parent’s virtual address space

· All object handles that were opened with the inheritance attribute

· Debugging and exception handling ports

However, any user-mode process becomes a wholly independent peer to its so-called parent process as soon as it has been created. After process creation, any process-to-process dependencies become the responsibility of a protected subsystem, such as the Win32 or POSIX subsystem.

A kernel-mode-only (a.k.a. “system”) process has the initial system process as its parent.

CID

Client identifier

A unique value that identifies a thread.

class driver

(1) A higher-level driver for a physical device, which is connected to a so-called intelligent controller, such as a SCSI HBA, or to an adapter bus. Such a class driver communicates with the corresponding, lower-level port driver according to an established protocol, such as NT-defined SRBs. The system-supplied SCSI class drivers are examples of this type of class driver.

(2) A higher-level driver that provides system-required but hardware-independent support for a given class of physical devices. Such a class driver communicates with a corresponding hardware-dependent port driver, using a set of system-defined device control requests, possibly with additional driver-defined (private) device control requests. The system-supplied keyboard and mouse class drivers are examples of this type of class driver, as are parallel class drivers.

client/server model

A model for structuring applications or operating systems such that the system is divided into processes (servers), each of which provides a set of specialized services to other processes (clients).

collided page fault

A page fault caused by one thread while an in-page I/O operation for another thread is being done on the same page.

commitment

The NT Memory Manager’s current value for paging file usage.

context

The execution state of a thread at any given moment:

· For a user-mode thread, the platform-dependent register state, kernel stack, TEB, and user stack in the address space of the process to which the thread belongs.

· For a kernel-mode thread, the platform-dependent register state and kernel stack.

Kernel-mode threads have neither a TEB nor a user-mode context, but they must have an associated process. See also process object.

Most NT device drivers do not have a context in this sense. Unless a driver (such as an FSD) creates its own process and/or thread(s), it does not have its own stack space or register state. For each NT driver, the set of objects it owns and the IRPs that it can access in the device queue associated with its device object or any driver-created internal queue(s) can be considered all or part of its context.

context record

A system-defined structure, containing the current register state for an exception handler. See also SEH.

control objects

A class of Kernel-defined object types, used to manage all kernel-mode operations except dispatching and synchronization. See also dispatcher objects.

Control objects include: APC, DPC, device queue, interrupt, and process objects. Note that all control object types except processes are kernel-mode-only objects, invisible to user-mode code. User-mode APC objects are also “invisible” in the sense that they look more like user-supplied routines to be called on return from certain system services (such as a request to read a file) than like objects.

controller object

A kernel-mode-only object type, defined by the I/O Manager. A controller object represents a hardware controller or channel. The driver calls IoAllocateController to carry out synchronized I/O on attached devices. Controller objects “connect” a set of similar devices attached to a controller with a single driver.

critical section

A piece of code that temporarily has exclusive access to a resource, such as device-state data stored in a device extension to which access must be synchronized among some number of routines.


D

DACL

Discretionary access control list

A discretionary ACL is part of the security descriptor for an object. It can be applied to a newly created object in order to constrain access to the object. See also ACE, access right, ACL and security descriptor.

DCB

Directory control block

An internal NT file system structure in which a file system maintains state for an open instance of a directory file.

deadlock

A runtime error condition that occurs when two threads of execution are blocked, each waiting to acquire a resource that the other holds, and both unable to continue running.

device extension

A part of a device object whose size is determined when a driver creates the device object and whose internal structure and contents are driver-defined. For many NT drivers, a device extension is the driver’s major and only global data-storage area, used to maintain device state and to contain any system-defined objects and other data that the driver writer decides to use.

device object

A kernel-mode-only, I/O Manager-defined object type, used to represent a physical, logical, or virtual device whose driver has been loaded into the system. Each driver calls IoCreateDevice to initialize a device object for each such device that driver services. An unnamed device object is invisible outside the creating driver. See also device extension and driver object.

Devices are “visible” to end users as named file objects, stored in a directory protected against careless or malicious user-mode access. User-mode code (protected subsystems) must access any device through the opaque handle returned by the system service that opens the file object that represents the device.

device queue object

A kernel-mode-only, Kernel-defined control object type, used to queue I/O requests for subsequent processing. A device queue object has an associated executive spin lock and a state (Busy or Not-Busy).

An I/O request, called a “queue entry,” is not actually placed in the queue unless the state of the device queue object is already Busy:

· On the transition from Not-Busy to Busy, the driver is expected to service the queue entry immediately because that entry is not queued.

· Subsequent requests while the state is Busy are queued in FIFO or key-sorted order.

· An attempt to remove a queue entry from an empty queue causes the transition from Busy to Not-Busy.

· An attempt to remove a queue entry from an empty queue when the state is Not-Busy is a programming error, which causes the system to “bugcheck” (i.e., crash) in a checked build.

directory file

A file that points to or lists a set of named file objects.

Note that on-disk directory files are represented in NT as file objects, not as object directory objects. See also file object and object directory object.

directory object

See object directory object.

DIRQL

Device interrupt request level

The IRQL at which a given device interrupts. See also IRQL and IDT

dispatch state

For a thread at any given moment, one of the following:

Initialized

Ready - queued and eligible for dispatch to a processor

Standby - ready and selected to execute, but a context switch to the thread has not yet occurred

Running

Waiting - suspended until a particular dispatcher object is set to the Signaled state

Terminated

dispatcher objects

A class of Kernel-defined object types, used to manage dispatching and synchronization. See also control objects.

Dispatcher objects have a state (either Signaled or Not-Signaled), and are arguments to the wait services or Kernel wait routines (KeWait Xxx). Dispatcher objects include: events, (kernel-mode-only) mutexes, queues, semaphores, threads, and timers.

Kernel-mode threads synchronize their operations by waiting on one or more dispatcher objects, or by waiting on other objects, such as user-mode file objects, that contain “hidden” kernel-mode dispatcher objects.

It is a fatal error to wait on a dispatcher object at IRQL >= DISPATCH_LEVEL.

DMA

Direct memory access

A method of transferring data between a device and main memory without intervention by the CPU. DMA is handled by the device itself (bus-mastering or first-party DMA) or by a DMA controller chip in the system (third-party DMA).

DPC

Deferred procedure call

A DPC is a Kernel-defined control object type, representing a procedure that is to be called later. DPCs are executed in kernel mode at IRQL DISPATCH_LEVEL. See also IDT and IRQL.

A DPC is primarily used when an ISR, such as a clock ISR or device driver’s interrupt service routine, needs to perform more work but should do so at a lower IRQL than the one at which an ISR executes. Note that getting the IRQL lowered back down quickly is important for overall system response time. The ISR, then, can request that a procedure be executed at a lower IRQL at a later time.

The Kernel queues DPC objects in a DPC queue, and other executive components and drivers queue their DPCs by calling Kernel-supplied support routines. When a DPC queue entry is dequeued, the Kernel calls the procedure.

driver object

A kernel-mode-only object representing an NT driver’s load image, used by the I/O Manager to locate certain entry points in the driver.


E

EA

Extended attribute

An EA is viewed as an untyped name-value pair that is defined by the user to describe extended information about a file. Typical system uses are to store the icon for an image, to indicate that the file is a symbolic link, etc.

ECC

Error correction code

EISA

Extended industry standard architecture

A standard that defines the architecture of (and interface to) the extended PC I/O bus. See also ISA.

event object

A user-mode object upon which one or more threads can wait until the event is set to the Signaled state. Every user-mode event object is implemented through the use of a kernel-mode event object. A kernel-mode event object is an instance of a Kernel-defined dispatcher object type.

Each event object can be classified as either of the following:

· When a synchronization event (a.k.a. “autoclearing event”) is set to the Signaled state, a single thread that was waiting on the event is released (its dispatch state transitions from waiting to ready, standby, or running), and the event is automatically reset to the Not-Signaled state.

· When a notification event is set to the Signaled state, all threads that were waiting on the event are released, and the event remains in the Signaled state until it is explicitly reset to the Not-Signaled state.

exception

A synchronous error condition, resulting from the execution of a particular machine instruction. See also SEH.

executive

The collection of kernel-mode components that form the base Windows NT operating system. Executive components include the Executive Support, Kernel, Memory Manager, Cache Manager, Process Structure, Interprocess Communication (LPC and RPC), Object Manager, I/O Manager, Configuration Manager, Hardware Abstraction Layer, and Security Reference Monitor. Each executive component except the Cache Manager and Hardware Abstraction Layer supplies a specialized set of native system services, and every executive component exports a set of kernel-mode support routines for use by other executive components.

NT drivers also call kernel-mode support routines supplied by executive components, including (but not limited to) routines beginning with the prefix “Ex” for Ex(ecutive) Support. Other support routines of interest to drivers begin with the prefixes: Hal, Ke(rnel), Ps (for Process Structure), Ob(ject), Io, Mm (for Memory Manager), Cc (for Cache Manager), Se(curity), Rtl (Runtime Library), and FsRtl (for File System Runtime Library). See also Zw routines.

Note that NT drivers, including device, intermediate, and file system drivers, are considered part of the executive after they are loaded. The “operating system” visible to end users is actually a protected subsystem that runs in user mode, calling system services, on top of the executive. That is, the executive is hidden from end users by a subsystem-supplied interface that emulates Windows®, POSIX, or some other operating system.

executive worker threads

See system worker threads.


F

FAT

File allocation table

adjective: an NT file system, originally a type of file system native to MS-DOS®; also, noun: the logical structure describing a disk partition for the FAT file system

fault tolerance

A feature of the Windows NT operating system environment, which includes the end-user-visible Win32 Disk Administrator utility supported by a system-supplied NT intermediate driver, ftdisk, that handles disk partition mirroring, striping, and volume set management.

FCB

File control block

An internal NT file system structure in which a file system maintains state for an open file.

file object

A user-mode-visible object that represents an open instance of a file, device, directory, volume, etc. A file object is accessed through the file handle returned by a protected subsystem’s function, which calls down to a system service that opens (or creates) a file object. Callers of the I/O system services can wait on a file handle.

At any given moment, several file objects can be associated with a single, shared data file, but each such file object has a unique handle and an object-specific value for the current file pointer.

A file object is sometimes called a “persistent object” because the entity that backs any runtime set of file objects is retained across system boots.

filter driver

An intermediate driver that intercepts and processes I/O requests bound for an underlying device. Such a driver calls IoAttachDevice at initialization to alias its own device objects to those of the underlying device driver(s) or to each intermediate driver layered above an underlying device driver. See also device object and SFD.

free build

a.k.a. “production” or “retail” system

A version of the system that has been compiled and built without symbols and support for debugging under-development components, making the free build smaller in size and faster than a checked build due to the lack of debugging symbols and checks. See also checked build.

FS

File system

An FS is a file system driver that registers itself with the NT I/O Manager as an active file system. Unlike many other operating systems, NT does not have a particular must-be-resident file system, and several NT file systems can be active at the same time. NT file systems are dynamically located when a volume is mounted or when a remote name is being resolved: the I/O Manager calls the registered FSs in turn until one file system “recognizes” the volume structure or remote name.

Examples of NT-supplied file systems are FAT, NTFS, CDFS, the LAN Manager redirector, NPFS (named pipe file system), and RAW. See also IFS.

FSD

File system driver

The FSD refers to the portion of a driver that executes in the context of the caller, to which an I/O request is dispatched when the request is originally made. The FSD has no process virtual address space and no particular thread context because it operates in the context of whoever happens to call it.

An FSD also executes in the context of the lower-level (intermediate or mass-storage device) driver’s DPC routine when the I/O operation is being completed if the FSD indicated that it should be called upon completion of the IRP.

The term FSD normally refers to the device driver part of a file system driver when the driver also has an FSP associated with it; otherwise, it’s called simply a file system.

FSP

File system process

The FSP usually refers to a set of device-dedicated or system worker threads that aid a file system driver (FSD) in getting work done, but can also refer to a kernel-mode process set up by an FSD. FSP threads are generally used when a file system driver needs a thread context in which to work. An example is when the driver needs to be able to wait without tying up the calling thread. An FSP kernel-mode process is used only when an FSD must have a driver-specific process address space in which its device-dedicated threads execute. Most NT file system drivers conserve system memory and improve performance by not creating their own kernel-mode processes.

Communication between the FSD and the FSP threads is performed through the use of a communication region. This region is generally allocated in a device object’s device extension and consists of an interlocked queue and a synchronization event. See also device object, device extension, and event object.

Note that any NT driver (not just file systems) can create one or more device-dedicated threads, and a file system driver can use system worker threads. In particular, an NT driver should create a thread for I/O operations that use synchronization mechanisms (such as events, timers, semaphores, mutexes, etc.) other than spin locks. Such a driver’s thread can wait for an indefinite, nonzero interval on a Kernel-defined dispatcher object, but context switches to the thread slow down such a driver’s I/O operations.


G

GUID

Globally unique identifier

See SID.


H

HAL

Hardware abstraction layer

An NT executive component that provides platform-specific support for the NT Kernel, I/O Manager, kernel-mode debuggers, and lowest-level device drivers. The HAL exports routines that abstract platform-specific hardware details about caches, I/O buses, interrupts, etc., and provides an interface between the platform’s hardware and the system software. For example, the HAL implements a routine to map each device driver’s bus-relative device interrupt vector to a system-assigned vector with a corresponding platform-specific hardware priority (DIRQL), as well as several routines that provide DMA support.

hardware exception

See SEH.

hardware priority

See IDT and IRQL.

HBA

Host bus adapter

A hardware device that “connects” NT and a SCSI bus. A SCSI miniport driver manages the lower layers of the SCSI-II protocol on the HBA. See miniport driver; see also port driver, class driver, SFD, and SCSI.

heap

See pool memory.

hyperspace

A block of virtual memory somewhere in kernel space, dedicated to the use of the NT Memory Manager. Every instance of a user-mode virtual address space has an associated hyperspace area where the Memory Manager maintains internal data (such as page table pages and WSLs) for the current process. See also kernel space and user space.


I

IDB

Interrupt dispatch block

An internal structure used by the NT Kernel.

IDT

Interrupt dispatch table

A Kernel-defined call table with a platform-dependent number of first-level entries (some for DIRQL ISRs) and second-level entries for interrupt transfer routines (dependent on a first-level ISR). For symmetric multiprocessor platforms, the Kernel sets up an IDT for each processor. See also multiprocessor machine.

The NT Kernel reserves eight first-level entries per IDT for its own use; the remaining first-level entries can be connected to a platform-specific I/O bus interrupt by the HAL or to a device interrupt. The Kernel’s reserved entries (in low-to-high IRQL priority) are defined by the following constants:

· PASSIVE_LEVEL - execute thread

· APC_LEVEL - execute special Kernel APC; page fault

· DISPATCH_LEVEL - dispatch (execute DPC)

· WAKE_LEVEL - debugger execution

· CLOCK2_LEVEL - interval-timer execution

· REQUEST_LEVEL - interprocessor request

· POWER_LEVEL - power failure notification

· HIGH_LEVEL - machine checks or bus errors

The set of software and hardware interrupt vectors mapped to these constants is platform-dependent, but none can be connected to a driver’s interrupt object. Note that the constants PASSIVE_LEVEL through DISPATCH_LEVEL and WAKE_LEVEL correspond to software interrupt vectors. DIRQLs for device drivers usually have hardware priority higher than DISPATCH_LEVEL but lower than CLOCK2_LEVEL.

IFS

Installable file system

See FS.

integral subsystem

A protected subsystem (server) that performs an essential operating system task. For NT, this group includes the Local Security Authority, the Security Accounts Manager, the Session Manager, and the network server. See also protected subsystem.

intermediate driver

An intermediate driver processes I/O requests “between” the highest-level driver (frequently a file system driver) and the lowest-level driver that controls the hardware device for which the request is bound. See also class driver, filter driver, and layered driver.

Examples of intermediate drivers include the NT ftdisk driver, which supports fault tolerance, other filter drivers, and many class drivers, particularly those layered over mass-storage device drivers.

interrupt

An asynchronous hardware signal, usually indicating that a peripheral device needs service, that is detected by the processor. An interrupt causes the processor to save state, to branch to a fixed location, and to resume execution at that location. See also IDT.

interrupt object

A kernel-mode-only, Kernel-defined control object type, used to connect a hardware interrupt source (see DIRQL) and an ISR to an IDT entry, or to connect an ISR and an interrupt transfer routine (dependent on the ISR at its IRQL) to a second-level IDT entry.

Each interrupt object:

· Can be associated with a single IDT entry (and, therefore, with a single processor in an SMP machine)

· Is either LevelSensitive or Latched, depending on the platform, I/O bus, or device

· Can be one of several interrupt objects associated with a given IDT entry if and only if every interrupt object for that entry is of a single type (LevelSensitive or Latched)

For a second-level IDT entry, an ISR, such as an ISR for a bus adapter that is associated with a first-level IRQL routes the interrupt to the device-specific interrupt transfer routine for service via its entry in the IDT.

When more than one set of interrupt objects are connected to the same first-level IDT entry, the corresponding ISRs are called in the same order in which they were connected.

invalid page

A page-sized range of virtual addresses for which a page fault occurs when any address in the range is referenced. For NT drivers, referencing an invalid page causes a fatal page fault unless the driver is running at an IRQL <= APC_LEVEL when the reference occurs.

I/O stack location

a.k.a. “IRP stack location or stack location”

An I/O stack location refers to the part(s) of an IRP that are allocated for each driver in a chain of layered drivers. Each driver owns one of the stack locations in the packet and obtains driver-specific information about what I/O operation to carry out on the target device, using the supplied parameters.

Higher-level drivers in such a chain are responsible for setting up the next-lower driver’s I/O stack location. Any higher-level driver’s I/O stack location can also be used to store context about an operation so that the driver’s IoCompletion routine can perform its cleanup operations. See also IRP.

I/O status block

An I/O status block is a data structure that is part of each IRP. An I/O status block serves two purposes:

· For asynchronous system calls, it provides the user or a higher-level driver’s IoCompletion routine a way of determining whether or not the service worked when the IRP is completed.

· It also provides more information about why the service either worked or did not work.

Upon completion of a system service, the Status field indicates whether the driver(s) that processed the IRP actually satisfied the request or failed the IRP with an error status. The Information field supplies the caller with more information about what actually occurred. For example, it contains the number of bytes actually transferred after a read or write operation.

IPC

Interprocess communication

The Windows NT system has two defined, message-passing mechanisms for IPC:

LPC, defining the port object type with a server process that supplies system (port) services to local (using the same physical memory on a single system) client processes

RPC, with an NT-supplied runtime library that implements such RPC capabilities as binding a client process in one address space with a server process in another address space and sending necessary messages to make a remote procedure call possible

IRP

I/O request packet

An IRP is the basic I/O Manager structure used to communicate with drivers and to allow drivers to communicate with each other. A packet consists of two different parts:

Header, or fixed part of the packet - This is used by the I/O Manager to store information about the original request, such as the caller’s device-independent parameters, the address of the device object upon which a file is open, etc. It is also used by drivers for storing such information as the final status of the request. See also I/O status block and device object.

I/O stack locations - Following the header is a set of I/O stack locations, one per driver in the chain of layered drivers for which the request is bound. Each stack location contains the parameters, function codes, and context used by the corresponding driver to determine what it is supposed to be doing.

IRQ

A hardware line over which a peripheral device, bus controller, other processor, or the Kernel signals a request for service to the microprocessor. (IRQ is a common abbreviation for interrupt request line.) See interrupt.

IRQL

Interrupt request level

The hardware priority level at which a given kernel-mode routine runs, thereby masking off interrupts with equivalent and lower IRQL on the processor. Note that such a routine can be preempted by any interrupt with a higher IRQL. Note also that running at IRQL DISPATCH_LEVEL or higher prevents threads (even those with the highest real-time priority level) from running on the same processor until the current kernel-mode routine lowers IRQL. However, running at raised IRQL on a given processor has no effect on the IRQL of any other processor in a symmetric multiprocessor machine. See also IDT, interrupt object, IRQ, ISR, multiprocessor machine, and priority.

ISA

Industry standard architecture

A standard defining the architecture of the PC I/O bus (a.k.a. “AT bus standard”).

ISR

Interrupt service routine

A routine whose function is to service a device when it generates an interrupt.

An NT driver’s ISR executes at raised IRQL, usually at the DIRQL of the interrupt object(s) set up for its device (i.e., drivers connected to second-level IDT entries execute at the IRQL of the first-level entry they connect to). Every NT driver’s ISR should execute as quickly as possible, doing only what is necessary to save sufficient state, to make the device stop generating interrupts, and to queue a DPC that completes interrupt processing at a lower IRQL. See also DPC object.


K

kernel mode

The privileged processor mode in which the executive runs. A driver or thread running in kernel mode has access to system memory and hardware. See also executive and user mode.

kernel space

(a.k.a. “system space”)

A block of virtual memory, dedicated to the use of kernel-mode code. Generally, the NT kernel space is a range of high-order virtual addresses around one to two gigabytes in size. See also hyperspace, pool memory, and user space.

key object

A NT object type defined by the Configuration Manager that represents an entry in the registry database. Each instance of a key object represents a registry node, that is, part of registry path to a set of subkeys, or it contains value entries. See also registry and value entry.


L

latched interrupt

(a.k.a. “edge-triggered interrupt”)

An interrupt that occurs at the transition from deasserted to asserted on the IRQ line.

layered driver

One of a collection of drivers that respond to the same IRPs. See also class driver, filter driver, intermediate driver, and I/O stack location.

The term “layered driver” describes the highest-level and lowest-level drivers in a chain of layered drivers that process the same IRPs, along with all intermediate drivers in the chain.

LBN

Logical block number

A logical block number identifies a physical block on a disk, using a logical address rather than physical disk values (for cylinder, track, and sector). For a disk with N blocks (a.k.a. “sectors”), the corresponding LBNs are numbered 0 through (N - 1). See also MCB and VBN.

level-sensitive interrupt

An interrupt that occurs when the signal is asserted on the IRQ line.

LPC

Local procedure call

See also IPC and port object.

little-endian

Refers to a processor memory architecture in which the byte layout is as follows:

· Byte N is the least significant (and, in conventional layout diagrams, the “rightmost”) byte of:

· A word composed of bytes N and (N + 1).

· A double word composed of bytes N, (N + 1), (N + 2), and (N + 3).

· A K-byte memory entity composed of bytes N, (N + 1),...,(N + K - 1).

The address of the preceding word, double word, or K-byte entity is its least significant byte, N.

Intel microprocessors always support little-endian addressing. Some RISC microprocessors can be configured for either big-endian or little-endian addressing. For a little-endian configuration, the least significant bit of a 16-bit short value is the “rightmost” bit at byte N, while the most significant bit is the “leftmost” bit of byte (N + 1). See also big-endian.

logical memory

A HAL-provided mapping between system physical memory and a device-accessible address range. See also map.

LSA

Local Security Authority

An integral subsystem responsible for managing security access tokens for users.

LU

Logical unit

From a SCSI-II HBA driver’s point of view, a physical or virtual peripheral device, addressable through a TID, attached to a SCSI bus. See also TID.

LUID

Locally unique identifier

See SID.


M

MACL

Mandatory access control list

A part of the security descriptor for each object in a system with a B1 or higher security rating from the U.S. National Computer Security Center.

map

(1) To translate a virtual address into a physical address or vice versa. See also MDL.

(2) To translate a bus-relative address into a logical address to be used by a device as if it were a physical address. See also HAL.

(3) To alias a bus-relative interrupt vector to a system-assigned interrupt vector. See also HAL.

MCB

Map control block

An MCB is a structure used by NT file systems in mapping the VBNs for a file to the corresponding LBNs on the disk.

MDL

Memory descriptor list

An opaque structure, defined by the Memory Manager, that uses an array of physical page frame numbers (PFNs) to describe the pages that back a virtual memory range.

method

A routine supplied with an NT object type that handles standard operations (such as open, close, delete, parse, dump, and read or reset security attributes) on every instance of the object type.

MIDI

Musical instrument digital interface

miniport driver

A device-specific kernel-mode driver linked to an NT port driver, usually implemented as a dynamic-link library, that provides an interface between the port driver and the system. NT defines the architecture of the following kinds of miniport drivers:

(1) An HBA-specific driver, linked against the NT-supplied SCSI port driver, that drives one or more SCSI buses, or a mass-storage device driver that uses the NT SCSI port driver as its interface to the system.

(2) A video-adapter-specific driver, linked against the NT-supplied video port driver, that supports a corresponding display driver.

(3) A network-adapter-specific driver, linked against the NT-supplied NDIS library.

mirror driver

a.k.a. “shadow driver or RAID1 driver”

An intermediate (layered somewhere between an FS and a disk DD) driver whose responsibility is to maintain a duplicate of a disk partition.

The system-supplied fault tolerant disk driver, ftdisk, is a mirror driver.

mount

Operation that requests a file system to make a volume useable by the I/O Manager. This is done by filling in the information in a VPB which indicates the serial number and label of the volume, as well as by filling in the pointer to the file system’s device object that represents the “volume” mounted on the real device. See VPB.

multiprocessor machine

A platform with more than one CPU. Windows NT is designed to run on multiprocessor machines, usually configured to be:

· Homogenous - all CPUs are identical, and either all have identical coprocessors or none has a coprocessor.

· Closely coupled - all CPUs share memory and have uniform access to memory.

· Symmetric- every CPU can access memory, handle any interrupt, and access I/O control registers just like every other CPU in the system. Note that any version of Windows NT that runs on a symmetric multiprocessor machine also runs unchanged on a uniprocessor machine based on the same CPU.

While Windows NT can run on asymmetric platforms, configured with a master CPU that handles all IRQs and one or more slave CPUs as long as the platform is both homogenous and closely coupled, the system has been designed to run primarily on symmetric multiprocessor platforms because SMP machines have better performance characteristics, particularly for I/O.

mutex object

A kernel-mode-only, Kernel-defined dispatcher object type, used to provide mutually-exclusive, deadlock-free access to a resource.

A mutex owner (thread) must run exclusively in kernel mode while it retains ownership; an attempt to switch to user mode while holding a mutex causes a system crash. Ownership of a mutex by a given thread has the following effects:

· Prevents the owning thread’s process from leaving the balance set

· Raises the owning thread’s priority to the the lowest real-time priority value if the owning thread’s priority is not already higher

· Prevents the delivery of normal kernel-mode APCs

Each mutex object has a caller-initialized, associated level value, and a given thread should acquire ownership of a mutex if and only if:

· The requested mutex is currently unowned by another thread.

· The requesting thread does not already own a mutex of a (numerically) higher level.

Implementing the preceding policy, together with the requirement that a mutex owner run exclusively in kernel mode, prevents deadlocks.

Note that recursive ownership of a mutex is possible. However, any thread that claims a mutex N times must explicitly release that mutex N times in order to set the mutex to the Signaled state.


N

native services

See system services.

NLS

National language support

A set of routines that give applications access to locale-specific information. See also Unicode.

nonpaged pool

See pool memory.

NtDetect

See ARC, (2).

NTFS

NT file system

A file system, native to NT, that supports file system and file data recovery, extremely large storage media, file-granular compression, and various other features.


O

object

A single, runtime instance of a system-defined object type. Objects visible in user mode include process, thread, section, file, event, semaphore, key, timer, port, object directory, symbolic link, I/O completion port, and token objects. See also object type and opaque.

Many user-mode objects are implemented through the use of a corresponding kernel-mode object, including processes, threads, events, semaphores, and timers.

Kernel-mode-only objects include APC, DPC, device queue, interrupt, mutex, driver, device, adapter, controller, and stream file objects.

object directory object

A type defined by the NT Object Manager and used to implement hierarchical object names.

Note that file system directories are implemented as file objects, not as object directory objects.

object type

An opaque data structure that defines a protected entity implemented and manipulated by the operating system. For example, the system service that reads a file operates on an open file object. See also opaque.

Every user-visible object has:

· A type (whose name must be unique).

· Two parts:

A header operated on by the NT Object Manager

A body operated on by type-specific system services

· A set of standardized rules for object creation, deletion, protection, access, management, and naming

User-visible objects can can be implemented using kernel-mode objects.

Kernel-mode objects are visible to (and, except for certain fields in driver and device objects, directly accessible only by) the executive component that defined a given object type. They are are indirectly accessible to other kernel-mode code through the type-specific support routines exported by the defining component. For example, KeInitializeDeviceQueue operates on device queue objects.

opaque

Pertains to any data structure, including NT objects, defined with a deliberately hidden internal structure that is known only to the executive component that defined the type. However, the defining component supplies system services and, usually, kernel-mode support routines to manipulate opaque objects. User-mode data types also can be opaque.


P

page frame

A block of contiguous physical memory used to store the contents of a virtual page. Note that the virtual page size and page frame size are usually identical, but that a page frame size is actually microprocessor-dependent.

paged pool

See pool memory.

paging file

A system file containing the contents of virtual pages that have been paged out of memory. See also backing store and system paging file.

PCI

Peripheral component interconnect

A dynamically configurable I/O bus, described by the PCI Local Bus Specification published by the PCI SIG.

PCMCIA

Personal computer memory card international association

PCR

Processor control registers

An internal data structure in which the Kernel stores per-processor information.

PEB

Process environment block

PFN

Page frame number

Also, the database in which the NT Memory Manager maintains information about every physical page in the system. Each PFN is an index for a physical page in the array of records that makes up the PFN database. See also PTE.

PIO

Programmed I/O (a.k.a. abbreviation for “parallel input/output”)

A technique of making data transfers between a device and system memory using remapped system-space virtual addresses, rather than DMA.

polling

A pernicious, but sometimes necessary, coding technique that wastes CPU cycles in a driver until the physical device updates its registers. NT device drivers should never implement polling loops and should minimize the interval they specify in calls to KeStallExecutionProcessor (no longer than 50 microseconds) if they must poll their devices.

pool memory

Regions located in kernel space from which memory blocks can be allocated and deallocated dynamically. (Some other systems call this “heap memory.”) The NT Memory Manager creates two kinds of pools for system use and for use by drivers via Ex(ecutive) Support routine calls:

· Paged pool is a based region that can be paged in and out of a process’s working set. Each process has its own set of PTEs that map paged pool into its address space. Touching a page in this pool can cause a page fault at any time, so NT drivers should allocate only those data structures accessed exclusively at IRQL PASSIVE_LEVEL or APC_LEVEL (see IDT) from paged pool.

· Nonpaged pool is a based region for which all processes share a set of PTEs. The NT Memory Manager guarantees that nonpaged pool is resident in physical memory at all times; therefore, this region can be accessed from any process’s address space without causing a page fault. However, nonpaged pool is a limited system resource.

NT drivers should allocate from paged pool for entities that are accessed exclusively at or below IRQL APC_LEVEL and in a nonarbitrary thread context. They should minimize their usage of nonpaged pool by “saving” it for data that must be accessed at or above IRQL DISPATCH_LEVEL. Allocations of contiguous or noncached memory come from nonpaged pool.

The Memory Manager allocates entities from both pools using a buddy scheme. See also kernel space.

port object

An instance of an object type representing a conduit for messages between local (server/client) processes. It is used in the LPC (Local Procedure Call) model of NT as an object accessible to user-mode processes through system services. The server (callee) process defines a named connection port object and sets up two (unnamed) communication port objects when a client (caller) process connects to the named port.

port driver

A lowest-level NT driver that responds to a set of system-defined device control requests and, possibly, to an additional set of driver-defined (private) device control requests, sent down by a corresponding class driver. A port driver insulates class drivers from the specifics of HBAs and synchronizes operations for all its class drivers. Some examples of system-supplied port drivers include the following:

(1) The NT SCSI port driver that supports a set of device-type-specific class drivers, and possibly SFDs, above it and one or more HBA-specific miniport drivers, and possibly nonSCSI mass-storage device drivers, which link themselves to this OS-dependent port driver (implemented as a dynamic-link library) and call its ScsiPort Xxx routines.

(2) The NT video port driver that supports display drivers and adapter-specific, kernel-mode miniport drivers, which link themselves to the NT-dependent port driver (implemented as a dynamic-link library) and call its VideoPort Xxx routines.

(3) The NT parallel port driver.

In general, any driver of a device that is a so-called “intelligent controller” or a bus adapter can be a port driver if it communicates with one or more class drivers according to an established protocol and synchronizes access to the controller or bus.

See also class driver, miniport driver, ScsiPort routines, and VideoPort routines.

POSIX

Portable operating system for (UN)IX

A Windows NT protected subsystem that supports the POSIX standard.

PRCB

Processor control block

An extension of the PCR.

priority

An attribute of a thread that determines when and how often it is scheduled to run. For a running thread, its priority falls into either of two classes, each class with sixteen levels:

· Variable priority class has values in the range 0 to 15. This class is used by most threads.

Threads with variable priority are always preemptible; that is, they are scheduled to run round-robin with other threads at the same level. In general, the Kernel manages a variable-priority thread as follows: when the thread is interactive with a user, its priority is high (given a boost); otherwise, its priority decays by one level per quantum the thread runs until it reaches its original programmer-defined base priority level.

· Real-time priority class has values in the range 16 to 31. This class is used by time-critical threads, making such a thread preemptible only by a thread with higher priority.

Note that any thread, whatever its priority attribute, is always preemptible by a software or hardware interrupt.

priority boosts

A set of system-defined constant values, supplied when NT drivers complete an IRP. For example, drivers for interactive devices usually call IoCompleteRequest with a device-type-specific priority boost value. The boost is added to the priority of the user-mode thread that originally requested the I/O operation to compensate for that thread’s wait on the I/O.

probe

To check whether a memory access of a particular kind (such as read) to a data entity, such as a handle for an object or a buffer, is allowed without causing an access violation. Note that user-supplied parameter values are always probed and captured on the kernel stack before an NT system service gets control in kernel mode. The system service is responsible for probing whatever a captured pointer accesses.

process object

A Kernel-defined control object type, representing the virtual address space and control information necessary for the execution of a set of thread objects. A user-mode process object defines the base priority, default affinity, and directory table base value for its threads and for any child processes it creates. Every user-visible process object is implemented through the use of an embedded kernel-mode process object.

A kernel-mode-only process object must be initialized before any thread objects that belong to the process.

protected subsystem

A server that appears to perform operating system functions for its native applications and subsystem-specific drivers by calling system services. A protected subsystem runs in user mode and its interface to end users emulates another operating system, such as Windows or POSIX, on top of NT. See also integral subsystem.

PTE

Page table entry

The Memory Manager uses a PTE to represent the state of a virtual page.

pulse

To set an event to the Signaled state, satisfy as many waiters on the event as possible, and to reset the event to the Not-Signaled state.


Q

quantum

a.k.a. “time slice”

A brief period of time during which a given thread executes in a multitasking operating system before it is subject to being rescheduled against other threads at the same priority.

quota

For each process, NT sets limits on certain system resources the process’s threads can use, including quotas for paging-file, paged-pool, and nonpaged-pool usage, etc. For example, the Memory Manager “charges quota” against the process as its threads use page-file, paged-pool, or nonpaged-pool memory; it also updates these values when threads release memory.


R

RAID

Redundant array of inexpensive disks. Alternate meanings: redundant array of independent disks; redundant array of inexpensive devices.

raise an exception

A deliberate transfer of control to an exception handler when an exception occurs. A kernel-mode component, including any NT driver, cannot raise an exeception while running at IRQL >= DISPATCH_LEVEL without bringing down the system. See also SEH.

raised IRQL

Any system-assigned hardware priority at which kernel-mode code runs that is greater than IRQL PASSIVE_LEVEL. See also DIRQL, IDT, and IRQL.

range

A finite and discrete sequence of values. Note that a virtual address range can be backed by a set of discontiguous physical pages or by a file stored in discontiguous sectors on disk.

RAW

An NT-supplied FSD that is the “last resort” for all NT I/O requests requiring file system support. When the I/O Manager calls active NT file systems to mount a volume, RAW is always called last because it recognizes all disk and tape media.

However, RAW supplies very primitive file handling capabilities. That is, it does not impose any on-disk file structure or metadata structures for the information on the media; it simply allows read/write access to the logical blocks on the physical disk. For example, it treats the whole disk as a single file and supplies physical-disk-level VBN access to the disk.

RAW is not an acronym.

redirector

A system-supplied (network) file system driver that provides access to files on remote machines.

region

A range of virtual addresses.

When a user-mode caller creates a section or maps a view, it must specify a region. The NT Memory Manager rounds the specified region’s starting address down to the nearest 64K boundary and rounds its size in bytes up to the next host-page-size boundary.

registry

a.k.a. “configuration registry”

A database containing configuration and control data for the system, including which file systems and drivers to load at system boot. The NT Configuration Manager exports system services to manipulate registry key objects contained in the database. See also key object.

resource

(1) An entity (such as a device object, file object, section object, variable, structure, or buffer) visible to one or more processes.

(2) In kernel mode, a system-defined type of opaque variable manipulated by the Ex..Resource Xxx support routines. A shared resource is a multiprocessor-safe synchronization mechanism, rather like a gating semaphore with a dynamic limit value. Drivers, usually NT file systems, can use one or more resource variables to control access to a memory entity, such as a file or database, on a multiple-reader (shared access), single-writer (exclusive access) basis.

In particular, NT file systems that support caching files and paging I/O share certain resource variables with the Cache and Memory Managers.

RPC

Remote procedure call

A call from one process’s thread (client) to another’s (server) that exists in a different address space, usually on another networked machine. See also IPC.

Rtl routines

There are two general sets of (C) Runtime Library routines supplied with the system, one each for user mode and for kernel mode. All NT drivers can call the kernel-mode Rtl Xxx.

run

verb: to execute on a processor.

noun: (1) within an MCB, a contiguous range of VBNs mapped to a contiguous range of LBNs (a.k.a. “extent”); (2) slang for a quantum, as in “when the thread is given the run...”

runtime priority inversion

A condition that can occur when threads with mismatched priority attributes share resources or when a higher priority thread waits for a lower priority thread to complete some action. Such an inversion occurs whenever:

· A high priority thread is blocked, waiting for a lower priority thread to release a shared resource or to complete an action (and probably to set a dispatcher object to the Signaled state).

· This lower priority thread is also blocked, because many other higher priority threads are ready for execution, so they will be run first.

Under these circumstances, the waiting high-priority thread undergoes a runtime priority inversion, because one or more lower priority threads will run before it does. Note that two threads with mismatched priorities must be very careful to avoid deadlocks if they “share” a resource in a mutually exclusive manner (that is, only one thread at a given time can access the resource). See also thread object, priority, resource, and deadlock.


S

SACL

System access control list

A part of the NT security descriptor for an object, used to maintain per object auditing information. See also security descriptor, ACE, and ACL.

SAM

Security Account Manager

An integral subsystem that maintains a database of information on user accounts, including passwords, any account groups a given user belongs to, the access rights each user is allowed, and any special privileges a given user has.

SCSI

Small computer standard interface (pronunciation: “scuzzy”)

NT SCSI drivers are assumed to comply with the ANSI SCSI-II standard. In addition, NT supplies an OS-specific SCSI port driver, implemented as a dynamic-link library, and several device-type-specific class drivers, together with defined interfaces to HBA-specific SCSI miniport drivers and additional class drivers.

ScsiPort routines

An interface to HBA-specific miniport drivers exported by the system-supplied SCSI port driver. SCSI miniport drivers call these routines to obtain all OS-specific, system support they need to carry out I/O operations.

Drivers of nonSCSI mass-storage devices, implemented as a pseudoSCSI miniport, also can use this interface.

section object

A user-mode object type, representing a memory entity that can be mapped onto a range of virtual addresses in a process’s address space. Note that creating or opening a section object does not allocate physical memory. See also view.

An opened section object can be either:

· File-backed - The original entity resides as a file on a disk.

· Page-backed - The original entity resides either in a paging file controlled by the Memory Manager or (wholly or partially) in physical memory where storage is page-granular and the page size is microprocessor-dependent.

Unnamed sections are not sharable, but a view into an unnamed section can be mapped through the creating process’s handle. Named sections are shareable. All sections are inheritable by child processes.

security descriptor

A data structure used to hold per-object security information, including the object’s owner, group, protection attributes, and audit information. See also ACE, ACL, SACL, and SID.

security violation

A user-mode (protected subsystem) attempt to access an object by passing its handle to a system service without having the correct, granted access rights for the requested operation. See also ACE and ACL.

Note that the Security component does not check the access rights on object handles used by kernel-mode code. However, the I/O Manager and network server driver can force a security access check on object handles.

SEH

Structured exception handling

A feature of the NT system, which supports control transfers to exception handlers when certain runtime exceptions occur. The system traps the following general kinds of exceptions:

1. Hardware-defined faults or traps, such as,

· Access violations (see also access violation)

· Data-type misalignments (such as a 16-bit entity aligned on an
odd-byte boundary)

· Illegal and privileged instructions

· Invalid lock sequences (attempting to execute an invalid sequence of
instructions within an interlocked section of code)

· Integer divides by zero and overflows

· Floating-point divides by zero, overflows, underflows, and reserved
operands

· Breakpoints and single steps (to support debuggers)

2. System software-defined exceptions, such as,

· Guard-page violations (attempting to load or store data from or to a
location within a guard page)

· Page read errors (attempting to read a page into memory and
encountering a concurrent I/O error)

· Paging file quota exceeded (attempting to commit backing store for a
page currently being removed from a process’s working set).

NT supplies standard exception handlers for data-type alignment faults and for ANSI/IEEE Std 754-1985 floating-point faults.

Both user-mode and kernel-mode code can set up custom exception handlers using the except, try, and finally constructs supplied by system-compatible C compilers and supported by the Rtl Xxx (Runtime Library). An exception handler can be associated with each call frame in the function-call hierarchy of a program. A CONTEXT record contains the register state for each exception handler. See also Rtl routines.

semaphore object

A user-mode semaphore gates access to resources, allowing some maximum number of threads, as specified when the semaphore object was created, to access the resources protected by the semaphore.

Each user-mode semaphore also has an associated current count, representing how many additional threads can acquire the semaphore. When the current count is zero, a thread attempting to acquire the semaphore waits until the count is incremented when another thread releases that semaphore. Every user-mode semaphore object is implemented through the use of a kernel-mode semaphore object.

A kernel-mode semaphore is an instance of a Kernel-defined dispatcher object type.

Any semaphore object has either of two uses:

· A counting semaphore gates access to a resource by allowing some number of threads up to a specified limit to access the resource while the semaphore is set to the Signaled state (semaphore count is nonzero).

· A binary semaphore gates access to a single resource, if and only if the limit is set to one and it is not possible for the semaphore to be over-Signaled (set to the Signaled state when it is already in the Signaled state). A binary semaphore gates exclusive access to a resource.

However, using a semaphore with a limit of one to gate access is not the same thing as using a mutex: semaphores have no ownership, deadlocks are possible, and the owner’s priority is unaffected.

server

(1) A process with one or more threads that accept requests from client processes. See also client/server model.

(2) Any file system driver that provides remote access to files, named pipes, comm devices, and/or print devices.

Session Manager

An integral subsystem that starts and keeps track of NT logon sessions and serves as an intermediary between protected subsystems.

SFD

SCSI filter driver

An add-on driver that is layered above an existing SCSI class driver. An SFD intercepts requests for a particular SCSI peripheral device that has special features (not shared by other SCSI devices of its class), or that does not accept standard SCSI-II CDBs, in order to set up device-specific requests.

SID

Security identifier

A value, unique across time and space, that identifies a process in the Security system. SIDs can either identify an individual process, usually containing a user’s logon identifier, or a group of processes.

Signaled

One of two possible states for Kernel-defined dispatcher objects, which support synchronization. When the Kernel sets such an object to the Signaled state, any threads waiting on the object are released from their wait and become eligible for execution.

single-byte coding scheme

A character encoding scheme, such as ASCII, that uses a byte to represent each character. See also Unicode.

SMP machine

Symmetric multiprocessor machine

See multiprocessor machine.

spin lock

A spin lock is a low-level, synchronization mechanism defined by the NT Kernel. An NT driver or any other kernel-mode component that runs at raised IRQL can use a spin lock to synchronize access to a shared resource, particularly in a multiprocessor machine.

When one routine holds a spin lock, a second routine running on another processor cannot access the resource protected by that spin lock until it acquires the lock. While a routine attempts to acquire a spin lock, it cannot carry out other operations (it “spins”) until the lock has been acquired.

All operations involving system-defined spin locks are IRQL-specific to help prevent deadlocks. An interrupt spin lock can only be acquired while executing at a DIRQL of the associated interrupt object(s). An executive spin lock can only be acquired while executing at the highest IRQL (usually DISPATCH_LEVEL) of any routine that can acquire that executive spin lock.

SRB

SCSI request block

An NT-defined structure, used to communicate I/O requests from a SCSI class driver to the NT port driver, which relays such requests to appropriate HBA-specific miniport drivers. Pseudominiport drivers of nonSCSI mass-storage devices, which link themselves with the NT SCSI port driver and rely on a SCSI class driver for the same type as their device(s), translate the information in SRBs into device-specific commands.

stream file object

A virtual file representing on-disk data associated with a file, some of which might not be part of the physical file that backs a file object. For example, a stream file object makes it possible to cache the EAs or ACL for a file object together with the file’s data. FS-created stream file objects also make it possible to cache a copy of the on-media metadata describing the contents of each FS-mounted volume. See also volume file.

stripe driver

(a.k.a. “RAID driver”)

An intermediate driver whose job is to get better performance for file accesses than the underlying disk driver can. A stripe driver is layered somewhere between the FSD and a disk device driver. It distributes pieces of each file over a set of partitions concurrently, thereby cutting down on synchronous read/write time for the file.

The system-supplied fault tolerant disk driver, ftdisk, is a (RAID5) stripe driver.

subject

The combination of a (security) token and of an associated program that may use system services. See also token.

supervisor mode

See kernel mode.

SVGA

Super VGA

A video adapter with special features, such as support for high-resolution modes or additional colors, that is also VGA-compatible.

symbolic link

(1) An instance of the symbolic link object type, representing a “soft alias” that equates one name to another within the NT Object Manager’s name space.

(2) A file object with special properties. When a symbolic link file is encountered as a component of a pathname, rather than opening the file itself, the file system is redirected to a target file.

Note that the I/O Manager does not actually use a symbolic link object to implement symbolic link files; it uses a file object.

symbolic link object

An instance of a type defined by the Object Manager, used to translate a reference to one named object into a different name.

system paging file

A system file containing the contents of paged pool allocations that are currently paged out to backing store. See also pool memory and paging file.

system services

The set of native, user-mode routines exported by the executive for use only by Windows NT protected subsystems. Each system service has a name of the form TwoLettersXxxYyy where:

· TwoLetters is the prefix for all system services.

· Xxx is usually a verb, describing the operation of a given service.

· Yyy is generally the object type the service operates on.

See also Zw routines.

system space

See kernel space.

system worker threads

A set of kernel-mode threads provided by the NT Executive Support component that NT drivers, particularly FSDs, use to get work done within a nonarbitrary thread/process context. Such a driver sets up a work queue item with a driver-supplied callback routine that is queued to be run when a system worker thread is dispatched for execution by the Kernel. See also FSP and thread object.


T

tape miniclass driver

A SCSI-device-specific driver linked with the system-supplied SCSI tape class driver. If a tape miniclass driver calls only routines in the tape class driver, the miniclass driver can be portable across Microsoft operating systems that support Win32 applications.

TB

Translation buffer

See TLB.

TEB

Thread environment block

thread object

A user-mode thread object represents a path of execution within the current process. Every user-mode thread object is implemented through the use of an embedded kernel-mode thread object.

A kernel-mode thread object is an instance of a Kernel-defined dispatcher object type. The thread that it represents is the basic schedulable entity in NT.

A thread object:

· Is dispatched for execution by the Kernel

· Has the following properties at any given moment:

Dispatch state

Priority

Context

Execution mode (kernel or user)

Affinity

· Is “owned by” a process object but can attach itself to another process’s address space

Usually, most NT drivers execute in the context of the currently running thread, that is, in an arbitrary thread context. While a file system driver can create an independent process for its own device-dedicated threads, NT file systems usually avoid setting up a driver-created process and threads in order to conserve system memory and to avoid the overhead of context switches.

FSs (and other NT drivers) can set up device-dedicated (system-process) threads and/or FSs can use system worker threads if they need a driver-specific thread context in which to execute. NT drivers use kernel-mode Ps (Process Structure) routines to create processes and/or device-dedicated threads. FSs call routines supplied by the NT Executive Support component to use system worker threads.

TID

Target identifier

One of up to eight target controllers on a SCSI-II bus through which peripheral devices are addressable, either as numbered TIDs or as logical units (LUs) subordinate to a particular TID.

timer object

A timer object is an instance of a Kernel-defined dispatcher object type. User-mode timer objects can be used to synchronize the execution of specific actions, optionally an APC, with time. Kernel-mode timer objects are used to synchronize the execution of specific actions, in particular a DPC, with time.

When set to a specified expiration time:

· The timer’s state is reset to Not-Signaled.

· The timer is placed in a queue that is ordered according to expiration time.

When its expiration time arrives a timer is set to the Signaled state. When a notification timer is Signaled, all waiting threads have their wait satisfied and the state of the timer remains Signaled until it is explicitly reset. When a synchronization timer expires, its state is set to Signaled until a single waiting thread is released and then the timer is reset to the Not-Signaled state.

A timer can expire just once, or can be set to expire repeatedly at a given interval (a periodic timer).

The I/O Manager also supplies timer functions for drivers to use.

TLB

Translation lookaside buffer

(Not directly accessible by system software for the i386; otherwise, an on-chip virtual-to-physical-page address translation cache for high-end x86 and MIPS microprocessors.)

toaster device

slang: A SCSI peripheral device of an unknown class.

token object

A security object type, representing an authenticated user process. Every process has an assigned token, which becomes the default token for each of that process’s threads. However, an individual thread can be assigned a token that overrides this default.

trap handler

A body of code in the Kernel to which the processor transfers control when an interrupt or exception occurs. The trap handler determines the type of condition that caused the interrupt or exception and transfers control to an ISR or exception handler.


U

Unicode

A standard, fixed-width, 16-bit character encoding scheme that the system uses to provide NLS support for locale-specific natural languages. See also NLS.

user mode

The nonprivileged processor mode in which application code, including protected subsystem code, executes. User-mode applications cannot gain access to system data except by calling subsystem-supplied functions, which, in turn, call system services. See also kernel mode.

user space

A block of virtual memory, dedicated to the use of the current user-mode process. Generally, the NT user space is a range of low-order virtual addresses around two gigabytes in size. See also hyperspace, kernel space, section object, and view.


V

valid page

A virtual page that is currently in physical memory. See also invalid page.

value entry

A named value with assigned data within a registry key. See also registry and key object.

VBN

Virtual block number

A virtual block number identifies a block (a.k.a. “sector”) relative to the start of a file. For a file with N blocks of data, the corresponding VBNs are numbered 1 through N.

VCB

Volume control block

An internal NT file system structure in which a file system maintains state about a mounted volume.

VDM

Virtual DOS machines

A protected subsystem that emulates MS-DOS and DOS-based Windows within Windows NT.

VideoPort routines

An interface to adapter-specific miniport drivers exported by the system-supplied video port driver. Video miniport drivers call these routines to obtain all system support they need to carry out I/O operations.

view

A whole or partial mapping of a section object in the virtual address space of a process. Note that mapping a view of a section that is backed by an executable image file, in effect, “loads” the image. Except during the system boot process, NT uses memory-mapped I/O, rather than a loader, to start program execution.

virtual memory

A view of memory that does not necessarily correspond to the underlying physical memory structure. For example, a given range of virtual addresses might be mapped to and backed by some number of discontiguous physical pages, even though the corresponding virtual pages can be accessed as a single, contiguous range.

VMCB

Volume map control block

An opaque structure that stores VBN-to-LBN mappings for an IFS’s volume file.

volume file

A virtual file, maintained by certain NT file systems, whose contents map metadata structures of the on-disk file system. A volume file is a type of stream file.

VPB

Volume parameter block

A VPB is a structure, defined by the I/O Manager, that maps a file system’s volume device object to the device or partition upon which the volume is mounted. The file system’s device object is actually used to represent the volume (VPB) mounted on the actual device (physical device object). Device objects for physical disks, tapes, CD ROMs, and RAM disks have associated VPBs. See also mount.

VRP

Video request packet

A structure used to communicate device I/O control requests from a display driver to its corresponding adapter-specific miniport driver. For example, when a display driver calls EngDeviceIoControl, this function calls a system service and the NT I/O Manager sets up an IRP and calls the NT-supplied video port driver with that IRP. The video port driver uses the IRP to set up a VRP and calls the corresponding video miniport driver’s StartIo entry point with the VRP.


W

window turn

Turning a window refers to the process of filling an MCB with the appropriate retrieval pointers in order to map a VBN for a file to an LBN for a disk.

working set

The set of pages that are resident for a given process while it has one or more running threads. See also balance set and quota.

The size of each process’s working set is bounded by a minimum number of pages that the NT Memory Manager guarantees to be resident while the process has one running thread and by a maximum. However, NT does not necessarily constrain a process’s working set to its maximum if many free pages are available.

WORM

Write-once, read many

WSL

Working set list

The set of WSLEs for the current process.

WSLE

Working set list entry


Z

zone

A block of storage from which entries of a specified, fixed size can be allocated and deallocated quickly. Callers of the Ex(ecutive) Support zone routines must synchronize their allocation and deallocation of entries within their zones.

For example, the I/O Manager uses zones to implement lookaside lists for fast allocation and deallocation of IRPs and MDLs. As another example, some of the system-supplied SCSI class drivers use zones to allocate and release memory for SRBs.

Zw routines

A set of entry points parallel to the executive’s system services. A call to a Zw Xxx entry point from kernel-mode code (including calls from other system services or NT drivers) supplies the corresponding system service, except the caller’s access rights and the arguments to the Zw “alias” are not checked for validity, and the call does not cause the previous mode to be set to user mode. It is the responsibility of a kernel-mode caller to check all user-supplied arguments for validity before such a caller passes them on to a Zw Xxx entry point. See also system services, probe, and SEH.


Chapter 1 Summary of Kernel-mode Support Routines

This chapter summarizes the kernel-mode support routines that Microsoft® Windows NT® kernel-mode drivers can call, according to the following general categories:

  • Initialization and Unload in Section 1.1

  • IRPs in Section 1.2

  • Synchronization in Section 1.3

  • Memory in Section 1.4

  • DMA in Section 1.5

  • PIO in Section 1.6

  • Driver-managed queues in Section 1.7

  • Driver-dedicated system threads and system worker threads in Section 1.8

  • Strings in Section 1.9

  • Data Conversions in Section 1.10

  • Access to and access rights on driver-managed objects in Section 1.11

  • Handling errors in Section 1.12

Some routines are listed in more than one section or subsection of this chapter.


1.1 Initialization and Unload

This section summarizes kernel-mode support routines that NT drivers can call from their DriverEntry, Reinitialize, or Unload routines, according to the following general categories:

  • Support routines that device drivers can call to get hardware configuration information about their devices and the current platform in Section 1.1.1

  • Support routines that drivers can call to get configuration information from, claim hardware resources in, and set configuration information in the registry in Section 1.1.2

  • Support routines that drivers can call to set up certain standard driver routines in Section 1.1.3

  • Support routines that drivers can call to set up and free the NT objects and resources they might use in Section 1.1.4

  • Support routines that drivers might call to initialize driver-managed internal queues in Section 1.1.5


1.1.1 Hardware Configuration

HalGetBusData
Returns bus-relative configuration information for the specified type of I/O bus, system-assigned bus number, slot number or device location, and a pointer to a driver-supplied buffer and the buffer’s length in bytes.
HalGetBusDataByOffset
Like HalGetBusData, returns bus-relative configuration information to a driver-supplied buffer. However, this routine returns configuration information for a given type of I/O bus, starting at the given offset in the bus-type-specific configuration data structure if the bus is dynamically configurable with a published standard interface.
IoAssignResources
Takes an input list of preferred and alternative, or required, hardware resources needed for a driver or device, inspects the registry for available hardware resources, claims hardware resources in the registry ResourceMap tree, and returns configuration information about the hardware resources it claimed for the caller.
HalAssignSlotResources
Like IoAssignResources, takes an input list of preferred and alternative hardware resources and claims available hardware resources in the registry ResourceMap tree, returning information about the resources it claimed to the caller. However, this routine claims hardware resources only for drivers of devices on any given type of dynamically configurable I/O bus with a published standard interface.
HalSetBusData
Sets I/O bus configuration data fora given slot or address on a particular bus.
HalSetBusDataByOffset
Sets I/O bus configuration data fora given slot or address on a particular bus, starting at the given offset in the bus-type-specific configuration structure.
IoQueryDeviceDescription
Supplies basic hardware information about one or more of the given bus type and bus number, controller type and controller number, and/or peripheral type and peripheral number from the registry to a driver-supplied configuration-callback routine. As an alternative, device drivers can call HalGetBusData or HalGetBusDataByOffset.
IoReportResourceUsage
Claims hardware resources, such as ports in I/O space or device registers in memory space, an interrupt vector, or a particular DMA controller channel, in the registry so that a subsequently loaded driver cannot attempt to use the same resources. To claim most types of resources, calling IoAssignResources or HalAssignSlotResources is preferable.
IoGetConfigurationInformation
Returns a pointer to the I/O Manager’s configuration information structure, which indicates the number of disk, floppy, CD-ROM, tape, SCSI HBAs, serial, and parallel device objects that have already been named by previously loaded drivers, as well as whether certain address ranges have been claimed by “AT” disk-type drivers.
HalTranslateBusAddress
Returns the corresponding physical address in system memory for a given bus-specific logical address.
HalGetInterruptVector
Returns a mapped system interrupt vector, DIRQL, and processor-affinity mask a driver can use to set up its interrupt service routine (ISR), given the type of bus on which the driver’s device is connected, the system-assigned bus number, and the bus’s interrupt vector and IRQL.
HalGetAdapter
Returns a pointer to the adapter object representing the DMA channel to which the driver’s device is connected or representing the driver’s busmaster adapter, together with the maximum number of map registers the driver can specify for each DMA transfer, given the input device description.
HalExamineMBR
Returns data from the master boot record (MBR) of a disk.
KeGetDcacheFillSize
Returns the processor’s data cache-line boundary.
IoReadPartitionTable
Returns a list of partitions on a disk with a given sector size.


1.1.2 Registry

IoAssignResources
Takes an input list of preferred and alternative, or required, hardware resources needed for a driver or device, inspects the registry for available hardware resources, claims hardware resources in the registry ResourceMap tree, and returns configuration information about the hardware resources it claimed for the caller.
HalAssignSlotResources
Like IoAssignResources, takes an input list of preferred and alternative hardware resources and claims available hardware resources in the registry ResourceMap tree, returning information about the resources it claimed to the caller. However, this routine claims hardware resources only for drivers of devices on any given type of dynamically configurable I/O bus with a published standard interface.
IoQueryDeviceDescription
Supplies basic hardware information about one or more of the given bus type and bus number, controller type and controller number, and/or peripheral type and peripheral number from the registry to a driver-supplied configuration-callback routine.
IoReportResourceUsage
Claims hardware resources, such as ports in I/O space or device registers in memory space, an interrupt vector, or a particular DMA controller channel, in the registry so that a subsequently loaded driver cannot attempt to use the same resources. To claim most types of resources, calling IoAssignResources or HalAssignSlotResources is preferable.
RtlCheckRegistryKey
Returns STATUS_SUCCESS if a key exists in the registry along the given relative path.
RtlCreateRegistryKey
Adds a key object in the registry along the given relative path.
RtlQueryRegistryValues
Gives the driver-supplied QueryRegistry callback (read only) access to the entries for the specified value name along the specified relative path in the registry when the QueryRegistry routine is given control.
RtlWriteRegistryValue
Writes caller-supplied data into the registry along the specified relative path at the given value name.
RtlDeleteRegistryValue
Removes the specified value name (and the associated value entries) from the registry along the given relative path.
InitializeObjectAttributes
Sets up a parameter of type OBJECT_ATTRIBUTES for a subsequent call to a ZwCreate Xxx or ZwOpen Xxx routine.
ZwCreateKey
Creates a new key in the registry with the given object attributes, allowed access, and creation options (such as whether the key is re-created when the system is booted), or opens an existing key, and returns a handle for the key object.
ZwOpenKey
Returns a handle for a key in the registry, given the object’s attributes (which must include a name for the key) and the desired access to the object.
ZwQueryKey
Returns information about the class of a key, and the number and sizes of its subkeys, such as the length of subkey names and the size of value entries.
ZwEnumerateKey
Returns the specified information about the subkeys of an opened key in the registry.
ZwEnumerateValueKey
Returns the specified information about the value entry, as selected by a zero-based index, of an opened key in the registry.
ZwQueryValueKey
Returns the value entry, as selected by a zero-based index, for an opened key in the registry.
ZwSetValueKey
Replaces (or creates) a value entry for an opened key in the registry.
ZwFlushKey
Forces changes made by ZwCreateKey or ZwSetValueKey for the opened key object to be written to disk.
ZwDeleteKey
Removes a key and its value entries from the registry as soon as the key is closed.
ZwClose
Releases the handle for an opened object, causing the handle to become invalid and decrementing the reference count of the object handle.


1.1.3 Standard Driver Routines

IoRegisterDriverReinitialization
Sets up a driver-supplied Reinitialize routine, together with its context, so the Reinitialize routine will be called when every subsequently loaded driver’s DriverEntry routine has returned control.
IoRegisterShutdownNotification
Sets up a (nonmass-storage) driver’s DispatchShutdown routine to be called when the system is being shut down.
IoUnregisterShutdownNotification
Removes a (nonmass-storage) driver’s DispatchShutdown routine from the list of those to be called when the system is being shut down.
HalGetInterruptVector
Returns a mapped system interrupt vector, DIRQL, and processor-affinity mask a driver can use to set up its ISR, given the type of bus on which the driver’s device is connected, the system-assigned bus number, and the bus’s interrupt vector and IRQL.
IoConnectInterrupt
Registers an ISR and sets up interrupt objects, using values supplied by HalGetInterruptVector. Returns a pointer to a set of interrupt objects that must be passed, along with the driver’s SynchCritSection entry point, to KeSynchronizeExecution.
IoDisconnectInterrupt
Releases a driver’s interrupt objects.
IoInitializeDpcRequest
Associates a driver-supplied DpcForIsr routine with a given device object, so the DpcForIsr can complete interrupt-driven I/O operations.
KeInitializeDpc
Initializes a DPC object, setting up a driver-supplied CustomDpc routine that can be called with a given context.
KeInitializeTimer
Initializes a notification timer object to the Not-Signaled state.
KeInitializeTimerEx
Initializes a notification or synchronization timer object to the Not-Signaled state.
IoInitializeTimer
Associates a timer with the given device object and registers a driver-supplied IoTimer routine for the device object.
MmLockPagableCodeSection
Locks a set of driver routines marked with a special compiler directive into system space, sometimes during driver initialization but usually in its DispatchCreate routine.
MmLockPagableDataSection
Locks a named data section marked with a special compiler directive into system space, when that data is used infrequently, predictably and at an IRQL less than DISPATCH_LEVEL.
MmLockPagableSectionByHandle
Locks a pageable section into system memory using a handle returned from MmLockPagableCodeSection or MmLockPagableDataSection.
MmUnlockPagableImageSection
Releases a set of driver routines or a set of data that were locked into nonpaged system space when the driver is no longer processing IRPs.
MmPageEntireDriver
MmPageEntireDriver allows a driver to page out all of its code and data regardless of the attributes of the various sections in the driver’s image.
MmResetDriverPaging
MmResetDriverPaging resets a driver’s pageable status to that specified by the sections which make up the driver’s image.


1.1.4 Objects and Resources

IoCreateDevice
Initializes a device object, representing a physical, virtual, or logical device for which the driver is being loaded into the system, and allocates space for the driver-defined device extension associated with the device object. This routine also initializes a device object representing a logical or virtual device if the driver can reconfigure its devices dynamically, as, for example, when a disk is repartitioned dynamically.
IoDeleteDevice
Removes a device object from the system when a driver is being unloaded or when its logical or virtual devices are being reconfigured dynamically.
IoGetDeviceObjectPointer
Returns a pointer to a device object with a given name if the caller’s desired access to the object can be granted; also returns a pointer to the file object referenced by the named device object. In effect, this routine establishes a connection between the caller and the next-lower-level driver. A successful caller can use the returned device object pointer to initialize its own device object(s) and as an argument to IoBuildAsynchronousFsdRequest, IoBuildSynchronousFsdRequest, IoBuildDeviceIoControlRequest, IoAttachDeviceToDeviceStack, and IoCallDriver.
IoAttachDevice
“Attaches” a given source device object (belonging to the caller) to a named target device object (usually belonging to the next-lower-level-driver), so that I/O requests bound for the target device are routed first to the caller.
IoAttachDeviceToDeviceStack
“Attaches” the caller’s device object to the highest device object in a chain of drivers and returns a pointer to the previously highest device object. I/O requests bound for the target device are routed first to the caller.
IoDetachDevice
Releases an attachment between the caller’s device object and a target driver’s device object.
IoCreateSymbolicLink
Sets up a symbolic link object, aliasing a named device object to a user-visible name for the same device.
IoCreateUnprotectedSymbolicLink
Sets up a symbolic link object, aliasing a named device object to a user-visible name for the same device and allowing user-mode callers to affect the mode of the device (for example, parallel and serial drivers call this routine so users can redirect output).
IoDeleteSymbolicLink
Releases a symbolic link between a device object name and a user-visible name.
IoAssignArcName
Sets up a symbolic link between a named device object (such as a tape, floppy, or CD-ROM) and the corresponding ARC name for the device.
IoDeassignArcName
Releases the symbolic link created by calling IoAssignArcName.
IoSetShareAccess
Sets the access allowed to a given file object representing a device. (Only highest-level drivers can call this routine.)
IoConnectInterrupt
Registers a driver’s ISR), according to the parameters obtained by calling HalGetInterruptVector. Returns a pointer to a set of allocated, initialized, and connected interrupt objects that is used as an argument to KeSynchronizeExecution.
IoDisconnectInterrupt
Releases a driver’s interrupt object(s) when the driver unloads.
IoReadPartitionTable
Returns a list of partitions on a disk with a given sector size.
IoSetPartitionInformation
Sets the partition type and number for a (disk) partition.
IoWritePartitionTable
Writes partition tables for a disk, given the device object representing the disk, the sector size, and a pointer to a buffer containing the drive geometry.
IoCreateController
Initializes a controller object, representing a physical device controller shared by two or more like devices with the same driver, and specifies the size of the controller extension.
IoDeleteController
Removes a controller object from the system.
KeInitializeSpinLock
Initializes a variable of type KSPIN_LOCK.
KeInitializeDpc
Initializes a DPC object, setting up a driver-supplied CustomDpc routine that can be called with a given context.
KeInitializeTimer
Initializes a notification timer object to the Not-Signaled state.
KeInitializeTimerEx
Initializes a notification or synchronization timer object to the Not-Signaled state.
KeInitializeEvent
Initializes an event object as a synchronization (single waiter) or notification (multiple waiters) type event and sets up its initial state (Signaled or Not-Signaled).
ExInitializeFastMutex
Initializes a fast mutex variable, used to synchronize mutually exclusive access to a shared resource by a set of threads.
KeInitializeMutex
Initializes a mutex object at a given level number as set to the Signaled state.
KeInitializeSemaphore
Initializes a semaphore object to a given count and specifies an upper bound the count can attain.
IoCreateNotificationEvent
Initializes a named notification event to be used to synchronize access between two or more components. Notification events are not auto-resetting.
IoCreateSynchronizationEvent
Initializes a named synchronization event to be used to serialize access to hardware between two otherwise unrelated drivers.
PsCreateSystemThread
Creates a kernel-mode thread associated with a given process object or with the default system process; returns a handle for the thread.
PsTerminateSystemThread
Terminates the current thread and satisfies as many waits as possible for the current thread object.
KeSetBasePriorityThread
Sets up the runtime priority, relative to the system process, for a driver-created thread.
KeSetPriorityThread
Sets up the runtime priority for a driver-created thread with a real-time priority attribute.
MmIsThisAnNtAsSystem
Returns TRUE if the current platform is a server, indicating that more resources are likely to be necessary to process I/O requests than if the machine is a client.
MmQuerySystemSize
Returns whether a small, medium, or large amount of memory is available on the current platform.
ExInitializeNPagedLookasideList
Initializes a lookaside list of nonpaged memory. After a successful initialization, fixed-size blocks can be allocated from and freed to the lookaside list.
ExInitializePagedLookasideList
Initializes a lookaside list of paged memory. After a successful initialization, fixed-size blocks can be allocated from and freed to the lookaside list.
ExInitializeZone
Initializes a zone header for a caller-allocated zone from which fixed-size entries can be allocated and to which entries can be released.
ExInitializeResourceLite
Initializes a resource, for which the caller provides the storage, to be used for synchronization by a set of threads.
ExReinitializeResourceLite
Reinitializes an existing resource variable.
ExDeleteResourceLite
Deletes a caller-initialized resource from the system’s resource list.
ObReferenceObjectByHandle
Returns a pointer to the object body and handle information (attributes and granted access rights), given the handle for an object, the object’s type, and a mask, specifying the desired access to the object, and the access mode (user or kernel, for which access is always granted). A successful call increments the reference count for the object.
ObReferenceObjectByPointer
Increments the reference count for a given object so the caller can ensure that the object is not removed from the system while the caller is using it.
ObDereferenceObject
Releases a reference to an object (decrements the reference count), given a pointer to the object body.
RtlInitString
Sets up the specified string in a buffer.
RtlInitAnsiString
Sets up the specified ANSI string in a buffer.
RtlInitUnicodeString
Sets up the specified Unicode string in a buffer.
InitializeObjectAttributes
Sets up a parameter of type OBJECT_ATTRIBUTES for a subsequent call to a ZwCreate Xxx or ZwOpen Xxx routine.
ZwCreateDirectoryObject
Creates a directory object with the specified object attributes and allowed access; returns a handle for the directory object.
ZwCreateFile
Creates or opens a file object representing a physical, logical, or virtual device, a directory, a data file, or a volume; returns a handle for the file object.
ZwCreateKey
Creates or opens a key object in the registry; returns a handle for the key object.
ZwDeleteKey
Deletes an existing, open key in the registry when the last handle for the key is closed.
ZwMakeTemporaryObject
Resets an opened object’s “permanent” attribute, so that the object and its name can be deleted when the reference count for the object becomes zero.
ZwClose
Releases the handle for an opened object, causing the handle to become invalid and decrementing the reference count of the object handle.


1.1.5 Initializing Driver-Managed Queues

KeInitializeSpinLock
Initializes a variable of type KSPIN_LOCK. An initialized spin lock is a required parameter to the Ex..InterlockedList routines.
InitializeListHead
Sets up a queue header for a driver’s internal queue, given a pointer to driver-supplied storage for the queue header and queue.
KeInitializeDeviceQueue
Initializes a device queue object to a not-busy state, setting up an associated spin lock for multiprocessor-safe access to device queue entries.


1.2 IRPs

This section summarizes kernel-mode support routines that NT drivers can call, according to the following general categories:

  • Support routines that drivers can call while processing IRPs in Section 1.2.1

  • Support routines that higher-level NT drivers can call to allocate and set up IRPs for requests to lower drivers in Section 1.2.2

  • Support routines that drivers can call to use file objects in Section 1.2.3


1.2.1 Processing IRPs

IoGetCurrentIrpStackLocation
Returns a pointer to the caller’s I/O stack location in a given IRP.
IoGetNextIrpStackLocation
Returns a pointer to the next-lower-level driver’s I/O stack location in a given IRP.
IoGetRelatedDeviceObject
Returns a pointer to the device object represented by a given file object.
IoGetFunctionCodeFromCtlCode
Returns the value of the function field within a given IOCTL_XXX or FSCTL_XXX.
IoSetCompletionRoutine
Registers a driver-supplied IoCompletion routine with a given IRP, so the IoCompletion routine is called when the next-lower-level driver has completed the requested operation in one or more of the following ways: successfully, with an error, and/or by cancelling the IRP.
IoCallDriver
Sends an IRP to a lower-level driver.
IoMarkIrpPending
Marks a given IRP indicating that STATUS_PENDING was returned because further processing is required by another driver routine or by a lower-level driver.
IoStartPacket
Calls the driver’s StartIo routine with the given IRP for the given device object or inserts the IRP into the device queue if the device is already busy, specifying whether the IRP is cancelable.
IoAcquireCancelSpinLock
Synchronizes cancelable-state transitions for IRPs in a multiprocessor-safe manner.
IoSetCancelRoutine
Sets or clears the Cancel routine in an IRP. Setting a Cancel routine makes an IRP cancelable.
IoReleaseCancelSpinLock
Releases the cancel spin lock when the driver has changed the cancelable state of an IRP or releases the cancel spin lock from the driver’s Cancel routine.
IoCancelIrp
Marks an IRP as cancelled.
IoReadPartitionTable
Returns a list of partitions on a disk with a given sector size.
IoSetPartitionInformation
Sets the partition type and number for a (disk) partition.
IoWritePartitionTable
Writes partition tables for a disk, given the device object representing the disk, the sector size, and a pointer to a buffer containing the drive geometry.
IoAllocateErrorLogEntry
Allocates and initializes an error log packet; returns a pointer so the caller can supply error-log data and call IoWriteErrorLogEntry with the packet.
IoWriteErrorLogEntry
Queues a previously allocated error log packet, filled in by the driver, to the system error logging thread.
IoIsErrorUserInduced
Returns a Boolean indicating whether an I/O request failed due to one of the following conditions: STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_UNRECOGNIZED_MEDIA, STATUS_VERIFY_REQUIRED, STATUS_WRONG_VOLUME, STATUS_MEDIA_WRITE_PROTECTED, or STATUS_NO_MEDIA_IN_DEVICE. If the result is TRUE, a removable-media driver must call IoSetHardErrorOrVerifyDevice before completing the IRP.
IoSetHardErrorOrVerifyDevice
Supplies the device object for which the given IRP was failed due to a user-induced error, such as supplying the incorrect media for the requested operation or changing the media before the requested operation was completed. A file system driver uses the associated device object to send a popup to the user, who can then correct the error and/or retry the operation.
IoGetDeviceToVerify
Returns a pointer to the device object, representing a removable-media device, that is the target of the given thread’s I/O request. (This routine is useful only to file systems or other highest-level drivers.)
IoRaiseHardError
Causes a popup to be sent to the user indicating that the given IRP was failed on the given device object for an optional VPB, so that the user can correct the error and/or can retry the operation.
IoRaiseInformationalHardError
Causes a popup to be sent to the user, showing an I/O error status and optional string supplying more information.
ExRaiseStatus
Raises an error status so that a caller-supplied structured exception handler is called. (This routine is useful only to highest-level drivers that supply exception handlers, in particular to file systems.)
IoStartNextPacket
Dequeues the next IRP for a given device object, specifying whether the IRP is cancelable, and calls the driver’s StartIo routine.
IoStartNextPacketByKey
Dequeues the next IRP, according to the specified sort-key value, for a given device object, specifies whether the IRP is cancelable, and calls the driver’s StartIo routine.
IoCompleteRequest
Completes an I/O request, giving a priority boost to the original caller and returning a given IRP to the I/O system for disposal: either to call any IoCompletion routines supplied by higher-level drivers, or to return status to the original requestor of the operation.
IoGetCurrentProcess
Returns a pointer to the current process. (This routine is useful only to highest-level drivers.)
IoGetInitialStack
Returns the initial base address of the current thread’s stack. (This routine is useful only to highest-level drivers.)


1.2.2 Driver-Allocated IRPs

IoBuildAsynchronousFsdRequest
Allocates and sets up an IRP specifying a major function code (IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_SHUTDOWN, or IRP_MJ_FLUSH_BUFFERS) with a pointer to the lower driver’s device object on which the I/O should occur, a pointer to a buffer which will contain the data to be read or which contains the data to be written, the length of the buffer in bytes, a pointer to the starting offset on the media, and a pointer to the I/O status block where the called driver can return status information and the caller’s IoCompletion routine can access it. Returns a pointer to the IRP so the caller can set any necessary minor function code and set up its IoCompletion routine before sending the IRP to the target driver.
IoBuildSynchronousFsdRequest
Allocates and sets up an IRP specifying a major function code (IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_SHUTDOWN, or IRP_MJ_FLUSH_BUFFERS) with a pointer to the lower driver’s device object on which the I/O should occur, a pointer to a buffer which will contain the data to be read or which contains the data to be written, the length of the buffer in bytes, a pointer to the starting offset on the media, a pointer to an event object to be set to the Signaled state when the requested operation completes, and a pointer to the I/O status block where the called driver can return status information and the caller’s IoCompletion routine can access it. Returns a pointer to the IRP so the caller can set any necessary minor function code and set up its IoCompletion routine before sending the IRP to the target driver.
IoBuildDeviceIoControlRequest
Allocates and sets up an IRP specifying a major function code (either IRP_MJ_INTERNAL_DEVICE_CONTROL or IRP_MJ_DEVICE_CONTROL) with an optional input or output buffer, a pointer to the lower driver’s device object, an event to be set to the Signaled state when the requested operation completes, and an I/O status block to be set by the to-be-called driver. Returns a pointer the the IRP so the caller can set the appropriate IOCTL_XXX before sending the IRP to the next-lower-level driver.
IoSizeOfIrp
Returns the size in bytes required for an IRP with a given count of I/O stack locations.
IoAllocateIrp
Allocates an IRP, given the number of I/O stack locations (optionally, for the caller, but at least one for each driver layered under the caller) and whether to charge quota against the caller. Returns a pointer to an IRP in nonpaged system space if successful; otherwise, returns NULL.
IoInitializeIrp
Initializes an IRP, given a pointer to an already allocated IRP, its length in bytes, and the number of I/O stack locations it has.
IoSetNextIrpStackLocation
Sets the current IRP stack location to the caller’s in an IRP, which must have been allocated by a preceding call to IoAllocateIrp that specified a stack-size argument large enough to give the caller its own stack location.
IoAllocateMdl
Allocates a MDL large enough to map the starting address and length supplied by the caller; optionally associates the MDL with a given IRP.
IoBuildPartialMdl
Builds an MDL for the specified starting virtual address and length in bytes from a given source MDL. Drivers that split large transfer requests into a number of smaller transfers can call this routine.
IoFreeMdl
Releases a given MDL allocated by the caller.
IoMakeAssociatedIrp
Allocates and initializes an IRP to be associated with a master IRP sent to the highest-level driver, allowing the driver to “split” the original request and send associated IRPs on to lower-level drivers or to the device.
IoSetCompletionRoutine
Registers a driver-supplied IoCompletion routine with a given IRP, so the IoCompletion routine is called when lower-level drivers have completed the request, so the caller can release the IRP it allocated with IoAllocateIrp or IoBuildAsynchronousFsdRequest and any other resources it allocated to set up an IRP for lower drivers and/or perform any I/O completion processing necessary.
IoCallDriver
Sends an IRP to a lower-level driver.
IoFreeIrp
Releases an IRP that was allocated by the caller.


1.2.3 File Objects

InitializeObjectAttributes
Sets up a parameter of type OBJECT_ATTRIBUTES for a subsequent call to a ZwCreate Xxx or ZwOpen Xxx routine.
ZwCreateFile
Creates or opens a file object representing a physical, logical, or virtual device, a directory, a data file, or a volume.
ZwQueryInformationFile
Returns information about the state or attributes of an open file.
IoGetFileObjectGenericMapping
Returns information about the mapping between generic access rights and specific access rights for file objects.
ZwReadFile
Returns data from an open file.
ZwSetInformationFile
Changes information about the state or attributes of an open file.
ZwWriteFile
Transfers data to an open file.
ZwClose
Releases the handle for an opened object, causing the handle to become invalid and decrementing the reference count of the object handle.


1.3 Synchronization

This section summarizes kernel-mode support routines that NT drivers can call, according to the following general categories:

  • Support routines that drivers can call to synchronize the execution of their own standard driver routines in Section 1.3.1

  • Support routines that temporarily change the current IRQL for a call to a support routine or that return the current IRQL in Section 1.3.2

  • Support routines that drivers can call to synchronize access to resources with spin locks or to perform interlocked operations without spinlocks in Section 1.3.3

  • Support routines that drivers can call to manage time outs or determine system time in Section 1.3.4

  • Support routines that driver can call to use system threads or to manage synchronization within a nonarbitrary thread context in Section 1.3.5


1.3.1 Driver Routines and I/O Objects

KeSynchronizeExecution
Synchronizes the execution of a driver-supplied SynchCritSection routine with that of the ISR associated with a set of interrupt objects, given a pointer to the interrupt object(s).
IoRequestDpc
Queues a driver-supplied DpcForIsr routine to complete interrupt-driven I/O processing at a lower IRQL.
KeInsertQueueDpc
Queues a DPC to be executed as soon as the IRQL of a processor drops below DISPATCH_LEVEL; returns FALSE if the DPC object is already queued.
KeRemoveQueueDpc
Removes a given DPC object from the DPC queue; returns FALSE if the object was not in the queue.
IoAllocateAdapterChannel
Connects a device object to an adapter object and calls a driver-supplied AdapterControl routine to carry out an I/O operation through the system DMA controller or a busmaster adapter as soon as the appropriate DMA channel is available and/or any necessary map registers are available. (This routine reserves exclusive access to a DMA channel and/or map registers for the specified device.)
IoFreeAdapterChannel
Releases an adapter object, representing a system DMA channel, and optionally releases map registers, if any were allocated.
IoFreeMapRegisters
Releases a set of map registers, saved from a call to IoAllocateAdapterChannel, after the registers have been used by IoMapTransfer and the busmaster DMA transfer is complete.
IoAllocateController
Connects a device object to a controller object and calls a driver-supplied ControllerControl routine to carry out an I/O operation on the device controller as soon as the controller is not busy. (This routine reserves exclusive access to the hardware controller for the specified device.)
IoFreeController
Releases a controller object, provided that all device operations queued to the controller for the current IRP have completed.
IoStartTimer
Enables the timer for a given device object and calls the driver-supplied IoTimer routine once per second thereafter.
IoStopTimer
Disables the timer for a given device object so the driver-supplied IoTimer routine is not called unless the driver re-enables the timer.
KeSetTimer
Sets the absolute or relative interval at which a timer object will be set to the Signaled state and optionally supplies a timer DPC to be executed when the interval expires.
KeSetTimerEx
Sets the absolute or relative interval at which a timer object will be set to the Signaled state, optionally supplies a timer DPC to be executed when the interval expires, and optionally supplies a recurring interval for the timer.
KeCancelTimer
Cancels a timer object before the interval passed to KeSetTimer expires; dequeues a timer DPC before the timer interval, if any was set, expires.
KeReadStateTimer
Returns whether a given timer object is set to the Signaled state.
IoStartPacket
Calls the driver’s StartIo routine with the given IRP for the given device object or inserts the IRP into the device queue if the device is already busy, specifying whether the IRP is cancelable.
IoStartNextPacket
Dequeues the next IRP for a given device object, specifying whether the IRP is cancelable, and calls the driver’s StartIo routine.
IoStartNextPacketByKey
Dequeues the next IRP, according to the specified sort-key value, for a given device object, specifies whether the IRP is cancelable, and calls the driver’s StartIo routine.
IoSetCompletionRoutine
Registers a driver-supplied IoCompletion routine with a given IRP, so the IoCompletion routine is called when the next-lower-level driver has completed the requested operation in one or more of the following ways: successfully, with an error, and/or by cancelling the IRP.
IoSetCancelRoutine
Sets or clears the Cancel routine in an IRP. Setting a Cancel routine makes an IRP cancelable.
KeStallExecutionProcessor
Stalls the caller (a device driver) for a given interval on the current processor.
ExAcquireResourceExclusiveLite
Acquires an initialized resource for exclusive access by the calling thread and optionally waits for the resource to be acquired.
ExTryToAcquireResourceExclusiveLite
Either acquires a given resource for exclusive access immediately or returns FALSE.
ExAcquireResourceSharedLite
Acquires an initialized resource for shared access by the calling thread and optionally waits for the resource to be acquired.
ExAcquireSharedStarveExclusive
Acquires a given resource for shared access without waiting for any pending attempts to acquire exclusive access to the same resource.
ExAcquireSharedWaitForExclusive
Acquires a given resource for shared access, optionally waiting for any pending exclusive waiters to acquire and release the resource first.
ExReleaseResourceForThreadLite
Releases a given resource that was acquired by the given thread.
ZwReadFile
Reads data from an open file. If the caller opened the file object with certain parameters, the caller can wait on the file handle for completion of the I/O.
ZwWriteFile
Writes data to an open file. If the caller opened the file object with certain parameters, the caller can wait on the file handle for completion of the I/O.


1.3.2 IRQL

KeRaiseIrql
Raises the hardware priority to a given IRQL value, thereby masking off interrupts of equivalent or lower IRQL on the current processor.
KeLowerIrql
Restores the IRQL on the current processor to its original value.
KeGetCurrentIrql
Returns the current hardware priority IRQL value.


1.3.3 Spin Locks and Interlocks

IoAcquireCancelSpinLock
Synchronizes cancelable-state transitions for IRPs in a multiprocessor-safe manner.
IoSetCancelRoutine
Sets or clears the Cancel routine in an IRP during a cancelable state transition. Setting a Cancel routine makes an IRP cancelable.
IoReleaseCancelSpinLock
Releases the cancel spin lock when the driver has changed the cancelable state of an IRP or releases the cancel spin lock from the driver’s Cancel routine.
KeInitializeSpinLock
Initializes a variable of type KSPIN_LOCK, used to synchronize access to data shared among nonISR routines. An initialized spin lock also is a required parameter to the ExInterlocked Xxx routines.
KeAcquireSpinLock
Acquires a spin lock so the caller can synchronize access to shared data in a multiprocessor-safe way.
KeReleaseSpinLock
Releases a spin lock that was acquired by calling KeAcquireSpinLock and restores the original IRQL at which the caller was running.
KeAcquireSpinLockAtDpcLevel
Acquires a spin lock, provided that the caller is already running at IRQL DISPATCH_LEVEL.
KeReleaseSpinLockFromDpcLevel
Releases a spin lock that was acquired by calling KeAcquireSpinLockAtDpcLevel.
ExInterlocked..List
Insert and remove IRPs in a driver-managed internal queue, which is protected by an initialized spin lock for which the driver provides the storage.
ExInterlocked..Zone
Allocates and deallocates fixed-size entries, under spin lock control, from a driver-allocated and initialized zone buffer.
Ke..DeviceQueue
Insert and remove IRPs in a driver-allocated and managed internal device queue object, which is protected by a built-in spin lock.
ExInterlockedAddUlong
Adds a value to a variable of type ULONG as an atomic operation, using a spin lock to ensure multiprocessor-safe access to the variable; returns the original (unsummed) value of the variable.
ExInterlockedAddLargeInteger
Adds a value to a variable of type LARGE_INTEGER as an atomic operation, using a spin lock to ensure multiprocessor-safe access to the variable; returns the original (unsummed) value of the variable.
InterlockedIncrement
Increments a variable of type LONG as an atomic operation. The sign of the return value is the sign of the result of the operation.
InterlockedDecrement
Decrements a variable of type LONG as an atomic operation. The sign of the return value is the sign of the result of the operation.
InterlockedExchange
Sets a variable of type LONG to a specified value as an atomic operation; returns the original (unset) value of the variable.
KeGetCurrentProcessorNumber
Returns the current processor number when debugging spin lock usage in SMP machines.


1.3.4 Timers

IoInitializeTimer
Associates a timer with the given device object and registers a driver-supplied IoTimer routine for the device object.
IoStartTimer
Enables the timer for a given device object and calls the driver-supplied IoTimer routine once per second thereafter.
IoStopTimer
Disables the timer for a given device object so the driver-supplied IoTimer routine is not called unless the driver re-enables the timer.
KeInitializeDpc
Initializes a DPC object, to set up a driver-supplied CustomTimerDpc routine that can be called with a given context.
KeInitializeTimer
Initializes a notification timer object to the Not-Signaled state.
KeInitializeTimerEx
Initializes a notification or synchronization timer object to the Not-Signaled state.
KeSetTimer
Sets the absolute or relative interval at which a timer object will be set to the Signaled state and optionally supplies a timer DPC to be executed when the interval expires.
KeSetTimerEx
Sets the absolute or relative interval at which a timer object will be set to the Signaled state, optionally supplies a timer DPC to be executed when the interval expires, and optionally supplies a recurring interval for the timer.
KeCancelTimer
Cancels a timer object before the interval passed to KeSetTimer expires; dequeues a timer DPC before the timer interval, if any was set, expires.
KeReadStateTimer
Returns whether a given timer object is set to the Signaled state.
KeQuerySystemTime
Returns the current system time.
KeQueryTickCount
Returns the number of interval-timer interrupts that have occurred since the system was booted.
KeQueryTimeIncrement
Returns the number of 100-nanosecond units that are added to the system time at each interval-timer interrupt.
KeQueryPerformanceCounter
Returns the system performance counter value in hertz.


1.3.5 Driver Threads, Dispatcher Objects, and Resources

KeDelayExecutionThread
Puts the current thread into an alertable or nonalertable wait state for a given interval.
ExInitializeWorkItem
Initializes a work queue item with a caller-supplied context and callback routine to be queued for execution when a system worker thread is given control.
ExQueueWorkItem
Queues an initialized work queue item so the driver-supplied routine will be called when a system worker thread is given control.
ExInitializeResourceLite
Initializes a resource, for which the caller provides the storage, to be used for synchronization by a set of threads (shared readers, exclusive writers).
ExReinitializeResourceLite
Reinitializes an existing resource variable.
ExAcquireResourceExclusiveLite
Acquires an initialized resource for exclusive access by the calling thread and optionally waits for the resource to be acquired.
ExTryToAcquireResourceExclusiveLite
Either acquires a given resource for exclusive access immediately or returns FALSE.
ExAcquireResourceSharedLite
Acquires an initialized resource for shared access by the calling thread and optionally waits for the resource to be acquired.
ExAcquireSharedStarveExclusive
Acquires a given resource for shared access without waiting for any pending attempts to acquire exclusive access to the same resource.
ExAcquireSharedWaitForExclusive
Acquires a given resource for shared access, optionally waiting for any pending exclusive waiters to acquire and release the resource first.
ExIsResourceAcquiredExclusiveLite
Returns whether the calling thread has exclusive access to a given resource.
ExIsResourceAcquiredSharedLite
Returns how many times the calling thread has acquired shared access to a given resource.
ExGetExclusiveWaiterCount
Returns the number of threads currently waiting to acquire a given resource for exclusive access.
ExGetSharedWaiterCount
Returns the number of threads currently waiting to acquire a given resource for shared access.
ExConvertExclusiveToSharedLite
Converts a given resource from acquired for exclusive access to acquired for shared access.
ExGetCurrentResourceThread
Returns the thread ID of the current thread.
ExReleaseResourceForThreadLite
Releases a given resource that was acquired by the given thread.
ExDeleteResourceLite
Deletes a caller-initialized resource from the system’s resource list.
KeSetTimer
Sets the absolute or relative interval at which a timer object will be set to the Signaled state and optionally supplies a timer DPC to be executed when the interval expires.
KeSetTimerEx
Sets the absolute or relative interval at which a timer object will be set to the Signaled state, optionally supplies a timer DPC to be executed when the interval expires, and optionally supplies a recurring interval for the timer.
KeCancelTimer
Cancels a timer object before the interval passed to KeSetTimer expires; dequeues a timer DPC before the timer interval, if any was set, expires.
KeReadStateTimer
Returns whether a given timer object is set to the Signaled state.
KeSetEvent
Returns the previous state of a given event object and sets the event to the Signaled state if not already Signaled.
KeClearEvent
Resets the event to the Not-Signaled state.
KeResetEvent
Returns the previous state of a given event object and resets the event to the Not-Signaled state.
KeReadStateEvent
Returns the current state (nonzero for Signaled or zero for Not-Signaled) of a given event object.
ExAcquireFastMutex
Acquires an initialized fast mutex, possibly after putting the caller into a wait state until it is acqired, and gives the calling thread ownership with APCs disabled.
ExTryToAcquireFastMutex
Either acquires the given fast mutex immediately for the caller with APCs disabled or returns FALSE.
ExReleaseFastMutex
Releases ownership of a fast mutex that was acquired with ExAcquireFastMutex or ExTryToAcquireFastMutex.
ExAcquireFastMutexUnsafe
Acquires an initialized fast mutex, possibly after putting the caller into a wait state until it is acquired.
ExReleaseFastMutexUnsafe
Releases ownership of a fast mutex that was acquired with ExAcquireFastMutexUnsafe.
KeReleaseMutex
Releases a given mutex object, specifying whether the caller will call one of the KeWait Xxx routines as soon as KeReleaseMutex returns the previous value of the mutex state (a zero for Signaled; otherwise, Not-Signaled).
KeReadStateMutex
Returns the current state (one for Signaled or any other value for Not-Signaled) of a given mutex object.
KeReleaseSemaphore
Releases a given semaphore object, supplying a (runtime) priority boost for waiting threads if the release sets the semaphore to the Signaled state, augmenting the semaphore count by a given value, and specifying whether the caller will call one of the KeWait Xxx routines as soon as KeReleaseSemaphore returns.
KeReadStateSemaphore
Returns the current state (zero for Not-Signaled or a positive value for Signaled) of a given semaphore object.
KeWaitForSingleObject
Puts the current thread into an alertable or nonalertable wait state until a given dispatcher object is set to the Signaled state or (optionally) until the wait times out.
KeWaitForMutexObject
Puts the current thread into an altertable or nonalertable wait state until a given mutex is set to the Signaled state or (optionally) until the wait times out.
KeWaitForMultipleObjects
Puts the current thread into an alertable or nonalertable wait state until any one or all of a number of dispatcher objects are set to the Signaled state or (optionally) until the wait times out.
IoGetCurrentProcess
Returns a handle for the process of the current thread.
PsGetCurrentProcess
Returns a pointer to the process of the current thread.
KeEnterCriticalRegion
Temporarily disables the delivery of special kernel APCs while a highest-level driver is running in the context of the user-mode thread that requested the current I/O operation.
KeLeaveCriticalRegion
Re-enables the delivery of special kernel APCs again as soon as possible.
ZwSetInformationThread
Sets the priority of a given thread for which the caller has a handle.


1.4 Memory

This section summarizes kernel-mode support routines that NT drivers can call, according to the following general categories:

  • Support routines that drivers can call to allocate and free temporary buffers and/or zone buffers in Section 1.4.1

  • Support routines that drivers can call to allocate long-term internal driver buffers in Section 1.4.2

  • Support routines that drivers can call to manage buffered data or to initialize driver-allocated buffers in Section 1.4.3

  • Support routines that drivers can call to get mapped addresses and to allocate or manage MDLs (memory descriptor lists) in Section 1.4.4

  • Macros that drivers can use with buffers and MDLs in Section 1.4.5

  • Macros that device drivers can use to communicate with their respective devices in Section 1.4.6

  • Support routines that some drivers can call to lock and unlock their pageable code or data sections or can call to make their entire driver pageable in Section 1.4.7

  • Support routines that drivers can call to set up mapped sections and views of memory in Section 1.4.8


1.4.1 Temporary Buffers and Zone Entries

ExAllocatePool
Allocates (optionally cache-aligned) memory from paged or nonpaged system space.
ExAllocatePoolWithQuota
Allocates pool memory charging quota against the original requestor of the I/O operation. (Only highest-level drivers can call this routine.)
ExAllocatePoolWithTag
Allocates (optionally cache-aligned) tagged memory from paged or nonpaged system space. The caller-supplied tag is put into any crash dump of memory that occurs.
ExAllocatePoolWithQuotaTag
Allocates tagged pool memory charging quota against the original requestor of the I/O operation. The caller-supplied tag is put into any crash dump of memory that occurs. (Only highest-level drivers can call this routine.)
ExFreePool
Releases memory to paged or nonpaged system space.
ExInitializeNPagedLookasideList
Initializes a lookaside list of nonpaged memory. After a successful initialization, fixed-size blocks can be allocated from and freed to the lookaside list.
ExAllocateFromNPagedLookasideList
Removes the first entry from the specified lookaside list in nonpaged memory. If the lookaside list is empty, an entry is allocated from nonpaged pool.
ExFreeToNPagedLookasideList
Returns an entry to the specified lookaside list in nonpaged memory. If the list has reached its maximum size, the entry is returned to nonpaged pool.
ExDeleteNPagedLookasideList
Deletes a nonpaged lookaside list.
ExInitializePagedLookasideList
Initializes a lookaside list of paged memory. After a successful initialization, fixed-size blocks can be allocated from and freed to the lookaside list.
ExAllocateFromPagedLookasideList
Removes the first entry from the specified lookaside list in paged memory. If the lookaside list is empty, an entry is allocated from paged pool.
ExFreeToPagedLookasideList
Returns an entry to the specified lookaside list in paged memory. If the list has reached its maximum size, the entry is returned to paged pool.
ExDeletePagedLookasideList
Deletes a paged lookaside list.
ExInitializeZone
Initializes a zone buffer header, setting up the fixed size for every block to be allocated from the zone.
ExAllocateFromZone
Allocates a fixed-size block of storage from a zone buffer.
ExFreeToZone
Returns a previously allocated, fixed-size block in a zone buffer to the zone’s free-block list.
ExIsFullZone
Determines whether all fixed-size blocks have been allocated from a given zone buffer.
ExInterlockedAllocateFromZone
Allocates a fixed-size block from a zone buffer, using a spin lock to ensure multiprocessor-safe access to the zone’s free-block list.
ExInterlockedFreeToZone
Releases a fixed-size block in a zone buffer, using a spin lock to ensure multiprocessor-safe access to the zone’s free-block list.
ExExtendZone
Extends an existing zone buffer by allocating a block-size-granular amount of additional memory, which cannot be released until the machine is rebooted.
ExInterlockedExtendZone
Extends an existing zone buffer, which cannot be released until the machine is rebooted, using a spin lock to ensure multiprocessor-safe access to the zone header.
ExIsObjectInFirstZoneSegment
Returns whether a given entry was allocated from the original zone segment or from an extension to the zone.
MmQuerySystemSize
Returns whether the machine has a small, medium, or large amount of available memory.
MmIsThisAnNtAsSystem
Returns whether the machine is running as a server or as a desktop client. If this routine returns TRUE, the caller is likely to require more resources to process I/O requests, and the machine is a server so it is likely to have more resources available.


1.4.2 Long-Term Internal Driver Buffers

MmAllocateContiguousMemory
Allocates a range of physically contiguous, cache-aligned memory in nonpaged pool.
MmFreeContiguousMemory
Releases a range of physically contiguous memory when the driver unloads.
MmAllocateNonCachedMemory
Allocates a virtual address range of noncached and cache-aligned memory in nonpaged system space (pool).
MmFreeNonCachedMemory
Releases a virtual address range of noncached memory in nonpaged system space when the driver unloads.
HalAllocateCommonBuffer
Allocates and maps a logically contiguous region of memory that is simultaneously accessible both from the processor and from a device, given access to an adapter object, the requested length of the memory region to allocate, and access to variables where the starting logical and virtual addresses of the allocated region are returned. This routine returns TRUE if the requested length was allocated. It can be used for continuous busmaster DMA or for system DMA using the autoinitialize mode of a system DMA controller.
HalFreeCommonBuffer
Releases an allocated common buffer and unmaps it, given access to the adapter object, the length, and the starting logical and virtual addresses of the region to be freed when the driver unloads. Arguments must match those passed in the call to HalAllocateCommonBuffer.


1.4.3 Buffered Data and Buffer Initialization

RtlCompareMemory
Compares data, given pointers to caller-supplied buffers and the length in bytes for the comparison. Returns the number of bytes that were equal.
RtlCopyMemory
Copies the data from one caller-supplied buffer to another, given pointers to both buffers and the length in bytes to be copied.
RtlMoveMemory
Copies the data from one caller-supplied memory range to another, given pointers to the base of both ranges and the length in bytes to be copied.
RtlFillMemory
Fills a caller-supplied buffer with the specified UCHAR value, given a pointer to the buffer and the length in bytes to be filled.
RtlZeroMemory
Fills a buffer with zeros, given a pointer to the caller-supplied buffer and the length in bytes to be filled.
RtlStoreUshort
Stores a USHORT value at a given address, avoiding alignment faults.
RtlRetrieveUshort
Retrieves a USHORT value at a given address, avoiding alignment faults, and stores the value at a given address, which is assumed to be aligned.
RtlStoreUlong
Stores a ULONG value at a given address, avoiding alignment faults.
RtlRetrieveUlong
Retrieves a ULONG value at a given address, avoiding alignment faults, and stores the value at a given address, which is assumed to be aligned.


1.4.4 Address Mappings and MDLs

HalTranslateBusAddress
Returns the corresponding physical address in system memory for a given bus-relative logical address.
MmGetPhysicalAddress
Returns the corresponding physical address for a given valid virtual address.
MmGetMdlVirtualAddress
Returns a (possibly invalid) virtual address for a buffer described by a given MDL; the returned address, used as an index to a physical address entry in the MDL, can be input to IoMapTransfer for drivers that use DMA.
MmGetSystemAddressForMdl
Returns a system-space virtual address that maps the physical pages described by a given MDL for drivers whose devices must use PIO.
MmBuildMdlForNonPagedPool
Fills in the corresponding physical addresses of a given MDL that specifies a range of virtual addresses in nonpaged pool.
MmGetMdlByteCount
Returns the length in bytes of the buffer mapped by a given MDL.
MmGetMdlByteOffset
Returns the byte offset within a page of the buffer described by a given MDL.
MmMapLockedPages
Maps already locked physical pages, described by a given MDL, to a returned virtual address range.
MmUnmapLockedPages
Releases a mapping set up by MmMapLockedPages.
MmIsAddressValid
Returns whether a page fault will occur if a read or write is done at the given virtual address.
MmSizeOfMdl
Returns the number of bytes required for an MDL describing the buffer specified by the given virtual address and length in bytes.
MmCreateMdl
Allocates and initializes an MDL describing a buffer specified by the given virtual address and length in bytes; returns a pointer to the MDL.
MmPrepareMdlForReuse
Reinitializes a caller-created MDL for reuse.
MmInitializeMdl
Initializes a caller-created MDL to describe a buffer specified by the given virtual address and length in bytes.
MmMapIoSpace
Maps a physical address range to a cached or noncached virtual address range in nonpaged system space.
MmUnmapIoSpace
Unmaps a virtual address range from a physical address range.
MmProbeAndLockPages
Probes the pages specified in an MDL for a particular kind of access, makes the pages resident, and locks them in memory; returns the MDL updated with corresponding physical addresses. (Usually, only highest-level drivers call this routine.)
MmUnlockPages
Unlocks the previously probed and locked pages specified in an MDL.
IoAllocateMdl
Allocates an MDL large enough to map the starting address and length supplied by the caller; optionally associates the MDL with a given IRP.
IoBuildPartialMdl
Builds an MDL for the specified starting virtual address and length in bytes from a given source MDL. Drivers that split large transfer requests into a number of smaller transfers can call this routine.
IoFreeMdl
Releases a given MDL allocated by the caller.


1.4.5 Buffer and MDL Management

ADDRESS_AND_SIZE_TO_SPAN_PAGES
Returns the number of pages required to contain a given virtual address and size in bytes.
BYTE_OFFSET
Returns the byte offset of a given virtual address within the page.
BYTES_TO_PAGES
Returns the number of pages necessary to contain a given number of bytes.
PAGE_ALIGN
Returns the page-aligned virtual address for the page that contains a given virtual address.
ROUND_TO_PAGES
Rounds a give size in bytes up to a page-size multiple.


1.4.6 Device Memory Access

For the following, XXX_REGISTER_XXX indicates device memory that is mapped onto system space, while XXX_PORT_XXX indicates device memory in I/O space.

READ_PORT_UCHAR
Reads a UCHAR value from the given I/O port address.
READ_PORT_USHORT
Reads a USHORT value from the given I/O port address.
READ_PORT_ULONG
Reads a ULONG value from the given I/O port address.
READ_PORT_BUFFER_UCHAR
Reads a given count of UCHAR values from the given I/O port into a given buffer.
READ_PORT_BUFFER_USHORT
Reads a given count of USHORT values from the given I/O port into a given buffer.
READ_PORT_BUFFER_ULONG
Reads a given count of ULONG values from the given I/O port into a given buffer.
WRITE_PORT_UCHAR
Writes a given UCHAR value to the given I/O port address.
WRITE_PORT_USHORT
Writes a given USHORT value to the given I/O port address.
WRITE_PORT_ULONG
Writes a given ULONG value to the given I/O port address.
WRITE_PORT_BUFFER_UCHAR
Writes a given count of UCHAR values from a given buffer to the given I/O port.
WRITE_PORT_BUFFER_USHORT
Writes a given count of USHORT values from a given buffer to the given I/O port.
WRITE_PORT_BUFFER_ULONG
Writes a given count of ULONG values from a given buffer to the given I/O port.
READ_REGISTER_UCHAR
Reads a UCHAR value from the given register address in memory space.
READ_REGISTER_USHORT
Reads a USHORT value from the given register address in memory space.
READ_REGISTER_ULONG
Reads a ULONG value from the given register address in memory space.
READ_REGISTER_BUFFER_UCHAR
Reads a given count of UCHAR values from the given register address into the given buffer.
READ_REGISTER_BUFFER_USHORT
Reads a given count of USHORT values from the given register address into the given buffer.
READ_REGISTER_BUFFER_ULONG
Reads a given count of ULONG values from the given register address into the given buffer.
WRITE_REGISTER_UCHAR
Writes a given UCHAR value to the given register address in memory space.
WRITE_REGISTER_USHORT
Writes a given USHORT value to the given register address in memory space.
WRITE_REGISTER_ULONG
Writes a given ULONG value to the given register address in memory space.
WRITE_REGISTER_BUFFER_UCHAR
Writes a given count of UCHAR values from a given buffer to the given register address.
WRITE_REGISTER_BUFFER_USHORT
Writes a given count of USHORT values from a given buffer to the given register address.
WRITE_REGISTER_BUFFER_ULONG
Writes a given count of ULONG values from a given buffer to the given register address.


1.4.7 Pageable Drivers

MmLockPagableCodeSection
Locks a set of driver routines marked with a special compiler directive into system space, sometimes during driver initialization but usually in a driver’s DispatchCreate routine.
MmLockPagableDataSection
Locks data marked with a special compiler directive into system space, when that data is accessed infrequently, predictably and at an IRQL less than DISPATCH_LEVEL.
MmLockPagableSectionByHandle
Locks a pageable section into system memory using a handle returned from MmLockPagableCodeSection or MmLockPagableDataSection.
MmUnlockPagableImageSection
Releases a section that was previously locked into system space when the driver is no longer processing IRPs or when the contents of the section is no longer required.
MmPageEntireDriver
MmPageEntireDriver allows a driver to page out all of its code and data regardless of the attributes of the various sections in the driver’s image.
MmResetDriverPaging
MmResetDriverPaging resets a driver’s pageable status to that specified by the sections which make up the driver’s image.


1.4.8 Sections and Views

InitializeObjectAttributes
Sets up a parameter of type OBJECT_ATTRIBUTES for a subsequent call to a ZwCreate Xxx or ZwOpen Xxx routine.
ZwOpenSection
Obtains a handle for an existing section, provided that the requested access can be granted.
ZwMapViewOfSection
Maps a view of an open section into the virtual address space of a given process. Returns an offset into the section (base of the mapped view) and the size mapped.
ZwUnMapViewOfSection
Releases a mapped view in the virtual address space of the given process.


1.4.9 Access to Structures

ARGUMENT_PRESENT
A macro that takes an argument pointer and returns FALSE if the pointer is NULL, TRUE otherwise.
CONTAINING_RECORD
Returns the base address of an instance of a structure given the type of the structure and the address of a field within the containing structure.
FIELD_OFFSET
Returns the byte offset of a named field in a known structure type.


1.5 DMA

HalGetAdapter
Returns a pointer to the adapter object representing the DMA channel to which the driver’s device is connected or representing the driver’s busmaster adapter, together with the maximum number of map registers the driver can specify for each DMA transfer, given the input device description.
IoAllocateAdapterChannel
“Connects” a device object to an adapter object and calls a driver-supplied AdapterControl routine to carry out an I/O operation through the system DMA controller or a busmaster adapter as soon as the appropriate DMA channel is available and/or any necessary map registers are available. (This routine reserves exclusive access to a DMA channel and/or map registers for the specified device.)
MmGetMdlVirtualAddress
Returns a (possibly invalid) virtual address for a buffer described by a given MDL; the returned address, used as an index to a physical address entry in the MDL, can be input to IoMapTransfer.
ADDRESS_AND_SIZE_TO_SPAN_PAGES
Returns the number of pages required to contain a given virtual address and size in bytes. (This macro can be used to determine whether a given transfer request must be split into partial transfers.)
IoMapTransfer
Sets up map registers (up to the maximum returned by HalGetAdapter) for the given adapter object (already allocated by a call to IoAllocateAdapterChannel) to map a transfer from a locked-down buffer specified by an MDL, starting address, and length. Returns the logical address of the mapped region and, for busmaster devices that support scatter/gather, the number of bytes mapped.
IoFlushAdapterBuffers
Forces any data remaining in a busmaster adapter’s or the system DMA controller’s internal buffers to be written into memory or to the device, if the caller currently “owns” the adapter object, or terminates a transfer in progress if the driver detects a time-out or other error condition.
KeFlushIoBuffers
Flushes the memory region described by a given MDL from all processors’ caches into memory.
IoFreeAdapterChannel
Releases an adapter object, representing a system DMA channel, and optionally releases map registers, if any were allocated.
IoFreeMapRegisters
Releases a set of map registers, saved from a call to IoAllocateAdapterChannel, after the registers have been used in one or more calls to IoMapTransfer and the busmaster DMA transfer is complete.
HalReadDmaCounter
Returns how many bytes remain to be transferred during the current system DMA operation (autoinitialize mode), given a pointer to the adapter object.
HalAllocateCommonBuffer
Allocates and maps a logically contiguous region of memory that is simultaneously accessible both from the processor and from a device, given access to an adapter object, the requested length of the memory region to allocate, and access to variables where the starting logical and virtual addresses of the allocated region are returned. This routine returns TRUE if the requested length was allocated.
HalFreeCommonBuffer
Releases an allocated common buffer and unmaps it, given access to the adapter object, the length, and the starting logical and virtual addresses of the region to be freed. (Arguments must match those passed in the preceding call to HalAllocateCommonBuffer.)
KeGetDcacheFillSize
Returns the processor’s data cache-line boundary in bytes, useful for allocating buffers and sizing transfers to prevent cache tearing during DMA transfers.


1.6 PIO

MmProbeAndLockPages
Probes the pages specified in an MDL for a particular kind of access, makes the pages resident, and locks them in memory; returns the MDL updated with corresponding physical addresses.
MmGetSystemAddressForMdl
Returns a system-space virtual address that maps the physical pages described by a given MDL for drivers whose devices must use PIO.
KeFlushIoBuffers
Flushes the memory region described by a given MDL from all processors’ caches into memory.
MmUnlockPages
Unlocks the previously probed and locked pages specified in an MDL.
MmMapIoSpace
Maps a physical address range to a cached or noncached virtual address range in nonpaged system space.
MmUnmapIoSpace
Unmaps a virtual address range from a physical address range.


1.7 Driver-managed Queues

KeInitializeSpinLock
Initializes a variable of type KSPIN_LOCK. An initialized spin lock is a required parameter to the Ex..InterlockedList routines.
InitializeListHead
Sets up a queue header for a driver’s internal queue, given a pointer to driver-supplied storage for the queue header and queue. An initialized queue header is a required parameter to the ExInterlockedInsert/Remove..List routines.
ExInterlockedInsertTailList
Inserts an entry at the tail of a doubly linked list, using a spin lock to ensure multiprocessor-safe access to the list and atomic modification of the list links.
ExInterlockedInsertHeadList
Inserts an entry at the head of a doubly linked list, using a spin lock to ensure multiprocessor-safe access to the list and atomic modification of the list links.
ExInterlockedRemoveHeadList
Removes an entry from the head of a doubly linked list, using a spin lock to ensure multiprocessor-safe access to the list and atomic modification of the list links.
ExInterlockedPopEntryList
Removes an entry from the head of a singly linked list as an atomic operation, using a spin lock to ensure multiprocessor-safe access to the list.
ExInterlockedPushEntryList
Inserts an entry at the head of a singly linked list as an atomic operation, using a spin lock to ensure multiprocessor-safe access to the list.
IsListEmpty
Returns whether a queue is empty, given its initialized list head. (This type of doubly linked list is not protected by a spin lock, unless the caller explicitly manages synchronization to queued entries with an initialized spin lock for which the caller supplies the storage.)
InsertTailList
Queues an entry at the end of the list.
InsertHeadList
Queues an entry at the head of the list.
RemoveHeadList
Dequeues an entry at the head of the list.
RemoveTailList
Dequeues an entry at the end of the list.
RemoveEntryList
Returns whether a given entry is in the given list and dequeues the entry if it is.
PushEntryList
Inserts an entry into the queue. (This type of singly linked list is not protected by a spin lock, unless the caller explicitly manages synchronization to queued entries with an initialized spin lock for which the caller supplies the storage.)
PopEntryList
Removes an entry from the queue.
KeInitializeDeviceQueue
Initializes a device queue object to a not-busy state, setting up an associated spin lock for multiprocessor-safe access to device queue entries.
KeInsertDeviceQueue
Acquires the device queue spin lock and queues an entry to a device driver if the device queue is not empty; otherwise, inserts the entry at the tail of the device queue.
KeInsertByKeyDeviceQueue
Acquires the device queue spin lock and queues an entry to a device driver if the device queue is not empty; otherwise, inserts the entry into the queue according to the given sort-key value.
KeRemoveDeviceQueue
Removes an entry from the head of a given device queue.
KeRemoveByKeyDeviceQueue
Removes an entry, selected according to the specified sort-key value, from the given device queue.
KeRemoveEntryDeviceQueue
Determines whether a given entry is in the given device queue and, if so, dequeues the entry.


1.8 Driver System Threads

PsCreateSystemThread
Creates a kernel-mode thread associated with a given process object or with the default system process; returns a handle for the thread.
PsTerminateSystemThread
Terminates the current thread and satisfies as many waits as possible for the current thread object.
PsGetCurrentThread
Returns a handle for the current thread.
KeSetBasePriorityThread
Sets up the runtime priority, relative to the system process, for a driver-created thread.
KeSetPriorityThread
Sets up the runtime priority for a driver-created thread with a real-time priority attribute.
KeDelayExecutionThread
Puts the current thread into an alertable or nonalertable wait state for a given interval.
ExInitializeWorkItem
Initializes a work queue item with a caller-supplied context and callback routine to be queued for execution when a system worker thread is given control.
ExQueueWorkItem
Queues an initialized work queue item so the driver-supplied routine will be called when a system worker thread is given control.
ZwSetInformationThread
Sets the priority of a given thread for which the caller has a handle.


1.9 Strings

RtlInitString
Sets up the specified string in a buffer.
RtlInitAnsiString
Sets up the specified ANSI string in a buffer.
RtlInitUnicodeString
Sets up the specified Unicode string in a buffer.
RtlAnsiStringToUnicodeSize
Returns the size in bytes required to hold a Unicode version of a given buffered ANSI string.
RtlAnsiStringToUnicodeString
Converts a buffered ANSI string to a Unicode string, given a pointer to the source-string buffer and the address of caller-supplied storage for a pointer to the destination buffer. (This routine allocates a destination buffer if the caller does not supply the storage.)
RtlFreeUnicodeString
Releases a buffer containing a Unicode string, given a pointer to the buffer returned by RtlAnsiStringToUnicodeString.
RtlUnicodeStringToAnsiString
Converts a buffered Unicode string to an ANSI string, given a pointer to the source-string buffer and the address of caller-supplied storage for a pointer to the destination buffer. (This routine allocates a destination buffer if the caller does not supply the storage.)
RtlFreeAnsiString
Releases a buffer containing an ANSI string, given a pointer to the buffer returned by RtlUnicodeStringToAnsiString.
RtlAppendUnicodeStringToString
Concatenates a copy of a buffered Unicode string to a buffered Unicode string, given pointers to both buffers.
RtlAppendUnicodeToString
Concatenates a given input string to a buffered Unicode string, given a pointer to the buffer.
RtlCopyString
Either copies the source string to the destination, given pointers to both buffers, or sets the length of the destination string (but not the length of the destination buffer) to zero if the optional pointer to the source-string buffer is NULL.
RtlCopyUnicodeString
Either copies the source string to the destination, given pointers to both buffers, or sets the length of the destination string (but not the length of the destination buffer) to zero if the optional pointer to the source-string buffer is NULL.
RtlEqualString
Returns TRUE if the given ANSI alphabetic strings are equivalent.
RtlEqualUnicodeString
Returns TRUE if the given buffered strings are equivalent.
RtlCompareString
Compares two buffered, single-byte character strings and returns a signed value indicating whether they are equivalent or which is greater.
RtlCompareUnicodeString
Compares two buffered Unicode strings and returns a signed value indicating whether they are equivalent or which is greater.
RtlUpperString
Converts a copy of a given buffered string to uppercase and stores the copy in the given destination buffer.
RtlUpcaseUnicodeString
Converts a copy of a given buffered Unicode string to uppercase and stores the copy in the given destination buffer.
RtlIntegerToUnicodeString
Converts a given unsigned integer value in the specified base to one or more Unicode characters in the given buffer.
RtlUnicodeStringToInteger
RtlUnicodeStringToInteger converts a Unicode string representation of an integer into its integer equivalent.


1.10 Data Conversions

InterlockedExchange
Sets a variable of type LONG to a given value as an atomic operation; returns the original value of the variable.
RtlConvertLongToLargeInteger
Converts a given, LONG value to a LARGE_INTEGER value.
RtlConvertUlongToLargeInteger
Converts a given ULONG value to a LARGE_INTEGER value.
RtlTimeFieldsToTime
Converts the given TIME_FIELDS-type information to system time.
RtlTimeToTimeFields
Converts a given system time value into a TIME_FIELDS-type buffered value.
ExSystemTimeToLocalTime
Adds the time-zone bias for the current locale to GMT system time, converting it to local time.
ExLocalTimeToSystemTime
Subtracts the time-zone bias from the local time, converting it to GMT system time.
RtlAnsiStringToUnicodeString
Converts a buffered ANSI string to a Unicode string, given a pointer to the source-string buffer and the address of caller-supplied storage for a pointer to the destination buffer. (This routine allocates a destination buffer if the caller does not supply the storage.)
RtlUnicodeStringToAnsiString
Converts a buffered Unicode string to an ANSI string, given a pointer to the source-string buffer and the address of caller-supplied storage for a pointer to the destination buffer. (This routine allocates a destination buffer if the caller does not supply the storage.)
RtlUpperString
Converts a copy of a given buffered string to uppercase and stores the copy in the given destination buffer.
RtlUpcaseUnicodeString
Converts a copy of a given buffered Unicode string to uppercase and stores the copy in the given destination buffer.
RtlCharToInteger
Converts a given, single-byte character value into an integer in the specified base.
RtlIntegerToUnicodeString
Converts a given unsigned integer value in the specified base to one or more Unicode characters in the given buffer.
RtlUnicodeStringToInteger
Converts a Unicode string representation of an integer into its integer equivalent.


1.11 Access to Driver-Managed Objects

IoCreateSymbolicLink
Sets up a symbolic link object, aliasing a named device object to a user-visible name for the same device.
IoCreateUnprotectedSymbolicLink
Sets up a symbolic link object, aliasing a named device object to a user-visible name for the same device and allowing user-mode callers to affect the mode of the device (for example, parallel and serial drivers call this routine so users can redirect output).
IoGetFileObjectGenericMapping
Returns information about the mapping between generic access rights and specific access rights for file objects.
IoSetShareAccess
Sets the access allowed to a given file object representing a device. (Only highest-level drivers can call this routine.)
IoCheckShareAccess
Checks whether a request to open a file object specifies a desired access that is compatible with the current shared access permissions for the open file object. (Only highest-level drivers can call this routine.)
IoUpdateShareAccess
Modifies the current share-access permissions on the given file object. (Only highest-level drivers can call this routine.)
IoRemoveShareAccess
Restores the shared-access permissions on the given file object that were modified by a preceding call to IoUpdateShareAccess.
RtlLengthSecurityDescriptor
Returns the size in bytes of a given security descriptor.
RtlValidSecurityDescriptor
Returns whether a given security descriptor is valid.
RtlCreateSecurityDescriptor
Initializes a new security descriptor to an absolute format with default values (in effect, with no security constraints).
RtlSetDaclSecurityDescriptor
Sets the discretionary ACL information for a given security descriptor in absolute format.
SeAssignSecurity
Builds a security descriptor for a new object, given the security descriptor of its parent directory (if any) and an originally requested security for the object.
SeDeassignSecurity
Deallocates the memory associated with a security descriptor that was created with SeAssignSecurity.
SeAccessCheck
Returns a Boolean indicating whether the requested access rights can be granted to an object protected by a security descriptor and, possibly, a current owner.
SeSinglePrivilegeCheck
Returns a Boolean indicating whether the current thread has at least the given privilege level.


1.12 Error Handling

IoAllocateErrorLogEntry
Allocates and initializes an error log packet; returns a pointer so the caller can supply error-log data and call IoWriteErrorLogEntry with the packet.
IoWriteErrorLogEntry
Queues a previously allocated error log packet, filled in by the driver, to the system error logging thread.
IoIsErrorUserInduced
Returns a Boolean indicating whether an I/O request failed due to one of the following (user-correctable) conditions: STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_UNRECOGNIZED_MEDIA, STATUS_VERIFY_REQUIRED, STATUS_WRONG_VOLUME, STATUS_MEDIA_WRITE_PROTECTED, or STATUS_NO_MEDIA_IN_DEVICE. If the result is TRUE, a removable-media driver must call IoSetHardErrorOrVerifyDevice before completing the IRP.
IoSetHardErrorOrVerifyDevice
Supplies the device object for which the given IRP was failed due to a user-induced error, such as supplying the incorrect media for the requested operation or changing the media before the requested operation was completed. (A file system driver uses the associated device object to send a popup to the user, who can then correct the error and/or retry the operation.)
IoGetDeviceToVerify
Returns a pointer to the device object, representing a removable-media device, that is the target of the given thread’s I/O request. (This routine is useful only to file systems or other highest-level drivers.)
IoRaiseHardError
Causes a popup to be sent to the user indicating that the given IRP was failed on the given device object for an optional VPB, so that the user can correct the error and/or can retry the operation.
IoRaiseInformationalHardError
Causes a popup to be sent to the user, showing an I/O error status and optional string supplying more information.
ExRaiseStatus
Raises an error status so that a caller-supplied structured exception handler is called. (This routine is useful only to highest-level drivers that supply exception handlers, in particular to file systems.)
KeBugCheckEx
Brings down the system in a controlled manner, displaying the bugcheck code and possibly more information, when the caller discovers an unrecoverable inconsistency that will corrupt the system unless it is brought down. (This routine can be called when debugging under-development drivers. Otherwise, drivers should never call this routine when they can handle an error by failing an IRP and by calling IoAllocateErrorLogEntry and IoWriteErrorLogEntry.)
KeBugCheck
Brings down the system in a controlled manner when the caller discovers an unrecoverable inconsistency that will corrupt the system if the caller continues to run. KeBugCheckEx is preferable.
KeInitializeCallbackRecord
Initializes a bug-check callback record before a device driver calls KeRegisterBugCheckCallback.
KeRegisterBugCheckCallback
Registers a device driver’s bug-check callback routine, which is called if a system bug check occurs. Such a driver-supplied routine saves driver-determined state information, such as the contents of device registers, that would not otherwise be written into the system crash-dump file.
KeDeregisterBugCheckCallback
Removes a device driver’s callback routine from the set of registered bug-check callbacks.


Chapter 2 Executive Support Routines

References for the Ex Xxx routines are in alphabetical order.

For an overview of the functionality of these routines, see Chapter 1.


ExAcquireFastMutex

VOID
ExAcquireFastMutex(
IN PFAST_MUTEX FastMutex
 );

ExAcquireFastMutex acquires the given fast mutex with APCs to the current thread disabled.

Parameters

FastMutex
Points to an initialized fast mutex for which the caller provides the storage.

Comments

ExAcquireFastMutex puts the caller into a wait state if the given fast mutex cannot be acquired immediately. Otherwise, the caller is given ownership of the fast mutex with APCs to the current thread disabled until it releases the fast mutex.

Use ExTryToAcquireFastMutex if the current thread can do other work before it waits on the acquisition of the given mutex.

Any fast mutex acquired with ExAcquireFastMutex or ExTryToAcquireFastMutex must be released with ExReleaseFastMutex.

Callers of ExAcquireFastMutex must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireFastMutexUnsafe, ExInitializeFastMutex, ExReleaseFastMutex, ExTryToAcquireFastMutex


ExAcquireFastMutexUnsafe

VOID
ExAcquireFastMutexUnsafe(
IN PFAST_MUTEX FastMutex
);

ExAcquireFastMutexUnsafe acquires the given fast mutex for the current thread.

Parameters

FastMutex
Points to an initialized fast mutex for which the caller provides the storage.

Comments

ExAcquireFastMutexUnsafe puts the caller into a wait state if the given fast mutex cannot be acquired immediately. Otherwise, the caller is given ownership of the fast mutex and exclusive access to the resource it protects until it releases the fast mutex.

This routine can be called only if APCs will not be delivered to the current thread while it is holding the fast mutex. For example, while running within a critical region, APCs to the current thread are disabled.

Any fast mutex acquired with ExAcquireFastMutexUnsafe must be released with ExReleaseFastMutexUnsafe.

Callers of ExAcquireFastMutexUnsafe must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireFastMutex, ExInitializeFastMutex, ExReleaseFastMutexUnsafe, KeEnterCriticalRegion, KeLeaveCriticalRegion


ExAcquireResourceExclusive

BOOLEAN
ExAcquireResourceExclusive(
IN PERESOURCE Resource,
 IN BOOLEAN Wait
);

ExAcquireResourceExclusive is exported to support existing driver binaries and is obsolete. Use ExAcquireResourceExclusiveLite instead.


ExAcquireResourceExclusiveLite

BOOLEAN
ExAcquireResourceExclusiveLite(
IN PERESOURCE Resource,
 IN BOOLEAN Wait
);

ExAcquireResourceExclusive acquires the given resource for exclusive access by the calling thread.

Parameters

Resource
Points to the resource to acquire.
Wait
Is set to TRUE if the caller should be put into a wait state until the resource can be acquired if it cannot be acquired immediately.

Return Value

ExAcquireResourceExclusiveLite returns TRUE if the resource is acquired. This routine returns FALSE if the input Wait is FALSE and exclusive access cannot be granted immediately.

Comments

For better performance, call ExTryToAcquireResourceExclusiveLite, rather than calling ExAcquireResourceExclusiveLite with Wait set to FALSE.

Callers of ExAcquireResourceExclusiveLite must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireResourceSharedLite, ExGetExclusiveWaiterCount, ExGetSharedWaiterCount, ExInitializeResourceLite, ExReinitializeResourceLite, ExIsResourceAcquiredExclusiveLite, ExReleaseResourceForThreadLite, ExTryToAcquireResourceExclusiveLite


ExAcquireResourceSharedLite

BOOLEAN
ExAcquireResourceSharedLite(
IN PERESOURCE Resource,
 IN BOOLEAN Wait
);

ExAcquireResourceSharedLite acquires the given resource for shared access by the calling thread.

Parameters

Resource
Points to the resource to acquire.
Wait
Is set to TRUE if the caller should be put into a wait state until the resource can be acquired if it cannot be acquired immediately.

Return Value

ExAcquireResourceSharedLite returns TRUE if (or when) the resource is acquired. This routine returns FALSE if the input Wait is FALSE and shared access cannot be granted immediately.

Comments

Whether or when the caller is given shared access to the given resource depends on the following:

  • If the resource is currently unowned, shared access is granted immediately to the current thread.

  • If the caller already has acquired the resource, the current thread is granted the same type of access recursively. Note that making this call does not convert a caller’s exclusive ownership of a given resource to shared.

  • If the resource is currently owned as shared by another thread and no thread is waiting for exclusive access to the resource, shared access is granted to the caller immediately. The caller is put into a wait state if there is an exclusive waiter.

  • If the resource is currently owned as exclusive by another thread or if there is another thread waiting for exclusive access and the caller does not already have shared access to the resource, the current thread either is put into a wait state (Wait set to TRUE) or ExAcquireResourceSharedLite returns FALSE.

Callers of ExAcquireResourceSharedLite must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExConvertExclusiveToSharedLite, ExGetExclusiveWaiterCount, ExGetSharedWaiterCount, ExInitializeResourceLite, ExReinitializeResourceLite, ExIsResourceAcquiredSharedLite, ExReleaseResourceForThreadLite


ExAcquireSharedStarveExclusive

BOOLEAN
ExAcquireSharedStarveExclusive(
IN PERESOURCE Resource,
 IN BOOLEAN Wait
);

ExAcquireSharedStarveExclusive acquires a given resource for shared access without waiting for any pending attempts to acquire exclusive access to the same resource.

Parameters

Resource
Points to the resource to be acquired for shared access.
Wait
Is set to TRUE if the caller will wait until the resource becomes available when access cannot be granted immediately.

Return Value

ExAcquireSharedStarveExclusive returns TRUE if the requested access is granted. This routine returns FALSE if the input Wait is FALSE and shared access cannot be granted immediately.

Comments

Whether or when the caller is given shared access to the given resource depends on the following:

  • If the resource is currently unowned, shared access is granted immediately to the current thread.

  • If the caller already has acquired the resource, the current thread is granted the same type of access recursively. Note that making this call does not convert a caller’s exclusive ownership of a given resource to shared.

  • If the resource is currently owned as shared by another thread, shared access is granted to the caller immediately, even if another thread is waiting for exclusive access to that resource.

  • If the resource is currently owned as exclusive by another thread, the caller either is put into a wait state (Wait set to TRUE) or ExAcquireSharedStarveExclusive returns FALSE.

Callers of ExAcquireSharedStarveExclusive usually need quick access to a shared resource in order to save an exclusive accessor from doing redundant work. For example, a file system might call this routine to modify a cached resource, such as a BCB pinned in the cache, before the Cache Manager can acquire exclusive access to the resource and write the cache out to disk.

Callers of ExAcquireSharedStarveExclusive must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireResourceSharedLite, ExAcquireSharedWaitForExclusive, ExConvertExclusiveToSharedLite, ExGetExclusiveWaiterCount, ExIsResourceAcquiredExclusiveLite, ExIsResourceAcquiredSharedLite, ExTryToAcquireResourceExclusiveLite


ExAcquireSharedWaitForExclusive

BOOLEAN
ExAcquireSharedWaitForExclusive(
IN PERESOURCE Resource,
 IN BOOLEAN Wait
);

ExAcquireSharedWaitForExclusive acquires the given resource for shared access immediately if shared access can be granted. Optionally, the caller can wait for other thread(s) to acquire and release exclusive ownership of the resource.

Parameters

Resource
Points to the resource to be acquired for shared access.
Wait
Is set to TRUE if the caller will wait until the resource becomes available when access cannot be granted immediately.

Return Value

ExAcquireSharedWaitForExclusive returns TRUE if the requested access is granted or an exclusive owner releases the resource. This routine returns FALSE if the input Wait is FALSE and shared access cannot be granted immediately.

Comments

Whether or when the caller is given shared access to the given resource depends on the following:

  • If the resource is currently unowned, shared access is granted immediately to the current thread.

  • If the caller already has exclusive access to the resource, the current thread is granted the same type of access recursively.

  • If the resource is currently owned as shared and there are no pending attempts to acquire exclusive access, shared access is granted to the caller immediately.

  • If the resource is currently owned as shared but there is a pending attempt to acquire exclusive access, the caller either is put into a wait state (Wait set to TRUE) or ExAcquireSharedWaitForExclusive returns FALSE.

    When the current thread waits to acquire the resource until after a pending exclusive ownership has been released, ExAcquireSharedWaitForExclusive returns TRUE when the current thread is granted shared access to the resource and resumes execution.

Callers of ExAcquireSharedWaitForExclusive must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireResourceSharedLite, ExAcquireSharedStarveExclusive, ExConvertExclusiveToSharedLite, ExGetExclusiveWaiterCount, ExIsResourceAcquiredExclusiveLite, ExIsResourceAcquiredSharedLite, ExTryToAcquireResourceExclusiveLite


ExAllocateFromNPagedLookasideList

PVOID
ExAllocateFromNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST Lookaside
);

ExAllocateFromNPagedLookasideList removes the first entry from the specified lookaside list in nonpaged memory. If the lookaside list is empty, an entry is allocated from nonpaged pool.

Parameters

Lookaside
Points to the header of the lookaside list from which the entry will be allocated.

Return Value

ExAllocateFromNPagedLookasideList returns a pointer to an entry if one can be allocated; otherwise it returns NULL.

Comments

If the lookaside list is not empty, ExAllocateFromNPagedLookasideList removes the first entry from the list and returns its address to the caller. If the lookaside list is empty, ExAllocateFromNPagedLookasideList calls the allocate routine specified at list initialization to allocate another entry from nonpaged pool and returns its address to the caller. If no custom allocate routine was specified, this routine calls ExAllocatePoolWithTag. If the allocate routine cannot allocate an entry, ExAllocateFromNPagedLookasideList returns NULL.

Callers of ExAllocateFromNPagedLookasideList must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeNPagedLookasideList, ExFreeToNPagedLookasideList


ExAllocateFromPagedLookasideList

PVOID
ExAllocateFromPagedLookasideList(
IN PPAGED_LOOKASIDE_LIST Lookaside
);

ExAllocateFromPagedLookasideList removes the first entry from the specified lookaside list in paged memory.

Parameters

Lookaside
Points to the header of the lookaside list from which the entry will be allocated.

Return Value

ExAllocateFromPagedLookasideList returns a pointer to an entry if one can be allocated; otherwise it returns NULL.

Comments

If the lookaside list is not empty, ExAllocateFromPagedLookasideList removes the first entry from the list and returns its address to the caller. If the lookaside list is empty, ExAllocateFromPagedLookasideList calls the allocate routine specified at list initialization to allocate another entry from paged pool and returns its address to the caller. If no custom allocate routine was specified, this routine calls ExAllocatePoolWithTag. If the allocate routine cannot allocate an entry, ExAllocateFromPagedLookasideList returns NULL.

Callers of ExAllocateFromPagedLookasideList must be running at IRQL < DISPATCH_LEVEL.

See Also

ExInitializePagedLookasideList, ExFreeToPagedLookasideList


ExAllocateFromZone

PVOID
ExAllocateFromZone(
IN PZONE_HEADER Zone
);

ExAllocateFromZone removes an entry from the free list for a given zone and returns a pointer to the entry.

Parameters

Zone
Points to the zone header that controls the storage from which the entry is to be allocated.

Return Value

ExAllocateFromZone returns a pointer to the entry if it can be allocated.

Comments

Consider using a lookaside list instead of a zone. Lookaside lists are more efficient and are easier to manage.

Callers of ExAllocateFromZone must be running at IRQL <= DISPATCH_LEVEL.

ExIsFullZone should be called immediately preceding a call to ExAllocateFromZone to be sure that a free entry is available. Allocate additional temporary memory with ExAllocatePool if neccesary.

See Also

ExAllocatePool, ExExtendZone, ExInitializeNPagedLookasideList, ExInitializePagedLookasideList, ExInitializeZone, ExFreeToZone, ExIsFullZone


ExAllocatePool

PVOID
ExAllocatePool(
IN POOL_TYPE PoolType,
 IN ULONG NumberOfBytes
);

ExAllocatePool allocates pool memory of the specified type and returns a pointer to the allocated block. This routine is used for general pool allocation of memory.

Parameters

PoolType
Specifies the type of pool memory to allocate, which can be one of the following:

NonPagedPool
NonPagedPoolMustSucceed
NonPagedPoolCacheAligned
NonPagedPoolCacheAlignedMustS
PagedPool
PagedPoolCacheAligned

NumberOfBytes
Specifies the number of bytes to allocate.

Return Value

If the PoolType is one of the Xxx MustS(ucceed) values, this call succeeds if the system has any available must-succeed memory, and ExAllocatePool returns a pointer to allocated pool memory.

ExAllocatePool returns a NULL pointer if the PoolType is not one of the Xxx MustS(ucceed) values and not enough free pool exists to satisfy the request.

Comments

If the NumberOfBytes requested is >= PAGE_SIZE, a page-aligned buffer is allocated. Memory requests for < PAGE_SIZE do not cross page boundaries. Memory requests for < PAGE_SIZE are not necessarily page-aligned but are aligned on an 8-byte boundary.

For the PoolType NonPagedPoolMustSucceed somewhat less than PAGE_SIZE memory is available. If such a call fails to allocate sufficient memory, ExAllocatePool causes a system crash. Consequently, a caller should request this type of memory only if that caller needs it to prevent the system from crashing or being corrupted. Very few drivers ever encounter a situation that requires them to allocate this type of memory.

A successful allocation requesting NumberOfBytes < PAGE_SIZE of nonpaged pool gives the caller exactly the number of requested bytes of memory. Any successful allocation that requests NumberOfBytes > PAGE_SIZE wastes all unused bytes on the last-allocated page.

Callers of ExAllocatePool must be running at IRQL <= DISPATCH_LEVEL. A caller at DISPATCH_LEVEL must specify a NonPaged Xxx PoolType. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

If ExAllocatePool returns NULL, the caller should return the NTSTATUS value STATUS_INSUFFICIENT_RESOURCES or should delay processing to another point in time.

See Also

ExAllocatePoolWithTag, ExFreePool


ExAllocatePoolWithQuota

PVOID
ExAllocatePoolWithQuota(
IN POOL_TYPE PoolType,
 IN ULONG NumberOfBytes
);

ExAllocatePoolWithQuota allocates pool memory, charging quota against the current thread.

Parameters

PoolType
Specifies the type of pool to allocate, which can be one of the following:

NonPagedPool
NonPagedPoolMustSucceed
NonPagedPoolCacheAligned
NonPagedPoolCacheAlignedMustS
PagedPool
PagedPoolCacheAligned

NumberOfBytes
Specifies the number of bytes to allocate.

Return Value

If the PoolType is one of the Xxx MustS(ucceed) values, this call succeeds if the system has any available memory, and ExAllocatePoolWithQuota returns a pointer to the allocated pool.

ExAllocatePoolWithQuota returns a NULL pointer when the PoolType is not one of the Xxx MustS(ucceed) values and not enough pool can be allocated to satisfy the request.

Comments

This routine is called by highest-level drivers that allocate memory to satisfy a request in the context of the thread that originally made the I/O request. Lower-level drivers call ExAllocatePool instead.

If the NumberOfBytes requested is >= PAGE_SIZE, a page-aligned buffer is allocated. Callers requesting any PoolType except an Xxx MustS(ucceed) must check the return value for NULL and should return STATUS_INSUFFICIENT_RESOURCES when their pool allocation requests fail.

Memory requests for < PAGE_SIZE are allocated within a page and do not cross page boundaries. Memory requests for < PAGE_SIZE are not necessarily page-aligned but are aligned on an 8-byte boundary.

Callers of ExAllocatePoolWithQuota must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAllocatePool, ExAllocatePoolWithQuotaTag, ExFreePool


ExAllocatePoolWithQuotaTag

PVOID
ExAllocatePoolWithQuotaTag(
IN POOL_TYPE PoolType,
 IN ULONG NumberOfBytes,
IN ULONG Tag
);

ExAllocatePoolWithQuotaTag allocates pool memory, charging the quota against the current thread. A call to this routine is equivalent to calling ExAllocatePoolWithQuota, except it inserts a caller-supplied tag before the allocation. This tag appears in any crash dump of the system that occurs.

Parameters

PoolType
Specifies the type of pool to allocate, which can be one of the following:

NonPagedPool
NonPagedPoolMustSucceed
NonPagedPoolCacheAligned
NonPagedPoolCacheAlignedMustS
PagedPool
PagedPoolCacheAligned

NumberOfBytes
Specifies the number of bytes to allocate.
Tag
Is a string, delimited by single quote marks, with up to four characters, usually specified in reversed order.

Return Value

If the PoolType is one of the Xxx MustS(ucceed) values, this call succeeds if the system has any available memory, and ExAllocatePoolWithQuotaTag returns a pointer to the allocated pool.

ExAllocatePoolWithQuotaTag returns a NULL pointer when the PoolType is not one of the Xxx MustS(ucceed) values and not enough pool can be allocated to satisfy the request.

Comments

During driver development on a checked build of the system, this routine can be useful for crash debugging. Calling this routine, rather than ExAllocatePoolWithQuota, inserts the caller-supplied tag into a crash dump of pool memory.

The Tag passed to this routine is more readable if its bytes are reversed when this routine is called. For example, if a caller passes ‘Fred’ as a Tag, it would appear as ‘derF’ if pool is dumped or when tracking pool usage in the debugger.

Callers of ExAllocatePoolWithQuotaTag, like callers of ExAllocatePoolWithQuota, must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAllocatePoolWithQuota, ExFreePool


ExAllocatePoolWithTag

PVOID
ExAllocatePoolWithTag(
IN POOL_TYPE PoolType,
 IN ULONG NumberOfBytes,
IN ULONG Tag
);

ExAllocatePoolWithTag allocates pool memory. A call to this routine is equivalent to calling ExAllocatePool, except it inserts a caller-supplied tag before the allocation. This tag appears in any crash dump of the system that occurs.

Parameters

PoolType
Specifies the type of pool memory to allocate, which can be one of the following:

NonPagedPool
NonPagedPoolMustSucceed
NonPagedPoolCacheAligned
NonPagedPoolCacheAlignedMustS
PagedPool
PagedPoolCacheAligned

NumberOfBytes
Specifies the number of bytes to allocate.
Tag
Is a string, delimited by single quote marks, with up to four characters, usually specified in reversed order.

Return Value

If the PoolType is one of the Xxx MustS(ucceed) values, this call succeeds if the system has any available memory, and ExAllocatePoolWithTag returns a pointer to allocated pool memory.

ExAllocatePoolWithTag returns a NULL pointer if the PoolType is not one of the Xxx MustS(ucceed) values and not enough free pool exists to satisfy the request.

Comments

During driver development on a checked build of the system, this routine can be useful for crash debugging. Calling this routine, rather than ExAllocatePool, inserts the caller-supplied tag into a crash dump of pool memory.

The Tag passed to this routine is more readable if its bytes are reversed when this routine is called. For example, if a caller passes ‘Fred’ as a Tag, it would appear as ‘derF’ if pool is dumped or when tracking pool usage in the debugger.

Callers of ExAllocatePoolWithTag, like callers of ExAllocatePool, can be running at IRQL DISPATCH_LEVEL only if the requested PoolType is one of the NonPaged Xxx. Otherwise, callers must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAllocatePool, ExAllocatePoolWithQuotaTag, ExFreePool


ExConvertExclusiveToSharedLite

VOID
ExConvertExclusiveToSharedLite(
IN PERESOURCE Resource
);

ExConvertExclusiveToSharedLite converts a given resource from acquired for exclusive access to acquired for shared access.

Parameters

Resource
Points to the resource for which the access should be converted.

Comments

The caller must have exclusive access to the given resource. During this conversion, the current thread and any other threads waiting for shared access to the resource are given shared access.

Callers of ExConvertExclusiveToSharedLite must be running at IRQL < DISPATCH_LEVEL.

See Also

ExIsResourceAcquiredExclusiveLite


ExDeleteNPagedLookasideList

VOID
ExDeleteNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST Lookaside
 );

ExDeleteNPagedLookasideList deletes a nonpaged lookaside list.

Parameters

Lookaside
Points to the header of the lookaside list to be deleted.

Return Value

None.

Comments

ExDeleteNPagedLookasideList removes the given lookaside list from the system list of nonpaged lookaside lists and frees any remaining entries in the list. This routine does not deallocate the list header.

Callers of ExDeleteNPagedLookasideList must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeNPagedLookasideList


ExDeletePagedLookasideList

VOID
ExDeletePagedLookasideList(
IN PPAGED_LOOKASIDE_LIST Lookaside
 );

ExDeletePagedLookasideList deletes a paged lookaside list.

Parameters

Lookaside
Points to the header of the lookaside list to be deleted.

Return Value

None.

Comments

ExDeletePagedLookasideList removes the given lookaside list from the system list of paged lookaside lists and frees any remaining entries in the list. This routine does not deallocate the list header.

Callers of ExDeletePagedLookasideList must be running at IRQL < DISPATCH_LEVEL.

See Also

ExInitializePagedLookasideList


ExDeleteResource

NTSTATUS
ExDeleteResource(
IN PERESOURCE Resource
);

ExDeleteResource is exported to support existing driver binaries and is obsolete. Use ExDeleteResourceLite instead.


ExDeleteResourceLite

NTSTATUS
ExDeleteResourceLite(
IN PERESOURCE Resource
);

ExDeleteResourceLite deletes a given resource from the system’s resource list.

Parameters

Resource
Points to the caller-supplied storage for the initialized resource variable to be deleted.

Return Value

ExDeleteResourceLite returns STATUS_SUCCESS if the resource was deleted.

Comments

After calling ExDeleteResourceLite, the caller can free the memory it allocated for its resource.

Callers of ExDeleteResourceLite must be running at IRQL < DISPATCH_LEVEL.

See Also

ExFreePool, ExInitializeResourceLite, ExReinitializeResourceLite


ExExtendZone

NTSTATUS
ExExtendZone(
IN PZONE_HEADER Zone,
 IN PVOID Segment,
 IN ULONG SegmentSize
);

ExExtendZone extends a zone by adding another segment’s worth of blocks to the zone.

Parameters

Zone
Points to the address of a zone header to be extended.
Segment
Points to a segment of storage. The base address of the segment must be aligned on an 8-byte boundary.
SegmentSize
Is the size in bytes of Segment.

Return Value

ExExtendZone returns one of the following:
Status | Meaning
STATUS_UNSUCCESSFUL | Segment was not aligned on an 8-byte boundary or a block size was larger than SegmentSize.
STATUS_SUCCESS | The zone was successfully extended.

Comments

When a zone has no free blocks, additional temporary memory should be allocated with ExAllocatePool, rather than ExExtendZone or ExInterlockedExtendZone. Such a temporary allocation can be released with ExFreePool when the original zone has free entries again.

ExExtendZone allocates memory that remains allocated to its caller until the machine is rebooted. Consequently, ExExtendZone should not be called every time a call to ExIsFullZone returns TRUE. Call ExExtendZone only for a very heavy I/O demand situation when neither the original zone’s entries are being released nor the temporary memory allocated with ExAllocatePool.

The first ZONE_SEGMENT_HEADER-sized portion of the segment is used by the zone allocator. The remainder of the segment is divided into fixed-size blocks, as specified by BlockSize in the original call to ExInitializeZone, and added to the zone.

Callers of ExExtendZone must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAllocateFromZone, ExAllocatePool, ExFreePool, ExFreeToZone, ExInitializeZone, ExInterlockedAllocateFromZone, ExInterlockedExtendZone, ExInterlockedFreeToZone, ExIsFullZone, ExIsObjectInFirstZoneSegment


ExFreePool

VOID
ExFreePool(
IN PVOID P
);

ExFreePool deallocates a block of pool memory.

Parameters

P
Specifies the address of the block of pool memory being deallocated.

Comments

This routine releases memory allocated by ExAllocatePool, ExAllocatePoolWithTag, ExAllocatePoolWithQuota, or ExAllocatePoolWithQuotaTag. The memory block must not be accessed after it is freed.

Callers of ExAllocatePool must be running at IRQL <= DISPATCH_LEVEL.

A caller at DISPATCH_LEVEL must have specified a NonPaged Xxx PoolType when the memory was allocated. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAllocatePool, ExAllocatePoolWithQuota, ExAllocatePoolWithQuotaTag, ExAllocatePoolWithTag


ExFreeToNPagedLookasideList

VOID
ExFreeToNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST Lookaside,
IN PVOID Entry
);

ExFreeToNPagedLookasideList returns an entry to the specified lookaside list in nonpaged memory. If the list has reached its maximum size, the entry is returned to nonpaged pool.

Parameters

Lookaside
Points to the header of the lookaside list to which the entry will be returned.
Entry
Points to the entry to be freed.

Return Value

None.

Comments

If the lookaside list has not reached the maximum number of entries specified in its list header, Entry is inserted at the front of the list. If the list has reached its maximum size, Entry is returned to nonpaged pool using the free routine specified when the was list initialized.

Callers of ExFreeToNPagedLookasideList must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAllocateFromNPagedLookasideList, ExInitializeNPagedLookasideList


ExFreeToPagedLookasideList

VOID
ExFreeToPagedLookasideList(
IN PPAGED_LOOKASIDE_LIST Lookaside,
IN PVOID Entry
);

ExFreeToPagedLookasideList returns an entry to the specified lookaside list in paged memory. If the list has reached its maximum size, the entry is returned to paged pool.

Parameters

Lookaside
Points to the header of the lookaside list to which the entry will be returned.
Entry
Points to the entry to be freed.

Return Value

None.

Comments

If the lookaside list has not reached the maximum number of entries specified in its list header, Entry is inserted at the front of the list. If the list has reached its maximum size, Entry is returned to paged pool using the free routine specified when the list was initialized.

Callers of ExFreeToPagedLookasideList must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAllocateFromPagedLookasideList, ExInitializePagedLookasideList


ExFreeToZone

PVOID
ExFreeToZone(
PZONE_HEADER Zone,
 PVOID Block
);

ExFreeToZone puts a given block of storage back in the free list for a given zone.

Parameters

Zone
Points to the zone header that controls the storage where the entry is to be released.
Block
Points to the block of storage to be freed.

Return Value

ExFreeToZone returns a pointer to the previous block of storage that was at the head of the free list. NULL implies that the zone went from no available free blocks to at least one free block.

Comments

Callers of ExFreeToZone must be running at IRQL <= DISPATCH_LEVEL.

This routine is a reciprocal to ExAllocateFromZone.

See Also

ExAllocateFromZone, ExInterlockedFreeToZone


ExGetCurrentResourceThread

ERESOURCE_THREAD
ExGetCurrentResourceThread(
);

ExGetCurrentResourceThread identifies the current thread for a subsequent call to ExReleaseResourceForThreadLite.

Return Value

ExGetCurrentResourceThread returns the thread ID of the current thread.

Comments

Callers of ExGetCurrentResourceThread must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExIsResourceAcquiredExclusiveLite, ExIsResourceAcquiredSharedLite, ExReleaseResourceForThreadLite


ExGetExclusiveWaiterCount

ULONG
ExGetExclusiveWaiterCount(
IN PERESOURCE Resource
);

ExGetExclusiveWaiterCount returns the number of waiters on exclusive access to a given resource.

Parameters

Resource
Points to the resource to be tested.

Return Value

ExGetExclusiveWaiterCount returns the number of threads currently waiting to acquire the given resource for exclusive access.

Comments

ExGetExclusiveWaiterCount can be called to get an estimate of how many other threads might be waiting to modify the data protected by a particular resource variable. The caller cannot assume that the returned value remains constant for any particular interval.

Callers of ExGetExclusiveWaiterCount can be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireResourceSharedLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExGetSharedWaiterCount, ExReleaseResourceForThreadLite


ExGetSharedWaiterCount

ULONG
ExGetSharedWaiterCount(
IN PERESOURCE Resource
);

ExGetSharedWaiterCount returns the number of waiters on shared access to a given resource.

Parameters

Resource
Points to the resource to be tested.

Return Value

ExGetSharedWaiterCount returns the number of threads currently waiting to acquire the given resource for shared access.

Comments

ExGetSharedWaiterCount can be called to get an estimate of how many other threads might be waiting to read the data protected by a particular resource variable. The caller cannot assume that the returned value remains constant for any particular interval.

Callers of ExGetSharedWaiterCount can be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireResourceSharedLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExGetExclusiveWaiterCount, ExReleaseResourceForThreadLite


ExInitializeFastMutex

VOID
ExInitializeFastMutex(
IN PFAST_MUTEX FastMutex
);

ExInitializeFastMutex initializes a fast mutex variable, used to synchronize mutually exclusive access to a shared resource by a set of threads.

Parameters

FastMutex
Points to the caller-supplied storage, which must be at least sizeof(FAST_MUTEX), for the fast mutex variable being initialized.

Comments

ExInitializeFastMutex must be called before any calls to other Ex..FastMutex routines occur.

Although the caller supplies the storage for the given fast mutex, the FAST_MUTEX structure is opaque: that is, its members are reserved for system use.

For better performance, use the Ex..FastMutex routines instead of the Ke..Mutex routines. However, a fast mutex cannot be acquired recursively, as a kernel mutex can.

Callers of ExInitializeFastMutex must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireFastMutex, ExAcquireFastMutexUnsafe, ExReleaseFastMutex, ExReleaseFastMutexUnsafe, ExTryToAcquireFastMutex, KeInitializeMutex


ExInitializeNPagedLookasideList

VOID
ExInitializeNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST Lookaside,
 IN PALLOCATE_FUNCTION Allocate, /* optional */
 IN PFREE_FUNCTION Free, /* optional */
 IN ULONG Flags,
 IN ULONG Size,
 IN ULONG Tag,
IN USHORT Depth
 );

ExInitializeNPagedLookasideList initializes a lookaside list of nonpaged memory. After a successful initialization, fixed-size blocks can be allocated from and freed to the lookaside list.

Parameters

Lookaside
Points to the caller-supplied lookaside list header to be initialized. The list header must be allocated from nonpaged memory and is defined as follows:
typedef struct _NPAGED_LOOKASIDE_LIST {
    SLIST_HEADER ListHead;
    USHORT Depth;
    USHORT Pad;
    ULONG TotalAllocates;
    ULONG AllocateMisses;
    ULONG TotalFrees;
    ULONG FreeMisses;
    POOL_TYPE Type;
    ULONG Tag;
    ULONG Size;
    PALLOCATE_FUNCTION Allocate;
    PFREE_FUNCTION Free;
    LIST_ENTRY ListEntry;
    KSPIN_LOCK Lock;
} NPAGED_LOOKASIDE_LIST, *PNPAGED_LOOKASIDE_LIST;
Allocate
Points to an optional, caller-supplied routine for allocating an entry when the lookaside list is empty. If Allocate is NULL, additional entries are allocated using ExAllocatePoolWithTag.
Free
Points to an optional, caller-supplied routine for freeing an entry back to nonpaged pool when the lookaside list has sufficient entries. If no routine is specified, surplus entries are returned to nonpaged pool using ExFreePool.
Flags
Specifies the flags for controlling pool allocation. This parameter can have the value zero or POOL_RAISE_IF_ALLOCATION_FAILURE. In addition to any caller-supplied flags, this routine sets the pool allocation type flag to NonPagedPool.
Size
Specifies the size in bytes of each entry in the lookaside list.
Tag
Specifies the pool tag for lookaside list entries. The Tag is a string of four characters delimited by single quote marks (for example, ‘derF’). The characters are usually specified in reverse order so they are easier to read when dumping pool or tracking pool usage in the debugger.
Depth
Specifies the maximum number of entries that can be on the lookaside list.

Return Value

None.

Comments

ExInitializeNPagedLookasideList does the following:

  • Zeros the list header counters

  • Sets the addresses of the allocate and free routines

  • Initializes the spin lock

  • Sets the entry size, list depth, list tag, and pool allocation flags

ExInitializeNPagedLookasideList initializes the caller-supplied list header but does not allocate memory for list entries. The initial entries are allocated on an as-needed basis by the Lookaside.Allocate routine. The list becomes populated as entries are freed back to the list. Entries collect on the list until Lookaside.Depth is reached. Once Depth entries are on the list, any additional entries that are freed are returned to nonpaged pool using the Lookaside.Free routine. If the list becomes empty, allocate requests are satisfied by the Lookaside.Allocate routine.

The OS maintains a list of all the nonpaged lookaside lists in use on the system.

For optimal performance, the OS uses an 8-byte compare exchange operation to synchronize access to the lookaside list if such an instruction is available. On platforms where such an instruction is not available, the spin lock is used.

Owners of a nonpaged lookaside list should choose the Depth of the list carefully. The entries on the list consume nonpaged memory, making that memory unavailable to other components on the system. The list Depth should provide a good hit rate on allocations without consuming excessive memory.

If the POOL_ALLOCATE_RAISE_IF_FAILURE flag is set, the system raises an exception instead of returning NULL if an allocate fails.

Callers of ExInitializeNPagedLookasideList must be running at IRQL <= DISPATCH_LEVEL, but are usually running at PASSIVE_LEVEL.

See Also

ExAllocateFromNPagedLookasideList, ExAllocatePool, ExAllocatePoolWithTag, ExDeleteNPagedLookasideList, ExFreeToNPagedLookasideList, ExFreePool, ExInitializePagedLookasideList


ExInitializePagedLookasideList

VOID
ExInitializePagedLookasideList(
IN PPAGED_LOOKASIDE_LIST Lookaside,
 IN PALLOCATE_FUNCTION Allocate, /* optional */
 IN PFREE_FUNCTION Free, /* optional */
 IN ULONG Flags,
 IN ULONG Size,
 IN ULONG Tag,
IN USHORT Depth
 );

ExInitializePagedLookasideList initializes a lookaside list of paged memory. After a successful initialization, fixed-size blocks can be allocated from and freed to the lookaside list.

Parameters

Lookaside
Points to the caller-supplied lookaside list header to be initialized. The list header must be allocated from nonpaged memory and is defined as follows:
typedef struct _PAGED_LOOKASIDE_LIST {
    SLIST_HEADER ListHead;
    USHORT Depth;
    USHORT Pad;
    ULONG TotalAllocates;
    ULONG AllocateMisses;
    ULONG TotalFrees;
    ULONG FreeMisses;
    POOL_TYPE Type;
    ULONG Tag;
    ULONG Size;
    PALLOCATE_FUNCTION Allocate;
    PFREE_FUNCTION Free;
    LIST_ENTRY ListEntry;
    FAST_MUTEX Lock;
} PAGED_LOOKASIDE_LIST, *PPAGED_LOOKASIDE_LIST;
Allocate
Points to an optional, caller-supplied routine for allocating an entry when the lookaside list is empty. If Allocate is NULL, additional entries are allocated using ExAllocatePoolWithTag.
Free
Points to an optional, caller-supplied routine for freeing an entry back to nonpaged pool when the lookaside list has sufficient entries. If Free is NULL, surplus entries are returned to nonpaged pool using ExFreePool.
Flags
Specifies the flags for controlling pool allocation. This parameter can have the value zero or POOL_RAISE_IF_ALLOCATION_FAILURE. In addition to any caller-supplied flags, this routine sets the pool allocation type flag to PagedPool.
Size
Specifies the size in bytes of each entry in the lookaside list.
Tag
Specifies the pool tag for lookaside list entries. The Tag is a string of four characters delimited by single quote marks (for example, ‘derF’). The characters are usually specified in reverse order so they are easier to read when dumping pool or using the PoolHitTag variable in the debugger.
Depth
Specifies the maximum number of entries that can be on the lookaside list.

Return Value

None.

Comments

ExInitializePagedLookasideList does the following:

  • Zeros the list header counters

  • Sets the addresses of the allocate and free routines

  • Initializes the fast mutex

  • Sets the entry size, list depth, list tag, and pool allocation flags

ExInitializePagedLookasideList initializes the list header but does not allocate memory for list entries. The initial entries are allocated on an as-needed basis by the Lookaside.Allocate routine. The list becomes populated as entries are freed back to the list. Entries collect on the list until Lookaside.Depth is reached. Once Depth entries are on the list, any additional entries that are freed are returned to paged pool using the Lookaside.Free routine. If the list becomes empty, allocate requests are satisfied by the Lookaside.Allocate routine.

The OS maintains a list of all the paged lookaside lists being used on the system.

For optimal performance, the OS uses an 8-byte compare exchange operation to synchronize access to the lookaside list if such an instruction is available. On platforms where such an instruction is not available, the fast mutex is used.

If the POOL_ALLOCATE_RAISE_IF_FAILURE flag is set, the system raises an an exception instead of returning NULL if an allocate fails.

Callers of ExInitializePagedLookasideList must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAllocateFromPagedLookasideList, ExAllocatePoolWithTag, ExDeletePagedLookasideList, ExFreePool, ExFreeToPagedLookasideList, ExInitializeNPagedLookasideList


ExInitializeResource

NTSTATUS
ExInitializeResource(
IN PERESOURCE Resource
);

ExInitializeResource is exported to support existing driver binaries and is obsolete. Use ExInitializeResourceLite instead.


ExInitializeResourceLite

NTSTATUS
ExInitializeResourceLite(
IN PERESOURCE Resource
);

ExInitializeResourceLite initializes a resource variable.

Parameters

Resource
Points to the caller-supplied storage, which must be at least sizeof(ERESOURCE), for the resource variable being initialized.

Return Value

ExInitializeResourceLite returns STATUS_SUCCESS.

Comments

The storage for ERESOURCE must not be allocated from paged pool.

The resource variable can be used for synchronization by a set of threads. Although the caller provides the storage for the resource variable, the ERESOURCE structure is opaque: that is, its members are reserved for system use.

Call ExDeleteResourceLite before freeing the memory for the resource.

Callers of ExInitializeResourceLite must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireResourceSharedLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExConvertExclusiveToSharedLite, ExDeleteResourceLite, ExIsResourceAcquiredExclusiveLite, ExIsResourceAcquiredSharedLite, ExReinitializeResourceLite, ExReleaseResourceForThreadLite, ExTryToAcquireResourceExclusiveLite


ExInitializeSListHead

VOID
ExInitializeSListHead(
IN PSLIST_HEADER SListHead
 );

ExInitializeSListHead initializes the list header for a sequenced, interlocked, singly linked list.

Parameters

SListHead
Points to the caller-supplied list header to be initialized. The list header must be allocated from nonpaged memory and is defined as follows:
typedef union _SLIST_HEADER {
    ULONGLONG Alignment;
    struct {
        SINGLE_LIST_ENTRY Next;
        USHORT Depth;
        USHORT Sequence;
    };
} SLIST_HEADER, *PSLIST_HEADER;

Return Value

None.

Comments

ExInitializeSListHead zeros the depth and sequence number in SListHead and sets the first-entry pointer to NULL.

The sequence number in an S-List is incremented each time an entry is inserted to, or removed from, the list.

Entries in an S-List must be nonpaged. Users of an S-List must provide a spin lock to ExInterlockedPushEntrySList and ExInterlockedPopEntrySList. The OS may or may not use the spin lock, but it must be provided. For optimal performance, the OS uses an 8-byte compare exchange operation to synchronize access to the S-List if such an instruction is available. On platforms that do not support such an instruction, the spin lock is used. Callers of ExInterlocked..SList should make no assumptions about whether the spin lock will be used.

To manage a pool of fixed-size entries from either paged or nonpaged memory, consider using lookaside lists (Ex..LookasideList).

Drivers that retry I/O operations should use a doubly linked interlocked queue and the ExInterlockedInsert/Remove..List routines.

Callers of ExInitializeSListHead should be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeNPagedLookasideList, ExInitializePagedLookasideList, ExInterlockedPopEntrySList, ExInterlockedPushEntrySList, ExQueryDepthSListHead, ExQueueWorkItem


ExInitializeWorkItem

VOID
ExInitializeWorkItem(
IN PWORK_QUEUE_ITEM Item,
 IN PWORKER_THREAD_ROUTINE Routine,
 IN PVOID Context
);

ExInitializeWorkItem initializes a work-queue item with a caller-supplied context and callback routine to be queued for execution when a system worker thread is given control.

Parameters

Item
Points to the caller-supplied storage to be initialized. The storage must be allocated from nonpaged pool.
Routine
Points to the routine that processes this work item when it is dequeued. This callback is declared as follows:
VOID
(*PWORKER_THREAD_ROUTINE)(
    IN PVOID Parameter
);
Context
Points to a caller-supplied context that is passed, uninterpreted, to Routine as its only parameter.

Comments

ExInitializeWorkItem initializes Item with pointers to Routine and Context and a NULL list pointer.

Highest-level drivers can call ExInitializeWorkItem.

The caller-supplied Routine is responsible for calling ExFreePool to release the storage for Item.

Callers of ExInitializeWorkItem must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExFreePool, ExQueueWorkItem


ExInitializeZone

NTSTATUS
ExInitializeZone(
IN PZONE_HEADER Zone,
 IN ULONG BlockSize,
 IN PVOID InitialSegment,
 IN ULONG InitialSegmentSize
);

ExInitializeZone initializes a zone header. After a successful initialization, fixed-size blocks can be allocated from and freed to the zone.

Parameters

Zone
Points to the zone header to be initialized, which is defined as follows:
typedef struct _ZONE_HEADER {
    SINGLE_LIST_ENTRY FreeList;
    SINGLE_LIST_ENTRY SegmentList;
    ULONG BlockSize;
    ULONG TotalSegmentSize;
} ZONE_HEADER, *PZONE_HEADER;
BlockSize
The size in bytes of the allocatable unit within the zone. This value must be less than InitialSegmentSize and must be 8-byte aligned.
InitialSegment
Points to a segment of storage allocated by the caller. The base address of this segment must be aligned on an 8-byte boundary.
InitialSegmentSize
Is the size in bytes of InitialSegment.

Return Value

ExInitializeZone returns one of the following:
Status | Meaning
STATUS_UNSUCCESSFUL | BlockSize or InitialSegment was not aligned properly, or BlockSize was larger than the initial segment size.
STATUS_SUCCESS | The zone was successfully initialized.

Comments

Consider using a lookaside list instead of a zone. Lookaside lists are more efficient are are easier to manage.

The first ZONE_SEGMENT_HEADER-sized portion of the caller-allocated segment is used by the zone allocator. The remainder of the segment is divided into BlockSize blocks and made available for allocation and deallocation from the zone.

A driver should call MmQuerySystemSize and MmIsThisAnNtAsSystem before calling ExInitializeZone to determine an appropriate InitialSegmentSize. Callers of ExInitializeZone should specify an InitialSegmentSize of <= PAGE_SIZE if a preceding call to MmQuerySystemSize returns MmSmallSystem.

Memory for a zone must be resident if the zone blocks can be accessed by any code running at IRQL >= DISPATCH_LEVEL. Resident memory can be allocated with ExAllocatePool, using the input PoolType NonPagedPool.

Callers of the ExInterlocked..Zone routines also must provide resident storage for a spin lock, which must be initialized with KeInitializeSpinLock before any call is made to an ExInterlocked..Zone routine.

Callers of ExInitializeZone must be running at IRQL PASSIVE_LEVEL.

See Also

ExAllocateFromZone, ExAllocatePool, ExFreePool, ExFreeToZone, ExInitializeNPagedLookasideList, ExInitializePagedLookasideList, ExInterlockedAllocateFromZone, ExInterlockedFreeToZone, KeInitializeSpinLock, MmIsThisAnNtAsSystem, MmQuerySystemSize


ExInterlockedAddLargeInteger

LARGE_INTEGER
ExInterlockedAddLargeInteger(
IN PLARGE_INTEGER Addend,
 IN LARGE_INTEGER Increment,
 IN PKSPIN_LOCK Lock
);

ExInterlockedAddLargeInteger adds a large integer value to a given addend as an atomic operation.

Parameters

Addend
Points to a large integer to be adjusted by the Increment value.
Increment
Specifies a value to be added to Addend.
Lock
Points to a spin lock to be used to synchronize access to Addend.

Return Value

ExInterlockedAddLargeInteger returns the initial value of the Addend.

Comments

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The Lock passed to ExInterlockedAddLargeInteger is used to assure that the add operation on Addend is atomic with respect to any other operations on the same value which synchronize with this same spin lock.

ExInterlockedAddLargeInteger masks interrupts. Consequently, it can be used for synchronization between an ISR and other device driver code, provided that the same Lock is never reused in a call to a routine that runs at IRQL DISPATCH_LEVEL.

Note that calls to InterlockedXxxxxxx are guaranteed to be atomic with respect to other InterlockedXxxxxx calls without caller supplied spin locks.

Callers of ExInterlockedAddLargeInteger run at any IRQL.

See Also

ExInterlockedAddUlong, InterlockedIncrement, InterlockedDecrement, KeInitializeSpinLock


ExInterlockedAddUlong

ULONG
ExInterlockedAddUlong(
IN PULONG Addend,
 IN ULONG Increment,
 PKSPIN_LOCK Lock
);

ExInterlockedAddUlong adds an unsigned long value to a given unsigned integer as an atomic operation.

Parameters

Addend
Points to an unsigned long integer whose value is to be adjusted by the Increment value.
Increment
Is an unsigned long integer to be added.
Lock
Points to a spin lock to be used to synchronize access to the Addend.

Return Value

ExInterlockedAddUlong returns the original (unsummed) value of the Addend.

Comments

Consider using InterlockedExchangeAdd instead of this routine. InterlockedExchangeAdd can be more efficient because it does not use a spin lock and it is inlined by the compiler.

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The Lock passed to ExInterlockedAddULong is used to assure that the add operation on Addend is atomic with respect to any other operations on the same value which synchronize with this same spin lock.

ExInterlockedAddUlong masks interrupts. Consequently, it can be used for synchronization between an ISR and other driver code, provided that the same Lock is never reused in a call to a routine that runs at IRQL DISPATCH_LEVEL.

Note that calls to InterlockedXxxxxxx are guaranteed to be atomic with respect to other InterlockedXxxxxx calls without caller supplied spin locks.

Callers of ExInterlockedAddUlong run at any IRQL.

See Also

ExInterlockedAddLargeInteger, InterlockedIncrement, InterlockedDecrement, KeInitializeSpinLock


ExInterlockedAllocateFromZone

PVOID
ExInterlockedAllocateFromZone(
IN PZONE_HEADER Zone,
 IN PKSPIN_LOCK Lock
);

ExInterlockedAllocateFromZone removes an entry from a given zone and returns a pointer to the entry.

Parameters

Zone
Points to the zone header that controls the storage for the entry.
Lock
Points to a caller-supplied spin lock. This spin lock is released before returning to the caller.

Return Value

The routine returns a pointer to a zone entry.

Comments

Consider using a lookaside list instead of a zone. Lookaside lists are more efficient and are easier to manage.

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The removal process is performed with the given Lock held to update the links in a multiprocessor-safe way. Always call ExIsFullZone immediately preceding the call to ExInterlockedAllocateFromZone and allocate additional temporary memory with ExAllocatePool if necessary. Otherwise, a call to ExInterlockedAllocateFromZone can tie up a processor trying to acquire the Lock for a zone that has no free entries.

Any of the Ex..Interlocked Xxx can be called at DIRQL from a device driver’s ISR or SynchCritSection routine, provided that other driver routines do not make calls to the ExInterlocked Xxx while running at < DIRQL with the same spin lock. Otherwise, callers of ExInterlockedAllocateFromZone must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInterlockedFreeToZone, ExInitializeNPagedLookasideList, ExInitializePagedLookasideList, ExInitializeZone, ExIsFullZone


ExInterlockedDecrementLong

INTERLOCKED_RESULT
ExInterlockedDecrementLong(
IN PLONG Addend,
 IN PKSPIN_LOCK Lock
);

ExInterlockedDecrementLong is exported to support existing driver binaries and is obsolete. Use InterlockedDecrement instead.


ExInterlockedExchangeUlong

ULONG
ExInterlockedExchangeUlong(
IN PULONG Target,
 IN ULONG Value,
 IN PKSPIN_LOCK Lock
);

ExInterlockedExchangeUlong is exported to support existing driver binaries and is obsolete. Use InterlockedExchange instead.


ExInterlockedExtendZone

NTSTATUS
ExInterlockedExtendZone(
IN PZONE_HEADER Zone,
 IN PVOID Segment,
 IN ULONG SegmentSize,
 IN PKSPIN_LOCK Lock
);

ExInterlockedExtendZone extends a zone by adding another segment’s worth of blocks to the zone.

Parameters

Zone
Points to the address of a zone header to be extended.
Segment
Points to a segment of storage. The base address of the segment must be aligned on an 8-byte boundary.
SegmentSize
Is the size in bytes of Segment.
Lock
Points to a caller-supplied spin lock. This spin lock is released before returning to the caller.

Return Value

ExInterlockedExtendZone returns one of the following:
Status | Meaning
STATUS_UNSUCCESSFUL | Segment was not aligned on an 8-byte boundary or a SegmentSize block could not be allocated.
STATUS_SUCCESS | The zone was successfully extended.

Comments

When a zone has no free blocks, additional temporary memory should be allocated with ExAllocatePool, rather than ExInterlockedExtendZone or ExExtendZone. Such a temporary allocation can be released with ExFreePool when the original zone has free entries again.

ExInterlockedExtendZone allocates memory that remains allocated to its caller until the machine is rebooted. Consequently, ExInterlockedExtendZone should not be called every time a call to ExIsFullZone returns TRUE. Call ExInterlockedExtendZone only for a very heavy I/O demand situation when neither the original zone’s entries are being released nor the temporary memory allocated with ExAllocatePool.

The first ZONE_SEGMENT_HEADER-sized portion of the segment is used by the zone allocator. The remainder of the segment is divided into fixed-size blocks, as specified by BlockSize in the original call to ExInitializeZone, and added to the zone.

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

Callers of ExInterlockedExtendZone must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAllocatePool, ExFreePool, ExExtendZone, ExInitializeZone, ExInterlockedAllocateFromZone, ExInterlockedFreeToZone, ExIsFullZone, ExIsObjectInFirstZoneSegment


ExInterlockedFreeToZone

PVOID
ExInterlockedFreeToZone(
IN PZONE_HEADER Zone,
 IN PVOID Block,
 IN PKSPIN_LOCK Lock
);

ExInterlockedFreeToZone places the given zone entry in the free block list for the zone.

Parameters

Zone
Points to the zone header controlling the storage for the entry.
Block
Points to the block of storage to be freed.
Lock
Points to the caller-supplied spin lock. The spin lock is released before returning to the caller.

Return Value

ExInterlockedFreeToZone returns a pointer to the previous block of storage that was at the head of the free list. NULL implies that the zone went from no available free blocks to at least one free block.

Comments

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

ExInterlockedFreeToZone updates the free block list while holding the given the spin lock so the list links are updated in a multiprocessor-safe way.

Any of the Ex..Interlocked routines can be called at DIRQL from a device driver’s ISR or SynchCritSection routines, provided that other driver routines do not make calls to the ExInterlocked Xxx while running at < DIRQL with the same spin lock. Otherwise, callers of ExInterlockedFreeToZone must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeZone, ExInterlockedAllocateFromZone, KeInitializeSpinLock


ExInterlockedIncrementLong

INTERLOCKED_RESULT
ExInterlockedIncrementLong(
IN PLONG Addend,
 IN PKSPIN_LOCK Lock
);

ExInterlockedIncrementLong is exported to support existing driver binaries and is obsolete. Use InterlockedIncrement instead.


ExInterlockedInsertHeadList

PLIST_ENTRY
ExInterlockedInsertHeadList(
IN PLIST_ENTRY ListHead,
 IN PLIST_ENTRY ListEntry,
 IN PKSPIN_LOCK Lock
);

ExInterlockedInsertHeadList inserts an entry at the head of a doubly linked list so that access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the doubly linked list into which an entry is to be inserted.
ListEntry
Points to the entry to be inserted at the head of the list.
Lock
Points to a caller-supplied spin lock used to synchronize access to the list.

Return Value

ExInterlockedInsertHeadList returns a pointer to the entry that was at the head of the interlocked queue before this entry was inserted. If the queue was empty, it returns NULL.

Comments

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The caller also must supply resident storage for the interlocked queue. The ListHead must be initialized with InitializedListHead before the initial call to an ExInterlocked..List.

If the caller uses only ExInterlocked..List routines to manipulate the list, then these routines can be called from a single IRQL that is <= DIRQL. If other driver routines access the list using any other routines, such as the noninterlocked InsertHeadList, then callers of ExInterlocked..List must be at <= DISPATCH_LEVEL.

Usually, drivers call ExInterlockedInsertTailList to insert an IRP into a driver-managed interlocked queue. They call ExInterlockedInsertHeadList only to requeue an IRP for a retry.

See Also

ExInterlockedInsertTailList, ExInterlockedRemoveHeadList, InitializeListHead, KeInitializeSpinLock


ExInterlockedInsertTailList

PLIST_ENTRY
ExInterlockedInsertTailList(
IN PLIST_ENTRY ListHead,
 IN PLIST_ENTRY ListEntry,
 IN PKSPIN_LOCK Lock
);

ExInterlockedInsertTailList inserts an entry at the tail of a doubly linked list so access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the doubly linked list into which an entry is to be inserted.
ListEntry
Points to the entry to be inserted at the tail of the list.
Lock
Points to a caller-supplied spin lock, used to synchronize access to the list.

Return Value

ExInterlockedInsertTailList returns a pointer to the entry that was at the tail of the interlocked queue before this entry was inserted. If the queue was empty, it returns NULL.

Comments

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The caller also must supply resident storage for the interlocked queue. The ListHead must be initialized with InitializeListHead before the initial call to an ExInterlocked..List routine.

If the caller uses only ExInterlocked..List routines to manipulate the list, then these routines can be called from a single IRQL that is <= DIRQL. If other driver routines access the list using any other routines, such as the noninterlocked InsertHeadList, then callers of ExInterlocked..List must be at <= DISPATCH_LEVEL.

See Also

ExInterlockedInsertHeadList, InitializeListHead, KeInitializeSpinLock


ExInterlockedPopEntryList

PSINGLE_LIST_ENTRY
ExInterlockedPopEntryList(
IN PSINGLE_LIST_ENTRY ListHead,
 IN PKSPIN_LOCK Lock
);

ExInterlockedPopEntryList removes an entry from the front of a singly linked list so access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the singly linked list from which an entry is to be removed.
Lock
Points to a caller-supplied spin lock.

Return Value

If the list has no entries, a NULL pointer is returned. Otherwise, a pointer to the dequeued entry is returned.

Comments

The ExInterlocked..EntryList routines manipulate a simple, singly linked list and use a spin lock for MP-safe synchronization. For greater efficiency, consider using the ExInterlocked..EntrySList routines that manipulate a sequenced, singly linked list (an S-List) and use an intrinsic 8-byte compare exchange operation for synchronization, where available.

ExInterlockedPopEntrySList removes the first entry from the specified singly linked list.

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The caller also must provide the storage for the interlocked queue. The ListHead pointers must be set up before the initial call to ExInterlockedPushEntryList.

Drivers that retry I/O operations should use a doubly linked interlocked queue and the ExInterlockedInsert/Remove..List routines.

Any of the Ex..Interlocked routines can be called at DIRQL from a device driver’s ISR or SynchCritSection routine(s), provided that other driver routines do not make calls to the ExInterlocked Xxx while running at < DIRQL with the same spin lock. Otherwise, callers of ExInterlockedPopEntryList must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeSListHead, ExInterlockedPopEntrySList, ExInterlockedPushEntryList, KeInitializeSpinLock


ExInterlockedPopEntrySList

PSINGLE_LIST_ENTRY
ExInterlockedPopEntrySList(
IN PSLIST_HEADER ListHead,
 IN PKSPIN_LOCK Lock
);

ExInterlockedPopEntrySList removes the first entry from a sequenced, singly linked list so access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the sequenced, singly linked list into which the specified entry is to be inserted.
Lock
Points to a caller-supplied spin lock.

Return Value

ExInterlockedPopEntrySList returns a pointer to the first entry in the list. If the list was empty, this routine returns NULL.

Comments

ExInterlockedPopEntrySList removes the entry at the head of the list. Prior to calling this routine, the list should be initialized with ExInitializeSListHead.

The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked..SList routine. The spin lock is not used on platforms that support an 8-byte compare exchange operation, but the spin lock must be provided on any ExInterlocked..SList call.

Callers of ExInterlockedPopEntrySList should be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeSListHead, ExInterlockedPushEntrySList, ExQueryDepthSListHead, KeInitializeSpinLock


ExInterlockedPushEntryList

PSINGLE_LIST_ENTRY
ExInterlockedPushEntryList(
IN PSINGLE_LIST_ENTRY ListHead,
 IN PSINGLE_LIST_ENTRY ListEntry,
 IN PKSPIN_LOCK Lock
);

ExInterlockedPushEntryList inserts an entry at the head of a singly linked list so access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the singly linked list into which the specified entry is to be inserted.
ListEntry
Points to the entry to be inserted.
Lock
Points to a caller-supplied spin lock.

Return Value

ExInterlockedPushEntryList returns NULL if the list has no entries. Otherwise, a pointer to the entry that is pushed (the previous list head) is returned.

Comments

The ExInterlocked..EntryList routines manipulate a simple, singly linked list and use a spin lock for MP-safe synchronization. For greater efficiency, consider using the ExInterlocked..EntrySList routines that manipulate a sequenced, singly linked list (an S-List) and use an intrinsic 8-byte compare exchange operation for synchronization, where available.

ExInterlockedPushEntrySList inserts an entry at the head of the specified singly linked list.

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The caller also must provide resident storage for the interlocked queue. The ListHead pointers must be set up before the initial call to ExInterlockedPushEntryList.

Drivers that retry I/O operations should use a doubly linked interlocked queue and the ExInterlockedInsert/Remove..List routines.

Any of the Ex..Interlocked routines can be called at DIRQL from a device driver’s ISR or SynchCritSection routine(s), provided that other driver routines do not make calls to the ExInterlocked Xxx while running at < DIRQL with the same spin lock. Otherwise, callers of ExInterlockedPushEntryList must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeSListHead, ExInterlockedPushEntrySList, ExInterlockedPopEntryList, KeInitializeSpinLock


ExInterlockedPushEntrySList

PSINGLE_LIST_ENTRY
ExInterlockedPushEntrySList(
IN PSLIST_HEADER ListHead,
 IN PSINGLE_LIST_ENTRY ListEntry,
 IN PKSPIN_LOCK Lock
);

ExInterlockedPushEntrySList inserts an entry at the head of a sequenced, singly linked list so access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the sequenced, singly linked list into which the specified entry is to be inserted.
ListEntry
Points to the entry to be inserted.
Lock
Points to a caller-supplied spin lock.

Return Value

ExInterlockedPushEntrySList returns a pointer to the previous first entry in the list. If the list was previously empty, this routine returns NULL.

Comments

ExInterlockedPushEntrySList inserts ListEntry at the head of the list. Prior to calling this routine, the list should be initialized with ExInitializeSListHead.

The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked..SList routine. The spin lock is not used on platforms that support an 8-byte compare exchange operation, but the spin lock must be provided on any ExInterlocked..SList call.

Drivers that retry I/O operations should use a doubly linked interlocked queue and the ExInterlockedInsert/Remove..List routines.

Callers of ExInterlockedPushEntrySList should be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeSListHead, ExInterlockedPopEntrySList, ExQueryDepthSListHead, KeInitializeSpinLock


ExInterlockedRemoveHeadList

PLIST_ENTRY
ExInterlockedRemoveHeadList(
IN PLIST_ENTRY ListHead,
 IN PKSPIN_LOCK Lock
);

ExInterlockedRemoveHeadList removes an entry from the head of a doubly linked list so access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the doubly linked list from which an entry is to be removed.
Lock
Points to a caller-supplied spin lock.

Return Value

If the list is empty, a NULL pointer is returned. Otherwise, a pointer to the dequeued entry is returned.

Comments

Support routines that do interlocked operations are assumed to be incapable of causing a page fault. That is, neither their code nor any of the data they touch can cause a page fault without bringing down the system. They use spin locks to achieve atomicity in SMP machines. The caller must provide resident storage for the Lock, which must be initialized with KeInitializeSpinLock before the initial call to an ExInterlocked Xxx.

The caller also must supply resident storage for the interlocked queue. The ListHead must be initialized with InitializeListHead before the initial call to an ExInterlocked..List routine.

If the caller uses only ExInterlocked..List routines to manipulate the list, then these routines can be called from a single IRQL that is <= DIRQL. If other driver routines access the list using any other routines, such as the noninterlocked InsertHeadList, then callers of ExInterlocked..List must be at <= DISPATCH_LEVEL.

See Also

ExInterlockedInsertHeadList, ExInterlockedInsertTailList, InitializeListHead, KeInitializeSpinLock


ExIsFullZone

BOOLEAN
ExIsFullZone(
IN PZONE_HEADER Zone
);

ExIsFullZone determines whether the given zone is full. A zone is considered full if its free list is empty.

Parameters

Zone
Points to the zone header to be tested.

Return Value

ExIsFullZone returns TRUE if an entry cannot be allocated from the zone when the call occurs.

Comments

This routine should always be called immediately before making a call to ExAllocateFromZone or ExInterlockedAllocateFromZone. If the return value is TRUE, the caller should back up the zone buffer by calling ExAllocatePool to get a temporary buffer it can use until the zone buffer has free blocks again.

Callers of ExIsFullZone usually run at IRQL <= DISPATCH_LEVEL. In particular, callers must be running at IRQL <= DISPATCH_LEVEL to make a subsequent call to ExAllocateFromZone or to ExInterlockedAllocateFromZone if the input Lock is used by any driver routine that runs at IRQL < DIRQL.

See Also

ExAllocateFromZone, ExAllocatePool, ExFreeToZone, ExInterlockedAllocateFromZone, ExInterlockedFreeToZone


ExIsObjectInFirstZoneSegment

BOOLEAN
ExIsObjectInFirstZoneSetment(
IN PZONE_HEADER Zone,
 IN PVOID Object
);

ExIsObjectInFirstZoneSegment determines whether the given object was allocated from the original zone segment or from an extension to the zone.

Parameters

Zone
Points to the zone header to be tested.
Object
Points to the object to be tested.

Return Value

ExIsObjectInFirstZoneSegment returns TRUE if the given object was allocated from the original zone segment.

Comments

Callers of ExIsObjectInFirstZoneSegment must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeZone, ExExtendZone, ExInterlockedExtendZone


ExIsResourceAcquiredExclusiveLite

BOOLEAN
ExIsResourceAcquiredExclusiveLite(
IN PERESOURCE Resource
);

ExIsResourceAcquiredExclusiveLite returns whether the current thread has exclusive access to a given resource.

Parameters

Resource
Points to the resource to be queried.

Return Value

ExIsResourceAcquiredExclusiveLite returns TRUE if the caller already has exclusive access to the given resource.

Comments

Callers of ExIsResourceAcquiredExclusiveLite must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExIsResourceAcquiredSharedLite, ExTryToAcquireResourceExclusiveLite


ExIsResourceAcquiredSharedLite

USHORT
ExIsResourceAcquiredSharedLite(
IN PERESOURCE Resource
);

ExIsResourceAcquiredSharedLite returns whether the current thread has shared access to a given resource.

Parameters

Resource
Points to the resource to be queried.

Return Value

ExIsResourceAcquiredSharedLite returns the number of times the caller has acquired shared access to the given resource.

Comments

Callers of ExIsResourceAcquiredSharedLite must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceSharedLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExIsResourceAcquiredExclusiveLite


ExLocalTimeToSystemTime

VOID
ExLocalTimeToSystemTime(
IN PLARGE_INTEGER LocalTime,
 OUT PLARGE_INTEGER SystemTime
);

ExLocalTimeToSystemTime converts a system time value for the current time zone to an unbiased, GMT value.

Parameters

LocalTime
Points to a variable set to the locale-specific time.
SystemTime
Points to the returned value for GMT system time.

Comments

ExLocalTimeToSystemTime adds the time-zone bias at the current locale to compute the corresponding GMT system time value.

Callers of ExLocalTimeToSystemTime can be running at any IRQL.

See Also

ExSystemTimeToLocalTime


ExQueryDepthSListHead

USHORT
ExQueryDepthSListHead(
IN PSLIST_HEADER SListHead
 );

ExQueryDepthSListHead returns the current number of entries in a sequenced, singly linked list.

Parameters

SListHead
Points to the head of the sequenced, singly linked list to be queried.

Return Value

ExQueryDepthSListHead returns the current number of entries in the S-List.

Comments

ExQueryDepthSListHead returns the current number of entries in the S-List. Prior to calling this routine, the list should be initialized with ExInitializeSListHead.

Callers of ExQueryDepthSListHead should be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeSListHead, ExInterlockedPushEntrySList, ExInterlockedPopEntrySList


ExQueueWorkItem

VOID
ExQueueWorkItem(
IN PWORK_QUEUE_ITEM WorkItem,
 IN WORK_QUEUE_TYPE QueueType
);

ExQueueWorkItem inserts a given work item into a queue from which a system worker thread removes the item and gives control to the routine that the caller supplied to ExInitializeWorkItem.

Parameters

WorkItem
Points to the work item that was set up by a preceding call to ExInitializeWorkItem.
QueueType
Specifies the queue into which WorkItem is inserted. QueueType can be either of the following:

Value

Meaning

CriticalWorkQueue

Insert the WorkItem into the queue from which a system thread with a real-time priority attribute will dequeue the entry.

DelayedWorkQueue

Insert the WorkItem into the queue from which a system thread with a variable priority attribute will dequeue the entry.

The QueueType value HyperCriticalWorkQueue is reserved for system use.

Comments

Highest-level drivers can call ExQueueWorkItem.

The callback is run within a system thread context at IRQL PASSIVE_LEVEL. This caller-supplied routine is responsible for calling ExFreePool to reclaim the storage allocated for WorkItem.

A driver must not wait for its callback routine to complete an operation if it is already holding one synchronization object and might attempt to acquire another. For example, a driver should release any currently held semaphores, mutexes, resource variables, and so forth before it calls ExQueueWorkItem. Releasing synchronization resources before queueing a synchronous worker-thread operation prevents deadlocks.

The value of QueueType determines the runtime priority at which the callback routine is run, as follows:

  • If the callback runs in the system thread with a real-time priority attribute, the callback routine cannot be preempted except by threads with higher real-time priorities.

  • If the callback runs in the system thread with a variable priority attribute, the callback can be preempted by threads with higher variable and real-time priorities, and the callback is scheduled to run round-robin with other threads of the same priority for a quantum each.

Threads at either priority remain interruptible.

Callers of ExQueueWorkItem must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExFreePool, ExInitializeWorkItem


ExRaiseStatus

VOID
ExRaiseStatus(
IN NTSTATUS Status
);

ExRaiseStatus is called by drivers that supply structured exception handlers to handle particular errors that occur while they are processing I/O requests.

Parameters

Status
Is one of the system-defined STATUS_XXX values.

Comments

Highest-level drivers, particularly file systems, can call ExRaiseStatus.

Callers of ExRaiseStatus must be running at IRQL PASSIVE_LEVEL.

See Also

IoAllocateErrorLogEntry, KeBugCheckEx


ExReinitializeResourceLite

VOID
ExReinitializeResourceLite(
IN PERESOURCE Resource
)

ExReinitializeResourceLite reinitializes an existing resource variable.

Parameters

Resource
Points to the caller supplied resource variable to be reinitialized.

Return Value

ExReinitializeResourceLite returns STATUS_SUCCESS.

Comments

With a single call to ExReinitializeResource, a driver writer can replace three calls: one to ExDeleteResourceLite, another to ExAllocatePool, and a third to ExInitializeResourceLite. As contention for a resource variable increases, memory is dynamically allocated and attached to the resource in order to track this contention. As an optimization, ExReinitializeResourceLite retains and zeroes this previously allocated memory.

The ERESOURCE structure is opaque: that is, the members are reserved for system use.

Callers of ExReinitializeResourceLite must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireResourceSharedLite, ExInitializeResourceLite, ExDeleteResourceLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExConvertExclusiveToSharedLite, ExIsResourceAcquiredExclusiveLite, ExIsResourceAcquiredSharedLite, ExReleaseResourceForThreadLite, ExTryToAcquireResourceExclusiveLite


ExReleaseFastMutex

VOID
ExReleaseFastMutex(
IN PFAST_MUTEX FastMutex
);

ExReleaseFastMutex releases ownership of a fast mutex that was acquired with ExAcquireFastMutex or ExTryToAcquireFastMutex.

Parameters

FastMutex
Points to the fast mutex to be released.

Comments

ExReleaseFastMutex releases ownership of the given fast mutex and reenables the delivery of APCs to the current thread.

It is a programming error to call ExReleaseFastMutex with a FastMutex that was acquired with ExAcquireFastMutexUnsafe.

Callers of ExReleaseFastMutex must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireFastMutex, ExInitializeFastMutex, ExTryToAcquireFastMutex


ExReleaseFastMutexUnsafe

VOID
ExReleaseFastMutexUnsafe(
IN PFAST_MUTEX FastMutex
);

ExReleaseFastMutexUnsafe releases ownership of a fast mutex that was acquired with ExAcquireFastMutexUnsafe.

Parameters

FastMutex
Points to the fast mutex to be released.

Comments

It is a programming error to call ExReleaseFastMutexUnsafe with a FastMutex that was acquired with ExAcquireFastMutex or ExTryToAcquireFastMutex.

Callers of ExReleaseFastMutexUnsafe must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireFastMutexUnsafe, ExInitializeFastMutex


ExReleaseResource

VOID
ExReleaseResource(
IN PERESOURCE Resource
);

ExReleaseResource is exported to support existing driver binaries and is obsolete. Use ExReleaseResourceForThreadLite instead.


ExReleaseResourceForThread

VOID
ExReleaseResourceForThreadLite(
IN PERESOURCE Resource,
 IN ERESOURCE_THREAD ResourceThreadId
);

ExReleaseResourceForThread is exported to support existing driver binaries and is obsolete. Use ExReleaseResourceForThreadLite instead.


ExReleaseResourceForThreadLite

VOID
ExReleaseResourceForThreadLite(
IN PERESOURCE Resource,
 IN ERESOURCE_THREAD ResourceThreadId
);

ExReleaseResourceForThreadLite releases the input resource of the indicated thread.

Parameters

Resource
Points to the resource to release.
ResourceThreadId
Identifies the thread that originally acquired the resource.

Comments

Callers of ExReleaseResourceForThreadLite must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireResourceSharedLite, ExAcquireSharedStarveExclusive, ExAcquireSharedWaitForExclusive, ExGetCurrentResourceThread, ExInitializeResourceLite, ExReinitializeResourceLite, ExTryToAcquireResourceExclusiveLite


ExSystemTimeToLocalTime

VOID
ExSystemTimeToLocalTime(
IN PLARGE_INTEGER SystemTime,
 OUT PLARGE_INTEGER LocalTime
);

ExSystemTimeToLocalTime converts a GMT system time value to the local system time for the current time zone.

Parameters

SystemTime
Points to a variable set to the unbiased, GMT system time.
LocalTime
Points to the returned value for the current locale.

Comments

ExSystemTimeToLocalTime subtracts the time-zone bias from the GMT system time value to compute the corresponding time at the current locale.

Callers of ExSystemTimeToLocalTime can be running at any IRQL.

See Also

ExLocalTimeToSystemTime


ExTryToAcquireFastMutex

BOOLEAN
ExTryToAcquireFastMutex(
IN PFAST_MUTEX FastMutex
);

ExTryToAcquireFastMutex acquires the given fast mutex, if possible, with APCs to the current thread disabled.

Parameters

FastMutex
Points to the fast mutex to be acquired if it is not currently owned by another thread.

Return Value

ExTryToAcquireFastMutex returns TRUE if the current thread is given ownership of the fast mutex.

Comments

If the given fast mutex is currently unowned, ExTryToAcquireFastMutex gives the caller ownership with APCs to the current thread disabled until it releases the fast mutex.

Use ExAcquireFastMutex if the current thread must wait on the acquisition of the given mutex before it can do useful work.

Any fast mutex acquired with ExTryToAcquireFastMutex or ExAcquireFastMutex must be released with ExReleaseFastMutex.

Callers of ExTryToAcquireFastMutex must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireFastMutex, ExInitializeFastMutex, ExReleaseFastMutex


ExTryToAcquireResourceExclusiveLite

BOOLEAN
ExTryToAcquireResourceExclusiveLite(
IN PERESOURCE Resource
);

ExTryToAcquireResourceExclusiveLite attempts to acquire the given resource for exclusive access.

Parameters

Resource
Points to the resource to be acquired.

Return Value

ExTryToAcquireResourceExclusiveLite returns TRUE if the given resource has been acquired for the caller.

Comments

Use ExAcquireResourceExclusiveLite if the caller must have exclusive access to the resource before it can do further useful work.

Callers of ExTryToAcquireResourceExclusiveLite must be running at IRQL < DISPATCH_LEVEL.

See Also

ExAcquireResourceExclusiveLite, ExAcquireSharedWaitForExclusive, ExIsResourceAcquiredExclusiveLite


InterlockedCompareExchange

PVOID
InterlockedCompareExchange(
IN OUT PVOID *Destination,
 IN PVOID Exchange,
IN PVOID Comperand
);

InterlockedCompareExchange compares a pointer value (Comperand) with a destination pointer value (Destination). If the values are equal, then a third pointer value (Exchange) is stored in the destination. Otherwise, no operation is performed. The compare and exchange operations are performed in an atomic operation.

Parameters

Destination
Points to a destination value that will be compared and possibly replaced.
Exchange
Points to the value that will replace the destination value if the comparison results in equality.
Comperand
Points to the value with which the destination value will be compared.

Return Value

InterlockedCompareExchange returns the original destination value.

Comments

InterlockedCompareExchange provides an atomic way to test a value and update it if the test passes. This routine is implemented inline by the compiler when appropriate and possible. It does not require a spin lock and can therefore be safely used on pageable data.

The InterlockedCompareExchange routine is atomic only with respect to other Interlocked Xxx calls.

Callers of InterlockedCompareExchange run at any IRQL.

See Also

InterlockedIncrement, InterlockedDecrement, InterlockedExchange


InterlockedDecrement

LONG
InterlockedDecrement(
IN PLONG Addend
);

Decrements a caller supplied variable of type LONG as an atomic operation.

Parameters

Addend
Points to a variable whose value is to be decremented.

Return Value

InterlockedDecrement returns the decremented value.

Comments

InterlockedDecrement should be used in place of the previous ExInterlockedDecrementLong. InterlockedDecrement is more efficient and faster. This routine is implemented inline by the compiler when appropriate and possible. The routine does not require a spin lock and can therefore be safely used on pageable data.

InterlockedDecrement is atomic only with respect to other InterlockedXxxxxx calls.

Callers of InterlockedDecrement run at any IRQL.

See Also

InterlockedIncrement, InterlockedExchange, ExInterlockedAddLargeInteger, ExInterlockedAddUlong


InterlockedExchange

LONG
InterlockedExchange(
IN OUT PLONG Target,
 IN LONG Value
);

InterlockedExchange sets an integer variable to a specified value as an atomic operation.

Parameters

Target
Points to a variable to be set to the supplied Value as an atomic operation.
Value
The caller-specified value to which Target is set.

Return Value

InterlockedExchange returns the original (unset) value of the Target variable.

Comments

This routine should be used in place of the previous ExInterlockedExchangeUlong. The new routine is faster and more efficient. It is implemented inline by the compiler when appropriate and possible. It does not require a spin lock and can therefore be safely used on pageable data.

The InterlockedExchange routine is atomic only with respect to other InterlockedXxxxxx calls.

Callers of InterlockedExchange run at any IRQL.

See Also

InterlockedIncrement, InterlockedDecrement, ExInterlockedAddLargeInteger, ExInterlockedAddUlong


InterlockedExchangeAdd

LONG
InterlockedExchangeAdd(
IN OUT PLONG Addend,
 IN LONG Value
);

InterlockedExchangeAdd adds a value to a given integer as an atomic operation and returns the initial value of the given integer.

Parameters

Addend
Points to an integer that will have Value added to it.
Value
Is the value to be added to Addend.

Return Value

InterlockedExchangeAdd returns the original (unsummed) value of the Addend variable.

Comments

This routine should be used in place of the previous ExInterlockedAddUlong. The new routine is faster and more efficient. It is implemented inline by the compiler when appropriate and possible. It does not require a spin lock and can therefore be safely used on pageable data.

The InterlockedExchangeAdd routine is atomic only with respect to other Interlocked Xxx calls.

Callers of InterlockedExchangeAdd run at any IRQL.

See Also

InterlockedIncrement, InterlockedDecrement, ExInterlockedAddLargeInteger, ExInterlockedAddUlong


InterlockedIncrement

LONG
InterlockedIncrement(
IN PLONG Addend
);

Increments a caller supplied variable of type LONG as an atomic operation.

Parameters

Addend
Points to a variable whose value is to be incremented.

Return Value

InterlockedIncrement returns the incremented value.

Comments

This routine should be used in place of the previous ExInterlockedIncrementLong. The new routine is more efficient and faster. It is implemented inline by the compiler when appropriate and possible. It does not require a spin lock and can therefore be safely used on pageable data.

The InterlockedIncrement routine is atomic only with respect to other InterlockedXxxxxx calls.

Callers of InterlockedIncrement run at any IRQL.

See Also

InterlockedDecrement, InterlockedExchange, ExInterlockedAddLargeInteger, ExInterlockedAddUlong


Chapter 3 Hardware Abstraction Layer Routines

References for the Hal Xxx routines and XXX_PORT/REGISTER macros are in alphabetical order. Click the browse button to view them.

For an overview of the functionality of these routines and macros, see Chapter 1.


HalAllocateCommonBuffer

PVOID
HalAllocateCommonBuffer(
IN PADAPTER_OBJECT AdapterObject,
 IN ULONG Length,
 OUT PPHYSICAL_ADDRESS LogicalAddress,
 IN BOOLEAN CacheEnabled
);

HalAllocateCommonBuffer allocates memory and maps it so that it is simultaneously accessible from both the processor and a device for DMA operations.

Parameters

AdapterObject
Points to the adapter object representing the busmaster adapter or DMA controller channel. This pointer was returned by HalGetAdapter.
Length
Specifies the number of bytes to allocate.
LogicalAddress
Points to a variable that receives the logical address the device can use to access the allocated buffer. Use this address rather than calling MmGetPhysicalAddress because the HAL can take into account any platform-specific memory restrictions.
CacheEnabled
Specifies whether the allocated memory can be cached.

Return Value

HalAllocateCommonBuffer returns the base virtual address of the allocated range. If the buffer cannot be allocated, it returns NULL.

Comments

HalAllocateCommonBuffer supports DMA in which the device and the processor are continuously communicating through system memory, as in a control structure for a busmaster DMA device.

HalAllocateCommonBuffer also supports slave devices whose drivers use a system DMA controller’s autoinitialize mode.

HalAllocateCommonBuffer does the following:

  • Allocates memory that can be reached from both the processor and the device. This memory appears contiguous to the device.

  • Allocates map registers to map the buffer, if required by the system.

  • Sets up a translation for the device, including loading map registers if necessary.

To use resident system memory economically, drivers should allocate as few of these buffers per device as possible. HalAllocateCommonBuffer allocates at least a page of memory, whatever the requested Length. For successful allocations requesting less than PAGE_SIZE, only the given Length can be accessed by the caller. For successful allocations requesting more than an integral multiple of PAGE_SIZE, any remaining bytes on the last allocated page are inaccessible to the caller.

If a driver needs several pages of common buffer space, but the pages need not be contiguous, the driver should make several one-page requests to HalAllocateCommonBuffer rather than one large request. This approach conserves contiguous memory.

Drivers typically call HalAllocateCommonBuffer during driver initialization. After initialization, it is possible that only one-page requests will succeed, if any.

Callers of HalAllocateCommonBuffer are typically running at IRQL PASSIVE_LEVEL. When called from IRQL_PASSIVE_LEVEL, HalAllocateCommonBuffer makes several attempts to locate the requested memory. HalAllocateCommonBuffer can be called from IRQL_DISPATCH_LEVEL, but less effort is made to locate the requested memory, and it is possible that only one-page requests will succeed, if any.

See Also

HalFreeCommonBuffer, HalGetAdapter


HalAssignSlotResources

NTSTATUS
HalAssignSlotResources(
IN PUNICODE_STRING RegistryPath,
 IN PUNICODE_STRING DriverClassName, /* optional */
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT DeviceObject, /* optional */
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
 IN ULONG SlotNumber,
 IN OUT PCM_RESOURCE_LIST *AllocatedResources
);

For drivers of devices connected to dynamically configurable I/O buses of a type with a published standard interface, HalAssignSlotResources determines the resource requirements of the target device, calls IoAssignResources to allocate them, sets the target device with its assigned resources, and returns the assignments to the caller.

Parameters

RegistryPath
Is the RegistryPath pointer passed in to the DriverEntry routine during initialization if the DeviceObject pointer is NULL. The buffered Unicode string is read-only, giving access to the driver-specific registry key \Registry\Machine\System\CurrentControlSet\Services\DriverName with its \Parameters subkey(s). The caller is responsible for creating and updating the RegistryPath string if it calls this routine to claim one or more sets of device-specific resources for the device(s) on a multifunction adapter at the given SlotNumber.
DriverClassName
Points to a Unicode string that describes the class of driver under which driver-specific information should be stored in the registry. A default type of Other is used if none is given, and a new key is created in the registry if a unique name is supplied. If this pointer is nonNULL, the caller is claiming a set of resources it uses in common for the device(s) at the given SlotNumber.
DriverObject
Points to the driver object that was input to the DriverEntry routine.
DeviceObject
Points to the driver-created device object representing the physical device for which the driver is attempting to claim device-specific resources. If this pointer is nonNULL, the caller usually supports more than one device on a multifunction adapter at the given SlotNumber. For each nonNULL DeviceObject, such a driver’s calls to this routine must write resource data into the registry under a unique, device-specific subkey of the RegistryPath pointer that was input to the DriverEntry routine.
BusType
Specifies the type of bus on which the device is connected. Currently, this value can be the following: PCIBus. However, additional types of dynamically configurable buses will be supported in future versions of Windows NT. The upper bound on the interface types supported is always MaximumInterfaceType.
BusNumber
Specifies the zero-based and system-assigned number of the bus.
SlotNumber
Specifies the logical slot number at which the driver found its device(s) when it called HalGetBusData.
*AllocatedResources
Specifies a pointer to an uninitialized pointer. If the call is successful, HalAssignSlotResources allocates a buffer, fills in the assigned resource list, and resets the embedded pointer to the start of the CM_RESOURCE_LIST buffer.

Return Value

HalAssignSlotResources returns STATUS_SUCCESS if a set of resources was claimed for the caller under the registry \ResourceMap key.

Comments

A driver cannot call this routine unless its device(s) are connected to a dynamically configurable I/O bus of a type that has a defined, publically documented, industry-standard interface. Drivers of devices that do not meet this condition can call IoAssignResources or IoReportResourceUsage instead.

The driver should save pertinent values from the AllocatedResources list so it can pass the appropriate values in calls to other configuration routines, such as HalGetInterrupt and HalGetAdapter. The caller is responsible for releasing the AllocatedResources buffer with ExFreePool.

Callers of HalAssignSlotResources must be running at IRQL PASSIVE_LEVEL.

See Also

CM_RESOURCE_LIST, ExFreePool, HalGetBusData, IoAssignResources


HalExamineMBR

VOID
HalExamineMBR(
IN PDEVICE_OBJECT DeviceObject,
 IN ULONG SectorSize,
 IN ULONG MBRTypeIdentifier,
 OUT PVOID Buffer,
 );

HalExamineMBR reads the master boot record (MBR) of a disk and returns data from the MBR if the MBR is of the type specified by the caller.

Parameters

DeviceObject
Points to the device object for the device being examined.
SectorSize
Specifies the minimum number of bytes that an I/O operation can fetch from the device being examined. If this value is less than 512, HalExamineMBR reads 512 bytes to ensure that it reads an entire partition table.
MBRTypeIdentifier
Specifies the type of MBR that may be on the disk.
Buffer
Points to a buffer that returns data from the MBR. The layout of the buffer depends on the MBRTypeIdentifier. The caller must deallocate this buffer as soon as possible with ExFreePool. This routine returns NULL in Buffer if the MBRTypeIdentifier of the disk does not match that specified by the caller or if there is an error.

Comments

Callers of HalExamineMBR must be running at IRQL PASSIVE_LEVEL.

See Also

ExFreePool


HalFreeCommonBuffer

VOID
HalFreeCommonBuffer(
IN PADAPTER_OBJECT AdapterObject,
 IN ULONG Length,
 IN PHYSICAL_ADDRESS LogicalAddress,
 IN PVOID VirtualAddress,
 IN BOOLEAN CacheEnabled
);

HalFreeCommonBuffer frees a common buffer and all resources it uses.

Parameters

AdapterObject
Points to the adapter object returned by the HalGetAdapter for which the common buffer was allocated.
Length
Specifies the number of bytes to deallocate.
LogicalAddress
Specifies the logical address of the allocated memory range.
VirtualAddress
Points to the corresponding virtual address of the allocated memory range.
CacheEnabled
Indicates whether the allocated memory is cached.

Comments

When a common buffer is to be released, the driver calls HalFreeCommonBuffer to unmap both its logical and virtual addresses. The parameters passed to HalFreeCommonBuffer must match exactly those passed to and returned from HalAllocateCommonBuffer. A driver cannot free part of an allocated common buffer.

Callers of HalFreeCommonBuffer must be running at IRQL <= DISPATCH_LEVEL. However, this routine is usually called when the driver is being unloaded and is running at IRQL PASSIVE_LEVEL.

See Also

HalAllocateCommonBuffer, HalGetAdapter


HalGetAdapter

PADAPTER_OBJECT
HalGetAdapter(
IN PDEVICE_DESCRIPTION DeviceDescription,
 IN OUT PULONG NumberOfMapRegisters
);

HalGetAdapter returns a pointer to the adapter object for the DMA device defined in the device description structure.

Parameters

DeviceDescription
Points to the following structure that describes the attributes of the device.
typedef struct _DEVICE_DESCRIPTION {
    ULONG Version;
        //Currently, DEVICE_DESCRIPTION_VERSION or
        //DEVICE_DESCRIPTION_VERSION1 to use IgnoreCount
    BOOLEAN Master;
        //TRUE = busmaster adapter
        //FALSE = slave DMA device
    BOOLEAN ScatterGather;
        //Device supports scatter/gather
    BOOLEAN DemandMode;
        //Uses system DMA controller's demand mode
    BOOLEAN AutoInitialize;
        //Uses system DMA controller's autoinit mode
    BOOLEAN Dma32BitAddresses;
    BOOLEAN IgnoreCount;
        //DMA controller in this platform does not maintain
        //an accurate transfer counter, so requires workaround,
        //and Version must be DEVICE_DESCRIPTION_VERSION1
    BOOLEAN Reserved1;
    BOOLEAN Reserved2;
        //Preceding Reserved members must be FALSE, and
        //Version must be DEVICE_DESCRIPTION_VERSION1
    ULONG BusNumber;
        //System-assigned value for I/O bus
    ULONG DmaChannel;
        //Slave device attached to this number channel
    INTERFACE_TYPE InterfaceType;
    DMA_WIDTH DmaWidth;    // 8-bit, 16-bit, etc.
    DMA_SPEED DmaSpeed;
    ULONG MaximumLength;
        //max number of bytes device can handle per DMA operation
    ULONG DmaPort;
        //MicroChannel-type bus port number
} DEVICE_DESCRIPTION, *PDEVICE_DESCRIPTION;
NumberOfMapRegisters
Returns the maximum number of map registers the device driver can allocate for any DMA transfer operation. The MaximumLength specified in the DeviceDescription effectively constrains the input value to no more than (MaximumLength / PAGE_SIZE + 1). On return, the updated value of this parameter can be less than the maximum possible input value.

Return Value

HalGetAdapter returns a pointer to the requested adapter object, or it returns NULL if an adapter object could not be created.

Comments

The caller should fill in values for all members of the device-description structure that are pertinent to its device. All other members must be set to zero before calling HalGetAdapter.

The InterfaceType specifies the bus interface. Its value can be one of the following: Internal, Isa, Eisa, MicroChannel, TurboChannel, or PCIBus. However, additional types of buses will be supported in future versions of Windows NT. The upper bound on the types of buses supported is always MaximumInterfaceType.

The DmaWidth specifies one of the following: Width8Bits, Width16Bits, or Width32Bits. The DmaSpeed specifies one of the following: Compatible, TypeA, TypeB, or TypeC.

Setting Version to DEVICE_DESCRIPTION_VERSION1 and IgnoreCount to TRUE indicates that the current platform’s DMA controller cannot be relied on to maintain an accurate transfer counter. In platforms with such a DMA controller, the HAL ignores the DMA counter but must take extra precautions to maintain data integrity during transfer operations. Using this workaround to compensate for such a deficient DMA controller degrades the speed of DMA transfers.

A successful caller must save the returned adapter object pointer in resident memory because this pointer is a required parameter to the DMA support routines. Usually, device drivers save this pointer in their device extensions.

HalGetAdapter must only be called during initialization when the caller is running at IRQL PASSIVE_LEVEL.

See Also

HalAllocateCommonBuffer, HalGetDmaAlignmentRequirement, IoAllocateAdapterChannel


HalGetBusData

ULONG
HalGetBusData(
IN BUS_DATA_TYPE BusDataType,
 IN ULONG BusNumber,
 IN ULONG SlotNumber,
 IN PVOID Buffer,
 IN ULONG Length
);

HalGetBusData obtains details about a given slot or address on a particular I/O bus.

Parameters

BusDataType
Specifies the type of bus data to be retrieved. Currently, its value can be one of the following: Cmos, EisaConfiguration, Pos, or PCIConfiguration. However, additional types of bus configuration will be supported in future versions of Windows NT. The upper bound on the types supported is always MaximumBusDataType.
BusNumber
Specifies the zero-based and system-assigned number of the bus in systems with several buses of the same BusDataType.
SlotNumber
Specifes the logical slot number or location of the device. If PCIConfiguration is specified as the BusDataType, this is specified as a PCI_SLOT_NUMBER-type value.
Buffer
Points to a caller-supplied buffer for configuration information specific to BusDataType.

If Cmos is specified, the buffer will contain the contents of the CMOS (bus number equals zero) or ECMOS (bus number equals one) locations starting with the location specified for SlotNumber.

If EisaConfiguration is specified, the buffer will contain the CM_EISA_SLOT_INFORMATION structure followed by zero or more CM_EISA_FUNCTION_INFORMATION structures for the specified slot.

If Pos is specified, the buffer will contain a CM_MCA_POS_DATA structure for the specified slot.

If PCIConfiguration is specified, the buffer will contain the PCI_COMMON_CONFIG information for the given SlotNumber.

Length
Specifies the maximum number of bytes to return in the Buffer.

Return Value

HalGetBusData returns the number of bytes of data it wrote in the given buffer. If the given BusDataType is not valid for the current platform, this routine returns zero.

When the input BusType is PCIConfiguration, HalGetBusData can return either of the following values to indicate an error:
Value | Meaning
0 (zero) | The specified PCI bus does not exist.
2 | The specified PCI bus exists, but there is no device at the given PCI SlotNumber. The Buffer also contains the value PCI_INVALID_VENDOR_ID at the PCI_COMMON_CONFIG VendorId member.

Comments

Device drivers call this routine during initialization to locate their devices on a particular I/O bus in the machine. The bus-type-specific configuration data returned is later used in calls to other configuration and initialization routines, such as HalTranslateBusAddress, HalGetInterrupt, HalGetAdapter, IoAssignResources, and so forth.

Calling HalGetBusData with any BusDataType except Cmos is the same as calling HalGetBusDataByOffset with an input Offset of zero.

If the input BusDataType is PCIConfiguration, callers of HalGetBusData can be running at IRQL <= DISPATCH_LEVEL. Otherwise, callers of HalGetBusData must be running at IRQL PASSIVE_LEVEL.

See Also

CM_EISA_FUNCTION_INFORMATION, CM_EISA_SLOT_INFORMATION, CM_MCA_POS_DATA, HalAssignSlotResources, HalGetAdapter, HalGetBusDataByOffset, HalGetInterruptVector, HalSetBusData, HalTranslateBusAddress, IoAssignResources, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER


HalGetBusDataByOffset

ULONG
HalGetBusDataByOffset(
IN BUS_DATA_TYPE BusDataType,
 IN ULONG BusNumber,
 IN ULONG SlotNumber,
 IN PVOID Buffer,
 IN ULONG Offset,
 IN ULONG Length
);

HalGetBusDataByOffset obtains details, starting at the given offset, about a given slot on an I/O bus.

Parameters

BusDataType
Specifies the type of bus data to be retrieved. Currently, its value can be the following: EisaConfiguration, Pos, or PCIConfiguration. However, additional types of dynamically configurable buses will be supported in future versions of Windows NT. The upper bound on the bus types supported is always MaximumBusDataType.
BusNumber
Specifies the zero-based and system-assigned number of the bus in systems with more than one bus of the same BusDataType.
SlotNumber
Specifes the logical slot number. When PCIConfiguration is specified, this is a PCI_SLOT_NUMBER-type value.
Buffer
Points to a caller-supplied buffer for configuration information specific to BusDataType

If EisaConfiguration is specified, the buffer will contain the CM_EISA_SLOT_INFORMATION structure followed by zero or more CM_EISA_FUNCTION_INFORMATION structures for the specified slot.

If Pos is specified, the buffer will contain a CM_MCA_POS_DATA structure for the specified slot.

When PCIConfiguration is specified, the buffer will contain some or all of the PCI_COMMON_CONFIG information for the given SlotNumber. The specified Offset and Length determine how much information is requested.

Offset
Specifies zero if the BusDataType is EisaConfiguration or Pos. Otherwise, specifies the byte offset in the PCI_COMMON_CONFIG structure for which the requested information should be returned. Callers can use the system-defined constant PCI_COMMON_HDR_LENGTH to specify the device-specific area of PCI_COMMON_CONFIG.
Length
Specifies the maximum number of bytes to return in the Buffer.

Return Value

HalGetBusDataByOffset returns the number of bytes of data it wrote in the given buffer. If the given BusDataType is not valid for the current platform, this routine returns zero.

When the input BusType is PCIConfiguration, HalGetBusDataByOffset can return either of the following values to indicate an error:
Value | Meaning
0 (zero) | The specified PCI bus does not exist.
2 | The specified PCI bus exists, but there is no device at the given PCI SlotNumber. The Buffer also contains the value PCI_INVALID_VENDOR_ID at the PCI_COMMON_CONFIG VendorId member.

Comments

Device drivers call this routine during initialization to locate their devices on a particular I/O bus in the machine. The bus-type-specific configuration data returned is later used in calls to other configuration and initialization routines, such as HalTranslateBusAddress, HalGetInterrupt, HalGetAdapter, IoAssignResources or HalAssignSlotResources, and so forth.

When accessing the device-specific area of the PCI configuration space, HalGetBusDataByOffset guarantees the following:

  • This routine never reads or writes data outside the range specified by the input Offset and Length.

  • Even if the input Length is exactly a byte or a (two-byte) word, this routine never accesses any data outside the requested range.

If the input BusDataType is PCIConfiguration, callers of HalGetBusDataByOffset can be running at IRQL <= DISPATCH_LEVEL. Otherwise, callers of HalGetBusDataByOffset must be running at IRQL PASSIVE_LEVEL.

See Also

HalAssignSlotResources, HalGetBusData, HalSetBusDataByOffset, HalTranslateBusAddress, IoAssignResources, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER


HalGetDmaAlignmentRequirement

ULONG
HalGetDmaAlignmentRequirement(
);

Return Value

HalGetDmaAlignmentRequirement returns the size of the cache boundary.

Comments

Device drivers can call this routine to determine alignment requirements for DMA buffers they allocate.

Callers of HalGetDmaAlignmentRequirement must be running at IRQL PASSIVE_LEVEL.

See Also

HalGetAdapter


HalGetInterruptVector

ULONG
HalGetInterruptVector(
IN INTERFACE_TYPE InterfaceType,
 IN ULONG BusNumber,
 IN ULONG BusInterruptLevel,
 IN ULONG BusInterruptVector,
 OUT PKIRQL Irql,
 OUT PKAFFINITY Affinity
);

HalGetInterruptVector returns a mapped system interrupt vector, interrupt level, and processor affinity mask that device drivers must pass to IoConnectInterrupt.

Parameters

InterfaceType
Specifies the bus interface type, which can be one of the following: Internal, Isa, Eisa, MicroChannel,TurboChannel, or PCIBus. However, additional types of buses will be suported in future versions of Windows NT. The upper bound on the types of buses supported is always MaximumInterfaceType.
BusNumber
Specifies the zero-based and system-assigned bus number of the bus for systems with more than one bus of the same InterfaceType.
BusInterruptLevel
Specifies the bus-specific interrupt level.
BusInterruptVector
Specifies the bus-specific interrupt vector.
Irql
Points to a variable that is set to the bus-relative IRQL. On return, this is reset to the DIRQL for the mapped vector.
Affinity
Points to a variable that is set to the system-assigned affinity mask. This mask determines the set of processors on which the device can interrupt.

Return Value

HalGetInterruptVector returns the mapped system vector.

Comments

HalGetInterruptVector returns the system-assigned interrupt vector, system-assigned DIRQL, and processor affinity for the corresponding bus-relative interrupt level and/or vector of the device. Callers can obtain the input values for these parameters with HalGetBusData, HalGetBusDataByOffset, IoQueryDeviceDescription, IoAssignResources, or HalAssignSlotResources, by querying the device, or by using default values if they cannot get hardware configuration information by other means.

Each bit set in the returned processor affinity mask indicates a processor to which the interrupt can be dispatched.

A device driver calls IoConnectInterrupt to initialize and connect an interrupt object and to register its ISR. This call includes parameters for the mapped vector, Irql, and Affinity obtained from HalGetInterruptVector, along with the address of the ISR and additional interrupt information. The mapped system vector, Irql, and Affinity values that HalGetInterruptVector returns must be passed, unchanged, to IoConnectInterrupt.

Callers of HalGetInterruptVector must be running at IRQL PASSIVE_LEVEL.

See Also

HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, IoAssignResources, IoQueryDeviceDescription, IoConnectInterrupt


HalQuerySystemInformation

This routine is reserved for future use. Use HalGetBusData or HalGetBusDataByOffset to get configuration information.


HalReadDmaCounter

ULONG
HalReadDmaCounter(
IN PADAPTER_OBJECT AdapterObject
);

HalReadDmaCounter returns the number of bytes remaining to be transferred during the current slave DMA operation, given a pointer to the adapter object.

Parameters

AdapterObject
Points to the adapter object representing the system DMA controller channel currently in use.

Return Value

HalReadDmaCounter returns the number of bytes remaining to be transferred.

Comments

HalReadDmaCounter can be called only by drivers of slave DMA devices. Usually, the caller is the driver of a slave device that uses a system DMA controller’s autoinitialize mode.

Callers of HalReadDmaCounter must be running at IRQL <= DISPATCH_LEVEL.

See Also

HalAllocateCommonBuffer, HalGetAdapter, IoFlushAdapterBuffers, IoMapTransfer


HalSetBusData

ULONG
HalSetBusData(
IN BUS_DATA_TYPE BusDataType,
 IN ULONG BusNumber,
 IN ULONG SlotNumber,
 IN PVOID Buffer,
 IN ULONG Length
);

HalSetBusData sets bus-configuration data for a given slot or address on a particular bus.

Parameters

BusDataType
Specifies the type of bus data to be set. Currently, its value can be the following: Cmos or PCIConfiguration. However, additional types of bus configuration will be supported in future versions of Windows NT. The upper bound on the bus types supported is always MaximumBusDataType.
BusNumber
Specifies the zero-based and system-assigned number of the bus in systems with more than one bus of the same BusDataType.
SlotNumber
Specifes the logical slot number for the device. When PCIConfiguration is specified, this is a PCI_SLOT_NUMBER-type value.
Buffer
Points to a caller-supplied buffer containing information specific to BusDataType.

When Cmos is specified, the buffer contains data to be written to CMOS (BusNumber equals zero) or ECMOS (BusNumber equals one) locations starting with the location specified by the SlotNumber.

When PCIConfiguration is specified, the buffer contains some or all of the PCI_COMMON_CONFIG information for the given SlotNumber. The specified Length determines how much information is supplied. Certain members of PCI_COMMON_CONFIG have read-only values, and the caller is responsible for preserving the system-supplied values of read-only members.

Length
Specifies the number of bytes of configuration data in Buffer.

Return Value

HalSetBusData returns the number of bytes of data successfully set for the given SlotNumber. If the given BusDataType is not valid for the current platform or if the supplied information is invalid, this routine returns zero.

Comments

Calling HalSetBusDataByOffset with a BusDataType of PCIConfiguration and an input Offset of zero is the same as calling HalSetBusData.

If the input BusDataType is PCIConfiguration, callers of HalSetBusData can be running at IRQL <= DISPATCH_LEVEL. Otherwise, callers of HalSetBusData must be running at IRQL PASSIVE_LEVEL.

See Also

HalGetBusData, HalGetBusDataByOffset, HalSetBusDataByOffset, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER


HalSetBusDataByOffset

ULONG
HalSetBusDataByOffset(
IN BUS_DATA_TYPE BusDataType,
 IN ULONG BusNumber,
 IN ULONG SlotNumber,
 IN PVOID Buffer,
 IN ULONG Offset,
 IN ULONG Length
);

HalSetBusDataByOffset sets bus-configuration data for a device on a dynamically configurable I/O bus with a published, standard interface.

Parameters

BusDataType
Specifies the type of bus data to be set. Currently, its value can be the following: PCIConfiguration. However, additional types of bus configuration will be supported in future versions of Windows NT. The upper bound on the bus types supported is always MaximumBusDataType.
BusNumber
Specifies the zero-based and system-assigned number of the bus in systems with more than one bus of the same BusDataType.
SlotNumber
Specifes the logical slot number. When PCIConfiguration is specified, this is a PCI_SLOT_NUMBER-type value.
Buffer
Points to a caller-supplied buffer with configuration information specific to BusDataType.

When PCIConfiguration is specified, the buffer contains some or all of the PCI_COMMON_CONFIG information for the given SlotNumber. The specified Offset and Length determine how much information is supplied. Certain members of PCI_COMMON_CONFIG have read-only values, and the caller is responsible for preserving the system-supplied values of read-only members.

Offset
Specifies the byte offset in the PCI_COMMON_CONFIG structure at which the caller-supplied configuration values begin. Callers can use the system-defined constant PCI_COMMON_HDR_LENGTH to specify the device-specific area of PCI_COMMON_CONFIG.
Length
Specifies the number of bytes in the Buffer.

Return Value

HalSetBusDataByOffset returns the number of bytes of data successfully set for the given SlotNumber. If the given BusDataType is not valid for the current platform or if the supplied information is invalid, this routine returns zero.

Comments

Callers of HalAssignSlotResources or IoAssignResources seldom call HalSetBusDataByOffset or HalSetBusData, because each of these Xx Assign Xxx routines sets up the configuration information for the given SlotNumber and returns the information to the caller. However, a driver can call HalSetBusDataByOffset or HalSetBusData if unusual circumstances or the nature of its device requires such a call. For example, a driver might call either of these routines to clear a bit in the PCI status register if its device signals a target abort during initialization.

When accessing the device-specific area of the PCI configuration space, HalSetBusDataByOffset guarantees the following:

  • This routine never reads or writes data outside the range specified by the input Offset and Length.

  • Even if the input Length is exactly a byte or a (two-byte) word, this routine never accesses any data outside the requested range.

If the input BusDataType is PCIConfiguration, callers of HalSetBusDataByOffset can be running at IRQL <= DISPATCH_LEVEL. Otherwise, callers of HalSetBusDataByOffset must be running at IRQL PASSIVE_LEVEL.

See Also

HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, HalTranslateBusAddress, IoAssignResources, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER


HalTranslateBusAddress

BOOLEAN
HalTranslateBusAddress(
IN INTERFACE_TYPE InterfaceType,
 IN ULONG BusNumber,
 IN PHYSICAL_ADDRESS BusAddress,
 IN OUT PULONG AddressSpace,
 OUT PPHYSICAL_ADDRESS TranslatedAddress
);

HalTranslateBusAddress translates a bus-specific address into the corresponding system logical address.

Parameters

InterfaceType
Specifies the bus interface type, which can be one of the following: Internal, Isa, Eisa, MicroChannel, TurboChannel, or PCIBus. However, additional types of buses will be supported in future versions of Windows NT. The upper bound on the types of buses supported is always MaximumInterfaceType.
BusNumber
Specifies the zero-based and system-assigned bus number for the device, used together with InterfaceType to identify the bus for systems with more than one bus of the same type.
BusAddress
Specifies the bus-relative address.
AddressSpace
In cross-platform-portable drivers, specifies whether the address is a port number or a memory address; AddressSpace 0x0 indicates memory, 0x1 is I/O space.
Translated Address
Is a pointer to the translated address.

Return Value

A return value of TRUE indicates the system logical address corresponding to the given BusNumber and BusAddress has been returned in TranslatedAddress.

Comments

The value returned for TranslatedAddress must be used in a subsequent call to MmMapIoSpace if the AddressSpace value of zero is returned.

There are many ways to connect a peripheral bus into a system. The memory address space of the bus, referred to as the logical address space, can be directly merged with the physical address space of the host, or some mapping may be involved. Also, some machines can have more than one bus, or a bus can have more than one address space, as in having separate memory and I/O addresses.

A device driver calls HalTranslateBusAddress to perform this translation. The parameters to this routine include a bus number to support platforms with more than one bus of the same InterfaceType, the bus address to be translated, and an AddressSpace specifier typically used to differentiate between memory and I/O space, if these are separate. The driver might obtain these parameters from the registry, by calling HalGetBusData or HalGetBusDataByOffset and interrogating the device hardware, by calling IoAssignResources or HalAssignSlotResources, or by using defaults and claiming them in the registry.

Callers of HalTranslateBusAddress must be running at IRQL <= DISPATCH_LEVEL. Usually, the caller is running at IRQL PASSIVE_LEVEL, because this routine is seldom called except when a driver is initializing.

See Also

HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, IoAssignResources, MmMapIoSpace


READ_PORT_BUFFER_UCHAR

VOID
READ_PORT_BUFFER_UCHAR(
IN PUCHAR Port,
 IN PUCHAR Buffer,
 IN ULONG Count
);

READ_PORT_BUFFER_UCHAR reads a number of bytes from the specified port address into a buffer.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Buffer
Points to a buffer into which an array of UCHAR values is read.
Count
Specifies the number of bytes to be read into the buffer.

Comments

The size of the buffer must be large enough to contain at least the specified number of bytes.

Callers of READ_PORT_BUFFER_UCHAR can be running at any IRQL, assuming the Buffer is resident and the Port is resident, mapped device memory.


READ_PORT_BUFFER_ULONG

VOID
READ_PORT_BUFFER_ULONG(
IN PULONG Port,
 IN PULONG Buffer,
 IN ULONG Count
);

READ_PORT_BUFFER_ULONG reads a number of ULONG values from the specified port address into a buffer.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Buffer
Points to a buffer into which an array of ULONG values is read.
Count
Specifies the number of ULONG values to be read into the buffer.

Comments

The size of the buffer must be large enough to contain at least the specified number of ULONG values.

Callers of READ_PORT_BUFFER_ULONG can be running at any IRQL, assuming the Buffer is resident and the Port is resident, mapped device memory.


READ_PORT_BUFFER_USHORT

VOID
READ_PORT_BUFFER_USHORT(
IN PUSHORT Port,
 IN PUSHORT Buffer,
 IN ULONG Count
);

READ_PORT_BUFFER_USHORT reads a number of USHORT values from the specified port address into a buffer.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Buffer
Points to a buffer into which an array of USHORT values is read.
Count
Specifies the number of USHORT values to be read into the buffer.

Comments

The size of the buffer must be large enough to contain at least the specified number of USHORT values.

Callers of READ_PORT_BUFFER_USHORT can be running at any IRQL, assuming the Buffer is resident and the Port is resident, mapped device memory.


READ_PORT_UCHAR

UCHAR
READ_PORT_UCHAR(
IN PUCHAR Port
);

READ_PORT_UCHAR reads a byte from the specified port address.

Parameters

Port
Points to the port address, which must be a mapped memory range in I/O space.

Return Value

READ_PORT_UCHAR returns the byte read from the specified port address.

Comments

Callers of READ_PORT_UCHAR can be running at any IRQL, assuming the Port is resident, mapped device memory.


READ_PORT_ULONG

ULONG
READ_PORT_ULONG(
IN PULONG Port
);

READ_PORT_ULONG reads a ULONG value from the specified port address.

Parameters

Port
Points to the port address, which must be a mapped range in I/O space.

Return Value

READ_PORT_ULONG returns the ULONG value read from the specified port address.

Comments

Callers of READ_PORT_ULONG can be running at any IRQL, assuming the Port is resident, mapped device memory.


READ_PORT_USHORT

USHORT
READ_PORT_USHORT(
IN PUSHORT Port
);

READ_PORT_USHORT reads a USHORT value from the specified port address.

Parameters

Port
Points to the port address, which must be a mapped range in I/O space.

Return Value

READ_PORT_USHORT returns the USHORT value read from the specified port address.

Comments

Callers of READ_PORT_USHORT can be running at any IRQL, assuming the Port is resident, mapped device memory.


READ_REGISTER_BUFFER_UCHAR

VOID
READ_REGISTER_BUFFER_UCHAR(
IN PUCHAR Register,
 IN PUCHAR Buffer,
 IN ULONG Count
);

READ_REGISTER_BUFFER_UCHAR reads a number of bytes from the specified register address into a buffer.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Buffer
Points to a buffer into which an array of UCHAR values is read.
Count
Specifies the number of bytes to be read into the buffer.

Comments

The size of the buffer must be large enough to contain at least the specified number of bytes.

Callers of READ_REGISTER_BUFFER_UCHAR can be running at any IRQL, assuming the Buffer is resident and the Register is resident, mapped device memory.


READ_REGISTER_BUFFER_ULONG

VOID
READ_REGISTER_BUFFER_ULONG(
IN PULONG Register,
 IN PULONG Buffer,
 IN ULONG Count
);

READ_REGISTER_BUFFER_ULONG reads a number of ULONG values from the specified register address into a buffer.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Buffer
Points to a buffer into which an array of ULONG values is read.
Count
Specifies the number of ULONG values to be read into the buffer.

Comments

The size of the buffer must be large enough to contain at least the specified number of ULONG values.

Callers of READ_REGISTER_BUFFER_ULONG can be running at any IRQL, assuming the Buffer is resident and the Register is resident, mapped device memory.


READ_REGISTER_BUFFER_USHORT

VOID
READ_REGISTER_BUFFER_USHORT(
IN PUSHORT Register,
 IN PUSHORT Buffer,
 IN ULONG Count
);

READ_REGISTER_BUFFER_USHORT reads a number of USHORT values from the specified register address into a buffer.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Buffer
Points to a buffer into which an array of USHORT values is read.
Count
Specifies the number of USHORT values to be read into the buffer.

Comments

The size of the buffer must be large enough to contain at least the specified number of USHORT values.

Callers of READ_REGISTER_BUFFER_USHORT can be running at any IRQL, assuming the Buffer is resident and the Register is resident, mapped device memory.


READ_REGISTER_UCHAR

UCHAR
READ_REGISTER_UCHAR(
IN PUCHAR Register
);

READ_REGISTER_UCHAR reads a byte from the specified register address.

Parameters

Register
Points to the register address, which must be a mapped range in memory space.

Return Value

READ_REGISTER_UCHAR returns the byte read from the specified register address.

Comments

Callers of READ_REGISTER_UCHAR can be running at any IRQL, assuming the Register is resident, mapped device memory.


READ_REGISTER_ULONG

ULONG
READ_REGISTER_ULONG(
IN PULONG Register
);

READ_REGISTER_ULONG reads a ULONG value from the specified register address.

Parameters

Register
Points to the register address, which must be a mapped range in memory space.

Return Value

READ_REGISTER_ULONG returns the ULONG value read from the specified register address.

Comments

Callers of READ_REGISTER_ULONG can be running at any IRQL, assuming the Register is resident, mapped device memory.


READ_REGISTER_USHORT

USHORT
READ_REGISTER_USHORT(
IN PUSHORT Register
);

READ_REGISTER_USHORT reads a USHORT value from the specified register address.

Parameters

Register
Points to the register address, which must be a mapped range in memory space.

Return Value

READ_REGISTER_USHORT returns the USHORT value read from the specified register address.

Comments

Callers of READ_REGISTER_USHORT can be running at any IRQL, assuming the Register is resident, mapped device memory.


WRITE_PORT_BUFFER_UCHAR

VOID
WRITE_PORT_BUFFER_UCHAR(
IN PUCHAR Port,
 IN PUCHAR Buffer,
 IN ULONG Count
);

WRITE_PORT_BUFFER_UCHAR writes a number of bytes from a buffer to the specified port.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Buffer
Points to a buffer from which an array of UCHAR values is to be written.
Count
Specifies the number of bytes to be written to the port.

Comments

The size of the buffer must be large enough to contain at least the specified number of bytes.

Callers of WRITE_PORT_BUFFER_UCHAR can be running at any IRQL, assuming the Buffer is resident and the Port is resident, mapped device memory.


WRITE_PORT_BUFFER_ULONG

VOID
WRITE_PORT_BUFFER_ULONG(
IN PULONG Port,
 IN PULONG Buffer,
 IN ULONG Count
);

WRITE_PORT_BUFFER_ULONG writes a number of ULONG values from a buffer to the specified port address.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Buffer
Points to a buffer from which an array of ULONG values is to be written.
Count
Specifies the number of ULONG values to be written to the port.

Comments

The size of the buffer must be large enough to contain at least the specified number of ULONGs.

Callers of WRITE_PORT_BUFFER_ULONG can be running at any IRQL, assuming the Buffer is resident and the Port is resident, mapped device memory.


WRITE_PORT_BUFFER_USHORT

VOID
WRITE_PORT_BUFFER_USHORT(
IN PUSHORT Port,
 IN PUSHORT Buffer,
 IN ULONG Count
);

WRITE_PORT_BUFFER_USHORT writes a number of USHORT values from a buffer to the specified port address.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Buffer
Points to a buffer from which an array of USHORT values is to be written.
Count
Specifies the number of USHORT values to be written to the port.

Comments

The size of the buffer must be large enough to contain at least the specified number of USHORTs.

Callers of WRITE_PORT_BUFFER_USHORT can be running at any IRQL, assuming the Buffer is resident and the Port is resident, mapped device memory.


WRITE_PORT_UCHAR

VOID
WRITE_PORT_UCHAR(
IN PUCHAR Port,
 IN UCHAR Value
);

WRITE_PORT_UCHAR writes a byte to the specified port address.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Value
Specifies a byte to be written to the port.

Comments

Callers of WRITE_PORT_UCHAR can be running at any IRQL, assuming the Port is resident, mapped device memory.


WRITE_PORT_ULONG

VOID
WRITE_PORT_ULONG(
IN PULONG Port,
 IN ULONG Value
);

WRITE_PORT_ULONG writes a ULONG value to the specified port address.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Value
Specifies a ULONG value to be written to the port.

Comments

Callers of WRITE_PORT_ULONG can be running at any IRQL, assuming the Port is resident, mapped device memory.


WRITE_PORT_USHORT

VOID
WRITE_PORT_USHORT(
IN PUSHORT Port,
 IN USHORT Value
);

WRITE_PORT_USHORT writes a USHORT value to the specified port address.

Parameters

Port
Points to the port, which must be a mapped memory range in I/O space.
Value
Specifies a USHORT value to be written to the port.

Comments

Callers of WRITE_PORT_USHORT can be running at any IRQL, assuming the Port is resident, mapped device memory.


WRITE_REGISTER_BUFFER_UCHAR

VOID
WRITE_REGISTER_BUFFER_UCHAR(
IN PUCHAR Register,
 IN PUCHAR Buffer,
 IN ULONG Count
);

WRITE_REGISTER_BUFFER_UCHAR writes a number of bytes from a buffer to the specified register.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Buffer
Points to a buffer from which an array of UCHAR values is to be written.
Count
Specifies the number of bytes to be written to the register.

Comments

The size of the buffer must be large enough to contain at least the specified number of bytes.

Callers of WRITE_REGISTER_BUFFER_UCHAR can be running at any IRQL, assuming the Buffer is resident and the Register is resident, mapped device memory.


WRITE_REGISTER_BUFFER_ULONG

VOID
WRITE_REGISTER_BUFFER_ULONG(
IN PULONG Register,
 IN PULONG Buffer,
 IN ULONG Count
);

WRITE_REGISTER_BUFFER_ULONG writes a number of ULONG values from a buffer to the specified register.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Buffer
Points to a buffer from which an array of ULONG values is to be written.
Count
Specifies the number of ULONG values to be written to the register.

Comments

The size of the buffer must be large enough to contain at least the specified number of ULONGs.

Callers of WRITE_REGISTER_BUFFER_ULONG can be running at any IRQL, assuming the Buffer is resident and the Register is resident, mapped device memory.


WRITE_REGISTER_BUFFER_USHORT

VOID
WRITE_REGISTER_BUFFER_USHORT(
IN PUSHORT Register,
 IN PUSHORT Buffer,
 IN ULONG Count
);

WRITE_REGISTER_BUFFER_USHORT writes a number of USHORT values from a buffer to the specified register.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Buffer
Points to a buffer from which an array of USHORT values is to be written.
Count
Specifies the number of USHORT values to be written to the register.

Comments

The size of the buffer must be large enough to contain at least the specified number of USHORTs.

Callers of WRITE_REGISTER_BUFFER_USHORT can be running at any IRQL, assuming the Buffer is resident and the Register is resident, mapped device memory.


WRITE_REGISTER_UCHAR

VOID
WRITE_REGISTER_UCHAR(
IN PUCHAR Register,
 IN UCHAR Value
);

WRITE_REGISTER_UCHAR writes a byte to the specified address.

Parameters

Register
Points to the register, which must be a mapped range in memory space.
Value
Specifies a byte to be written to the register.

Comments

Callers of WRITE_REGISTER_UCHAR can be running at any IRQL, assuming the Register is resident, mapped device memory.


WRITE_REGISTER_ULONG

VOID
WRITE_REGISTER_ULONG(
IN PULONG Register,
 IN ULONG Value
);

WRITE_REGISTER_ULONG writes a ULONG value to the specified address.

Parameters

Register
Points to the register which must be a mapped range in memory space.
Value
Specifies a ULONG value to be written to the register.

Comments

Callers of WRITE_REGISTER_ULONG can be running at any IRQL, assuming the Register is resident, mapped device memory.


WRITE_REGISTER_USHORT

VOID
WRITE_REGISTER_USHORT(
IN PUSHORT Register,
 IN USHORT Value
);

WRITE_REGISTER_USHORT writes a USHORT value to the specified address.

Parameters

Register
Points to the register, which must be a mapped range in memory space.

Value

Specifies a USHORT value to be written to the register.

Comments

Callers of WRITE_REGISTER_USHORT can be running at any IRQL, assuming the Register is resident, mapped device memory.


Chapter 4 I/O Manager Routines

All kernel-mode drivers except video and SCSI miniport drivers and NDIS drivers call Io Xxx routines.

References for the Io Xxx routines are in alphabetical order.

For an overview of the functionality of these routines, see Chapter 1.


IoAcquireCancelSpinLock

VOID
IoAcquireCancelSpinLock(
OUT PKIRQL Irql
);

IoAcquireCancelSpinLock synchronizes cancelable-state transitions for IRPs in a multiprocessor-safe way.

Parameters

Irql
Points to a variable in which to save the current IRQL for a subsequent call to IoReleaseCancelSpinLock. Usually, the Irql is saved on the stack as a local variable.

Comments

A driver that uses the I/O-manager-supplied device queues in the device object must be holding the cancel spin lock whenever it changes the cancelable state of an IRP with IoSetCancelRoutine.

A driver that manages its own queue(s) of IRPs does not need to hold the cancel spin lock when calling IoSetCancelRoutine.

The holder of the cancel spin lock should release it promptly by calling IoReleaseCancelSpinLock.

A driver-supplied Cancel routine is called with the cancel spin lock held. It must release the cancel spin lock when it has completed the IRP to be cancelled.

Callers of IoAcquireCancelSpinLock must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoReleaseCancelSpinlock, IoSetCancelRoutine


IoAllocateAdapterChannel

NTSTATUS
IoAllocateAdapterChannel(
IN PADAPTER_OBJECT AdapterObject,
 IN PDEVICE_OBJECT DeviceObject,
 IN ULONG NumberOfMapRegisters,
 IN PDRIVER_CONTROL ExecutionRoutine,
 IN PVOID Context
);

IoAllocateAdapterChannel allocates an adapter object for a DMA operation on the target device object and calls a driver-supplied routine to carry out an I/O operation through the system DMA controller or a busmaster adapter as soon as the appropriate DMA channel is available and/or any necessary map registers are available.

Parameters

AdapterObject
Points to an adapter object, representing the adapter channel or busmaster adapter to be allocated. The driver obtained this pointer by calling HalGetAdapter.
DeviceObject
Points to the device object, representing the target device for a requested DMA operation.
NumberOfMapRegisters
Specifies the number of map registers. This value is the lesser of (the number of map registers needed to satisfy the current transfer request) or (the number of available map registers returned when the driver called HalGetAdapter).
ExecutionRoutine
Points to a driver-supplied AdapterControl routine to be called as soon the system DMA controller or busmaster adapter is available. This routine is declared as follows:
IO_ALLOCATION_ACTION
(*PDRIVER_CONTROL)(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID MapRegisterBase,
    IN PVOID Context
    );
Context
Points to the driver-determined context passed to the driver’s AdapterControl routine when it is called.

Return Value

This routine can return one of the following NTSTATUS values:
Value | Meaning
STATUS_SUCCESS | The adapter object has been allocated.
STATUS_INSUFFICIENT_RESOURCES | The NumberOfMapRegisters is larger than the value returned by HalGetAdapter.

Comments

This routine reserves exclusive access to a DMA controller channel and/or map registers for the one or more DMA operations required to satisfy the current IRP’s transfer request for the specified device.

If the system DMA controller or busmaster adapter is already busy, the driver-supplied AdapterControl routine is queued. Otherwise, the driver supplied AdapterControl routine is called immediately.

IoAllocateAdapterChannel passes an Irp to the caller’s AdapterControl routine only if this routine is called from the StartIo routine to process the same IRP passed in to the StartIo routine. Otherwise, the Irp has no meaning, and a driver should consider the Irp a system-reserved parameter to its AdaperControl routine.

The return value of the AdapterControl routine depends on whether the device is a busmaster or uses system DMA. Drivers of busmaster devices return DeallocateObjectKeepRegisters; drivers of slave devices return KeepObject.

Callers of IoAllocateAdapterChannel must be running at IRQL DISPATCH_LEVEL.

See Also

HalGetAdapter, HalReadDmaCounter, IoFlushAdapterBuffers, IoFreeAdapterChannel, IoFreeMapRegisters, IoMapTransfer, KeFlushIoBuffers


IoAllocateController

VOID
IoAllocateController(
IN PCONTROLLER_OBJECT ControllerObject,
 IN PDEVICE_OBJECT DeviceObject,
 IN PDRIVER_CONTROL ExecutionRoutine,
 IN PVOID Context
);

IoAllocateController sets up the call to a driver-supplied ControllerControl routine as soon as the device controller, represented by the given controller object, is available to carry out an I/O operation for the target device, represented by the given device object.

Parameters

ControllerObject
Points to a driver-created controller object, usually representing a physical controller to be allocated for an I/O operation on an attached device.
DeviceObject
Points to the device object, representing the target device of the current IRP.
ExecutionRoutine
Points to the driver-supplied ControllerControl routine. This routine is declared as follows:
IO_ALLOCATION_ACTION
(*PDRIVER_CONTROL)(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID MapRegisterBase,
    IN PVOID Context
    );

The MapRegisterBase pointer is a system-reserved value.

Context
Points to a driver-determined context, passed to the driver’s ControllerControl routine when it is called.

Comments

This routine reserves exclusive access to the hardware controller for the specified device.

The ControllerControl routine returns a value indicating whether the controller remains allocated to the device, either DeallocateObject or KeepObject. If it returns KeepObject, the driver must subsequently call IoFreeController to release the controller object.

Callers of IoAllocateController must be running at IRQL DISPATCH_LEVEL.

See Also

IoCreateController, IoDeleteController, IoFreeController


IoAllocateErrorLogEntry

PVOID
IoAllocateErrorLogEntry(
IN PVOID IoObject,
 IN UCHAR EntrySize
);

IoAllocateErrorLogEntry allocates an error log packet and returns a pointer to the packet so the caller can supply information about an I/O error.

Parameters

IoObject
Points to a device object representing the device on which an I/O error occurred, or to a driver object representing the driver that found an error.
EntrySize
Specifies the size in bytes of the packet to be allocated. This value cannot exceed ERROR_LOG_MAXIMUM_SIZE.

Return Value

IoAllocateErrorLogEntry returns a pointer to the allocated packet in which the driver sets up the error data, or NULL if a packet could not be allocated.

Comments

The driver must fill in the packet with information about the error and, then, call IoWriteErrorLogEntry to post the entry to the error log.

The value supplied for EntrySize depends on how much DumpData the driver includes in the error log packet, as well as any InsertionStrings it provides for the error. In general, the value of EntrySize is the following:

sizeof(IO_ERROR_LOG_PACKET) + (n * sizeof(ULONG))
        + sizeof(InsertionStrings)

Where n depends on how much DumpData the driver supplies with the error. If IoAllocateErrorLogPacket returns NULL, the driver should continue to run and assume that, if the same error occurs again, the driver can log the error at that point. The returned error log packet is defined as follows:

typedef struct _IO_ERROR_LOG_PACKET (
    UCHAR MajorFunctionCode;
    UCHAR RetryCount;
    USHORT DumpDataSize;
    USHORT NumberOfStrings;
    USHORT StringOffset;
    USHORT EventCategory;
    NTSTATUS ErrorCode;
    ULONG UniqueErrorValue;
    NTSTATUS FinalStatus;
    ULONG SequenceNumber;
    ULONG IoControlCode;
    LARGE_INTEGER DeviceOffset;
    ULONG DumpData[1];
}IO_ERROR_LOG_PACKET, *PIO_ERROR_LOG_PACKET;

Caller-supplied data in the packet includes the following:
Member | Meaning
MajorFunctionCode | The IRP_MJ_XXX of the current IRP.
RetryCount | A count of how many times the caller has retried the operation and encountered the same error.
DumpDataSize | The number of bytes of caller-suplied DumpData, if any.
NumberOfStrings | The number of insertion strings supplied following the DumpData; the initial string, if any, is assumed to be the driver name or that of the target device object.
StringOffset | The offset, immediately following any DumpData, of the caller-supplied, zero-terminated Unicode insertion strings, if any.
EventCategory | A caller-determined value matching that in the caller’s message file for categories if the driver set itself up in the registry as an event-logging component; otherwise, zero.
ErrorCode | An IO_ERR_XXX value.
UniqueErrorValue | A driver-determined value that indicates where in the driver the error was encountered.
FinalStatus | The STATUS_XXX value to be set in the I/O status block of the IRP or a STATUS_XXX returned by a support routine.
SequenceNumber | A driver-assigned number for the current IRP, constant for the life of the request.
IoControlCode | An IOCTL_XXX if MajorFunctionCode is IRP_MJ_DEVICE_CONTROL, IRP_MJ_INTERNAL_DEVICE_CONTROL, or IRP_MJ_SCSI; otherwise, zero.
DeviceOffset | The offset on the device where the error occurred, or zero.
DumpData | Caller-supplied data, such as device register contents, if DumpDataSize is nonzero.

The I/O Manager uses the first insertion string for the device or driver name derived from the IoObject.

Callers of IoAllocateErrorLogEntry must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoWriteErrorLogEntry


IoAllocateIrp

PIRP
IoAllocateIrp(
IN CCHAR StackSize,
 IN BOOLEAN ChargeQuota
);

IoAllocateIrp allocates an IRP, given the number of I/O stack locations for each driver layered under the caller, and, optionally, for the caller.

Parameters

StackSize
Specifies the number of I/O stack locations to be allocated for the IRP. This value must be at least equal to the StackSize of the next-lower driver’s device object, but can be one greater than this value. The calling driver need not allocate a stack location in the IRP for itself.
ChargeQuota
Should be set to FALSE by intermediate drivers. This can be set to TRUE only by highest-level drivers that are called in the context of the thread that originates the I/O request for which the driver is allocating another IRP.

Return Value

IoAllocateIrp returns a pointer to an IRP, which was allocated from nonpaged system space, or NULL if an IRP could not be allocated.

Comments

An intermediate or highest-level driver can call IoAllocateIrp to create IRPs for requests it sends to lower-level drivers. Such a driver must initialize the IRP and must set its IoCompletion routine in the IRP it creates so the caller can dispose of the IRP when lower-level drivers have completed processing of the request.

An intermediate or highest-level driver also can call IoBuildDeviceIoControlRequest, IoBuildAsynchronousFsdRequest or IoBuildSynchronousFsdRequest to set up requests it sends to lower-level drivers. Only a highest-level driver can call IoMakeAssociatedIrp.

Callers of IoAllocateIrp must be running at IRQL <= DISPATCH_LEVEL.

See Also

IO_STACK_LOCATION, IoBuildAsynchronousFsdRequest, IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IoFreeIrp, IoInitializeIrp, IoMakeAssociatedIrp, IoSetCompletionRoutine, IRP


IoAllocateMdl

PMDL
IoAllocateMdl(
IN PVOID VirtualAddress,
 IN ULONG Length,
 IN BOOLEAN SecondaryBuffer,
 IN BOOLEAN ChargeQuota,
 IN OUT PIRP Irp /* optional */
);

IoAllocateMdl allocates an MDL large enough to map a buffer, given its starting address and length. Optionally, this routine associates the MDL with an IRP.

Parameters

VirtualAddress
Points to the base virtual address of the buffer the MDL is to describe.
Length
Specifies the length in bytes of the buffer the MDL is to describe.
SecondaryBuffer
Indicates whether the buffer is a primary or secondary buffer. This parameter determines how the MDL is to be linked to the IRP. All buffers except the first buffer described by an MDL in an IRP are considered secondary buffers. This field must be FALSE if no IRP is associated with the MDL.
ChargeQuota
Should be set to FALSE by intermediate drivers. This can be set to TRUE only by highest-level drivers that are called in the context of the thread that originates the I/O request for which the driver is allocating another IRP.
Irp
Points to an IRP to be associated with the MDL. If the Irp pointer is nonNULL, the allocated MDL is associated with the specified IRP’s MDL list, according to the value of SecondaryBuffer.

Return Value

IoAllocateMdl returns a pointer to an MDL or a NULL pointer if the MDL cannot be allocated.

Comments

IoAllocateMdl can be used by a driver that needs to break a buffer into pieces, each mapped by a separate MDL, or to map a driver-allocated buffer. The driver should call MmBuildMdlForNonPagedPool with the MDL allocated by this call to set up an MDL describing a driver-allocated buffer in nonpaged pool.

Callers of IoAllocateMdl must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoBuildPartialMdl, IoFreeMdl, MmBuildMdlForNonPagedPool


IoAssignArcName

VOID
IoAssignArcName(
IN PUNICODE_STRING ArcName,
 IN PUNICODE_STRING DeviceName
);

IoAssignArcName creates a symbolic link between the ARC name of a physical device and the name of the corresponding device object when it has been created.

Parameters

ArcName
Points to a buffer containing the ARC name of the device. The ARC name must be a Unicode string.
DeviceName
Points to a buffer containing the name of the device object, representing the same device. The device object name must be a Unicode string.

Comments

Drivers of hard disk devices need not call this routine. Drivers of other mass-storage devices, including floppy, CD_ROM, and tape devices, should call IoAssignArcName during their initialization.

Callers of IoAssignArcName must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateDevice


IoAssignResources

NTSTATUS
IoAssignResources(
IN PUNICODE_STRING RegistryPath,
 IN PUNICODE_STRING DriverClassName, /* optional */
IN PDRIVER_OBJECT DriverObject,
 IN PDEVICE_OBJECT DeviceObject, /* optional */
IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources,
 IN OUT PCM_RESOURCE_LIST *AllocatedResources
);

IoAssignResources takes an input list of requested hardware resources for a driver or device, claims an available set of hardware resources, such as an interrupt vector, device memory range and/or I/O port range, and possibly a particular DMA controller channel, in the \Registry\Hardware\Machine\ResourceMap tree, and returns a list of allocated hardware resources for the driver or device. As an alternative, drivers of PCI-type devices can call HalAssignSlotResources.

Parameters

RegistryPath
Points to the \Registry\Machine\System\CurrentControlSet\Services\DriverName key or one of its subkeys, depending on whether the input DeviceObject pointer is NULL. If a driver uses resources in common for all its devices, RegistryPath is the pointer input to its DriverEntry routine and the DeviceObject pointer must be NULL. A driver that needs device-specific hardware resources, rather than driver-specific resources in common for all its devices, must pass a RegistryPath pointer to an updated, device-specific string naming a subkey of DriverName, at each call to IoAssignResources with a nonNULL pointer to a unique DeviceObject.
DriverClassName
Points to a buffered Unicode string that describes the class of driver under which the driver’s configuration information should be stored. A default type Other is used if none is given, and a new key is created in the registry if a unique name is supplied.
DriverObject
Points to the driver object that was input to the DriverEntry routine.
DeviceObject
This pointer is optional. If it is NULL, the caller-supplied RequestedResources list specifies resources that the driver itself needs, possibly to control several devices that it supports. Otherwise, DeviceObject points to the driver-created device object representing a physical device for which the driver is attempting to claim device-specific hardware resources.
RequestedResources
Points to a caller-supplied list of hardware resources needed to control its device(s), which the driver has found by calling HalGetBusData, HalGetBusDataByOffset, by interrogating its device, or by some other means. The caller must allocate a buffer, which can be paged memory, for its IO_RESOURCE_REQUIREMENTS_LIST(s), defined by the following structures:
typedef struct _IO_RESOURCE_REQUIREMENTS_LIST {
    //
    // This structure identifies the location on the I/O bus of the device
    // or multifunction adapter for which the driver is supplying
    // a variable-sized IO_RESOURCE_LIST requesting specific hardware resources.
    //
    ULONG ListSize;                  // in bytes
    INTERFACE_TYPE InterfaceType;    // system-defined enum for the bus, one of
                                     // Internal, Isa, Eisa, MicroChannel, TurboChannel
                                     // or PCIBus
    ULONG BusNumber;                 // system-assigned and zero-based number of this bus
    ULONG SlotNumber;                // logical slot or location on the bus of the device
    ULONG Reserved[3];               // for system use
    ULONG AlternativeLists;          // count of alternative IO_RESOURCE_LIST
                                     // elements with IO_RESOURCE_DESCRIPTORs
    IO_RESOURCE_LIST List[1];        // contains a variable-sized array of descriptors
} IO_RESOURCE_REQUIREMENTS_LIST, *PIO_RESOURCE_REQUIREMENTS_LIST

A driver can supply any number of IO_RESOURCE_LIST elements, each containing IO_RESOURCE_DESCRIPTORs specifying both preferred and alternative hardware resources the driver can use, if the device or I/O bus does not constrain that driver to using a fixed range of I/O ports or device memory, a fixed bus-specific interrupt vector, and/or a particular DMA channel or port number. In particular, drivers of devices that can be configured to use alternate sets of hardware resources are expected to take advantage of this capability, although drivers of PCI-type devices can call HalAssignSlotResources instead. If IoAssignResources cannot claim a preferred set of resources, it tries an alternative set and returns the set of resources claimed as soon as it can satisfy the request with a given alternate resource list. The IO_RESOURCE_LIST structure is defined as follows:

typedef struct _IO_RESOURCE_LIST {
    USHORT Version;                  // of this structure, currently 1
    USHORT Revision;                 // of this structure, currently 1
    ULONG Count;                     // of IO_RESOURCE_DESCRIPTORs in array
    IO_RESOURCE_DESCRIPTOR Descriptors[1];
} IO_RESOURCE_LIST, *PIO_RESOURCE_LIST

Each descriptor in the IO_RESOURCE_LIST’s array specifies a single hardware resource that the driver needs, prefers, or can use as an alternative to that specified in another array element. Members of the IO_RESOURCE_DESCRIPTOR structure are described separately, following this definition of the structure itself:

typedef struct _IO_RESOURCE_DESCRIPTOR {
    UCHAR Option;
    UCHAR Type;
    UCHAR ShareDisposition;
    UCHAR Spare1;                               // reserved for system use
    USHORT Flags;
    USHORT Spare2;                              // reserved for system use

    union{
         struct {
             ULONG Length;
             ULONG Alignment;
             PHYSICAL_ADDRESS MinimumAddress;  // 8-byte, bus-relative physical address
             PHYSICAL_ADDRESS MaximumAddress;
         } Port;
         struct {
             ULONG Length;
             ULONG Alignment;
             PHYSICAL_ADDRESS MinimumAddress;  // 8-byte, bus-relative physical address
             PHYSICAL_ADDRESS MaximumAddress;
         } Memory;
         struct {
             ULONG MinimumVector;
             ULONG MaximumVector;
         } Interrupt;
         struct {
             ULONG MinumumChannel;
             ULONG MaximumChannel;
         } Dma;
     } u;
} IO_RESOURCE_DESCRIPTOR, *PIO_RESOURCE_DESCRIPTOR

Members

Members of the IO_RESOURCE_DESCRIPTOR structure include the following:
Option

Is set to IO_RESOURCE_PREFERRED to indicate the caller’s preferred setting for the described resource if other descriptors in the list for the same type of resource have this member set to IO_RESOURCE_ALTERNATIVE. This member should be set to zero if the resource described is fixed: that is, the caller cannot use an alternative.

Type
Specifies which of the following is contained in this descriptor: CmResourceTypePort, CmResourceTypeMemory, CmResourceTypeInterrupt, or CmResourceTypeDma.
ShareDisposition
Specifies whether or how this resource can be shared as one of the following: CmResourceShareDeviceExclusive, CmResourceShareDriverExclusive, or CmResourceShareShared.
Flags
Specifies type-dependent information about this descriptor:

· When Type is set with CmResourceTypePort, can be either of the following: CM_RESOURCE_PORT_MEMORY or CM_RESOURCE_PORT_IO.

· When Type is set with CmResourceTypeMemory, can be one of the following: CM_RESOURCE_MEMORY_READ_WRITE, CM_RESOURCE_MEMORY_READ_ONLY, or CM_RESOURCE_MEMORY_WRITE_ONLY.

· When Type is set with CmResourceTypeInterrupt, can be either of the following: CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE or CM_RESOURCE_INTERRUPT_LATCHED.

u.Port
Describes a bus-relative range of device ports in I/O space, including the Length in bytes of the port range, the required byte Alignment boundary for the range, its lowest possible MinimumAddress and its highest possible MaximumAddress in the bus-relative physical address range of I/O port space. Type must be CmResourceTypePort.
u.Memory
Describes a bus-relative range of device memory in memory space, including the Length in bytes of the range, the required byte Alignment boundary for the range, its lowest possible MinimumAddress and its highest possible MaximumAddress in the bus-relative physical address range of device-memory space. Type must be CmResourceTypeMemory.
u.Interrupt
Describes a range of one or more bus-relative interrupt vectors at which the device can interrupt. Type must be CmResourceTypeInterrupt.
u.Dma
Describes a range of one or more DMA channel numbers or MCA-type DMA port numbers that the device can use. Type must be CmResourceTypeDma.
AllocatedResources
Points to the address of an uninitialized pointer. If successful, IoAssignResources resets this uninitialized pointer to a buffer it allocates for a returned CM_RESOURCE_LIST, which describes the hardware resources it allocated for the caller. The caller is responsible for freeing the buffer.

Return Value

IoAssignResources returns STATUS_SUCCESS if it claimed a set of the specified hardware resources for the caller and returned information in the AllocatedResources buffer. Otherwise, it returns an error status, resets the pointer at AllocatedResources to NULL, and logs an error if it finds a resource conflict.

Comments

For most device drivers, calling IoAssignResources after locating the device and getting whatever configuration information HalGetBusData or HalGetBusDataByOffset can supply is preferable to making paired calls to IoQueryDeviceDescription and IoReportResourceUsage.

Note that IoAssignResources does not handle IO_RESOURCE_DESCRIPTOR entries with the Type member set to CmResourceTypeDeviceSpecific. Drivers that have hardware resources of this type can call IoReportResourceUsage to store this configuration information in the \\Registry\..\ResourceMap tree. Otherwise, a successful call to IoAssignResources writes the caller’s claims on every other type of hardware resource into the registry \ResourceMap tree.

IoAssignResources automatically searches the registry for resource conflicts between resources requested and resources claimed by previously installed drivers. It first matches the preferred entries in the RequestedResources descriptor array against all other resource lists stored in the registry to determine whether a conflict exists. If it finds a conflict, it then matches any supplied alternative descriptors for the already claimed resource again, attempting to allocate a set of resources the caller can use.

The caller is responsible for releasing the AllocatedResources buffer, which is pageable, with ExFreePool after it has consumed the returned information and before the DriverEntry routine returns control.

If a driver claims resources on a device-specific basis for more than one device, the driver must call this routine at least once for each such device, and must update the RegistryPath string to supply a unique subkey name for each call with a unique DeviceObject pointer.

This routine can be called more than once for a given device or driver. If a new list of RequestedResources is supplied, it will overwrite or, possibly, be appended to the previous resource list in the registry. However, making a single call for each set of device-specific resources makes a driver load much faster than if it calls IoAssignResources many times to amend or incrementally construct the input RequestedResources for each of its devices. Note that subsequent calls to IoAssignResources can reassign the caller’s previously claimed resources if that caller does not adjust the input RequestedReources to “fix” its claim on the resources to be kept.

A driver must call IoAssignResources with a value of NULL for the RequestedResources parameter to erase its claim on resources in the registry if the driver is unloaded.

Callers of IoAssignResources must be running at IRQL PASSIVE_LEVEL.

See Also

CM_RESOURCE_LIST, ExAllocatePool, ExAllocatePoolWithTag, ExFreePool, HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffse t, IoReportResourceUsage, IoQueryDeviceDescription


IoAttachDevice

NTSTATUS
IoAttachDevice(
IN PDEVICE_OBJECT SourceDevice,
 IN PUNICODE_STRING TargetDevice,
 OUT PDEVICE_OBJECT *AttachedDevice
);

IoAttachDevice attaches the caller’s device object to a named target device object, so that I/O requests bound for the target device are routed first to the caller.

Parameters

SourceDevice
Points to the caller-created device object.
TargetDevice
Points to a buffer containing the name of the device object to which the specified SourceDevice is to be attached.
AttachedDevice
Points to caller-allocated storage for a pointer. On return, contains a pointer to the target device object if the attachment succeeds.

Return Value

IoAttachDevice can return one of the following NTSTATUS values:

STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_OBJECT_TYPE_MISMATCH
STATUS_OBJECT_NAME_INVALID
STATUS_INSUFFICIENT_RESOURCES

Comments

IoAttachDevice establishes layering between drivers so that the same IRPs can be sent to each driver in the chain.

This routine is used by intermediate drivers during initialization. It allows such a driver to attach its own device object to another device in such a way that any requests being made to the original device are given first to the intermediate driver.

The caller can only be layered at the top of an existing chain of layered drivers. IoAttachDevice searches for the highest device object layered over TargetDevice and attaches to that object (that can be the TargetDevice). Therefore, this routine must not be called if a driver that must be higher-level has already layered itself over the target device.

This routine sets the AlignmentRequirement in SourceDevice to the value in the next-lower device object and sets the StackSize to the value in the next-lower-object plus one.

Callers of IoAttachDevice must be running at IRQL PASSIVE_LEVEL.

See Also

IoAttachDeviceToDeviceStack, IoCreateDevice, IoDetachDevice


IoAttachDeviceByPointer

NTSTATUS
IoAttachDeviceByPointer(
IN PDEVICE_OBJECT SourceDevice,
 IN PDEVICE_OBJECT TargetDevice
);

This routine is obsolete; use IoAttachDeviceToDeviceStack.

IoAttachDeviceByPointer attaches the caller’s device object to a target device object so that I/O requests bound for the target device are routed first to the caller. This routine returns STATUS_SUCCESS or STATUS_NO_SUCH_DEVICE.


IoAttachDeviceToDeviceStack

PDEVICE_OBJECT
IoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT SourceDevice,
 IN PDEVICE_OBJECT TargetDevice
);

IoAttachDeviceToDeviceStack attaches the caller’s device object to the highest device object in the chain and returns a pointer to the previously highest device object. I/O requests bound for the target device are routed first to the caller.

Parameters

SourceDevice
Points to the caller-created device object.
TargetDevice
Points to another driver’s device object, such as a pointer returned by a preceding call to IoGetDeviceObjectPointer.

Return Value

IoAttachDeviceToDeviceStack returns a pointer to the device object to which the SourceDevice was attached. The returned device object pointer can differ from TargetDevice if TargetDevice had additional drivers layered on top of it.

IoAttachDeviceToDeviceStack returns NULL if it could not attach the device object because, for example, the target device was being unloaded.

Comments

IoAttachDeviceToDeviceStack establishes layering between drivers so that the same IRPs are sent to each driver in the chain.

An intermediate driver can use this routine during initialization to attach its own device object to another driver’s device object. Subsequent I/O requests sent to TargetDevice are sent first to the intermediate driver.

This routine sets the AlignmentRequirement in SourceDevice to the value in the next-lower device object and sets the StackSize to the value in the next-lower-object plus one.

A driver writer must take care to call this routine before any drivers that must layer on top of their driver. IoAttachDeviceToDeviceStack attaches SourceDevice to the highest device object currently layered in the chain and has no way to determine whether drivers are being layered in the correct order.

A driver that acquired a pointer to the target device by calling IoGetDeviceObjectPointer should call ObDereferenceObject with the file object pointer that was returned by IoGetDeviceObjectPointer to release its reference to the file object before it detaches its own device object, for example, when such a higher-level driver is unloaded.

Callers of IoAttachDeviceToDeviceStack must be running at IRQL PASSIVE_LEVEL.

See Also

IoAttachDevice, IoDetachDevice, ObDereferenceObject, IoGetDeviceObjectPointer


IoBuildAsynchronousFsdRequest

PIRP
IoBuildAsynchronousFsdRequest(
IN ULONG MajorFunction,
 IN PDEVICE_OBJECT DeviceObject,
 IN OUT PVOID Buffer, /* optional */
IN ULONG Length, /* optional */
IN PLARGE_INTEGER StartingOffset, /* optional */
IN PIO_STATUS_BLOCK IoStatusBlock /* optional */
);

IoBuildAsynchronousFsdRequest allocates and sets up an IRP to be sent to lower-level drivers.

Parameters

MajorFunction
Specifies the major function code to be set in the IRP, one of IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_FLUSH_BUFFERS, or IRP_MJ_SHUTDOWN.
DeviceObject
Points to the next-lower driver’s device object, representing the target device for the read, write, flush, or shutdown operation.
Buffer
Points to a buffer into which data is read or from which data is written. The value of this argument is NULL for flush and shutdown requests.
Length
Specifies the length in bytes of Buffer. The value of this argument is zero for flush and shutdown requests.
StartingOffset
Points to the starting offset on the input/output media. The value of this argument is zero for flush and shutdown requests.
IoStatusBlock
Points to the address of an I/O status block in which the to-be-called driver(s) return final status about the requested operation.

Return Value

IoBuildAsynchronousFsdRequest returns a pointer to an IRP or a NULL pointer if the IRP cannot be allocated.

Comments

Intermediate or highest-level drivers can call IoBuildAsynchronousFsdRequest to set up IRPs for requests sent to lower-level drivers. Such a driver must set its IoCompletion routine in the IRP so the IRP can be deallocated with IoFreeIrp.

The IRP that gets built contains only enough information to get the operation started and to complete the IRP. No other context information is tracked because an asynchronous request is context-independent.

Callers of IoBuildAsynchronousFsdRequest must be running at IRQL <= DISPATCH_LEVEL.

An intermediate or highest-level driver also can call IoBuildDeviceIoControlRequest, IoAllocateIrp, or IoBuildSychronousFsdRequest to set up requests it sends to lower-level drivers. Only a highest-level driver can call IoMakeAssociatedIrp.

See Also

IO_STACK_LOCATION, IoAllocateIrp, IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IoCallDriver, IoFreeIrp, IoMakeAssociatedIrp, IoSetCompletionRoutine, IRP


IoBuildDeviceIoControlRequest

PIRP
IoBuildDeviceIoControlRequest(
IN ULONG IoControlCode,
 IN PDEVICE_OBJECT DeviceObject,
 IN PVOID InputBuffer, /* optional */
IN ULONG InputBufferLength,
 IN OUT PVOID OutputBuffer, /* optional */
IN ULONG OutputBufferLength,
 IN BOOLEAN InternalDeviceIoControl,
 IN PKEVENT Event,
 OUT PIO_STATUS_BLOCK IoStatusBlock
);

IoBuildDeviceIoControlRequest allocates and sets up an IRP for a device control request, optionally with an I/O buffer if the I/O control code requires the caller to supply an input or output buffer.

Parameters

IoControlCode
Specifies the IOCTL_XXX to be set up. For more information about device-type-specific I/O codes, see Part II of this manual.
DeviceObject
Points to the next-lower driver’s device object, representing the target device.
InputBuffer
Points to an input buffer to be passed to the lower driver or NULL if the request does not pass input data to lower driver(s).
InputBufferLength
Specifies the length in bytes of the input buffer. If InputBuffer is NULL, this value must be zero.
OutputBuffer
Points to an output buffer in which the lower driver is to return data or NULL if the request does not require lower driver(s) to return data.
OutputBufferLength
Specifies the length in bytes of the output buffer. If OutputBuffer is NULL, this value must be zero.
InternalDeviceIoControl
If InternalDeviceControl is TRUE the target driver’s Dispatch routine for IRP_MJ_INTERNAL_DEVICE_CONTROL or IRP_MJ_SCSI is called; otherwise, the Dispatch routine for IRP_MJ_DEVICE_CONTROL is called.
Event
Points to an initialized event object for which the caller provides the storage. The event is set to the Signaled state when lower driver(s) have completed the requested operation. The caller can wait on the event object for the completion of the IRP allocated by this routine.
IoStatusBlock
Specifies an I/O status block to be set when the request is completed by lower drivers.

Return Value

IoBuildDeviceIoControlRequest returns a pointer to an IRP with the next-lower driver’s I/O stack location partially set up from the supplied parameters. The returned pointer is NULL if an IRP cannot be allocated.

Comments

An intermediate or highest-level driver can call IoBuildDeviceIoControlRequest to set up IRPs for requests sent to lower-level drivers. The next-lower driver’s I/O stack location is set up with the given IoControlCode at Parameters.DeviceIoControl.IoControlCode. Because the caller can wait on the completion of this driver-allocated IRP by calling KeWaitForSingleObject on the given Event, the caller need not set an IoCompletion routine in the IRP before calling IoCallDriver. When the next-lower driver completes this IRP, the I/O Manager releases it.

Callers of IoBuildDeviceIoControlRequest must be running at IRQL PASSIVE_LEVEL.

See Also

IO_STACK_LOCATION, IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoBuildSynchronousFsdRequest, IoCallDriver, IoCompleteRequest, IRP, KeInitializeEvent, KeWaitForSingleObject


IoBuildPartialMdl

VOID
IoBuildPartialMdl(
IN PMDL SourceMdl,
 IN OUT PMDL TargetMdl,
 IN PVOID VirtualAddress,
 IN ULONG Length
);

IoBuildPartialMdl maps a portion of a buffer described by another MDL into an MDL.

Parameters

SourceMdl
Points to an MDL describing the original buffer, of which a subrange is to be mapped.
TargetMdl
Points to a caller-allocated MDL. The MDL must be large enough to map the subrange specified by VirtualAddress and Length.
VirtualAddress
Points to the base virtual address for the subrange to be mapped in the TargetMdl.
Length
Specifies the length in bytes to be mapped by the TargetMdl. This value, in combination with VirtualAddress, must specify a buffer that is a proper subrange of the buffer described by SourceMdl. If Length is zero, the subrange to be mapped starts at VirtualAddress and includes the remaining range described by the SourceMdl.

Comments

IoBuildPartialMdl maps a subrange of a buffer currently mapped by SourceMdl. The VirtualAddress and Length parameters describe the subrange to be mapped from the SourceMdl into the TargetMdl.

Drivers that must split large transfer requests can use this routine. The caller must release the partial MDL it allocated when it has transferred all the requested data or completed the IRP with an error status.

Callers of IoBuildPartialMdl must be running at IRQL PASSIVE_LEVEL.

See Also

IoAllocateMdl, IoCallDriver, IoFreeMdl, IoSetCompletionRoutine


IoBuildSynchronousFsdRequest

PIRP
IoBuildSynchronousFsdRequest(
IN ULONG MajorFunction,
 IN PDEVICE_OBJECT DeviceObject,
 IN OUT PVOID Buffer, /* optional */
IN ULONG Length, /* optional */
IN PLARGE_INTEGER StartingOffset, /* optional */
IN PKEVENT Event,
 OUT PIO_STATUS_BLOCK IoStatusBlock
);

IoBuildSynchronousFsdRequest allocates and builds IRP to be sent synchronously to lower driver(s).

Parameters

MajorFunction
Specifies the major function code, one of IRP_MJ_READ, IRP_MJ_WRITE, IRP_MJ_FLUSH_BUFFERS, or IRP_MJ_SHUTDOWN.
DeviceObject
Points to the next-lower driver’s device object representing the target device for the read, write, flush, or shutdown operation.
Buffer
Points to a buffer containing data to be written when MajorFunction is IRP_MJ_WRITE, or is the location to receive data read when MajorFunction is IRP_MJ_READ. This parameter must be NULL for the MajorFunction IRP_MJ_FLUSH_BUFFERS or IRP_MJ_SHUTDOWN.
Length
Specifies the length, in bytes, of Buffer. For devices such as disks, this value must be an integral of 512. This parameter is required for read/write requests, but must be zero for flush and shutdown requests.
StartingOffset
Points to the offset on the disk to read/write from/to. This parameter is required for read/write requests, but must be zero for flush and shutdown requests.
Event
Points to an initialized event object for which the caller provides the storage. The event is set to the Signaled state when the requested operation completes. The caller can wait on the event object for the completion of the IRP allocated by this routine.
IoStatusBlock
Points to the I/O status block that is set when the IRP is completed by the lower driver(s).

Return Value

IoBuildSynchronousFsdRequest returns a pointer to the IRP or NULL if an IRP cannot be allocated.

Comments

Intermediate or highest-level drivers can call IoBuildSynchronousFsdRequest to set up IRPs for requests sent to lower-level drivers, only if the caller is running in a nonarbitrary thread context and at IRQL PASSIVE_LEVEL.

IoBuildSynchronousFsdRequest allocates and sets up an IRP that can be sent to a device driver to perform a synchronous read, write, flush, or shutdown operation. The IRP contains only enough information to get the operation started.

The caller can determine when the I/O has completed by calling KeWaitForSingleObject with the Event. Performing this wait operation causes the current thread to wait. Therefore, this operation can be requested during the initialization of an intermediate driver or from an FSD in the context of a thread requesting a synchronous I/O operation. A driver cannot wait for a nonzero interval on the Event at raised IRQL in an arbitrary thread context.

Because the caller can wait on a given Event, the caller need not set an IoCompletion routine in the caller-allocated IRP before calling IoCallDriver. When the caller completes the IRP, the I/O Manager releases it.

See Also

IO_STACK_LOCATION, IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoCompleteRequest, IRP, KeInitializeEvent, KeWaitForSingleObject


IoCallDriver

NTSTATUS
IoCallDriver(
IN PDEVICE_OBJECT DeviceObject,
 IN OUT PIRP Irp
);

IoCallDriver sends an IRP to the next-lower-level driver after the caller has set up the I/O stack location in the IRP for that driver.

Parameters

DeviceObject
Points to the next-lower driver’s device object, representing the target device for the requested I/O operation.
Irp
Points to the IRP.

Return Value

IoCallDriver returns the NTSTATUS value that a lower driver set in the I/O status block for the given request or STATUS_PENDING if the request was queued for additional processing.

Comments

IoCallDriver assigns the DeviceObject input parameter to the device object field of the IRP stack location for the next lower driver.

An IRP passed in a call to IoCallDriver becomes inaccessible to the higher-level driver, unless the higher-level driver has set up its IoCompletion routine for the IRP with IoSetCompletionRoutine. If it does, the IRP input to the driver-supplied IoCompletion routine has its I/O status block set by the lower driver(s) and all lower-level driver(s)’ I/O stack locations filled with zeros.

Callers of IoCallDriver must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IoSetCompletionRoutine


IoCancelIrp

BOOLEAN
IoCancelIrp(
IN PIRP Irp
);

IoCancelIrp sets the cancel bit in a given IRP and calls the cancel routine for the IRP if there is one.

Parameters

Irp
Points to the IRP to be canceled.

Return Value

IoCancelIrp returns TRUE if the IRP was canceled and FALSE if the IRP’s cancel bit was set but the IRP was not cancelable.

Comments

If the IRP has a cancel routine, IoCancelIrp sets the cancel bit and calls the cancel routine.

If Irp->CancelRoutine is NULL, and therefore the IRP is not cancelable, IoCancelIrp sets the IRP’s cancel bit and returns FALSE. The IRP should be canceled at a later time when it becomes cancelable.

If a driver that does not own the IRP calls IoCancelIrp, the results are unpredictable. The IRP might be completed with a successful status even though its cancel bit was set.

An intermediate driver should not arbitrarily call IoCancelIrp unless that driver created the IRP passed in the call. Otherwise, the intermediate driver might cancel an IRP that some higher-level driver is tracking for purposes of its own.

Callers of IoCancelIrp must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoSetCancelRoutine


IoCheckShareAccess

NTSTATUS
IoCheckShareAccess(
IN ACCESS_MASK DesiredAccess,
 IN ULONG DesiredShareAccess,
 IN OUT PFILE_OBJECT FileObject,
 IN OUT PSHARE_ACCESS ShareAccess,
 IN BOOLEAN Update
);

IoCheckShareAccess is called by FSDs or other highest-level drivers to check whether shared access to a file object is permitted.

Parameters

DesiredAccess
Specifies the type(s) of access to the given FileObject for the current open request. Generally, the value of this parameter is equal to the DesiredAccess passed to the file system or highest-level driver by the I/O manager when an open request is made.
DesiredShareAccess
Specifies the type(s) of shared access to the file object for the current open request. This parameter is generally identical to the ShareAccess that was passed to the file system or highest-level driver by the I/O Manager when the open request was made.
FileObject
Points to the file object, to which accesses are to be checked for the current open request.
ShareAccess
Points to the common share-access data structure associated with the file object being opened. This structure is treated as an opaque type by drivers.
Update
Specifies whether or not the system should update the share-access status of the specified file object. A Boolean value of TRUE means the share access information for the file is to be updated if the open request is permitted.

Return Value

IoCheckShareAccess returns STATUS_SUCCESS if the requestor’s access to the file object is compatible with the way in which it is currently open. If the request is denied because of a sharing violation, then STATUS_SHARING_VIOLATION is returned.

Comments

IoCheckShareAccess checks a file object open request to determine whether the types of desired and shared accesses specified are compatible with the way in which the file object is currently being accessed by other opens.

File systems maintain state about files through structures called file control blocks (FCBs). The SHARE_ACCESS is a structure describing how the file is currently accessed by all opens. This state is contained in the FCB as part of the open state for each file object. Other highest-level drivers might call this routine to check the access requested when a file object representing such a driver’s device object is opened.

Callers of IoCheckShareAccess must be running at IRQL PASSIVE_LEVEL.

See Also

IoGetRelatedDeviceObject, IoRemoveShareAccess, IoSetShareAccess, IoUpdateShareAccess


IoCompleteRequest

VOID
IoCompleteRequest(
IN PIRP Irp,
 IN CCHAR PriorityBoost
);

IoCompleteRequest indicates the caller has completed all processing for a given I/O request and is returning the given IRP to the I/O Manager.

Parameters

Irp
Points to the IRP to be completed.
PriorityBoost
Specifies a system-defined constant by which to increment the runtime priority of the original thread that requested the operation. This value is IO_NO_INCREMENT if the original thread requested an operation the driver could complete quickly (so the requesting thread is not compensated for its assumed wait on I/O) or if the IRP is completed with an error. Otherwise, the set of PriorityBoost constants are device-type-specific. See ntddk.h for these constants.

Comments

When a driver has finished all processing for a given IRP, it calls IoCompleteRequest. The I/O Manager checks the IRP to determine whether any higher-level drivers have set up an IoCompletion routine for the IRP. If so, each IoCompletion routine is called, in turn, until every layered driver in the chain has completed the IRP.

When all drivers have completed a given IRP, the I/O Manger returns status to the original requestor of the operation. Note that a higher-level driver that sets up a driver-created IRP must supply an IoCompletion routine to release the IRP it created.

Callers of IoCompleteRequest must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoSetCompletionRoutine


IoConnectInterrupt

NTSTATUS
IoConnectInterrupt(
OUT PKINTERRUPT *InterruptObject,
 IN PKSERVICE_ROUTINE ServiceRoutine,
 IN PVOID ServiceContext,
 IN PKSPIN_LOCK SpinLock, /* optional */
IN ULONG Vector,
 IN KIRQL Irql,
 IN KIRQL SynchronizeIrql,
 IN KINTERRUPT_MODE InterruptMode,
 IN BOOLEAN ShareVector,
 IN KAFFINITY ProcessorEnableMask,
 IN BOOLEAN FloatingSave
);

IoConnectInterrupt registers a device driver’s ISR to be called when its device interrupts on any of a given set of processors.

Parameters

InterruptObject
Points to the address of driver-supplied storage for a pointer to a set of interrupt objects, which was obtained by calling HalGetInterruptVector. This pointer must be passed in subsequent calls to KeSynchronizeExecution.
ServiceRoutine
Points to the entry point for the driver-supplied ISR declared as follows:
BOOLEAN
(*PKSERVICE_ROUTINE)(
    IN PKINTERRUPT Interrupt,
    IN PVOID ServiceContext
    );
ServiceContext
Points to the driver-determined context with which the specified ISR will be called. The ServiceContext area must be in resident memory: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the device driver.
SpinLock
Points to an initialized spin lock, for which the driver supplies the storage, that will be used to synchronize access to driver-determined data shared by other driver routines. This parameter is required if the ISR handles more than one vector or if the driver has more than one ISR. Otherwise, the driver need not allocate storage for an interrupt spin lock and the input pointer is NULL.
Vector
Specifies the interrupt vector returned by the driver’s call to HalGetInterruptVector.
Irql
Specifies the DIRQL returned by HalGetInterruptVector for the Vector.
SynchronizeIrql
Specifies the DIRQL at which the ISR will execute. If the ISR handles more than one interrupt vector or the driver has more than one ISR, this value must be the highest of the Irql values returned by calls to HalGetInterruptVector for each vector. Otherwise, the Irql and SynchronizeIrql values are identical.
InterruptMode
Specifies whether the device interrupt is LevelSensitive or Latched.
ShareVector
Specifies whether the interrupt vector is sharable.
ProcessorEnableMask
Specifies the set of processors on which device interrupts can occur in this platform. This value also was returned by HalGetInterruptVector.
FloatingSave
Specifies whether to save the floating-point stack when the driver’s device interrupts. For X86-based platforms, this value must be set to FALSE.

Return Value

IoConnectInterrupt can return one of the following NTSTATUS values:

STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_INSUFFICIENT_RESOURCES

Comments

If the driver supplies the storage for the SpinLock, it must call KeInitializeSpinLock before passing its interrupt spin lock to IoConnectInterrupt.

On return from a successful call to IoConnectInterrupt, the caller’s ISR can be called if interrupts are enabled on the driver’s device or if ShareVector was set to TRUE.

Callers of IoConnectInterrupt must be running at IRQL PASSIVE_LEVEL.

See Also

HalGetInterruptVector, KeInitializeSpinLock, KeSynchronizeExecution


IoCreateController

PCONTROLLER_OBJECT
IoCreateController(
IN ULONG Size
);

IoCreateController allocates memory for and initializes a controller object with a controller extension of a driver-determined size.

Parameters

Size
Specifies the number of bytes to be allocated for the controller extension.

Return Value

IoCreateController returns a pointer to the controller object or a NULL pointer if memory could not be allocated for the requested device extension.

Comments

A controller object usually represents a physical device controller with attached devices on which a single driver carries out I/O requests. The controller extension is allocated from nonpaged pool and is guaranteed to be accessible by any driver routine and in an arbitrary thread context.

The controller object is used to synchronize I/O operations to target devices for which I/O requests can come in concurrently to a single, monolithic driver. A driver also might use a controller object to synchronize operations through device channels.

If IoCreateController returns NULL, such a driver should not load.

Callers of IoCreateController must be running at IRQL PASSIVE_LEVEL.

See Also

CONTROLLER_OBJECT, IoAllocateController, IoFreeController, IoDeleteController


IoCreateDevice

NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
 IN ULONG DeviceExtensionSize,
 IN PUNICODE_STRING DeviceName, /* optional */
IN DEVICE_TYPE DeviceType,
 IN ULONG DeviceCharacteristics,
 IN BOOLEAN Exclusive,
 OUT PDEVICE_OBJECT *DeviceObject
);

IoCreateDevice allocates memory for and initializes a device object for use by a driver. A device object represents a physical, virtual, or logical device whose driver is being loaded into the system, or it represents a virtual or logical device whose driver is required to support the reconfiguration of its device objects dynamically.

Parameters

DriverObject
Points to the driver object created by the I/O Manager when the driver was loaded. Each driver’s DriverEntry routine is called with a pointer to the corresponding driver object.
DeviceExtensionSize
Specifies the driver-determined number of bytes to be allocated for the device extension of the device object. The internal structure of the device extension is driver-defined, used to maintain context about the I/O operations on the device represented by the DeviceObject.
DeviceName
Points to a buffer containing a zero-terminated Unicode string that names the device object. The string must be a full path name. Any device object that can be the target of an I/O request or that a higher-level driver can connect to must have a DeviceName. An unnamed device object is visible only to the driver that created it or to an FSD through a volume parameter block (VPB).
DeviceType
Specifies one of the system-defined FILE_DEVICE_XXX constants indicating the type of device (such as FILE_DEVICE_DISK, FILE_DEVICE_SERIAL, etc.) or a driver-defined value for a new type of device. For more information, see Part II of this manual.
DeviceCharacteristics
Specifies one or more (ORed) of the following system-defined constants: FILE_REMOVABLE_MEDIA, FILE_READ_ONLY_DEVICE, FILE_FLOPPY_DISKETTE, FILE_WRITE_ONCE_MEDIA, and FILE_REMOTE_DEVICE, describing the nature of the driver’s device, if appropriate. Other types of drivers can ignore this field.
Exclusive
Indicates whether or not the device object represents an exclusive device. That is, only a single thread at a time can send I/O requests to the corresponding device.
DeviceObject
Points to the newly created device object if the call succeeds.

Return Value

IoCreateDevice can return one of the following NTSTATUS values:

STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES
STATUS_OBJECT_NAME_EXISTS
STATUS_OBJECT_NAME_COLLISION

Comments

This routine allocates space in nonpaged pool for the driver-defined device extension associated with the device object, so that the device extension is accessible to the driver in any execution context and at any IRQL. The returned device extension is initialized with zeros.

In general, drivers that set Exclusive to TRUE control interactive devices, such as video, serial, parallel, or sound devices.

Device objects for disks, tapes, CD ROMs, and RAM disks are given a Volume Parameter Block (VPB) that is initialized to indicate that the volume has never been mounted on the device.

Each driver must create at least one named device object by calling IoCreateDevice. Otherwise, no I/O requests can be sent to the driver. An unnamed device object is invisible to other drivers except, possibly, FSDs and to user-mode protected subsystems because a symbolic link cannot be set up for an unnamed device object. Consequently, higher-level drivers cannot attach their device objects to an unnamed device object nor can the unnamed object be the target of an IRP.

If a driver’s call to IoCreateDevice returns an error, it should release any resources it allocated for that device. If the driver cannot set up a device object to represent any of the devices it controls, that driver should not load.

Certain device drivers call IoCreateDevice from a Dispatch routine. In particular, disk drivers must call IoCreateDevice in response to a device control request to repartition a disk dynamically.

Callers of IoCreateDevice must be running at IRQL PASSIVE_LEVEL.

See Also

DEVICE_OBJECT, IoAttachDevice, IoAttachDeviceToDeviceStack, IoCreateSymbolicLink, IoDeleteDevice


IoCreateNotificationEvent

PKEVENT
IoCreateNotificationEvent(
IN PUNICODE_STRING EventName,
 OUT PHANDLE EventHandle
);

IoCreateNotificationEvent creates or opens a named notification event used to notify one or more threads of execution that an event has occurred. When a notification event is set to the Signaled state it remains in that state until it is explicitly cleared.

Parameters

EventName
Points to a buffer containing a zero-terminated Unicode string that names the event.
EventHandle
Points to a location in which to return a handle for the event object. The handle includes bookkeeping information, such as a reference count and security context.

Return Value

IoCreateNotificationEvent returns a pointer to the created or opened event object or NULL if the event object could not be created or opened.

Comments

IoCreateNotificationEvent creates and opens the event object if it does not already exist. IoCreateNotificationEvent sets the state of a new notification event to Signaled. If the event object already exists, IoCreateNotificationEvent just opens the event object.

Notification events, like synchronization events, are used to coordinate execution. Unlike a synchronization event, a notification event is not auto-resetting. Once a notification event is in the Signaled state, it remains in that state until it is explicitly reset (with a call to KeClearEvent or KeResetEvent).

To synchronize on a notification event:

  1. Open the notification event with IoCreateNotificationEvent. Identify the event with the EventName string.

  2. Wait for the event to be signaled by calling KeWaitForSingleObject with the PKEVENT returned by IoCreateNotificationEvent. More than one thread of execution can wait on a given notification event. To poll instead of stall, specify a Timeout of zero to KeWaitForSingleObject.

  3. Close the handle to the notification event with ZwClose when access to the event is no longer needed.

Callers of IoCreateNotificationEvent must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateSynchronizationEvent, KeClearEvent, KeResetEvent, KeSetEvent, KeWaitForSingleObject, RtlInitUnicodeString, ZwClose


IoCreateSymbolicLink

NTSTATUS
IoCreateSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName,
 IN PUNICODE_STRING DeviceName
);

IoCreateSymbolicLink sets up a symbolic link between a device object name and a user-visible name for the device.

Parameters

SymbolicLinkName
Points to a buffered Unicode string that is the user-visible name.
DeviceName
Points to a buffered Unicode string that is the name of the driver-created device object.

Return Value

IoCreateSymbolicLink returns STATUS_SUCCESS if the symbolic link object was created.

Callers of IoCreateSymbolicLink must be running at IRQL PASSIVE_LEVEL.

See Also

IoAssignArcName, IoCreateUnprotectedSymbolicLink, IoDeleteSymbolicLink


IoCreateSynchronizationEvent

PKEVENT
IoCreateSynchronizationEvent(
IN PUNICODE_STRING EventName,
 OUT PHANDLE EventHandle
);

IoCreateSynchronizationEvent creates or opens a named synchronization event for use in serialization of access to hardware between two otherwise unrelated drivers.

Parameters

EventName
Points to a buffer containing a zero-terminated Unicode string that names the event.
EventHandle
Points to a location in which to return a handle for the event object.

Return Value

IoCreateSynchronizationEvent returns a pointer to the created or opened event object or NULL if the event object could not be created or opened.

Comments

The event object is created if it does not already exist. IoCreateSynchronizationEvent sets the state of a new synchronization event to Signaled. If the event object already exists, it is simply opened. The pair of drivers that use a synchronization event call KeWaitForSingleObject with the PKEVENT pointer returned by this routine.

When a synchronization event is set to the Signaled state, a single thread of execution that was waiting on the event is released, and the event is automatically reset to the Not-Signaled state.

To release the event, a driver calls ZwClose with the event handle.

Callers of IoCreateSynchronizationEvent must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateNotificationEvent, KeWaitForSingleObject, RtlInitUnicodeString, ZwClose


IoCreateUnprotectedSymbolicLink

NTSTATUS
IoCreateUnprotectedSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName,
 IN PUNICODE_STRING DeviceName
);

IoCreateUnprotectedSymbolicLink sets up an unprotected symbolic link between a device object name and a corresponding Win32®-visible name.

Parameters

SymbolicLinkName
Supplies the symbolic link name as a Unicode string.
DeviceName
Supplies the name of the device object to which the symbolic link name refers.

Return Value

IoCreateUnprotectedSymbolicLink returns the final status of the operation.

Comments

IoCreateUnprotectedSymbolicLink can be used by drivers if the user needs to be able to manipulate the symbolic link. For example, the parallel and serial drivers create unprotected symbolic links for LPTx and COMx, so that users can manipulate and reassign them by using the MODE command.

In general, drivers should call this routine instead of IoCreateSymbolicLink if a protected subsystem lets end users change what a named device references as, for example, when using LPT1 to access a network printer.

Callers of IoCreateUnprotectedSymbolicLink must be running at IRQL PASSIVE_LEVEL.

See Also

IoAssignArcName, IoCreateSymbolicLink, IoDeleteSymbolicLink


IoDeassignArcName

VOID
IoDeassignArcName(
IN PUNICODE_STRING ArcName
);

IoDeassignArcName removes a symbolic link between the ARC name for a device and the named device object. This is generally called if the driver is deleting the device object, for example, when the driver is unloading.

Parameters

ArcName
Points to a buffered Unicode string that is the ARC name.

Comments

Callers of IoDeassignArcName must be running at IRQL PASSIVE_LEVEL.

See Also

IoAssignArcName


IoDeleteController

VOID
IoDeleteController(
IN PCONTROLLER_OBJECT ControllerObject
);

IoDeleteController removes a given controller object from the system, for example, when the driver that created it is being unloaded.

Parameters

ControllerObject
Points to the controller object to be released.

Comments

IoDeleteController deallocates the memory for the controller object, including the controller extension.

This routine must be called when a driver that created a controller object is being unloaded or when the DriverEntry routine encounters a fatal initialization error, such as being unable to properly initialize a physical device.

A driver must release certain resources for which the driver supplied storage in its controller extension before it calls IoDeleteController. For example, if the driver stores the pointer to its interrupt object(s) in the controller extension, it must call IoDisconnectInterrupt before IoDeleteController.

Callers of IoDeleteController must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateController, IoDisconnectInterrupt


IoDeleteDevice

VOID
IoDeleteDevice(
IN PDEVICE_OBJECT DeviceObject
);

IoDeleteDevice removes a device object from the system, for example, when the driver that created it is being unloaded.

Parameters

DeviceObject
Points to the driver’s device object to be released.

Comments

This routine must be called when a driver that created the device object is being unloaded or when the DriverEntry routine encounters a fatal initialization error, such as being unable to properly initialize a physical device. This routine also is called when a driver reconfigures its devices dynamically. For example, a disk driver called to repartition a disk would call IoDeleteDevice to tear down the device objects representing partitions to be replaced.

A driver must release certain resources for which the driver supplied storage in its device extension before it calls IoDeleteDevice. For example, if the driver stores the pointer to its interrupt object(s) in the device extension, it must call IoDisconnectInterrupt before IoDeleteDevice.

When IoDeleteDevice is called, there cannot be any outstanding references to the device being deleted nor any attached devices. If there are, a system erorr occurs.

Callers of IoDeleteDevice must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateDevice, IoDisconnectInterrupt


IoDeleteSymbolicLink

NTSTATUS
IoDeleteSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName
);

IoDeleteSymbolicLink removes a symbolic link from the system.

Parameters

SymbolicLinkName
Points to a buffered Unicode string that is the user-visible name for the symbolic link.

Return Value

IoDeleteSymbolicLink returns STATUS_SUCCESS if the symbolic link object is deleted.

Comments

Callers of IoDeleteSymbolicLink must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateSymbolicLink, IoCreateUnprotectedSymbolicLink, IoDeassignArcName


IoDetachDevice

VOID
IoDetachDevice(
IN OUT PDEVICE_OBJECT TargetDevice
);

IoDetachDevice releases an attachment between the caller’s device object and a lower driver’s device object.

Parameters

TargetDevice
Points to the lower driver’s device object. The caller previously called IoAttachDevice or IoAttachDeviceToDeviceStack successfully to get this pointer.

Comments

IoDetachDevice decrements the reference count of the TargetDevice object. If the reference count goes to zero and the lower driver has been marked for an unload operation, the lower driver is unloaded.

Callers of IoDetachDevice must be running at IRQL PASSIVE_LEVEL.

See Also

IoAttachDevice, IoAttachDeviceToDeviceStack


IoDisconnectInterrupt

VOID
IoDisconnectInterrupt(
IN PKINTERRUPT InterruptObject
);

IoDisconnectInterrupt releases a device driver’s set of interrupt object(s) when the driver is being unloaded.

Parameters

InterruptObject
Specifies the pointer returned by IoConnectInterrupt.

Comments

A driver should disable interrupts from its device before it calls IoDisconnectInterrupt.

If the driver stored the pointer to its interrupt object(s) in the device extension of its device object or in the controller extension of its controller object, it must call IoDisconnectInterrupt before it calls IoDeleteDevice or IoDeleteController.

Callers of IoDisconnectInterrupt must be running at IRQL PASSIVE_LEVEL.

See Also

IoConnectInterrupt, IoDeleteController, IoDeleteDevice


IoFlushAdapterBuffers

BOOLEAN
IoFlushAdapterBuffers(
IN PADAPTER_OBJECT AdapterObject,
 IN PMDL Mdl,
 IN PVOID MapRegisterBase,
 IN PVOID CurrentVa,
 IN ULONG Length,
 IN BOOLEAN WriteToDevice
);

IoFlushAdapterBuffers flushes any data remaining in the system DMA controller’s internal cache or in a busmaster adapter’s internal cache at the end of a DMA transfer operation.

Parameters

AdapterObject
Is the adapter object pointer returned by HalGetAdapter when the driver initialized.
Mdl
Points to the MDL that describes the buffer passed in the driver’s call to IoMapTransfer.
MapRegisterBase
Points to the handle returned by the driver’s call to IoAllocateAdapterChannel.
CurrentVa
Points to the current virtual address in the buffer, described by the Mdl, where the I/O operation occurred.
Length
Specifies the length, in bytes, of the buffer.
WriteToDevice
Specifies the direction of the DMA transfer operation: TRUE for a transfer from a buffer in system memory to the driver’s device.

Return Value

IoFlushAdapterBuffers returns TRUE if any data remaining in the DMA controller’s or busmaster adapter’s internal cache has been successfully flushed into system memory or out to the device.

Comments

Every driver that performs DMA operations must call IoFlushAdapterBuffers before completing the IRP that requested the DMA transfer to ensure that the transfer is complete.

The initial CurrentVa for the start of a packet-based DMA transfer can be obtained by calling MmGetMdlVirtualAddress. However, the value returned is an index into the Mdl, rather than a valid virtual address. If the driver must split a large transfer request into more than one DMA operation, CurrentVa and Length must be updated for each DMA operation.

Callers of IoFlushAdapterBuffers must be running at IRQL <= DISPATCH_LEVEL.

See Also

HalGetAdapter, IoAllocateAdapterChannel, IoMapTransfer, KeFlushIoBuffers, MmGetMdlVirtualAddress


IoFreeAdapterChannel

VOID
IoFreeAdapterChannel(
IN PADAPTER_OBJECT AdapterObject
);

IoFreeAdapterChannel releases the system DMA controller when a driver has completed all DMA operations necessary to satisfy the current IRP.

Parameters

AdapterObject
Points to the adapter object representing the DMA controller channel or MCA-type port to be deallocated. This pointer was returned by HalGetAdapter when the driver initialized.

Comments

IoFreeAdapterChannel releases the system DMA controller previously allocated with a call to IoAllocateAdapterChannel, after the calling driver has transferred all the data.

If map registers were allocated by the call to IoAllocateAdapterChannel, they are deallocated when the driver calls IoFreeAdapterChannel.

Callers of IoFreeAdapterChannel must be running at IRQL DISPATCH_LEVEL.

See Also

HalGetAdapter, IoAllocateAdapterChannel, IoFlushAdapterBuffers, IoFreeMapRegisters, IoMapTransfer


IoFreeController

VOID
IoFreeController(
IN PCONTROLLER_OBJECT ControllerObject
);

IoFreeController releases a previously allocated controller object when the driver has completed an I/O request.

Parameters

ControllerObject
Is a pointer to the driver’s controller object, which was allocated for the current I/O operation on a particular device by calling IoAllocateController.

Comments

The connection between the current target device object and the controller object is released only if no requests are currently queued to the same device. Otherwise, the driver’s ControllerControl routine is called with the next IRP bound through the device controller to the target device.

Callers of IoFreeController must be running at IRQL DISPATCH_LEVEL.

See Also

IoAllocateController, IoCreateController, IoDeleteController


IoFreeIrp

VOID
IoFreeIrp(
IN PIRP Irp
 );

IoFreeIrp releases a caller-allocated IRP from the caller’s IoCompletion routine.

Parameters

Irp
Points to the IRP that is to be released.

Comments

This routine is the reciprocal to IoAllocateIrp or IoBuildAsynchronousFsdRequest. The released IRP must have been allocated by the caller.

This routine also releases an IRP allocated with IoMakeAssociatedIrp in which the caller set up its IoCompletion routine that returns STATUS_MORE_PROCESSING_REQUIRED for the associated IRP.

Callers of IoFreeIrp must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoMakeAssociatedIrp, IoSetCompletionRoutine


IoFreeMapRegisters

VOID
IoFreeMapRegisters(
IN PADAPTER_OBJECT AdapterObject,
 IN PVOID MapRegisterBase,
 IN ULONG NumberOfMapRegisters
);

IoFreeMapRegisters releases a set of map registers that were saved from a call to IoAllocateAdapterChannel when the driver of a busmaster device has completed the current packet-based DMA transfer request.

Parameters

AdapterObject
Is the pointer to the adapter object representing the busmaster adapter. This pointer was returned by HalGetAdapter when the driver initialized.
MapRegisterBase
Points to the handle returned by the driver’s call to IoAllocateAdapterChannel.
NumberOfMapRegisters
Specifies the number of map registers to be released, which must match the number specified in the preceding call to IoAllocateAdapterChannel.

Comments

IoFreeMapRegisters releases map registers previously allocated by a call to IoAllocateAdapterChannel and retained by a busmaster device driver’s AdapterControl routine.

Callers of IoFreeMapRegisters must be running at IRQL DISPATCH_LEVEL.

See Also

HalAllocateCommonBuffer, HalGetAdapter, IoAllocateAdapterChannel, IoMapTransfer


IoFreeMdl

VOID
IoFreeMdl(
IN PMDL Mdl
);

IoFreeMdl releases a caller-allocated MDL.

Parameters

Mdl
Points to the MDL to be released.

Comments

If a driver allocates an MDL to describe a buffer, it must explicitly release the MDL when operations on the buffer are done.

Callers of IoFreeMdl must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateMdl, IoBuildPartialMdl


IoGetConfigurationInformation

PCONFIGURATION_INFORMATION
IoGetConfigurationInformation(
);

IoGetConfigurationInformation returns a pointer to the I/O Manager’s global configuration information structure, which contains the current values for how many physical disk, floppy, CD-ROM, tape, SCSI HBA, serial, and parallel devices have device objects created to represent them by drivers as they are loaded.

Return Value

IoGetConfigurationInformation returns a pointer to the configuration information structure. This structure is defined as follows:

typedef struct _CONFIGURATIONAL_INFORMATION{
    //
    // Each field indicates the total number of physical
    // devices of a particular type in the machine.  The value
    // should be used by the driver to determine the digit
    // suffix for device object names. This field must be
    // updated as the driver finds new devices of its own.
    //
    ULONG DiskCount;            // Count of hard disks so far.
    ULONG FloppyCount;          // Count of floppy drives so far.
    ULONG CDRomCount;           // Count of CD-ROM drives so far.
    ULONG TapeCount;            // Count of tape drives so far.
    ULONG ScsiPortCount;        // Count of HBAs so far.
    ULONG SerialCount;          // Count of serial ports so far.
    ULONG ParallelCount;        // Count of parallel ports so far.
    //
    // The next two fields indicate ownership of
    // either of the two I/O address spaces
    // that are used by WD1003-compatible disk controllers.
    //
    BOOLEAN AtDiskPrimaryAddressClaimed;       //0x1F0-0x1FF
    BOOLEAN AtDiskSecondaryAddressClaimed;     //0x170-0x17F
} CONFIGURATION_INFORMATION,*PCONFIGURATION_INFORMATION

Comments

Certain types of device drivers can use the configuration information structure’s values to construct device object names with appropriate digit suffixes when each driver creates its device objects. Note that the digit suffix for device object names is a zero-based count, while the counts maintained in the configuration information structure represent the number of device objects of a particular type already created. That is, the configuration information counts are one-based.

Any driver that calls IoGetConfigurationInformation must increment the count for its kind of device in this structure when it creates a device object to represent a physical device.

The system-supplied SCSI port driver supplies the count of SCSI HBAs present in the machine. SCSI class drivers can read this value to determine how many HBA-specific miniport drivers might control a SCSI bus with an attached device of the class driver’s type.

The configuration information structure also contains a value indicating whether an already loaded driver has claimed either of the “AT” disk I/O address ranges.

Callers of IoGetConfigurationInformation must be running at IRQL PASSIVE_LEVEL.

See Also

HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, IoAssignResources, IoQueryDeviceDescription, IoReportResourceUsage


IoGetCurrentIrpStackLocation

PIO_STACK_LOCATION
IoGetCurrentIrpStackLocation(
IN PIRP Irp
);

IoGetCurrentIrpStackLocation returns a pointer to the caller’s stack location in the given IRP.

Parameters

Irp
Points to the IRP.

Return Value

The routine returns a pointer to the I/O stack location for the driver.

Comments

Every driver must call IoGetCurrentIrpStackLocation with each IRP it is sent in order to get any parameters for the current request. Unless a driver supplies a Dispatch routine for each IRP_MJ_XXX that driver handles, the driver also must check its I/O stack location in the IRP to determine what operation is being requested.

Intermediate and highest-level drivers also call IoGetCurrentIrpStackLocation so that they can copy pertinent data from their own stack location into that of the next-lower driver whenever they pass a request on to lower drivers.

See Also

IO_STACK_LOCATION, IoCallDriver, IoGetNextIrpStackLocation, IoSetNextIrpStackLocation


IoGetCurrentProcess

PEPROCESS
IoGetCurrentProcess(
 );

Return Value

IoGetCurrentProcess returns a pointer to the current process.

Comments

In general, highest-level drivers, particularly file systems, are most likely to call this routine. An intermediate or underlying device driver seldom is called in the context of a thread that originates the current I/O request that the driver is processing, so it cannot get access to such a thread’s process space.

Callers of IoGetCurrentProcess must be running at IRQL PASSIVE_LEVEL.

See Also

PsGetCurrentThread


IoGetDeviceObjectPointer

NTSTATUS
IoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName,
 IN ACCESS_MASK DesiredAccess,
 OUT PFILE_OBJECT *FileObject,
 OUT PDEVICE_OBJECT *DeviceObject
);

IoGetDeviceObjectPointer returns a pointer to a named device object and corresponding file object if the requested access to the objects can be granted.

Parameters

ObjectName
Points to a buffer containing a Unicode string that is the name of the device object.
DesiredAccess
Specifies one or more (ORed) system-defined constants, usually FILE_READ_DATA, (infrequently) FILE_WRITE_DATA, and/or FILE_ALL_ACCESS, requesting access rights to the object.
FileObject
Points to the file object that represents the corresponding device object to user-mode code if the call is successful.
DeviceObject
Points to the device object that represents the named logical, virtual, or physical device if the call is successful.

Return Value

IoGetDeviceObjectPointer can return one of the following NTSTATUS values:
STATUS_SUCCESS
STATUS_OBJECT_TYPE_MISMATCH
STATUS_INVALID_PARAMETER
STATUS_PRIVILEGE_NOT_HELD
STATUS_INSUFFICIENT_RESOURCES
STATUS_OBJECT_NAME_INVALID

Comments

IoGetDeviceObjectPointer establishes a “connection” between the caller and the next-lower-level driver. A successful caller can use the returned device object pointer to initialize its own device object(s). It can also be used as as an argument to IoAttachDeviceToDeviceStack, IoCallDriver, and any routine that creates IRPs for lower drivers. The returned pointer is a required argument to IoCallDriver.

This routine also returns a pointer to the corresponding file object, which is useful only to highest-level drivers. A lower-level driver should call ObDereferenceObject with the returned file object pointer if such a driver is unloaded to decrement the reference count for the file object. Otherwise, the next-lower driver cannot be unloaded.

After any higher-level driver has chained itself over another driver by successfully calling this routine, the higher-level driver must set the StackSize field in its device object to that of the next-lower-level driver’s device object plus one.

Callers of IoGetDeviceObjectPointer must be running at IRQL PASSIVE_LEVEL.

See Also

DEVICE_OBJECT, IoAllocateIrp, IoAttachDevice, IoAttachDeviceToDeviceStack, ObDereferenceObject, ObReferenceObjectByPointer


IoGetDeviceToVerify

PDEVICE_OBJECT
IoGetDeviceToVerify(
IN PETHREAD Thread
);

IoGetDeviceToVerify returns a pointer to the device object, representing a removable-media device, that is the target of the given thread’s I/O request.

Return Value

IoGetDeviceToVerify returns a pointer to the device object representing a device on which the media should be verified, or it returns NULL.

Parameters

Thread
Points to the thread for which a highest-level driver is attempting to verify the validity of the media on which the thread has opened a file.

Comments

In general, highest-level drivers, particularly file systems, are most likely to call this routine.

An underlying removable-media device driver is responsible for notifying higher-level drivers, particularly the file system, when the media appears to have changed since the last access to the target device. For more information about handling removable media, see the Kernel-Mode Driver Design Guide.

Callers of IoGetDeviceToVerify must be running at IRQL PASSIVE_LEVEL.

See Also

IoIsErrorUserInduced, IoSetHardErrorOrVerifyDevice, PsGetCurrentThread


IoGetFileObjectGenericMapping

PGENERIC_MAPPING
IoGetFileObjectGenericMapping(
);

IoGetFileObjectGenericMapping returns information about the mapping between each generic access right and the set of specific access rights for file objects.

Return Value

IoGetFileObjectGenericMapping returns a pointer to the generic mapping for file objects.

Comments

The generic mapping structure is defined as follows:

typedef struct _GENERIC_MAPPING {
    ACCESS_MASK GenericRead;
    ACCESS_MASK GenericWrite;
    ACCESS_MASK GenericExecute;
    ACCESS_MASK GenericAll;
} GENERIC_MAPPING;

typedef GENERIC_MAPPING *PGENERIC_MAPPING;

Callers of IoGetFileObjectGenericMapping must be running at IRQL PASSIVE_LEVEL.

See Also

IoCheckShareAccess, IoSetShareAccess, ZwCreateFile


IoGetFunctionCodeFromCtlCode

ULONG
IoGetFunctionCodeFromCtlCode(
IN ULONG ControlCode
);

IoGetFunctionCodeFromCtlCode returns the value of the Function in a given IOCTL_XXX control code.

Return Value

IoGetFunctionCodeFromCtlCode returns the value of the Function field (bits 2 through 13, inclusive) in the IOCTL_XXX.

Parameters

ControlCode
Points to the IOCTL _XXX (or FSCTL_XXX) in the driver’s I/O stack location of the IRP at Parameters.DeviceIoControl.IoControlCode.

Comments

See Part II of this manual for more information about the layout of IOCTL_XXX codes and using the CTL_CODE macro.

File system drivers also can call this routine to extract FSCTL_XXX function codes on receipt of an IRP_MJ_FILE_SYSTEM_CONTROL request.

Callers of IoGetFunctionCodeFromCtlCode must be running at IRQL <= DISPATCH_LEVEL.

See Also

CTL_CODE, IoBuildDeviceIoControlRequest


IoGetInitialStack

PVOID
IoGetInitialStack(
);

Return Value

IoGetInitialStack returns the initial base address of the current thread’s stack.

Comments

Highest-level drivers can call this routine, particularly file systems attempting to determine whether they’ve been passed a pointer to a location on the current thread’s stack.

Callers of IoGetInitialStack must be running at IRQL PASSIVE_LEVEL.


IoGetNextIrpStackLocation

PIO_STACK_LOCATION
IoGetNextIrpStackLocation(
IN PIRP Irp
);

IoGetNextIrpStackLocation gives a higher level driver access to the next-lower driver’s I/O stack location in an IRP so the caller can set it up for the lower driver.

Parameters

Irp
Points to the IRP.

Return Value

IoGetNextIrpStackLocation returns a pointer to the next-lower-level driver’s I/O stack location in the given IRP.

Comments

Each driver that passes IRPs on to lower drivers must call IoGetNextIrpStackLocation so it can set up the stack location for the next-lower driver and call IoCallDriver with the IRP.

Callers of IoGetNextIrpStackLocation must be running at IRQL <= DISPATCH_LEVEL.

See Also

IO_STACK_LOCATION, IoCallDriver, IoGetCurrentIrpStackLocation, IoSetNextIrpStackLocation


IoGetRelatedDeviceObject

PDEVICE_OBJECT
IoGetRelatedDeviceObject(
IN PFILE_OBJECT FileObject
);

IoGetRelatedDeviceObject returns a pointer to the device object represented by the given file object.

Parameters

FileObject
Points to the file object representing the open device.

Return Value

IoGetRelatedDeviceObject returns a pointer to the device object.

Comments

The caller must be running at IRQL <= DISPATCH_LEVEL. Usually, callers of this routine are running at IRQL PASSIVE_LEVEL.

See Also

IoGetDeviceObjectPointer


IoInitializeDpcRequest

VOID
IoInitializeDpcRequest(
IN PDEVICE_OBJECT DeviceObject,
 IN PIO_DPC_ROUTINE DpcRoutine
);

IoInitializeDpcRequest registers a driver-supplied DpcForIsr routine when a device driver initializes.

Parameters

DeviceObject
Points to the device object representing the physical device that generates interrupts.
DpcRoutine
Points to the driver-supplied DpcForIsr routine, which is declared as follows:
VOID
(*PIO_DPC_ROUTINE)(
    IN PKDPC Dpc,
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    );

Comments

IoInitializeDpcRequest associates a driver-supplied DpcForIsr routine with a given device object so the driver’s ISR can call IoRequestDpc to queue the DpcForIsr. This routine completes interrupt-driven I/O operations at a lower IRQL than that of the ISR.

Callers of IoInitializeDpcRequest must be running at IRQL PASSIVE_LEVEL.

It is possible to call KeInitializeDpc to initialize another DPC at IRQL <= DISPATCH_LEVEL.

See Also

IoRequestDpc, KeInitializeDpc


IoInitializeIrp

VOID
IoInitializeIrp(
IN OUT PIRP Irp,
 IN USHORT PacketSize,
 IN CCHAR StackSize
);

IoInitializeIrp initializes a given IRP that was allocated by the caller.

Parameters

Irp
Points to the IRP to be initialized.
PacketSize
Specifies the size in bytes of the IRP.
StackSize
Specifies the number of stack locations in the IRP.

Comments

If a driver calls IoAllocateIrp, it also must call IoInitializeIrp with the same StackSize value and the pointer returned by IoAllocateIrp.

If the driver associates an MDL with the IRP it allocated, the driver is responsible for releasing the MDL when the IRP is completed.

An intermediate or highest-level driver also can call IoBuildDeviceIoControlRequest, IoBuildAsynchronousFsdRequest, or IoBuildSynchronousFsdRequest to set up requests it sends to lower-level drivers. Only a highest-level driver can call IoMakeAssociatedIrp.

Callers of IoInitializeIrp must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateIrp, IoAllocateMdl, IoBuildPartialMdl, IoFreeIrp, IoFreeMdl, IoSetNextIrpStackLocation, IoSizeOfIrp, IRP


IoInitializeTimer

NTSTATUS
IoInitializeTimer(
IN PDEVICE_OBJECT DeviceObject,
 IN PIO_TIMER_ROUTINE TimerRoutine,
 IN PVOID Context
);

IoInitializeTimer sets up a driver-supplied IoTimer routine associated with a given device object.

Parameters

DeviceObject
Points to a device object representing a device on which I/O operations can time out.
TimerRoutine
Points to the driver-supplied IoTimer routine, which is declared as follows:
VOID
(*PIO_TIMER_ROUTINE) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PVOID Context
    );
Context
Points to the driver-determined context with which its IoTimer routine will be called.

Return Value

IoInitializeTimer returns STATUS_SUCCESS if the IoTimer routine is set up.

Comments

A driver’s IoTimer routine is called once per second after the driver enables the timer by calling IoStartTimer.

The driver can disable the timer by calling IoStopTimer and can re-enable it again with IoStartTimer.

Callers of IoInitializeTimer must be running at IRQL PASSIVE_LEVEL.

When the timer is running, the I/O Manager calls the driver-supplied IoTimer routine once per second. Drivers whose time-out routines should be called at variable intervals or at intervals of finer granularity can set up a CustomTimerDpc routine and use the Ke..Timer routines.

See Also

IoStartTimer, IoStopTimer, KeInitializeTimer, KeSetTimer


IoIsErrorUserInduced

BOOLEAN
IoIsErrorUserInduced(
IN NTSTATUS Status
);

IoIsErrorUserInduced determines whether an I/O error encountered while processing a request to a removable-media device was caused by the user.

Parameters

Status
Specifies the current NTSTATUS value, usually within the driver’s DpcForIsr routine.

Return Value

IoIsErrorUserInduced returns TRUE if an I/O request failed because of a user-induced error.

Comments

This routine indicates whether an I/O request failed for one of the following user-correctable conditions:
STATUS_DEVICE_NOT_READY
STATUS_IO_TIMEOUT
STATUS_MEDIA_WRITE_PROTECTED
STATUS_NO_MEDIA_IN_DEVICE
STATUS_UNRECOGNIZED_MEDIA
STATUS_VERIFY_REQUIRED
STATUS_WRONG_VOLUME

If IoIsErrorUserInduced returns TRUE, the removable-media driver must call IoSetHardErrorOrVerifyDevice before completing the IRP.

Callers of IoIsErrorUserInduced must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoSetHardErrorOrVerifyDevice, IoAllocateErrorLogEntry, IoWriteErrorLogEntry


IoIsTotalDeviceFailure

BOOLEAN
IoIsTotalDeviceFailure(
IN NTSTATUS Status
);

IoIsTotalDeviceFailure determines whether a disk I/O error encountered while processing a request is unrecoverable.

Parameters

Status
Specifies the current NTSTATUS value, usually within a file system’s or fault-tolerant disk driver’s IoCompletion routine.

Return Value

IoIsTotalDeviceFailure returns TRUE if an I/O request failed because the physical device is out of commission.

Comments

This routine can be called only by higher-level drivers layered above a disk device driver.

If IoIsTotalDeviceFailure returns TRUE, a higher-level driver, such as a file system or fault-tolerant disk driver, usually logs an error before completing the IRP.

IoIsTotalDeviceFailure does not return TRUE for either of the status values STATUS_DEVICE_DATA_ERROR and STATUS_CRC_ERROR, which are assumed to indicate a sector failure rather than a total disk failure.

Callers of IoIsTotalDeviceFailure must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateErrorLogEntry, IoRaiseHardError, IoRaiseInformationalHardError, IoWriteErrorLogEntry


IoMakeAssociatedIrp

PIRP
IoMakeAssociatedIrp(
IN PIRP Irp,
 IN CCHAR StackSize
);

IoMakeAssociatedIrp allocates and initalizes an IRP to be associated with a master IRP sent to a highest-level driver, allowing the caller to split the original request and send associated IRPs on to lower-level drivers.

Parameters

Irp
Points to the master IRP that was input to a highest-level driver’s Dispatch routine.
StackSize
Specifies the number of stack locations to be allocated for the associated IRP. The value must be at least equal to the StackSize of the next-lower driver’s device object, but the associated IRP can have an additional stack location for the caller.

Return Value

IoMakeAssociatedIrp returns a pointer to the associated IRP or returns a NULL pointer if an IRP cannot be allocated.

Comments

Only a highest-level driver can call this routine.

The I/O Manager completes the master IRP automatically when lower drivers have completed all associated IRPs as long as the caller has not set its IoCompletion routine in an associated IRP and returned STATUS_MORE_PROCESSING_REQUIRED from its IoCompletion routine. In these circumstances, the caller must explicitly complete the master IRP when that driver has determined that all associated IRPs were completed.

Callers of IoMakeAssociatedIrp must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IoCallDriver, IoSetCompletionRoutine, IRP


IoMapTransfer

PHYSICAL_ADDRESS
IoMapTransfer(
IN PADAPTER_OBJECT AdapterObject,
 IN PMDL Mdl,
 IN PVOID MapRegisterBase,
 IN PVOID CurrentVa,
 IN OUT PULONG Length,
 IN BOOLEAN WriteToDevice
);

IoMapTransfer sets up a number of map registers (up to the number returned by HalGetAdapter) for the given adapter object to map a transfer from a locked-down buffer specified by Mdl, the given CurrentVa into the MDL, Length in bytes to be transferred, and transfer direction.

Parameters

AdapterObject
Points to the adapter object pointer returned by HalGetAdapter and already passed in a call to IoAllocateAdapterChannel for the current IRP’s transfer request.
Mdl
Points to the MDL describing the buffer either in the current IRP at MdlAddress or the MDL that the driver of a slave device using auto-initialize mode set up to describe the driver’s common buffer.
MapRegisterBase
Points to the handle returned by IoAllocateAdapterChannel, which the driver already called for the current IRP.
CurrentVa
Points to the current virtual address in the buffer, described by the Mdl, to be mapped for a DMA transfer operation.
Length
Specifies the length, in bytes, to be mapped. If the driver indicated that its device was a busmaster with scatter/gather support when it called HalGetAdapter, the value of Length on return from IoMapTransfer indicates how many bytes were mapped. Otherwise, the input and output values of Length are identical.
WriteToDevice
Indicates the direction of the transfer operation: TRUE for a transfer from the locked-down buffer to the device.

Return Value

IoMapTransfer returns the logical address of the region mapped, which the driver of a busmaster adapter can use. Drivers of devices that use a system DMA controller cannot use this value and should ignore it.

Comments

The AdapterObject must have already been allocated to the driver in a preceding call to IoAllocateAdapterChannel.

The number of map registers that can be set up cannot exceed the maximum returned when the driver called HalGetAdapter.

The initial CurrentVa for the start of a packet-based DMA transfer can be obtained by calling MmGetMdlVirtualAddress. However, the value returned is an index into the Mdl, rather than a valid virtual address. If the driver must split a large transfer request into more than one DMA operation, CurrentVa and Length must be updated for each DMA operation.

The driver of a busmaster device with scatter/gather support can use the returned logical address and updated Length value to build a scatter/gather list, calling IoMapTransfer repeatedly until it has used all available map registers for the transfer operation.

Callers of IoMapTransfer must be running at IRQL <= DISPATCH_LEVEL.

See Also

ADDRESS_AND_SIZE_TO_SPAN_PAGES, HalAllocateCommonBuffer, HalGetAdapter, IoAllocateAdapterChannel, IoFlushAdapterBuffers, IoFreeAdapterChannel, IoFreeMapRegisters, KeFlushIoBuffers, MmGetMdlVirtualAddress


IoMarkIrpPending

VOID
IoMarkIrpPending(
IN OUT PIRP Irp
);

IoMarkIrpPending marks the given IRP, indicating that a driver’s Dispatch routine returned STATUS_PENDING because further processing is required by other driver routines.

Parameters

Irp
Points to the IRP to be marked as pending.

Comments

Unless a driver calls IoCompleteRequest from its Dispatch routine with a given IRP or passes the IRP on to lower drivers, it must call IoMarkIrpPending with the IRP. Otherwise, the I/O Manager attempts to complete the IRP as soon as the Dispatch routine returns control.

A routine that calls IoMarkIrpPending must return STATUS_PENDING.

Callers of IoMarkIrpPending must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoCompleteRequest, IoStartPacket, IRP


IoQueryDeviceDescription

NTSTATUS
IoQueryDeviceDescription(
IN PINTERFACE_TYPE BusType, /* optional */
IN PULONG BusNumber, /* optional */
IN PCONFIGURATION_TYPE ControllerType, /* optional */
IN PULONG ControllerNumber, /* optional */
IN PCONFIGURATION_TYPE PeripheralType, /* optional */
IN PULONG PeripheralNumber, /* optional */
IN PIO_QUERY_DEVICE_ROUTINE CalloutRoutine,
IN PVOID Context
);

IoQueryDeviceDescription retrieves hardware configuration information about a given bus, controller or peripheral object, or any combination of these three types from the \Registry\Machine\Hardware\Description tree.

Parameters

BusType
Specifies the type of bus searched for in the registry’s hardware description tree which can be one of the following values: Internal, Isa, Eisa, MicroChannel TurboChannel, or PCIBus. However, additional types of buses will be supported in future versions of Windows NT. The upper bound on the types supported is always MaximumInterfaceType.
BusNumber
Specifies the zero-based and system-assigned number of the bus. This parameter is optional. If BusType is supplied but no specific BusNumber is specified, information on all buses of type BusType is returned.
ControllerType
Specifies the type of controller for which to return information. It can be one of the following: DiskController, TapeController, CdRomController, WormController, SerialController, NetworkController, DisplayController, ParallelController, PointerController, KeyboardController, AudioController, or OtherController. If no ControllerType or PeripheralType value is specified, only bus information is returned.
ControllerNumber
Specifies the zero-based number of the controller. This parameter is optional. If ControllerType is supplied but a specific ControllerNumber is not, information on all controllers of type ControllerType is returned.
PeripheralType
Specifies the type of peripheral for which to return information. It can be one of the following: DiskPeripheral, FloppyDiskPeripheral, TapePeripheral, ModemPeripheral, MonitorPeripheral, PrinterPeripheral, PointerPeripheral, KeyboardPeripheral, TerminalPeripheral, OtherPeripheral, LinePeripheral, or NetworkPeripheral. If no peripheral type is specified, only bus information and controller information are returned.
PeripheralNumber
Specifies the zero-based number of the peripheral. This parameter is optional. If PeripheralType is supplied but a specific PeripheralNumber is not, information on all peripherals of type PeripheralType is returned.
CalloutRoutine
Points to a driver-supplied routine to be called when the requested information has been located. This routine is declared as follows:
NTSTATUS
(*PIO_QUERY_DEVICE_ROUTINE) (
    IN PVOID Context,
    IN PUNICODE_STRING PathName,
    IN INTERFACE_TYPE BusType,
    IN ULONG BusNumber,
    IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
    IN CONFIGURATION_TYPE ControllerType,
    IN ULONG ControllerNumber,
    IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
    IN CONFIGURATION_TYPE PeripheralType,
    IN ULONG PeripheralNumber,
    IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
    );
Context
Specifies the context value that is passed to the callback routine.

Return Value

IoQueryDeviceDescription returns the STATUS_XXX returned by the callback routine.

Comments

This routine queries the registry for description(s) of the given bus type and number, controller type and number, and/or peripheral type and number. The information retrieved is passed to a driver-supplied ConfigCallback routine.

While the bus, controller, and peripheral parameters are each optional, the caller must supply at least one type parameter.

On entry, the driver’s ConfigCallback routine is given pointers to registry keys for bus, controller, and/or peripheral information. Each such pointer is actually a pointer to an array of IO_QUERY_DEVICE_DATA_FORMAT pointers identified as follows:
IoQueryDeviceIdentifier
IoQueryDeviceConfigurationData
IoQueryDeviceComponentInformation

When the ConfigCallback routine returns control, these pointers become invalid. The driver’s ConfigCallback routine should save pertinent information about the I/O ports or device memory, the bus-relative interrupt vector or IRQL, and/or the DMA channel or port, that is available in the registry for the DriverEntry routine to use in subsequent calls to HalTranslateBusAddress (and possibly MmMapIoSpace), HalGetInterruptVector, and/or HalGetAdapter.

As an alternative, a driver can call HalGetBusData or HalGetBusDataByOffset to locate its device(s) and to retrieve bus-relative configuration information. Then, the driver can call IoAssignResources, which checks the input resource list against the hardware configuration information in the registry and also encapsulates most of the functionality of IoReportResourceUsage. As an alternative, drivers of PCI-type devices can call HalAssignSlotResources.

Callers of IoQueryDeviceDescription must be running at IRQL PASSIVE_LEVEL.

See Also

IoAssignResources, IoReportResourceUsage, HalAssignSlotResources, HalGetAdapter, HalGetBusData, HalGetBusDataByOffset, HalGetInterruptVector, HalTranslateBusAddress, MmMapIoSpace


IoRaiseHardError

VOID
IoRaiseHardError(
IN PIRP Irp,
 IN PVPB Vpb, /* optional */
IN PDEVICE_OBJECT RealDeviceObject
);

IoRaiseHardError causes a popup to be sent to the user, warning about device I/O errors that can indicate a physical device might be failing.

Parameters

Irp
Points to the IRP that failed because of a device I/O error.
Vpb
Points to the volume parameter block for the mounted file object.
RealDeviceObject
Points to the device object representing the physical device on which the I/O operation failed.

Comments

Highest-level drivers, particularly file system drivers, call this routine.

Callers of IoRaiseHardError must be running at IRQL < DISPATCH_LEVEL.

See Also

IoGetRelatedDeviceObject, IoIsTotalDeviceFailure, IoSetHardErrorOrVerifyDevice


IoRaiseInformationalHardError

BOOLEAN
IoRaiseInformationalHardError(
IN NTSTATUS ErrorStatus,
IN PUNICODE_STRING String, /* optional */
IN PKTHREAD Thread /* optional */
);

IoRaiseInformationalHardError sends a popup to the user, warning about a device I/O error that indicates why a user I/O request failed.

Parameters

ErrorStatus
Identifies the error status (IO_ERR_XXX).
String
Points to a Unicode string, which provides additional information about the error. Some NT status codes require a string parameter, such as a file or directory name. If the ErrorStatus does not require a parameter, then String is NULL.
Thread
Points to the thread whose IRP was failed due to the error.

Return Value

IoRaiseInformationalHardError returns TRUE if the popup was successfully queued. This routine returns FALSE if popups are disabled for Thread, a pool allocation failed, too many popups are already queued, or an equivalent popup is already pending a user response (such as waiting for the user to press RETURN).

Comments

IoRaiseInformationalHardError takes a system-defined NT error value as a parameter. Driver writers can use the event log APIs to communicate driver-defined event strings to the user.

Callers of IoRaiseInformationalHardError must be running at IRQL < DISPATCH_LEVEL.

See Also

IoSetHardErrorOrVerifyDevice, PsGetCurrentThread


IoReadPartitionTable

NTSTATUS
IoReadPartitionTable(
IN PDEVICE_OBJECT DeviceObject,
 IN ULONG SectorSize,
 IN BOOLEAN ReturnRecognizedPartitions,
 OUT struct _DRIVE_LAYOUT_INFORMATION **PartitionBuffer
);

IoReadPartitionTable reads a list of partitions on a disk having a specified sector size and creates an entry in the partition list for each recognized partition.

Parameters

DeviceObject
Points to the device object for the disk whose partitions are to be read.
SectorSize
Specifies the size of the sectors on the disk.
ReturnRecognizedPartitions
Indicates whether only recognized partitions or all partition entries should be returned.
PartitionBuffer
Is a pointer to an uninitialized address. If successful, IoReadPartitionTable allocates the memory for this buffer from nonpaged pool and returns the drive layout information in it.

Return Value

This routine returns a value of STATUS_SUCCESS if at least one sector table was read. Otherwise, it returns an error status and sets the pointer at PartitionBuffer to NULL.

Comments

Disk device drivers call this routine during driver initialization.

It is the responsibility of the caller to deallocate the PartitionBuffer that was allocated by this routine with ExFreePool.

The algorithm used by this routine is determined by the Boolean value ReturnRecognizedPartitions:

  • Read each partition table and, for each valid and recognized partition found, fill in a partition information entry. Extended partitions are located in order to find other partition tables, but no entries are built for them.

  • Read each partition table and, for each and every entry, fill in a partition information entry. Extended partitions are located to find each partition on the disk, and entries are built for these as well.

The drive layout structure contains a variable-sized array of partition information elements, defined as follows:

typedef struct _DRIVE_LAYOUT_INFORMATION {
    ULONG PartitionCount;
    ULONG Signature;                // of disk
    PARTITION_INFORMATION PartitionEntry[1];
} DRIVE_LAYOUT_INFORMATION, *PDRIVE_LAYOUT_INFORMATION;

typedef strtuct _PARTITION_INFORMATION {
    LARGE_INTEGER StartingOffset;
    LARGE_INTEGER PartitionLength;
    ULONG HiddenSectors;
    ULONG PartitionNumber;
    UCHAR PartitionType;            // 12-bit FAT etc.
    BOOLEAN BootIndicator;
    BOOLEAN RecognizedPartition;
    BOOLEAN RewritePartition;
} PARTITION_INFORMATION, *PPARTITION_INFORMATION;

For the currently defined PartitionType values, see the Win32 SDK.

Note that disk drivers also use the DRIVE_LAYOUT_INFORMATION structure to return and set partition information in response to IRP_MJ_DEVICE_CONTROL requests with the following I/O control codes:

IOCTL_DISK_GET_PARTITION_INFO
IOCTL_DISK_GET_DRIVE_LAYOUT
IOCTL_DISK_SET_DRIVE_LAYOUT

Callers of IoReadPartitionTable must be running at IRQL PASSIVE_LEVEL.

See Also

IOCTL_DISK_GET_PARTITION_INFO, IOCTL_DISK_GET_DRIVE_LAYOUT, IOCTL_DISK_SET_DRIVE_LAYOUT, IoSetPartitionInformation, IoWritePartitionTable


IoRegisterDriverReinitialization

VOID
IoRegisterDriverReinitialization(
IN PDRIVER_OBJECT DriverObject,
 IN PDRIVER_REINITIALIZE DriverReinitializationRoutine,
 IN PVOID Context
);

IoRegisterDriverReinitialization is called by a driver during its initialization or reinitialization to register its Reinitialize routine to be called again before the driver’s and, possibly the system’s, initialization is complete.

Parameters

DriverObject
Points to the driver object that was input to the DriverEntry routine.
DriverReinitializationRoutine
Specifies the entry point for the driver-supplied Reinitialize routine, which is declared as follows:
VOID
(*PDRIVER_REINITIALIZE)(
    IN PDRIVER_OBJECT DriverObject,
    IN PVOID Context,
    IN ULONG Count
    );
Context
Points to the context passed to the driver’s Reinitialize routine.

Comments

A driver can call this routine only if its DriverEntry routine will return STATUS_SUCCESS. If the driver-supplied Reinitialize routine must use the registry, the DriverEntry routine should include its input RegistryPath pointer as part of the context passed to the Reinitialize routine in this call.

If the driver is loaded dynamically, it is possible for this to occur during a normally running system, so all references to the reinitialization queue must be synchronized.

The Count input to a DriverReinitializationRoutine indicates how many times this routine has been called.

Callers of IoRegisterDriverReinitialization must be running at IRQL PASSIVE_LEVEL.

See Also

DRIVER_OBJECT


IoRegisterShutdownNotification

NTSTATUS
IoRegisterShutdownNotification(
IN PDEVICE_OBJECT DeviceObject
);

IoRegisterShutdownNotification registers a driver-supplied DispatchShutdown routine to be called before system shutdown. The driver gains control before the system is fully shut down.

Parameters

DeviceObject
Points to the driver’s device object.

Return Value

IoRegisterShutdownNotification returns STATUS_SUCCESS when the shutdown routine is registered.

Comments

Only highest-level drivers chained above an underlying nonmass-storage device can register their shutdown routines by calling IoRegisterShutdownNotification. For mass-storage drivers, an FSD handles shutdown requests and calls lower drivers to flush cached or buffered data out to the device before the system is shut down.

Callers of IoRegisterShutdownNotification must be running at IRQL PASSIVE_LEVEL.

See Also

IoUnregisterShutdownNotification


IoReleaseCancelSpinLock

VOID
IoReleaseCancelSpinLock(
IN KIRQL Irql
);

IoReleaseCancelSpinLock releases the cancel spin lock after the driver has changed the cancelable state of an IRP. This routine also releases the cancel spin lock from the driver’s Cancel routine.

Parameters

Irql
Points to the IRQL returned by IoAcquireCancelSpinLock.

Comments

This routine is a reciprocal to IoAcquireCancelSpinLock.

The holder of the cancel spin lock executes at DISPATCH_LEVEL IRQL after calling IoAcquireCancelSpinLock. IoReleaseCancelSpinLock restores the original IRQL of its caller.

Callers of IoReleaseCancelSpinLock must be running at IRQL DISPATCH_LEVEL.

See Also

IoAcquireCancelSpinLock, IoSetCancelRoutine


IoRemoveShareAccess

VOID
IoRemoveShareAccess(
IN PFILE_OBJECT FileObject,
 IN OUT PSHARE_ACCESS ShareAccess
);

IoRemoveShareAccess removes the access and share-access information for a given open instance of a file object.

Parameters

FileObject
Points to the file object, which usually is being closed by the current thread.
ShareAccess
Points to the share-access structure that describes how the open file object is currently being accessed.

Comments

This routine is a reciprocal to IoUpdateShareAccess.

Callers of IoRemoveShareAccess must be running at IRQL PASSIVE_LEVEL and in the context of the thread that requested a close of the given file object.

See Also

IoCheckShareAccess, IoSetShareAccess, IoUpdateShareAccess


IoReportResourceUsage

NTSTATUS
IoReportResourceUsage(
IN PUNICODE_STRING DriverClassName, /* optional */
IN PDRIVER_OBJECT DriverObject,
 IN PCM_RESOURCE_LIST DriverList, /* optional */
IN ULONG DriverListSize, /* optional */
IN PDEVICE_OBJECT DeviceObject,
 IN PCM_RESOURCE_LIST DeviceList, /* optional */
IN ULONG DeviceListSize, /* optional */
IN BOOLEAN OverrideConflict,
 OUT PBOOLEAN ConflictDetected
);

IoReportResourceUsage claims hardware resources, such as an interrupt vector, device memory range or a particular DMA controller channel in the \Registry\Machine\Hardware\ResourceMap tree, so that a subsequently loaded driver cannot attempt to use the same resources.

Parameters

DriverClassName
Points to a buffered Unicode String that describes the class of driver under which the resource information should be stored. A default type Other is used if none is given, and a new key is created in the registry if a unique name is supplied.
DriverObject
Points to the driver object that was input to the DriverEntry routine.
DriverList
Points to the driver’s resource list if the driver claims the same resources for all its devices. This pointer is NULL if the driver claims resources for its devices separately.
DriverListSize
Specifies the size in bytes of the driver’s resource list if the DriverList pointer is nonNULL; otherwise, zero.
DeviceObject
Points to the driver-created device object representing a device for which the driver is attempting to claim resources.
DeviceList
Points to the device’s resource list, if the driver claims resources separately for each of its devices.
DeviceListSize
Specifies the size in bytes of the DeviceList if the DeviceList pointer is nonNULL; otherwise zero.
OverrideConflict
Specifies a Boolean value that indicates whether the information should be written into the registry even if a conflict is found with another driver or device. The default value is FALSE.
ConflictDetected
Points to a Boolean value set to TRUE on return from IoReportResourceUsage if a previously loaded driver has already claimed a resource specified in the caller’s DriverList or DeviceList.

Return Value

IoReportResourceUsage can return one of the following:

STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES

Comments

The values supplied in the CM_RESOURCE_LIST must be identical to those found in the driver’s call to IoQueryDeviceDescription, HalGetBusDataByOffset, or HalGetBusData, not those returned by the driver’s calls to HalTranslateBusAddress, HalGetAdapter, or HalGetInterruptVector.

This routine automatically searches the configuration registry for resource conflicts between resources requested and resources claimed by previously installed drivers. The contents of DriverList or DeviceList are matched against all other resource lists stored in the registry to determine whether a conflict exists.

If no conflict is detected or if OverrideConflict is set to TRUE, this routine creates appropriate entries in the registry ResourceMap that contains the specified resource lists.

If OverrideConflict is set to FALSE, this routine logs an error recording the exact nature of the conflict that is displayed in the Win32 event viewer. If OverrideConflict is reset to TRUE, no such error is reported if a resource conflict exists and the caller’s resource list is written into the registry. However, the caller cannot use any resource for which a conflict was detected.

If a driver claims resources on a device-specific basis for more than one device, the driver must call this routine for each such device.

This routine can be called more than once for a given device or driver. If a new resource list is given, it will replace the previous resource list in the registry. A driver must call IoReportResourceUsage with a DriverList or DeviceList CM_RESOURCE_LIST in which the Count is zero to erase its claim on resources in the registry if the driver is unloaded.

A CM_RESOURCE_LIST contains two variable-sized arrays. Each array has a default size of one. If either array has more than one element, memory must be allocated dynamically to contain the additional elements. A side effect of this definition is that only one CM_PARTIAL_RESOURCE_DESCRIPTOR can be part of each CM_FULL_RESOURCE_DESCRIPTOR in the list, except for the last full resource descriptor in the CM_RESOURCE_LIST, which can have additional partial resource descriptors in its array.

As an alternative, a device driver can call HalGetBusDataByOffset or HalGetBusData to locate its device(s) and to retrieve bus-relative configuration information. Then the driver can call IoAssignResources, which encapsulates most of the functionality of IoReportResourceUsage, allows the caller to specify preferred and alternative resources in a single IO_RESOURCE_REQUIREMENTS_LIST, and returns a CM_RESOURCE_LIST specifying the hardware resources it claimed on behalf of the caller. Drivers of PCI-type devices can call HalAssignSlotResources, rather than IoReportResourceUsage or IoAssignResources.

Callers of IoReportResourceUsage must be running at IRQL PASSIVE_LEVEL.

See Also

CM_FULL_RESOURCE_DESCRIPTOR, CM_PARTIAL_RESOURCE_DESCRIPTOR, CM_RESOURCE_LIST, HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, IoAssignResources, IoQueryDeviceDescription


IoRequestDpc

VOID
IoRequestDpc(
IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp,
 IN PVOID Context
);

IoRequestDpc queues a driver-supplied DpcForIsr routine from the ISR to complete interrupt-driven I/O processing at a lower IRQL.

Parameters

DeviceObject
Points to the device object for which the request that caused the interrupt is being processed.
Irp
Points to the current IRP for the specified device.
Context
Points to a driver-determined context to be passed to the DPC routine.

Comments

Callers of IoRequestDpc must be running at DIRQL.

Because IoRequestDpc is called from the device driver’s ISR, the DIRQL is the SynchronizeIrql value that was specified when the driver called IoConnectInterrupt. However, it is actually possible to queue a DPC at any IRQL >= DISPATCH_LEVEL using the Ke..Dpc routines.

See Also

IoInitializeDpcRequest, KeInitializeDpc, KeInsertQueueDpc


IoSetCancelRoutine

PDRIVER_CANCEL
IoSetCancelRoutine(
IN PIRP Irp,
 IN PDRIVER_CANCEL CancelRoutine
);

IoSetCancelRoutine sets up a driver-supplied Cancel routine to be called if a given IRP is canceled. This routine can disable the Cancel routine currently set in an IRP.

Parameters

Irp
Points to the IRP being put into or removed from a cancelable state.
CancelRoutine
Specifies the entry point of the caller-supplied Cancel routine to be called if the specified IRP is canceled or is NULL if the given IRP is being removed from the cancelable state. This routine is declared as follows:
VOID
(*PDRIVER_CANCEL)(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

Return Value

IoSetCancelRoutine returns the previous value of Irp->CancelRoutine.

Comments

A driver must hold the system cancel spin lock when calling this routine if the driver uses the I/O-manager-supplied device queue in the device object. The driver executes at IRQL DISPATCH_LEVEL after calling IoAcquireCancelSpinLock until it releases the cancel spin lock with IoReleaseCancelSpinLock.

If the driver manages its own queue(s) of IRPs, then the driver need not hold the cancel spin lock when calling this routine. IoSetCancelSpinLock uses an interlocked exchange intrinsic to set the address of the Cancel routine as an atomic operation. Reduced usage of the cancel spin lock can improve driver performance and overall system performance.

Driver Cancel routines are called at IRQL DISPATCH_LEVEL with the cancel spin lock held. The Cancel routine must release the cancel spin lock before it returns control.

See Also

IoAcquireCancelSpinLock, IoReleaseCancelSpinLock


IoSetCompletionRoutine

VOID
IoSetCompletionRoutine(
IN PIRP Irp,
 IN PIO_COMPLETION_ROUTINE CompletionRoutine,
 IN PVOID Context,
 IN BOOLEAN InvokeOnSuccess,
 IN BOOLEAN InvokeOnError,
 IN BOOLEAN InvokeOnCancel
);

IoSetCompletionRoutine registers an IoCompletion routine to be called when the next-lower-level driver has completed the requested operation for the given IRP.

Parameters

Irp
Points to the IRP that the driver wants to track.
CompletionRoutine
Specifies the entry point for the driver-supplied IoCompletion routine to be called when the next-lower driver completes the packet. This routine is declared as follows:
NTSTATUS
(*PIO_COMPLETION_ROUTINE)(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    );
Context
Points to a driver-determined context to pass to the IoCompletion routine.
InvokeOnSuccess
Specifies whether the completion routine is called if the IRP is completed with STATUS_SUCCESS in the I/O status block.
InvokeOnError
Specifies whether the completion routine is called if the IRP is completed with an error STATUS_XXX in the I/O status block.
InvokeOnCancel
Specifies whether the completion routine is called if the IRP is completed with STATUS_CANCELLED set in the I/O status block.

Comments

This routine sets the transfer address of the IoCompletion routine in the given IRP. The lowest-level driver in a chain of layered drivers cannot call this routine.

IoSetCompletionRoutine registers the specified routine to be called when the next-lower-level driver has completed the requested operation in any or all of the following ways:

  • Successfully

  • With an error

  • Canceled the IRP

Usually, the I/O status block is set by the underlying device driver. It is read but not altered by any higher-level drivers’ IoCompletion routines.

Higher-level drivers that allocate IRP’s with IoAllocateIrp or IoBuildAsynchronousFsdRequest must call this routine with all InvokeOnXxx parameters set to TRUE before passing the driver-allocated IRP to IoCallDriver. When the IoCompletion routine is called with such an IRP, it must free the driver-allocated IRP and any other resources that the driver set up for the request, such as MDLs with IoBuildPartialMdl. Such a driver should return STATUS_MORE_PROCESSING_REQUIRED when it calls IoFreeIrp to forstall the I/O Manager’s completion processing for the driver-allocated IRP.

Callers of IoSetCompletionRoutine must be running at IRQL <= DISPATCH_LEVEL.

See Also

IO_STACK_LOCATION, IoAllocateIrp, IoBuildAsynchronousFsdRequest, IRP, IoBuildPartialMdl, IoFreeIrp


IoSetHardErrorOrVerifyDevice

VOID
IoSetHardErrorOrVerifyDevice(
IN PIRP Irp,
 IN PDEVICE_OBJECT DeviceObject
);

IoSetHardErrorOrVerifyDevice must be called before completing an IRP if a removable-media driver’s call to IoIsErrorUserInduced returns TRUE.

Parameters

Irp
Points to the IRP for which the driver encountered a user-induced error.
DeviceObject
Points to the target device to be verified for the I/O operation.

Comments

A file system driver uses the corresponding file object of the specified device object to send a popup to the user who can correct the error and retry the operation or cancel it.

Calling IoSetHardErrorOrVerifyDevice ensures that the popup is sent to the user thread that originally made the I/O request to the target device on which the media might have changed.

Callers of IoSetHardErrorOrVerifyDevice must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoIsErrorUserInduced, IoRaiseHardError, IoRaiseInformationalHardError


IoSetNextIrpStackLocation

VOID
IoSetNextIrpStackLocation(
IN OUT PIRP Irp
);

IoSetNextIrpStackLocation sets the IRP stack location in a driver-allocated IRP to that of the caller.

Parameters

Irp
Points to the IRP whose stack location is to be set.

Comments

In general, this routine is seldom used by drivers. It is primarily used by drivers that require their own stack location in an IRP that they have allocated, on their own, to send to another driver.

IoSetNextIrpStackLocation is generally not needed because either:

  • The driver received the IRP it is passing from another, higher-level driver, and so it already owns a stack location,

  • Or, the driver allocated the IRP but does not need its own stack location because it can keep everything that it needs in a context block whose address can be passed to its IoCompletion routine.

Care should be taken if this routine is called, especially when allocating the IRP with IoAllocateIrp or IoMakeAssociatedIrp. The writer of the allocating driver must remember that a caller-specific stack location is not included in the number of stack locations required by the lower-level drivers to which it sends IRPs with IoCallDriver. A driver must explicitly specify a stack location for itself in its call to IoAllocateIrp or IoMakeAssociatedIrp if it calls IoSetNextIrpStackLocation with the IRP returned by either routine.

A driver cannot call IoSetNextIrpStackLocation with any IRP it allocates by calling IoBuildAsynchronousFsdRequest, IoBuildDeviceIoControlRequest, or IoBuildSynchronousFsdRequest.

Callers of IoSetNextIrpStackLocation must be running at IRQL <= DISPATCH_LEVEL.

See Also

IO_STACK_LOCATION, IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IoCallDriver, IoSetCompletionRoutine


IoSetPartitionInformation

NTSTATUS
IoSetPartitionInformation(
IN PDEVICE_OBJECT DeviceObject,
 IN ULONG SectorSize,
 IN ULONG PartitionNumber,
 IN ULONG PartitionType
);

IoSetPartitionInformation sets the partition type and number in a partition table entry for a given disk represented by the device object.

Parameters

DeviceObject
Points to the device object representing the device on which the partition type is to be set.
SectorSize
Specifies the size, in bytes, of sectors on the disk.
PartitionNumber
Specifies the partition number on the device whose partition type is to be set.
PartitionType
Specifies the type for the partition. For the currently defined PartitionType values, see the Win32 SDK.

Return Value

If IoSetPartitionInformation returns STATUS_SUCCESS, the disk driver updates its notion of the partition type for this partition in its device extension.

Comments

This routine is called when a disk device driver is asked to set the partition type in a partition table entry by an IRP_MJ_DEVICE_CONTROL request. This request is generally issued by the format utility, which performs I/O control functions on the partition. The driver passes a pointer to the device object representing the physical disk and the number of the partition associated with the device object that the format utility has open.

This routine is synchronous and must be called by the disk driver’s Dispatch routine or by a driver thread. Thus, all user and file system threads must be prepared to enter a wait state when issuing the device control request to set the partition type for the device.

This routine assumes the partition number passed in by the disk driver actually exists.

This routine must be called at PASSIVE_LEVEL IRQL because it uses a kernel event object to synchronize I/O completion on the device. The event cannot be set to the Signaled state without queuing and executing the I/O system’s special kernel APC routine for I/O completion.

See Also

IoReadPartitionTable, IoWritePartitionTable


IoSetShareAccess

VOID
IoSetShareAccess(
IN ACCESS_MASK DesiredAccess,
 IN ULONG DesiredShareAccess,
 IN OUT PFILE_OBJECT FileObject,
 OUT PSHARE_ACCESS ShareAccess
);

IoSetShareAccess sets up the access rights for sharing the given file object.

Parameters

DesiredAccess
Specifies the access level for the given FileObject.
DesiredShareAccess
Specifies a requested share access to be set for the file object.
FileObject
Points to the file object whose share access is being set or reset.
ShareAccess
Specifies the share access that was set for the given FileObject on return from IoSetShareAccess.

Comments

If a kernel-mode driver calls this routine, it must be a highest-level driver. That is, the call must occur in the context of the first thread that attempts to open the FileObject.

This routine sets the access and share access information when the given file object is first opened.

Generally, FSDs are most likely to call this routine. However, other highest-level drivers can call IoSetShareAccess to control what kind of access is allowed to a driver-created device object associated with the given FileObject.

Callers of IoSetShareAccess must be running at IRQL PASSIVE_LEVEL.

See Also

IoCheckShareAccess, IoGetFileObjectGenericMapping, IoGetRelatedDeviceObject, IoRemoveShareAccess, IoUpdateShareAccess


IoSizeOfIrp

USHORT
IoSizeOfIrp(
IN CCHAR StackSize
);

IoSizeOfIrp determines the size in bytes for an IRP, given the number of stack locations in the IRP.

Parameters

StackSize
Specifies the number of stack locations for the IRP.

Return Value

IoSizeOfIrp returns the size, in bytes, of the IRP.

Comments

Callers of IoSizeOfIrp can be running at any IRQL level.

The input StackSize value is either that of the next-lower driver’s device object or one more than that value.

See Also

IoAllocateIrp, IoMakeAssociatedIrp


IoStartNextPacket

VOID
IoStartNextPacket(
IN PDEVICE_OBJECT DeviceObject,
 IN BOOLEAN Cancelable
);

IoStartNextPacket dequeues the next IRP, if any, from the given device object’s associated device queue and calls the driver’s StartIo routine.

Parameters

DeviceObject
Points to the device object for which the IRP is to be dequeued.
Cancelable
Specifies whether IRPs in the device queue can be canceled. TRUE means the update of the current IRP is synchronized using the cancel spin lock.

Comments

If there are no IRPs currently in the device queue for the target DeviceObject, this routine simply returns control to the caller.

Drivers that do not have a StartIo routine cannot call IoStartNextPacket.

Callers of IoStartNextPacket must be running at IRQL DISPATCH_LEVEL. Usually, this routine is called from a device driver’s DpcForIsr or CustomDpc routine, both of which are run at IRQL DISPATCH_LEVEL.

See Also

DEVICE_OBJECT, IoStartNextPacketByKey, IoStartPacket


IoStartNextPacketByKey

VOID
IoStartNextPacketByKey(
IN PDEVICE_OBJECT DeviceObject,
 IN BOOLEAN Cancelable,
 IN ULONG Key
);

IoStartNextPacketByKey dequeues the next packet from the given device object’s associated device queue according to a specified sort-key value and calls the driver’s StartIo routine with that IRP.

Parameters

DeviceObject
Points to the device object for which the IRP is to be dequeued.
Cancelable
Specifies whether IRPs in the device queue can be canceled. TRUE means the update of the current IRP is synchronized by using the cancel spin lock.
Key
Specifies the sort key that determines which entry to remove from the queue.

Comments

If there are no IRPs currently in the device queue for the target device object, this routine simply returns control to the caller.

Drivers that do not have a StartIo routine cannot call IoStartNextPacketByKey.

Callers of IoStartNextPacketByKey must be running at IRQL <= DISPATCH_LEVEL. Usually, this routine is called from a device driver’s DpcForIsr or CustomDpc routine, both of which are run at IRQL DISPATCH_LEVEL.

See Also

DEVICE_OBJECT, IoStartNextPacket, IoStartPacket


IoStartPacket

VOID
IoStartPacket(
IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp,
 IN PULONG Key, /* optional */
IN PDRIVER_CANCEL CancelFunction /* optional */
);

IoStartPacket calls the driver’s StartIo routine with the given IRP or inserts the IRP into the device queue associated with the given device object if the device is already busy.

Parameters

DeviceObject
Points to the target device object for the IRP.
Irp
Points to the IRP to be processed.
Key
Points to a value that determines where to insert the packet into the device queue. If this is zero, the packet is inserted at the tail of the device queue.
CancelFunction
Specifies the entry point for a driver-supplied Cancel routine.

Comments

If the driver is already busy processing a request for the target device object, then the packet is queued in the device queue. Otherwise, this routine calls the driver’s StartIo routine with the specified IRP.

If a nonNULL CancelFunction pointer is supplied, it is set in the IRP so the driver’s Cancel routine is called if the IRP is canceled before its completion.

Drivers that do not have a StartIo routine cannot call IoStartPacket.

Callers of IoStartPacket must be running at IRQL <= DISPATCH_LEVEL. Usually, this routine is called from a device driver’s Dispatch routine at IRQL PASSIVE_LEVEL.

See Also

DEVICE_OBJECT, IoMarkIrpPending, IoSetCancelRoutine, IoStartNextPacket, IoStartNextPacketByKey


IoStartTimer

VOID
IoStartTimer(
IN PDEVICE_OBJECT DeviceObject
);

IoStartTimer enables the timer associated with a given device object so the driver-supplied IoTimer routine is called once per second.

Parameters

DeviceObject
Points to a device object whose timer routine is to be called.

Comments

The DriverEntry routine must have set up the IoTimer routine for the given DeviceObject by calling IoInitializeTimer.

Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoInitializeTimer, IoStopTimer, KeInitializeDpc, KeInitializeTimer, KeSetTimer


IoStopTimer

VOID
IoStopTimer(
IN PDEVICE_OBJECT DeviceObject
);

IoStopTimer disables the timer for a specified device object so the driver-supplied IoTimer routine is not called.

Parameters

DeviceObject
Points to the device object with which the IoTimer routine is associated.

Comments

The driver-supplied IoTimer routine can be re-enabled with a call to IoStartTimer.

Do not call IoStopTimer from within a driver’s IoTimer routine.

Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoInitializeTimer, IoStartTimer


IoUnregisterShutdownNotification

VOID
IoUnregisterShutdownNotification(
IN PDEVICE_OBJECT DeviceObject
);

IoUnregisterShutdownNotification removes a registered driver from the shutdown notification queue. The driver is not notified when the system is being shut down.

Parameters

DeviceObject
Points to the driver’s device object.

Comments

IoUnregisterShutdownNotification can be called by a driver only if that driver previously called IoRegisterShutdownNotification with the given DeviceObject. This routine is usually called from a driver’s Unload routine.

Callers of IoUnregisterShutdownNotification must be running at IRQL PASSIVE_LEVEL.

See Also

IoRegisterShutdownNotification


IoUpdateShareAccess

VOID
IoUpdateShareAccess(
IN PFILE_OBJECT FileObject,
 IN OUT PSHARE_ACCESS ShareAccess
);

IoUpdateShareAccess updates the share access for the given file object, usually when the file is being opened.

Parameters

FileObject
Points to a referenced file object representing the file or associated device object for which to update the share access.
ShareAccess
Specifies the type of share access the caller would like to update. This parameter can be one of the following values:

Value

Meaning

FILE_SHARE_READ

Other open operations may be performed on the file for read access.

FILE_SHARE_WRITE

Other open operations may be performed on the file for write access.

FILE_SHARE_DELETE

Other open operations may be performed on the file for delete access.

Comments

Callers of IoUpdateShareAccess must be running at IRQL PASSIVE_LEVEL. The caller must have made a successful call to IoCheckShareAccess with Update set to FALSE before it called IoUpdateShareAccess.

See Also

IoCheckShareAccess, IoRemoveShareAccess, IoSetShareAccess


IoWriteErrorLogEntry

VOID
IoWriteErrorLogEntry(
IN PVOID ElEntry
);

IoWriteErrorLogEntry queues a given error log packet to the system error logging thread.

Parameters

ElEntry
Points to the error log packet the driver has allocated with IoAllocateErrorLogEntry and filled in.

Comments

Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateErrorLogEntry


IoWritePartitionTable

NTSTATUS
IoWritePartitionTable(
IN PDEVICE_OBJECT DeviceObject,
 IN ULONG SectorSize,
 IN ULONG SectorsPerTrack,
 IN ULONG NumberOfHeads,
 IN struct _DRIVE_LAYOUT_INFORMATION *PartitionBuffer
);

IoWritePartitionTable writes partition tables from the entries in the partition list buffer for each partition on the disk represented by the given device object.

Parameters

DeviceObject
Points to the device object representing the disk whose partition tables are to be written.
SectorSize
Specifies the size in bytes of sectors on the device.
SectorsPerTrack
Specifies the track size on the device.
NumberOfHeads
Specifies the number of tracks per cylinder.
PartitionBuffer
Points to the drive layout buffer that contains the partition list entries. For more detailed information about the DRIVE_LAYOUT_INFORMATION structure, see IoReadPartitionTable.

Return Value

If all writes are completed without error, IoWritePartitionTable returns STATUS_SUCCESS.

Comments

IoWritePartitionTable is called when a disk device driver is asked to set the partition type in a partition table entry or to repartition the disk by an IRP_MJ_DEVICE_CONTROL request. The device control request is generally issued by the format utility, which performs I/O control functions on the partitions and disks in the machine.

To reset a partition type, the driver passes a pointer to the device object representing the physical disk and the number of the partition associated with the device object that the format utility has open. When a disk is to be repartitioned dynamically, the disk driver must tear down its set of device objects representing the current disk partitions and create a new set of device objects representing the new partitions on the disk.

Applications that create and delete partitions and require full descriptions of the system should call IoReadPartitionTable with ReturnRecognizedPartitions set to FALSE. The drive layout structure can be modified by the system format utility to reflect a new configuration of the disk.

IoWritePartitionTable is synchronous. It must be called by the disk driver’s Dispatch routine or by a driver thread. Thus, all user and file system threads must be prepared to enter a wait state when issuing the device control request to reset partition types for the device.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

IoCreateDevice, IoReadPartitionTable, IoSetPartitionInformation


Chapter 5 Kernel Routines

All kernel-mode drivers except video and SCSI miniport drivers and NDIS V3.0 drivers are likely to call at least some Ke Xxx routines.

References for the Ke Xxx routines are in alphabetical order.

For an overview of the functionality of these routines, see Chapter 1.


KeAcquireSpinLock

VOID
KeAcquireSpinLock(
IN PKSPIN_LOCK SpinLock,
 OUT PKIRQL OldIrql
);

KeAcquireSpinLock acquires a spin lock so the caller can synchronize access to shared data in a multiprocessor-safe way by raising IRQL.

Parameters

SpinLock
Points to an initialized spin lock for which the caller provides the storage.
OldIrql
Points to a variable that is set to the current IRQL when this call occurs.

Comments

The current IRQL is saved in OldIrql. Then, the current IRQL is reset to DISPATCH_LEVEL, and the specified spin lock is acquired.

The OldIrql value must be specified when the spin lock is released with KeReleaseSpinLock.

Spin locks can cause serious problems if not used judiciously. In particular, no deadlock protection is performed and dispatching is disabled while the spin lock is held. Therefore:

  • The code within a critical region guarded by an spin lock must neither be pageable nor make any references to pageable data.

  • The code within a critical region guarded by a spin lock can neither call any external function that might access pageable data or raise an exception, nor can it generate any exceptions.

  • The caller should release the spin lock with KeReleaseSpinLock as quickly as possible.

Callers of KeAcquireSpinLock must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeAcquireSpinLockAtDpcLevel, KeInitializeSpinLock, KeReleaseSpinLock


KeAcquireSpinLockAtDpcLevel

VOID
KeAcquireSpinLockAtDpcLevel(
IN PKSPIN_LOCK SpinLock
);

KeAcquireSpinLockAtDpcLevel acquires a spin lock when the caller is already running at IRQL DISPATCH_LEVEL.

Parameters

SpinLock
Points to an initialized spin lock for which the caller must provide the storage.

Comments

Drivers call KeAcquireSpinLockAtDpcLevel instead of KeAcquireSpinLock for better driver performance if and only if they are already running at IRQL DISPATCH_LEVEL.

If a driver is running at IRQL < DISPATCH_LEVEL, it should call KeAcquireSpinLock to have IRQL raised by that routine. KeAcquireSpinLockAtDpcLevel assumes the caller is already running at IRQL DISPATCH_LEVEL, so no raise is necessary.

The caller should release the spin lock with KeReleaseSpinLockFromDpcLevel as quickly as possible.

See Also

KeAcquireSpinLock, KeInitializeSpinLock, KeReleaseSpinLock, KeReleaseSpinLockFromDpcLevel


KeBugCheck

VOID
KeBugCheck(
IN ULONG BugCheckCode
);

KeBugCheck brings down the system in a controlled manner when the caller discovers an unrecoverable inconsistency that would corrupt the system if the caller continued to run.

Parameters

BugCheckCode
Specifies a value that indicates the reason for the bug check.

Comments

A bug check is a system-detected error that causes an immediate, controlled shutdown of the system. Various kernel-mode components perform runtime consistency checking. When such a component discovers an unrecoverable inconsistency, it causes a bug check to be generated.

If possible, all kernel-mode components log an error and continue to run, rather than calling KeBugCheck, unless the system itself could become corrupted by the inconsistency discovered. Most system components call KeBugCheckEx, which provides more information about the cause of such an inconsistency than KeBugCheck.

Callers of KeBugCheck can be running at any IRQL.

See Also

IoAllocateErrorLogEntry, IoWriteErrorLogEntry, KeBugCheckEx, KeRegisterBugCheckCallback


KeBugCheckEx

VOID
KeBugCheckEx(
IN ULONG BugCheckCode,
 IN ULONG BugCheckParameter1,
 IN ULONG BugCheckParameter2,
 IN ULONG BugCheckParameter3,
 IN ULONG BugCheckParameter4
);

KeBugCheckEx brings down the system in a controlled manner when the caller discovers an unrecoverable inconsistency that would corrupt the system if the caller continued to run.

Parameters

BugCheckCode
Specifies a value that indicates the reason for the bug check.
BugCheckParameterX
Supply additional information, such as the address and data where a memory-corruption error occurred, depending on the value of BugCheckCode.

Comments

A bug check is a system-detected error that causes an immediate, controlled shutdown of the system. Various kernel-mode components perform runtime consistency checking. When such a component discovers an unrecoverable inconsistency, it causes a bug check to be generated.

If possible, all kernel-mode components log an error and continue to run, rather than calling KeBugCheckEx, unless the system itself could become corrupted by the inconsistency discovered.

Callers of KeBugCheckEx can be running at any IRQL.

See Also

IoAllocateErrorLogEntry, IoWriteErrorLogEntry, KeBugCheck, KeRegisterBugCheckCallback


KeCancelTimer

BOOLEAN
KeCancelTimer(
IN PKTIMER Timer
);

KeCancelTimer dequeues a timer object before the timer interval, if any was set, expires.

Parameters

Timer
Points to an initialized timer object, for which the caller provides the storage.

Return Value

If the specified timer object is in the system timer queue, KeCancelTimer returns TRUE.

Comments

If the timer object is currently in the system timer queue, it is removed from the queue. If a DPC object is associated with the timer, it too is canceled. Otherwise, no operation is performed.

Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeInitializeTimer, KeReadStateTimer, KeSetTimer


KeClearEvent

VOID
KeClearEvent(
IN PKEVENT Event
);

KeClearEvent sets the given event to the Not-Signaled state.

Parameters

Event
Points to an initialized dispatcher object of type event for which the caller supplies the storage.

Comments

The state of Event is set to Not-Signaled, meaning its value is set to zero.

For better performance, use KeClearEvent unless the caller uses the value returned by KeResetEvent to determine what to do next.

Callers of KeClearEvent must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeInitializeEvent, KeReadStateEvent, KeResetEvent, KeSetEvent


KeDelayExecutionThread

NTSTATUS
KeDelayExecutionThread(
IN KPROCESSOR_MODE WaitMode,
 IN BOOLEAN Alertable,
 IN PLARGE_INTEGER Interval
);

KeDelayExecutionThread puts the current thread into an alertable or nonalertable wait state for a given interval.

Parameters

WaitMode
Specifies the processor mode in which the caller is waiting, which can be either KernelMode or UserMode. Lower-level drivers should specify KernelMode.
Alertable
Specifies TRUE if the wait is alertable. Lower-level drivers should specify FALSE.
Interval
Specifies the absolute or relative time, in units of 100 nanoseconds, for which the wait is to occur. A negative value indicates relative time. Absolute expiration times track any changes in system time; relative expiration times are not affected by system time changes.

Return Value

The return value of KeDelayExecutionThread identifies how the delay was completed, and can be one of the following:
Value | Meaning
STATUS_SUCCESS | The delay was completed because the specified interval elapsed.
STATUS_ALERTED | The delay was completed because the thread was alerted.
STATUS_USER_APC | A user-mode APC was delivered before the given Interval expired.

Comments

The expiration time is computed and the current thread is put in a wait state. When the specified interval has passed, the thread exits the wait state and is put in the ready state, becoming eligible for execution.

The Alertable parameter specifies whether the thread can be alerted in the wait state. If the value of this parameter is TRUE and the thread is alerted for a mode that is equal to or more privileged than the given WaitMode, the thread’s wait will be satisfied with a completion status of STATUS_ALERTED.

If the WaitMode parameter is UserMode and the Alertable parameter is TRUE, the thread can also be awakened to deliver a user-mode APC. Kernel-mode APCs always cause the subject thread to be awakened if the wait IRQL is PASSIVE_LEVEL and no kernel APC is in progress.

The expiration time of the delay is expressed as either an absolute time at which the delay is to expire, or a time relative to the current system time. If the Interval parameter is a negative value, the expiration time is relative.

Callers of KeDelayExecutionThread must be running at IRQL < DISPATCH_LEVEL.

See Also

KeQuerySystemTime


KeDeregisterBugCheckCallback

BOOLEAN
KeDeregisterBugCheckCallback(
IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord
);

KeDeregisterBugCheckCallback removes a device driver’s callback routine from the set of registered bug-check callbacks.

Parameters

CallbackRecord
Points to the caller-allocated storage containing an initialized bug-check callback record. This pointer was previously passed in successful calls to KeInitializeCallbackRecord and KeRegisterBugCheckCallback.

Return Value

KeDeregisterBugCheckCallback returns TRUE if the caller-supplied bug-check callback routine will no longer be called if a bug check occurs. If the given callback record was not registered, it returns FALSE.

Comments

KeDeregisterBugCheckCallback is the reciprocal of KeRegisterBugCheckCallback.

If an unloadable device driver sets up a bug-check callback routine with KeRegisterBugCheckCallback, that driver’s Unload routine must call KeDeregisterBugCheckCallback before it frees the storage it allocated at CallbackRecord.

Callers of KeDeregisterBugCheckRecord can be running at any IRQL. Usually, a device driver is running at IRQL PASSIVE_LEVEL in its Unload routine when it calls KeDeregisterBugCheckRecord.

See Also

KeBugCheck, KeBugCheckEx, KeInitializeCallbackRecord, KeRegisterBugCheckCallback


KeEnterCriticalRegion

VOID
KeEnterCriticalRegion(
);

KeEnterCriticalRegion disables the delivery of special kernel APCs temporarily.

Comments

Highest-level drivers can call this routine while running in the context of a the thread that requested the current I/O operation. Any caller of this routine should call KeLeaveCriticalRegion as quickly as possible.

Callers of KeEnterCriticalRegion must be running at IRQL PASSIVE_LEVEL.

See Also

KeLeaveCriticalRegion


KeFlushIoBuffers

VOID
KeFlushIoBuffers(
IN PMDL Mdl,
 IN BOOLEAN ReadOperation,
 IN BOOLEAN DmaOperation
);

KeFlushIoBuffers flushes the memory region described by a given MDL from caches of all processors.

Parameters

Mdl
Points to an MDL that describes the range for the I/O buffer.
ReadOperation
Specifies TRUE if the flush is being performed for a read operation.
DmaOperation
Specifies TRUE for a DMA transfer, FALSE for PIO.

Comments

Device drivers call KeFlushIoBuffers to maintain data integrity during DMA or PIO device transfer operations. Calling this routine affects all processors in the machine.

If ReadOperation is TRUE, the driver is reading information from the device to system memory, so valid data still might be in the processor instruction and data caches. KeFlushIoBuffers flushes data from all processors’ caches to system memory, including the processor on which the caller is running.

If ReadOperation is FALSE, the driver is writing data from system memory to a device, so valid data might be in the processor’s data cache but not yet transferred to the device. KeFlushIoBuffers flushes all processor’s data caches, including the processor on which the caller is running.

As a general rule, drivers should call this routine just before beginning a DMA transfer operation or immediately following any PIO read operation.

Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoFlushAdapterBuffers


KeGetCurrentIrql

KIRQL
KeGetCurrentIrql(
);

KeGetCurrentIrql returns the current IRQL.


KeGetCurrentProcessorNumber

ULONG
KeGetCurrentProcessorNumber(
);

KeGetCurrentProcessorNumber returns the system-assigned number of the current processor on which the caller is running.

Comments

KeGetCurrentProcessorNumber can be called by drivers to debug spin lock usage on SMP machines during driver development.

The number of processors in an SMP machine is a zero-based value.

If the call to KeGetCurrentProcessorNumber occurs at IRQL < DISPATCH_LEVEL, a processor switch can occur between instructions. Consequently, callers of KeGetCurrentProcessorNumber usually run at IRQL >= DISPATCH_LEVEL.


KeGetDcacheFillSize

ULONG
KeGetDcacheFillSize(
);

KeGetDcacheFillSize returns the microprocessor’s data cache-line boundary in bytes.

Comments

Drivers that do DMA can use the information returned by this routine to avoid cache-line tearing during DMA transfer operations.

Such a driver can use the value returned by KeGetDcacheFillSize to allocate cache-aligned buffers for DMA. Such a driver also can use the value returned by KeGetDcacheFillSize during DMA transfer operations.

For example, in a platform with the processor’s data cache aligned on a 16-byte boundary, a DMA device driver should transfer data as an integral number of 16-byte blocks to or from a buffer aligned on a 16-byte boundary if at all possible. Otherwise, cache-line tearing occurs during DMA transfers, which can cause data integrity problems for both the driver and the system.

See Also

ExAllocatePool, HalGetAdapter, IoMapTransfer, KeFlushIoBuffers


KeInitializeCallbackRecord

VOID
KeInitializeCallbackRecord(
IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord
);

Device drivers call KeInitializeCallbackRecord to initialize a bug-check callback record before calling KeRegisterBugCheckCallback.

Parameters

CallbackRecord
Points to caller-allocated, nonpaged memory for the callback record to be initialized.

Comments

Before a device driver calls KeRegisterBugCheckCallback, it must call KeInitializeCallbackRecord.

Such a driver must provide resident storage for a bug-check record, which can be in a device extension, in a controller extension, in nonpaged pool allocated by the driver, or statically allocated in the driver. The structure and contents of the memory at CallbackRecord should be considered opaque, but this record must be preserved unless the driver has called KeDeregisterBugCheckCallback. After this call, the device driver is responsible for freeing the memory it allocated for the bug-check record if necessary.

Callers of KeInitializeCallbackRecord can be running at any IRQL. Usually, a device driver is running at IRQL PASSIVE_LEVEL in its DriverEntry routine when it calls KeInitializeCallbackRecord.

See Also

ExAllocatePool, KeDeregisterBugCheckCallback, KeRegisterBugCheckCallback


KeInitializeDeviceQueue

VOID
KeInitializeDeviceQueue(
IN PKDEVICE_QUEUE DeviceQueue
);

KeInitializeDeviceQueue initializes a device queue object to a Not-Busy state.

Parameters

DeviceQueue
Points to a device queue object for which the caller provides the storage.

Comments

The device queue object is initialized and the state of the device queue is set to Not-Busy.

Device drivers should initialize their kernel-defined objects within their respective DriverEntry routines at IRQL PASSIVE_LEVEL. Storage for the device queue object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

See Also

KeInsertByKeyDeviceQueue, KeInsertDeviceQueue, KeRemoveDeviceQueue, KeRemoveEntryDeviceQueue


KeInitializeDpc

VOID
KeInitializeDpc(
IN PKDPC Dpc,
 IN PKDEFERRED_ROUTINE DeferredRoutine,
 IN PVOID DeferredContext
);

KeInitializeDpc initializes a DPC object, setting up a deferred procedure that can be called with a given context.

Parameters

Dpc
Points to a DPC object for which the caller provides the storage.
DeferredRoutine
Specifies the entry point for a routine to be called when the DPC object is removed from the DPC queue. A DeferredRoutine is declared as follows:
VOID
(*PKDEFERRED_ROUTINE)(
    IN PKDPC Dpc;
    IN PVOID DeferredContext;
    IN PVOID SystemArgument1;
    IN PVOID SystemArgument2;
    );
DeferredContext
Points to a caller-supplied context to be passed to the DeferredRoutine when it is called.

Comments

The caller can queue an initialized DPC with KeInsertQueueDpc. The caller also can set up a timer object associated with the initialized DPC object and queue the DPC with KeSetTimer.

Storage for the DPC object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

KeInsertQueueDpc, KeRemoveQueueDpc, KeSetTimer


KeInitializeEvent

VOID
KeInitializeEvent(
IN PKEVENT Event,
 IN EVENT_TYPE Type,
 IN BOOLEAN State
);

KeInitializeEvent initializes an event object as a synchronization (single waiter) or notification type event and sets its initial state to Signaled or Not-Signaled.

Parameters

Event
Points to an event object, for which the caller provides the storage.
Type
Specifies the event type, either NotificationEvent or SynchronizationEvent.
State
Specifies the initial state of the event. TRUE indicates Signaled.

Comments

A caller cannot wait at raised IRQL for a nonzero interval on an event object or in a nonarbitrary thread context.

Storage for an event object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

KeClearEvent, KeReadStateEvent, KeResetEvent, KeSetEvent, KeWaitForMultipleObjects, KeWaitForSingleObject


KeInitializeMutex

VOID
KeInitializeMutex(
IN PKMUTEX Mutex,
 IN ULONG Level
);

KeInitializeMutex initializes a mutex object at a given level number, setting it to the Signaled state.

Parameters

Mutex
Points to a mutex object, for which the caller provides the storage.
Level
Specifies the level number to be assigned to the mutex.

Comments

For better performance, use the Ex..FastMutex routines instead of the Ke..Mutex. However, a fast mutex cannot be acquired recursively, as a kernel mutex can.

The mutex object is initialized with the specified Level and an initial state of Signaled.

A driver cannot wait at raised IRQL nor in an arbitrary thread context for a nonzero interval on a mutex object.

Storage for a mutex object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

ExInitializeFastMutex, KeReadStateMutex, KeReleaseMutex, KeWaitForMultipleObjects, KeWaitForMutexObject, KeWaitForSingleObject


KeInitializeSemaphore

VOID
KeInitializeSemaphore(
IN PKSEMAPHORE Semaphore,
 IN LONG Count,
 IN LONG Limit
);

KeInitializeSemaphore initializes a semaphore object with a given count and specifies an upper limit that the count can attain.

Parameters

Semaphore
Points to a dispatcher object of type semaphore, for which the caller provides the storage.
Count
Specifies the initial count value to be assigned to the semaphore. This value must be positive. A nonzero value sets the initial state of the semaphore to Signaled.
Limit
Specifies the maximum count value that the semaphore can attain. This value must be positive. It determines how many waiting threads become eligible for execution when the semaphore is set to the Signaled state and can therefore access the resource that the semaphore protects.

Comments

The semaphore object is initialized with the specified initial count and limit.

Storage for a semaphore object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

Callers of KeInitializeSemaphore must be running at IRQL PASSIVE_LEVEL.

See Also

KeReadStateSemaphore, KeReleaseSemaphore, KeWaitForMultipleObjects, KeWaitForSingleObject


KeInitializeSpinLock

VOID
KeInitializeSpinLock(
IN PKSPIN_LOCK SpinLock
);

KeInitializeSpinLock initializes a variable of type KSPIN_LOCK.

Parameters

SpinLock
Points to a spin lock, for which the caller must provide the storage.

Comments

This routine must be called before an initial call to KeAcquireSpinLock or to any other support routine that requires a spin lock as an argument.

Storage for a spin lock object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

Callers of this routine can be running at any IRQL. Usually, a caller is running at IRQL PASSIVE_LEVEL in a DriverEntry routine.

See Also

KeAcquireSpinLock, KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLock


KeInitializeTimer

VOID
KeInitializeTimer(
IN PKTIMER Timer
);

KeInitializeTimer initializes a timer object.

Parameters

Timer
Points to a timer object, for which the caller provides the storage.

Comments

The timer object is initialized with a state of Not-Signaled.

Storage for a timer object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

KeInitializeTimer can only initialize a notification timer. Use KeInitializeTimerEx to initialize a notification timer or a synchronization timer.

Callers of KeInitializeTimer should be running at IRQL DISPATCH_LEVEL or lower. It is best to initialize timers at IRQL PASSIVE_LEVEL.

Use KeSetTimer or KeSetTimerEx to define when the timer will expire.

See Also

KeCancelTimer, KeInitializeTimerEx, KeReadStateTimer, KeSetTimer, KeSetTimerEx, KeWaitForMultipleObjects, KeWaitForSingleObject


KeInitializeTimerEx

VOID
KeInitializeTimerEx(
IN PKTIMER Timer,
IN TIMER_TYPE Type
);

KeInitializeTimerEx initializes an extended kernel timer object.

Parameters

Timer
Points to a timer object, for which the caller provides the storage.
Type
Specifies the type of the timer object, either NotificationTimer or SynchronizationTimer.

Comments

The timer object is initialized with a state of Not-Signaled.

Storage for a timer object must be resident: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the caller.

When a notification timer expires, all waiting threads are released and the timer remains in the Signaled state until it is explicitly reset. When a synchronization timer expires, its state is set to Signaled until a single waiting thread is released and then the timer is reset to the Not-Signaled state.

Callers of KeInitializeTimerEx should be running at IRQL DISPATCH_LEVEL or lower. It is best to initialize timers at IRQL PASSIVE_LEVEL.

Use KeSetTimer or KeSetTimerEx to define when the timer will expire.

See Also

KeCancelTimer, KeReadStateTimer, KeSetTimer, KeSetTimerEx, KeWaitForMultipleObjects, KeWaitForSingleObject


KeInsertByKeyDeviceQueue

BOOLEAN
KeInsertByKeyDeviceQueue(
IN PKDEVICE_QUEUE DeviceQueue,
 IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry,
 IN ULONG SortKey
);

KeInsertByKeyDeviceQueue acquires the spin lock for the given DeviceQueue and queues an entry according to the given sort-key value if the state of the device queue is Busy.

Parameters

DeviceQueue
Points to a control object of the device queue type for which the caller provides the storage.
DeviceQueueEntry
Points to the device queue entry to be inserted into the device queue according to the specific key value.
SortKey
Specifies the sort-key value that determines the position in the device queue in which to insert the entry.

Return Value

If the device queue is empty, FALSE is returned, meaning the DeviceQueueEntry is not inserted in the device queue.

Comments

The given device queue spin lock is acquired and the state of the device queue is checked. If the state of the device queue is Busy, the IRP specified by the DeviceQueueEntry is inserted into the device queue according to its sort-key value and the device queue spin lock is released.

The new entry is positioned in the device queue after any entries in the queue with sort-key values less than or equal to its sort-key value and preceding any entries with sort-key values that are greater.

If KeInsertByKeyDeviceQueue returns FALSE, the caller must begin processing the IRP. A call to KeInsertDeviceQueue or KeInsertByKeyDeviceQueue when the queue is empty causes the device queue state transition from Not-Busy to Busy.

This routine is for code that queues an I/O request to a device driver.

Callers of KeInsertByKeyDeviceQueue must be running at IRQL DISPATCH_LEVEL.

See Also

KeInitializeDeviceQueue, KeInsertDeviceQueue, KeRemoveDeviceQueue, KeRemoveEntryDeviceQueue


KeInsertDeviceQueue

BOOLEAN
KeInsertDeviceQueue(
IN PKDEVICE_QUEUE DeviceQueue,
 IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
);

KeInsertDeviceQueue acquires the spin lock for the given device queue object and, if the device queue state is Busy, queues the given entry.

Parameters

DeviceQueue
Points to a control object of type device queue for which the caller provides the storage.
DeviceQueueEntry
Points to the device queue entry that is to be inserted.

Return Value

If the device queue is empty, FALSE is returned and the DeviceQueueEntry is not inserted in the device queue.

Comments

If the state of the device queue is Busy, the specified DeviceQueueEntry is inserted at the tail of the device queue and the device queue spin lock is released.

If KeInsertDeviceQueue returns FALSE, the entry was not queued and the caller must begin processing the IRP. A call to KeInsertDeviceQueue or KeInsertByKeyDeviceQueue when the queue is empty causes the device queue state transition from Not-Busy to Busy.

This routine is for code that queues an I/O request to a device driver.

Callers of KeInsertDeviceQueue must be running at IRQL DISPATCH_LEVEL.

See Also

KeInitializeDeviceQueue, KeInsertByKeyDeviceQueue, KeRemoveDeviceQueue, KeRemoveEntryDeviceQueue


KeInsertQueueDpc

BOOLEAN
KeInsertQueueDpc(
IN PKDPC Dpc,
 IN PVOID SystemArgument1,
 IN PVOID SystemArgument2
);

KeInsertQueueDpc queues a DPC for execution when the IRQL of a processor drops below DISPATCH_LEVEL.

Parameters

Dpc
Points to an initialized control object of type DPC for which the caller provides the storage.
SystemArgument1, SystemArgument2
Points to a set of two parameters that contain untyped data.

Return Value

If the specified DPC object is not currently in the queue, KeInsertQueueDpc queues the DPC and returns TRUE.

Comments

If the specified DPC object is already in the DPC queue, no operation is performed except to return FALSE. Otherwise, the DPC object is inserted in the DPC queue and a software interrupt is requested at IRQL DISPATCH_LEVEL on the current processor.

Note that a given DPC object and the function it represents can each be queued for execution only once at any given moment.

The deferred procedure is run when IRQL on the current processor drops below DISPATCH_LEVEL.

Callers of KeInsertQueueDpc must be running at IRQL >= DISPATCH_LEVEL.

See Also

KeInitializeDpc, KeRemoveQueueDpc


KeLeaveCriticalRegion

VOID
KeLeaveCriticalRegion(
);

KeLeaveCriticalRegion re-enables the delivery of special kernel APCs that was disabled by a preceding call to KeEnterCriticalRegion.

Comments

Highest-level drivers can call this routine while running in the context of the thread that requested the current I/O operation.

Callers of KeLeaveCriticalRegion must be running at IRQL PASSIVE_LEVEL.

See Also

KeEnterCriticalRegion


KeLowerIrql

VOID
KeLowerIrql(
IN KIRQL NewIrql
);

KeLowerIrql restores the IRQL on the current processor to its original value.

Parameters

NewIrql
Specifies the IRQL that was returned from KeRaiseIrql.

Comments

It is a fatal error to call KeLowerIrql with an input NewIrql that was not returned by the immediately preceding call to KeRaiseIrql.

Callers of KeLowerIrql can be running at any IRQL that was passed to KeRaiseIrql.

See Also

KeRaiseIrql


KeQueryPerformanceCounter

LARGE_INTEGER
KeQueryPerformanceCounter(
IN PLARGE_INTEGER PerformanceFrequency /* optional */
);

KeQueryPerformanceCounter provides the finest grained running count available in the system.

Parameters

PerformanceFrequency
Specifies an optional pointer to a variable that is to receive the performance counter frequency.

Return Value

KeQueryPerformanceCounter returns the performance counter value in hertz.

Comments

KeQueryPerformanceCounter always returns a 64-bit integer representing the number of ticks per second. Accumulating the count begins when the system is booted. The count is in ticks; the frequency is reported by PerformanceFrequency if this optional parameter is supplied.

The resolution of the timer used to accumulate the current count can be obtained by specifying PerformanceFrequency. For example, if the returned PerformanceFrequency is 2 million, each tick is 1/2 millionth of a second. Each 1/x millionth increment of the count corresponds to one second of elapsed time.

KeQueryPerformanceCounter is intended for time stamping packets or for computing performance and capacity measurements. It is not intended for measuring elapsed time, for computing stalls or waits, or for iterations.

Use this routine sparingly, calling it as infrequently as possible. Depending on the platform, KeQueryPerformanceCounter can disable system-wide interrupts for a minimal interval. Consequently, calling this routine frequently or repeatedly, as in an iteration, defeats its purpose of returning very fine-grained, running time-stamp information. Calling this routine too frequently can degrade I/O performance for the calling driver and for the system as a whole.

Callers of KeQueryPerformanceCounter can be running at any IRQL.

See Also

KeQuerySystemTime, KeQueryTickCount, KeQueryTimeIncrement


KeQuerySystemTime

VOID
KeQuerySystemTime(
OUT PLARGE_INTEGER CurrentTime
);

KeQuerySystemTime obtains the current system time.

Parameters

CurrentTime
Points to the current time on return from KeQuerySystemTime.

Comments

System time is a count of 100-nanosecond intervals since January 1, 1601.

Callers of this routine can be running at any IRQL.

See Also

KeQueryPerformanceCounter, KeQueryTickCount, KeQueryTimeIncrement


KeQueryTickCount

VOID
KeQueryTickCount(
OUT PLARGE_INTEGER TickCount
);

KeQueryTickCount maintains a count of the interval timer interrupts that have occurred since the system was booted.

Parameters

TickCount
Points to the tick count value on return from KeQueryTickCount.

Comments

The TickCount value increases by one at each interval timer interrupt while the system is running.

The preferred method of determining elapsed time is by using TickCount for relative timing and time stamps.

To determine the absolute elapsed time multiply the returned TickCount by the KeQueryTimeIncrement return value using compiler support for 64-bit integer operations.

The caller can be running at any IRQL.

See Also

KeQueryPerformanceCounter, KeQueryTimeIncrement


KeQueryTimeIncrement

ULONG
KeQueryTimeIncrement(
);

KeQueryTimeIncrement returns the number of 100-nanosecond units that are added to the system time each time the interval clock interrupts.

Comments

The caller can be running at any IRQL.

See Also

KeQueryPerformanceCounter, KeQuerySystemTime, KeQueryTickCount


KeRaiseIrql

VOID
KeRaiseIrql(
IN KIRQL NewIrql,
 OUT PIRQL OldIrql
);

KeRaiseIrql raises the hardware priority to a given IRQL value, thereby masking off interrupts of equivalent or lower IRQL on the current processor.

Parameters

NewIrql
Specifies the new IRQL to which the hardware priority is to be raised.
OldIrql
Points to the storage for the original (unraised) IRQL value to be used in a subsequent call to KeLowerIrql.

Comments

If the new IRQL is less than the current IRQL, a bug check occurs. Otherwise, the current IRQL is set to the specified value.

Callers of this routine can be running at any IRQL. Any caller should restore the original IRQL with KeLowerIrql as soon as possible.

A call to KeLowerIrql is valid if it specifies NewIrql <= CurrentIrql. A call to KeRaiseIrql is valid if the caller specifies NewIrql >= CurrentIrql.

See Also

KeLowerIrql


KeReadStateEvent

LONG
KeReadStateEvent(
IN PKEVENT Event
);

KeReadStateEvent returns the current state, Signaled or Not-Signaled, of a given event object.

Parameters

Event
Points to an initialized event object for which the caller provides the storage.

Return Value

If the current state of the event object is Signaled, a nonzero value is returned. Otherwise, zero is returned.

Comments

It is also possible to read the state of an event from a driver’s interrupt service routine at DIRQL, if the following conditions are met: the driver’s event object is resident (probably in its device extension), and any other function that accesses the event synchronizes its access with the ISR.

Callers of KeReadStateEvent must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeClearEvent, KeInitializeEvent, KeResetEvent, KeSetEvent


KeReadStateMutex

LONG
KeReadStateMutex(
IN PKMUTEX Mutex
);

KeReadStateMutex returns the current state, Signaled or Not-Signaled, of a given mutex object.

Parameters

Mutex
Points to an initialized mutex object for which the caller provides the storage.

Return Value

If the return value is one, the state of the mutex object is Signaled.

Comments

Callers of KeReadStateMutex must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExInitializeFastMutex, KeInitializeMutex, KeReleaseMutex


KeReadStateSemaphore

LONG
KeReadStateSemaphore(
IN PKSEMAPHORE Semaphore
);

KeReadStateSemaphore returns the current state, Signaled or Not-Signaled, of a given semaphore object.

Parameters

Semaphore
Points to an initialized semaphore object for which the caller provides the storage.

Return Value

If the return value is zero, the current state of the semaphore object is Not-Signaled.

Comments

Callers of KeReadStateSemaphore can be running at any IRQL.

See Also

KeInitializeSemaphore, KeReleaseSemaphore


KeReadStateTimer

BOOLEAN
KeReadStateTimer(
IN PKTIMER Timer
);

KeReadStateTimer reads the current state of a given timer object.

Parameters

Timer
Points to an initialized timer object, for which the caller provides the storage.

Return Value

If the current state of the timer object is Signaled, TRUE is returned.

Comments

Callers of KeReadStateTimer must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeCancelTimer, KeInitializeTimer, KeSetTimer


KeRegisterBugCheckCallback

BOOLEAN
KeRegisterBugCheckCallback(
IN PKBUGCHECK_CALLBACK_RECORD CallbackRecord,
 IN PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,
 IN PVOID Buffer,
 IN ULONG Length,
 IN PUCHAR Component
);

Device drivers can call KeRegisterBugCheckCallback to register their bug-check callback routines. If a system bug check occurs, such a callback usually saves device-state information to be written into the system crash dump file.

Parameters

CallbackRecord
Points to a callback record, already initialized with KeInitializeCallbackRecord, for which the caller provides nonpaged storage.
CallbackRoutine
Specifies the entry point of the caller-supplied routine to be registered, declared to conform to the following function type:
VOID
(*PKBUGCHECK_CALLBACK_ROUTINE) (
    IN PVOID Buffer,
    IN ULONG Length
    );

This caller-supplied routine is responsible for writing driver-determined state information at Buffer if a bug check occurs.

Buffer
Points to a caller-supplied buffer, which must be allocated from nonpaged pool.
Length
Specifies the size in bytes of the caller-allocated buffer.
Component
Points to a zero-terminated ANSI string identifying the caller. Usually, this is the name of the device driver, or possibly of its device.

Return Value

KeRegisterBugCheckCallback returns TRUE if the caller-supplied routine has been successfully added to the set of registered bug-check callbacks.

Comments

KeRegisterBugCheckCallback sets up a driver-supplied routine to be called if a bug check occurs so a device driver can save state information, such as the contents of device registers, that would not otherwise be saved in a system crash-dump file.

A driver-supplied bug-check callback routine writes whatever information the driver designer chooses into the memory at Buffer. The format of the data written at Buffer is driver-determined. This memory cannot be freed unless the driver first calls KeDeregisterBugCheckCallback. Like the driver-allocated memory at Buffer, such a bug-check callback routine cannot be pageable.

When the callback routine runs, interrupts are disabled. A callback routine cannot allocate resources, such as memory, because the system is being shut down. A bug-check callback also cannot use synchronization mechanisms, such as a spin lock. However, it should not need to acquire synchronization resources because other driver routines are effectively disabled while the system is being shut down for a bug check. The callback routine can safely call the HAL’s READ_PORT_XXX and/or READ_REGISTER_XXX to collect state information from the device and transfer this data to the driver-allocated buffer. It can call any Ke Xxx or Hal Xxx that neither allocates memory nor acquires a synchronization resource.

The given Component string should identify the driver to aid in crash-dump debugging. During driver development, the Component identifier can be passed to the debugger to select only that component’s dump data for examination. A bug-check callback routine also can be debugged.

Callers of KeRegisterBugCheckCallback can be running at any IRQL. Usually, a device driver is running at IRQL PASSIVE_LEVEL in its DriverEntry routine when it calls KeRegisterBugCheckCallback.

See Also

ExAllocatePool, KeBugCheck, KeBugCheckEx, KeDeregisterBugCheckCallback, KeInitializeCallbackRecord


KeReleaseMutex

LONG
KeReleaseMutex(
IN PKMUTEX Mutex,
 IN BOOLEAN Wait
);

KeReleaseMutex releases a given mutex object, specifying whether the caller is to call one of KeWait Xxx as soon as KeReleaseMutex returns control.

Parameters

Mutex
Points to an initialized mutex object for which the caller provides the storage.
Wait
Specifies whether or not the call to KeReleaseMutex is to be immediately followed by a call to one of KeWait Xxx.

Return Value

If the return value is zero, the mutex object was released and attained a state of Signaled.

Comments

For better performance, use the Ex..FastMutex routines instead of the Ke..Mutex. However, a fast mutex cannot be acquired recursively, as a kernel mutex can.

If the mutex object attains a Signaled state, an attempt is made to satisfy a wait for the mutex object.

A mutex object can only be released by the thread currently holding the mutex. If an attempt is made to release a mutex not held by the thread, a bug check occurs. An attempt to release a mutex object whose current state is Signaled also causes a bug check to occur.

When a mutex object attains a Signaled state, it is removed from the list of mutexes held by that thread. If the thread’s owned mutex list does not contain any more entries, the thread’s original priority is restored.

If the value of the Wait parameter is TRUE, the return to the caller is executed without lowering IRQL or releasing the dispatcher database spin lock. Therefore, the call to KeReleaseMutex must be followed immediately by a call to one of KeWait Xxx.

This allows the caller to release a mutex and wait as one atomic operation, preventing a possibly superfluous context switch. However, a caller cannot wait at raised IRQL nor in an arbitrary thread context for a nonzero interval on a mutex object.

If a mutex is acquired recursively, the holding thread must call KeReleaseMutex as many times as it acquired the mutex to set it to the Signaled state.

Callers of KeReleaseMutex must be running at IRQL PASSIVE_LEVEL.

See Also

ExReleaseFastMutex, ExReleaseFastMutexUnsafe, KeInitializeMutex, KeReadStateMutex, KeWaitForMultipleObjects, KeWaitForMutexObject, KeWaitForSingleObject


KeReleaseSemaphore

LONG
KeReleaseSemaphore(
IN PKSEMAPHORE Semaphore,
 IN KPRIORITY Increment,
 IN LONG Adjustment,
 IN BOOLEAN Wait
);

KeReleaseSemaphore releases a given semaphore object. This routine supplies a runtime priority boost for waiting threads. If this call sets the semaphore to the Signaled state, the semaphore count is augmented by the given value. The caller can also specify whether it will call one of the KeWait Xxx routines as soon as KeReleaseSemaphore returns control.

Parameters

Semaphore
Points to an initialized semaphore object for which the caller provides the storage.
Increment
Specifies the priority increment to be applied if releasing the semaphore causes a wait to be satisfied.
Adjustment
Specifies a value to be added to the current semaphore count. This value must be positive.
Wait
Specifies whether the call to KeReleaseSemaphore is to be followed immediately by a call to one of the KeWait Xxx.

Return Value

If the return value is zero, the previous state of the semaphore object is Not-Signaled.

Comments

Releasing a semaphore object causes the semaphore count to be augmented by the value of the Adjustment parameter. If the resulting value is greater than the limit of the semaphore object, the count is not adjusted and an exception, STATUS_SEMAPHORE_COUNT_EXCEEDED, is raised.

Augmenting the semaphore object count causes the semaphore to attain a state of Signaled, and an attempt is made to satisfy as many waits as possible on the semaphore object.

If the value of the Wait parameter is TRUE, the return to the caller is executed without lowering IRQL or releasing the dispatcher database spin lock. Therefore, the call to KeReleaseSemaphore must be followed immediately by a call to one of the KeWait Xxx.

This capability allows the caller to release a semaphore and to wait as one atomic operation, preventing a possibly superfluous context switch. However, the caller cannot wait at raised IRQL nor in an arbitrary thread context for a nonzero interval on a semaphore object.

Callers of KeReleaseSemaphore must be running at IRQL <= DISPATCH_LEVEL provided that Wait is set to FALSE. Otherwise, the caller must be running at IRQL PASSIVE_LEVEL.

See Also

KeInitializeSemaphore, KeReadStateSemaphore, KeWaitForMultipleObjects, KeWaitForSingleObject


KeReleaseSpinLock

VOID
KeReleaseSpinLock(
IN PKSPIN_LOCK SpinLock,
 IN KIRQL NewIrql
);

KeReleaseSpinLock releases a spin lock and restores the original IRQL at which the caller was running.

Parameters

SpinLock
Points to a spin lock for which the caller provides the storage.
NewIrql
Specifies the IRQL value saved from the preceding call to KeAcquireSpinLock.

Comments

This call is a reciprocal to KeAcquireSpinLock. The input NewIrql value must be the OldIrql returned by KeAcquireSpinLock.

Callers of this routine are running at IRQL DISPATCH_LEVEL. On return from KeReleaseSpinLock, IRQL is restored to the NewIrql value.

See Also

KeAcquireSpinLock, KeInitializeSpinLock


KeReleaseSpinLockFromDpcLevel

VOID
KeReleaseSpinLockFromDpcLevel(
IN PKSPIN_LOCK SpinLock
);

KeReleaseSpinLockFromDpcLevel releases an executive spin lock.

Parameters

SpinLock
Points to an executive spin lock for which the caller provides the storage.

Comments

Drivers call KeReleaseSpinLockFromDpcLevel to release a spin lock acquired by calling KeAcquireSpinLockAtDpcLevel.

It is an error to call KeReleaseSpinLockFromDpcLevel if the given spin lock was acquired by calling KeAcquireSpinLock because the caller’s original IRQL is not restored, which can cause deadlocks or fatal page faults.

Callers of KeReleaseSpinLockAtDpcLevel must be running at IRQL DISPATCH_LEVEL.

See Also

KeAcquireSpinLock, KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLock


KeRemoveByKeyDeviceQueue

PKDEVICE_QUEUE_ENTRY
KeRemoveByKeyDeviceQueue(
IN PKDEVICE_QUEUE DeviceQueue,
 IN ULONG SortKey
);

KeRemoveByKeyDeviceQueue removes an entry, selected according to a sort-key value, from a given device queue.

Parameters

DeviceQueue
Points to an initialized device queue object for which the caller provides the storage.
SortKey
Specifies the position in the DeviceQueue from which the entry is to be removed.

Return Value

If the given device queue is empty but the device queue object’s state is Busy, KeRemoveByKeyDeviceQueue returns NULL.

Comments

It is an error to call KeRemoveByKeyDeviceQueue when the state of the device queue object is Not-Busy. If the state is Busy and an IRP is queued, this routine dequeues the entry and returns a pointer to the IRP. A call to KeRemoveByKeyDeviceQueue when the state of the device queue object is Busy but no IRPs are queued causes the state transition to Not-Busy.

Callers of KeRemoveByKeyDeviceQueue must be running at IRQL DISPATCH_LEVEL.

See Also

KeInitializeDeviceQueue, KeInsertByKeyDeviceQueue, KeInsertDeviceQueue, KeRemoveDeviceQueue, KeRemoveEntryDeviceQueue


KeRemoveDeviceQueue

PKDEVICE_QUEUE_ENTRY
KeRemoveDeviceQueue(
IN PKDEVICE_QUEUE DeviceQueue
);

KeRemoveDeviceQueue removes an entry from the head of a specified device queue.

Parameters

DeviceQueue
Points to an initialized device queue object for which the caller provides the storage.

Return Value

If the device queue is empty but the device queue’s state is Busy, KeRemoveDeviceQueue returns NULL.

Comments

The specified device queue spin lock is acquired and the state of the device queue is checked. If the state is Busy and an IRP is queued, this routine dequeues the entry and returns a pointer to the IRP. A call to KeRemoveDeviceQueue when the state of the device queue object is Busy but no IRPs are queued causes the state transition to Not-Busy. The given device queue’s spin lock is released.

It is an error to call KeRemoveDeviceQueue when the state of the device queue object is Not-Busy.

Callers of KeRemoveDeviceQueue must be running at IRQL DISPATCH_LEVEL.

See Also

KeInitializeDeviceQueue, KeInsertByKeyDeviceQueue, KeInsertDeviceQueue, KeRemoveByKeyDeviceQueue, KeRemoveEntryDeviceQueue


KeRemoveEntryDeviceQueue

BOOLEAN
KeRemoveEntryDeviceQueue(
IN PKDEVICE_QUEUE DeviceQueue,
 IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
);

KeRemoveEntryDeviceQueue returns whether the specified entry is in the device queue and removes it, if it was queued, from the device queue.

Parameters

DeviceQueue
Points to an initialized device queue object for which the caller provides the storage.
DeviceQueueEntry
Points to the entry to be removed from the specified DeviceQueue.

Return Value

If the DeviceQueueEntry is queued, it is removed and KeRemoveEntryDeviceQueue returns TRUE.

Comments

The IRQL is set to DISPATCH_LEVEL and the DeviceQueue spin lock is acquired.

If the given DeviceQueueEntry is not in the queue, the IRP either is already being processed, or the IRP has been canceled. In this case, KeRemoveEntryDeviceQueue simply returns FALSE.

The specified DeviceQueue spin lock is released and IRQL is restored to its previous value.

Callers of KeRemoveEntryDeviceQueue must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeInitializeDeviceQueue, KeInsertByKeyDeviceQueue, KeInsertDeviceQueue, KeRemoveByKeyDeviceQueue, KeRemoveDeviceQueue


KeRemoveQueueDpc

BOOLEAN
KeRemoveQueueDpc(
IN PKDPC Dpc
);

KeRemoveQueueDpc removes a given DPC object from the system DPC queue.

Parameters

Dpc
Points to an initialized DPC object that was queued by calling KeInsertQueueDpc.

Return Value

KeRemoveQueueDpc returns TRUE if the DPC object is in the DPC queue. If the given DPC object is not currently in the DPC queue, no operation is performed and FALSE is returned.

Comments

If the given DPC object is currently queued, it is removed from the queue, canceling a call to the associated DPC routine.

Callers of KeRemoveQueueDpc can be running at any IRQL.

See Also

KeInitializeDpc, KeInsertQueueDpc


KeResetEvent

LONG
KeResetEvent(
IN PKEVENT Event
);

KeResetEvent resets the state of a specified event object to Not-Signaled and returns the previous state of that event object.

Parameters

Event
Points to an initialized dispatcher object of type event for which the caller provides the storage.

Return Value

KeResetEvent returns the previous state of the given Event, nonzero for Signaled.

Comments

The state of Event is reset to Not-Signaled, meaning its value is set to zero.

Unless the caller uses the value returned by KeResetEvent, setting the state of a given event object to Not-Signaled with KeClearEvent is faster.

Callers of KeResetEvent must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeClearEvent, KeInitializeEvent, KeReadStateEvent, KeSetEvent, KeWaitForMultipleObjects, KeWaitForSingleObject


KeSetBasePriorityThread

LONG
KeSetBasePriorityThread(
IN PKTHREAD Thread,
 IN LONG Increment
);

KeSetBasePriorityThread sets the runtime priority, relative to the current process, for a given thread.

Parameters

Thread
Points to a dispatcher object of type KTHREAD.
Increment
Is the value to be added to the base priority of the process for the Thread.

Return Value

KeSetBasePriorityThread returns the previous base priority increment of the given thread. The previous base priority increment is defined as the difference between the specified thread’s old base priority and the base priority of the thread’s process.

Comments

The new base priority is computed by adding the given Increment, which can be a negative value, to the base priority of the specified thread’s process. The resultant value is stored as the base priority of the specified thread.

Drivers that set up device-dedicated threads with variable priority attributes can call this routine to set such a thread’s priority relative to the system process in which the thread is created.

The new base priority is restricted to the priority class of the given thread’s process. Therefore, the base priority is not allowed to cross over from a variable priority class to a real-time priority class or vice versa.

Callers of KeSetBasePriorityThread must be running at IRQL PASSIVE_LEVEL.

See Also

KeSetPriorityThread


KeSetEvent

LONG
KeSetEvent(
IN PKEVENT Event,
 IN KPRIORITY Increment,
 IN BOOLEAN Wait
);

KeSetEvent sets the state of an event object to Signaled if the event was not already signaled, and returns the previous state of the event object.

Parameters

Event
Points to an initialized event object for which the caller provides the storage.
Increment
Specifies the priority increment to be applied if setting the event causes a wait to be satisfied.
Wait
Specifies whether the call to KeSetEvent is to be followed immediately by a call to a KeWait Xxx.

Return Value

If the previous state of the event object was Signaled, a nonzero value is returned.

Comments

Calling KeSetEvent causes the event to attain a Signaled state, and therefore, an attempt is made to satisfy as many waits as possible on the event object.

If the Wait parameter is TRUE, the return to the caller is executed without lowering IRQL or releasing the dispatcher database spin lock. Therefore, the call to KeSetEvent must be followed immediately by a call to one of the KeWait Xxx.

This allows the caller to set an event and wait as one atomic operation, preventing a possibly superfluous context switch. However, the caller cannot wait at raised IRQL nor in an arbitrary thread context for a nonzero interval on an event object.

If Wait is set to FALSE, the caller can be running at IRQL <= DISPATCH_LEVEL. Otherwise, callers of KeSetEvent must be running at IRQL PASSIVE_LEVEL and in a nonarbitrary thread context.

See Also

KeClearEvent, KeInitializeEvent, KeReadStateEvent, KeResetEvent, KeWaitForMultipleObjects, KeWaitForSingleObject


KeSetPriorityThread

KPRIORITY
KeSetPriorityThread(
IN PKTHREAD Thread,
 IN KPRIORITY Priority
);

KeSetPriorityThread sets the runtime priority of a driver-created thread.

Parameters

Thread
Points to the driver-created thread.
Priority
Specifies the priority of the driver-created thread, usually to the real-time priority value, LOW_REALTIME_PRIORITY. The value LOW_PRIORITY is reserved for system use.

Return Value

KeSetPriorityThread returns the old priority of the thread.

Comments

If a call to KeSetPriorityThread resets the thread’s priority to a lower value, execution of the thread can be rescheduled even if it is currently running or is about to be dispatched for execution.

Callers of KeSetPriorityThread must be running at IRQL PASSIVE_LEVEL.

See Also

KeSetBasePriorityThread


KeSetTimer

BOOLEAN
KeSetTimer(
IN PKTIMER Timer,
 IN LARGE_INTEGER DueTime,
 IN PKDPC Dpc /* optional */
);

KeSetTimer sets the absolute or relative interval at which a timer object is to be set to the Signaled state and, optionally, supplies a CustomTimerDpc routine to be executed when that interval expires.

Parameters

Timer
Points to a timer object that was initialized with KeInitializeTimer or KeInitializeTimerEx.
DueTime
Specifies the absolute or relative time at which the timer is to expire. If the value of the DueTime parameter is negative, the expiration time is relative to the current system time. Otherwise, the expiration time is absolute. The expiration time is expressed in system time units (100-nanosecond intervals). Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.
Dpc
Points to a DPC object that was initialized by KeInitializeDpc. This parameter is optional.

Return Value

If the timer object was already in the system timer queue, KeSetTimer returns TRUE.

Comments

KeSetTimer:

  • Computes the expiration time.

  • Sets the state of the timer to Not-Signaled.

  • Inserts the timer object in the system timer queue.

If the timer object was already in the timer queue, it is implicitly canceled before being set to the new expiration time. A call to KeSetTimer before the previously specified DueTime has expired cancels both the timer and the call to the Dpc, if any, associated with the previous call.

If the Dpc parameter is specified, a DPC object is associated with the timer object. When the timer expires, the timer object is removed from the system timer queue and its state is set to Signaled. If a DPC object was associated with the timer when it was set, the DPC object is inserted in the system DPC queue to be executed as soon as conditions permit after the timer interval expires.

Only one instantiation of a given DPC object can be queued at any given moment. To avoid potential race conditions, the DPC passed to KeSetTimer should not be passed to KeInsertQueueDpc.

A caller cannot wait at raised IRQL nor in an arbitrary thread context for a timer to expire by calling KeWait Xxx.

Callers of KeSetTimer can specify one expiration time for a timer. To set a recurring timer use KeSetTimerEx.

Callers of KeSetTimer must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeCancelTimer, KeInitializeDpc, KeInitializeTimer, KeInitializeTimerEx, KeReadStateTimer, KeSetTimerEx, KeWaitForMultipleObjects, KeWaitForSingleObject


KeSetTimerEx

BOOLEAN
KeSetTimerEx(
IN PKTIMER Timer,
 IN LARGE_INTEGER DueTime,
IN LONG Period, /* optional */
 IN PKDPC Dpc /* optional */
);

KeSetTimerEx sets the absolute or relative interval at which a timer object is to be set to the Signaled state, optionally supplies a CustomTimerDpc routine to be executed when that interval expires, and optionally supplies a recurring interval for the timer.

Parameters

Timer
Points to a timer object that was initialized with KeInitializeTimer or KeInitializeTimerEx.
DueTime
Specifies the absolute or relative time at which the timer is to expire. If the value of the DueTime parameter is negative, the expiration time is relative to the current system time. Otherwise, the expiration time is absolute. The expiration time is expressed in system time units (100-nanosecond intervals). Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.
Period
Specifies an optional period for the timer in milliseconds. Must be less than or equal to MAXLONG.
Dpc
Points to a DPC object that was initialized by KeInitializeDpc. This parameter is optional.

Return Value

If the timer object was already in the system timer queue, KeSetTimerEx returns TRUE.

Comments

KeSetTimerEx:

  • Computes the expiration time.

  • Sets the state of the timer to Not-Signaled.

  • Sets the recurring interval for the timer, if one was specified.

  • Inserts the timer object in the system timer queue.

If the timer object was already in the timer queue, it is implicitly canceled before being set to the new expiration time. A call to KeSetTimerEx before the previously specified DueTime has expired cancels both the timer and the call to the Dpc, if any, associated with the previous call.

If the Dpc parameter is specified, a DPC object is associated with the timer object. When the timer expires, the timer object is removed from the system timer queue and its state is set to Signaled. If a DPC object was associated with the timer when it was set, the DPC object is inserted in the system DPC queue to be executed as soon as conditions permit after the timer interval expires.

A DPC routine cannot deallocate a periodic timer. A DPC routine can deallocate a nonperiodic timer.

Only one instantiation of a given DPC object can be queued at any given moment. To avoid potential race conditions, the DPC passed to KeSetTimerEx should not be passed to KeInsertQueueDpc.

A caller cannot wait at raised IRQL nor in an arbitrary thread context for a timer to expire by calling KeWait Xxx.

Callers of KeSetTimerEx must be running at IRQL <= DISPATCH_LEVEL.

See Also

KeCancelTimer, KeInitializeDpc, KeInitializeTimer, KeInitializeTimerEx, KeReadStateTimer, KeWaitForMultipleObjects, KeWaitForSingleObject


KeStallExecutionProcessor

VOID
KeStallExecutionProcessor(
IN ULONG MicroSeconds
);

KeStallExecutionProcessor stalls the caller on the current processor for the given interval.

Parameters

MicroSeconds
Specifies the number of microseconds to stall.

Comments

KeStallExecutionProcessor is a processor-dependent routine that busy-waits for at least the specified number of microseconds, but not significantly longer.

This routine is for use by device drivers and other software that must wait for an interval of less than a clock tick but more than for a few instructions. Drivers that call this routine should minimize the number of microseconds they specify (no more than 50). If a driver must wait for a longer interval, it should use another synchronization mechanism.

Callers of KeStallExecutionProcessor can be running at any IRQL.

See Also

KeDelayExecutionThread, KeWaitForMultipleObjects, KeWaitForSingleObject


KeSynchronizeExecution

BOOLEAN
KeSynchronizeExecution(
IN PKINTERRUPT Interrupt,
 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
 IN PVOID SynchronizeContext
);

KeSynchronizeExecution synchronizes the execution of a given routine with that of the ISR associated with the given interrupt object pointer.

Parameters

Interrupt
Is a pointer to a set of interrupt objects. This pointer was returned by IoConnectInterrupt.
SynchronizeRoutine
Is the entry point for a caller-supplied SynchCritSection routine whose execution is to be synchronized with the execution of the ISR associated with the interrupt object(s). A SynchronizeRoutine is declared as follows:
BOOLEAN
(*PKSYNCHRONIZE_ROUTINE) (
    IN PVOID SynchronizeContext
    );
SynchronizeContext
Points to a caller-supplied context area to be passed to the SynchronizeRoutine when it is called.

Return Value

KeSynchronizeExecution returns TRUE if the operation succeeds.

Comments

When this routine is called, the following occurs:

  1. The IRQL is raised to the SynchronizeIrql specified in the call to IoConnectInterrupt.

  2. Access to SynchronizeContext is synchronized with the corresponding ISR by acquiring the associated interrupt object spin lock.

  3. The specified SynchronizeRoutine is called with the input pointer to SynchronizeContext.

The caller-supplied SynchronizeRoutine runs at DIRQL, so it must execute very quickly.

Callers of KeSynchronizeExecution must be running at IRQL <= DIRQL, that is, less than or equal to the value of the SynchronizeIrql parameter specified when the caller registered its ISR(s) with IoConnectInterrupt.

See Also

IoConnectInterrupt


KeWaitForMultipleObjects

NTSTATUS
KeWaitForMultipleObjects(
IN ULONG Count,
 IN PVOID Object[],
 IN WAIT_TYPE WaitType,
 IN KWAIT_REASON WaitReason,
 IN KPROCESSOR_MODE WaitMode,
 IN BOOLEAN Alertable,
 IN PLARGE_INTEGER Timeout, /* optional */
IN PKWAIT_BLOCK WaitBlockArray /* optional */
);

KeWaitForMultipleObjects puts the current thread into an alertable or nonalertable wait state until any or all of a number of dispatcher objects are set to the Signaled state or (optionally) until the wait times out.

Parameters

Count
Specifies the number of objects to be waited on.
Object
Points to an array of pointers to dispatcher objects (events, mutexes, semaphores, threads, and timers) for which the caller supplies the storage.
WaitType
Specifies either WaitAll, indicating that all of the specified objects must attain a state of Signaled before the wait is satisfied, or WaitAny, indicating that any one of the objects must attain a state of Signaled before the wait is satisfied.
WaitReason
Specifies the reason for the wait. Drivers should set this value to Executive or, if the driver is doing work on behalf of a user and is running in the context of a User thread, to UserRequest.
WaitMode
Specifies whether the caller waits in KernelMode or UserMode. Intermediate and lowest level drivers should specify KernelMode. If the set of objects waited on includes a mutex, the caller must specify KernelMode.
Alertable
Specifies a Boolean value that indicates whether the thread can be alerted while it is in the waiting state.
Timeout
Points to an absolute or relative value representing the upper limit for the wait. A negative value specifies an interval relative to the current system time. The value should be expressed in units of 100 nanoseconds. Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.
WaitBlockArray
Points to an optional array of Wait blocks that describe the Wait operation.

Return Value

KeWaitForMultipleObjects can return one of the following NTSTATUS values:
Value | Meaning
STATUS_SUCCESS | Depending on the specified WaitType, one or all of the dispatcher objects in the Object array satisfied the wait.
STATUS_ALERTED | The wait is completed because of an alert to the thread.
STATUS_USER_APC | A user APC was delivered to the current thread before the specified Timeout interval expired.
STATUS_TIMEOUT | A time-out occurred before the specified set of wait conditions was met. This value can be returned when an explicit time-out value of zero is specified, but the specified set of wait conditions cannot be met immediately.

If KeWaitForMultipleObjects returns STATUS_SUCCESS and if WaitAny is specified as the WaitType, KeWaitForMultipleObjects also returns the zero-based index of the object that satisfied the wait at NTSTATUS.

Comments

Each thread object has a built-in array of wait blocks that can be used to wait on several objects concurrently. Whenever possible, the built-in array of wait blocks should be used in a wait-multiple operation because no additional wait block storage need be allocated and later deallocated. However, if the number of objects to be waited on concurrently is greater than the number of built-in wait blocks, use the WaitBlockArray parameter to specify an alternate set of wait blocks to be used in the wait operation.

If the WaitBlockArray parameter is NULL, the Count parameter must be less than or equal to THREAD_WAIT_OBJECTS or a bug check will occur.

If the WaitBlockArray pointer is nonNULL, the Count parameter must be less than or equal to MAXIMUM_WAIT_OBJECTS or a bug check will occur.

The current state for each of the specified objects is examined to determine whether the wait can be satisfied immediately. If the necessary side effects are performed on the objects, an appropriate value is returned.

If the wait cannot be satisfied immediately and either no time-out value or a nonzero time-out value has been specified, the current thread is put in a waiting state and a new thread is selected for execution on the current processor. If no Timeout is supplied, the calling thread will remain in a Wait state until the conditions specified by Object and WaitType are satisfied.

If Timeout is specified, the wait will be automatically satisfied if none of the specified wait conditions is met when the given interval expires.

A Timeout value of zero allows the testing of a set of wait conditions, conditionally performing any side effects if the wait can be immediately satisfied, as in the acquisition of a mutex.

The Alertable parameter specifies whether the thread can be alerted in the wait state. If the value of this parameter is TRUE and the thread is alerted for a mode that is equal to or more privileged than the given WaitMode, the thread’s wait will be satisfied with a completion status of STATUS_ALERTED.

If the WaitMode parameter is UserMode and the Alertable parameter is TRUE, the thread can also be awakened to deliver a user-mode APC. Kernel-mode APCs always cause the subject thread to be awakened if the wait IRQL is PASSIVE_LEVEL and no kernel APC is in progress.

Callers of KeWaitForMultipleObjects can be running at IRQL <= DISPATCH_LEVEL. However, the caller cannot wait at raised IRQL for a nonzero interval nor in an arbitrary thread context on any dispatcher object, so callers usually are running at IRQL PASSIVE_LEVEL. A call while running at IRQL DISPATCH_LEVEL is valid if and only if the caller specifies a Timeout of zero. That is, a driver must not wait for a nonzero interval at IRQL DISPATCH_LEVEL.

See Also

ExInitializeFastMutex, KeInitializeEvent, KeInitializeMutex, KeInitializeSemaphore, KeInitializeTimer, KeWaitForMutexObject, KeWaitForSingleObject


KeWaitForMutexObject

NTSTATUS
KeWaitForMutexObject(
IN PKMUTEX Mutex,
 IN KWAIT_REASON WaitReason,
 IN KPROCESSOR_MODE WaitMode,
 IN BOOLEAN Alertable,
 IN PLARGE_INTEGER Timeout /* optional */
);

KeWaitForMutexObject puts the current thread into an alertable or nonalertable wait state until the given mutex object is set to the Signaled state or (optionally) until the wait times out.

Parameters

Mutex
Points to an initialized mutex object for which the caller supplies the storage.
WaitReason
Specifies the reason for the wait, which should be set to Executive, or if the driver is doing work on behalf of a user and is running in the context of a User thread, to UserRequest.
WaitMode
The caller must specify KernelMode.
Alertable
Specifies a Boolean value that indicates whether the wait is alertable.
Timeout
Points to a time-out value that specifies the absolute or relative time at which the wait is to be completed (optional). A negative value specifies an interval relative to the current time. The value should be expressed in units of 100 nanoseconds. Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.

Return Value

KeWaitForMutexObject can return one of the following status values:
Value | Meaning
STATUS_SUCCESS | The dispatcher object specified by the Mutex parameter satisfied the wait.
STATUS_ALERTED | The wait was completed because of an alert to the thread.
STATUS_USER_APC | A user APC was delivered to the current thread before the specified Timeout interval expired.
STATUS_TIMEOUT | A time-out occurred before the mutex was set to Signaled. This value can be returned when an explicit time-out value of zero is specified and the specified set of wait conditions cannot be immediately met.

Comments

KeWaitForMutexObject is a macro which converts to KeWaitForSingleObject which can be used instead.

For better performance, use the Ex..FastMutex routines instead of the Ke..Mutex. However, a fast mutex cannot be acquired recursively, as a kernel mutex can.

The current state of the given mutex object is examined to determine whether the wait can be satisfied immediately. If so, the necessary side effects are performed on the mutex. Otherwise, the current thread is put in a waiting state and a new thread is selected for execution on the current processor.

The Alertable parameter specifies whether the thread can be alerted in the wait state. If the value of this parameter is TRUE and the thread is alerted for a mode that is equal to or more privileged than the given WaitMode, the thread’s wait will be satisfied with a completion status of STATUS_ALERTED.

If the WaitMode parameter is UserMode and the Alertable parameter is TRUE, the thread can also be awakened to deliver a user-mode APC. Kernel-mode APCs always cause the subject thread to be awakened if the wait IRQL is PASSIVE_LEVEL and no kernel APC is in progress.

If no Timeout is supplied, the calling thread will remain in a Wait state until the Mutex is signalled.

A Timeout of zero allows the testing of a set of wait conditions and for conditionally performing any side effects if the wait can be immediately satisfied, such as acquiring the Mutex.

Callers of KeWaitForMutexObject must be running at IRQL <= DISPATCH_LEVEL. Usually, the caller must be running at IRQL PASSIVE_LEVEL and in a nonarbitrary thread context. A call while running at IRQL DISPATCH_LEVEL is valid if and only if the caller specifies a Timeout of zero. That is, a driver must not wait for a nonzero interval at IRQL DISPATCH_LEVEL.

See Also

ExAcquireFastMutex, ExAcquireFastMutexUnsafe, ExInitializeFastMutex, KeBugCheckEx, KeInitializeMutex, KeReadStateMutex, KeReleaseMutex


KeWaitForSingleObject

NTSTATUS
KeWaitForSingleObject(
IN PVOID Object,
 IN KWAIT_REASON WaitReason,
 IN KPROCESSOR_MODE WaitMode,
 IN BOOLEAN Alertable,
 IN PLARGE_INTEGER Timeout /* optional */
);

KeWaitForSingleObject puts the current thread into an alertable or nonalertable wait state until the given dispatcher object is set to the Signaled state or (optionally) until the wait times out.

Parameters

Object
Points to an initialized dispatcher object (event, mutex, semaphore, thread, or timer) for which the caller supplies the storage.
WaitReason
Specifies the reason for the wait, which should be set to Executive by drivers or, if the driver is doing work on behalf of a user and is running in the context of a User thread, to UserRequest.
WaitMode
Specifies whether the caller waits in KernelMode or UserMode. Lowest-level and intermediate drivers should specify KernelMode. If the given Object is a mutex, the caller must specify KernelMode.
Alertable
Specifies a Boolean value that indicates whether the wait is alertable.
Timeout
Points to a time-out value that specifies the absolute or relative time at which the wait is to be completed (optional). A negative value specifies an interval relative to the current time. The value should be expressed in units of 100 nanoseconds. Absolute expiration times track any changes in the system time; relative expiration times are not affected by system time changes.

Return Value

KeWaitForSingleObject can return one of the following status values:
Value | Meaning
STATUS_SUCCESS | The dispatcher object specified by the Object parameter satisfied the wait.
STATUS_ALERTED | The wait was completed because of an alert to the thread.
STATUS_USER_APC | A user APC was delivered to the current thread before the specified Timeout interval expired.
STATUS_TIMEOUT | A time-out occurred before the object was set to Signaled. This value can be returned when an explicit time-out value of zero is specified and the specified set of wait conditions cannot be immediately met.

Comments

The current state of the specified Object is examined to determine whether the wait can be satisfied immediately. If so, the necessary side effects are performed on the object. Otherwise, the current thread is put in a waiting state and a new thread is selected for execution on the current processor.

The Alertable parameter specifies whether the thread can be alerted in the wait state. If the value of this parameter is TRUE and the thread is alerted for a mode that is equal to or more privileged than the given WaitMode, the thread’s wait will be satisfied with a completion status of STATUS_ALERTED.

If the WaitMode parameter is UserMode and the Alertable parameter is TRUE, the thread can also be awakened to deliver a user-mode APC. Kernel-mode APCs always cause the subject thread to be awakened if the wait IRQL is PASSIVE_LEVEL and no kernel APC is in progress.

If no Timeout is supplied, the calling thread will remain in a Wait state until the Object is signalled.

A Timeout of zero allows the testing of a set of wait conditions and for conditionally performing any side effects if the wait can be immediately satisfied, as in the acquisition of a mutex.

Callers of KeWaitForSingleObject must be running at IRQL <= DISPATCH_LEVEL. Usually, the caller must be running at IRQL PASSIVE_LEVEL and in a nonarbitrary thread context. A call while running at IRQL DISPATCH_LEVEL is valid if and only if the caller specifies a Timeout of zero. That is, a driver must not wait for a nonzero interval at IRQL DISPATCH_LEVEL.

See Also

ExInitializeFastMutex, KeBugCheckEx, KeInitializeEvent, KeInitializeMutex, KeInitializeSemaphore, KeInitializeTimer, KeWaitForMultipleObjects, KeWaitForMutexObject


Chapter 6 Memory Manager Routines

References for the Mm Xxx routines and macros are in alphabetical order. Click the browse button to view them in sequence.

For an overview of the functionality of these routines and macros, see Chapter 1.


ADDRESS_AND_SIZE_TO_SPAN_PAGES

ULONG
ADDRESS_AND_SIZE_TO_SPAN_PAGES(
IN PVOID Va,
 IN ULONG Size
);

ADDRESS_AND_SIZE_TO_SPAN_PAGES takes a virtual address and size in bytes of a transfer request and returns the number of pages spanned by the virtual range.

Parameters

Va
Points to the virtual address that is the base of the range.
Size
Specifies the size in bytes of the transfer request.

Return Value

ADDRESS_AND_SIZE_TO_SPAN_PAGES returns the number of pages for the virtual range starting at Va.

Comments

ADDRESS_AND_SIZE_TO_SPAN_PAGES is called by drivers that make DMA transfers to determine whether a given transfer request must be split up into a sequence of device DMA operations.

The system-defined constant PAGE_SIZE can be used to determine whether a given length in bytes for a transfer is less than the page size of the current platform.

Callers of ADDRESS_AND_SIZE_TO_SPAN_PAGES can be running at any IRQL, and usually run at IRQL DISPATCH_LEVEL.

See Also

IoMapTransfer


ARGUMENT_PRESENT

BOOLEAN
ARGUMENT_PRESENT(
IN (CHAR *) ArgumentPointer
);

ARGUMENT_PRESENT is a macro that takes an argument pointer and returns FALSE if the pointer is NULL, TRUE otherwise.

Parameters

ArgumentPointer
The value of the pointer argument to be tested.

Return Value

ARGUMENT_PRESENT returns FALSE if the value of ArgumentPointer is NULL, true otherwise.

Comments

This macro can be called in conditional code to determine whether an optional argument has been passed in a call.

Callers of ARGUMENT_PRESENT can be running at any IRQL.


BYTE_OFFSET

ULONG
BYTE_OFFSET(
IN PVOID Va
);

BYTE_OFFSET takes a virtual address and returns the byte offset of that address within the page.

Parameters

Va
Points to the virtual address.

Return Value

BYTE_OFFSET returns the offset portion of the virtual address.

Comments

Callers of BYTE_OFFSET can be running at any IRQL.


BYTES_TO_PAGES

ULONG
BYTES_TO_PAGES(
IN ULONG Size
);

BYTES_TO_PAGES takes the size in bytes of the transfer request and calculates the number of pages required to contain the bytes.

Parameters

Size
Specifies the size in bytes of the transfer request.

Return Value

BYTES_TO_PAGES returns the number of pages required to contain the specified number of bytes.

Comments

The system-defined constant PAGE_SIZE can be used to determine whether a given length in bytes for a transfer is less than the page size of the current platform.

Callers of BYTES_TO_PAGES can be running at any IRQL.

See Also

ADDRESS_AND_SIZE_TO_SPAN_PAGES


COMPUTE_PAGES_SPANNED

ULONG
COMPUTE_PAGES_SPANNED(
IN PVOID Va,
 IN ULONG Size
);

Use ADDRESS_AND_SIZE_TO_SPAN_PAGES instead of this macro.


CONTAINING_RECORD

PCHAR
CONTAINING_RECORD(
IN PCHAR Address,
IN TYPE Type,
 IN PCHAR Field
);

CONTAINING_RECORD returns the base address of an instance of a structure given the type of the structure and the address of a field within the containing structure.

Parameters

Address
Points to a field in an instance of a structure of type Type.
Type
The name of the type of the structure whose base address is to be returned. For example, type IRP.
Field
The name of the field pointed to by Address and which is contained in a structure of type Type .

Return Value

Returns the address of the base of the structure containing Field.

Comments

Called to determine the base address of a structure whose type is known when the caller has a pointer to a field inside such a structure. This macro is useful for symbolically accessing other fields in a structure of known type.

Callers of CONTAINING_RECORD can be running at any IRQL as long as the structure is resident. If a page fault might occur, callers must be at or below IRQL APC_LEVEL.

See Also

FIELD_OFFSET


FIELD_OFFSET

LONG
FIELD_OFFSET(
IN TYPE Type,
 IN PCHAR Field
);

FIELD_OFFSET returns the byte offset of a named field in a known structure type.

Parameters

Type
The name of a known structure type containing Field.
Field
The name of a field in a structure of type Type.

Return Value

Returns the byte offset of the caller supplied Field in the Type structure.

Comments

Used by device driver writers to symbolically determine the offset of a known field in a known structure type.

Callers of FIELD_OFFSET can be running at any IRQL as long as the structure is resident. If a page fault could occur, callers must be at or below IRQL APC_LEVEL.

See Also

CONTAINING_RECORD


MmAllocateContiguousMemory

PVOID
MmAllocateContiguousMemory(
IN ULONG NumberOfBytes,
 IN PHYSICAL_ADDRESS HighestAcceptableAddress
);

MmAllocateContiguousMemory allocates a range of physically contiguous, cache-aligned memory from nonpaged pool.

Parameters

NumberOfBytes
Specifies the size in bytes of the block of contiguous memory to be allocated.
HighestAcceptableAddress
Specifies the highest valid physical address the driver can use. For example, if a device can only reference physical memory in the lower 16MB, this value would be set to 0x00000000FFFFFF.

Return Value

MmAllocateContiguousMemory returns the base virtual address for the allocated memory. If the request cannot be satisfied, NULL is returned.

Comments

MmAllocateContiguousMemory can be called to allocate a contiguous block of physical memory for a long-term internal buffer, usually from the DriverEntry routine.

A device driver that must use contiguous memory should allocate only what it needs during driver initialization because nonpaged pool is likely to become fragmented as the system runs. Such a driver must deallocate the memory if it is unloaded. Contiguous allocations are aligned on an integral multiple of the processor’s data-cache-line size to prevent cache and coherency problems.

Callers of MmAllocateContiguousMemory must be running at IRQL = PASSIVE_LEVEL.

See Also

HalAllocateCommonBuffer, KeGetDcacheFillSize, MmAllocateNonCachedMemory, MmFreeContiguousMemory


MmAllocateNonCachedMemory

PVOID
MmAllocateNonCachedMemory(
IN ULONG NumberOfBytes
);

MmAllocateNonCachedMemory allocates a virtual address range of noncached and cache-aligned memory.

Parameters

NumberOfBytes
Specifies the size in bytes of the range to be allocated.

Return Value

If the requested memory cannot be allocated, the return value is NULL. Otherwise, it is the base virtual address of the allocated range.

Comments

MmAllocateNonCachedMemory can be called from a DriverEntry routine to allocate a noncached block of virtual memory for various device-specific buffers.

A device driver that must use noncached memory should allocate only what it needs during driver initialization because nonpaged pool is likely to become fragmented as the system runs. Such a driver must deallocate the memory if it is unloaded. Noncached allocations are aligned on an integral multiple of the processor’s data-cache-line size to prevent cache and coherency problems.

Callers of MmAllocateNonCachedMemory must be running at IRQL < DISPATCH_LEVEL.

See Also

HalAllocateCommonBuffer, KeGetDcacheFillSize, MmAllocateContiguousMemory, MmFreeNonCachedMemory


MmBuildMdlForNonPagedPool

VOID
MmBuildMdlForNonPagedPool(
IN OUT PMDL MemoryDescriptorList
);

MmBuildMdlForNonPagedPool fills in the corresponding physical page array of a given MDL for a buffer in nonpaged system space (pool).

Parameters

MemoryDescriptorList
Points to an MDL that supplies a virtual address, byte offset, and length.

Comments

The physical page portion of the MDL is updated as the pages are locked in memory. The input MDL virtual address must be within the nonpaged portion of system space.

Callers of MmBuildMdlForNonPagedPool must be running at IRQL <= DISPATCH_LEVEL.

See Also

ExAllocatePool, ExAllocatePoolWithTag, IoAllocateMdl, MmCreateMdl, MmInitializeMdl, MmIsNonPagedSystemAddressValid, MmMapIoSpace, MmSizeOfMdl


MmCreateMdl

PMDL
MmCreateMdl(
IN PMDL MemoryDescriptorList, /* optional */
IN PVOID Base,
 IN ULONG Length
);

MmCreateMdl allocates and initializes an MDL.

Parameters

MemoryDescriptorList
Points to an MDL to initialize. If this pointer is NULL, an MDL is allocated from nonpaged pool and initialized. Otherwise, the caller-supplied MDL is initialized.
Base
Points to the base virtual address for the buffer.
Length
Specifies the size in bytes of the buffer.

Return Value

MmCreateMdl returns a pointer to the initialized MDL.

Comments

The caller must be running at IRQL < DISPATCH_LEVEL, if the caller passes a NULL pointer for MemoryDescriptorList and expects MmCreateMdl to allocate the storage.

If the call to MmCreateMdl occurs while running at IRQL >= DISPATCH_LEVEL, the caller must allocate memory for the MDL from nonpaged pool and must supply a nonNULL pointer for MemoryDescriptorList.

See Also

MmSizeOfMdl


MmFreeContiguousMemory

VOID
MmFreeContiguousMemory(
IN PVOID BaseAddress
);

MmFreeContiguousMemory releases a range of physically contiguous memory that was allocated with MmAllocateContiguousMemory.

Parameters

BaseAddress
Points to the virtual address of the memory to be freed.

Comments

This routine is the reciprocal of MmAllocateContiguousMemory, and is usually called only when a driver is being unloaded. The input BaseAddress must have been returned by a preceding call to MmAllocateContiguousMemory.

A device driver that must use contiguous memory should allocate only what it needs during driver initialization because nonpaged pool is likely to become fragmented as the system runs. Such a driver must deallocate the memory if it is unloaded.

Callers of MmFreeContiguousMemory must be running at IRQL = PASSIVE_LEVEL.

See Also

MmAllocateContiguousMemory


MmFreeNonCachedMemory

VOID
MmFreeNonCachedMemory(
IN PVOID BaseAddress,
 IN ULONG NumberOfBytes
);

MmFreeNonCachedMemory releases a range of noncached memory that was allocated with MmAllocateNonCachedMemory.

Parameters

BaseAddress
Points to the virtual address of the memory to be freed.
NumberOfBytes
Specifies the size of the range to be freed. The value must match the size passed in a preceding call to MmAllocateNonCachedMemory.

Comments

This routine is the reciprocal of MmAllocateNonCachedMemory, and is usually called only when a driver is being unloaded.

A device driver that must use noncached memory should allocate only what it needs during driver initialization because nonpaged pool is likely to become fragmented as the system runs. Such a driver must deallocate the memory if it is unloaded.

Callers of MmFreeNonCachedMemory must be running at IRQL < DISPATCH_LEVEL.

See Also

MmAllocateNonCachedMemory


MmGetMdlByteCount

ULONG
MmGetMdlByteCount(
IN PMDL Mdl
);

MmGetMdlByteCount returns the length in bytes of the buffer described by a given MDL.

Parameters

Mdl
Points to an MDL.

Return Value

MmGetMdlByteCount returns the byte count of the buffer described by Mdl.

Comments

Callers of MmGetMdlByteCount can be running at any IRQL. Usually, callers are running at IRQL <= DISPATCH_LEVEL.

See Also

MmGetMdlByteOffset.


MmGetMdlByteOffset

ULONG
MmGetMdlByteOffset(
IN PMDL Mdl
);

MmGetMdlByteOffset returns the byte offset within a page of the buffer described by a given MDL.

Parameters

Mdl
Points to an MDL.

Return Value

MmGetMdlByteOffset returns the offset in bytes.

Comments

Callers of MmGetMdlByteOffset can be running at any IRQL. Usually, callers are running at IRQL <= DISPATCH_LEVEL.

See Also

MmGetMdlByteCount MmGetMdlVirtualAddress.


MmGetMdlVirtualAddress

PVOID
MmGetMdlVirtualAddress(
IN PMDL Mdl
);

MmGetMdlVirtualAddress returns the initial virtual address for a buffer described by a given MDL. The returned address, used as an index to a physical address entry in the MDL, can be input to IoMapTransfer.

Parameters

Mdl
Points to an MDL for a buffer whose initial virtual address is to be returned.

Return Value

MmGetMdlVirtualAddress returns the virtual address that corresponds to the buffer specified by the MDL.

Comments

MmGetMdlVirtualAddress returns a virtual address that is not necessarily valid in the current thread context. Lower-level drivers should not attempt to use the returned virtual address to access memory, particularly user-space memory.

Callers of MmGetMdlVirtualAddress can be running at any IRQL. Usually, the caller is running at IRQL DISPATCH_LEVEL because this routine is commonly called to obtain the CurrentVa parameter to IoMapTransfer.

See Also

IoMapTransfer, MmGetMdlByteOffset, MmIsAddressValid, MmIsNonPagedSystemAddressValid


MmGetPhysicalAddress

PHYSICAL_ADDRESS
MmGetPhysicalAddress(
IN PVOID BaseAddress
);

MmGetPhysicalAddress returns the physical address corresponding to a valid virtual address.

Parameters

BaseAddress
Points to the virtual address for which to return the physical address.

Return Value

MmGetPhysicalAddress returns the physical address that corresponds to the given virtual address.

Comments

Callers of MmGetPhysicalAddress can be running at any IRQL, provided that the BaseAddress value is valid.

See Also

MmIsAddressValid, MmIsNonPagedSystemAddressValid, MmMapIoSpace, MmProbeAndLockPages


MmGetSystemAddressForMdl

PVOID
MmGetSystemAddressForMdl(
IN PMDL Mdl
);

MmGetSystemAddressForMdl returns a nonpaged system-space virtual address for the buffer described by the MDL. It maps the physical pages described by a given MDL into system space, if they are not already mapped to system space.

Parameters

Mdl
Points to a buffer whose corresponding base virtual address is to be mapped.

Return Value

MmGetSystemAddressForMdl returns the base system-space virtual address that maps the physical pages described by the given MDL.

Comments

Drivers of PIO devices call this routine to translate a virtual address range, described by the MDL at Irp->MdlAddress, for a user buffer to a system-space address range.

A caller running at IRQL DISPATCH_LEVEL must supply an MDL that maps nonpageable virtual addresses. The input MDL must describe an already locked-down user-space buffer returned by MmProbeAndLockPages, a locked-down buffer returned by MmBuildMdlForNonPagedPool, or system-space memory allocated from nonpaged pool, contiguous memory, noncached memory, or a zone buffer.

The returned base address has the same offset as the virtual address in the MDL.

Callers of MmGetSystemAddressForMdl must be running at IRQL <= DISPATCH_LEVEL.

See Also

MmBuildMdlForNonPagedPool, MmProbeAndLockPages


MmInitializeMdl

VOID
MmInitializeMdl(
IN PMDL MemoryDescriptorList,
 IN PVOID BaseVa,
 IN ULONG Length
);

MmInitializeMdl initializes the header of an MDL.

Parameters

MemoryDescriptorList
Points to the MDL to be initialized.
BaseVa
Points to the base virtual address of a buffer.
Length
Specifies the length in bytes of the buffer to be described by the MDL.

Comments

Callers of MmInitializeMdl must be running at <= DISPATCH_LEVEL.

See Also

IoAllocateMdl, IoBuildPartialMdl, IoFreeMdl, MmBuildMdlForNonPagedPool, MmCreateMdl, MmPrepareMdlForReuse, MmSizeOfMdl


MmIsAddressValid

BOOLEAN
MmIsAddressValid(
IN PVOID VirtualAddress
);

MmIsAddressValid checks whether a page fault will occur for a read or write operation at a given virtual address.

Parameters

VirtualAddress
Points to the virtual address to check.

Return Value

If no page fault will occur from reading or writing at the given virtual address, MmIsAddressValid returns TRUE.

Comments

Even if MmIsAddressValid returns TRUE, accessing the address can cause page faults unless the memory has been locked down or the address is a valid nonpaged pool address.

Callers of MmIsAddressValid must be running at <= DISPATCH_LEVEL.

See Also

MmProbeAndLockPages


MmIsNonPagedSystemAddressValid

BOOLEAN
MmIsNonPagedSystemAddressValid(
IN PVOID VirtualAddress
);

MmIsNonPagedSystemAddressValid is exported to support existing drivers and is obsolete.


MmIsThisAnNtAsSystem

BOOLEAN
MmIsThisAnNtAsSystem(
);

MmIsThisAnNtAsSystem checks whether the current platform is running a workstation or server version of Windows NT.

Return Value

If Windows NT Advanced Server is running, MmIsThisAnNtAsSystem returns TRUE.

Comments

Drivers can use this routine during initialization, along with MmQuerySystemSize, for sizing estimates of how many resources to allocate. For example, if MmIsThisAnNtAsSystem returns TRUE, the caller can increase the number of threads or the size of a zone segment that it creates in medium and large systems.

Callers of MmIsThisAnNtAsSystem must be running at IRQL PASSIVE_LEVEL.

See Also

MmQuerySystemSize, ExInitializeZone


MmLockPagableCodeSection

PVOID
MmLockPagableCodeSection(
IN PVOID AddressWithinSection
);

MmLockPagableCodeSection locks a section of driver code, containing a set of driver routines marked with a special compiler directive, into system space.

Parameters

AddressWithinSection
Is a symbolic address, usually the entry point of a driver routine, within the pageable section of driver code.

Return Value

MmLockPagableCodeSection returns a handle for the locked-down section of driver code. The handle must be passed subsequently to MmLockPagableSectionByHandle or to MmUnlockPagableImageSection.

Comments

This routine and its reciprocal, MmUnlockPagableImageSection, support drivers that can do the following:

  • Defer loading a subset of driver routines into resident memory until incoming I/O requests for the driver’s device(s) make it necessary for these routines to process IRPs

  • Make the same subset of driver routines available for paging out when they have completed the processing of I/O requests and no additional requests for the driver’s device(s) are currently expected

MmLockPagableCodeSection, MmLockPagableSectionByHandle and MmUnlockPagableImageSection are intended for use by device and intermediate drivers that have the following characteristics:

  • The driver has code paths that might not be needed while the system is running, but, if they are needed, the driver’s code must be resident because it runs in an arbitrary thread context and/or at raised IRQL.

  • The driver can determine exactly when the pageable-routines should be loaded and when they can be paged out again.

For example, the system-supplied fault-tolerant disk driver supports the creation of mirror sets, stripe sets, and volume sets. Yet, a particular machine can be configured only with a mirror set, only with a stripe set, only with a volume set, or with any combination of these three possible options. In these circumstances, the system ftdisk driver reduces the size of its loaded image by marking routines that explictly support mirror, stripe, and volume sets as belonging to pageable-code sections. During driver initialization, pageable-code section(s) are made resident only if the user has configured the disks to have mirror, stripe, and/or volume sets. If the user repartitions the disks dynamically, the ftdisk driver loads any additional pageable-code sections necessary to support any mirror, stripe, and/or volume sets that the user requests.

As other examples, the system-supplied serial and parallel drivers have DispatchCreate and DispatchClose routines that are called when a particular port is opened for exclusive I/O and when the handle for an opened port is released, respectively. Yet, serial and parallel I/O requests are sporadic, determined by which applications the end user is currently running and which application options the end user is currently exercising. In these circumstances, the system serial and parallel drivers reduce the sizes of their loaded images by marking many routines as belonging to a pageable-code section that the DispatchCreate routine makes resident only when the first port is opened for I/O.

Note that each of the preceding system drivers satisfies both criteria for having pageable sections: the driver has code paths that might not be needed while the system is running and the driver can determine exactly when its pageable section should be loaded and can be paged out again.

Note also that calling MmLockPagableCodeSection and MmUnlockPagableImageSection reduce the size of each driver’s loaded image at a cost to the driver’s and system’s performance. Drivers of performance-critical devices, such as keyboard and mouse drivers, and drivers of devices that are constantly in use, such as disk drivers, generally should not have pageable sections: the loss in performance is simply not worth the temporary reduction in driver image size.

Because it is an expensive operation to lock down a section, if a pageable-code section is locked down in more than place by a driver, use MmLockPagableCodeSection for the first request. Make subsequent lock requests by calling MmLockPagableSectionByHandle passing the handle returned by MmLockPagableCodeSection. Locking by handle significantly improves driver performance because the memory manager uses the handle to find the section rather than searching a loaded module list. A locked down section is unlocked by calling MmUnlockPagableImageSection.

Each driver routine within a pageable code section must be marked with the following compiler directive, supported by all Windows NT-compatible compilers:

#pragma alloc_text(PAGE xxxx, DriverRoutine), where xxxx is an optional four-character, unique identifier for the caller’s pageable section and DriverRoutine is an entry point to be included within the pageable-code section. For Windows NT-compatible compilers, the keyword PAGE and the driver-determined suffix, which can be up to four characters, are case-sensitive, that is, PAGE must be capitalized.

A single call to MmLockPagableCodeSection in, for example, a driver’s DispatchCreate routine causes the entire section, containing every driver routine marked with the same PAGE xxxx identifier to be locked in system space.

Certain types of driver routines cannot be made part of any driver’s pageable section, including the following:

  • Never make an ISR pageable. It is possible for a device driver to receive a spurious interrupt even if its device is not in use, particularly if the interrupt vector could be shared. In general, even if a driver can explictly disable interrupts on its device, an ISR should not be made pageable.

  • Never make a DPC routine pageable if the driver cannot control when the DPC is queued, such as any DpcForIsr or CustomDpc routine that might be queued from an ISR. In general, driver routines that run at raised IRQL and that can be called in an arbitrary thread context or in response to a random external event should not be made pageable.

  • Never make the DispatchRead or DispatchWrite routine pageable in any driver that might be part of the system paging I/O path. The driver of a disk that might contain the system page file must have DispatchRead and DispatchWrite routines that are resident while the system is running, as must all drivers layered above such a disk driver.

Note that routines in a pageable section marked with the compiler directive #pragma alloc_text(PAGExxxx, ...)differ from routines marked with the compiler directive #pragma alloc_text(INIT, ...). The routines in the INIT section are not pageable and are discarded as soon as the driver returns from its DriverEntry or its Reinitialize routine, if it has one.

The Memory Manager maintains an internal lock count on any driver’s pageable-section. Calls to MmLockPagableCodeSection increment this count and the reciprocal MmUnlockPagableImageSection decrements the count. A driver’s pageable section is not available to be paged out unless this count is zero.

Callers of MmLockPagableCodeSection and MmLockPagableDataSection must take care to use the former for code sections and the latter for data sections. If the incorrect form of MmLockPagableXxxxSection is used, a fatal error will occur on some platforms.

For more information on creating pageable code sections, see The Programmer’s Guide.

Callers of MmLockPagableCodeSection run at IRQL PASSIVE_LEVEL.

See Also

MmUnlockPagableImageSection, MmPageEntireDriver, MmResetDriverPaging, MmLockPagableDataSection, MmLockPagableSectionByHandle


MmLockPagableDataSection

PVOID
MmLockPagableDataSection(
IN PVOID AddressWithinSection
);

MmLockPagableDataSection locks an entire section of driver data into system space.

Parameters

AddressWithinSection
Is the symbolic address of one item of data within the pageable section.

Return Value

MmLockPagableDataSection returns a handle to the section. This handle must be passed subsequently to MmLockPagableSectionByHandle or to MmUnlockPagableImageSection.

Comments

This routine, MmLockPagableSectionByHandle and MmUnlockPagableImageSection are used by drivers to make data pageable but locked in memory on demand.

Data can be pageable if:

  • The data is accessed at <DISPATCH_LEVEL.

  • The driver uses the data infrequently and predictably.

Drivers for mixer devices use pageable-data sections. Because the driver uses sufficient data to make creating a pageable-data section worthwhile and the driver knows when the data is needed, such a driver uses MmLockPagableDataSection, MmLockPagableSectionByHandle and MmUnlockPagableImageSection to bring a data section into system space when needed and make it available to be paged out when not needed.

A single call to MmLockPagableDataSection causes the entire section, containing the referenced data, to be locked into system space.

It is an expensive operation to lock down a section. If a pageable-data section is locked down in more than one place by a driver, use MmLockPagableDataSection for the first request. Make subsequent lock requests by calling MmLockPagableSectionByHandle, passing the handle returned by MmLockPagableDataSection. Locking by handle significantly improves driver performance. A locked down section is unlocked by calling MmUnlockPagableImageSection.

The memory manager maintains a reference count on the handle to a section. A pageable-data section is only available to be paged out when the reference count is zero. Every lock request increments the count; every unlock request decrements the count. A driver must unlock a section as many times as it locks a section to insure that such a section will be available to be paged out when the section is not needed. A handle is always valid, no matter what the count. If the count on a handle is zero and a call is made to MmLockPagableSectionByHandle, the count is set to one, and if the section has been paged out, it will be paged in.

Callers of MmLockPagableDataSection and MmLockPagableCodeSection must use the former for data sections and the latter for code sections. If the incorrect form of MmLockPagableXxxxSection is used, a fatal error will occur on some platforms.

Data in a pageable-data section is marked by a directive available in Windows NT-compatible compilers. To create a pageable data section, use #pragma data_seg (“PAGE”), at the beginning of the data module, and #pragma data_seg () at the end of the module. The keyword PAGE is case-sensitive, that is, PAGE must be capitalized.

Note that there is also a #pragma data_seg(“INIT”) that is used to make data discardable after system initialization. Except for the use of INIT rather than PAGE, the syntax is the same. However the result is not; use of the PAGE directive makes the data section pageable. When the INIT directive is used, the data in the section is discarded as soon as the driver returns from its driver entry routine or its reinitialization routine if the driver has one.

For more information about paging data, see The Programmer’s Guide.

Callers of MmLockPagableDataSection run at IRQL PASSIVE_LEVEL.

See Also

MmLockPagableCodeSection, MmLockPagableSectionByHandle, MmPageEntireDriver, MmResetDriverPaging, MmUnlockPagableImageSection


MmLockPagableSectionByHandle

VOID
MmLockPagableSectionByHandle(
IN PVOID ImageSectionHandle
);

MmLockPagableSectionByHandle takes a handle returned by MmLockPagableDataSection or MmLockPagableCodeSection. This routine checks to see if the referenced section is resident in the caller’s address space and if so, simply increments a reference count on the section. If the section is not resident, MmLockPagableImage pages in the section, locks it in system space and sets the reference count to one.

Parameters

ImageSectionHandle
Supplies the handle returned by a call to MmLockPagableCodeSection or MmLockPagableDataSection.

Return Value

None.

Comments

If a pageable section is locked down in more than one place by a driver, use MmLockPagableXxxxSection for the first request. Make subsequent lock requests by calling MmLockPagableSectionByHandle passing the handle returned by MmLockPagableXxxxSection. A locked down section is unlocked by calling MmUnlockPagableImageSection.

A handle returned from an MmLockPagableXxxxSection is valid until a driver is unloaded.

Locking by handle significantly improves driver performance. When MmLockPagableCodeSection or MmLockPagableDataSection is called, the memory manager walks the entire loaded module list to find the module containing the specified address. This is an expensive operation. Calling MmLockPagableImageSectionByHandle reduces this burden because if the caller supplies a handle to the section, the memory manager no longer has to search.

The memory manager maintains a reference count on the handle to the section. A pageable section is only available to be paged out when the reference count is zero. Every lock request increments the count; every unlock request decrements the count. A driver must take care to unlock a section as many times as it locks a section to insure that such a section will be eligible to be paged out when the section is not needed. Once a handle is obtained, it is always valid, no matter what the count until the driver is unloaded. If the count on a handle is zero and a call is made to MmLockPagableSectionByHandle, the count is set to one, and if the section has been paged out, it will be paged in.

A driver cannot call MmLockPagableSectionByHandle to lock down user buffers passed in IRPs. Use MmProbeAndLockPages instead.

For more information about paging code and data, see The Programmer’s Guide.

Callers of MmLockPagableSectionByHandle runs at IRQL PASSIVE_Level.

See Also

MmLockPagableDataSection, MmLockPagableCodeSection, MmProbeAndLockPages, MmPageEntireDriver, MmResetDriverPaging, MmUnlockPagableImageSection


MmMapIoSpace

PVOID
MmMapIoSpace(
IN PHYSICAL_ADDRESS PhysicalAddress,
 IN ULONG NumberOfBytes,
 IN BOOLEAN CacheEnable
);

MmMapIoSpace maps the given physical address range to nonpaged system space.

Parameters

PhysicalAddress
Specifies the starting physical address of the I/O range to be mapped.
NumberOfBytes
Specifies the number of bytes to be mapped.
CacheEnable
Indicates whether the physical address range can be mapped as cached memory. For device registers, this value usually is FALSE.

Return Value

MmMapIoSpace returns the base virtual address that maps the base physical address for the range. If space for mapping the range is insufficient, it returns NULL.

Comments

Device drivers call this routine during initialization to get a logical address for their device memory if a call to HalTranslateBusAddress indicates that the bus-relative device memory range has been mapped to system memory space.

For example, drivers of PIO devices that allocate long-term I/O buffers can call this routine to make such a buffer accessible or to make device memory accessible.

Callers of MmMapIoSpace must be running at IRQL = PASSIVE_LEVEL.

See Also

HalAllocateCommonBuffer, HalTranslateBusAddress, MmAllocateContiguousMemory, MmAllocateNonCachedMemory, MmMapLockedPages, MmUnmapIoSpace


MmMapLockedPages

PVOID
MmMapLockedPages(
IN PMDL MemoryDescriptorList,
 IN KPROCESSOR_MODE AccessMode
);

MmMapLockedPages maps physical pages described by a given MDL.

Parameters

MemoryDescriptorList
Points to an MDL that has been updated by MmProbeAndLockPages.
AccessMode
Specifies the access mode in which to map the MDL, either KernelMode or UserMode. Lower-level drivers should use KernelMode.

Return Value

MmMapLockedPages returns the base virtual address that maps the locked pages for the range described by the MDL.

Comments

The MDL in an IRP at Irp->MdlAddress does not necessarily map a buffer for the current thread when that IRP is processed by lower-level drivers. Therefore, the user-mode virtual addresses corresponding to the locked-down physical pages can be invisible or invalid.

Callers of MmMapLockedPages must be running at IRQL <= DISPATCH_LEVEL if AccessMode is KernelMode. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

MmGetSystemAddressForMdl, MmProbeAndLockPages, MmUnmapLockedPages


MmPageEntireDriver

VOID
MmPageEntireDriver(
IN PVOID AddressWithinSection
);

MmPageEntireDriver causes all of a driver’s code and data to be made pageable, overriding the attributes of the various sections that make up the driver’s image.

Parameters

AddressWithinSection
Points to a symbolic address within the driver, for example, DriverEntry.

Comments

Use this routine to force a driver to be completely pageable. The driver must not have interrupts connected on its device before making this call since as a result of this call, the entire driver will be marked as pageable. If the driver is pageable, such code as the ISR code may be paged out after the driver calls MmPageEntireDriver.

MmPageEntireDriver may be called multiple times without intervening calls to MmResetDriverPaging. However, subsequent calls to MmPageEntireDriver do nothing if the driver’s pageable status has not been reset by a call to MmResetDriverPaging.

For more information about paging an entire driver, see The Programmer’s Guide.

Callers of MmPageEntireDriver run at IRQL PASSIVE_LEVEL.

See Also

MmResetDriverPaging, MmLockPagableCodeSection, MmLockPagableDataSection, MmLockPagableSectionByHandle, MmUnlockPagableImageSection


MmResetDriverPaging

VOID
MmResetDriverPaging(
IN PVOID AddressWithinSection
);

MmResetDriverPaging resets the pageable status of a driver’s sections to that specified when the driver was compiled.

Parameters

AddressWithinSection
Points to a symbolic address in the driver, for example, DriverEntry.

Comments

MmResetDriverPaging causes those routines that would not normally be pageable, to be locked into memory. Hence, image sections such as .text and .data will be locked in memory if this routine is called.

A driver that calls this routine must do so before enabling interrupts on its device.

A call to MmPageEntireDriver is not a prerequisite to calling this routine. However, calls to MmResetDriverPaging do nothing if the driver’s image-section attributes have never been overridden by a call to MmPageEntireDriver.

For more information about paging an entire driver, see The Programmer’s Guide.

Callers of MmResetDriverPaging must be running at IRQL PASSIVE_LEVEL.

See Also

MmPageEntireDriver, MmLockPagableCodeSection, MmLockPagableDataSection, MmLockPagableSectionByHandle, MmUnlockPagableImageSection


MmPrepareMdlForReuse

VOID
MmPrepareMdlForReuse(
IN PMDL MDL
);

MmPrepareMdlForReuse reinitializes a caller-allocated MDL.

Parameters

MDL
Points to the MDL that will be reused.

Comments

MmPrepareMdlForReuse is called by drivers that allocate a set of MDLs that they use and reuse repeatedly. After each call to MmPrepareMdlForReuse, the given MDL must be reinitialized. Very few drivers call this routine.

Callers of MmPrepareMdlForReuse must be running at IRQL <= DISPATCH_LEVEL.

See Also

IoAllocateMdl, IoBuildPartialMdl, IoFreeMdl, MmCreateMdl, MmInitializeMdl


MmProbeAndLockPages

VOID
MmProbeAndLockPages(
IN OUT PMDL MemoryDescriptorList,
 IN KPROCESSOR_MODE AccessMode,
 IN LOCK_OPERATION Operation
);

MmProbeAndLockPages probes specified pages, makes them resident, and locks the physical pages mapped by the virtual address range in memory. The MDL is updated to describe the physical pages.

Parameters

MemoryDescriptorList
Points to an MDL that supplies a virtual address, byte offset, and length. The physical page portion of the MDL is updated when the pages are locked in memory.
AccessMode
Specifies the access mode in which to probe the arguments, either KernelMode or UserMode.
Operation
Specifies the type of operation for which the caller wants the access rights probed and the pages locked, one of IoReadAccess, IoWriteAccess, or IoModifyAccess.

Comments

The highest-level driver in a chain of layered drivers that use direct I/O calls this routine. Drivers that use buffered I/O never call MmProbeAndLockPages.

If this routine fails, an exception is raised. Any driver that calls MmProbeAndLockPages must handle such an exception.

A lower-level driver cannot attempt to pass such an exception on to a higher-level driver. It cannot assume anything about a higher-level driver’s exception handling capabilities. In particular, the driver cannot call ExRaiseStatus to pass on such an exception.

Callers of MmProbeAndLockPages must be running at IRQL < DISPATCH_LEVEL for pageable addresses, or at IRQL <= DISPATCH_LEVEL for nonpageable addresses.

See Also

MmUnlockPages


MmQuerySystemSize

MM_SYSTEM_SIZE
MmQuerySystemSize(
);

MmQuerySystemSize returns an estimate of the amount of memory in the system.

Return Value

MmQuerySystemSize returns one of MmSmallSystem, MmMediumSystem, or MmLargeSystem.

Comments

This routine can be called during driver initialization to determine how much memory it is practical to allocate for an internal buffer, such as a zone segment.

Callers of MmQuerySystemSize must be running at IRQL PASSIVE_LEVEL.

See Also

ExAllocatePool, ExInitializeZone, MmIsThisAnNtAsSystem


MmSizeOfMdl

ULONG
MmSizeOfMdl(
IN PVOID Base,
 IN ULONG Length
);

MmSizeOfMdl returns the number of bytes to allocate for an MDL describing a given address range.

Parameters

Base
Points to the base virtual address for the range.
Length
Supplies the size, in bytes, of the range.

Return Value

MmSizeOfMdl returns the number of bytes required to contain the MDL.

Comments

The given address range must be locked down if it will be accessed at raised IRQL.

Memory for the MDL itself must be allocated from nonpaged pool if the caller subsequently passes a pointer to the MDL while running at IRQL >= DISPATCH_LEVEL.

Callers of MmSizeOfMdl run at any IRQL.

See Also

MmCreateMdl, MmInitializeMdl


MmUnlockPages

VOID
MmUnlockPages(
IN PMDL MemoryDescriptorList
);

MmUnlockPages unlocks physical pages described by a given MDL.

Parameters

MemoryDescriptorList
Points to an MDL.

Comments

The memory described by the specified MDL must have been locked previously by a call to MmProbeAndLockPages. As the pages are unlocked, the MDL is updated.

Callers of MmUnlockPages must be running at IRQL <= DISPATCH_LEVEL.

See Also

MmProbeAndLockPages


MmUnlockPagableImageSection

VOID
MmUnlockPagableImageSection(
IN PVOID ImageSectionHandle
);

MmUnlockPagableImageSection releases a section of driver code or driver data, previously locked into system space with MmLockPagableCodeSection, MmLockPagableDataSection or MmLockPagableSectionByHandle, so the section can be paged out again.

Parameters

ImageSectionHandle
Is the handle returned by a call to MmLockPagableCodeSection or MmLockPagableDataSection.

Comments

The handle for a driver’s pageable section must not be released if the driver has any outstanding IRPs in its device queue(s) or internal queue(s). A call to MmUnlockPagableImageSection restores the pageability of that entire section when there are no more references to the handle for that section.

The memory manager maintains the reference count on the handle to a section. A pageable section is only available to be paged out when the reference count is zero. Every lock request increments the count; every unlock request decrements the count. A driver must unlock a section as many times as it locks a section to make the section available to be paged out.

A handle is always valid, no matter what the count. If the count on a handle is zero and a call is made to MmLockPagableSectionByHandle, the count is set to one, and if the section has been paged out, it will be paged in.

In most cases, MmUnlockPagableImageSection is called before a driver’s Unload routine. That is, a driver with a pageable section is likely to have its DispatchClose and/or DispatchShutdown routine call MmUnlockPagableImageSection before its Unload routine is called. However, care should be taken in unloadable drivers to release any pageable section before the driver itself is unloaded from the system.

For more information on paging code and data, see The Programmer’s Guide.

Callers of MmUnlockPagableImageSection must be running at IRQL PASSIVE_LEVEL.

See Also

MmPageEntireDriver, MmResetDriverPaging, MmLockPagableCodeSection, MmLockPagableDataSection, MmLockPagableSectionByHandle


MmUnmapIoSpace

VOID
MmUnmapIoSpace(
IN PVOID BaseAddress,
 IN ULONG NumberOfBytes
);

MmUnmapIoSpace unmaps a specified range of physical addresses previously mapped by MmMapIoSpace.

Parameters

BaseAddress
Points to the base virtual address to which the physical pages were mapped.
NumberOfBytes
Specifies the number of bytes that were mapped.

Comments

Callers of MmUnmapIoSpace must be running at IRQL = PASSIVE_LEVEL.

See Also

MmMapIoSpace


MmUnmapLockedPages

VOID
MmUnmapLockedPages(
IN PVOID BaseAddress,
 IN PMDL MemoryDescriptorList
);

MmUnmapLockedPages releases a mapping set up by a preceding call to MmMapLockedPages.

Parameters

BaseAddress
Points to the base virtual address to which the physical pages were mapped.
MemoryDescriptorList
Points to an MDL.

Comments

Callers of MmUnmapLockedPages must be running at IRQL <= DISPATCH_LEVEL if the pages were mapped to system space. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

MmMapLockedPages


PAGE_ALIGN

PVOID
PAGE_ALIGN(
IN PVOID Va
);

PAGE_ALIGN returns a page-aligned virtual address for a given virtual address.

Parameters

Va
Points to the virtual address.

Return Value

PAGE_ALIGN returns a pointer to the page-aligned virtual address.

Comments

Callers of PAGE_ALIGN can be running at any IRQL.


ROUND_TO_PAGES

ULONG
ROUND_TO_PAGES(
IN ULONG Size
);

ROUND_TO_PAGES takes a size in bytes and rounds it up to the next full page.

Parameters

Size
Specifies the size in bytes to round up to a page multiple.

Return Value

ROUND_TO_PAGES returns the input size rounded up to a multiple of the page size for the current platform.

Comments

Callers of ROUND_TO_PAGES can be running at any IRQL.


Chapter 7 Object Manager Routines

References for the Ob Xxx routines are in alphabetical order. Click the browse button to view them.

For an overview of the functionality of these routines, see Chapter 1.


ObDereferenceObject

VOID
ObDereferenceObject(
IN PVOID Object
);

ObDereferenceObject decrements the given object’s reference count and performs retention checks.

Parameters

Object
Points to the object’s body.

Comments

When the reference count for an object reaches zero, a kernel-mode component can remove the object from the system.

A named object with the permanent attribute, such as a directory object created to hold a driver’s symbolic link objects, can be deleted by calling this routine and then calling the appropriate ZwOpen Xxx or ZwCreate Xxx to get a handle for the object, ZwMakeTemporaryObject with the handle, and, then, ZwClose with the handle.

Callers of ObDereferenceObject must be running at IRQL PASSIVE_LEVEL.

See Also

ZwClose, ZwMakeTemporaryObject


ObReferenceObjectByHandle

NTSTATUS
ObReferenceObjectByHandle(
IN HANDLE Handle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_TYPE ObjectType, /* optional */
IN KPROCESSOR_MODE AccessMode,
 OUT PVOID *Object,
 OUT POBJECT_HANDLE_INFORMATION HandleInformation /* optional */
);

ObReferenceObjectByHandle provides access validation on the object handle, and, if access can be granted, returns the corresponding pointer to the object’s body.

Parameters

Handle
Specifies an open handle for an object.
DesiredAccess
Specifies the requested types of access to the object. The interpretation of this field is dependent on the object type.
ObjectType
Points to the object type structure for the object’s type (optional).
AccessMode
Specifies the access mode to use for the access check. It must be either UserMode or KernelMode. Lower-level drivers should specify KernelMode.
Object
Points to a variable that receives a pointer to the object’s body.
HandleInformation
Points to a structure that receives the handle attributes and the granted access rights for the object.

Return Value

ObReferenceObjectByHandle can return one of the following status codes:

STATUS_SUCCESS
STATUS_OBJECT_TYPE_MISMATCH
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

A pointer to the object body is retrieved from the object table entry and returned to the caller by means of the Object parameter.

If the AccessMode parameter is KernelMode, the requested access is always allowed. If AccessMode is UserMode, the requested access is compared to the granted access for the object. Only highest-level drivers can safely specify UserMode for the input AccessMode.

If the call succeeds, a pointer to the object body is returned to the caller and the pointer reference count is incremented. Incrementing this count prevents the object from being deleted while the pointer is being referenced.

Callers of this function must be running at IRQL PASSIVE_LEVEL.

See Also

ObDereferenceObject


ObReferenceObjectByPointer

NTSTATUS
ObReferenceObjectByPointer(
IN PVOID Object,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_TYPE ObjectType,
 IN KPROCESSOR_MODE AccessMode
);

ObReferenceObjectByPointer increments the pointer reference count for a given object.

Parameters

Object
Points to the object’s body.
DesiredAccess
Specifies a mask representing the requested access to the object.
ObjectType
Points to the object-type structure for the object.
AccessMode
Indicates the access mode to use for the access check. It must be either UserMode or KernelMode. Lower-level drivers should specify KernelMode.

Return Value

ObReferenceObjectByPointer can return one of the following status codes:

STATUS_SUCCESS
STATUS_OBJECT_TYPE_MISMATCH

Comments

Calling this routine prevents the object from being deleted, possibly by another component’s call to ObDereferenceObject or ZwClose.

Callers of this function must be running at IRQL PASSIVE_LEVEL.

See Also

ObDereferenceObject, ZwClose


Chapter 8 Process Structure Routines

References for the Ps Xxx routines are in alphabetical order. Click the browse button to view them.

For an overview of the functionality of these routines, see Chapter 1.


PsCreateSystemThread

NTSTATUS
PsCreateSystemThread(
OUT PHANDLE ThreadHandle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes, /* optional */
IN HANDLE ProcessHandle, /* optional */
OUT PCLIENT_ID ClientId, /* optional */
IN PKSTART_ROUTINE StartRoutine,
 IN PVOID StartContext
);

PsCreateSystemThread creates a system thread that executes in kernel mode and returns a handle for the thread.

Parameters

ThreadHandle
Points to a variable that will receive the handle.
DesiredAccess
Specifies the requested types of access to the created thread. This value can be THREAD_ALL_ACCESS or (ACCESS_MASK) 0L for a driver-created thread.
ObjectAttributes
Points to a structure that specifies the object’s attributes. OBJ_PERMANENT, OBJ_EXCLUSIVE, OBJ_OPEN_IF, and OBJ_OPEN_LINK are not valid attributes for a thread object. This value should be NULL for a driver-created thread.
ProcessHandle
Specifies an open handle for the process in whose address space the thread is to be run. The caller’s thread must have PROCESS_CREATE_THREAD access to this process. If this parameter is not supplied, the thread will be created in the initial system process. This value should be NULL for a driver-created thread.
ClientId
Points to a structure that receives the client identifier of the new thread. This value should be NULL for a driver-created thread.
StartRoutine
Is the entry point for a driver thread.
StartContext
Supplies a single argument passed to the thread when it begins execution.

Return Value

PsCreateSystemThread returns STATUS_SUCCESS if the thread was created.

Comments

Drivers that create device-dedicated threads call this routine, either when they initialize or when I/O requests begin to come in to such a driver’s Dispatch routines. For example, a driver might create such a thread when it receives an asynchronous device control request.

PsCreateSystemThread creates a kernel-mode thread that begins a separate thread of execution within the system. Such a system thread has no TEB or user-mode context and runs only in kernel mode.

If the input ProcessHandle is NULL, the created thread is associated with the system process. Such a thread continues running until either the system is shut down or the thread terminates itself by calling PsTerminateSystemThread.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

KeSetBasePriorityThread, KeSetPriorityThread, PsTerminateSystemThread, ZwSetInformationThread


PsGetCurrentProcess

PEPROCESS
PsGetCurrentProcess(
);

PsGetCurrentProcess returns a pointer to the process of the current thread.

Comments

Highest-level drivers, file systems in particular, can call this routine. Lower-level drivers should call IoGetCurrentProcess instead.

PsGetCurrentProcess is the most efficient way to get a pointer to the current process, no matter what environment is being used.

Callers of PsGetCurrentProcess must be running at IRQL PASSIVE_LEVEL.

See Also

IoGetCurrentProcess, PsGetCurrentThread


PsGetCurrentThread

PETHREAD
PsGetCurrentThread(
);

PsGetCurrentThread identifies the current thread.

Return Value

PsGetCurrentThread returns a pointer to the thread object representing the currently executing thread.

Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.

See Also

PsCreateSystemThread, PsGetCurrentProcess


PsTerminateSystemThread

NTSTATUS
PsTerminateSystemThread(
IN NTSTATUS ExitStatus
);

PsTerminateSystemThread terminates a caller-created system thread.

Parameters

ExitStatus
Specifies the status of the terminating system thread to the thread creator.

Return Value

PsTerminateSystemThread returns the STATUS_XXX supplied by the caller-created thread, usually STATUS_SUCCESS.

Comments

Drivers that create a device-dedicated thread call this routine, either when the driver is unloaded or when there are no outstanding I/O requests for the driver to process. For such a driver, PsTerminateSystemThread must be called in the context of the driver’s thread; that is, the driver-created thread must terminate itself by making this call.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

PsCreateSystemThread


Chapter 9 Runtime Library Routines

References for these routines are in alphabetical order. Click the browse button to view them.

For an overview of the functionality of these routines, see Chapter 1.


InitializeListHead

VOID
InitializeListHead(
IN PLIST_ENTRY ListHead
);

InitializeListHead initializes any doubly linked, driver-managed interlocked queue or driver-maintained doubly linked list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the interlocked queue or list. For an interlocked queue, the storage must be resident and the driver also must provide storage for a spin lock.

Comments

InitializeListHead must be called from the DriverEntry routine if a driver sets up internal interlocked queues for IRPs or manages internal linked lists.

The ListHead of type LIST_ENTRY is doubly linked. Entries in an interlocked queue can be queued and dequeued by calling ExInterlockedInsert..List and ExInterlockedRemoveHeadList. Entries can be inserted into and removed from a driver-maintained list with Insert..List and Remove..List.

For an interlocked queue, a driver must provide resident storage: in the device extension of a driver-created device object, in the controller extension of a driver-created controller object, or in nonpaged pool allocated by the driver. The driver also must provide storage for a spin lock, which must be initialized with KeInitializeSpinLock before the driver’s initial call to ExInterlocked Xxx with the spin lock.

For a driver-maintained list, the driver must synchronize access to the list so that it is impossible for any two routines to be inserting and/or removing entries from the list simultaneously in SMP machines. Consequently, most drivers use the ExInterlocked Xxx routines to manage the necessary synchronization, rather than setting up a driver-managed list, which is likely to require spin lock protection anyway.

Callers of InitializeListHead must be running at IRQL PASSIVE_LEVEL.

See Also

ExInterlockedInsertHeadList, ExInterlockedInsertTailList, ExInterlockedRemoveHeadList, ExInterlockedPopEntryList, ExInterlockedPushEntryList, InsertHeadList, InsertTailList, IsListEmpty, KeInitializeSpinLock, PopEntryList, PushEntryList, RemoveEntryList, RemoveHeadList, RemoveTailList


InitializeObjectAttributes

VOID
InitializeObjectAttributes(
OUT POBJECT_ATTRIBUTES InitializedAttributes,
 IN PUNICODE_STRING ObjectName,
 IN ULONG Attributes,
 IN HANDLE RootDirectory,
 IN PSECURITY_DESCRIPTOR SecurityDescriptor
);

InitializeObjectAttributes sets up a parameter of type OBJECT_ATTRIBUTES for a subsequent call to a ZwCreate Xxx or ZwOpen Xxx routine.

Parameters

InitializedAttributes
Is a pointer to the initialized attributes for the object on return from InitializeObjectAttributes.
ObjectName
Is the full path name for the object.
Attributes
Is the set of attributes for the object, which can be a combination (ORed) of the following system-defined values:

OBJ_INHERIT
OBJ_PERMANENT
OBJ_EXCLUSIVE
OBJ_CASE_INSENSITIVE
OBJ_OPENIF

The validity of these values depends on the object type.

RootDirectory
Is a handle for the root directory in which the created object should be placed, in which an object to be opened is contained, or NULL.
SecurityDescriptor
Is an initialized absolute security descriptor for the object or NULL.

Comments

Callers of InitializeObjectAttributes must be running at IRQL PASSIVE_LEVEL.

See Also

ZwCreateDirectoryObject, ZwCreateFile, ZwCreateKey, ZwOpenKey, ZwOpenSection


InsertHeadList

VOID
InsertHeadList(
IN PLIST_ENTRY ListHead,
 IN PLIST_ENTRY Entry
);

InsertHeadList inserts an entry at the head of a doubly linked, driver-managed list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type LIST_ENTRY is doubly linked.

Entry
Points to an entry to be inserted in the list.

Comments

Callers of InsertHeadList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

ExInterlockedInsertHeadList, InitializeListHead, InsertTailList, IsListEmpty, RemoveHeadList, RemoveTailList


InsertTailList

VOID
InsertTailList(
IN PLIST_ENTRY ListHead,
 IN PLIST_ENTRY Entry
);

InsertTailList inserts an entry at the tail of a doubly linked, driver-managed list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type LIST_ENTRY is doubly linked.

Entry
Points to an entry to be inserted in the list.

Comments

Callers of InsertTailList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

ExInterlockedInsertTailList, InitializeListHead, InsertHeadList, IsListEmpty, RemoveHeadList, RemoveTailList


IsListEmpty

BOOLEAN
IsListEmpty(
IN PLIST_ENTRY ListHead
);

IsListEmpty indicates whether a doubly linked, driver-maintained list is empty.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type LIST_ENTRY is doubly linked.

Return Value

IsListEmpty returns TRUE if there are currently no entries in the list.

Comments

Callers of IsListEmpty can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

InitializeListHead, RemoveHeadList, RemoveTailList, RemoveEntryList


PopEntryList

PSINGLE_LIST_ENTRY
PopEntryList(
IN PSINGLE_LIST_ENTRY ListHead
);

PopEntryList removes an entry in a singly linked, driver-managed list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type LIST_ENTRY is singly linked. The ListHead must be initialized to NULL before entries can be pushed and popped.

Return Value

PopEntryList returns a pointer to the last-pushed entry (LIFO order) or a NULL pointer if the list is currently empty.

Comments

Callers of PopEntryList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

ExInterlockedPopEntryList, PushEntryList


PushEntryList

VOID
PushEntryList(
IN PSINGLE_LIST_ENTRY ListHead,
 IN PSINGLE_LIST_ENTRY Entry
);

PushEntryList pushes an entry into a singly linked, driver-maintained list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type SINGLE_LIST_ENTRY is singly linked. It must be initialized to NULL before the initial entry is pushed.

Entry
Points to the driver-allocated storage for an entry in the list.

Comments

Callers of PushEntryList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

ExInterlockedPushEntryList, PopEntryList


RemoveEntryList

VOID
RemoveEntryList(
IN PLIST_ENTRY Entry
);

RemoveEntryList resets the links for an entry from a doubly linked, driver-managed list.

Parameters

Entry
Points to the entry.

Comments

Callers of RemoveEntryList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for Entry is resident.

RemoveEntryList sets the forward and backward links for the entry to each other.

See Also

InitializeListHead, IsListEmpty, RemoveHeadList, RemoveTailList


RemoveHeadList

PLIST_ENTRY
RemoveHeadList(
IN PLIST_ENTRY ListHead
);

RemoveHeadList removes an entry from the head of a doubly linked list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type LIST_ENTRY is doubly linked.

Return Value

RemoveHeadList returns a pointer to the entry at the head of the list or a NULL pointer if the list is currently empty.

Comments

Callers of RemoveHeadList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

ExInterlockedRemoveHeadList, InitializeListHead, IsListEmpty, RemoveTailList, RemoveEntryList


RemoveTailList

PLIST_ENTRY
RemoveTailList(
IN PLIST_ENTRY ListHead
);

RemoveTailList removes an entry from the tail of a doubly linked list.

Parameters

ListHead
Points to the driver-allocated storage for the head of the list.

The ListHead of type LIST_ENTRY is doubly linked.

Comments

Callers of RemoveTailList can be running at IRQL >= DISPATCH_LEVEL only if the caller-allocated storage for ListHead is resident and only if pointers to every list entry remain valid at IRQL >= DISPATCH_LEVEL as well.

See Also

InitializeListHead, IsListEmpty, RemoveHeadList, RemoveEntryList


RtlAnsiStringToUnicodeSize

ULONG
RtlAnsiStringToUnicodeSize(
IN PANSI_STRING AnsiString
);

RtlAnsiStringToUnicodeSize returns the number of bytes required to hold an ANSI string converted into a Unicode string.

Parameters

AnsiString
Points to a buffer containing the ANSI string.

Return Value

RtlAnsiStringToUnicodeSize returns the necessary size in bytes for a Unicode string buffer.

Comments

Callers of RtlAnsiStringToUnicodeSize must be running at IRQL PASSIVE_LEVEL.

See Also

RtlAnsiStringToUnicodeString


RtlAnsiStringToUnicodeString

NTSTATUS
RtlAnsiStringToUnicodeString(
IN OUT PUNICODE_STRING DestinationString, /* optional */
IN PANSI_STRING SourceString,
 IN BOOLEAN AllocateDestinationString
);

RtlAnsiStringToUnicodeString converts the given ANSI source string into a Unicode string. The translation conforms to the current system locale information.

Parameters

DestinationString
Points to a caller-allocated buffer for a converted Unicode string or a NULL pointer if AllocateDestinationString is TRUE. This routine sets the length field of DestinationString only if AllocateDestinationString is TRUE.
SourceString
Points to the ANSI string to be converted to Unicode.
AllocateDestinationString
Is TRUE if this routine should allocate the buffer space for the destination string. If it does, the caller must deallocate the buffer by calling RtlFreeUnicodeString.

Return Value

If the conversion succeeds, RtlAnsiStringToUnicodeString returns STATUS_SUCCESS. Otherwise, no storage was allocated, nor was the conversion performed.

Comments

Only storage for the DestinationString buffer can be allocated by this routine. The caller must supply the buffer for the input SourceString.

Callers of RtlAnsiStringToUnicodeString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlAnsiStringToUnicodeSize, RtlFreeUnicodeString, RtlInitAnsiString, RtlUnicodeStringToAnsiString


RtlAppendUnicodeStringToString

NTSTATUS
RtlAppendUnicodeStringToString(
IN OUT PUNICODE_STRING Destination,
 IN PUNICODE_STRING Source
);

RtlAppendUnicodeStringToString concatenates two Unicode strings. It copies bytes from the source up to the length of the destination buffer.

Parameters

Destination
Points to a buffered Unicode string.
Source
Points to the buffered string to be concatenated.

Return Value

RtlAppendUnicodeStringToString can return one of the following values:
Value | Meaning
STATUS_SUCCESS | The source string was successfully appended to the destination counted string.
STATUS_BUFFER_TOO_SMALL | The destination string length is too small to allow the source string to be concatenated. Accordingly, the destination string length is not updated.

Comments

The Destination and Source buffers must be resident if the caller is running at IRQL >= DISPATCH_LEVEL.

See Also

RtlAppendUnicodeToString


RtlAppendUnicodeToString

NTSTATUS
RtlAppendUnicodeToString(
IN OUT PUNICODE_STRING Destination,
 IN PWSTR Source
);

RtlAppendUnicodeToString concatenates the supplied Unicode string to a buffered Unicode string. It copies bytes from the source string to the destination string up to the end of the destination buffer.

Parameters

Destination
Points to the buffered string.
Source
Points to the string to be appended to the Destination string.

Return Value

RtlAppendUnicodeToString can return one of the following status codes:
Value | Meaning
STATUS_SUCCESS | The source string was successfully appended to the destination counted string.
STATUS_BUFFER_TOO_SMALL | The destination string length was too small to allow the source string to be appended, so the destination string length is not updated.

Comments

The Destination buffer must be resident if the caller is running at IRQL >= DISPATCH_LEVEL.

See Also

RtlAppendUnicodeStringToString


RtlCharToInteger

NTSTATUS
RtlCharToInteger(
IN PCSZ String,
 IN ULONG Base, /* optional */
IN OUT PULONG Value
);

RtlCharToInteger converts a single-byte character to an integer value in the specified base.

Parameters

String
Points to a zero-terminated, single-byte character string.
Base
Specifies decimal, binary, octal, or hexadecimal base.
Value
Points to a location to which the converted value is returned.

Return Value

RtlCharToInteger returns STATUS_SUCCESS if the given character is converted. Otherwise, it can return STATUS_INVALID_PARAMETER.

Comments

RtlCharToInteger converts ANSI alphanumeric characters.

Callers of RtlCharToInteger must be running at IRQL PASSIVE_LEVEL.

See Also

RtlInitString, RtlIntegerToUnicodeString


RtlCheckRegistryKey

NTSTATUS
RtlCheckRegistryKey(
IN ULONG RelativeTo,
 IN PWSTR Path
);

RtlCheckRegistryKey checks for the existence of a given named key in the registry.

Parameters

RelativeTo
Specifies whether Path is an absolute registry path or is relative to a predefined key path as one of the following:

Value

Meaning

RTL_REGISTRY_ABSOLUTE

Path is an absolute registry path.

RTL_REGISTRY_SERVICES

Path is relative to \Registry\Machine\System\CurrentControlSet\Services.

RTL_REGISTRY_CONTROL

Path is relative to \Registry\Machine\System\CurrentControlSet\Control.

RTL_REGISTRY_WINDOWS_NT

Path is relative to \Registry\Machine\Software\Microsoft\
Windows NT\CurrentVersion.

RTL_REGISTRY_DEVICEMAP

Path is relative to \Registry\Machine\Hardware\DeviceMap.

RTL_REGISTRY_USER

Path is relative to \Registry\User\CurrentUser.

RTL_REGISTRY_OPTIONAL

Specifies that the key referenced by this parameter and the Path parameter are optional.

RTL_REGISTRY_HANDLE

Specifies that the Path parameter is actually a registry handle to use. This value is optional.

Path
Specifies the registry path according to the RelativeTo value. If RTL_REGISTRY_HANDLE is set, Path is a handle to be used directly.

Return Value

If the given, named key exists in the registry along the given relative path, RtlCheckRegistryKey returns STATUS_SUCCESS.

Comments

Callers of RtlCheckRegistryKey must be running at IRQL PASSIVE_LEVEL.

See Also

RtlQueryRegistryValues


RtlCompareMemory

ULONG
RtlCompareMemory(
IN PVOID Source1,
 IN PVOID Source2,
 IN ULONG Length
);

RtlCompareMemory compares blocks of memory and returns the number of bytes that are equivalent.

Parameters

Source1
Points to a block of memory to compare.
Source2
Points to a block of memory to compare.
Length
Specifies the number of bytes to be compared.

Return Value

RtlCompareMemory returns the number of bytes that compare as equal. If all bytes compare as equal, the input Length is returned.

Comments

Callers of RtlCompareMemory can be running at any IRQL if both blocks of memory are resident.


RtlCompareString

LONG
RtlCompareString(
PSTRING String1,
 PSTRING String2,
 BOOLEAN CaseInsensitive
);

RtlCompareString compares two counted strings.

Parameters

String1
Points to the first string.
String2
Points to the second string.
CaseInSensitive
If TRUE, case should be ignored when doing the comparison.

Return Value

RtlCompareString returns a signed value that gives the results of the comparison:
Zero | String1 equals String2.
< Zero | String1 is less than String2.
> Zero | String1 is greater than String2.

Comments

Callers of RtlCompareString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCompareUnicodeString, RtlEqualString


RtlCompareUnicodeString

LONG
RtlCompareUnicodeString(
IN PUNICODE_STRING String1,
 IN PUNICODE_STRING String2,
 IN BOOLEAN CaseInSensitive
);

RtlCompareUnicodeString compares two Unicode strings.

Parameters

String1
Points to the first string.
String2
Points to the second string.
CaseInSensitive
If TRUE, case should be ignored when doing the comparison.

Return Value

RtlCompareUnicodeString returns a signed value that gives the results of the comparison:
Zero | String1 equals String2.
< Zero | String1 is less than String2.
> Zero | String1 is greater than String2.

Comments

Callers of RtlCompareUnicodeString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCompareString, RtlEqualString


RtlConvertLongToLargeInteger

LARGE_INTEGER
RtlConvertLongToLargeInteger(
IN LONG SignedInteger
);

RtlConvertLongToLargeInteger converts the input signed integer to a signed large integer.

Parameters

SignedInteger
Specifies an integer of type LONG.

Return Value

RtlConvertLongToLargeInteger returns the large integer result.

Comments

Callers of RtlConvertLongToLargeInteger can be running at any IRQL.


RtlConvertUlongToLargeInteger

LARGE_INTEGER
RtlConvertUlongToLargeInteger(
IN ULONG UnsignedInteger
);

RtlConvertUlongToLargeInteger converts the input unsigned integer to a signed large integer.

Parameters

UnsignedInteger
Is a value of type ULONG.

Return Value

RtlConvertUlongToLargeInteger returns the converted large integer.

Comments

Callers of RtlConvertUlongToLargeInteger can be running at any IRQL.


RtlCopyBytes

VOID
RtlCopyBytes(
IN PVOID Destination,
 IN CONST VOID *Source,
 IN ULONG Length
);

RtlCopyBytes copies a given number of bytes from one location to another.

Parameters

Destination
Points to the destination where the bytes are to be copied.
Source
Points to the memory to be copied.
Length
Specifies the number of bytes to be copied.

Comments

The (Source + Length) can overlap the Destination range passed to RtlCopyBytes.

Callers of RtlCopyBytes can be running at any IRQL if both memory blocks are resident. Otherwise, callers must be running at IRQL<DISPATCH_LEVEL.

See Also

RtlFillMemory, RtlMoveMemory, RtlZeroMemory


RtlCopyMemory

VOID
RtlCopyMemory(
IN VOID UNALIGNED *Destination,
 IN CONST VOID UNALIGNED *Source,
 IN ULONG Length
);

RtlCopyMemory copies the contents of one buffer to another.

Parameters

Destination
Points to the destination of the move.
Source
Points to the memory to be copied.
Length
Specifies the number of bytes to be copied.

Comments

RtlCopyMemory runs faster than RtlMoveMemory. However, the (Source + Length) cannot overlap the Destination range passed in to RtlCopyMemory.

Callers of RtlCopyMemory can be running at any IRQL if both memory blocks are resident. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlMoveMemory


RtlCopyString

VOID
RtlCopyString(
IN OUT PSTRING DestinationString,
 IN PSTRING SourceString /* optional */
);

RtlCopyString copies a source string to a destination string.

Parameters

DestinationString
Points to the destination string buffer.
SourceString
Points to the source string buffer.

Comments

The DestinationString Length is set to zero if no source string is supplied, but this does not affect the length of the DestinationString buffer. The MaximumLength and Buffer members of the DestinationString are not modified by this routine.

The number of bytes copied from the SourceString is either the length of SourceString or the maximum length of DestinationString, whichever is smaller.

The DestinationString and SourceString buffers must be resident if the caller is running at IRQL >= DISPATCH_LEVEL.

See Also

RtlCopyUnicodeString


RtlCopyUnicodeString

VOID
RtlCopyUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
 IN PUNICODE_STRING SourceString
);

RtlCopyUnicodeString copies a source string to a destination string.

Parameters

DestinationString
Points to the destination string buffer.
SourceString
Points to the source string buffer.

Comments

If the source string is longer than the destination string, this routine copies bytes from the source to the end of the destination buffer, effectively truncating the copied source string.

The DestinationString and SourceString buffers must be resident if the caller is running at IRQL >= DISPATCH_LEVEL.

See Also

RtlCopyString


RtlCreateRegistryKey

NTSTATUS
RtlCreateRegistryKey(
IN ULONG RelativeTo,
 IN PWSTR Path
);

RtlCreateRegistryKey adds a key object in the registry along a given relative path.

Parameters

RelativeTo
Specifies whether Path is an absolute registry path or is relative to a predefined key path as one of the following:

Value

Meaning

RTL_REGISTRY_ABSOLUTE

Path is an absolute registry path.

RTL_REGISTRY_SERVICES

Path is relative to \Registry\Machine\System\CurrentControlSet\Services.

RTL_REGISTRY_CONTROL

Path is relative to \Registry\Machine\System\CurrentControlSet\Control.

RTL_REGISTRY_WINDOWS_NT

Path is relative to \Registry\Machine\Software\Microsoft\
Windows NT\CurrentVersion.

RTL_REGISTRY_DEVICEMAP

Path is relative to \Registry\Machine\Hardware\DeviceMap.

RTL_REGISTRY_USER

Path is relative to \Registry\User\CurrentUser.

RTL_REGISTRY_OPTIONAL

Specifies that the key referenced by this parameter and the Path parameter are optional.

RTL_REGISTRY_HANDLE

Specifies that the Path parameter is actually a registry handle to use. This value is optional.

Path
Specifies the registry path according to the RelativeTo value. If RTL_REGISTRY_HANDLE is set, Path is a handle to be used directly.

Return Value

RtlCreateRegistryKey returns STATUS_SUCCESS if the key is created.

Comments

Callers of RtlCreateRegistryKey must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCheckRegistryKey, RtlDeleteRegistryValue, RtlQueryRegistryValues, RtlWriteRegistryValue, ZwEnumerateKey, ZwOpenKey


RtlCreateSecurityDescriptor

NTSTATUS
RtlCreateSecurityDescriptor(
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
 IN ULONG Revision
);

RtlCreateSecurityDescriptor initializes a new absolute-format security descriptor. On return, the security descriptor is initialized with no system ACL, no discretionary ACL, no owner, no primary group, and all control flags set to zero.

Parameters

SecurityDescriptor
Points to the buffer for the security descriptor to be initialized.
Revision
Specifies the revision level to assign to the security descriptor.

Return Value

RtlCreateSecurityDescriptor can return one of the following status codes:
Value | Meaning
STATUS_SUCCESS | The call completed successfully.
STATUS_UNKNOWN_REVISION | The Revision level provided is not supported.

Comments

In effect, a successful call to this routine initializes a security descriptor without security constraints.

Callers of RtlCreateSecurityDescriptor must be running at IRQL PASSIVE_LEVEL.

See Also

RtlLengthSecurityDescriptor, RtlSetDaclSecurityDescriptor, RtlValidSecurityDescriptor


RtlDeleteRegistryValue

NTSTATUS
RtlDeleteRegistryValue(
IN ULONG RelativeTo,
 IN PWSTR Path,
 IN PWSTR ValueName
);

RtlDeleteRegistryValue removes the specified entry name and the associated values from the registry along the given relative path.

Parameters

RelativeTo
Specifies whether Path is an absolute registry path or is relative to a predefined key path as one of the following:

Value

Meaning

RTL_REGISTRY_ABSOLUTE

Path is an absolute registry path.

RTL_REGISTRY_SERVICES

Path is relative to \Registry\Machine\System\CurrentControlSet\Services.

RTL_REGISTRY_CONTROL

Path is relative to \Registry\Machine\System\CurrentControlSet\Control.

RTL_REGISTRY_WINDOWS_NT

Path is relative to \Registry\Machine\Software\Microsoft\
Windows NT\CurrentVersion.

RTL_REGISTRY_DEVICEMAP

Path is relative to \Registry\Machine\Hardware\DeviceMap.

RTL_REGISTRY_USER

Path is relative to \Registry\User\CurrentUser.

RTL_REGISTRY_OPTIONAL

Specifies that the key referenced by this parameter and the Path parameter are optional.

RTL_REGISTRY_HANDLE

Specifies that the Path parameter is actually a registry handle to use. This value is optional.

Path
Specifies the registry path according to the RelativeTo value. If RTL_REGISTRY_HANDLE is set, Path is a handle to be used directly.
ValueName
Points to the value name to be removed from the registry.

Return Value

RtlDeleteRegistryValue returns STATUS_SUCCESS if the value entry was deleted.

Callers of RtlDeleteRegistryValue must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCheckRegistryKey, RtlQueryRegistryValues, RtlWriteRegistryValue, ZwEnumerateKey, ZwOpenKey


RtlEnlargedIntegerMultiply

LARGE_INTEGER
RtlEnlargedIntegerMultiply(
IN LONG Multiplicand,
 IN LONG Multiplier
);

RtlEnlargedIntegerMultiply is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlEnlargedUnsignedDivide

ULONG
RtlEnlargedUnsignedDivide(
IN ULARGE_INTEGER Dividend,
 IN ULONG Divisor,
 IN OUT PULONG Remainder
);

RtlEnlargedUnsignedDivide is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlEnlargedUnsignedMultiply

LARGE_INTEGER
RtlEnlargedUnsignedMultiply(
IN ULONG Multiplicand,
 IN ULONG Multiplier
);

RtlEnlargedUnsignedMultiply is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlEqualString

BOOLEAN
RtlEqualString(
IN PSTRING String1,
 IN PSTRING String2,
 IN BOOLEAN CaseInSensitive
);

RtlEqualString compares two alphabetic strings to determine whether they are equal.

Parameters

String1
Points to the first string.
String2
Points to the second string.
CaseInSensitive
If TRUE, case should be ignored when doing the comparison.

Return Value

RtlEqualString returns TRUE if the two strings are equal.

Comments

RtlEqualString compares strings containing only ANSI alphabetic characters.

Callers of RtlEqualString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlEqualUnicodeString


RtlEqualUnicodeString

BOOLEAN
RtlEqualUnicodeString(
IN PUNICODE_STRING String1,
 IN PUNICODE_STRING String2,
 IN BOOLEAN CaseInSensitive
);

RtlEqualUnicodeString compares two Unicode strings to determine whether they are equal.

Parameters

String1
Points to the first Unicode string.
String2
Points to the second Unicode string.
CaseInSensitive
If TRUE, case should be ignored when doing the comparison.

Return Value

RtlEqualUnicodeString returns TRUE if the two Unicode strings are equal.

Comments

Callers of RtlEqualUnicodeString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlEqualString


RtlExtendedIntegerMultiply

LARGE_INTEGER
RtlExtendedIntegerMultiply(
IN LARGE_INTEGER Multiplicand,
 IN LONG Multiplier
);

RtlExtendedIntegerMultiply is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlExtendedLargeIntegerDivide

LARGE_INTEGER
RtlExtendedLargeIntegerDivide(
IN LARGE_INTEGER Dividend,
 IN ULONG Divisor,
 IN OUT PULONG Remainder
);

RtlExtendedLargeIntegerDivide is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlExtendedMagicDivide

LARGE_INTEGER
RtlExtendedMagicDivide(
IN LARGE_INTEGER Dividend,
 IN LARGE_INTEGER MagicDivisor,
 IN CCHAR ShiftCount
);

RtlExtendedMagicDivide is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlFillMemory

VOID
RtlFillMemory(
IN PVOID Destination,
 IN ULONG Length,
 IN UCHAR Fill
);

RtlFillMemory fills a caller-supplied buffer with the given character.

Parameters

Destination
Points to the memory to be filled.
Length
Specifies the number of bytes to be filled.
Fill
Specifies the value to fill the memory.

Comments

Callers of RtlFillMemory can be running at any IRQL, provided that the Destination buffer is resident.

See Also

RtlZeroMemory


RtlFreeAnsiString

VOID
RtlFreeAnsiString(
IN PANSI_STRING AnsiString
);

RtlFreeAnsiString releases storage that was allocated by RtlUnicodeStringToAnsiString.

Parameters

AnsiString
Points to the ANSI string buffer previously allocated by RtlUnicodeStringToAnsiString.

Comments

This routine does not release the Unicode string buffer passed to RtlUnicodeStringToAnsiString.

Callers of RtlFreeAnsiString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlUnicodeStringToAnsiString


RtlFreeUnicodeString

VOID
RtlFreeUnicodeString(
IN PUNICODE_STRING UnicodeString
);

RtlFreeUnicodeString releases storage that was allocated by RtlAnsiStringToUnicodeString or RtlUpcaseUnicodeString.

Parameters

UnicodeString
Points to the Unicode string buffer previously allocated by RtlAnsiStringToUnicodeString or RtlUpcaseUnicodeString.

Comments

This routine does not release the ANSI string buffer passed to RtlAnsiStringToUnicodeString.

Callers of RtlFreeUnicodeString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlAnsiStringToUnicodeString, RtlUpcaseUnicodeString


RtlInitAnsiString

VOID
RtlInitAnsiString(
IN OUT PANSI_STRING DestinationString,
 IN PCSZ SourceString
);

RtlInitAnsiString initializes a counted ANSI string.

Parameters

DestinationString
Points to the buffer for a counted ANSI string to be initialized.
SourceString
Points to a zero-terminated string with which to initialize the counted string.

Comments

The DestinationString is initialized to point to the SourceString. The length and maximum length for the DestinationString are initialized to the length of the SourceString. If SourceString is NULL, the lengths are zero.

Callers of RtlInitAnsiString can be running at IRQL <= DISPATCH_LEVEL if the DestinationString buffer is nonpageable. Usually, callers run at IRQL PASSIVE_LEVEL because most other Rtl..String routines cannot be called at raised IRQL.

See Also

RtlInitString, RtlInitUnicodeString


RtlInitString

VOID
RtlInitString(
IN OUT PSTRING DestinationString,
 IN PCSZ SourceString
);

RtlInitString initializes a counted string.

Parameters

DestinationString
Points to the buffer for a counted string to be initialized.
SourceString
Points to a zero-terminated string value with which to initialize the counted string.

Comments

DestinationString is initialized to point to SourceString and the length and maximum length for the DestinationString are initialized to the length of SourceString. The lengths are is zero if SourceString is NULL.

Callers of RtlInitString can be running at IRQL <= DISPATCH_LEVEL if the DestinationString buffer is nonpageable. Usually, callers run at IRQL PASSIVE_LEVEL because most other Rtl..String routines cannot be called at raised IRQL.

See Also

RtlInitAnsiString, RtlInitUnicodeString


RtlInitUnicodeString

VOID
RtlInitUnicodeString(
IN OUT PUNICODE_STRING DestinationString,
 IN PCWSTR SourceString
);

RtlInitUnicodeString initializes a counted Unicode string.

Parameters

DestinationString
Points to the buffer for a counted Unicode string to be initialized.
SourceString
Points to a zero-terminated Unicode string with which to initialize the counted string.

Comments

DestinationString is initialized to point to SourceString. The length and maximum length for DestinationString are initialized to the length of SourceString. If SourceString is NULL, the length is zero.

Callers of RtlInitUnicodeString can be running at IRQL <= DISPATCH_LEVEL if the DestinationString buffer is nonpageable. Usually, callers run at IRQL PASSIVE_LEVEL because most other Rtl..String routines cannot be called at raised IRQL.

See Also

RtlInitAnsiString, RtlInitString


RtlIntegerToUnicodeString

NTSTATUS
RtlIntegerToUnicodeString(
IN ULONG Value,
 IN ULONG Base, /* optional */
IN OUT PUNICODE_STRING String
);

RtlIntegerToUnicodeString converts a given unsigned integer value in the specified base to one or more buffered Unicode characters.

Parameters

Value
Identifies an unsigned integer of type ULONG.
Base
Specifies decimal, binary, octal, or hexadecimal base.
String
Points to a buffer to contain the converted value.

Return Value

If RtlIntegerToUnicodeString succeeds, it returns STATUS_SUCCESS. Otherwise, it can return STATUS_INVALID_PARAMETER if the given Value is illegal for the specified base.

Comments

Callers of RtlIntegerToUnicodeString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlUnicodeStringToInteger, RtlAppendUnicodeStringToString


RtlLargeIntegerAdd

LARGE_INTEGER
RtlLargeIntegerAdd(
IN LARGE_INTEGER Addend1,
 IN LARGE_INTEGER Addend2
);

RtlLargeIntegerAdd is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerAnd

VOID
RtlLargeIntegerAnd(
IN OUT LARGE_INTEGER Result,
 IN LARGE_INTEGER Source,
 IN LARGE_INTEGER Mask
);

RtlLargeIntegerAnd is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerArithmeticShift

LARGE_INTEGER
RtlLargeIntegerArithmeticShift(
IN LARGE_INTEGER LargeInteger,
 IN CCHAR ShiftCount
);

RtlLargeIntegerArithmeticShift is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerDivide

LARGE_INTEGER
RtlLargeIntegerDivide(
IN LARGE_INTEGER Dividend,
 IN LARGE_INTEGER Divisor,
 IN OUT PLARGE_INTEGER Remainder
);

RtlLargeIntegerDivide is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerEqualTo

BOOLEAN
RtlLargeIntegerEqualTo(
IN LARGE_INTEGER Operand1,
 IN LARGE_INTEGER Operand2
);

RtlLargeIntegerEqualTo is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerEqualToZero

BOOLEAN
RtlLargeIntegerEqualToZero(
IN LARGE_INTEGER Operand
);

RtlLargeIntegerEqualToZero is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerGreaterThan

BOOLEAN
RtlLargeIntegerGreaterThan(
IN LARGE_INTEGER Operand1,
 IN LARGE_INTEGER Operand2
);

RtlLargeIntegerGreaterThan is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerGreaterThanOrEqualTo

BOOLEAN
RtlLargeIntegerGreaterThanOrEqualTo(
IN LARGE_INTEGER Operand1,
 IN LARGE_INTEGER Operand2
);

RtlLargeIntegerGreaterThanOrEqualTo is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerGreaterOrEqualToZero

BOOLEAN
RtlLargeIntegerGreaterOrEqualToZero(
IN LARGE_INTEGER Operand
);

RtlLargeIntegerGreaterOrEqualToZero is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerGreaterThanZero

BOOLEAN
RtlLargeIntegerGreaterThanZero(
IN LARGE_INTEGER Operand
);

RtlLargeIntegerGreaterThanZero is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerLessThan

BOOLEAN
RtlLargeIntegerLessThan(
IN LARGE_INTEGER Operand1,
 IN LARGE_INTEGER Operand2
);

RtlLargeIntegerLessThan is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerLessThanOrEqualTo

BOOLEAN
RtlLargeIntegerLessThanOrEqualTo(
IN LARGE_INTEGER Operand1,
 IN LARGE_INTEGER Operand2
);

RtlLargeIntegerLessThanOrEqualTo is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerLessOrEqualToZero

BOOLEAN
RtlLargeIntegerLessOrEqualToZero(
IN LARGE_INTEGER Operand
);

RtlLargeIntegerLessOrEqualToZero is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerLessThanZero

BOOLEAN
RtlLargeIntegerLessThanZero(
IN LARGE_INTEGER Operand
);

RtlLargeIntegerLessThanZero is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerNegate

LARGE_INTEGER
RtlLargeIntegerNegate(
IN LARGE_INTEGER Subtrahend
);

RtlLargeIntegerNegate is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerNotEqualTo

BOOLEAN
RtlLargeIntegerNotEqualTo(
IN LARGE_INTEGER Operand1,
 IN LARGE_INTEGER Operand2
);

RtlLargeIntegerNotEqualTo is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerNotEqualToZero

BOOLEAN
RtlLargeIntegerNotEqualToZero(
IN LARGE_INTEGER Operand
);

RtlLargeIntegerNotEqualToZero is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerShiftLeft

LARGE_INTEGER
RtlLargeIntegerShiftLeft(
IN LARGE_INTEGER LargeInteger,
 IN CCHAR ShiftCount
);

RtlLargeIntegerShiftLeft is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerShiftRight

LARGE_INTEGER
RtlLargeIntegerShiftRight(
IN LARGE_INTEGER LargeInteger,
 IN CCHAR ShiftCount
);

RtlLargeIntegerShiftRight is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLargeIntegerSubtract

LARGE_INTEGER
RtlLargeIntegerSubtract(
IN LARGE_INTEGER Minuend,
 IN LARGE_INTEGER Subtrahend
);

RtlLargeIntegerSubtract is exported to support existing driver binaries and is obsolete. For better performance, use the compiler support for 64-bit integer operations.


RtlLengthSecurityDescriptor

ULONG
RtlLengthSecurityDescriptor(
IN PSECURITY_DESCRIPTOR SecurityDescriptor
);

RtlLLengthSecurityDescriptor returns the size of a given security descriptor.

Parameters

SecurityDescriptor
Points to a security descriptor.

Return Value

RtlLengthSecurityDescriptor returns the size in bytes of the descriptor.

Comments

Callers of RtlLengthSecurityDescriptor must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCreateSecurityDescriptor, RtlSetDaclSecurityDescriptor, RtlValidSecurityDescriptor


RtlMoveMemory

VOID
RtlMoveMemory(
IN PVOID Destination,
 IN CONST VOID *Source,
 IN ULONG Length
);

RtlMoveMemory moves memory either forward or backward, aligned or unaligned, in 4-byte blocks, followed by any remaining bytes.

Parameters

Destination
Points to the destination of the move.
Source
Points to the memory to be copied.
Length
Specifies the number of bytes to be copied.

Comments

The (Source + Length) can overlap the Destination range passed in to RtlMoveMemory.

Callers of RtlMoveMemory can be running at any IRQL if both memory blocks are resident. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlCopyMemory


RtlQueryRegistryValues

NTSTATUS
RtlQueryRegistryValues(
IN ULONG RelativeTo,
 IN PWSTR Path,
 IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
 IN PVOID Context,
 IN PVOID Environment /* optional */
);

RtlQueryRegistryValues allows the caller to query several values from the registry subtree with a single call.

Parameters

RelativeTo
Specifies whether Path is an absolute registry path or is relative to a predefined path as one of the following:

Value

Meaning

RTL_REGISTRY_ABSOLUTE

Path is an absolute registry path.

RTL_REGISTRY_SERVICES

Path is relative to \Registry\Machine\System\CurrentControlSet\Services.

RTL_REGISTRY_CONTROL

Path is relative to \Registry\Machine\System\CurrentControlSet\Control.

RTL_REGISTRY_WINDOWS_NT

Path is relative to \Registry\Machine\Software\Microsoft\
Windows NT\CurrentVersion.

RTL_REGISTRY_DEVICEMAP

Path is relative to \Registry\Machine\Hardware\DeviceMap.

RTL_REGISTRY_USER

Path is relative to \Registry\User\CurrentUser.

RTL_REGISTRY_OPTIONAL

Specifies that the key referenced by this parameter and the Path parameter are optional.

RTL_REGISTRY_HANDLE

Specifies that the Path parameter is actually a registry handle to use. This value is optional.

Path
Points to either an absolute registry path or a path relative to the known location specified by the RelativeTo parameter. Note that the names of keys in such a path must be known to the caller, including the last key in the path. If the RTL_REGISTRY_HANDLE flag is specified, this parameter is a registry handle for an already opened key to be queried directly.
QueryTable
Points to a table of one or more value names and subkey names in which the caller is interested. Each table entry contains a caller-supplied QueryRoutine that will be called for each value name that exists in the registry. The table must be terminated with a NULL table entry, which is a table entry with a NULL QueryRoutine and a NULL Name field. The QueryTable structure is defined as follows:
typedef struct _RTL_QUERY_REGISTRY_TABLE {
    PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
    ULONG Flags;
    PWSTR Name;
    PVOID EntryContext;
    ULONG DefaultType;
    PVOID DefaultData;
    ULONG DefaultLength;
} RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;

This routine is called with the name, type, data, and data length of a registry value. If this field is NULL, it marks the end of the table.

A caller-supplied QueryRoutine is declared as follows:

NTSTATUS
(*PRTL_QUERY_REGISTRY_ROUTINE)(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    );

The remaining members of the QueryTable structure include the following.

Flags
Control how the remaining fields are to be interpreted, as follows:

Value

Meaning

RTL_QUERY_REGISTRY_SUBKEY

The Name of this table entry is another path to a registry key, and all following table entries are for that key rather than the key specified by the Path parameter. This change in focus lasts until the end of the table or until another RTL_REGISTRY_SUBKEY or RTL_QUERY_REGISTRY_TOPKEY entry is seen. Each such entry must specify a path that is relative to the Path specified in the call to RtlQueryRegistryValues.

RTL_QUERY_REGISTRY_TOPKEY

Resets the current registry key handle to the original one specified by the RelativeTo and Path parameters. This is useful for getting back to the original node after descending into subkeys with the RTL_QUERY_REGISTRY SUBKEY flag.

RTL_QUERY_REGISTRY_REQUIRED

Specifies that this value is required and, if it is not found, STATUS_OBJECT_NAME_NOT_FOUND is returned. This is used for a table entry that specifies a NULL Name so that RtlQueryRegistryValues will enumerate all of the value names under a key and return STATUS_OBJECT_NAME_NOT_FOUND only if there are no value keys under the current key.

RTL_QUERY_REGISTRY_NOVALUE

Specifies that even though there is no Name for this table entry, all the caller wants is a callback: that is, the caller does not want to enumerate all the values under the current key. The QueryRoutine is called with NULL for ValueData, REG_NONE for ValueType, and zero for ValueLength.

RTL_QUERY_REGISTRY_NOEXPAND

Specifies that, if the type of this registry value is REG_EXPAND_SZ or REG_MULTI_SZ, RtlQueryRegistryValues is not to do any preprocessing of these registry values before calling the QueryRoutine. By default, RtlQueryRegistryValues expands environment variable references into REG_EXPAND_SZ values, enumerates each zero-terminated string in a REG_MULTI_SZ value, and calls the QueryRoutine once with each, making it look like there is more than one REG_SZ value with the same ValueName.

RTL_QUERY_REGISTRY_DIRECT

The QueryRoutine is ignored, and the EntryContext points to the location to store the value. For zero-terminated strings, EntryContext points to a UNICODE_STRING structure that describes the maximum size of the buffer. If the Buffer is NULL, a buffer is allocated.

RTL_QUERY_REGISTRY_DELETE

This is used to delete value keys after they have been queried.

Name
This is the name of a Value that the caller queried. If Name is NULL, the QueryRoutine specified for this table entry is called for all values associated with the current registry key.
EntryContext
This is an arbitrary 32-bit field that is passed uninterpreted each time the QueryRoutine is called.
DefaultType
Specifies the REG_xxx type of the data to be queried if Flags is not set with RTL_REGISTRY_REQUIRED; otherwise, zero.
DefaultData
Points to default value(s) for a named value entry of the DefaultType if it is not already in the registry under Name and if Flags is not set with RTL_REGISTRY_REQUIRED; otherwise, NULL.
DefaultLength
If there is no value name that matches the name given by the Name, and the DefaultType field is not REG_NONE, the QueryRoutine for this table entry is called with the contents of the following fields as if the value had been found in the registry. If the DefaultType is REG_SZ, REG_EXPANDSZ, or REG_MULTI_SZ and the DefaultLength is zero, the value of DefaultLength will be computed based on the length of the Unicode string pointed to by DefaultData.
Context
Points to a context that is passed, uninterpreted, when each QueryRoutine is called.
Environment
Points to the environment used when expanding variable values in REG_EXPAND_SZ registry values, or a NULL pointer (optional).

Return Value

RtlQueryRegistryValues returns STATUS_SUCCESS if the entire QueryTable was processed successfully. If an error occurs, RtlQueryRegistryValues returns an error status such as:

STATUS_INVALID_PARAMETER
STATUS_OBJECT_NAME_NOT_FOUND
STATUS_XXX (an error status from a user-supplied QueryRoutine)

Comments

The caller specifies an initial key path and a table. The table contains one or more entries that describe the key values and subkey names in which the caller is interested. RtlQueryRegistryValues starts at the initial key and enumerates the entries in the table.

For each entry specifying a value name or subkey name that exists in the registry, RtlQueryRegistryValues calls the QueryRoutine associated with each table entry. Each entry’s caller-supplied QueryRoutine is passed the value name, type, data, and data length.

When building the QueryTable, be sure to allocate an entry for each value being queried, plus a NULL entry at the end. Zero the table and then initialize the entries.

If an error occurs at any stage of processing the QueryTable, RtlQueryRegistryValues stops processing the table and returns the error status.

Callers of RtlQueryRegistryValues must be running at IRQL PASSIVE_LEVEL.

See Also

RtlZeroMemory, ZwEnumerateKey, ZwOpenKey


RtlRetrieveUlong

VOID
RtlRetrieveUlong(
IN PULONG DestinationAddress,
 IN PULONG SourceAddress
);

RtlRetrieveUlong retrieves a ULONG value from the source address, avoiding alignment faults. The destination address is assumed to be aligned.

Parameters

DestinationAddress
Points to a ULONG-aligned location in which to store the ULONG value.
SourceAddress
Points to a location from which to retrieve the ULONG value.

Comments

Callers of RtlRetrieveUlong can be running at any IRQL if the given addresses are in nonpaged pool. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlStoreUlong


RtlRetrieveUshort

VOID
RtlRetrieveUshort(
IN PUSHORT DestinationAddress,
 IN PUSHORT SourceAddress
);

RtlRetrieveUshort retrieves a USHORT value from the source address, avoiding alignment faults.

Parameters

DestinationAddress
Points to a USHORT-aligned location in which to store the value.
SourceAddress
Points to a location from which to retrieve the value.

Comments

Callers of RtlRetrieveUshort can be running at any IRQL if the given addresses are in nonpaged pool. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlStoreUshort


RtlSetDaclSecurityDescriptor

NTSTATUS
RtlSetDaclSecurityDescriptor(
IN OUT PSECURITY_DESCRIPTOR SecurityDescriptor,
 IN BOOLEAN DaclPresent,
 IN PACL Dacl, /* optional */
IN BOOLEAN DaclDefaulted /* optional */
);

RtlSetDaclSecurityDescriptor sets the DACL information of an absolute-format security descriptor. If there is already a DACL present in the security descriptor, it is superseded.

Parameters

SecurityDescriptor
Points to the security descriptor to which the DACL is to be applied.
DaclPresent
If FALSE, indicates that the DaclPresent flag in the security descriptor should be set to FALSE. In this case, the remaining optional parameters are ignored. Otherwise, the DaclPresent control flag in the security descriptor is set to TRUE and the remaining optional parameters are not ignored.
Dacl
Points to the DACL for the security descriptor. If this parameter is NULL, a NULL ACL is assigned to the security descriptor. A NULL DACL unconditionally grants access. The DACL is referenced by, but not copied into, the security descriptor.
DaclDefaulted
When set, indicates that the DACL was picked up from some default mechanism rather than explicitly specified by the caller. This value is set in the DaclDefaulted control flag in the security descriptor. If this parameter is NULL, the DaclDefaulted flag will be cleared.

Return Value

RtlSetDaclSecurityDescriptor can return one of the following status codes:
Value | Meaning
STATUS_SUCCESS | Indicates the call completed successfully.
STATUS_UNKNOWN_REVISION | Indicates the revision of the security descriptor is unknown.
STATUS_INVALID_SECURITY_DESCR | Indicates the security descriptor is not an absolute format security descriptor.

Comments

Callers of RtlSetDaclSecurityDescriptor must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCreateSecurityDescriptor, RtlLengthSecurityDescriptor, RtlValidSecurityDescriptor


RtlStoreUlong

VOID
RtlStoreUlong(
IN PULONG Address,
 IN ULONG Value
);

RtlStoreUlong stores a ULONG value at a particular address, avoiding alignment faults.

Parameters

Address
Points to a location in which to store a given ULONG value.
Value
Specifies a ULONG value to be stored.

Comments

The caller can be running at any IRQL if Address points to nonpaged pool. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlRetrieveUlong


RtlStoreUshort

VOID
RtlStoreUshort(
IN PUSHORT Address,
 IN USHORT Value
);

RtlStoreUshort stores a USHORT value at a particular address, avoiding alignment faults.

Parameters

Address
Points to a location in which to store a USHORT value.
Value
Specifies a USHORT value to be stored.

Comments

The caller can be running at any IRQL if Address points to nonpaged pool. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlRetrieveUshort


RtlTimeFieldsToTime

BOOLEAN
RtlTimeFieldsToTime(
IN PTIME_FIELDS TimeFields,
 IN PLARGE_INTEGER Time
);

RtlTimeFieldsToTime converts TIME_FIELDS information to a system time value.

Parameters

TimeFields
Points to the following structure, containing the time information to be converted:
typedef struct TIME_FIELDS {
    CSHORT Year;
    CSHORT Month;
    CSHORT Day;
    CSHORT Hour;
    CSHORT Minute;
    CSHORT Second;
    CSHORT Milliseconds;
    CSHORT Weekday;
} TIME_FIELDS;

Members

Year
Is in the range from 1601 on.
Month
Is in the range from 1 to 12.
Day
Is in the range from 1 to 31.
Hour
Is in the range from 0 to 23.
Minute
Is in the range from 0 to 59.
Second
Is in the range from 0 to 59.
Milliseconds
Is in the range from 0 to 999.
Weekday
Is in the range from 0 to 6 (Sunday to Saturday).
Time
Points to a buffer, which is to contain the converted system time value as a large integer.

Return Value

RtlTimeFieldsToTime returns TRUE if the input TimeFields data was successfully converted.

Comments

RtlTimeFieldsToTime ignores the Weekday value in TimeFields.

Callers of RtlTimeFieldsToTime can be running at any IRQL if both input buffers are resident.

See Also

ExLocalTimeToSystemTime, ExSystemTimeToLocalTime, KeQuerySystemTime, RtlTimeToTimeFields


RtlTimeToTimeFields

VOID
RtlTimeToTimeFields(
IN PLARGE_INTEGER Time,
 IN PTIME_FIELDS TimeFields
);

RtlTimeToTimeFields converts system time into a TIME_FIELDS structure.

Parameters

Time
Points to a buffer containing the absolute system time as a large integer, accurate to 100-nanosecond resolution.
TimeFields
Points to a caller-allocated buffer, which must be at least sizeof(TIME_FIELDS), to contain the returned information.

Comments

Callers of RtlTimeToTimeFields can be running at any IRQL if both input buffers are resident.

See Also

ExLocalTimeToSystemTime, ExSystemTimeToLocalTime, KeQuerySystemTime, RtlTimeFieldsToTime


RtlUnicodeStringToAnsiString

NTSTATUS
RtlUnicodeStringToAnsiString(
IN OUT PANSI_STRING DestinationString, /* optional */
IN PUNICODE_STRING SourceString,
 IN BOOLEAN AllocateDestinationString
);

RtlUnicodeStringToAnsiString converts a given Unicode string into an ANSI string. The translation is done in accord with the current system-locale information.

Parameters

DestinationString
Points to a caller-allocated buffer for the ANSI string or is NULL if AllocateDestinationString is set to TRUE. If the translation cannot be done because a character in the Unicode string does not map to an ANSI character in the current system locale, an error is returned.
SourceString
Points to the Unicode source string to be converted to ANSI.
AllocateDestinationString
TRUE if this routine is to allocate the buffer space for the DestinationString. If it does, the buffer must be deallocated by calling RtlFreeAnsiString.

Return Value

If the conversion succeeds, RtlUnicodeStringToAnsiString returns STATUS_SUCCESS. Otherwise, no storage was allocated, and no conversion was done.

Comments

Callers of RtUnicodeStringToAnsiString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlAnsiStringToUnicodeString, RtlFreeAnsiString


RtlUnicodeStringToInteger

NTSTATUS
RtlUnicodeStringToInteger(
IN PUNICODE_STRING String,
IN ULONG Base, /* optional */
OUT PULONG Value
 );

RtlUnicodeStringToInteger converts a Unicode string representation of an integer into its integer equivalent.

Parameters

String
Points to the Unicode string to be converted to its integer equivalent.
Base
An optional argument that indicates the base of the number expressed as a Unicode string.
Value
Points to caller supplied storage of type ULONG. RtlUnicodeStringToInteger returns the integer conversion results in Value.

Return Value

If the conversion is successful, RtlUnicodeStringToInteger returns STATUS_SUCCESS and Value is set to the integer equivalent of the Unicode string. Otherwise, the Value is set to 0, and RtlUnicodeStringToInteger returns STATUS_INVALID_PARAMETER.

Comments

If the first character of the string is a “-”, the sign of the output Value is negative, otherwise if the first character is a “+” or there is no sign character, the sign of Value is positive.

If no Base is supplied, RtlUnicodeStringToInteger checks for a leading character to indicate the base of the number. An “x” indicates the string is to be converted as a hexidecimal integer; an “o” indicates the string is to be converted as an octal integer; a “b” indicates the string is to be converted as a binary integer. Otherwise RtlUnicodeStringToInteger assumes the number is to be converted as a base 10 integer.

Callers of RtlUnicodeStringToInteger must be running at IRQL PASSIVE_LEVEL.

See Also

RtlIntegerToUnicodeString .


RtlUpcaseUnicodeString

NTSTATUS
RtlUpcaseUnicodeString(
IN OUT PUNICODE_STRING DestinationString, /* optional */
IN PUNICODE_STRING SourceString,
 IN BOOLEAN AllocateDestinationString
);

RtlUpcaseUnicodeString converts a copy of the source string to upper case and writes the converted string in the destination buffer.

Parameters

DestinationString
Points to a caller-allocated buffer for the converted Unicode string or is NULL if AllocateDestinationString is set to TRUE.
SourceString
Points to the source Unicode string to be converted to upper case.
AllocateDestinationString
TRUE if RtlUpcaseUnicodeString is to allocate the buffer space for the DestinationString. If it does, the buffer must be deallocated by calling RtlFreeUnicodeString.

Return Value

If the operation succeeds, RtlUpcaseUnicodeString returns STATUS_SUCCESS. Otherwise, no storage was allocated, and no conversion was done.

Comments

Callers of RtlUpcaseUnicodeString must be running at IRQL PASSIVE_LEVEL.

See Also

RtlFreeUnicodeString


RtlUpperString

VOID
RtlUpperString(
IN OUT PSTRING DestinationString,
 IN PSTRING SourceString
);

RtlUpperString copies the given SourceString to the DestinationString buffer, converting it to uppercase.

Parameters

DestinationString
Points to the buffer for the converted destination string.
SourceString
Points to the source string to be converted to uppercase.

Comments

The MaximumLength and Buffer fields of DestinationString are not modified by this routine.

The number of bytes copied from SourceString is either the Length of SourceString or the MaximumLength of DestinationString, whichever is smaller.

Callers of RtlUpperString must be running at IRQL PASSIVE_LEVEL.


RtlValidSecurityDescriptor

BOOLEAN
RtlValidSecurityDescriptor(
IN PSECURITY_DESCRIPTOR SecurityDescriptor
);

RtlValidSecurityDescriptor checks a given security descriptor’s validity.

Parameters

SecurityDescriptor
Points to the security descriptor to be checked.

Return Value

RtlValidSecurityDescriptor returns TRUE if the given descriptor is valid.

Comments

Callers of RtlValidSecurityDescriptor must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCreateSecurityDescriptor, RtlLengthSecurityDescriptor, RtlSetDaclSecurityDescriptor


RtlWriteRegistryValue

NTSTATUS
RtlWriteRegistryValue(
IN ULONG RelativeTo,
 IN PWSTR Path,
 IN PWSTR ValueName,
 IN ULONG ValueType,
 IN PVOID ValueData,
 IN ULONG ValueLength
);

RtlWriteRegistryValue writes caller-supplied data into the registry along the specified relative path at the given value name.

Parameters

RelativeTo
Specifies whether Path is an absolute registry path or is relative to a predefined path as one of the following:

Value

Meaning

RTL_REGISTRY_ABSOLUTE

Path is an absolute registry path.

RTL_REGISTRY_SERVICES

Path is relative to \Registry\Machine\System\CurrentControlSet\Services.

RTL_REGISTRY_CONTROL

Path is relative to \Registry\Machine\System\CurrentControlSet\Control.

RTL_REGISTRY_WINDOWS_NT

Path is relative to \Registry\Machine\Software\Microsoft\
Windows NT\CurrentVersion.

RTL_REGISTRY_DEVICEMAP

Path is relative to \Registry\Machine\Hardware\DeviceMap.

RTL_REGISTRY_USER

Path is relative to \Registry\User\CurrentUser.

RTL_REGISTRY_OPTIONAL

Specifies that the key referenced by this parameter and the Path parameter are optional.

RTL_REGISTRY_HANDLE

Specifies that the Path parameter is actually a registry handle to use. This value is optional.

Path
Points to either an absolute registry path or a path relative to the known location specified by the RelativeTo parameter. If the RTL_REGISTRY_HANDLE flag is specified, this parameter is a registry handle for an already opened key to be used directly.
ValueName
Points to the name of a subkey or value entry to be written into the registry.
ValueType
Points to the type of value, identified by the ValueName parameter, to be placed in the registry.
ValueData
Points to the name of a subkey or values for its value entries (or both) to be written into the registry.
ValueLength
Specifies the number of bytes of ValueData to be written into the registry.

Return Value

RtlWriteRegistryValue returns the status of the operation.

Comments

Callers of RtlWriteRegistryValue must be running at IRQL PASSIVE_LEVEL.

See Also

RtlCheckRegistryKey, RtlCreateRegistryKey, RtlDeleteRegistryValue, RtlQueryRegistryValues, ZwOpenKey


RtlZeroMemory

VOID
RtlZeroMemory(
IN PVOID Destination,
 IN ULONG Length
);

RtlZeroMemory fills a block of memory with zeros, given a pointer to the block and the length, in bytes, to be filled.

Parameters

Destination
Points to the memory to be filled with zeros.
Length
Specifies the number of bytes to be zeroed.

Comments

Callers of RtlZeroMemory can be running at any IRQL if the Destination block is in nonpaged pool. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

RtlFillMemory


Chapter 10 Security Reference Monitor Routines

Generally, higher-level network drivers call these routines.

References for the Se Xxx routines are in alphabetical order. For an overview of the functionality of these routines, see Chapter 1.


SeAccessCheck

BOOLEAN
SeAccessCheck(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
 IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
 IN BOOLEAN SubjectContextLocked,
 IN ACCESS_MASK DesiredAccess,
 IN ACCESS_MASK PreviouslyGrantedAccess,
 OUT PPRIVILEGE_SET *Privileges, /* optional */
IN PGENERIC_MAPPING GenericMapping,
 IN KPROCESSOR_MODE AccessMode,
 OUT PACCESS_MASK GrantedAccess,
 OUT PNTSTATUS AccessStatus
);

SeAccessCheck determines whether the requested access rights can be granted to an object protected by a security descriptor and an object owner.

Parameters

SecurityDescriptor
Points to the security descriptor protecting the object being accessed.
SubjectSecurityContext,
Points to the subject’s captured security context.
SubjectContextLocked
Indicates whether the user’s subject context is locked, so that it does not have to be locked again.
DesiredAccess
Specifies the access mask for rights that the caller is attempting to acquire.
PreviouslyGrantedAccess
Specifies the access rights already granted, for example, as a result of holding a privilege.
Privileges
Points to a set of buffered privileges used as part of the access validation. The buffer must be released by the caller with ExFreePool when the caller has consumed this information.
GenericMapping
Points to the generic mapping associated with this object type.
AccessMode
Specifies the access mode to be used in the check, one of UserMode or KernelMode.
GrantedAccess
Points to a returned access mask indicating the granted access.
AccessStatus
Points to the status value indicating why access was denied.

Return Value

If access is allowed, SeAccessCheck returns TRUE.

Comments

Network transport drivers call this routine.

SeAccessCheck might perform privilege tests for SeTakeOwnershipPrivilege and/or SeSecurityPrivilege, depending on the accesses being requested. It might perform additional privilege testing in future releases of Windows NT.

This routine also might check whether the subject is the owner of the object in order to grant WRITE_DAC access.

If this routine returns FALSE, the caller should use the returned AccessStatus as its return value. That is, the caller should avoid hardcoding a return value of STATUS_ACCESS_DENIED or any other specific STATUS_XXX value.

Callers of this routine must be running at IRQL PASSIVE_LEVEL.

See Also

ExFreePool, IoGetFileObjectGenericMapping


SeAssignSecurity

NTSTATUS
SeAssignSecurity(
IN PSECURITY_DESCRIPTOR ParentDescriptor, /* optional*/
IN PSECURITY_DESCRIPTOR ExplicitDescriptor,
 OUT PSECURITY_DESCRIPTOR *NewDescriptor,
 IN BOOLEAN IsDirectoryObject,
 IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
 IN PGENERIC_MAPPING GenericMapping,
 IN POOL_TYPE PoolType
);

SeAssignSecurity builds a self-relative security descriptor for a new object, given the security descriptor of its parent directory and any originally requested security for the object.

Parameters

ParentDescriptor
Points to a buffer containing the security descriptor of the parent directory containing the new object being created.
ExplicitDescriptor
Points to a buffer containing the security descriptor specified by the user that is applied to the new object.
NewDescriptor
Receives a pointer to the returned security descriptor for which this routine allocates a buffer according to the given PoolType.
IsDirectoryObject
Specifies whether the new object is a directory object. TRUE indicates the object contains other objects.
SubjectContext
Points to a buffer containing the security context of the subject creating the object. This is used to retrieve default security information for the new object, such as the default owner, the primary group, and discretionary access control.
GenericMapping
Points to an array of access mask values denoting the mapping between each generic right to nongeneric rights.
PoolType
Specifies the pool type to use when allocating a new security descriptor, which can be one of the following:

NonPagedPool
PagedPool
NonPagedPoolMustSucceed
NonPagedPoolCacheAligned
NonPagedPoolCacheAlignedMustS
PagedPoolCacheAligned

Usually, a caller specifies PagedPool, or else NonPagedPool if the buffer will be accessed at raised IRQL in an arbitrary thread context.

Return Value

SeAssignSecurity can return one of the following:
Status | Meaning
STATUS_SUCCESS | Indicates the operation was successful.
STATUS_INVALID_OWNER | The owner SID that was provided as the owner of the target security descriptor is not one the caller is authorized to assign as the owner of an object.
STATUS_PRIVILEGE_NOT_HELD | The caller does not have the privilege (SeSecurityPrivilege) necessary to explicitly assign the specified system ACL.

Comments

Network transport drivers call this routine.

The final security descriptor returned to the caller may contain a mix of information, some explicitly provided from the new object’s parent.

SeAssignSecurity assumes privilege checking has not been performed. This routine performs privilege checking.

The assignment of system and discretionary ACLs is governed by the logic illustrated in the following table:
 | Explicit (nondefault) ACL specified | Explicit default ACL specified | No ACL specified
Inheritable ACL from parent | Assign specified ACL | Assign inherited ACL | Assign inherited ACL
No inheritable ACL from parent | Assign specified ACL | Assign default ACL | Assign no ACL

An explicitly specified ACL, whether a default ACL or not, can be empty or null. The caller must be a kernel-mode client or be appropriately privileged to explicitly assign a default or nondefault system ACL.

The assignment of the new object’s owner and group is governed by the following logic:

  • If the passed security descriptor includes an owner, it is assigned as the new object’s owner. Otherwise, the caller’s token is considered to determine the owner. Within the token, the default owner, if any, is assigned. Otherwise, the caller’s user ID is assigned.

  • If the passed security descriptor includes a group, it is assigned as the new object’s group. Otherwise, the caller’s token is considered to determine the group. Within the token, the default group, if any, is assigned. Otherwise, the caller’s primary group ID is assigned.

Callers of SeAssignSecurity must be running at IRQL PASSIVE_LEVEL.

See Also

IoGetFileObjectGenericMapping, SeDeassignSecurity


SeDeassignSecurity

NTSTATUS
SeDeassignSecurity(
IN OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
);

SeDeassignSecurity deallocates the memory associated with a security descriptor that was assigned using SeAssignSecurity.

Parameters

SecurityDescriptor
Points to the buffered security descriptor being released.

Return Value

If the deallocation succeeds, SeDeassignSecurity returns STATUS_SUCCESS.

Comments

Callers of SeDeassignSecurity must be running at IRQL PASSIVE_LEVEL.

See Also

SeAssignSecurity


SeSinglePrivilegeCheck

BOOLEAN
SeSinglePrivilegeCheck(
LUID PrivilegeValue,
 KPROCESSOR_MODE PreviousMode
);

SeSinglePrivilegeCheck checks for the passed privilege value in the context of the current thread.

Parameters

PrivilegeValue
Specifies the value of the privilege being checked.
PreviousMode
Specifies the previous execution mode, one of UserMode or KernelMode.

Return Value

SeSinglePrivilegeCheck returns TRUE if the current subject has the required privilege.

Comments

Network transport drivers call this routine.

If PreviousMode is KernelMode, the privilege check always succeeds. Otherwise, this routine uses the token of the user mode thread to determine whether the current (user-mode) thread has been granted the given privilege.

Callers of SeSinglePrivilegeCheck must be running at IRQL PASSIVE_LEVEL.


Chapter 11 ZwXxx Routines

Device and intermediate drivers might call some Zw Xxx routines, which this chapter describes in compressed form: that is, information relevant to device and intermediate drivers is covered here.

References for the Zw Xxx routines are in alphabetical order. Click on the browse button to view them.

For an overview of the functionality of these routines, see Chapter 1.


ZwClose

NTSTATUS
ZwClose(
IN HANDLE Handle
);

ZwClose closes object handles. A named object is not actually deleted until all of its valid handles are closed and no referenced pointers remain.

Parameters

Handle
Is a valid handle for an open object.

Return Value

ZwClose can return one of the following values:

STATUS_SUCCESS
STATUS_OBJECT_TYPE_MISMATCH
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

ZwClose is a generic routine that operates on any type of object.

Closing an open handle for an object causes that handle to become invalid. The reference count for the object handle is decremented and object retention checks are performed.

Callers of ZwClose must be running at IRQL PASSIVE_LEVEL.

See Also

ZwCreateDirectoryObject, ZwCreateFile, ZwCreateKey, ZwOpenKey, ZwOpenSection


ZwCreateDirectoryObject

NTSTATUS
ZwCreateDirectoryObject(
OUT PHANDLE DirectoryHandle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes
);

ZwCreateDirectoryObject creates or opens a directory object, which is a container for other objects.

Parameters

DirectoryHandle
Points to a variable that receives the directory object handle if the call is successful.
DesiredAccess
Specifies the type of access that the caller requires to the directory object. This value is compared with the granted access on an existing directory object. A caller can specify one or a combination of the following:

DesiredAccess Flags

Meaning

DIRECTORY_QUERY

Query access to the directory object

DIRECTORY_TRAVERSE

Name-lookup access to the directory object

DIRECTORY_CREATE_OBJECT

Name-creation access to the directory object

DIRECTORY_CREATE_SUBDIRECTORY

Subdirectory-creation access to the directory object

DIRECTORY_ALL_ACCESS

All of the preceding

ObjectAttributes
Points to a structure that specifies the object’s attributes, which has already been initialized with InitializeObjectAttributes.

Return Value

ZwCreateDirectoryObject can return one of the following values:

STATUS_SUCCESS
STATUS_ACCESS_DENIED
STATUS_ACCESS_VIOLATION
STATUS_DATATYPE_MISALIGNMENT

Comments

A directory object is a container for other objects. Note that file system directories are not represented by directory objects, but rather by file objects.

Directory objects are an integral part of the system’s object management and are manipulated indirectly as a result of other operations. For example, when a device object is created, its name is inserted in a directory object and the pointer counts of both the directory object and the named device object are incremented. Any named object’s header contains a pointer to the directory object containing that object’s name.

Drivers that create a set of device objects might set up a directory object when they initialize. For example, a disk driver might use this technique to group the device object representing a physical disk and the device objects representing partitions on that disk in a driver-created directory object.

Before the DriverEntry routine returns control, such a driver calls ZwMakeTemporaryObject if its directory object was initialized with the permanent attribute, and ZwClose to release the directory object created to hold such a group of related device objects.

If a directory object was initialized as temporary and its handle count becomes zero, the directory object’s name is deleted. Name deletion occurs for a temporary object when the last handle to the object has been closed. A driver also can delete a directory object it creates when the object is no longer needed by using this technique.

Callers of ZwCreateDirectoryObject must be running at IRQL PASSIVE_LEVEL.

See Also

InitializeObjectAttributes, ObDereferenceObject, ZwClose, ZwMakeTemporaryObject


ZwCreateFile

NTSTATUS
ZwCreateFile(
OUT PHANDLE FileHandle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 IN PLARGE_INTEGER AllocationSize, /* optional */
IN ULONG FileAttributes,
 IN ULONG ShareAccess,
 IN ULONG CreateDisposition,
 IN ULONG CreateOptions,
 IN PVOID EaBuffer, /* optional */
IN ULONG EaLength
);

ZwCreateFile either causes a new file or directory to be created, or it opens an existing file, device, directory, or volume, giving the caller a handle for the file object. This handle can be used by subsequent calls to manipulate data within the file or the file object’s state or attributes. For example, a driver might call this routine during initialization to open a file of microcode for its device.

Parameters

FileHandle
Points to a variable that receives the file handle if the call is successful.
DesiredAccess
Specifies the type of access that the caller requires to the file or directory. The set of system-defined DesiredAccess flags determines the following specific access rights for file objects:

DesiredAccess Flags

Meaning

DELETE

The file can be deleted.

FILE_READ_DATA

Data can be read from the file.

FILE_READ_ATTRIBUTES

FileAttributes flags, described later, can be read.

FILE_READ_EA

Extended attributes associated with the file can be read. This flag is irrelevant to device and intermediate drivers.

READ_CONTROL

The access control list (ACL) and ownership information associated with the file can be read.

FILE_WRITE_DATA

Data can be written to the file.

FILE_WRITE_ATTRIBUTES

FileAttributes flags can be written.

FILE_WRITE_EA

Extended attributes (EAs) associated with the file can be written. This flag is irrelevant to device and intermediate drivers.

FILE_APPEND_DATA

Data can be appended to the file.

WRITE_DAC

The discretionary access control list (DACL) associated with the file can be written.

WRITE_OWNER

Ownership information associated with the file can be written.

SYNCHRONIZE

The returned FileHandle can be waited on to synchronize with the completion of an I/O operation.

FILE_EXECUTE

Data can be read into memory from the file using system paging I/O. This flag is irrelevant to device and intermediate drivers.

Callers of ZwCreateFile can specify one or a combination of the following, possibly ORed with additional compatible flags from the preceding DesiredAccess Flags list, for any file object that does not represent a directory file:

DesiredAccess to File Values

Maps to DesiredAccess Flags

GENERIC_READ

STANDARD_RIGHTS_READ, FILE_READ_DATA, FILE_READ_ATTRIBUTES, and FILE_READ_EA

GENERIC_WRITE

STANDARD_RIGHTS_WRITE, FILE_WRITE_DATA, FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, and FILE_APPEND_DATA

GENERIC_EXECUTE

STANDARD_RIGHTS_EXECUTE, SYNCHRONIZE, and FILE_EXECUTE. This value is irrelevant to device and intermediate drivers.

The STANDARD_RIGHTS_XXX are predefined system values used to enforce security on system objects.

To open or create a directory file, as also indicated with the CreateOptions parameter, callers of ZwCreateFile can specify one or a combination of the following, possibly ORed with one or more compatible flags from the preceding DesiredAccess Flags list:

DesiredAccess to Directory Values

Meaning

FILE_LIST_DIRECTORY

Files in the directory can be listed.

FILE_TRAVERSE

The directory can be traversed: that is, it can be part of the pathname of a file.

The FILE_READ_DATA, FILE_WRITE_DATA, FILE_EXECUTE, and FILE_APPEND_DATA DesiredAccess flags are incompatible with creating or opening a directory file.

ObjectAttributes
Points to a structure already initialized with InitializeObjectAttributes. Members of this structure for a file object include the following:

Member

Value

ULONG Length

Specifies the number of bytes of ObjectAttributes data supplied. This value must be at least sizeof(OBJECT_ATTRIBUTES).

PUNICODE_STRING ObjectName

Points to a buffered Unicode string naming the file to be created or opened. This value must be a fully qualified file specification or the name of a device object, unless it is the name of a file relative to the directory specified by RootDirectory. For example, \Device\Floppy1\myfile.dat or \??\B:\myfile.dat could be the fully qualified file specification, provided that the floppy driver and overlying file system are already loaded. (Note: \?? replaces \DosDevices as the name of the Win32 object namespace. \DosDevices will still work, but \?? is translated faster by the object manager.)

HANDLE RootDirectory

Optionally specifies a handle to a directory obtained by a preceding call to ZwCreateFile. If this value is NULL, the ObjectName member must be a fully qualified file specification that includes the full path to the target file. If this value is nonNULL, the ObjectName member specifies a file name relative to this directory.

PSECURITY_DESCRIPTOR SecurityDescriptor

Optionally specifies a security descriptor to be applied to a file. ACLs specified by such a security descriptor are only applied to the file when it is created. If the value is NULL when a file is created, the ACL placed on the file is file-system-dependent; most file systems propagate some part of such an ACL from the parent directory file combined with the caller's default ACL. Device and intermediate drivers can set this member to NULL.

PSECURITY_QUALITY_OF_SERVICE SecurityQualityOfService

Specifies the access rights a server should be given to the client's security context. This value is nonNULL only when a connection to a protected server is established, allowing the caller to control which parts of the caller’s security context are made available to the server and whether the server is allowed to impersonate the caller. Device and intermediate drivers usually set this member to NULL.

ULONG Attributes

Is a set of flags that controls the file object attributes. This value can be zero or OBJ_CASE_INSENSITIVE, which indicates that name-lookup code should ignore the case of ObjectName rather than performing an exact-match search. The value OBJ_INHERIT is irrelevant to device and intermediate drivers.

IoStatusBlock
Points to a variable that receives the final completion status and information about the requested operation. On return from ZwCreateFile, the Information member contains one of the following values:

FILE_CREATED
FILE_OPENED
FILE_OVERWRITTEN
FILE_SUPERSEDED
FILE_EXISTS
FILE_DOES_NOT_EXIST

AllocationSize
Optionally specifies the initial allocation size in bytes for the file. A nonzero value has no effect unless the file is being created, overwritten, or superseded.
FileAttributes
Explicitly specified attributes are applied only when the file is created, superseded, or, in some cases, overwritten. By default, this value is FILE_ATTRIBUTE_NORMAL, which can be overridden by any other flag or by an ORed combination of compatible flags. Possible FileAttributes flags include the following:

FileAttributes Flags

Meaning

FILE_ATTRIBUTE_NORMAL

A file with standard attributes should be created.

FILE_ATTRIBUTE_READONLY

A read-only file should be created.

FILE_ATTRIBUTE_HIDDEN

A hidden file should be created.

FILE_ATTRIBUTE_SYSTEM

A system file should be created.

FILE_ATTRIBUTE_ARCHIVE

The file should be marked so that it will be archived.

FILE_ATTRIBUTE_TEMPORARY

A temporary file should be created.

FILE_ATTRIBUTE_ATOMIC_WRITE

An atomic-write file should be created. This flag is irrelevant to device and intermediate drivers.

FILE_ATTRIBUTE_XACTION_WRITE

A transaction-write file should be created. This flag is irrelevant to device and intermediate drivers.

ShareAccess
Specifies the type of share access that the caller would like to the file, as zero, or as one or a combination of the following:

ShareAccess Flags

Meaning

FILE_SHARE_READ

The file can be opened for read access by other threads’ calls to ZwCreateFile.

FILE_SHARE_WRITE

The file can be opened for write access by other threads’ calls to ZwCreateFile.

FILE_SHARE_DELETE

The file can be opened for delete access by other threads’ calls to ZwCreateFile.

Device and intermediate drivers usually set ShareAccess to zero, which gives the caller exclusive access to the open file.

CreateDisposition
Specifies what to do, depending on whether the file already exists, as one of the following:

CreateDispostion Values

Meaning

FILE_SUPERSEDE

If the file already exists, replace it with the given file. If it does not, create the given file.

FILE_CREATE

If the file already exists, fail the request and do not create or open the given file. If it does not, create the given file.

FILE_OPEN

If the file already exists, open it instead of creating a new file. If it does not, fail the request and do not create a new file.

FILE_OPEN_IF

If the file already exists, open it. If it does not, create the given file.

FILE_OVERWRITE

If the file already exists, open it and overwrite it. If it does not, fail the request.

FILE_OVERWRITE_IF

If the file already exists, open it and overwrite it. If it does not, create the given file.

CreateOptions
Specifies the options to be applied when creating or opening the file, as a compatible combination of the following flags:

CreateOptions Flags

Meaning

FILE_DIRECTORY_FILE

The file being created or opened is a directory file. With this flag, the CreateDisposition parameter must be set to one of FILE_CREATE, FILE_OPEN, or FILE_OPEN_IF. With this flag, other compatible CreateOptions flags include only the following: FILE_SYNCHRONOUS_IO_ALERT, FILE_SYNCHRONOUS_IO
_NONALERT, FILE_WRITE_THROUGH, FILE_OPEN_FOR_BACKUP_INTENT, and FILE_OPEN_BY_FILE_ID.

FILE_NON_DIRECTORY_FILE

The file being opened must not be a directory file or this call will fail. The file object being opened can represent a data file, a logical, virtual, or physical device, or a volume.

FILE_WRITE_THROUGH

System services, FSDs, and drivers that write data to the file must actually transfer the data into the file before any requested write operation is considered complete. This flag is automatically set if the CreateOptions flag FILE_NO_INTERMEDIATE
_BUFFERING is set.

FILE_SEQUENTIAL_ONLY

All accesses to the file will be sequential.

FILE_RANDOM_ACCESS

Accesses to the file can be random, so no sequential read-ahead operations should be performed on the file by FSDs or the system.

FILE_NO_INTERMEDIATE
_BUFFERING

The file cannot be cached or buffered in a driver’s internal buffers. This flag is incompatible with the DesiredAccess FILE_APPEND_DATA flag.

FILE_SYNCHRONOUS_IO_ALERT

All operations on the file are performed synchronously. Any wait on behalf of the caller is subject to premature termination from alerts. This flag also causes the I/O system to maintain the file position context. If this flag is set, the DesiredAccess SYNCHRONIZE flag also must be set.

FILE_SYNCHRONOUS_IO
_NONALERT

All operations on the file are performed synchronously. Waits in the system to synchronize I/O queueing and completion are not subject to alerts. This flag also causes the I/O system to maintain the file position context. If this flag is set, the DesiredAccess SYNCHRONIZE flag also must be set.

FILE_CREATE_TREE_CONNECTION

Create a tree connection for this file in order to open it over the network. This flag is irrelevant to device and intermediate drivers.

FILE_COMPLETE_IF_OPLOCKED

Complete this operation immediately with an alternate success code if the target file is oplocked, rather than blocking the caller's thread. If the file is oplocked, another caller already has access to the file over the network. This flag is irrelevant to device and intermediate drivers.

FILE_NO_EA_KNOWLEDGE

If the extended attributes on an existing file being opened indicate that the caller must understand EAs to properly interpret the file, fail this request because the caller does not understand how to deal with EAs. Device and intermediate drivers can ignore this flag.

FILE_DELETE_ON_CLOSE

Delete the file when the last handle to it is is passed to ZwClose.

FILE_OPEN_BY_FILE_ID

The file name contains the name of a device and a 64-bit ID to be used to open the file. This flag is irrelevant to device and intermediate drivers.

FILE_OPEN_FOR_BACKUP_INTENT

The file is being opened for backup intent, hence, the system should check for certain access rights and grant the caller the appropriate accesses to the file before checking the input DesiredAccess against the file's security descriptor. This flag is irrelevant to device and intermediate drivers.

EaBuffer
For device and intermediate drivers, this parameter must be a NULL pointer.
EaLength
For device and intermediate drivers, this parameter must be zero.

Return Value

ZwCreateFile either returns STATUS_SUCCESS or an appropriate error status. If it returns an error status, the caller can find more information about the cause of the failure by checking the IoStatusBlock.

Comments

There are two alternate ways to specify the name of the file to be created or opened with ZwCreateFile:

  1. As a fully qualified pathname, supplied in the ObjectName member of the input ObjectAttributes

  2. As pathname relative to the directory file represented by the handle in the RootDirectory member of the input ObjectAttributes

Certain DesiredAccess flags and combinations of flags have the following effects:

  • For a caller to synchronize an I/O completion by waiting on the returned FileHandle, the SYNCHRONIZE flag must be set. Otherwise, a caller that is a device or intermediate driver must synchronize an I/O completion by using an event object.

  • If only the FILE_APPEND_DATA and SYNCHRONIZE flags are set, the caller can write only to the end of the file, and any offset information on writes to the file is ignored. However, the file will automatically be extended as necessary for this type of write operation.

  • Setting the FILE_WRITE_DATA flag for a file also allows writes beyond the end of the file to occur. The file is automatically extended for this type of write, as well.

  • If only the FILE_EXECUTE and SYNCHRONIZE flags are set, the caller cannot directly read or write any data in the file using the returned FileHandle: that is, all operations on the file occur through the system pager in response to instruction and data accesses. Device and intermediate drivers should not set the FILE_EXECUTE flag in DesiredAccess.

The ShareAccess parameter determines whether separate threads can access the same file, possibly simultaneously. Provided that both file openers have the privilege to access a file in the specified manner, the file can be successfully opened and shared. If the original caller of ZwCreateFile does not specify FILE_SHARE_READ, FILE_SHARE_WRITE, or FILE_SHARE_DELETE, no other open operations can be performed on the file: that is, the original caller is given exclusive access to the file.

In order for a shared file to be successfully opened, the requested DesiredAccess to the file must be compatible with both the DesiredAccess and ShareAccess specifications of all preceding opens that have not yet been released with ZwClose. That is, the DesiredAccess specified to ZwCreateFile for a given file must not conflict with the accesses that other openers of the file have disallowed.

The CreateDispostion value FILE_SUPERSEDE requires that the caller have DELETE access to a existing file object. If so, a successful call to ZwCreateFile with FILE_SUPERSEDE on an existing file effectively deletes that file, and then recreates it. This implies that, if the file has already been opened by another thread, it opened the file by specifying a ShareAccess parameter with the FILE_SHARE_DELETE flag set. Note that this type of disposition is consistent with the POSIX style of overwriting files.

The CreateDisposition values FILE_OVERWRITE_IF and FILE_SUPERSEDE are similar. If ZwCreateFile is called with a existing file and either of these CreateDisposition values, the file will be replaced.

Overwriting a file is semantically equivalent to a supersede operation, except for the following:

  • The caller must have write access to the file, rather than delete access. This implies that, if the file has already been opened by another thread, it opened the file with the FILE_SHARE_WRITE flag set in the input ShareAccess.

  • The specified file attributes are logically ORed with those already on the file. This implies that, if the file has already been opened by another thread, a subsequent caller of ZwCreateFile cannot disable existing FileAttributes flags but can enable additional flags for the same file. Note that this style of overwriting files is consistent with MS-DOS™, with DOS-based Windows™, and with OS/2®.

The CreateOptions FILE_DIRECTORY_FILE value specifies that the file to be created or opened is a directory file. When a directory file is created, the file system creates an appropriate structure on the disk to represent an empty directory for that particular file system's on-disk structure. If this option was specified and the given file to be opened is not a directory file, or if the caller specified an inconsistent CreateOptions or CreateDispostion value, the call to ZwCreateFile will fail.

The CreateOptions FILE_NO_INTERMEDIATE_BUFFERING flag prevents the file system from performing any intermediate buffering on behalf of the caller. Specifying this value places certain restrictions on the caller's parameters to other Zw..File routines, including the following:

  • Any optional ByteOffset passed to ZwReadFile or ZwWriteFile must be an integral of the sector size.

  • The Length passed to ZwReadFile or ZwWriteFile, must be an integral of the sector size. Note that specifying a read operation to a buffer whose length is exactly the sector size might result in a lesser number of significant bytes being transferred to that buffer if the end of the file was reached during the transfer.

  • Buffers must be aligned in accordance with the alignment requirement of the underlying device. This information can be obtained by calling ZwCreateFile to get a handle for the file object that represents the physical device, and, then, calling ZwQueryInformationFile with that handle. For a list of the system FILE_XXX_ALIGNMENT values, see DEVICE_OBJECT in Chapter 12.

  • Calls to ZwSetInformationFile with the FileInformationClass parameter set to FilePositionInformation must specify an offset that is an integral of the sector size.

The CreateOptions FILE_SYNCHRONOUS_IO_ALERT and FILE_SYNCHRONOUS_IO_NONALERT, which are mutually exclusive as their names suggest, specify that all I/O operations on the file are to be synchronous as long as they occur through the file object referred to by the returned FileHandle. All I/O on such a file is serialized across all threads using the returned handle. With either of these CreateOptions, the DesiredAccess SYNCHRONIZE flag must be set so that the I/O Manager will use the file object as a synchronization object. With either of these CreateOptions set, the I/O Manager maintains the “file position context” for the file object, an internal, current file position offset. This offset can be used in calls to ZwReadFile and ZwWriteFile. Its position also can be queried or set with ZwQueryInformationFile and ZwSetInformationFile.

Callers of ZwCreateFile must be running at IRQL PASSIVE_LEVEL.

See Also

InitializeObjectAttributes, DEVICE_OBJECT, IO_STATUS_BLOCK, ZwClose, ZwReadFile, ZwQueryInformationFile, ZwSetInformationFile, ZwWriteFile


ZwCreateKey

NTSTATUS
ZwCreateKey(
OUT PHANDLE KeyHandle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes,
 IN ULONG TitleIndex,
 IN PUNICODE_STRING Class, /* optional*/
IN ULONG CreateOptions,
 OUT PULONG Disposition /* optional*/
);

ZwCreateKey opens an existing key or creates a new key in the registry.

Parameters

KeyHandle
Points to a returned handle for a newly created or existing key if this call is successful.
DesiredAccess
Specifies the type of access that the caller requires to the key. The set of system-defined DesiredAccess flags determines the following specific access rights for key objects:

DesiredAccess Flags

Meaning

KEY_QUERY_VALUE

Value entries for the key can be read.

KEY_SET_VALUE

Value entries for the key can be written.

KEY_CREATE_SUB_KEYS

Subkeys for the key can be created.

KEY_ENUMERATE_SUB_KEYS

All subkeys for the key can be read.

KEY_NOTIFY

This flag is irrelevant to device and intermediate drivers, and to other kernel-mode code.

KEY_CREATE_LINK

A symbolic link to the key can be created. This flag is irrelvant to device and intermediate drivers.

Callers of ZwCreateKey can specify one or a compatible combination of the following for any key object:

DesiredAccess to Key Values

Maps to DesiredAccess Flags

KEY_READ

STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY

KEY_WRITE

STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUBKEY

KEY_EXECUTE

KEY_READ. This value is irrelevant to device and intermediate drivers.

KEY_ALL_ACCESS

STANDARD_RIGHTS_ALL, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY, KEY_ENUMERATE_SUBKEY, KEY_NOTIFY and KEY_CREATE_LINK

The STANDARD_RIGHTS_XXX are predefined system values used to enforce security on system objects.

ObjectAttributes
Points to the initialized object attributes of the key being opened or created. An ObjectName string for the key must be specified. If a RootDirectory handle also is supplied, the given name is relative to the key represented by the handle. Any given name must be within the object name space allocated to the registry, meaning that all names must begin with \Registry. RootHandle, if present, must be a handle to the root directory object, to \Registry, or to a key under \Registry.
TitleIndex
Device and intermediate drivers should set this parameter to zero.
Class
Points to the object class of the key. To the Configuration Manager, this is just a Unicode string.
CreateOptions
Specifies options to be applied when creating a key, as a compatible combination of the following:

Value

Meaning

REG_OPTION_VOLATILE

Key is not to be stored across boots.

REG_OPTION_NON_VOLATILE

Key is preserved when the system is rebooted.

REG_OPTION_CREATE_LINK

The created key is a symbolic link. This value is irrelevant to device and intermediate drivers.

REG_OPTION_BACKUP_RESTORE

Key is being opened or created with special privileges allowing backup/restore operations. This value is irrelevant to device and intermediate drivers.

Disposition
Points to a variable that receives a value indicating whether a new key was created in the \Registry tree or an existing one opened:

Value

Meaning

REG_CREATED_NEW_KEY

A new key object was created.

REG_OPENED_EXISTING_KEY

An existing key object was opened.

Return Value

ZwCreateKey returns STATUS_SUCCESS if the given key was created or opened.

Comments

If the key specified by ObjectAttributes does not exist, an attempt is made to create it. For this attempt to succeed, the new key must be a direct subkey of the key referred to by KeyHandle, and the given KeyHandle must have been opened for KEY_CREATE_SUB_KEY access.

If the specified key already exists, it is opened and its value is not affected in any way.

The security attributes specified by ObjectAttributes when a key is created determine whether the specified DesiredAccess is granted on subsequent calls to ZwCreateKey and ZwOpenKey.

Callers of ZwCreateKey must be running at IRQL PASSIVE_LEVEL.

See Also

InitializeObjectAttributes, ZwClose, ZwDeleteKey, ZwEnumerateKey, ZwEnumerateValueKey, ZwFlushKey, ZwOpenKey, ZwQueryValueKey, ZwSetValueKey


ZwDeleteKey

NTSTATUS
ZwDeleteKey(
IN HANDLE KeyHandle
);

ZwDeleteKey deletes an open key from the registry.

Parameters

KeyHandle
Is a handle returned by a successful call to ZwCreateKey or ZwOpenKey.

Return Value

ZwDeleteKey can return one of the following values:

STATUS_SUCCESS
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

The key must have been opened for DELETE access for a deletion to succeed; the DesiredAccess value KEY_ALL_ACCESS includes DELETE access. The actual storage for the key is deleted when the last handle to the key is closed.

A call to ZwDeleteKey causes the KeyHandle to become invalid.

Callers of ZwDeleteKey must be running at IRQL PASSIVE_LEVEL.

See Also

ZwClose, ZwCreateKey, ZwOpenKey


ZwEnumerateKey

NTSTATUS
ZwEnumerateKey(
IN HANDLE KeyHandle,
 IN ULONG Index,
 IN KEY_INFORMATION_CLASS KeyInformationClass,
 OUT PVOID KeyInformation,
 IN ULONG Length,
 OUT PULONG ResultLength
);

ZwEnumerateKey returns information about the subkeys of an open key.

Parameters

KeyHandle
Is the handle, returned by a successful call to ZwCreateKey or ZwOpenKey, of the key whose subkeys are to be enumerated.
Index
Specifies the zero-based index of the subkey for which the information is requested.
KeyInformationClass
Specifies the type of information returned in the KeyInformation buffer as one of the following system-defined values:

KeyBasicInformation
KeyNodeInformation
KeyFullInformation

KeyInformation
Points to a caller-allocated buffer to receive the requested data.
Length
Is the size in bytes of the KeyInformation buffer, which the caller should set according to the given KeyInformationClass.
ResultLength
Points to the number of bytes actually returned to KeyInformation or, if the input Length is too small, points to the number of bytes required for the available information.

Return Value

ZwEnumerateKey returns STATUS_SUCCESS, together with the name of the Index subkey to the given KeyInformation buffer. Otherwise, ZwEnumerateKey can return one of the following values:

STATUS_NO_MORE_ENTRIES
STATUS_BUFFER_OVERFLOW

Comments

The KeyHandle passed to ZwEnumerateKey must have been opened with the KEY_ENUMERATE_SUB_KEY DesiredAccess flag set for this call to succeed. See ZwCreateKey for a description of possible values for DesiredAccess.

The Index parameter is simply a way to select among subkeys of the key referred to by the KeyHandle. Two calls to ZwEnumerateKey with the same Index are not guaranteed to return the same result.

Note that callers of the Rtl..Registry routines are required to provide the name of the key. Drivers can call ZwEnumerateKey to get unknown names of the subkeys for a key with a known name.

Callers of ZwEnumerateKey must be running at IRQL PASSIVE_LEVEL.

See Also

KEY_BASIC_INFORMATION, KEY_FULL_INFORMATION, KEY_NODE_INFORMATION, RtlCheckRegistryKey, RtlCreateRegistryKey, RtlDeleteRegistryValue, RtlQueryRegistryValues, RtlWriteRegistryValue, ZwCreateKey, ZwEnumerateValueKey, ZwOpenKey


ZwEnumerateValueKey

NTSTATUS
ZwEnumerateValueKey(
IN HANDLE KeyHandle,
 IN ULONG Index,
 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
 OUT PVOID KeyValueInformation,
 IN ULONG Length,
 OUT PULONG ResultLength
);

ZwEnumerateValueKey returns information about the value entries of an open key.

Parameters

KeyHandle
Is the handle, returned by a successful call to ZwCreateKey or ZwOpenKey, of the key whose value entries are to be enumerated.
Index
Specifies the zero-based index of a subkey for which the value information is requested.
KeyValueInformationClass
Specifies the type of information returned in the KeyValueInformation buffer as one of the following:

KeyValueBasicInformation
KeyValueFullInformation
KeyValuePartialInformation

KeyValueInformation
Points to a caller-allocated buffer to receive the requested data.
Length
Is the size in bytes of the KeyValueInformation buffer, which the caller should set according to the given KeyValueInformationClass.
ResultLength
Points to number of bytes actually returned to KeyValueInformation or, if the input Length is too small, points to the number of bytes required for the available information.

Return Value

ZwEnumerateValueKey returns STATUS_SUCCESS, together with the name of the Index subkey to the given KeyValueInformation buffer. Otherwise, ZwEnumerateValueKey can return one of the following values:

STATUS_NO_MORE_VALUES
STATUS_BUFFER_OVERFLOW
STATUS_INVALID_PARAMETER

Comments

The KeyHandle passed to ZwEnumerateValueKey must have been opened with the KEY_QUERY_VALUE DesiredAccess flag set for this call to succeed. See ZwCreateKey for a description of possible values for DesiredAccess.

The Index is simply a way to select among subkeys with value entries. Two calls to ZwEnumerateValueKey with the same Index are not guaranteed to return the same results.

Callers of ZwEnumerateValueKey must be running at IRQL PASSIVE_LEVEL.

See Also

ZwClose, ZwCreateKey, ZwOpenKey, ZwQueryValueKey


ZwFlushKey

NTSTATUS
ZwFlushKey(
IN HANDLE KeyHandle
);

ZwFlushKey forces a registry key to be committed to disk.

Parameters

KeyHandle
Is the handle, returned by a successful call to ZwCreateKey or ZwOpenKey, of the key to be flushed.

Return Value

ZwFlushKey returns STATUS_SUCCESS if the key information was transferred to disk.

Comments

Changes made by ZwCreateKey or ZwSetValueKey can be flushed to disk with ZwFlushKey. This routine does not return to its caller until any changed data associated with the given KeyHandle has been written to permanent store.

Note This routine can flush the entire registry. Accordingly, it can generate a great deal of I/O. Since the system automatically flushes key changes every few seconds, it is seldom necessary to call ZwFlushKey.

Callers of ZwFlushKey must be running at IRQL PASSIVE_LEVEL.

See Also

ZwCreateKey, ZwOpenKey, ZwSetValueKey


ZwMakeTemporaryObject

NTSTATUS
ZwMakeTemporaryObject(
IN HANDLE Handle
);

ZwMakeTemporaryObject changes the attributes of an object to make it temporary.

Parameters

Handle
Specifies an open handle for an object.

Return Value

ZwMakeTemporaryObject can return one of the following values:

STATUS_SUCCESS
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

ZwMakeTemporaryObject is a generic routine that operates on any type of object.

Making an object temporary causes the permanent flag of the associated object to be cleared. A temporary object has a name only as long as its handle count is greater than zero. When the handle count reaches zero, the system deletes the object name and adjusts the pointer count for the object appropriately.

Callers of ZwMakeTemporaryObject must be running at IRQL PASSIVE_LEVEL.

See Also

InitializeObjectAttributes, ZwClose, ZwCreateDirectoryObject, ZwCreateFile


ZwMapViewOfSection

NTSTATUS
ZwMapViewOfSection(
IN HANDLE SectionHandle,
 IN HANDLE ProcessHandle,
 IN OUT PVOID *BaseAddress,
 IN ULONG ZeroBits,
 IN ULONG CommitSize,
 IN OUT PLARGE_INTEGER SectionOffset, /* optional */
IN OUT PULONG ViewSize,
 IN SECTION_INHERIT InheritDisposition,
 IN ULONG AllocationType,
 IN ULONG Protect
);

ZwMapViewOfSection maps a view of a section into the virtual address space of a subject process.

Parameters

SectionHandle
Is the handle returned by a successful call to ZwOpenSection.
ProcessHandle
Is the handle of an opened process object, representing the process for which the view should be mapped.
BaseAddress
Points to a variable that will receive the base address of the view. If the initial value of this argument is nonNULL, the view is allocated starting at the specified virtual address rounded down to the next 64-kilobyte address boundary.
ZeroBits
Specifies the number of high-order address bits that must be zero in the base address of the section view. The value of this argument must be less than 21 and is used only when the operating system determines where to allocate the view, as when BaseAddress is NULL.
CommitSize
Specifies the size, in bytes, of the initially committed region of the view. CommitSize is only meaningful for page-file backed sections. For mapped sections, both data and image are always committed at section creation time. This parameter is ignored for mapped files. This value is rounded up to the next host-page-size boundary.
SectionOffset
Points to the offset, in bytes, from the beginning of the section to the view. If this pointer is nonNULL, the given value is rounded down to the next allocation granularity size boundary.
ViewSize
Points to a variable that will receive the actual size, in bytes, of the view. If the value of this parameter is zero, a view of the section will be mapped starting at the specified section offset and continuing to the end of the section. Otherwise, the initial value of this argument specifies the size of the view, in bytes, and is rounded up to the next host page-size boundary.
InheritDispostion
Specifies how the view is to be shared by a child process created with a create process operation. Device and intermediate drivers should set this parameter to zero.
AllocationType
A set of flags that describes the type of allocation to be performed for the specified region of pages.
Protect
Specifies the protection for the region of initially committed pages. Device and intermediate drivers should set this value to PAGE_READWRITE.

Return Value

ZwMapViewOfSection can return one of the following values:

STATUS_SUCCESS
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

Several different views of a section can be concurrently mapped into the virtual address space of a process. Likewise, several different views of a section can be concurrently mapped into the virtual address space of several processes.

If the specified section does not exist or the access requested is not allowed, ZwMapViewOfSection returns an error.

Callers of ZwMapViewOfSection must be running at IRQL PASSIVE_LEVEL.

See Also

ZwOpenSection, ZwUnmapViewOfSection


ZwOpenKey

NTSTATUS
ZwOpenKey(
OUT PHANDLE KeyHandle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes
);

ZwOpenKey opens an existing key in the registry.

Parameters

KeyHandle
Points to a returned handle for the key specified in ObjectAttributes if this call is successful.
DesiredAccess
Specifies the access rights desired to the key. See ZwCreateKey for a description of possible values for this parameter.
ObjectAttributes
Points to the initialized object attributes of the key being opened. See the description of ZwCreateKey for more information.

Return Value

ZwOpenKey returns STATUS_SUCCESS if the given key was opened. Otherwise, it can return an error status, including the following values:

STATUS_INVALID_HANDLE
STATUS_ACCESS_DENIED

Comments

ZwOpenKey or ZwCreateKey must be called before any of the Zw...Key routines that require an input KeyHandle.

If the specified key does not exist or the DesiredAccess requested is not allowed, ZwOpenKey returns an error status, and the KeyHandle remains invalid.

ZwOpenKey ignores the security information in the input ObjectAttributes. Access rights for a key object can be set only when the key is created.

Callers of ZwOpenKey must be running at IRQL PASSIVE_LEVEL.

See Also

InitializeObjectAttributes, ZwCreateKey, ZwDeleteKey, ZwEnumerateKey, ZwEnumerateValueKey, ZwFlushKey, ZwQueryKey, ZwQueryValueKey, ZwSetValueKey


ZwOpenSection

NTSTATUS
ZwOpenSection(
OUT PHANDLE SectionHandle,
 IN ACCESS_MASK DesiredAccess,
 IN POBJECT_ATTRIBUTES ObjectAttributes
);

ZwOpenSection opens a handle for an existing section object.

Parameters

SectionHandle
Points to a variable that will receive the section object handle if this call is successful.
DesiredAccess
Specifies a mask representing the requested access to the object. The set of system-defined DesiredAccess flags relevant to device and intermediate drivers are the following:

DesiredAccess Flags

Meaning

SECTION_MAP_WRITE

A mapped view can be written.

SECTION_MAP_READ

A mapped view can be read.

A caller can specify SECTION_ALL_ACCESS, which sets all of the defined flags ORed with the system-defined STANDARD_RIGHTS_REQUIRED.

ObjectAttributes
Points to the initialized object attributes of the section to be opened.

Return Value

ZwOpenSection can return one of the following values:

STATUS_SUCCESS
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

If the specified section does not exist or the access requested is not allowed, the operation fails.

Callers of ZwOpenSection must be running at PASSIVE_LEVEL.

See Also

InitializeObjectAttributes, ZwMapViewOfSection, ZwUnmapViewOfSection


ZwQueryInformationFile

NTSTATUS
ZwQueryInformationFile(
IN HANDLE FileHandle,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 OUT PVOID FileInformation,
 IN ULONG Length,
 IN FILE_INFORMATION_CLASS FileInformationClass
);

ZwQueryInformationFile returns various kinds of information about a given file object.

Parameters

FileHandle
Is the handle returned by a successful call to ZwCreateFile.
IoStatusBlock
Points to a variable that receives the final completion status and information about the operation.
FileInformation
Points to a caller-allocated buffer or variable that receives the desired information about the file. The contents of FileInformation are defined by the FileInformationClass parameter, described later.
Length
Specifies the size in bytes of FileInformation, which the caller should set according to the given FileInformationClass.
FileInformationClass
Specifies the type of information to be returned about the file. Device and intermediate drivers, can specify any of the following:

FileInformationClass Value

Meaning

FileBasicInformation

Return FILE_BASIC_INFORMATION about the file. The caller must have opened the file with the DesiredAccess FILE_READ_ATTRIBUTES flag set.

FileStandardInformation

Return FILE_STANDARD_INFORMATION about the file. The caller can query this information as long as the file is open, without any particular requirements for DesiredAccess.

FilePositionInformation

Return FILE_POSITION_INFORMATION about the file. The caller must have opened the file with the DesiredAccess FILE_READ_DATA or FILE_WRITE_DATA flag set and with either of the CreateOptions FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT.

FileAlignmentInformation

Return FILE_ALIGNMENT_INFORMATION about the file. The caller can query this information as long as the file is open, without any particular requirements for DesiredAccess. This information is useful if the file was opened with the CreateOptions FILE_NO
_INTERMEDIATE_BUFFERING flag set.

Return Value

ZwQueryInformationFile returns STATUS_SUCCESS or an appropriate error status. It also returns the number of bytes actually written to the given FileInformation buffer in the Information member of IoStatusBlock.

Comments

ZwQueryInformationFile returns information about the given file. Note that it returns zero in any member of a FILE_XXX_INFORMATION structure that is not supported by a particular device or file system. For example, the FAT file system does not support file-creation times, so ZwQueryInformationFile sets the CreationTime member of returned FILE_BASIC_INFORMATION to zero for files on a FAT partition.

Callers of ZwQueryInformationFile must be running at IRQL PASSIVE_LEVEL.

See Also

FILE_ALIGNMENT_INFORMATION, FILE_BASIC_INFORMATION, FILE_POSITION_INFORMATION, FILE_STANDARD_INFORMATION, ZwCreateFile, ZwSetInformationFile


ZwQueryKey

NTSTATUS
ZwQueryKey(
IN HANDLE KeyHandle,
 IN KEY_INFORMATION_CLASS KeyInformationClass,
 OUT PVOID KeyInformation,
 IN ULONG Length,
 OUT PULONG ResultLength
);

ZwQueryKey provides data about the class of a key, and the number and sizes of its subkeys.

Parameters

KeyHandle
Is the handle, returned by a successful call to ZwCreateKey or ZwOpenKey, of the key to be queried.
Index
Specifies the zero-based index of a subkey for which the key information is requested.
KeyInformationClass
Specifies the type of information returned in the buffer as one of the following:

KeyBasicInformation
KeyFullInformation
KeyNodeInformation

KeyInformation
Points to a caller-allocated buffer to receive the requested data.
Length
Is the size in bytes of the KeyInformation buffer, which the caller should set according to the given KeyInformationClass.
ResultLength
Points to number of bytes actually returned to KeyInformation or, if the input Length is too small, points to the number of bytes required for the available information.

Return Value

ZwQueryKey returns STATUS_SUCCESS if it returned the requested information in the KeyInformation buffer. Otherwise, ZwQueryKey can return one of the following values:

STATUS_BUFFER_OVERFLOW
STATUS_INVALID_PARAMETER

Comments

The KeyHandle passed to ZwQueryKey must have been opened with the KEY_QUERY_KEY DesiredAccess flag set for this call to succeed. See ZwCreateKey for a description of possible values for DesiredAccess.

ZwQueryKey returns information about the size of the value entries, the number of subkeys, the length of their names, and the size of their value entries that its caller can use to allocate buffers for registry data.

For example, a successful caller of ZwQueryKey might allocate a buffer for a subkey, call ZwEnumerateKey to get the name of the subkey, and pass that name to an Rtl..Registry routine.

Callers of ZwQueryKey must be running at IRQL PASSIVE_LEVEL.

See Also

KEY_BASIC_INFORMATION, KEY_FULL_INFORMATION, KEY_NODE_INFORMATION, ZwClose, ZwEnumerateKey, ZwOpenKey


ZwQueryValueKey

NTSTATUS
ZwQueryValueKey(
IN HANDLE KeyHandle,
 IN PUNICODE_STRING ValueName,
 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
 OUT PVOID KeyValueInformation,
 IN ULONG Length,
 OUT PULONG ResultLength
);

ZwQueryValueKey returns the value entries for an open registry key.

Parameters

KeyHandle
Is the handle, returned by a successful call to ZwCreateKey or ZwOpenKey, of key for which value entries are to be read.
ValueName
Points to the name of the value entry for which the data is requested.
KeyValueInformationClass
Specifies the type of information requested as one of the following:

KeyValueBasicInformation
KeyValueFullInformation
KeyValuePartialInformation

KeyValueInformation
Points to a caller-allocated buffer to receive the requested data.
Length
Is the size in bytes of the KeyValueInformation buffer, which the caller should set according to the given KeyValueInformationClass.
ResultLength
Points to number of bytes actually returned to KeyValueInformation or, if the input Length is too small, points to the number of bytes required for the available information.

Return Value

ZwQueryValueKey returns STATUS_SUCCESS if it returned the requested information in the KeyValueInformation buffer. Otherwise, ZwQueryValueKey can return one of the following values:

STATUS_BUFFER_OVERFLOW
STATUS_INVALID_PARAMETER
STATUS_OBJ_NAME_NOT_FOUND

Comments

The KeyHandle passed to ZwQueryValueKey must have been opened with the KEY_QUERY_VALUE DesiredAccess flag set for this call to succeed. See ZwCreateKey for a description of possible values for DesiredAccess.

Callers of ZwQueryValueKey must be running at IRQL PASSIVE_LEVEL.

See Also

KEY_VALUE_BASIC_INFORMATION, KEY_VALUE_FULL_INFORMATION, KEY_VALUE_PARTIAL_INFORMATION, ZwCreateKey, ZwEnumerateValueKey, ZwOpenKey


ZwReadFile

NTSTATUS
ZwReadFile(
IN HANDLE FileHandle,
 IN HANDLE Event, /* optional */
IN PIO_APC_ROUTINE ApcRoutine, /* optional */
IN PVOID ApcContext, /* optional */
OUT PIO_STATUS_BLOCK IoStatusBlock,
 OUT PVOID Buffer,
 IN ULONG Length,
 IN PLARGE_INTEGER ByteOffset, /* optional */
IN PULONG Key /* optional */
);

Data can be read from an opened file with ZwReadFile.

Parameters

FileHandle
Is the handle returned by a successful call to ZwCreateFile.
Event
Is an optional handle for an event to be set to the Signaled state when the read operation completes. Device and intermediate drivers should set this parameter to NULL.
ApcRoutine
Device and intermediate drivers should set this pointer to NULL.
ApcContext
Device and intermediate drivers should set this pointer to NULL.
IoStatusBlock
Points to a variable that receives the final completion status and information about the requested read operation.
Buffer
Points to a caller-allocated buffer to receive the data read from the file.
Length
Specifies the size in bytes of the given Buffer. A successful call to ZwReadFile returns the given number of bytes from the file, unless this routine reaches the end of file first.
ByteOffset
Points to a variable specifying the starting byte offset within the file at which to begin the read operation. If an attempt is made to start reading beyond the end of file, ZwReadFile returns an error.

If the call to ZwCreateFile set either of the CreateOptions flags FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT, the I/O Manager maintains the current file position. If so, the caller of ZwReadFile can specify that the current file position offset be used instead of an explicit ByteOffset value in either of the following ways:

  • Specify the system-defined value FILE_USE_FILE_POINTER_POSITION.

  • Pass a NULL pointer for ByteOffset.

ZwReadFile updates the current file position by adding the number of bytes read when it completes the read operation, if it is using the current file position maintained by the I/O Manager.

Even when the I/O Manager is maintaining the current file position, the caller can reset this position by passing an explicit ByteOffset value to ZwReadFile. Doing this automatically changes the current file position to that ByteOffset, performs the read, and then updates the position according to the number of bytes actually read. This technique gives the caller atomic seek-and-read service.

Key
Device and intermediate drivers should set this pointer to NULL.

Return Value

ZwReadFile either returns STATUS_SUCCESS or an appropriate error status. The number of bytes actually read from the file is returned in the Information member of the IoStatusBlock.

Comments

Callers of ZwReadFile must have already called ZwCreateFile with the DesiredAccess flag FILE_READ_DATA set, either explicitly or by setting this flag with GENERIC_READ.

If the preceding call to ZwCreateFile set the CreateOptions flag FILE_NO_INTERMEDIATE_BUFFERING, certain restrictions on the parameters to ZwReadFile are enforced. See ZwCreateFile for specifics.

ZwReadFile begins reading from the given ByteOffset or the current file position into the given Buffer. It terminates the read operation under one of the following conditions:

  • The buffer is full because the number of bytes specified by the Length parameter has been read. Therefore, no more data can be placed into the buffer without an overflow.

  • The end of file is reached during the read operation, so there is no more data in the file to be transferred into the buffer.

If the caller opened the file with the DesiredAccess SYNCHRONIZE flag set, the caller can wait on this routine’s setting the given FileHandle to the Signaled state.

Callers of ZwReadFile must be running at IRQL PASSIVE_LEVEL.

See Also

KeInitializeEvent, ZwCreateFile, ZwQueryInformationFile, ZwSetInformationFile, ZwWriteFile


ZwSetInformationFile

NTSTATUS
ZwSetInformationFile(
IN HANDLE FileHandle,
 OUT PIO_STATUS_BLOCK IoStatusBlock,
 IN PVOID FileInformation,
 IN ULONG Length,
 IN FILE_INFORMATION_CLASS FileInformationClass
);

ZwSetInformationFile changes various kinds of information about a given file object.

Parameters

FileHandle
Is the handle returned by a successful call to ZwCreateFile.
IoStatusBlock
Points to a variable that receives the final completion status and information about the operation.
FileInformation
Points to a buffer or variable containing the information to be set for the file. The contents of FileInformation are defined by the FileInformationClass parameter, described later. Setting any member of the structure in this buffer or variable to zero tells ZwSetInformationFile to leave the current information about the file for that member unchanged.
Length
Specifies the size in bytes of FileInformation, which the caller should set according to the given FileInformationClass.
FileInformationClass
Specifies the type of information to be reset for the file. Device and intermediate drivers, can specify any of the following:

FileInformationClass Value

Meaning

FileBasicInformation

Change FILE_BASIC_INFORMATION about the file. The caller must have opened the file with the DesiredAccess FILE_WRITE_ATTRIBUTES flag set.

FileDispositionInformation

Usually, sets DeleteFile in FILE_DISPOSITION_INFORMATION to TRUE, so the file can be deleted when ZwClose is called to release the last open handle for the file object. The caller must have opened the file with the DesiredAccess DELETE flag set.

FilePositionInformation

Change the current FILE_POSITION_INFORMATION for the file. The caller must have opened the file with the DesiredAccess FILE_READ_DATA or FILE_WRITE_DATA flag set and with either of the CreateOptions FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT.

FileEndOfFileInformation

Change the current FILE_END_OF_FILE_INFORMATION for the file: either truncate or extend the amount of valid data in the file by moving the current end-of-file position. The caller must have opened the file with the DesiredAccess FILE_WRITE_DATA flag set.

Return Value

ZwSetInformationFile returns STATUS_SUCCESS or an appropriate error status. It also returns the number of bytes set on the file in the Information member of IoStatusBlock.

Comments

ZwSetInformationFile changes information about a file. It ignores any member of a FILE_XXX_INFORMATION structure that is not supported by a particular device or file system. For example, the FAT file system does not support file-creation times, so ZwSetInformationFile ignores the CreationTime member of the FILE_BASIC_INFORMATION structure when it is called to change basic file information for files on a FAT partition.

A caller that sets FileInformationClass to FileDispositionInformation can pass the FileHandle subsequently to ZwClose but to no other Zw..File routine. On return from ZwSetInformationFile, the file has been marked for deletion. It is a programming error to attempt any subsequent operation on the open file except closing it.

If the the caller sets FileInformationClass to FilePositionInformation and the preceding call to ZwCreateFile set the CreateOptions flag FILE_NO_INTERMEDIATE_BUFFERING, certain restrictions on the input FILE_POSITION_INFORMATION CurrentByteOffset are enforced. See ZwCreateFile for specifics.

If the caller sets FileInformationClass to FileEndOfFileInformation and the input FILE_END_OF_FILE_INFORMATION EndOfFile value specifies an offset beyond the current end-of-file mark, ZwSetInformationFile extends the file and writes pad bytes of zeroes between the old and new end-of-file marks.

Callers of ZwSetInformationFile must be running at IRQL PASSIVE_LEVEL.

See Also

FILE_BASIC_INFORMATION, FILE_DISPOSITION_INFORMATION, FILE_END_OF_FILE_INFORMATION, FILE_POSITION_INFORMATION, ZwCreateFile, ZwQueryInformationFile


ZwSetInformationThread

NTSTATUS
ZwSetInformationThread(
IN HANDLE ThreadHandle,
 IN THREADINFOCLASS ThreadInformationClass,
 IN PVOID ThreadInformation,
 IN ULONG ThreadInformationLength
);

ZwSetInformationThread can be called to set the priority of a thread for which the caller has a handle.

Parameters

ThreadHandle
Is the open handle for a thread.
ThreadInformationClass
Is one of the system-defined values ThreadPriority or ThreadBasePriority.
ThreadInformation
Points to a variable specifying the information to be set. If ThreadInformationClass is ThreadPriority, this value must be > LOW_PRIORITY and <= HIGH_PRIORITY. If ThreadInformationClass is ThreadBasePriority, this value must fall within the system’s valid base priority range and the original priority class for the given thread: that is, if a thread’s priority class is variable, that thread’s base priority cannot be reset to a real-time priority value and vice versa.
ThreadInformationLength
Is the size in bytes of ThreadInformation, which must be at least sizeof(KPRIORITY).

Return Value

ZwSetInformationThread returns STATUS_SUCCESS or an error status, such as STATUS_INFO_LENGTH_MISMATCH or STATUS_INVALID_PARAMETER.

Comments

ZwSetInformationThread can be called by higher-level drivers to set the priority of a thread for which they have a handle.

The caller must have THREAD_SET_INFORMATION access rights for the given thread in order to call this routine.

Usually, device and intermediate drivers that set up driver-created threads call KeSetBasePriorityThread or KeSetPriorityThread from their driver-created threads, rather than ZwSetInformationThread. However, a driver can call ZwSetInformationThread to raise the priority of a driver-created thread before that thread is run.

Callers of ZwSetInformationThread must be running at IRQL PASSIVE_LEVEL.

See Also

KeSetBasePriorityThread, KeSetPriorityThread, PsCreateSystemThread


ZwSetValueKey

NTSTATUS
ZwSetValueKey(
IN HANDLE KeyHandle,
 IN PUNICODE_STRING ValueName,
 IN ULONG TitleIndex, /* optional */
IN ULONG Type,
 IN PVOID Data,
 IN ULONG DataSize
);

ZwSetValueKey replaces or creates a value entry for a key in the registry.

Parameters

KeyHandle
Is the handle, returned by a successful call to ZwCreateKey or ZwOpenKey, of key for which a value entry is to be written in the registry.
ValueName
Points to the name of the value entry for which the data is to be written. This parameter can be a NULL pointer if the value entry has no name. If a name string is specified and the given name is not unique relative to its containing key, the data for an existing value entry is replaced.
TitleIndex
Device and intermediate drivers should set this parameter to zero.
Type
Specifies the type of the data to be written for ValueName. System-defined types include the following:

REG_XXX Type

Value

REG_BINARY

Binary data in any form

REG_DWORD

A 4-byte numerical value

REG_DWORD_LITTLE_ENDIAN

A 4-byte numerical value whose least significant byte is at the lowest address, which is identical to type REG_DWORD

REG_DWORD_BIG_ENDIAN

A 4-byte numerical value whose least significant byte is at the highest address

REG_EXPAND_SZ

A zero-terminated Unicode string, containing unexpanded references to environment variables, such as “%PATH%”

REG_LINK

A Unicode string naming a symbolic link; this type is irrelevant to device and intermediate drivers.

REG_MULTI_SZ

An array of zero-terminated strings, terminated by another zero

REG_NONE

Data with no particular type

REG_SZ

A zero-terminated Unicode string

REG_RESOURCE_LIST

A device driver’s list of hardware resources, used by the driver or one of the physical devices it controls, in the \ResourceMap tree

REG_RESOURCE_REQUIREMENTS
_LIST

A device driver’s list of possible hardware resources it or one of the physical devices it controls can use, from which the system writes a subset into the \ResourceMap tree

REG_FULL_RESOURCE
_DESCRIPTOR

A list of hardware resources that a physical device is using, detected and written into the \HardwareDescription tree by the system

Device drivers need not, and should not attempt to, call ZwSetValueKey directly to write value entries in a subkey of the \Registry..\ResourceMap key. Only the system can write value entries to the \Registry..\HardwareDescription tree.

Data
Points to a caller-allocated buffer containing the data for the value entry.
DataSize
Specifies the size in bytes of the Data buffer. If Type is any of the REG_XXX_SZ, this value must include the terminating zero(s).

Return Value

ZwSetValueKey can return one of the following values:

STATUS_SUCCESS
STATUS_ACCESS_DENIED
STATUS_INVALID_HANDLE

Comments

The KeyHandle passed to ZwSetValueKey must have been opened with the KEY_SET_VALUE DesiredAccess flag set for this call to succeed. See ZwCreateKey for a description of possible values for DesiredAccess.

If the given key has no existing value entry with a name matching the given ValueName, ZwSetValueKey creates a new value entry with the given name. If a matching value entry name exists, this routine overwrites the original value entry for the given ValueName. Thus, ZwSetValueKey preserves a unique name for each value entry of any particular key. While each value entry name must be unique to its containing key, many different keys in the registry can have value entries with the same names.

Callers of ZwSetValueKey must be running at IRQL PASSIVE_LEVEL.

See Also

HalAssignSlotResources, IoAssignResources, IoQueryDeviceDescription, IoReportResourceUsage, ZwClose, ZwCreateKey, ZwFlushKey, ZwOpenKey


ZwUnmapViewOfSection

NTSTATUS
ZwUnmapViewOfSection(
IN HANDLE ProcessHandle,
 IN PVOID BaseAddress
);

ZwUnmapViewOfSection unmaps a view of a section from the virtual address space of a subject process.

Parameters

ProcessHandle
Specifies an open handle of the process that was passed in a preceding call to ZwMapViewOfSection.
BaseAddress
Points to the base virtual address of the view that is to be unmapped. This value can be any virtual address within the view.

Return Value

ZwUnmapViewOfSection can return one of the following values:

STATUS_NORMAL
STATUS_INVALID_PARAMETER
STATUS_NO_ACCESS

Comments

The entire view of the section specified by the BaseAddress parameter is unmapped from the virtual address space of the specified process.

The virtual address region occupied by the view is no longer reserved and is available to map other views or private pages. If the view was also the last reference to the underlying section, then all committed pages in the section are decommitted and the section is deleted.

Callers of ZwUnmapViewOfSection must be running at IRQL PASSIVE_LEVEL.

See Also

ZwMapViewOfSection, ZwOpenSection


ZwWriteFile

NTSTATUS
ZwWriteFile(
IN HANDLE FileHandle,
 IN HANDLE Event, /* optional */
IN PIO_APC_ROUTINE ApcRoutine, /* optional */
IN PVOID ApcContext, /* optional */
OUT PIO_STATUS_BLOCK IoStatusBlock,
 IN PVOID Buffer,
 IN ULONG Length,
 IN PLARGE_INTEGER ByteOffset, /* optional */
IN PULONG Key /* optional */
);

Data can be written to an open file with ZwWriteFile.

Parameters

FileHandle
Is the handle returned by a successful call to ZwCreateFile.
Event
Is an optional handle for an event to be set to the Signaled state when the write operation completes. Device and intermediate drivers should set this parameter to NULL.
ApcRoutine
Device and intermediate drivers should set this pointer to NULL.
ApcContext
Device and intermediate drivers should set this pointer to NULL.
IoStatusBlock
Points to a variable that receives the final completion status and information about the requested write operation.
Buffer
Points to a caller-allocated buffer containing the data to be written to the file.
Length
Specifies the size in bytes of the given Buffer. A successful call to ZwWriteFile transfers the given number of bytes to the file. If necessary, the length of the file is extended.
ByteOffset
Points to a variable specifying the starting byte offset within the file at which to begin the write operation. If a given Length and ByteOffset specify a write operation past the current end-of-file mark, ZwWriteFile automatically extends the file and updates the end-of-file mark; any bytes that are not explicitly written between such old and new end-of-file marks are defined to be zero.

If the call to ZwCreateFile set only the DesiredAccess flag FILE_APPEND_DATA, ByteOffset is ignored. Data in the given Buffer, for Length bytes, is written starting at the current end of file.

If the call to ZwCreateFile set either of the CreateOptions flags FILE_SYNCHRONOUS_IO_ALERT or FILE_SYNCHRONOUS_IO_NONALERT, the I/O Manager maintains the current file position. If so, the caller of ZwWriteFile can specify that the current file position offset be used instead of an explicit ByteOffset value in either of the following ways:

  • Specify the system-defined value FILE_USE_FILE_POINTER_POSITION.

  • Pass a NULL pointer for ByteOffset.

ZwWriteFile updates the current file position by adding the number of bytes written when it completes the write operation, if it is using the current file position maintained by the I/O Manager.

Even when the I/O Manager is maintaining the current file position, the caller can reset this position by passing an explicit ByteOffset value to ZwWriteFile. Doing this automatically changes the current file position to that ByteOffset, performs the write, and then updates the position according to the number of bytes actually written. This technique gives the caller atomic seek-and-write service.

It is also possible to cause a write to start at the current end of file by specifying FILE_WRITE_TO_END_OF_FILE for the ByteOffset parameter even if the I/O Manager is not maintaining the current file position.

Key
Device and intermediate drivers should set this pointer to NULL.

Return Value

ZwWriteFile either returns STATUS_SUCCESS or an appropriate error status. The number of bytes actually written to the file is returned in the Information member of IoStatusBlock.

Comments

Callers of ZwWriteFile must have already called ZwCreateFile with the DesiredAccess flags FILE_WRITE_DATA and/or FILE_APPEND_DATA set, either explicitly or by setting these flags with GENERIC_WRITE. Note that having only FILE_APPEND_DATA access to a file does not allow the caller to write anywhere in the file except at the current end-of-file mark, while having FILE_WRITE_DATA access to a file does not preclude the caller from writing to or beyond the end of a file.

If the preceding call to ZwCreateFile set the CreateOptions flag FILE_NO_INTERMEDIATE_BUFFERING, certain restrictions on the parameters to ZwWriteFile are enforced. See ZwCreateFile for specifics.

ZwWriteFile begins writing data from the given Buffer at the given ByteOffset in the file, at the current file position within the file, or at the end-of-file mark. It terminates the write operation when it has written Length bytes to the file, extending the length of the file if necessary, and resetting the end-of-file mark.

If the caller opened the file with the DesiredAccess SYNCHRONIZE flag set, the caller can wait on this routine’s setting the given FileHandle to the Signaled state.

Callers of ZwWriteFile must be running at IRQL PASSIVE_LEVEL.

See Also

KeInitializeEvent, ZwCreateFile, ZwQueryInformationFile, ZwReadFile, ZwSetInformationFile


Chapter 12 System Structures

This chapter describes system structures and objects that are parameters to more than one support routine or standard NT driver routine. It also describes some bus-type-specific and device-type-specific configuration structures that the system defines for the convenience of device driver writers.

Other system-defined structures are described in the context of the support routines in preceding chapters, in particular those structures that are relevant only to a single support routine or pair of support routines. For example, see HalGetAdapter in Chapter 3 for a description of the system-defined DEVICE_DESCRIPTION structure, or see IoGetConfigurationInformation in Chapter 4 for a description of the CONFIGURATION_INFORMATION structure.


ANSI_STRING

typedef struct _STRING {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
} STRING *PANSI_STRING;

The STRING structure defines a counted string used for ANSI strings.

Members

Length
The length in bytes of the string stored in Buffer.
MaximumLength
The maximum length in bytes of Buffer.
Buffer
Points to a buffer used to contain a string of characters.

Comments

The STRING structure is used to pass ANSI strings.

If the string is NULL terminated, Length does not include the trailing NULL.

The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine such as RtlUnicodeStringToAnsiString the returned string does not exceed the buffer size.

See Also

OEM_STRING, UNICODE_STRING, RtlAnsiStringToUnicodeSize , RtlAnsiStringToUnicodeString, RtlFreeAnsiString, RtlInitAnsiString, RtlUnicodeStringToAnsiString


CM_EISA_FUNCTION_INFORMATION

typedef struct _CM_EISA_FUNCTION_INFORMATION {
    ULONG CompressedId;
    UCHAR IdSlotFlags1;
    UCHAR IdSlotFlags2;
    UCHAR MinorRevision;
    UCHAR MajorRevision;
    UCHAR Selections[26];
    UCHAR FunctionFlags;
    UCHAR TypeString[80];
    EISA_MEMORY_CONFIGURATION EisaMemory[9];
    EISA_IRQ_CONFIGURATION EisaIrq[7];
    EISA_DMA_CONFIGURATION EisaDma[4];
    EISA_PORT_CONFIGURATION EisaPort[20];
    UCHAR InitializationData[60];
} CM_EISA_FUNCTION_INFORMATION, *PCM_EISA_FUNCTION_INFORMATION;

CM_EISA_FUNCTION_INFORMATION defines detailed EISA configuration information returned by HalGetBusData for the input BusDataType EisaConfiguration, or by HalGetBusDataByOffset for the input BusDataType EisaConfiguration and the Offset zero, assuming the caller-allocated Buffer is of sufficient Length.

Members

CompressedId
The EISA compressed identification of the device at this slot. The value is identical to the CompressedId member of the CM_EISA_SLOT_INFORMATION structure.
IdSlotFlags1
The EISA slot identification flags.
IdSlotFlags2
The EISA slot identification flags.
MinorRevision
Information supplied by the manufacturer.
MajorRevision
Information supplied by the manufacturer.
Selections[26]
The EISA selections for the device.
FunctionFlags
Indicates which of the members has available information. Callers can use the following system-defined masks to determine whether a particular type of configuration information can be or has been returned by HalGetBusData or HalGetBusDataByOffset:

EISA_FUNCTION_ENABLED
EISA_FREE_FORM_DATA
EISA_HAS_PORT_INIT_ENTRY
EISA_HAS_PORT_RANGE
EISA_HAS_DMA_ENTRY
EISA_HAS_IRQ_ENTRY
EISA_HAS_MEMORY_ENTRY
EISA_HAS_TYPE_ENTRY
EISA_HAS_INFORMATION

The EISA_HAS_INFORMATION mask is a combination of the following:

EISA_HAS_PORT_RANGE
EISA_HAS_DMA_ENTRY
EISA_HAS_IRQ_ENTRY
EISA_HAS_MEMORY_ENTRY
EISA_HAS_TYPE_ENTRY

TypeString[80]
Specifies the type of device.
EisaMemory[9]
Describes the EISA device memory configuration information, defined as follows:
typedef struct _EISA_MEMORY_CONFIGURATION {
    EISA_MEMORY_TYPE ConfigurationByte;
    UCHAR DataSize;
    USHORT AddressLowWord;
    UCHAR AddressHighByte;
    USHORT MemorySize;
} EISA_MEMORY_CONFIGURATION, *PEISA_MEMORY_CONFIGURATION;
EisaIrq[7]
Describes the EISA interrupt configuration information, defined as follows:
typedef struct _EISA_IRQ_CONFIGURATION {
    EISA_IRQ_DESCRIPTOR ConfigurationByte;
    UCHAR Reserved;
} EISA_IRQ_CONFIGURATION, *PEISA_IRQ_CONFIGURATION;
EisaDma[4]
Describes the EISA DMA configuration information, defined as follows:
typedef struct _EISA_DMA_CONFIGURATION {
    DMA_CONFIGURATION_BYTE0 ConfigurationByte0;
    DMA_CONFIGURATION_BYTE1 ConfigurationByte1;
} EISA_DMA_CONFIGURATION, *PEISA_DMA_CONFIGURATION;
EisaPort[20]
Describes the EISA device port configuration information, defined as follows:
typedef struct _EISA_PORT_CONFIGURATION {
    EISA_PORT_DESCRIPTOR Configuration;
    USHORT PortAddress;
} EISA_PORT_CONFIGURATION, *PEISA_PORT_CONFIGURATION;
InitializationData[60]
Vendor-supplied, device-specific initialization data, if any.

Comments

The information returned by HalGetBusData or HalGetBusDataByOffset in CM_EISA_FUNCTION_INFORMATION and/or in the CM_EISA_SLOT_INFORMATION header immediately preceding it is read-only.

See Also

CM_EISA_SLOT_INFORMATION, HalGetBusData, HalGetBusDataByOffset


CM_EISA_SLOT_INFORMATION

typedef struct _CM_EISA_SLOT_INFORMATION {
    UCHAR ReturnCode;
    UCHAR ReturnFlags;
    UCHAR MajorRevision;
    UCHAR MinorRevision;
    USHORT Checksum;
    UCHAR NumberFunctions;
    UCHAR FunctionInformation;
    ULONG CompressedId;
} CM_EISA_SLOT_INFORMATION, *PCM_EISA_SLOT_INFORMATION;

CM_EISA_SLOT_INFORMATION defines EISA configuration header information returned by HalGetBusData for the input BusDataType EisaConfiguration, or by HalGetBusDataByOffset for the input BusDataType EisaConfiguration and the Offset zero, assuming the caller-allocated Buffer is of sufficient Length.

Members

ReturnCode
Contains a status code if an error occurs when the EISA BIOS is queried. Possible status codes include the following:

EISA_INVALID_SLOT
EISA_INVALID_FUNCTION
EISA_INVALID_CONFIGURATION
EISA_EMPTY_SLOT
EISA_INVALID_BIOS_CALL

ReturnFlags
The return flags.
MajorRevision
Information supplied by the manufacturer.
MinorRevision
Information supplied by the manufacturer.
Checksum
The checksum value, allowing validation of the configuration data.
NumberFunctions
The number at this slot.
FunctionInformation
Whether there is available CM_EISA_FUNCTION_INFORMATION for this slot.
CompressedId
The EISA compressed identification of the device at this slot. This value is identical to the CompressedId member of the CM_EISA_FUNCTION_INFORMATION structure. This member can be read to determine whether the caller should call HalGetBusData or HalGetBusDataByOffset again with sufficient buffer space to get more detailed CM_EISA_FUNCTION_INFORMATION for a device it supports.

Comments

The information returned by HalGetBusData or HalGetBusDataByOffset in CM_EISA_SLOT_INFORMATION and in CM_EISA_FUNCTION_INFORMATION immediately following it is read-only.

The driver of an EISA device might call HalGetBusData or HalGetBusDataByOffset for each slot on each EISA bus in the system, requesting only CM_EISA_SLOT_INFORMATION in order to find the device(s) it supports by examining the returned CompressedId values. Then, such a driver could allocate sufficient buffer space to call HalGetBusData(ByOffset) again for CM_EISA_SLOT_INFORMATION and CM_EISA_FUNCTION_INFORMATION at slots where its device(s) can be found.

See Also

CM_EISA_FUNCTION_INFORMATION, HalGetBusData, HalGetBusDataByOffset


CM_FLOPPY_DEVICE_DATA

typedef struct _CM_FLOPPY_DEVICE_DATA {
    USHORT Version;
    USHORT Revision;
    CHAR Size[8];
    ULONG MaxDensity;
    ULONG MountDensity;
    //
    // New data fields for version >= 2.0
    //
    UCHAR StepRateHeadUnloadTime;
    UCHAR HeadLoadTime;
    UCHAR MotorOffTime;
    UCHAR SectorLengthCode;
    UCHAR SectorPerTrack;
    UCHAR ReadWriteGapLength;
    UCHAR DataTransferLength;
    UCHAR FormatGapLength;
    UCHAR FormatFillCharacter;
    UCHAR HeadSettleTime;
    UCHAR MotorSettleTime;
    UCHAR MaximumTrackValue;
    UCHAR DataTransferRate;
} CM_FLOPPY_DEVICE_DATA, *PCM_FLOPPY_DEVICE_DATA;

CM_FLOPPY_DEVICE_DATA defines a device-type-specific data record that is stored in the \\Registry\Machine\Hardware\Description tree for a floppy controller if the system can collect this information during the boot process.

Members

Version
The version number of this structure.
Revision
The revision of this structure.
Size[8]
The floppy disk density size.
MaxDensity
The maximum density.
MountDensity
The mount density.
StepRateHeadUnloadTime
The step rate head unload time in milliseconds.
HeadLoadTime
The head load time in milliseconds.
MotorOffTime
The motor off time in seconds.
SectorLengthCode
Indicates the sector size as an exponent in the formula ((2**code) * 128).
SectorPerTrack
The number of sectors per track.
ReadWriteGapLength
The read/write gap length, in bytes.
DataTransferLength
The data transfer length, in bytes, not including the synchronization field.
FormatGapLength
The format gap length, in bytes.
FormatFillCharacter
The format fill character.
HeadSettleTime
The head settle time in milliseconds.
MotorSettleTime
The motor settle time in milliseconds.
MaximumTrackValue
The maximum track number on the media. Track numbers are zero-based values.
DataTransferRate
The value written to the Datarate register before accessing the media.

See Also

IoQueryDeviceDescription, IoReportResourceUsage


CM_FULL_RESOURCE_DESCRIPTOR

typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
    INTERFACE_TYPE InterfaceType;
    ULONG BusNumber;
    CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;

CM_FULL_RESOURCE_DESCRIPTOR defines an array element in the CM_RESOURCE_LIST structure.

Members

InterfaceType
Specifies the bus interface type, which can be one of the following: Internal, Isa, Eisa, MicroChannel, TurboChannel, or PCIBus. However, additional types of buses will be supported in future versions of Windows NT. The upper bound on the types of buses supported is always MaximumInterfaceType.
BusNumber
The system-assigned and zero-based number for this bus of the given InterfaceType.
PartialResourceList
The partial resource list information.

Comments

BusNumber and InterfaceType are redundant information, included so a driver need not append this information when creating a resource list that could possibly span more than one bus.

See Also

CM_RESOURCE_LIST, CM_PARTIAL_RESOURCE_LIST, HalAssignSlotResources, IoAssignResources, IoReportResourceUsage


CM_INT13_DRIVE_PARAMETER

typedef struct _CM_INT13_DRIVE_PARAMETER {
    USHORT DriveSelect;
    ULONG MaxCylinders;
    USHORT SectorsPerTrack;
    USHORT MaxHeads;
    USHORT NumberDrives;
} CM_INT13_DRIVE_PARAMETER, *PCM_INT13_DRIVE_PARAMETER;

CM_INT13_DRIVE_PARAMETER defines a device-type-specific data record that is stored in the \\Registry\Machine\Hardware\Description tree for a disk controller if the system can collect this information during the boot process.

Members

DriveSelect
The drive selected value.
MaxCylinders
The maximum number of cylinders.
SectorsPerTrack
The number of sectors per track.
MaxHeads
The maximum number of heads.
NumberDrives
The number of drives.

See Also

IoQueryDeviceDescription, IoReportResourceUsage


CM_KEYBOARD_DEVICE_DATA

typedef struct _CM_KEYBOARD_DEVICE_DATA {
    USHORT Version;
    USHORT Revision;
    UCHAR Type;
    UCHAR Subtype;
    USHORT KeyboardFlags;
} CM_KEYBOARD_DEVICE_DATA, *PCM_KEYBOARD_DEVICE_DATA;

CM_KEYBOARD_DEVICE_DATA defines a device-type-specific data record that is stored in the \\Registry\Machine\Hardware\Description tree for a keyboard peripheral if the system can collect this information during the boot process.

Members

Version
The version number of this structure.
Revision
The revision of this structure.
Type
The type of the keyboard.
Subtype
The subtype of the keyboard.
KeyboardFlags
Defined by x86 BIOS INT 16h, function 02 as:

Bit

Defined As

7

Insert on.

6

Caps Lock on.

5

Num Lock on.

4

Scroll Lock on.

3

Alt Key is down.

2

Ctrl Key is down.

1

Left shift key is down.

0

Right shift key is down.

See Also

IoQueryDeviceDescription, IoReportResourceUsage


CM_MCA_POS_DATA

typedef struct _CM_MCA_POS_DATA {
    USHORT AdapterId;
    UCHAR PosData1;
    UCHAR PosData2;
    UCHAR PosData3;
    UCHAR PosData4;
} CM_MCA_POS_DATA, *PCM_MCA_POS_DATA;

CM_MCA_POS_DATA defines IBM-compatible MCA POS configuration information for a slot.

See Also

HalGetBusData, HalGetBusDataByOffset


CM_PARTIAL_RESOURCE_DESCRIPTOR

typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
    UCHAR Type;
    UCHAR ShareDisposition;
    USHORT Flags;
    union {

        //
        // Range of port numbers, inclusive. These are physical and
        // bus-relative values, which should be passed, unchanged,
        // to HalTranslateBusAddress to get the mapped logical port
        // range the driver uses to communicate with its device.
        //

        struct {
            PHYSICAL_ADDRESS Start;	    // 8-byte physical address
            ULONG Length;
        } Port;
        //
        // Bus-relative IRQL or vector and affinity, which are returned
        // by IoQueryDeviceDescription or HalGetBusData and passed,
        // unchanged, to HalGetInterruptVector and/or
        // IoReportResourceUsage.
        //

        struct {
            ULONG Level;
            ULONG Vector;
            ULONG Affinity;
        } Interrupt;

        //
        // Range of device memory, inclusive. These are physical and
        // bus-relative values, which should be passed, unchanged,
        // to HalTranslateBusAddress to get the mapped logical address.
        // This, in turn, can be passed to MmMapIoSpace to get the virtual
        // address range that the driver uses to communicate with its device.
        //

        struct {
            PHYSICAL_ADDRESS Start;    // 8-byte physical addresses.
            ULONG Length;
        } Memory;

        //
        // The DMA channel number or MCA-type DMA port for the device.
        //

        struct {
            ULONG Channel;
            ULONG Port;
            ULONG Reserved1;
        } Dma;

        //
        // Device-specific information defined by the system or driver.
        // The DataSize field indicates the size of the data in bytes. The
        // data is located immediately after the Reserved2 member of
        // the structure.
        //

        struct {
            ULONG DataSize;
            ULONG Reserved1;
            ULONG Reserved2;
        } DeviceSpecificData;
    } u;
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;

Each CM_PARTIAL_RESOURCE_DESCRIPTOR defines an element of an array within a CM_PARTIAL_RESOURCE_LIST. Each CM_PARTIAL_RESOURCE_DESCRIPTOR describes only one type of resource used by a driver or device controlled by that driver.

Members

Type
Specifies which of the following is described by this partial descriptor: CmResourceTypePort, CmResourceTypeInterrupt, CmResourceTypeMemory, CmResourceTypeDma, CmResourceTypeDeviceSpecific.
ShareDisposition
Specifies whether or how the particular resource can be shared as one of the following: CmResourceShareDeviceExclusive, CmResourceShareDriverExclusive, CmResourceShareShared.
Flags
Specifies type-dependent information about this descriptor:
  • When Type is set with CmResourceTypePort, can be either of the following: CM_RESOURCE_PORT_MEMORY, CM_RESOURCE_PORT_IO.

  • When Type is set with CmResourceTypeInterrupt, can be either of the following: CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE, CM_RESOURCE_INTERRUPT_LATCHED.

  • When Type is set with CmResourceTypeMemory can be one of the following: CM_RESOURCE_MEMORY_READ_WRITE, CM_RESOURCE_MEMORY_READ_ONLY, CM_RESOURCE_MEMORY_WRITE_ONLY.
u.Port
Describes a range of device ports in I/O space, as the following:
Start
Bus-specific, 8-byte base physical address for the range.
Length
The length, in bytes, of the range.
u.Interrupt
Describes a bus-relative interrupt, as the following:
Level
The IRQL at which the bus interrupts.
Vector
The vector at which the bus interrupts.
Affinity
The set of processors to which bus interrupts are dispatched. Set this to -1 in any partial resource descriptor describing interrupt configuration information that is passed to IoReportResourceUsage.
u.Memory
Describes a bus-relative range of device memory, which is in system memory space, as the following:
Start
Bus-specific, 8-byte base physical address for the range.
Length
Size in bytes of the range.
u.Dma
Describes a bus-relative DMA setting, as one of the following:
Channel
Number of the DMA channel on a system DMA controller that the device can use.
Port
Number of the DMA port that an MCA-type device can use.
u.DeviceSpecificData
Describes how much device-specific data follows immediately after the Reserved2 member, as the following:
DataSize
The size in bytes of the device-specific data area.

Comments

For some examples of DeviceSpecificData, see the CM_XXX_DEVICE_DATA structures also described in this chapter.

For partial resource descriptors with CmResourceTypeInterrupt information, either the Level or Vector member of u.Interrupt is set, depending on the platform and type of bus. The Affinity member should either be left as returned by IoQueryDeviceDescription or be set to -1 when claiming resources in the registry with IoReportResourceUsage.

See Also

CM_PARTIAL_RESOURCE_LIST, HalGetInterruptVector, HalTranslateBusAddress, IoAssignResources, IoQueryDeviceDescription, IoReportResourceUsage, MmMapIoSpace


CM_PARTIAL_RESOURCE_LIST

typedef struct _CM_PARTIAL_RESOURCE_LIST {
    USHORT Version;
    USHORT Revision;
    ULONG Count;
    CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;

Each CM_PARTIAL_RESOURCE_LIST defines a variable-sized array of partial resource descriptors within a CM_FULL_RESOURCE_DESCRIPTOR.

Members

Version
The version number of this structure.
Revision
The revision of this structure.
Count
The number of partial resource descriptors in the following array.
PartialDescriptors[1]
An array of partial resource descriptors.

Comments

A partial resource list is collected from the ARC firmware or constructed by NtDetect. The Configuration Manager transforms this structure into a full resource descriptor when it is about to store it in the registry.

This structure allows drivers for the same types of devices to follow a convention for the ordering of common configuration information, which should be defined on a device by device basis.

See Also

CM_FULL_RESOURCE_DESCRIPTOR, CM_PARTIAL_RESOURCE_DESCRIPTOR


CM_RESOURCE_LIST

typedef struct _CM_RESOURCE_LIST {
    ULONG Count;
    CM_FULL_RESOURCE_DESCRIPTOR List[1];
} CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;

A CM_RESOURCE_LIST is stored in the registry \ResourceMap tree by drivers.

Members

Count
The number of full resource descriptors in the following array.
CM_FULL_RESOURCE_DESCRIPTOR List[1]
An array of full resource descriptors.

Comments

The CM_RESOURCE_LIST, with its nested CM_FULL_RESOURCE_DESCRIPTOR array containing a CM_PARTIAL_RESOURCE_LIST with a nested CM_PARTIAL_RESOURCE_DESCRIPTOR array, is an input parameter to IoReportResourceUsage.

As an alternative, consider calling IoAssignResources.

See Also

CM_FULL_RESOURCE_DESCRIPTOR, IoAssignResources, IoReportResourceUsage


CM_SCSI_DEVICE_DATA

typedef struct _CM_SCSI_DEVICE_DATA {
    USHORT Version;
    USHORT Revision;
    UCHAR HostIdentifier;
} CM_SCSI_DEVICE_DATA, *PCM_SCSI_DEVICE_DATA;

CM_SCSI_DEVICE_DATA defines a device-type-specific data record that is stored in the \\Registry\Machine\Hardware\Description tree for a SCSI HBA if the system can collect this information during the boot process.

Members

Version
The version number of this structure.
Revision
The revision for this structure.
HostIdentifier
The SCSI bus identifier used by the ARC firmware.

See Also

IoQueryDeviceDescription, IoReportResourceUsage


CM_SERIAL_DEVICE_DATA

typedef struct _CM_SERIAL_DEVICE_DATA {
    USHORT Version;
    USHORT Revision;
    ULONG BaudClock;
} CM_SERIAL_DEVICE_DATA, *PCM_SERIAL_DEVICE_DATA;

CM_SERIAL_DEVICE_DATA defines a device-type-specific data record that is stored in the \\Registry\Machine\Hardware\Description tree for a serial controller if the system can collect this information during the boot process.

Members

Version
The version number of this structure.
Revision
The revision of this structure.
BaudClock
The clock baud rate, in MHz, at which data is transferred.

See Also

IoQueryDeviceDescription, IoReportResourceUsage


CONTROLLER_OBJECT

A controller object represents a hardware adapter or controller with homogenous devices that are the actual targets for I/O requests. A controller object can be used to synchronize a device driver’s I/O to the target devices through its hardware adapter/controller.

A controller object is partially opaque. Driver writers must know about a certain field associated with the controller object because their drivers access this field through the controller object pointer returned by IoCreateController. The following field in a controller object is accessible to the creating driver.

Accessible Fields

PVOID ControllerExtension
Points to the controller extension. The structure and contents of the controller extension are driver-defined. The size is driver-determined, specified in the driver’s call to IoCreateController. Usually, drivers maintain common state about I/O operations in the controller extension and device-specific state about I/O for a target device in the corresponding device extension.

Comments

Most driver routines that process IRPs are given a pointer to the target device object. Consequently, device drivers that use controller objects frequently store the controller object pointer returned by IoCreateController in each device extension.

Note that a controller object has no name so it cannot be the target of an I/O request, and higher-level drivers cannot connnect or attach their device objects to a device driver’s controller object.

Undocumented fields within a controller object should be considered inaccessible. Drivers with dependencies on object field locations or access to undocumented fields might not remain portable and interoperable with other drivers over time.

See Also

IoCreateController


DEVICE_OBJECT

A device object represents a logical, virtual, or physical device for which a loaded driver handles I/O requests. Every kernel-mode driver must call IoCreateDevice one or more times from its DriverEntry routine when it initializes to create its device object(s).

A device object is partially opaque. Driver writers must know about certain fields and system-defined symbolic constants associated with device objects because their drivers must access these fields through the device object pointer returned by IoCreateDevice and passed to most standard driver routines. The following fields in device objects are accessible to drivers.

Accessible Fields

PDRIVER_OBJECT DriverObject
Points to the driver object, representing the driver’s loaded image, that was input to the DriverEntry routine.
PDEVICE_OBJECT NextDevice
Points to the next device object, if any, created by the same driver.The I/O Manager updates this list at each successful call to IoCreateDevice. A driver that is being unloaded must walk the list of its device objects and delete them. A driver that re-creates its device objects dynamically also uses this field.
PIRP CurrentIrp
Points to the current IRP if the driver has a StartIo routine whose entry point was set in the driver object and if the driver is currently processing IRP(s). Otherwise, this field is NULL.
ULONG Flags
Device drivers OR this field in their newly created device objects with either of the following system-defined values: DO_BUFFERED_IO or DO_DIRECT_IO. Higher-level drivers OR this field with the same value as the next-lower driver, except possibly for highest-level drivers. While processing transfer requests, removable-media device drivers also OR this field with DO_VERIFY_VOLUME, as well as checking whether this field is already set with DO_VERIFY_VOLUME in the target for a transfer request.
ULONG Characteristics
Set when a floppy, CD-ROM, WORM, or other removable-media device driver calls IoCreateDevice with one of the following values, as appropriate: FILE_REMOVABLE_MEDIA, FILE_READ_ONLY_DEVICE, FILE_FLOPPY_DISKETTE, FILE_WRITE_ONCE_MEDIA.
PVOID DeviceExtension
Points to the device extension. The structure and contents of the device extension are driver-defined. The size is driver-determined, specified in the driver’s call to IoCreateDevice. Most driver routines that process IRPs are given a pointer to the device object so the device extension is usually every driver’s primary global storage area and frequently a driver’s only global storage area for objects, resources, and any state the driver maintains about the I/O requests it handles.
DEVICE_TYPE DeviceType
Set when a driver calls IoCreateDevice as appropriate for the type of underlying device. A driver writer can define a new FILE_DEVICE_XXX with a value in the customer range 32768 to 65535 if none of the system-defined values describes the type of the new device. For a list of the system-defined values, see the FILE_DEVICE_XXX in Chapter 1 of Part II in this manual.
CCHAR StackSize
Specifies the minimum number of stack locations in IRPs to be sent to this driver. IoCreateDevice sets this field to one in newly created device objects; lowest-level drivers can therefore ignore this field. The I/O manager automatically sets the StackSize field in a higher-level driver’s device object to the appropriate value if the driver calls IoAttachDevice or IoAttachDeviceToDeviceStack. Only a higher-level driver that chains itself over another driver with IoGetDeviceObjectPointer must explicitly set the value of StackSize in its own device object(s) to (1 + the StackSize value of the next-lower driver’s device object).
ULONG AlignmentRequirement
Some higher-level drivers, such as a class driver layered over a corresponding port driver, that call IoGetDeviceObjectPointer reset this field in their device objects to the value of the next-lower driver’s device object. Other higher-level drivers set this field at the discretion of the driver designer or leave it as set by the I/O Manager. Each device driver sets this field in its newly created device object(s) to the greater of (the alignment requirement of the device - 1) or (the initialized value of this field), which can be one of the following system-defined values:

FILE_BYTE_ALIGNMENT
FILE_WORD_ALIGNMENT
FILE_LONG_ALIGNMENT
FILE_QUAD_ALIGNMENT
FILE_OCTA_ALIGNMENT
FILE_32_BYTE_ALIGNMENT
FILE_64_BYTE_ALIGNMENT
FILE_128_BYTE_ALIGNMENT
FILE_512_BYTE_ALIGNMENT

Comments

The DeviceType range 0 to 32767 is reserved for use by Microsoft.

Undocumented fields within a device object should be considered inaccessible. Drivers with dependencies on object field locations or access to undocumented fields might not remain portable and interoperable with other drivers over time.

The system-supplied video port driver sets up the fields of the device objects it creates on behalf of video miniport drivers. For more information about these video drivers, see the Graphics Driver Design Guide and the Graphics Driver Design Reference.

The system-supplied SCSI port driver sets up the fields of the device objects it creates on behalf of HBA miniport drivers. For more information about these SCSI drivers, see the Kernel-Mode Driver Design Guide and Part 3 of this manual.

The system-supplied NDIS library sets up the fields of the device objects it creates on behalf of netcard drivers. For more information about NDIS drivers, see the Network Driver Design Guide.

See Also

DRIVER_OBJECT, IoAttachDevice, IoAttachDeviceToDeviceStack, IoCreateDevice, IoDeleteDevice, IoGetDeviceObjectPointer


DRIVER_OBJECT

Each driver object represents the image of a loaded kernel-mode driver. A pointer to the driver object is an input parameter to a driver’s DriverEntry and optional Reinitialize routines and to its Unload routine, if any.

A driver object is partially opaque. Driver writers must know about certain fields of a driver object to initialize a driver and to unload it if the driver is unloadable. The following fields in the driver object are accessible to drivers.

Accessible Fields

PDEVICE_OBJECT DeviceObject
Points to the device object(s) created by the driver. This field is automatically updated when the DriverEntry routine calls IoCreateDevice successfully. If a driver is unloaded, its Unload routine uses this field and the NextDevice field of the DEVICE_OBJECT to call IoDeleteDevice with each device object that the driver created.
PUNICODE_STRING HardwareDatabase
Points to the \Registry\Machine\Hardware path to the hardware configuration information in the registry.
PFAST_IO_DISPATCH FastIoDispatch
Points to a structure defining the driver’s fast I/O entry points. This field is used only by FSDs and network transport drivers.
PDRIVER_INITIALIZE DriverInit
Is the entry point for the DriverEntry routine, which is set up by the I/O Manager. A DriverEntry routine is declared as follows:
NTSTATUS
(*PDRIVER_INITIALIZE) (
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );
PDRIVER_STARTIO DriverStartIo
Is the entry point for the driver’s StartIo routine, if any, which is set by the DriverEntry routine when the driver initializes. If a driver has no StartIo routine, this field is NULL. A StartIo routine is declared as follows:
VOID
(*PDRIVER_STARTIO) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );
PDRIVER_UNLOAD DriverUnload
Is the entry point for the driver’s Unload routine, if any, which is set by the DriverEntry routine when the driver initializes. If a driver has no Unload routine, this field is NULL. An Unload routine is declared as follows:
VOID
(*PDRIVER_UNLOAD) (
    IN PDRIVER_OBJECT DriverObject
    );
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1]
Is an array of one or more entry points for the driver’s Dispatch routines. Each driver must set at least one Dispatch entry point in this array for the IRP_MJ_XXX requests that the driver handles. Any driver can set as many separate Dispatch entry points as the IRP_MJ_XXX codes that the driver handles. Each Dispatch routine is declared as follows:
NTSTATUS
(*PDRIVER_DISPATCH) (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

Comments

Each kernel-mode driver’s initialization routine should be named DriverEntry so the system will load the driver automatically. If this routine’s name is something else, the driver writer must define the name of the initialization routine for the linker; otherwise, the OS loader or I/O Manager cannot find the driver’s transfer address. The names of other standard driver routines can be chosen at the discretion of the driver writer.

A driver must set its Dispatch entry point(s) in the driver object that is passed in to the DriverEntry routine when the driver is loaded. A device driver must set one or more Dispatch entry points for the IRP_MJ_XXX that any driver of the same type of device is required to handle. A higher-level driver must set one or more Dispatch entry points for all the IRP_MJ_XXX that it must pass on to the underlying device driver. Otherwise, a driver is not sent IRPs for any IRP_MJ_XXX for which it does not set up a Dispatch routine in the driver object. For more information about the set of IRP_MJ_XXX that drivers for different types of underlying devices are required to handle, see Part II of this manual.

The DriverEntry routine also sets the driver’s StartIo and/or Unload entry points, if any, in the driver object.

The HardwareDatabase string can be used by device drivers to get hardware configuration information from the registry when the driver is loaded. A driver is given read-only access to this string.

The RegistryPath input to the DriverEntry routine points to the \Registry\Machine\System\CurrentControlSet\Services\DriverName key, where the value entry of DriverName identifies the driver. As for the HardwareDatabase in the input driver object, a driver is given read-only access to this string.

Undocumented fields within a driver object should be considered inaccessible. Drivers with dependencies on object field locations or access to undocumented fields might not remain portable and interoperable with other drivers over time.

See Also

DEVICE_OBJECT, IoAssignResources, IoCreateDevice, IoDeleteDevice, IoQueryDeviceDescription, IoReportResourceUsage


FILE_ALIGNMENT_INFORMATION

typedef struct _FILE_ALIGNMENT_INFORMATION {
    ULONG AlignmentRequirement;
} FILE_ALIGNMENT_INFORMATION;

Members

AlignmentRequirement
Is the buffer alignment required by the underlying device. For a list of system-defined values, see DEVICE_OBJECT.

See Also

DEVICE_OBJECT, ZwQueryInformationFile, ZwSetInformationFile


FILE_BASIC_INFORMATION

typedef struct FILE_BASIC_INFORMATION {
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    ULONG FileAttributes;
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;

Members

CreationTime
The time that the file was created.
LastAccessTime
The last time that the file was accessed.
LastWriteTime
The last time that the file was written to.
ChangeTime
The last time the file was changed.
FileAttributes
The file attributes, which can be any valid combination of the following:

FILE_ATTRIBUTE_READONLY
FILE_ATTRIBUTE_HIDDEN
FILE_ATTRIBUTE_SYSTEM
FILE_ATTRIBUTE_DIRECTORY
FILE_ATTRIBUTE_ARCHIVE
FILE_ATTRIBUTE_NORMAL
FILE_ATTRIBUTE_TEMPORARY
FILE_ATTRIBUTE_ATOMIC_WRITE
FILE_ATTRIBUTE_XACTION_WRITE
FILE_ATTRIBUTE_COMPRESSED
FILE_ATTRIBUTE_HAS_EMBEDDING

Comments

FILE_ATTRIBUTE_NORMAL can neither be set nor returned in combination with any other attributes. All other FileAttributes values override this attribute.

All dates and times are in system-time format. Absolute system time is the number of 100-nanosecond intervals since the start of 1601.

See Also

KeQuerySystemTime, ZwCreateFile, ZwQueryInformationFile, ZwSetInformationFile


FILE_DISPOSITION_INFORMATION

typedef struct _FILE_DISPOSITION_INFORMATION {
    BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION;

Members

DeleteFile
If set to TRUE, delete the file when it is closed.

Comments

The caller must have DELETE access to a given file in order to call ZwSetInformationFile with DeleteFile set to TRUE in this structure. Subsequently, the only legal operation by such a caller is to close the open file handle.

A file marked for deletion is not actually deleted until all open handles for the file object have been closed and the link count for the file is zero.

See Also

ZwClose, ZwSetInformationFile


FILE_END_OF_FILE_INFORMATION

typedef struct _FILE_END_OF_FILE_INFORMATION {
    LARGE_INTEGER EndOfFile;
} FILE_END_OF_FILE_INFORMATION;

Members

EndOfFile
The absolute new end of file position as a byte offset from the start of the file.

Comments

EndOfFile specifies the byte offset to the end of the file. Because this value is zero-based, it actually refers to the first free byte in the file: that is, it is the offset to the byte immediately following the last valid byte in the file.

See Also

ZwQueryInformationFile, ZwSetInformationFile


FILE_FS_DEVICE_INFORMATION

typedef struct FILE_FS_DEVICE_INFORMATION {
    DEVICE_TYPE DeviceType;
    ULONG Characteristics;
} FILE_FS_DEVICE_INFORMATION, *PFILE_FS_DEVICE_INFORMATION;

FILE_FS_DEVICE_INFORMATION provides file system device information about the type of device object associated with a file object.

Members

DeviceType
Set when a driver calls IoCreateDevice as appropriate for the type of underlying device. A driver writer can define a new FILE_DEVICE_XXX with a value in the customer range 32768 to 65535 if none of the system-defined values describes the type of a new device. For a list of the system-defined values, see the FILE_DEVICE_XXX in Chapter 1 of Part II in this manual.
Characteristics
The device characteristics. For a description of relevant values, see DEVICE_OBJECT.

See Also

DEVICE_OBJECT


FILE_FULL_EA_INFORMATION

typedef struct _FILE_FULL_EA_INFORMATION {
    ULONG NextEntryOffset;
    UCHAR Flags;
    UCHAR EaNameLength;
    USHORT EaValueLength;
    CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

FILE_FULL_EA_INFORMATION provides extended attribute information. This structure is used primarily by network drivers.

Members

NextEntryOffset
The offset of the next FILE_FULL_EA_INFORMATION-type entry. This member is zero if no other entries follow this one.
Flags
Can be zero or can be set with FILE_NEED_EA, indicating that the file to which the EA belongs cannot be interpreted without understanding the associated extended attributes.
EaNameLength
The length in bytes of the EaName array. This value does not include a zero-terminator to EaName.
EaValueLength
The length in bytes of each EA value in the array.
EaName
An array of characters naming the EA for this entry.

Comments

This structure is longword-aligned. If a set of FILE_FULL_EA_INFORMATION entries is buffered, NextEntryOffset value in each entry, except the last, falls on a longword boundary.

The value(s) associated with each entry follows the EaName array. That is, an EA’s values are located at EaName + (EaNameLength + 1).

See Also

ZwCreateFile


FILE_OBJECT

To user-mode protected subsystems, a file object represents an open instance of a file, device, directory, or volume. To device and intermediate drivers, a file object usually represents a device object.

A file object is partially opaque. Certain types of drivers, such as FSDs and network transport drivers, use some of the fields of file objects. The following fields in file objects are accessible to drivers.

Accessible Fields

PDEVICE_OBJECT DeviceObject
Points to the device object on which the file is opened.
PVOID FsContext
Points to whatever optional state a driver maintains about the file object; otherwise, NULL.
PVOID FsContext2
Points to whatever additional state a driver maintains about the file object; otherwise, NULL.
UNICODE_STRING FileName
Is the name of the file opened on the device, or the Length of the string is zero if the device represented by DeviceObject is being opened.

Comments

Drivers can use the FsContext fields to maintain driver-determined state about an open file object. A driver cannot use these fields of a file object unless it is accessible in the driver’s I/O stack location of an IRP.

The remaining fields in a file object are opaque. They are reserved for use by the I/O Manager and file systems.

Undocumented fields within a file object should be considered inaccessible. Drivers with dependencies on object field locations or access to undocumented fields might not remain portable and interoperable with other drivers over time.

A higher-level driver that successfully calls IoGetDeviceObjectPointer during initialization is given a pointer to the file object that represents the next-lower driver’s device object in user mode. Such a higher-level driver should save the returned file object pointer. To release its reference to this file object, for example when the driver is being unloaded, the driver must call ObDereferenceObject with this file object pointer.

See Also

DEVICE_OBJECT, IoGetDeviceObjectPointer, ObDereferenceObject


FILE_POSITION_INFORMATION

typedef struct FILE_POSITION_INFORMATION {
    LARGE_INTEGER CurrentByteOffset;
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;

Members

CurrentByteOffset
The byte offset of the current file pointer.

Comments

FILE_READ_DATA or FILE_WRITE_DATA access to the file is required to change this information about the file, and the file must be opened for synchronous I/O.

If the file was opened or created with the FILE_NO_INTERMEDIATE_BUFFERING option, the value of CurrentByteOffset must be an integral multiple of the sector size of the underlying device.

See Also

ZwCreateFile, ZwSetInformationFile


FILE_STANDARD_INFORMATION

typedef struct FILE_STANDARD_INFORMATION {
    LARGE_INTEGER AllocationSize;
    LARGE_INTEGER EndOfFile;
    ULONG NumberOfLinks;
    BOOLEAN DeletePending;
    BOOLEAN Directory;
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;

Members

AllocationSize
The file allocation size in bytes. Usually, this value is a multiple of the sector or cluster size of the underlying physical device.
EndOfFile
The end of file location as a byte offset.
NumberOfLinks
The number of hard links to the file.
DeletePending
The delete pending status. TRUE indicates that a file deletion has been requested.
Directory
The file directory status. TRUE indicates the file object represents a directory.

Comments

EndOfFile specifies the byte offset to the end of the file. Because this value is zero-based, it actually refers to the first free byte in the file: that is, it is the offset to the byte immediately following the last valid byte in the file.

See Also

ZwCreateFile, ZwQueryInformationFile, ZwSetInformationFile


IO_STACK_LOCATION

typedef struct _IO_STACK_LOCATION {
    UCHAR MajorFunction;
    UCHAR MinorFunction;
    UCHAR Flags;
    UCHAR Control;
    //
    // The following Parameters depend on the IRP_MJ_XXX that is set
    // in MajorFunction. This declaration shows examples for IRP_MJ_READ,
    // IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL or, possibly,
    // IRP_MJ_INTERNAL_DEVICE_CONTROL requests, as well as for IRP_MJ_SCSI,
    // which is equivalent to IRP_MJ_INTERNAL_DEVICE_CONTROL.
    //
    union {
        .
        .
        struct {
            ULONG Length;
            ULONG Key;
            LARGE_INTEGER ByteOffset;
        } Read;

        struct {
            ULONG Length;
            ULONG Key;
            LARGE_INTEGER ByteOffset;
        } Write;
        .
        .
        struct {
            ULONG OutputBufferLength;
            ULONG InputBufferLength;
            ULONG IoControlCode;         // IOCTL_XXX
            PVOID Type3InputBuffer;
        } DeviceIoControl;
        .
        .
        struct {
            struct _SCSI_REQUEST_BLOCK *Srb;
        } Scsi;
        .
        .
    } Parameters;
    PDEVICE_OBJECT DeviceObject;
    PFILE_OBJECT FileObject;
    .
    .
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

Each I/O stack location in a given IRP has some common members and some request-type-specific members. The following summarizes the general structure of every stack location.

Members

MajorFunction
Is the IRP_MJ_XXX telling the driver what I/O operation is requested.
MinorFunction
Is a subfunction code for MajorFunction, which is used almost exclusively by file system drivers. SCSI class drivers also set this member for some requests. For more information about SCSI class drivers, see the Kernel-mode Driver Design Guide and Part II of this manual.
Flags
Is set with request-type-specific values and used almost exclusively by file system drivers. However, removable-media device drivers check whether this member is set with SL_OVERRIDE_VERIFY_VOLUME for read requests to determine whether to continue the read operation even if the device object’s Flags is set with DO_VERIFY_VOLUME. Intermediate drivers layered over a removable-media device driver must copy this member into the I/O stack location of the next-lower driver in all incoming IRP_MJ_READ requests.
Control
Drivers can check this member to determine whether it is set with SL_PENDING_RETURNED. Drivers have read-only access to this member.
Parameters.Xxx
Depends on the value of MajorFunction. For more detailed information about which IRP_MJ_XXX different types of drivers must handle, and for the Parameters.Xxx for each IRP_MJ_XXX, see Part II of this manual.
DeviceObject
Is a pointer to the driver-created device object representing the target physical, logical, or virtual device for which this driver is to handle the IRP.
FileObject
Is a pointer to the file object, if any, associated with DeviceObject.

Comments

Every higher-level driver is responsible for setting up the I/O stack location for the next-lower driver in each IRP.

In some cases, a higher-level driver layered over a mass-storage device driver is responsible for splitting up large transfer requests for the underlying device driver. In particular, SCSI class drivers must check the Parameters.Read.Length and Parameters.Write.Length, determine whether the size of the requested transfer exceeds the underlying HBA’s transfer capabilities, and, if so, split the Length of the original request into a sequence of partial transfers to satisfy the original IRP.

A higher-level driver’s call to IoCallDriver sets up the DeviceObject pointer to the next-lower-level driver’s target device object in the I/O stack location of the lower driver. The I/O Manager passes each higher-level driver’s IoCompletion routine a pointer to its own DeviceObject when or if the IoCompletion routine is called on completion of the IRP.

If a higher-level driver allocates IRPs to make requests of its own, its IoCompletion routine is passed a NULL DeviceObject pointer if that driver neither allocates a stack location for itself nor sets up the DeviceObject pointer in its own stack location of the newly allocated IRP.

See Also

IoCallDriver, IoGetCurrentIrpStackLocation, IoGetNextIrpStackLocation, IoSetCompletionRoutine, IoSetNextIrpStackLocation, IO_STATUS_BLOCK, IRP


IO_STATUS_BLOCK

typedef struct _IO_STATUS_BLOCK {
    NTSTATUS Status;
    ULONG Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

The I/O status block in the IRP is set to indicate the final status of a given request before a driver calls IoCompleteRequest with the IRP.

Members

Status
Is the completion status, either STATUS_SUCCESS if the requested operation was completed successfully or an informational, warning, or error STATUS_XXX value.
Information
Is set to a request-dependent value. For example, on successful completion of a transfer request, this is set to the number of bytes transferred. If a transfer request is completed with another STATUS_XXX, this member is set to zero. See Part II of this manual for more request-specific information.

Comments

Unless a driver completes an IRP with an error from its Dispatch routine for that IRP_MJ_XXX, the lowest-level driver in the chain frequently sets the I/O status block in an IRP to the values that are returned to the original requestor of the I/O operation.

The IoCompletion routine(s) of higher-level drivers usually check the I/O status block in IRPs completed by lower drivers. By design, the I/O status block in an IRP is the only information passed back from the underlying device driver to all higher-level drivers’ IoCompletion routines.

See Also

IO_STACK_LOCATION, IoCompleteRequest, IoSetCompletionRoutine, IRP


IRP

typedef struct _IRP {
    .
    .
    PMDL MdlAddress;
    ULONG Flags;
    union {
        struct _IRP *MasterIrp;
        .
        .
        PVOID SystemBuffer;
    } AssociatedIrp;
    .
    .
    IO_STATUS_BLOCK IoStatus;
    KPROCESSOR_MODE RequestorMode;
    .
    .
    BOOLEAN Cancel;
    KIRQL CancelIrql;
    .
    .
    PDRIVER_CANCEL CancelRoutine;
    PVOID UserBuffer;
    union {
        struct {
        .
        .
        union {
            KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
            struct {
                PVOID DriverContext[4];
            };
        };
        .
        .
        PETHREAD Thread;
        .
        .
        LIST_ENTRY ListEntry;
        .
        .
        } Overlay;
    .
    .
    } Tail;
} IRP, *PIRP;

In addition to the request-specific parameters in each driver’s I/O stack location in an IRP, drivers also can use the following members of the IRP structure for various purposes.

Members

MdlAddress
Points to an MDL describing a user buffer for an IRP_MJ_READ or IRP_MJ_WRITE request if the driver set up its device object(s) for direct I/O. Drivers that handle IRP_MJ_INTERNAL_DEVICE_CONTROL requests also use this field if the I/O control code was defined with METHOD_DIRECT. For more information about IOCTLs, see Part II of this manual.
Flags
File system drivers use this field, which is read-only for all drivers. Network and, possibly, highest-level device drivers also might read this field, which can be set with one or more of the following system-defined masks:

IRP_NOCACHE
IRP_PAGING_IO
IRP_MOUNT_COMPLETION
IRP_SYNCHRONOUS_API
IRP_ASSOCIATED_IRP
IRP_BUFFERED_IO
IRP_DEALLOCATE_BUFFER
IRP_INPUT_OPERATION
IRP_SYNCHRONOUS_PAGING_IO
IRP_CREATE_OPERATION
IRP_READ_OPERATION
IRP_WRITE_OPERATION
IRP_CLOSE_OPERATION
IRP_DEFER_IO_COMPLETION

AssociatedIrp.MasterIrp
Points to the master IRP in an IRP that was created by a highest-level driver’s call to IoMakeAssociatedIrp.
AssociatedIrp.SystemBuffer
Points to a system-space buffer for one of the following: (1) a transfer request to a driver that set up its device object(s) requesting buffered I/O; (2) an IRP_MJ_DEVICE_CONTROL request, (3) an IRP_MJ_INTERNAL_DEVICE_CONTROL request with an I/O control code that was defined with METHOD_BUFFERED. In any case, the underlying device driver usually transfers data to or from this buffer.
IoStatus
Is the I/O status block in which a driver stores status and information before calling IoCompleteRequest.
RequestorMode
Indicates the execution mode of the original requestor of the operation, one of UserMode or KernelMode.
Cancel
If set to TRUE, the IRP either is or should be cancelled.
CancelIrql
Is the IRQL at which a driver is running when IoAcquireCancelSpinLock is called.
CancelRoutine
Is the entry point for a driver-supplied Cancel routine to be called if the IRP is cancelled. NULL indicates that the IRP is not currently cancelable.
UserBuffer
Contains the address of an output buffer if the major function code in the I/O stack location is IRP_MJ_INTERNAL_DEVICE_CONTROL and the I/O control code was defined with METHOD_NEITHER.
Tail.Overlay.DeviceQueueEntry
If IRPs are queued in the device queue associated with the driver’s device object, this field links IRPs in the device queue. These links can be used only while the driver is processing the IRP.
Tail.Overlay.DriverContext
If IRPs are not queued in the device queue associated with the driver’s device object, this field can be used by the driver to store up to four pointers. This field can be used only while the driver owns the IRP.
Tail.Overlay.Thread
Is a pointer to the caller’s thread control block. Higher-level drivers that allocate IRPs for lower-level removable-media drivers must set this field in the IRPs they allocate. Otherwise, the FSD cannot determine which thread to notify if the underlying device driver indicates that the media requires verification.
Tail.Overlay.ListEntry
If a driver manages its own internal queue(s) of IRPs, it uses this field to link one IRP to the next. These links can be used only while the driver is holding the IRP in its queue or is processing the IRP.

Comments

Undocumented members of the IRP are reserved, used only by the I/O Manager or, in some cases, by FSDs.

Each IRP also has one or more I/O stack locations for the driver(s) that process the request. A driver must call IoGetCurrentIrpStackLocation to get a pointer to its own stack location in each IRP. Higher-level drivers must call IoGetNextIrpStackLocation to get a pointer to the next-lower driver’s stack location so the higher-level driver can set it up before calling IoCallDriver with the IRP.

While a higher-level driver might check the value of the Cancel Boolean in an IRP, that driver cannot assume the IRP will be completed with STATUS_CANCELLED by a lower-level driver even if the value is TRUE.

See Also

IoCreateDevice, IoGetCurrentIrpStackLocation, IoGetNextIrpStackLocation, IoSetCancelRoutine, IoSetNextIrpStackLocation, IO_STACK_LOCATION, IO_STATUS_BLOCK


KEY_BASIC_INFORMATION

typedef struct _KEY_BASIC_INFORMATION {
    LARGE_INTEGER LastWriteTime;
    ULONG   TitleIndex;
    ULONG   NameLength;
    WCHAR   Name[1];            // Variable-length string
} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;

KEY_BASIC_INFORMATION defines a subset of the full information available for a registry key.

Members

LastWriteTime
The last time the key or any of its values changed.
TitleIndex
Device and intermediate drivers should ignore this member.
NameLength
The size in bytes of the following name, including the zero-terminating character.
Name
A zero-terminated Unicode string naming the key.

See Also

ZwEnumerateKey, ZwQueryKey


KEY_FULL_INFORMATION

typedef _KEY_FULL_INFORMATION {
    LARGE_INTEGER LastWriteTime;
    ULONG TitleIndex;
    ULONG ClassOffset;
    ULONG ClassLength;
    ULONG SubKeys;
    ULONG MaxNameLen;
    ULONG MaxClassLen;
    ULONG Values;
    ULONG MaxValueNameLen;
    ULONG MaxValueDataLen;
    WCHAR Class[1];
} KEY_FULL_INFORMATION; PKEY_FULL_INFORMATION

KEY_FULL_INFORMATION defines the information available for a registry key, including information about its subkeys and the maximum length for their names and value entries. This information can be used to size buffers to get the names of subkeys and their value entries.

Members

LastWriteTime
The last time the key or any of its values changed.
TitleIndex
Device and intermediate drivers should ignore this member.
ClassOffset
The offset from the start of this structure to the Class member.
ClassLength
The number of bytes in the Class name.
SubKeys
The number of subkeys for the key.
MaxNameLen
The maximum length of any name for a subkey.
MaxClassLen
The maximum length for a Class name.
Values
The number of value entries.
MaxValueNameLen
The maximum length of any value entry name.
MaxValueDataLen
The maximum length of any value entry data field.
Class[1]
A zero-terminated Unicode string naming the class of the key.

See Also

ZwEnumerateKey, ZwQueryKey


KEY_NODE_INFORMATION

typedef struct _KEY_NODE_INFORMATION {
    LARGE_INTEGER LastWriteTime;
    ULONG   TitleIndex;
    ULONG   ClassOffset;
    ULONG   ClassLength;
    ULONG   NameLength;
    WCHAR   Name[1];        // Variable-length string
} KEY_NODE_INFORMATION, *PKEY_NODE_INFORMATION;

KEY_NODE_INFORMATION defines basic information available for a registry (sub)key.

Members

LastWriteTime
The last time the key or any of its values changed.
TitleIndex
Device and intermediate drivers should ignore this member.
ClassOffset
The offset from the start of this structure to the class name string, which is located immediately following the Name string.
ClassLength
The number of bytes in the class name string.
NameLength
The size in bytes of the following name, including the zero-terminating character.
Name
A zero-terminated Unicode string naming the key.

See Also

ZwEnumerateKey, ZwQueryKey


KEY_VALUE_BASIC_INFORMATION

typedef struct _KEY_VALUE_BASIC_INFORMATION {
    ULONG   TitleIndex;
    ULONG   Type;
    ULONG   NameLength;
    WCHAR   Name[1];            // Variable size
} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;

KEY_VALUE_BASIC_INFORMATION defines a subset of the full information available for a value entry of a registry key.

Members

TitleIndex
Device and intermediate drivers should ignore this member.
Type
The system-defined type for the registry value in the Data member, which is one of the following:

REG_XXX Type

Value

REG_BINARY

Binary data in any form

REG_DWORD

A 4-byte numerical value

REG_DWORD_LITTLE_ENDIAN

A 4-byte numerical value whose least significant byte is at the lowest address

REG_DWORD_BIG_ENDIAN

A 4-byte numerical value whose least significant byte is at the highest address

REG_EXPAND_SZ

A zero-terminated Unicode string, containing unexpanded references to environment variables, such as “%PATH%”

REG_LINK

A Unicode string naming a symbolic link. This type is irrelevant to device and intermediate drivers

REG_MULTI_SZ

An array of zero-terminated strings, terminated by another zero

REG_NONE

Data with no particular type

REG_SZ

A zero-terminated Unicode string

REG_RESOURCE_LIST

A device driver’s list of hardware resources, used by the driver or one of the physical devices it controls, in the \ResourceMap tree

REG_RESOURCE
_REQUIREMENTS_LIST

A device driver’s list of possible hardware resources it or one of the physical devices it controls can use, from which the system writes a subset into the \ResourceMap tree

REG_FULL_RESOURCE
_DESCRIPTOR

A list of hardware resources that a physical device is using, detected and written into the \HardwareDescription tree by the system

NameLength
The size in bytes of the following value entry name, including the zero-terminating character.
Name
A zero-terminated Unicode string naming a value entry of the key.

See Also

ZwEnumerateValueKey, ZwQueryValueKey


KEY_VALUE_FULL_INFORMATION

typedef struct _KEY_VALUE_FULL_INFORMATION {
    ULONG   TitleIndex;
    ULONG   Type;
    ULONG   DataOffset;
    ULONG   DataLength;
    ULONG   NameLength;
    WCHAR   Name[1];            // Variable size
} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;

KEY_VALUE_FULL_INFORMATION defines information available for a value entry of a registry key.

Members

TitleIndex
Device and intermediate drivers should ignore this member.
Type
The system-defined type for the registry value(s) following the Name member. For a summary of these types, see KEY_VALUE_BASIC_INFORMATION.
DataOffset
The offset from the start of this structure to the data immediately following the Name string.
DataLength
The number of bytes of registry information for the value entry identified by Name.
NameLength
The size in bytes of the following value entry name, including the zero-terminating character.
Name
A zero-terminated Unicode string naming a value entry of the key.

See Also

KEY_VALUE_BASIC_INFORMATION, ZwEnumerateValueKey, ZwQueryValueKey


KEY_VALUE_PARTIAL_INFORMATION

typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
    ULONG   TitleIndex;
    ULONG   Type;
    ULONG   DataLength;
    UCHAR   Data[1];            // Variable size
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;

KEY_VALUE_PARTIAL_INFORMATION defines a subset of the value information available for a value entry of a registry key.

Members

TitleIndex
Device and intermediate drivers should ignore this member.
Type
The system-defined type for the registry value in the Data member. For a summary of these types, see KEY_VALUE_BASIC_INFORMATION.
DataLength
The size in bytes of the Data member.
Data
A value entry of the key.

See Also

KEY_VALUE_BASIC_INFORMATION, ZwEnumerateValueKey, ZwQueryValueKey


OEM_STRING

typedef struct _STRING {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
} STRING *POEM_STRING;

The STRING structure defines a counted string used for OEM strings.

Members

Length
The length in bytes of the string stored in Buffer.
MaximumLength
The maximum length in bytes of Buffer.
Buffer
Points to a buffer used to contain a string of characters.

Comments

The STRING structure is used to pass OEM strings.

If the string is NULL terminated, Length does not include the trailing NULL.

See Also

ANSI_STRING, UNICODE_STRING, RtlAnsiStringToUnicodeSize , RtlAnsiStringToUnicodeString, RtlFreeAnsiString, RtlInitAnsiString, RtlUnicodeStringToAnsiString


PCI_COMMON_CONFIG

typedef struct _PCI_COMMON_CONFIG {
    USHORT VendorID;
    USHORT DeviceID;
    USHORT Command;
    USHORT Status;
    UCHAR RevisionID;
    UCHAR ProgIf;
    UCHAR SubClass;
    UCHAR BaseClass;
    UCHAR CacheLineSize;
    UCHAR LatencyTimer;
    UCHAR HeaderType;
    UCHAR BIST;

    union {
         struct _PCI_HEADER_TYPE_0 {
             ULONG BaseAddresses[PCI_TYPE0_ADDRESSES];
             ULONG Reserved1[2];
             ULONG ROMBaseAddress;
             ULONG Reserved2[2];
             UCHAR InterruptLine;
             UCHAR InterruptPin;
             UCHAR MinimumGrant;
             UCHAR MaximumLatency;
         } type0;
     } u;
     UCHAR DeviceSpecific[192];
} PCI_COMMON_CONFIG, *PPCI_COMMON_CONFIG;

PCI_COMMON_CONFIG defines standard PCI configuration information returned by HalGetBusData or HalGetBusDataByOffset for the input BusDataType PCIConfiguration, assuming the caller-allocated Buffer is of sufficient Length.

Members

VendorID
Identifies the manufacturer of the device. This must be a value allocated by the PCI SIG.
DeviceID
Identifies the particular device. This value is assigned by the manufacturer.
Command
Accesses the PCI device’s control register. Writing a zero to this register renders the device logically disconnected from the PCI bus except for configuration access. Otherwise, the functionality of the register is device-dependent. Possible system-defined bit encodings for this member include:

PCI_ENABLE_IO_SPACE
PCI_ENABLE_MEMORY_SPACE
PCI_ENABLE_BUS_MASTER
PCI_ENABLE_SPECIAL_CYCLES
PCI_ENABLE_WRITE_AND_VALIDATE
PCI_ENABLE_VGA_COMPATIBLE_PALETTE
PCI_ENABLE_PARITY
PCI_ENABLE_WAIT_CYCLE
PCI_ENABLE_SERR
PCI_ENABLE_FAST_BACK_TO_BACK

Status
Accesses the PCI device’s status register. The functionality of this register is device-dependent. Possible system-defined bit encodings for this member include:

PCI_STATUS_FAST_BACK_TO_BACK // read-only
PCI_STATUS_DATA_PARITY_DETECTED
PCI_STATUS_DEVSEL // 2 bits wide
PCI_STATUS_SIGNALED_TARGET_ABORT
PCI_STATUS_RECEIVED_TARGET_ABORT
PCI_STATUS_RECEIVED_MASTER_ABORT
PCI_STATUS_SIGNALED_SYSTEM_ERROR
PCI_STATUS_DETECTED_PARITY_ERROR

RevisionID
Specifies the revision level of the device described by the DeviceID member. This value is assigned by the manufacturer.
ProgIf
Identifies the register-level programming interface, if any, for the device, according to the PCI classification scheme.
SubClass
Identifies the subtype, if any, of the device, according to the PCI classification scheme.
BaseClass
Identifies type of the device, according to the PCI classification scheme.
CacheLineSize
Contains the system cache line size in 32-bit units. This member is relevant only for PCI busmaster devices. The system determines this value during the boot process.
LatencyTimer
Contains the value of the latency timer in units of PCI bus clocks. This member is relevant only for PCI busmaster devices. The system determines this value during the boot process.
HeaderType
The system ORs the value of this member with PCI_MULTIFUNCTION, if appropriate to the device. The value of this member indicates the PCI_HEADER_TYPE_0 layout that follows.
BIST
Zero indicates that the device does not support built-in self test. Otherwise, the device supports built-in self test according to the PCI standard.
u.type0
Drivers call HalAssignSlotResources to configure these values and to get back the bus-relative values passed to other configuration routines.
DeviceSpecific
Contains any device-specific initialization information that is available.

Comments

Certain members of this structure have read-only values, so attempts to reset them are ignored. These members include the following: VendorID, DeviceID, RevisionID, ProgIf, SubClass, BaseClass, HeaderType, InterruptPin, MinimumGrant, and MaximumLatency.

Other members are provisionally read-only: that is, the system initializes them to their correct values, so drivers can safely treat them as read-only. However, they can be reset if a busmaster driver finds it necessary. These members include the following: CacheLineSize and LatencyTimer.

See Also

HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, HalSetBusData, HalSetBusDataByOffset


PCI_SLOT_NUMBER

typedef struct _PCI_SLOT_NUMBER {
    union {
        struct {
            ULONG DeviceNumber:5;
            ULONG FunctionNumber:3;
            ULONG Reserved:24;
        } bits;
        ULONG AsULONG;
    } u;
} PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER;

PCI_SLOT_NUMBER defines the format of the Slot parameter to the Hal..BusData routines when they are called with the BusDataType value PCIConfiguration.

Members

u.bits
Specifies the particular device on a multifunction adapter at the given slot that is being configured. The DeviceNumber indicates the logical slot number for the adapter; the FunctionNumber indicates the particular device on that adapter.
u.AsULONG
Species the logical slot number of the device being configured.

Comments

Drivers of PCI devices can call HalGetBusData or HalGetBusDataByOffset more than once for the same slot number to get the configuration information for their device(s).

For example, a driver might search for devices it supports on all PCI buses in the machine first, and then call HalGetBusData(ByOffset) again to request more configuration information about devices of interest. Such a driver could code a loop that calls HalGetBusData(ByOffset) with an input Buffer of sufficient Length only to contain enough of the PCI_COMMON_CONFIG to determine the VendorID and DeviceID of each PCI device. After finding the Slot numbers for any promising PCI devices, the driver would call HalGetBusData or HalGetBusDataByOffset one or more times with additional buffer space to get the information needed to configure its device(s).

See Also

HalAssignSlotResources, HalGetBusData, HalGetBusDataByOffset, HalSetBusData, HalSetBusDataByOffset, PCI_COMMON_CONFIG


UNICODE_STRING

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING *PUNICODE_STRING;

The UNICODE_STRING structure is used to define UNICODE strings.

Members

Length
The length in bytes of the string stored in Buffer.
MaximumLength
The maximum length in bytes of Buffer.
Buffer
Points to a buffer used to contain a string of wide characters.

Comments

The STRING structure is used to pass UNICODE strings.

If the string is NULL terminated, Length does not include the trailing NULL.

The MaximumLength is used to indicate the length of Buffer so that if the string is passed to a conversion routine such as RtlUnicodeStringToAnsiString the returned string does not exceed the buffer size.

See Also

OEM_STRING, ANSI_STRING, RtlAnsiStringToUnicodeSize , RtlAnsiStringToUnicodeString, RtlFreeAnsiString, RtlInitAnsiString, RtlUnicodeStringToAnsiString


Chapter 1 IRP Function Codes and IOCTLs

Each driver-specific I/O stack location in every IRP has a major function code (IRP_MJ_XXX) that tells the driver what operation it or the underlying device driver should carry out to satisfy the I/O request. Each Microsoft® Windows NT® kernel-mode driver must set up one or more Dispatch entry points for a required subset of system-defined major function codes that are set in the I/O stack location(s) of IRPs.

The subset of major function codes that a device driver must handle depends on the nature of its device. That is, the IRP_MJ_XXX requests sent to a keyboard driver are necessarily somewhat different from those sent to a disk driver.

Every higher-level driver must set up the appropriate I/O stack location in IRPs for the next-lower-level driver and call IoCallDriver either with each input IRP or with a driver-created IRP if the higher-level driver holds on to the input IRP. Consequently, every intermediate driver must supply a Dispatch entry point for each major function code that the underlying device driver handles. Otherwise, a new intermediate driver will “break the chain” whenever an application or still higher-level driver attempts to send an I/O request, which is valid but unsupported by the new intermediate driver, down to the underlying device driver.

File system drivers also handle a required subset of system-defined IRP_MJ_XXX, some with subordinate IRP_MN_XXX.

This chapter summarizes the basic I/O requests sent to the system device and intermediate drivers so that driver designers can determine which major function codes their new drivers must handle. For the most common IRP_MJ_XXX handled by device and intermediate drivers, this chapter summarizes general information about each request. It also discusses the definition of I/O control codes for device-type-specific, device-specific, and driver-specific device I/O control requests.


1.1 Determining Required I/O Support by Device Object Type

Every kernel-mode driver must set an appropriate value in the Type field of its device objects when they are created. This value determines which IRP_MJ_XXX a device or intermediate driver must handle.

The system device and intermediate drivers set one of the following system-defined constants in the Type fields of their respective device objects:

FILE_DEVICE_BEEP
FILE_DEVICE_CD_ROM
FILE_DEVICE_CONTROLLER
FILE_DEVICE_DISK
FILE_DEVICE_INPORT_PORT
FILE_DEVICE_KEYBOARD
FILE_DEVICE_MIDI_IN
FILE_DEVICE_MIDI_OUT
FILE_DEVICE_MOUSE
FILE_DEVICE_NULL
FILE_DEVICE_PARALLEL_PORT
FILE_DEVICE_PRINTER
FILE_DEVICE_SCANNER
FILE_DEVICE_SERIAL_MOUSE_PORT
FILE_DEVICE_SERIAL_PORT
FILE_DEVICE_SCREEN
FILE_DEVICE_SOUND
FILE_DEVICE_TAPE
FILE_DEVICE_UNKNOWN
FILE_DEVICE_VIDEO
FILE_DEVICE_VIRTUAL_DISK
FILE_DEVICE_WAVE_IN
FILE_DEVICE_WAVE_OUT
FILE_DEVICE_8042_PORT

The FILE_DEVICE_Xxx constants are defined in ntddk.h.

The FILE_DEVICE_DISK specification covers both floppy and fixed-disk devices, as well as disk partitions.

Intermediate drivers usually set the Type fields of their respective device objects to that of the underlying device. For example, the system supplied fault-tolerant disk driver, ftdisk, has device objects of type FILE_DEVICE_DISK; it does not define new FILE_DEVICE_XXX values for the mirror sets, stripe sets, and volume sets it manages.

File system and network drivers set other system-defined FILE_DEVICE_XXX in the Type fields of their respective device objects.

FILE_DEVICE_XXX values in the range 0-32767 are reserved to Microsoft. All driver writers must use one of these system-defined constants for new drivers when the underlying device corresponds to a type in the preceding list.

However, a driver designer can define another FILE_DEVICE_XXX for a new kind of device. Values in the range 32768-65535 are available for Microsoft customers who develop new kinds of kernel-mode drivers for Windows NT.


1.2 Input and Output Parameters for Common I/O Requests

Device drivers handle IRPs set with some or all of the following major function codes:

IRP_MJ_CREATE
IRP_MJ_CLEANUP
IRP_MJ_CLOSE
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_SHUTDOWN
IRP_MJ_DEVICE_CONTROL //with device-type-specific
                      //(public) I/O control codes
IRP_MJ_INTERNAL_DEVICE_CONTROL //with device-specific
                      //or driver-specific I/O control codes

As a general rule, an intermediate driver must handle at least the same IRP_MJ_XXX as the next-lower driver. That is, an intermediate driver must provide a Dispatch entry point for every IRP_MJ_XXX that the I/O Manager or a higher-level driver might send to the underlying device.

The specific operations a driver carries out for a given IRP_MJ_XXX depends somewhat on the underlying device, particularly for IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL requests. However, the I/O Manager defines the parameters and I/O stack location contents for each major function code that it sets in IRPs.

The rest of this section summarizes request-specific information about parameters for the IRP_MJ_XXX that device and intermediate drivers handle, as well as general information about what kinds of drivers must handle each type of request and how they satisfy each type of request. See also Chapter 12 in Part I of this manual for more information about the IRP, I/O stack location, and I/O status block structures.

The input and output parameters described in this section are the function-specific parameters in the IRP.

IRP_MJ_CREATE

Every kernel-mode driver must handle create requests.

Input Parameters

None

Output Parameters

None

When Sent

Receipt of this request indicates that a user-mode protected subsystem, possibly on behalf of an application, has requested a handle for the file object that represents the target device object, or that a higher-level driver is connecting or attaching its device object to the target device object.

Operation

Many device and intermediate drivers merely set STATUS_SUCCESS in the I/O status block of the IRP and complete the create request. However, what a given driver does on receipt of a create request depends on the driver’s design. For example, a driver with pageable-image sections, like the system serial driver, maps in its paged-out code and allocates any resources necessary to handle subsequent I/O requests for the user-mode thread that is attempting to open the device for I/O.

IRP_MJ_CLEANUP

Drivers that have Cancel routines also must handle cleanup requests.

Input Parameters

None

Output Parameters

None

When Sent

Receipt of this request indicates that the handle for the file object, representing the target device object, is being released.

Operation

If the driver’s device objects were set up as exclusive, thereby allowing only a single thread to use the device at any given time, the driver completes every IRP currently queued to the target device object with STATUS_CANCELLED set in each IRP’s I/O status block. Otherwise, the driver must cancel and complete any currently queued IRPs for the holder of the file object handle that is being released. After cancelling outstanding IRPs in the queue, the driver completes the cleanup IRP with STATUS_SUCCESS set in its I/O status block.

IRP_MJ_CLOSE

Every Windows NT driver must handle close requests, with the possible exception of a driver whose device cannot be disabled or removed from the machine without bringing down the system. A disk driver whose device holds the system page file is an example of such a driver. Note that the driver of such a device also cannot be unloaded dynamically.

Input Parameters

None

Output Parameters

None

When Sent

Receipt of this request indicates that the handle of the file object that represents the target device object has been released.

Operation

Many device and intermediate drivers merely set STATUS_SUCCESS in the I/O status block of the IRP and complete the close request. However, what a given driver does on receipt of a close request depends on the driver’s design. In general, a driver should undo whatever actions it takes on receipt of the create request. Device drivers whose device object(s) are exclusive, such as a serial driver, also can reset the hardware on receipt of a close request

IRP_MJ_READ

Every device driver that transfers data from its device to the system must handle read requests, as must any higher-level driver layered over such a device driver.

Input Parameters

The driver’s I/O stack location in the IRP indicates how many bytes to transfer at Parameters.Read.Length.

Some drivers use the value at Parameters.Read.Key to sort incoming read requests into a driver-determined order in the device queue or in a driver-managed internal queue of IRPs. Certain types of drivers also use the value at Parameters.Read.ByteOffset, which indicates the starting offset for the transfer operation.

Output Parameters

Depending on whether the underlying device driver sets up the target device object’s Flags with DO_BUFFERED_IO or with DO_DIRECT_IO, data is transferred into one of the following:

  • The buffer at Irp->AssociatedIrp.SystemBuffer if the driver uses buffered I/O

  • The buffer described by the MDL at Irp->MdlAddress if the underlying device driver uses direct I/O (DMA or PIO)

When Sent

Any time following the successful completion of a create request

Possibly, a user-mode application or Win32® component with a handle for the file object representing the target device object has requested a data transfer from the device. Possibly, a higher-level driver has created and set up the read IRP.

Operation

On receipt of a read request, a higher-level driver sets up the I/O stack location in the IRP for the next-lower driver, or it creates and sets up additional IRP(s) for one or more lower drivers. It can set up its IoCompletion routine, which is optional for the input IRP but required for driver-created IRPs, by calling IoSetCompletionRoutine. Then, the driver passes the request on to the next-lower driver with IoCallDriver.

On receipt of a read request, a device driver transfers data from its device to system memory. The device driver sets the Information field of the I/O status block to the number of bytes transferred when it completes the IRP.

IRP_MJ_WRITE

Every device driver that transfers data from the system to its device must handle write requests, as must any higher-level driver layered over such a device driver.

Input Parameters

The driver’s I/O stack location in the IRP indicates how many bytes to transfer at Parameters.Write.Length.

Some drivers use the value at Parameters.Write.Key to sort incoming write requests into a driver-determined order in the device queue or in a driver-managed internal queue of IRPs. Certain types of drivers also use the value at Parameters.Write.ByteOffset, which indicates the starting offset for the transfer operation.

Depending on whether the underlying device driver sets up the target device object’s Flags with DO_BUFFERED_IO or with DO_DIRECT_IO, data is transferred from one of the following:

  • The buffer at Irp->AssociatedIrp.SystemBuffer if the driver uses buffered I/O

  • The buffer described by the MDL at Irp->MdlAddress if the underlying device driver uses direct I/O (DMA or PIO)

Output Parameters

None

When Sent

Any time following the successful completion of a create request

Possibly, a user-mode application or Win32 component with a handle for the file object representing the target device object has requested a data transfer to the device. Possibly, a higher-level driver has created and set up the write IRP.

Operation

On receipt of a write request, a higher-level driver sets up the I/O stack location in the IRP for the next-lower driver, or it creates and sets up additional IRP(s) for one or more lower drivers. It can set up its IoCompletion routine, which is optional for the input IRP but required for driver-created IRPs, by calling IoSetCompletionRoutine. Then, the driver passes the request on to the next-lower driver with IoCallDriver.

On receipt of a write request, a device driver transfers data from system memory to its device. The device driver sets the Information field of the I/O status block to the number of bytes transferred when it completes the IRP.

IRP_MJ_FLUSH_BUFFERS

Drivers of devices with internal caches for data and drivers that maintain internal buffers for data must handle this request.

Input Parameters

None

Output Parameters

None

When Sent

Receipt of a flush request indicates that the driver should flush the device’s cache or its internal buffer, or, possibly, should discard the data in its internal buffer.

Operation

The driver transfers any data currently cached in the device or held in the driver’s internal buffer(s) before completing the flush request. The driver of an input-only device that buffers data internally might simply discard the currently buffered device data before completing the flush IRP, depending on the nature of its device.

IRP_MJ_SHUTDOWN

Drivers of mass-storage devices that have internal caches for data must handle this request. Drivers of mass-storage devices and intermediate drivers layered over them also must handle this request if an underlying driver maintains internal buffers for data.

Input Parameters

None

Output Parameters

None

When Sent

Receipt of a shutdown request indicates that a file system driver is sending notice that the system is being shut down.

One or more file system drivers can send such a lower-level driver more than one shutdown request when a user logs off or when the system is being shut down for some other reason.

Operation

The driver must complete the transfer of any data currently cached in the device or held in the driver’s internal buffer(s) before completing the shutdown request.

IRP_MJ_DEVICE_CONTROL

Every driver whose device objects are of the same type (see Section 1.1), is required to support this request. Every higher-level driver usually passes these requests on to an underlying device driver. Each device driver of a given type is assumed to support this request and a device-type-specific set of system-defined (also called public) I/O control codes (IOCTL_XXX) that determine what the driver does on receipt of this request.

Input Parameters

Depending on the I/O control code at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP, can be one of the following:

  1. Caller-supplied data in the buffer at Irp->AssociatedIrp.SystemBuffer, together with the buffer’s size in the I/O stack location at Parameters.DeviceIoControl.InputBufferLength and/or a pointer to an additional input buffer in the I/O stack location at Parameters.DeviceIoControl.Type3InputBuffer

  2. Caller-supplied data in the buffer described by the MDL at
    Irp->MdlAddress, with the buffer’s size in the I/O stack location at Parameters.DeviceIoControl.InputBufferLength if the lowest-level driver’s device uses DMA or PIO and if the requested operation requires the transfer of a large amount of data quickly

  3. Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer into which the driver transfers data

Output Parameters

Also determined by the I/O control code in the I/O stack location of the IRP

Data can be written into the buffer at Irp->AssociatedIrp.SystemBuffer or, using DMA or PIO, into the buffer described by the MDL at
Irp->MdlAddress, as long as the transfer does not exceed the buffer’s size.

When Sent

Any time following the successful completion of a create request

A user-mode thread has called the Win32 DeviceIoControl function, or a higher-level kernel-mode driver has set up the request. Possibly, a user-mode driver has called DeviceIoControl, passing in a driver-defined (also called private) I/O control code, to request device- or driver-specific support from a closely coupled, kernel-mode device driver.

Operation

On receipt of a device I/O control request, a higher-level driver usually passes the IRP on to the next-lower driver. However, there are some exceptions to this practice. For example, a class driver that has stored configuration information obtained from the underlying port driver might complete certain IOCTL_XXX requests without passing the IRP down to the corresponding port driver.

On receipt of a device I/O control request, a device driver examines the I/O control code to determine how to satisfy the request. For most public I/O control codes, device drivers transfer a small amount of data to or from the buffer at Irp->AssociatedIrp.SystemBuffer.

IRP_MJ_INTERNAL_DEVICE_CONTROL

In general, any replacement for an existing driver that supports internal device control requests should handle this request. Such a driver also should support at least the same set of internal I/O control codes as the driver it replaces. Otherwise, existing higher-level drivers might not work with the new driver.

Drivers that replace certain lower-level system drivers are required to handle this request. For example, a replacement for the system parallel port driver must continue to support existing parallel class drivers. Note that certain system drivers that handle this request cannot be replaced, in particular, the system-supplied SCSI and video port drivers.

Input Parameters

Depending on the I/O control code at Parameters.DeviceIoControl.IoControlCode in the I/O stack location of the IRP, can be one of the following:

  1. Caller-supplied data in the buffer at Irp->AssociatedIrp.SystemBuffer, together with the buffer’s size in the I/O stack location at Parameters.DeviceIoControl.InputBufferLength and/or a pointer to an additional input buffer in the I/O stack location at Parameters.DeviceIoControl.Type3InputBuffer

  2. Caller-supplied data in the buffer described by the MDL at
    Irp->MdlAddress, with the buffer’s size in the I/O stack location at Parameters.DeviceIoControl.InputBufferLength if the lowest-level driver’s device uses DMA or PIO and if the requested operation requires the transfer of a large amount of data quickly

  3. Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer into which the driver transfers data

Output Parameters

Also determined by the I/O control code in the I/O stack location of the IRP

Data can be written into the buffer at Irp->AssociatedIrp.SystemBuffer or, using DMA or PIO, into the buffer described by the MDL at
Irp->MdlAddress, as long as the transfer does not exceed the buffer’s size.

When Sent

Any time after the successful completion of a create request

For this request, the I/O control code (IOCTL_INTERNAL_XXX) has been defined for communication between paired and layered kernel-mode drivers, such as one or more class drivers layered over a port driver. The higher-level driver sets up IRPs with device- or driver-specific I/O control codes, requesting support from the next-lower driver.

Operation

The requested operation is device- or driver-specific.

For general information about I/O control codes for IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL requests, see Section 1.3. For more information about device-type-specific I/O control codes, see Section 1.4.


1.3 Defining I/O Control Codes

All system-defined I/O control codes for IRP_MJ_DEVICE_CONTROL requests can be considered public in the sense that they are exported to one or more user-mode protected subsystems that run on top of the Windows NT executive. As public device I/O control codes, some are also assumed to be exported to user-mode applications native to a protected subsystem, particularly to Win32 applications.

For a new kind of device, the driver’s designer can define a public set of I/O control codes for IRP_MJ_DEVICE_CONTROL requests. However, since such a set of codes should be generally useful to other drivers of similar devices in the future, public I/O control codes must have the approval of and must be built into the system by Microsoft Corporation.

For new devices or for common kinds of devices with special features, driver designers also can define a set of I/O control codes for IRP_MJ_INTERNAL_DEVICE_CONTROL requests. Such a set of internal I/O control codes can be used by paired kernel-mode drivers to control the underlying device.

For example, kernel-mode drivers designed to the class/port model might use such a set of internal I/O control codes to take advantage of the special features of a particular device or type of device. The system-defined SCSI class/port interface uses this technique to define a SCSI-specific set of requests that class drivers send down to the system SCSI port driver, which transforms them into OS-independent SCSI requests for HBA-specific miniport drivers.

When it is sent a device control request, a class driver sets up the next-lower port driver’s I/O stack location in the IRP and passes the request on to the underlying device driver, like any other higher-level driver.

A class driver also can allocate IRPs for I/O control requests and send them to the underlying port driver as follows:

  1. Call IoBuildDeviceIoControlRequest to allocate an IRP with the major function code IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL.

  2. Set up the port driver’s I/O stack location in the IRP with the IOCTL_XXX and appropriate parameters.

  3. Note that parameters for a system-defined I/O control code almost never include an embedded pointer to avoid synchronization problems and possible access violations. With the exception of certain SCSI requests, the buffers at Irp->AssociatedIrp.SystemBuffer, at Irp->MdlAddress, and/or at Parameters.DeviceIoControl.Type3InputBuffer in the I/O stack location of the IRP neither have a pointer to another data buffer nor contain a structure with such a pointer for the public and system-defined internal I/O control codes.

  4. Nevertheless, a pair of class/port drivers that define internal I/O control codes can pass an embedded pointer to driver-allocated memory from the higher-level driver to the device driver. Such a pair of class/port drivers is responsible for ensuring that only one driver at a time can access the data and that their private data buffer is accessible in an arbitrary thread context by the port driver.

  5. Call IoSetCompletionRoutine with the IRP, as necessary, so that the class driver can determine how the corresponding port driver handled a given request, reuse the IRP to send another request, or dispose of a driver-created IRP when the port driver completes a requested operation.

  6. Call IoCallDriver to pass the request on to the port driver.

By calling the GDI function EngDeviceIoControl, a display driver also can send privately defined, device-specific I/O control requests, as well as system-defined public I/O control requests, through the system video port driver down to the corresponding adapter-specific video miniport driver.

With a call to DeviceIoControl, a user-mode VDD can send I/O control requests to the corresponding kernel-mode driver for an MS-DOS-application-dedicated device.

For more information about the functionality of video miniport drivers and display drivers, see the Graphics Driver Design Guide. For additional VDD details see Virtual DOS Drivers.

Figure 1.1 illustrates the layout of I/O control codes.

Figure 1.1 I/O Control Code Layout

Designers of drivers for a new FILE_DEVICE_XXX type of device must set the Common flag at bit 31 in the private I/O control codes they define. Those who define a private set of I/O control codes for IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL requests also must set the Custom flag at bit 13 in the I/O control codes they define.

All system-defined I/O control codes have both these C flags cleared.

Driver writers can use the system-supplied macro CTL_CODE to set up new I/O control codes. To define an I/O control code, follow these guidelines for using CTL_CODE:

  • Choose a descriptive constant name of the form IOCTL_Device_Function, where Device indicates the type of device and Function indicates the operation.

  • Supply the following parameters to the CTL_CODE macro in the following order:

    1. DeviceType matches that set in the Type field of the driver’s device objects (see Section 1.1).

    2. FunctionCode is in the range 0x800 to 0xfff for private I/O control codes defined by customers of Microsoft. Values in the range 0x000 to 0x7ff are reserved by Microsoft for public I/O control codes. The CTL_CODE macro automatically sets the Custom flag at bit 13 for values in the range 0x800 to 0xfff.

    3. TransferType indicates how data is passed to the driver as one of the following system-defined constants:

      METHOD_BUFFERED if the driver transfers small amounts of data for the request

      With this method, IRPs containing the I/O control code will supply a pointer to the buffer into which or from which to transfer data at
      Irp->AssociatedIrp.SystemBuffer. Most I/O control codes for device and intermediate drivers use this TransferType value.

      METHOD_IN_DIRECT if the underlying device driver will read a large amount of data for the request using DMA or PIO and must transfer the data quickly

      With this method, IRPs containing the I/O control code will supply a pointer to an MDL, describing the output buffer at Irp->MdlAddress.

      METHOD_OUT_DIRECT if the underlying device driver will write a large amount of data to the device for the request using DMA or PIO and must transfer the data quickly

      With this method, IRPs containing the I/O control code will supply a pointer to an MDL, describing the data buffer, at Irp->MdlAddress.

      METHOD_NEITHER if the driver can be sent such a request only while it is running in the context of the thread that originates the I/O control request

      Only a highest-level kernel-mode driver is guaranteed to meet this condition, so this value is seldom used for the I/O control codes passed to device drivers. With this method, the highest-level driver must determine whether to set up buffered or direct access to user data on receipt of the request, possibly must lock down the user buffer, and must wrap its access to the user buffer in a structured exception handler. Otherwise, the originating user-mode caller might change the buffered data out from under the driver or the caller could be swapped out just as the driver is accessing the user buffer.

    4. RequiredAccess indicates the type of access that must be requested when the caller opens the file object representing the device (see the IRP_MJ_CREATE request in Section 1.2). In other words, the I/O Manager will create IRPs and call the driver with a given I/O control code only if the caller has requested the necessary access rights for the driver to perform the requested operation. RequiredAccess can be one of the following system-defined constants:

      FILE_ANY_ACCESS if the driver can carry out the requested operation for any caller that has a handle for the file object representing the target device object

      FILE_READ_DATA if the driver can carry out the requested operation only for a caller with read access rights

      With this required access, the underlying device driver transfers data from the device to system memory.

      FILE_WRITE_DATA if the driver can carry out the requested operation only for a caller with write access rights

      With this required access, the underlying device driver transfers data from system memory to its device.

      (FILE_READ_DATA | FILE_WRITE_DATA) if the caller must have both read and write access rights

      With this required access, the underlying device driver transfers data between system memory and the device

Most public I/O control requests sent to device drivers are assigned FILE_ANY_ACCESS as their RequiredAccess value, particularly those sent to drivers of exclusive devices and those that are buffered by the I/O Manager or a higher-level driver. Many internal I/O control requests for system-supplied drivers also specify this type of RequiredAccess.

However, for certain types of devices, the public I/O control codes require the caller to have read access rights, write access rights, or both.

For example, the definition of the public I/O control code IOCTL_DISK_SET_PARTITION_INFO shows that this I/O request can be sent to a disk driver and to all drivers layered above the disk driver only if the caller has both read and write access rights, as shown by the following definition:

#define IOCTL_DISK_SET_PARTITION_INFO\
   CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
         FILE_READ_DATA | FILE_WRITE_DATA)

Driver designers who want to define a set of public control codes must consult with Microsoft Corporation to have new codes added to the system header files. Private I/O control codes should be defined in the driver(s)’ device-specific header files.


1.4 Device-type-specific I/O Requests

The remaining chapters in this part of the manual summarize the device-type-specific I/O requests handled by the system drivers of the most common kinds of devices.

Any new kernel-mode driver must handle the same set of I/O requests as a system-supplied driver if the new driver meets any of the following conditions:

  • The new driver will replace a system driver for the same type of device.

  • The new driver is for another device of a type already in the system.

  • The new driver is an intermediate driver to be layered between two system drivers.

Such a new driver must handle every IRP_MJ_XXX that the system-supplied driver(s) handle. In most cases, a new device driver should also handle the same set of IOCTL_XXX for IRP_MJ_DEVICE_CONTROL requests, even if the new driver must emulate the behavior of the corresponding system-supplied driver. Otherwise, the new driver might “break” user-mode applications that expect these kinds of requests to be honored.

The remaining chapters in this part of the manual also supply tips about the NTSTATUS values that drivers can set in the I/O status block of IRPs, set as necessary in an error log packet, and return for specific requests. These tips for selecting request-specific NTSTATUS values do not include STATUS_PENDING, which any driver can return for an IRP it has not yet completed. Use this information to decide on the appropriate status values to be returned by new drivers for similar types of devices, or as an aide in determining the appropriate status values to be returned by the driver for a new type of device.

For more information about the following kinds of drivers and the requests that each is required to support, see the following:

  • Keyboard and mouse drivers in Chapter 2

  • Beep device drivers in Chapter 3

  • Parallel and serial drivers in Chapter 4

  • Multimedia sound, wave, midi, aux, and mixer drivers in Chapter 5

  • Disk and floppy drivers in Chapter 6

  • CD-ROM drivers in Chapter 7

  • Tape drivers in Chapter 8

  • SCSI drivers in Chapter 9

  • Video miniport drivers in the Graphics Driver Reference


Chapter 2 I/O Requests for Keyboard and Mouse Drivers

The system-supplied keyboard and mouse device drivers are implemented as class drivers layered over one or more port drivers. However, keyboard and mouse device drivers could each be designed as monolithic drivers that support the same functionality as the system’s class/port drivers.

The following sections discuss keyboard and mouse drivers separately but describe whether each request is handled within the respective system class driver or passed on to the underlying port device driver.


2.1 Keyboard I/O Requests

IRP_MJ_CREATE (class)

Operation

The keyboard class driver sends an IRP_MJ_INTERNAL_DEVICE_CONTROL request with the I/O control code IOCTL_INTERNAL_KEYBOARD_ENABLE to the port driver.

When Called

This request is sent to the class driver when the Win32 subsystem, or possibly a vendor-supplied device driver applet, requests a handle for the file object that represents the keyboard device object. A keyboard is a nonexclusive device. However, only the Win32 subsystem has sufficient privilege to read incoming keyboard data.

I/O Status Block

The Information field is set to zero. The class driver sets the Status field to STATUS_SUCCESS or to the error status returned by the port driver for the IOCTL_INTERNAL_KEYBOARD_ENABLE request.

IRP_MJ_CREATE (port)

Operation

Returns STATUS_SUCCESS, indicating that the port exists.

When Called

This request is sent to the port driver when the class driver’s DriverEntry routine calls IoGetDeviceObjectPointer as part of its attempt to layer itself above the port driver.

I/O Status Block

The port driver sets the Status field to STATUS_SUCCESS.

IRP_MJ_CLEANUP (class)

Operation

Cancels each IRP currently queued on behalf of the caller in the device queue of the class driver’s device object by setting STATUS_CANCELLED in the IRP’s I/O status block and completing the IRP. The next request on behalf of this caller will be an IRP_MJ_CLOSE.

When Called

Either the system is being shut down because the Win32 subsystem is releasing its file object handles, or the Win32 subsystem is releasing the handle of the class driver’s device on behalf of an application.

I/O Status Block

Always sets the Status field to STATUS_SUCCESS and the Information field to zero for the cleanup IRP.

IRP_MJ_CLOSE (class)

Operation

The keyboard class driver sends an IRP_MJ_INTERNAL_DEVICE_CONTROL request with the I/O control code IOCTL_INTERNAL_KEYBOARD_DISABLE to the port driver.

When Called

This request is sent to the class driver following its completion of an IRP_MJ_CLEANUP request or when the system is being shut down.

I/O Status Block

Information is set to zero. The class driver sets Status to STATUS_SUCCESS or to the error status returned by the port driver for the IOCTL_INTERNAL_KEYBOARD_DISABLE request.

IRP_MJ_CLOSE (port)

Operation

Returns STATUS_SUCCESS.

When Called

The port is being closed, normally only when the system is being shut down.

I/O Status Block

The port driver sets the Status field to STATUS_SUCCESS.

IRP_MJ_READ

Operation

Transfers data from the device to the buffer at Irp->AssociatedIrp.SystemBuffer. The device data is packaged into one or more keyboard data packets defined as follows:

typedef struct KEYBOARD_INPUT_DATA {
    USHORT UnitId;   // zero-based unit number of the keyboard port
    USHORT MakeCode; // the make scan code (key depression)
    USHORT Flags;    // indicates a break (key release) and
                     // other miscellaneous scan-code info
    USHORT Reserved;
    ULONG ExtraInformation; // device-specific additional
                            // information for the event
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA

When Called

Any time after the IRP_MJ_CREATE request has been completed with STATUS_SUCCESS. A read request can be completed successfully only if it was made by a trusted subsystem, that is, by the Win32 subsystem. The class driver performs a privilege check to enforce this restriction.

I/O Status Block

The Information field always contains the number of bytes read into the system buffer. This is at most the requested length and at least one keyboard data packet when the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_BUFFER_TOO_SMALL, STATUS_PRIVILEGE_NOT_HELD, or STATUS_CANCELLED, and no data is returned.

IRP_MJ_FLUSH_BUFFERS (class)

Operation

Clears the keyboard class driver’s internal data queue and flushes the keyboard device buffer, if applicable.

When Called

Not currently. Reserved for future use.

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is zero.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControlCode.IoControlCode in the driver’s I/O stack location of the IRP. The system-supplied keyboard class driver usually performs sanity checks on the validity of the parameters for the I/O control code, determines the target UnitId (port) for the request, and then passes the request on to the port driver.

Drivers should validate the input UnitId, which is a zero-based value identifying the target keyboard port, for all I/O control codes for which the corresponding structures have this member. A driver should fail the request when an input UnitId value is out of range for the number of keyboards it supports.

When Called

The Win32 subsystem, or possibly a vendor-supplied device driver applet, has called DeviceIoControl to communicate a request to the keyboard driver.

I/O Status Block

The Status field value depends on the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine or the port driver and /or those chosen by the driver designer. In general, the Information field of the I/O status block is set to the number of bytes of returned data when the device driver completes the IRP.

The following section Keyboard I/O Control Codes summarizes the I/O control codes handled by keyboard drivers.


2.2 Keyboard I/O Control Codes

The system-supplied keyboard class driver passes most IRP_MJ_DEVICE_CONTROL requests on to the underlying port driver as IRP_MJ_INTERNAL_DEVICE_CONTROL requests. In addition, the class driver communicates with the port driver using driver-defined I/O control codes and driver-created IRPs set up with IRP_MJ_INTERNAL_DEVICE_CONTROL and one of the IOCTL_INTERNAL_KEYBOARD_XXX.

IOCTL_KEYBOARD_QUERY_ATTRIBUTES

Operation

The keyboard class driver passes this request on to the port driver, which returns the KEYBOARD_ATTRIBUTES information.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the output buffer, which must be >= sizeof(KEYBOARD_ATTRIBUTES).

Output

The driver returns the KEYBOARD_ATTRIBUTES data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes of returned attribute data if the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_INVALID_PARAMETER or to STATUS_BUFFER_TOO_SMALL.

IOCTL_KEYBOARD_QUERY_TYPEMATIC

Operation

The class driver passes this request on to the port driver, which returns the KEYBOARD_TYPEMATIC_PARAMETERS information.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains a UnitId value identifying the target keyboard port. The driver should validate the input UnitId before collecting the requested typematic information.

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the output buffer, which must be >= sizeof(KEYBOARD_TYPEMATIC_PARAMETERS).

Output

The driver returns the KEYBOARD_TYPEMATIC_PARAMETERS data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes of returned typematic data if the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_INVALID_PARAMETER if the input UnitId is invalid or to STATUS_BUFFER_TOO_SMALL.

IOCTL_KEYBOARD_SET_TYPEMATIC

Operation

The keyboard class driver passes this request to the port driver, so that it can set the typematic rate and delay on the device.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the KEYBOARD_TYPEMATIC_PARAMETERS data specifying the rate and delay values. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the buffer’s size in bytes. The driver should validate the input UnitId, before resetting the typematic rate and delay.

Output

None

I/O Status Block

The Information field is set to zero. If the Status field is not set to STATUS_SUCCESS, it can be set to STATUS_INVALID_PARAMETER if the input UnitId is invalid, to STATUS_PARITY_ERROR if the number of retries exceeds the driver’s limit, to STATUS_IO_TIMEOUT if the requested operation timed out on the device, or to STATUS_BUFFER_TOO_SMALL.

IOCTL_KEYBOARD_QUERY_INDICATORS

Operation

The class driver passes this request on to the port driver, which returns the KEYBOARD_INDICATOR_PARAMETERS information.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains a UnitId value identifying the target keyboard port. The driver should validate the input UnitId before collecting the requested indicators information.

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the output buffer, which must be >= sizeof(KEYBOARD_INDICATOR_PARAMETERS).

Output

The driver returns the KEYBOARD_INDICATOR_PARAMETERS data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes of returned indicator data if the Status field is set to STATUS_SUCCESS. Otherwise, it can be set to STATUS_INVALID_PARAMETER if the input UnitId is invalid or to STATUS_BUFFER_TOO_SMALL.

IOCTL_KEYBOARD_SET_INDICATORS

Operation

The keyboard class driver passes this request to the port driver, which sets the indicators on the device.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the KEYBOARD_INDICATOR_PARAMETERS to be set on the device. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(KEYBOARD_INDICATOR_PARAMETERS).

The buffer at Irp->AssociatedIrp.SystemBuffer contains a UnitId value identifying the target keyboard port. The driver should validate the input UnitId before resetting the indicators.

Output

None

I/O Status Block

The Information field is set to zero. If the Status field is not set to STATUS_SUCCESS, it can be set to STATUS_INVALID_PARAMETER if the input UnitId is invalid, to STATUS_PARITY_ERROR if the number of retries exceeds the driver’s limit, to STATUS_IO_TIMEOUT if the requested operation timed out on the device, or to STATUS_BUFFER_TOO_SMALL.

IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION

Operation

The class driver passes this request on to the port driver, which returns the KEYBOARD_INDICATOR_TRANSLATION information.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the output buffer, which must be >= sizeof(KEYBOARD_INDICATOR_TRANSLATION). Note that this structure contains a variable-sized array of INDICATOR_LIST entries, and the size of this array depends on the device-specific number of indicators.

Output

The driver returns the KEYBOARD_INDICATOR_TRANSLATION data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes of returned translation data if the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_INVALID_PARAMETER if the input UnitId is invalid or to STATUS_BUFFER_TOO_SMALL.

IOCTL_KEYBOARD_INSERT_DATA

Operation

None. This request is intercepted within the Win32 subsystem.

IOCTL_INTERNAL_KEYBOARD_CONNECT

Operation

After the class driver has opened the device object representing the keyboard port, the class driver sets up and sends this request to the port driver in order to establish a connection to the port device.

Input

The buffer at Parameters.DeviceIoControl.Type3InputBuffer contains the CONNECT_DATA, including a pointer to the class device object and a pointer to the class service callback routine. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the input buffer’s size in bytes.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or to STATUS_NO_SUCH_DEVICE if the device is not present, to STATUS_SHARING_VIOLATION if the device is already connected, or to STATUS_INVALID_PARAMETER if the connection data is not valid.

IOCTL_INTERNAL_KEYBOARD_DISCONNECT

Operation

None. Currently, the keyboard device cannot be unloaded dynamically so this request is never sent to the port driver.

IOCTL_INTERNAL_KEYBOARD_ENABLE

Operation

Enables keyboard interrupts at the request of the class driver, which has just been sent a create request.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or to STATUS_DEVICE_DATA_ERROR if the device interrupt could not be enabled.

IOCTL_INTERNAL_KEYBOARD_DISABLE

Operation

Disables keyboard interrupts at the request of the class driver, which has just been sent a close request.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or to STATUS_DEVICE_DATA_ERROR if the device interrupt could not be disabled or was already disabled.


2.3 Mouse I/O Requests

IRP_MJ_CREATE (class)

Operation

The mouse class driver sends an IRP_MJ_INTERNAL_DEVICE_CONTROL request with the I/O control code IOCTL_INTERNAL_MOUSE_ENABLE to the port driver.

When Called

This request is sent to the class driver when the Win32 subsystem, or a vendor-supplied device driver applet, requests a handle for the file object that represents the mouse device object. Note that a mouse is a nonexclusive device. However, only the Win32 subsystem has sufficient privilege to read incoming mouse data.

I/O Status Block

The Information field is set to zero. The class driver sets the Status field to STATUS_SUCCESS or to the error status returned by the port driver for the IOCTL_INTERNAL_MOUSE_ENABLE request.

IRP_MJ_CREATE (port)

Operation

Returns STATUS_SUCCESS.

When Called

This request is sent to the port driver when the class driver’s DriverEntry routine calls IoGetDeviceObjectPointer as part of its attempt to layer itself above the port driver.

I/O Status Block

The port driver sets the Status field to STATUS_SUCCESS.

IRP_MJ_CLEANUP (class)

Operation

Cancels each IRP currently queued on behalf of the caller in the device queue of the class driver’s device object by setting STATUS_CANCELLED in the IRP’s I/O status block and completing the IRP. The next request on behalf of this caller will be an IRP_MJ_CLOSE.

When Called

Either the system is being shut down because the Win32 subsystem is releasing its file object handles, or the file object handle of the class driver’s device is being released on behalf of an application.

I/O Status Block

Always sets the Status field to STATUS_SUCCESS and the Information field to zero for the cleanup IRP.

IRP_MJ_CLOSE (class)

Operation

The mouse class driver sends an IRP_MJ_INTERNAL_DEVICE_CONTROL request with the I/O control code IOCTL_INTERNAL_MOUSE_DISABLE to the port driver.

When Called

This request is sent to the class driver following its completion of an IRP_MJ_CLEANUP request or when the system is being shut down.

I/O Status Block

The Information field is set to zero. The class driver sets the Status field to STATUS_SUCCESS or to the error status returned by the port driver for the IOCTL_INTERNAL_MOUSE_DISABLE request.

IRP_MJ_CLOSE (port)

Operation

Returns STATUS_SUCCESS.

When Called

The port is being closed, normally only when the system is being shut down.

I/O Status Block

The port driver sets the Status field to STATUS_SUCCESS.

IRP_MJ_READ

Operation

Transfers data from the device to the buffer at Irp->AssociatedIrp.SystemBuffer. The device data is packaged into one or more mouse data packets, defined as follows:
typedef struct MOUSE_INPUT_DATA {
    USHORT UnitId;     // zero-based unit number of the mouse port
    USHORT Flags;      // indicator flags
    union {
        ULONG Buttons; // transition state of the mouse buttons
        struct {
            USHORT ButtonFlags; // trans. state of mouse buttons
            USHORT ButtonData;  // data for flags (such as amount
                                // of movement if MOUSE_WHEEL set)
        };
    };
    ULONG RawButtons;  // the raw state of the mouse buttons,
                       // currently not used by the Win32 subsystem
    LONG LastX;        // the signed relative or absolute motion
                       // in the X direction
    LONG LastY;        // the signed relative or absolute motion
                       // in the Y direction
    ULONG ExtraInformation;  // device-specific information
                             // for the event
} MOUSE_INPUT_DATA, *PMOUSE_INPUT_DATA

When Called

Any time after the IRP_MJ_CREATE request has been completed with STATUS_SUCCESS. A read request can be completed successfully only when made by a trusted subsystem, that is, by the Win32 subsystem. The class driver performs a privilege check to enforce this restriction.

I/O Status Block

The Information field always contains the number of bytes read into the system buffer. This is at most the requested length and at least one mouse data packet when the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_BUFFER_TOO_SMALL, STATUS_PRIVILEGE_NOT_HELD, or STATUS_CANCELLED, and no data is returned.

IRP_MJ_FLUSH_BUFFERS (class)

Operation

Clears the mouse class driver’s internal data queue and flushes the mouse device buffer, if applicable.

When Called

Not currently. Reserved for future use.

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is set to zero.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControlCode.IoControlCode in the driver’s I/O stack location of the IRP.

When Called

The Win32 subsystem, or a vendor-supplied device driver applet, has called DeviceIoControl to communicate a request to the mouse driver.

I/O Status Block

The Status field value depends on the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine or the port driver and /or those chosen by the driver designer. In general, the Information field of the I/O status block is set to the number of bytes of returned data when the device driver completes the IRP.

The following section Mouse I/O Control Codes summarizes the I/O control codes handled by mouse drivers.


2.4 Mouse I/O Control Codes

The system-supplied mouse class driver passes most IRP_MJ_DEVICE_CONTROL requests on to the underlying port driver as IRP_MJ_INTERNAL_DEVICE_CONTROL requests. In addition, the class driver communicates with the port driver using driver-defined I/O control codes and driver-created IRPs set up with IRP_MJ_INTERNAL_DEVICE_CONTROL and one of the IOCTL_INTERNAL_MOUSE_XXX shown in the following summary.

IOCTL_MOUSE_QUERY_ATTRIBUTES

Operation

The mouse class driver passes this request on to the port driver, which returns the MOUSE_ATTRIBUTES information.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the output buffer, which must be >= sizeof(MOUSE_ATTRIBUTES).

Output

The driver returns the MOUSE_ATTRIBUTES data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes of returned attribute data if the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_INVALID_PARAMETER or to STATUS_BUFFER_TOO_SMALL.

IOCTL_MOUSE_INSERT_DATA

Operation

None. This request is intercepted within the Win32 subsystem.

IOCTL_INTERNAL_MOUSE_CONNECT

Operation

After the class driver has opened the device object representing the mouse port, the class driver sets up and sends this request to the port driver in order to establish a connection to the port device.

Input

The buffer at Parameters.DeviceIoControl.Type3InputBuffer contains the CONNECT_DATA, including a pointer to the class device object and a pointer to the class service callback routine. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the input buffer’s size in bytes.

Output

None

I/O Status Block

The Information field is set to zero, whether the Status field is set to STATUS_SUCCESS or to STATUS_NO_SUCH_DEVICE if the device is not present, to STATUS_SHARING_VIOLATION if the device is already connected, or to STATUS_INVALID_PARAMETER if the connection data is not valid.

IOCTL_INTERNAL_MOUSE_DISCONNECT

Operation

None. Currently, the mouse device cannot be unloaded dynamically so this request is never sent to the port driver.

IOCTL_INTERNAL_MOUSE_ENABLE

Operation

Enables mouse interrupts at the request of the class driver, which has just been sent a create request.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or to STATUS_DEVICE_DATA_ERROR if the device interrupt could not be enabled.

IOCTL_INTERNAL_MOUSE_DISABLE

Operation

Disables mouse interrupts at the request of the class driver, which has just been sent a close request.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or to STATUS_DEVICE_DATA_ERROR if the device interrupt could not be disabled or was already disabled.


Chapter 3 I/O Requests for Beep Device Drivers

Any replacement for the system beep driver must handle the following requests:

IRP_MJ_CREATE

Operation

Returns STATUS_SUCCESS, indicating that the device exists.

When Called

An application or higher-level driver has opened the file object representing the beep device.

I/O Status Block

Always sets Status to STATUS_SUCCESS and Information to zero.

IRP_MJ_CLEANUP

Operation

Cancels every IRP currently in the device queue for the target device object, setting STATUS_CANCELLED in each IRP’s I/O status block and completing each queued IRP before completing the cleanup request. (The next request will be a close request.)

When Called

An application, subsystem, or higher-level driver with a handle for the file object representing the beep device has relinquished the handle or has been canceled.

I/O Status Block

Sets Status to STATUS_SUCCESS and Information to zero for the cleanup request.

IRP_MJ_CLOSE

Operation

Returns STATUS_SUCCESS.

When Called

Following a cleanup request.

I/O Status Block

Always sets Status to STATUS_SUCCESS and Information to zero.

IRP_MJ_DEVICE_CONTROL

Operation

Causes the device to emit a beep.

When Called

Any time after the successful completion of a create request. A Win32 application, VDM, Console Manager, or the Windows NT subsystem has called DeviceIoControl to request a beep.

Input

The I/O control code in the I/O stack location at Parameters.DeviceIoControl.IoControlCode is IOCTL_BEEP_SET, the buffer at Irp->AssociatedIrp.SystemBuffer contains BEEP_SET_PARAMETERS data (frequency and duration), and the I/O stack location contains the size in bytes of the input buffer at Parameters.DeviceIoControl.InputBufferLength.

Output

None

I/O Status Block

Always sets Information to zero, whether Status is set to STATUS_SUCCESS, to STATUS_CANCELLED, or to STATUS_INVALID_PARAMETER if the input frequency or duration is invalid or missing.


Chapter 4 I/O Requests for Parallel and Serial Drivers

Windows NT® kernel-mode parallel drivers follow a variant of the class/port driver model:

  • A device-type-specific class driver handles requests for each type of device that can communicate through a parallel port.

  • A lowest-level port driver controls access to each physical parallel port.

Consequently, a single parallel port driver can have more than one parallel class driver layered above it in a given machine.

While serial drivers also might follow the class/port driver model, the system-supplied serial driver is monolithic for faster I/O throughput.


4.1 Parallel I/O Requests

The system-supplied parallel port driver supports any number of parallel class drivers in sharing its hardware ports. Class drivers set up internal device I/O control requests for the port driver, causing it to allocate and free a parallel port as needed to process incoming requests for each class driver’s device.

In general, a parallel class driver should request a port allocation for each incoming IRP it gets and should release the allocated port after each incoming request is satisfied. Allocating and freeing the port on a per-IRP basis allows any number of class drivers to share the machine’s parallel port(s) efficiently.

The set of I/O requests that a parallel class driver must support depends on the nature of its device. For example, the system-supplied parclass driver, which supports printing through parallel port(s), need not support read requests. As an application-dedicated driver supporting RAS (Remote Access Services), the system-supplied parlink class driver handles both read and write requests.

The following summarizes all the requests that parallel drivers must or can handle:

IRP_MJ_CREATE

Operation

All parallel class drivers and the parallel port driver must support this request. On receipt of this request, a class driver’s design determines the operations it must perform. For example, if the class driver is designed with pageable-image section(s), it maps in its code and sets up any resources it needs for handling subsequent I/O requests to the device represented by the input device object before the class driver sets the I/O status block, completes the IRP, and returns. If the class driver is resident, it simply sets the I/O status block, completes the IRP, and returns. The port driver sets the I/O status block, completes the IRP, and returns STATUS_SUCCESS to indicate that the parallel port represented by the input device object exists.

When Called

For a class driver, a user-mode application, protected subsystem component (for example, the Win32 spooler on behalf of a user-mode printer driver), or another driver has made a request to open the file object representing the class driver’s device on a parallel port.

For the port driver, a class driver has just attempted to layer itself over the port driver during initialization, so the port driver is called with an input device object representing the port.

I/O Status Block

The Information field is set to zero, and the Status field is set to STATUS_SUCCESS.

IRP_MJ_CLEANUP

Operation

Cancels any IRPs currently queued to the device or parallel port represented by the input device object. The next request for the target device object will be a close.

When Called

Any time following the successful completion of a create request and the receipt of additional requests for the target device object

The current holder of the file object handle representing the device on the parallel port has canceled an input or output request, released its handle, or has been terminated.

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS in the cleanup IRP, after the driver has completed all queued IRPs with their Status fields set to STATUS_CANCELLED.

IRP_MJ_CLOSE

Operation

All parallel class drivers and the parallel port driver must support this request. On receipt of this request, a class driver’s design determines the operations it must perform. For example, if the class driver is designed with pageable-image section(s), it releases any resources and mapped code it set up at the create request for the target device object before the driver sets the I/O status block, completes the IRP, and returns. If the class driver is resident, it simply sets the I/O status block, completes the IRP, and returns. The parallel port driver returns status indicating that the port has been closed.

When Called

An application or protected subsystem component has released an opened file object handle representing the class driver’s device on the parallel port. For example, when a print job has been spooled by the protected subsystem on behalf of a user-mode application and all data has been transferred out to the attached device, the port can be closed; when a print job has been canceled, this request follows a cleanup request.

I/O Status Block

The Information field is set to zero, and the Status field is set to STATUS_SUCCESS.

IRP_MJ_QUERY_INFORMATION

Operation

Class drivers must support this request by returning end-of-file information (always set to zero) for the opened parallel port to the buffer at Irp->AssociatedIrp.SystemBuffer.

When Called

Any time following the successful completion of a create request

The holder of a file handle, representing the device on an opened parallel port, has requested information about the length of the file.

I/O Status Block

The Information field is set to either sizeof(FILE_STANDARD_INFORMATION) or to sizeof(FILE_POSITION_INFORMATION) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field is set to STATUS_INVALID_PARAMETER.

IRP_MJ_SET_INFORMATION

Operation

Class drivers must support this request by setting the end-of-file information (always to zero) for the opened parallel port.

When Called

Any time following the successful completion of a create request

The holder of a file handle, representing the device on an opened parallel port, has sent an EOF.

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or STATUS_INVALID_PARAMETER if the FileInformationClass value in the I/O stack location is not FileEndOfFileInformation.

IRP_MJ_READ

Operation

Transfers data from the attached device through the parallel port to the buffer at Irp->AssociatedIrp.SystemBuffer. Whether a class driver supports this request depends on the nature of its device.

When Called

Any time following the successful completion of a create request

For example, the system RAS application can call the system-supplied parlink class driver to read data as soon as the application has gotten a file object handle for the parlink driver’s device.

I/O Status Block

The Information field is set to the number of bytes transferred. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_CANCELLED, STATUS_PENDING, or STATUS_INVALID_PARAMETER.

IRP_MJ_WRITE

Operation

Transfers data from the buffer at Irp->AssociatedIrp.SystemBuffer through the parallel port to the attached device. Whether a class driver supports this request depends on the nature of its device.

When Called

Any time following the successful completion of a create request

For example, the Win32 spooler can call the system-supplied parclass driver whenever an application, VDM, Console Manager, or the Win32 GDI engine makes a print request after it has gotten a handle for the file object representing the printer on the parallel port.

I/O Status Block

The Information field is set to the number of bytes transferred to the attached device. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_CANCELLED, STATUS_PENDING, or STATUS_INVALID_PARAMETER, or, for printers, to STATUS_DEVICE_PAPER_EMPTY, STATUS_DEVICE_OFF_LINE, STATUS_DEVICE_BUSY, STATUS_DEVICE_NOT_CONNECTED, or STATUS_DEVICE_DATA_ERROR.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP. All parallel class drivers must support this request. The nature of a class driver’s device determines which I/O control codes it must support.

When Called

Any time following the successful completion of a create request

A Win32 application, VDM, Console Manager, or the GDI engine has called DeviceIoControl to make a request to the parallel class driver, which can set up an internal device I/O control request for the port driver in order to allocate the port.

I/O Status Block

The Status field value depends on the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine or the port driver and/or those chosen by the driver designer. Usually, the Information field of the I/O status block is set to the number of bytes of returned or transferred data when the class driver completes the IRP.

IRP_MJ_INTERNAL_DEVICE_CONTROL

Operation

Class drivers set up these requests and send them down to the port driver in order to get information about the location of the port controller registers, to allocate and free parallel ports as IRPs come in requesting I/O on the class drivers’ devices and to connect to and disconnect from interrupts on a parallel port.

When Called

Any time following the successful completion of a create request

A Win32 application, VDM, Console Manager, or the GDI engine has made an I/O request sent to a parallel class driver, which, in turn, has set up an internal request to the port driver.

I/O Status Block

The Status field value depends on the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine and/or those chosen by the port driver designer.

The following summarizes the I/O control codes handled by parallel drivers.


4.2 Parallel I/O Control Codes

Since a printer is the standard device connected to a parallel port, this section summarizes the public I/O control codes that must be supported by parallel class drivers for printing devices. A class driver for another type of device might handle a completely different set of I/O control codes, depending on what kind of application support it must provide and on the nature of its device.

This section also summarizes the set of internal I/O control codes that all class drivers set into IRPs that they send down to the parallel port driver.

IOCTL_PAR_QUERY_INFORMATION

Operation

Returns the status of the printer represented by the input device object.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(UCHAR).

Output

Returns the Status of the printer, one of PARALLEL_PAPER_EMPTY, PARALLEL_OFF_LINE, PARALLEL_POWER_OFF, PARALLEL_NOT_CONNECTED, PARALLEL_BUSY, or PARALLEL_SELECTED, to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information is set to sizeof(UCHAR) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field can be set to STATUS_CANCELLED, STATUS_PENDING, or STATUS_BUFFER_TOO_SMALL.

IOCTL_PAR_SET_INFORMATION

Operation

Sets up the printer for a job (write operation), according to the specified constant, PARALLEL_INIT, optionally ORed with PARALLEL_AUTOFEED.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the value to be set, and Parameters.DeviceIoControl.InputBufferLength indicates the size of the buffer, which must be >= sizeof(UCHAR).

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or possibly to STATUS_PENDING, STATUS_CANCELLED, or STATUS_INVALID_PARAMETER.

IOCTL_PAR_QUERY_DEVICE_ID

Operation

Returns the IEEE 1284 device identifier for the device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer. A device identifier can be up to 64 K. An application can use IOCTL_PAR_QUERY_DEVICE_ID_SIZE to determine the size of the ID for a particular device.

Output

Returns the device ID to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

When the driver sets Status to STATUS_SUCCESS, it sets the Information field to the size of the returned device ID. When Status is an error, such as STATUS_IO_DEVICE_ERROR or STATUS_BUFFER_TOO_SMALL, the driver sets Information to zero.

IOCTL_PAR_QUERY_DEVICE_ID_SIZE

Operation

Returns the size of the IEEE 1284 device identifier for the device.

Input

Irp->AssociatedIrp.SystemBuffer of type PPAR_DEVICE_ID_SIZE_INFORMATION to receive the ID size.

Output

Returns the size of the device ID to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

When the driver sets Status to STATUS_SUCCESS, it sets the Information field to sizeof(PPAR_DEVICE_ID_SIZE_INFORMATION). When Status is an error, such as STATUS_IO_DEVICE_ERROR, the driver sets Information to zero.

IOCTL_SERIAL_GET_TIMEOUTS

Operation

Returns the current time-out setting on the printer for writes to the device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size of the buffer, which must be >= sizeof(SERIAL_TIMEOUTS).

Output

The driver returns the information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_TIMEOUTS) if the Status field is set to STATUS_SUCCESS. Otherwise, the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_TIMEOUTS

Operation

Resets the time-out value for writes to the printer.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the value to be set, and Parameters.DeviceIoControl.InputBufferLength indicates the size of the buffer, which must be >= sizeof(SERIAL_TIMEOUTS).

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_INVALID_PARAMETER, STATUS_PENDING, or STATUS_CANCELLED.

IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO

Operation

Returns the address range, as a controller base address and span of bytes, for the port controller registers so the class driver can do I/O operations on its device. Class drivers can store the port address range in their device extensions when they initialize. A class driver can access and program the controller registers only when it has successfully allocated the port, for example with an IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE request.

Also returns pointers to three functions: TryAllocatePort, FreePort and QueryNumWaiters.

TryAllocate Port returns immediately from the port driver with a TRUE status if the port was successfully allocated or a FALSE status if the port was busy.

FreePort releases a previously allocated port that the class driver has been using. The class driver must switch the device back to compatibility mode if necessary.

QueryNumWaiters is used by a class driver to query the number of other class drivers waiting for a port so that a driver that allocates a port for a long time can release the port if it detects that another driver is waiting. QueryNumWaiters returns the number of IRPs queued to a port.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the output buffer, which must be >= sizeof(PARALLEL_PORT_INFORMATION).

Output

The port driver returns the information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field contains the number of bytes returned if the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero and the Status field can be set to STATUS_BUFFER_TOO_SMALL.

IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE

Operation

Gives a class driver exclusive access to a parallel port temporarily. The port driver queues this IRP, that is cancelable, if the port is currently in use by a class driver. A class driver that has allocated the port should call the FreePort function when it has satisfied its current IRP or when its device is busy, if possible. This allows other class drivers to use the port.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. When the port has been allocated to the caller, the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_PENDING or STATUS_CANCELLED.

IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT

Operation

A parallel class driver uses the IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT function to supply an interrupt service routine to a port driver. This ISR will be called by the port driver whenever the parallel port generates an interrupt.

A parallel class driver can also use this IOCTL to supply a deferred port check function that will be called by the port driver whenever the port is not in use. The port is allocated by the port driver prior to calling this deferred port check function and freed by the port driver after this deferred port check function returns.

The IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT function returns a pointer to the Interrupt object as well as pointers to functions to allocate the port and free the port at DIRQL.

If the class driver does not already have the port allocated, it can attempt to allocate the port in the ISR by calling TryAllocatePortAtInterruptLevel.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the input buffer which must be >= sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE). Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the output buffer which must be >= sizeof(PARALLEL_INTERRUPT_INFORMATION).

Output

The port driver returns the information to the buffer at Irp->AssociatedIrp.SystemBuffer.

Status Block

The Information field contains the number of bytes returned if the Status field is set to STATUS_SUCCESS. Otherwise the Information field is set to zero and the Status field can be set to STATUS_BUFFER_TOO_SMALL.

IOCTL_INTERNAL_PARALLEL_DISCONNECT_INTERRUPT

Operation

This function is called during Unload by a class driver that called IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT. It disconnects the Interrupt Service Routine and the Deferred Port Check Routine if one was specified in the Connect call.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the length in bytes of the input buffer, which must be >= sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE). The parameters in the input buffer must be the same as those supplied to IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT.

Output

None

Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS, or possibly STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER.


4.3 Serial I/O Requests

As both a lowest- and highest-level driver, the system-supplied serial device driver has much in common with all kernel-mode device drivers and some features in common with file system drivers. That is, the serial driver handles interrupts and controls its hardware directly like any device driver. The serial driver, like a file system driver, also must provide Dispatch routines that handle requests about the nature of the file objects that represent its ports to user-mode applications.

IRP_MJ_CREATE

Operation

Returns STATUS_SUCCESS if the serial port represented by the input device object is ready for I/O requests. If the driver is designed with pageable-image section(s), it maps in its code and sets up any resources it needs, such as internal buffers or queues, for handling subsequent I/O requests from the caller that is attempting to open the port. Then, it sets the I/O status block, completes the IRP, and returns. If the driver is resident, it simply sets the I/O status block, completes the IRP, and returns.

When Called

A user-mode application, protected subsystem, or another driver has made a request to open the serial port.

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to an error status returned by a support routine that the driver called to allocate necessary resources.

IRP_MJ_CLEANUP

Operation

Cancels any IRPs currently queued to the serial port represented by the input device object. The next request for the device will be a close.

When Called

Any time following the successful completion of a create request and the receipt of additional requests for the target device object.

The current holder of the file object handle representing the serial port has canceled an I/O request, released its handle, or been terminated.

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS in the cleanup IRP, after the driver has completed all queued IRPs with their Status fields set to STATUS_CANCELLED.

IRP_MJ_CLOSE

Operation

Delays the close operation for an interval equal to ten send characters at the current line-control rate, disables interrupts from the device, lowers the hardware control lines, and resets the device. If the driver is designed with pageable-image section(s), it also releases any resources and mapped code it set up at the create request for the given device object.

When Called

Following a cleanup request for the same device object.

I/O Status Block

The Information field is set to zero and the Status field is set to STATUS_SUCCESS.

IRP_MJ_QUERY_INFORMATION

Operation

Returns end-of-file information (always set to zero) for the opened serial port to the buffer at Irp->AssociatedIrp.SystemBuffer.

When Called

Any time following the successful completion of a create request

The holder of a file handle, representing the opened serial port, has requested information about the length of the file.

I/O Status Block

The Information field is set to either sizeof(FILE_STANDARD_INFORMATION) or to sizeof(FILE_POSITION_INFORMATION) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field is set to STATUS_INVALID_PARAMETER.

IRP_MJ_SET_INFORMATION

Operation

Sets the end-of-file information (always to zero) for the opened serial port.

When Called

Any time following the successful completion of a create request

The holder of a file handle, representing the opened serial port, has sent an EOF.

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or STATUS_INVALID_PARAMETER if the FileInformationClass value in the I/O stack location is not FileEndOfFileInformation.

IRP_MJ_READ

Operation

Transfers characters from the device, which can be already held in the driver’s internal buffers, to the buffer at Irp->AssociatedIrp.SystemBuffer.

When Called

Any time following the successful completion of a create request

The current holder of the handle for the serial port requested receive data.

I/O Status Block

The Information field is set to the number of characters read when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero if the Status field is set to STATUS_PENDING or STATUS_CANCELLED.

IRP_MJ_WRITE

Operation

Transfers characters from the buffer at Irp->AssociatedIrp.SystemBuffer to the device or, possibly, into the driver’s internal buffers first.

When Called

Any time following the successful completion of a create request

The current holder of the handle for the serial port sent a data-transmit request.

I/O Status Block

The Information field is set to the number of characters written when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero if the Status field is set to STATUS_PENDING or STATUS_CANCELLED.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP.

When Called

Any time following the successful completion of a create request

A Win32 application, VDM, or subsystem component has called DeviceIoControl to communicate a request to the serial driver.

I/O Status Block

The Status field value depends on the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine and/or those chosen by the driver designer. Usually, the Information field of the I/O status block is set to the number of bytes of returned or transferred data when the driver completes the IRP.


4.4 Serial I/O Control Codes

The following summarizes the I/O control codes handled by serial drivers.

IOCTL_SERIAL_GET_STATS

Operation

Returns the current values of the performance statistics for the device. The statistics include the number of characters transmitted, the number of characters received, and various errors. The values increase until they are explicitly reset with SERIAL_IOCTL_CLEAR_STATS.

If a driver does not implement this IOCTL, it should return an error and not modify the output buffer. If the modem driver is involved in processing the I/O, it will adjust the return value from error to success but only return zeros for the counter values.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIALPERF_STATS).

Output

The driver returns data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIALPERF_STATS) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero if the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_CLEAR_STATS

Operation

Resets the performance statistics for the device.

Input

None.

Output

None

I/O Status Block

The Information field is set to zero and the Status field is set to STATUS_SUCCESS.

IOCTL_SERIAL_GET_PROPERTIES

Operation

Returns information about the characteristics of the attached communication device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_COMMPROP).

Output

The driver returns the information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_COMMPROP) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero and the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_GET_MODEMSTATUS

Operation

Returns a bitmask indicating the status.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(ULONG).

Output

The driver returns the bitmask to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(ULONG) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero and the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_GET_COMMSTATUS

Operation

Returns general status information, including how many Errors and HoldReasons have occurred, how much data is in the driver’s buffers as indicated by the AmountInInQueue and AmountInOutQueue values, and whether EofReceived and WaitForImmediate are set.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_STATUS).

Output

The driver returns information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_STATUS) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero and the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_RESET_DEVICE

Operation

Sets the device to a known state.

Input

None

Output

None

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is set to zero.

IOCTL_SERIAL_PURGE

Operation

Purges the specified operation(s) or queues: one or more of the current and all pending writes, the current and all pending reads, the transmit buffer if one exists, and the receive buffer if one exists.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer at Irp->AssociatedIrp.SystemBuffer, which contains a bitmask of type ULONG, indicating what to purge.

Output

None

I/O Status Block

The Information field is set to zero, and the Status field is set to STATUS_SUCCESS or possibly to STATUS_PENDING, STATUS_CANCELLED, or STATUS_INVALID_PARAMETER.

IOCTL_SERIAL_LSRMST_INSERT

Operation

Enables or disables insertion of the line status or modem status into the RX stream at the behest of a user-mode application.While this state is enabled, the serial driver inserts the caller-supplied escape character, together with one of the following, into the data stream being read from the device:

  • SERIAL_LSRMST_LSR_DATA, indicating that a line-status change occurred. Immediately following this, the driver inserts the value of the line status register and the character present in the receive hardware when the line-status change was processed.

  • SERIAL_LSRMST_LSR_NODATA, indicating that a line-status change occurred but that no data was available in the receive buffer. Immediately following this, the driver inserts the value of the line status register when the line-status change was processed.

  • SERIAL_LSRMST_MST, indicating that a modem-status change occurred. Immediately following this, the driver inserts the value of the modem status register when the modem-status change was processed.

  • SERIAL_LSRMST_ESCAPE, indicating that the next character in the stream, which was received from the device, is identical to the caller-supplied escape character.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer at Irp->AssociatedIrp.SystemBuffer, which contains a value of type UCHAR, indicating whether to enable LSRMST-insertion mode. A value of zero indicates the driver should disable LSRMST-insertion mode. A nonzero value is the caller-supplied escape character.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER if the input escape character is the same as the current XOFF or XON character, or if handflow error replacement is enabled.

IOCTL_SERIAL_GET_BAUD_RATE

Operation

Returns the current baud-rate setting.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_BAUD_RATE).

Output

The driver returns the data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_BAUD_RATE) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_BAUD_RATE

Operation

Sets the speed at which characters are sent over the serial line.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_BAUD_RATE)) of the buffer at
Irp->AssociatedIrp.SystemBuffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_PARAMETER or STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_QUEUE_SIZE

Operation

Resizes the driver’s internal typeahead and input buffers. The driver can allocate buffers larger than the requested sizes and can refuse to allocate buffers larger than its capacity.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_QUEUE_SIZE)) of the buffer at Irp->AssociatedIrp.SystemBuffer, containing the InSize and OutSize specifications.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INSUFFICIENT_RESOURCES if the driver cannot satisfy the request by allocating more memory.

IOCTL_SERIAL_GET_HANDFLOW

Operation

Returns the current values for the flow control and handshaking.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_HANDFLOW).

Output

The driver returns the information to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_HANDFLOW) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero and the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_HANDFLOW

Operation

Sets up flow control and handshaking for subsequent operations.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_HANDFLOW)) of the buffer at Irp->AssociatedIrp.SystemBuffer, which contains the values to be set.

Output

None

I/O Status Block

The Information field is set to zero, and the Status field can be set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER.

IOCTL_SERIAL_GET_LINE_CONTROL

Operation

Returns the current values of the stop-bits, parity, and word-length parameters for the device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_LINE_CONTROL).

Output

The driver returns data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_LINE_CONTROL) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero if the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_LINE_CONTROL

Operation

Resets the stop-bits, parity, and word-length parameters for the device.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_LINE_CONTROL)) of the buffer at Irp->AssociatedIrp.SystemBuffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER.

IOCTL_SERIAL_SET_BREAK_ON

Operation

Causes a break condition to be transmitted by the UART.

Input

None

Output

None

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is set to zero.

IOCTL_SERIAL_SET_BREAK_OFF

Operation

Disables the transmission of break conditions by the UART.

Input

None

Output

None

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is set to zero.

IOCTL_SERIAL_GET_TIMEOUTS

Operation

Returns the current settings for read and write time-outs on the device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_TIMEOUTS).

Output

The driver returns information to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_TIMEOUTS) when the Status field is set to STATUS_SUCCESS. The Information field is set to zero when the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_TIMEOUTS

Operation

Sets up time-out constraints for serial I/O operations.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_TIMEOUTS)) of the buffer at Irp->AssociatedIrp.SystemBuffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_IMMEDIATE_CHAR

Operation

Causes a single character to be transmitted as soon as the hardware is available.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the character to be sent. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(UCHAR).

Output

None

I/O Status Block

The Information field is set to one when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field can be set to STATUS_PENDING, STATUS_CANCELLED, STATUS_BUFFER_TOO_SMALL, or STATUS_INVALID_PARAMETER if another immediate character is already pending.

IOCTL_SERIAL_XOFF_COUNTER

Operation

Sends the specified XOFF character, sets up a timer to track the specified time-out value, and decrements the supplied count of characters for each subsequently written character until the count goes to zero or a time-out occurs. This IOCTL supports software emulation of the serial chip in certain protected subsystems.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_XOFF_COUNTER)) of the buffer at Irp->AssociatedIrp.SystemBuffer, which contains the data.

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS (all requested characters written) or possibly to STATUS_SERIAL_MORE_WRITES, STATUS_SERIAL_COUNTER_TIMEOUT, STATUS_BUFFER_TOO_SMALL, or STATUS_INVALID_PARAMETER if the XoffChar specified is inconsistent with the current flow-control specification.

IOCTL_SERIAL_SET_DTR

Operation

Sets DTR (data terminal ready).

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to STATUS_INVALID_PARAMETER if the current handshake setup is inconsistent with the requested operation.

IOCTL_SERIAL_CLR_DTR

Operation

Clears DTR.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to STATUS_INVALID_PARAMETER if the current handshake setup is inconsistent with the requested operation.

IOCTL_SERIAL_SET_RTS

Operation

Sets RTS (request to send).

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to STATUS_INVALID_PARAMETER if the current handshake setup is inconsistent with the requested operation.

IOCTL_SERIAL_CLR_RTS

Operation

Clears RTS.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to STATUS_INVALID_PARAMETER if the current handshake setup is inconsistent with the requested operation.

IOCTL_SERIAL_GET_DTRRTS

Operation

Returns the state of the DTR and RTS lines at the time this request is processed.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(ULONG).

Output

The driver returns information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(ULONG) when the Status field is set to STATUS_SUCCESS. The Information field is set to zero when the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_XON

Operation

Enables emulation of receipt of characters, which the serial driver might already have in its internal buffers.

Input

None

Output

None

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is set to zero.

IOCTL_SERIAL_SET_XOFF

Operation

Disables emulation of receipt of characters.

Input

None

Output

None

I/O Status Block

The Status field is set to STATUS_SUCCESS and the Information field is set to zero.

IOCTL_SERIAL_GET_WAIT_MASK

Operation

Returns the current value of the event wait mask.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(ULONG).

Output

The driver returns the information to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(ULONG) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field can be set to STATUS_PENDING, STATUS_CANCELLED, or STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_WAIT_MASK

Operation

Causes the driver to track the specified events, or, if the specified value is zero, to complete pending waits.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(ULONG)) of the bitmask at Irp->AssociatedIrp.SystemBuffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_PENDING, STATUS_CANCELLED, STATUS_BUFFER_TOO_SMALL, or STATUS_INVALID_PARAMETER.

IOCTL_SERIAL_WAIT_ON_MASK

Operation

Returns information about which events have occurred among those that the caller was waiting on.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes (must be >= sizeof(ULONG)) of the buffer.

Output

The driver returns a bitmask with bits set for events that occurred (or with a value of zero if the preceding set-waitmask request specified zero) to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(ULONG) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field can be set to STATUS_PENDING or STATUS_INVALID_PARAMETER if a wait is already pending.

IOCTL_SERIAL_GET_CHARS

Operation

Returns the current values for the special characters used by the driver.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(SERIAL_CHARS).

Output

The driver returns the information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(SERIAL_CHARS) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero and the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_SERIAL_SET_CHARS

Operation

Sets special characters (EofChar, ErrorChar, BreakChar, EventChar, XonChar, and XoffChar) used by the driver.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (must be >= sizeof(SERIAL_CHARS)) of the buffer at Irp->AssociatedIrp.SystemBuffer, which contains the values to the used.

Output

None

I/O Status Block

The Information field is set to zero, and the Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER if the specified values for XonChar and XoffChar are identical.


Chapter 5 I/O Requests for Multimedia Drivers

Most Windows NT® multimedia drivers are Win32® user-mode drivers. Many of these user-mode drivers ultimately depend on support from an underlying kernel-mode CD-ROM driver that also supports other types of applications. However, user-mode multimedia audio drivers depend on a dedicated sound adapter with an underlying kernel-mode driver. This chapter summarizes the requests that kernel-mode sound drivers must handle.

For more information about user-mode multimedia drivers, see the Multimedia Drivers documentation.


5.1 Sound I/O Requests

The system-supplied soundblaster driver creates device objects of the types FILE_DEVICE_WAVE_IN, FILE_DEVICE_WAVE_OUT, FILE_DEVICE_MIDI_IN, FILE_DEVICE_MIDI_OUT, and FILE_DEVICE_SOUND, which supports the Win32 Multimedia Aux driver handler. Information about all requests for the soundblaster driver’s device objects has been compressed.

IRP_MJ_CREATE

Operation

Determines whether the caller can be given a handle for the file object representing the target device object (one of wave input, wave output, MIDI input, MIDI output, or MMAux), according to the access rights requested. In general, each of the wave input, wave output, and MIDI input device objects can be opened for write access once, while all device objects can be opened concurrently for read access. That is, each device object can have only one writer at a time but many readers. However, the driver satisfies I/O control requests to set the volume from callers with read access rights when a device object has already been opened with shared-write access, so that mixer applications can be written without undue complexity.

When Called

A user-mode multimedia application has called a Win32 function and a user-mode audio driver handler opens its corresponding file object for the wave, MIDI, or MMAux device object.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_ACCESS_DENIED if the requested access is not at least FILE_READ_DATA for target input devices and both FILE_READ_DATA and FILE_WRITE_DATA for target output devices, or to STATUS_DEVICE_BUSY if the requested access is valid but resource(s) are currently unavailable.

IRP_MJ_CLEANUP

Operation

Resets the device to an idle state and cancels any IRPs currently queued to the target device object.

When Called

The Win32 subsystem is closing a multimedia application for which the device was opened.

Input

None

Output

None

I/O Status Block

The Information field is set to zero, and the Status field is set to STATUS_SUCCESS.

IRP_MJ_CLOSE

Operation

Resets the device to an idle state.

When Called

Following a cleanup request.

Input

None

Output

None

I/O Status Block

The Information field is set to zero, and the Status field is set to STATUS_SUCCESS.

IRP_MJ_READ

Operation

For the wave input device, adds the request to the queue of buffers to be recorded into. The driver copies data from its internal DMA buffer to each user-supplied buffer. For the MIDI input device, reads in data or queues the request to be read in later.

Input

Parameters.Read.Length indicates the size of the user-supplied buffer in bytes, which must be >= sizeof(MIDI_INPUT_DATA) for requests to the MIDI input device.

Output

For the wave input device, the driver copies data from its DMA buffer to the user buffer at Irp->MdlAddress. For the MIDI input device, the driver returns some number of MIDI_INPUT_DATA packets to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

For the wave input device, the Information field is set to the number of bytes recorded when Status is set to STATUS_SUCCESS or to STATUS_CANCELLED, but Status can be set to STATUS_PENDING if the data has not yet been recorded, to STATUS_NOT_SUPPORTED if the target device object is not the wave input device, or to STATUS_BUFFER_TOO_SMALL. For the MIDI input device, the Information field is set to the amount of data recorded when the Status field is set to STATUS_SUCCESS; otherwise, Status can be set to STATUS_PENDING if the request is queued or to STATUS_BUFFER_TOO_SMALL.

IRP_MJ_WRITE

Operation

If the target of the write is a wave output device, adds the request to the list of buffers to be played on the wave output device. The driver copies user-supplied data at Irp->MdlAddress to an internal buffer for DMA to the device.

If the target of the write is a mixer device, sets the controls details for a given control id.

Input

For a wave output device, Parameters.Write.Length indicates the number of bytes of user-supplied wave data to be played. The user buffer at Irp->MdlAddress contains the wave data to be played, which is queued in the order of received requests and, then, copied into the driver’s internal DMA buffer.

For a mixer output device, Parameters.Write.Length indicates the number of bytes to be written. Parameters.Write.ByteOffset.LowPart contains the control id of the target. Irp->AssociatedIrp.SystemBuffer contains the data for the mixer control defined by control id.

Output

None

I/O Status Block

For a wave output device, the Information field is set to the number of bytes played when Status is set to STATUS_SUCCESS, but Status can be set to STATUS_PENDING if the data has not yet been played or to STATUS_NOT_SUPPORTED if the target device object is not the wave output device.

For a mixer device, the Information field is set to zero. If the call completes normally, the Status field is set to STATUS_SUCCESS. Status can be set to STATUS_BUFFER_TOO_SMALL if the data length is invalid for the control id or to STATUS_INVALID_PARAMETER if the control id is invalid.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP.

When Called

A Win32 application has called a Win32 multimedia function or DeviceIoControl to communicate a request to the sound driver for the MIDI or wave device.

Input

Determined by the I/O control code, but can be caller-supplied data in the buffer at Irp->AssociatedIrp.SystemBuffer, in the buffer described by the MDL at Irp->MdlAddress, or (for MIDI output) in the user buffer at Parameters.DeviceIoControl.Type3InputBuffer, with the buffer’s size in the I/O stack location at Parameters.DeviceIoControl.InputBufferLength.

Output

Also determined by the I/O control code in the I/O stack location of the IRP, but data can be written into the buffer at Irp->AssociatedIrp.SystemBuffer or into the buffer described by the MDL at Irp->MdlAddress, as long as the transfer does not exceed the buffer’s size in the I/O stack location at Parameters.DeviceIoControl.OutputBufferLength.

I/O Status Block

The Status field value depends on the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine or those chosen by the driver designer. Usually, the Information field is set to the number of bytes of data transferred.


5.2 Wave, MIDI, and Aux I/O Control Codes

The following summarizes the I/O control codes for sound device drivers. The Status field of the I/O status block also can be set to STATUS_CANCELLED for any asynchronous device I/O control request.

IOCTL_WAVE_GET_CAPABILITIES

Operation

Returns information about the capabilities of the device, such as how many channels the wave input or output device has, which formats it supports, and the driver’s version.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer.

Output

The driver returns as much WAVEOUTCAPSW or WAVEINCAPSW data as the buffer’s size permits to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

Always returns STATUS_SUCCESS in the Status field and the size in bytes of the returned data in the Information field.

IOCTL_WAVE_QUERY_FORMAT

Operation

Determines whether the device supports the specified format.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the PCMWAVEFORMAT value to be tested. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_NOT_SUPPORTED if the device does not support the specified wave format or if the input buffer is not sizeof(PCMWAVEFORMAT).

IOCTL_WAVE_SET_FORMAT

Operation

Sets the format for a subsequent IOCTL_WAVE_PLAY request to the device. If the wave output device is opened but this request has not been received, the driver plays the wave output data at a device-specific default rate. If wave output is currently being played when this request is received, the interval until the new format takes effect is undefined.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the PCMWAVEFORMAT value to be set. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_NOT_SUPPORTED if the device does not support the specified wave format or if the input buffer is not sizeof(PCMWAVEFORMAT).

IOCTL_WAVE_GET_STATE

Operation

Determines the current state of the target device object.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes (a ULONG) of the buffer.

Output

The driver returns a constant indicating the current state, one of WAVE_DD_IDLE (for the wave input device), WAVE_DD_STOPPED, WAVE_DD_PLAYING (for the wave output device), or WAVE_DD_RECORDING (for the wave input device) to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

When Status is set to STATUS_SUCCESS, the Information field is set to sizeof(ULONG). Otherwise, Status can be set to STATUS_BUFFER_TOO_SMALL.

IOCTL_WAVE_SET_STATE

Operation

Sets the target device to the specified state.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the state specification, one of the constants WAVE_DD_STOP (suspends input or output, but completes recording of at least one buffer from the wave input device), WAVE_DD_RESET (cancels all input or output and sets the position to zero, but may complete outstanding read/write requests for the wave input/output devices), WAVE_DD_PLAY (for the wave output device), or WAVE_DD_RECORD (for the wave input device). Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(ULONG).

Output

None

I/O Status Block

When the Status field is set to STATUS_SUCCESS, the Information field is set to zero, as it is for every other possible Status value, which can be STATUS_BUFFER_TOO_SMALL, STATUS_PENDING if the wave output state cannot be set immediately, or STATUS_DEVICE_BUSY if a previously submitted request to set the wave output state is still pending.

IOCTL_WAVE_GET_POSITION

Operation

Determines the number of sound samples and bytes processed.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_POSITION).

Output

The driver returns the position information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero when the Status field is set to STATUS_BUFFER_TOO_SMALL.

IOCTL_WAVE_GET_VOLUME

Operation

Determines the current volume of the wave output device, or returns the maximum value possible (WAVE_DD_MAX_VOLUME) if the device does not support volume control.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_VOLUME).

Output

The driver returns the volume information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_NOT_SUPPORTED if the target device object is not wave output.

IOCTL_WAVE_SET_VOLUME

Operation

Sets the volume of the wave output device if it supports volume control.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the volume to be set. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_VOLUME).

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_NOT_SUPPORTED if the device hardware does not support volume setting, or STATUS_INVALID_PARAMETER if the target device object is not wave output.

IOCTL_WAVE_GET_PITCH

Operation

Determines the current pitch setting of the wave output device if the device supports pitch control.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_PITCH).

Output

The driver returns the current pitch setting to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_NOT_SUPPORTED if the device does not support pitch control, or STATUS_INVALID_PARAMETER if the target device object is not wave output.

IOCTL_WAVE_SET_PITCH

Operation

Sets the pitch of the wave output device for a subsequent IOCTL_WAVE_PLAY request if the device supports pitch control.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the pitch to be set. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_PITCH).

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_NOT_SUPPORTED, or STATUS_INVALID_PARAMETER.

IOCTL_WAVE_GET_PLAYBACK_RATE

Operation

Determines the current playback rate of the wave output device if the device supports playback-rate control.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_PLAYBACK_RATE).

Output

The driver returns the playback-rate information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field can be set to STATUS_BUFFER_TOO_SMALL, STATUS_NOT_SUPPORTED if the device does not support playback-rate control, or STATUS_INVALID_PARAMETER if the target device object is not wave output.

IOCTL_WAVE_SET_PLAYBACK_RATE

Operation

Sets the playback rate of the wave output device if the device supports playback-rate control.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the playback rate to be set. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(WAVE_DD_PLAYBACK_RATE).

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_NOT_SUPPORTED, or STATUS_INVALID_PARAMETER.

IOCTL_WAVE_SET_LOW_PRIORITY

Operation

Sets the wave input device to “low priority mode,” that is, temporarily suspends recording if the adapter supports the operation. This request occurs when the wave input device object has already been opened (see the IRP_MJ_CREATE description in Section 5.1) and a second application gets a handle for the wave input device or for another device object that shares the same hardware. To satisfy this request, the driver suspends recording until the second application releases its handle (see the IRP_MJ_CLOSE description in Section 5.1). Supporting this I/O control request allows multimedia applications to use the hardware while voice input is being used to detect voice commands.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_INVALID_PARAMETER if the target device object is not wave input or to STATUS_DEVICE_BUSY if low priority mode is already in effect or the device is busy for some other reason.

IOCTL_WAVE_PLAY

Operation

None. This IOCTL is obsolete.

IOCTL_WAVE_RECORD

Operation

None. This IOCTL is obsolete.

IOCTL_WAVE_BREAK_LOOP

Operation

None. This IOCTL is obsolete.

IOCTL_MIDI_GET_CAPABILITIES

Operation

Returns information about the capabilities of the device, such as how many voices and notes the output device has and whether it has volume control, or the manufacture and product identification of a MIDI input device, as well as the driver version number.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer.

Output

The driver returns as much MIDIOUTCAPSW or MIDIINCAPSW data as the buffer’s size permits to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

Always returns STATUS_SUCCESS in the Status field and the size in bytes of the returned data in the Information field.

IOCTL_MIDI_GET_STATE

Operation

Determines the current state of the target device object.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes (a ULONG) of the buffer.

Output

The driver returns a constant indicating the current state, one of MIDI_DD_IDLE, MIDI_DD_STOPPED, MIDI_DD_PLAYING (for the MIDI output device), or MIDI_DD_RECORDING (for the MIDI input device) to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Status field is set to STATUS_SUCCESS, and the Information field is set to sizeof(ULONG).

IOCTL_MIDI_SET_STATE

Operation

Sets the target device to the specified state.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the state specification, one of the constants MIDI_DD_STOP (suspend playing or recording), MIDI_DD_RESET (stop playing and cancel outstanding requests for the MIDI output device), MIDI_DD_PLAY (for the MIDI output device), or MIDI_DD_RECORD (for the MIDI input device). Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes (a ULONG) of the buffer.

Output

None

I/O Status Block

When the Status field is set to STATUS_SUCCESS, the Information field is set to zero, as it is for every other possible Status value, which can be STATUS_PENDING or STATUS_DEVICE_BUSY if another state-change operation is not yet complete.

IOCTL_MIDI_GET_VOLUME

Operation

If the MIDI output device supports volume control, the driver returns the current volume setting of the device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(MIDI_DD_VOLUME).

Output

The driver returns the volume information in the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the amount of returned data. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_NOT_SUPPORTED.

IOCTL_MIDI_SET_VOLUME

Operation

If the MIDI output device supports volume control, the driver sets the volume of the device.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the volume to be set. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(MIDI_DD_VOLUME).

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_NOT_SUPPORTED.

IOCTL_MIDI_CACHE_PATCHES

Operation

Preloads the specified bank of patches (zero for the default patch bank) if the device’s internal synthesizer cannot keep all patches loaded simultaneously.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes, which must be >= sizeof(MIDI_DD_CACHE_PATCHES), of the buffer at
Irp->AssociatedIrp.SystemBuffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_NOT_SUPPORTED.

IOCTL_MIDI_CACHE_DRUM_PATCHES

Operation

Preloads the specified bank of key-based percussion patches (zero for the default drum patch bank) if the device’s internal synthesizer cannot keep all percussion patches loaded simultaneously.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes, which must be >= sizeof(MIDI_DD_CACHE_DRUM_PATCHES), of the buffer at Irp->AssociatedIrp.SystemBuffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_NOT_SUPPORTED.

IOCTL_MIDI_PLAY

Operation

Transfers data from the calling user-mode thread’s buffer to the device.

Input

The driver’s I/O stack location in the IRP has a pointer to the user buffer at Parameters.DeviceIoControl.Type3InputBuffer. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to STATUS_DEVICE_BUSY if the MIDI output device is currently playing.

IOCTL_MIDI_RECORD

Operation

None. This IOCTL is obsolete.

IOCTL_AUX_GET_CAPABILITIES

Operation

Returns information about the capabilities of the MMAux (sound) device.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer.

Output

The driver returns as much AUXCAPSW data as the buffer’s size permits to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

Always returns STATUS_SUCCESS in the Status field and the size in bytes of the returned data in the Information field.

IOCTL_AUX_GET_VOLUME

Operation

Determines the current volume of the MMAux device, or returns the maximum value possible (AUX_DD_MAX_VOLUME) if the device does not support volume control.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(AUX_DD_VOLUME).

Output

The driver returns the volume information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_NOT_SUPPORTED if the target device object is not the MMAux device.

IOCTL_AUX_SET_VOLUME

Operation

Sets the volume of the MMAux device if it supports volume control.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the volume to be set. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(AUX_DD_VOLUME).

Output

None

I/O Status Block

The Information field is set to zero. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_NOT_SUPPORTED if the device hardware does not support volume setting, or STATUS_INVALID_PARAMETER if the target device object is not the MMAux device.

IOCTL_MIX_GET_CONFIGURATION

Operation

Returns the configuration information defined by:

  • MIXER_DD_CONFIGURATION_DATA, containing capabilities data, the NumberOfLines and the NumberOfControls.

    • MIXER_DD_LINE_CONFIGURATION_DATA, containing the destination, source, and string ids which are resource strings in the DLL portion of the driver. There is one such structure for each destination/source combination.

    • MIXER_DD_CONTROL_CONFIGURATION_DATA containing control information such as the maximum and minimum values for a control, and string ids which are resource strings in the DLL portion of the driver. There is one such structure for each control.

    • MIXER_DD_CONTROL_LISTTEXT which describes a single entry for a mixer or mux. For each multiple select control (either a mixer or a mux) there are cMultipleItems entries where cMultipleItems is a member of the MIXER_DD_CONTROL_CONFIGURATION_DATA structure for that control.

The output will contain the following data in the order listed here:

  • The data defined by the MIXER_DD_CONFIGURATION_DATA structure.

    • NumberOfLines MIXER_DD_LINE_CONFIGURATION DATA structures ordered according to the destination line order.

    • NumberOfControls MIXER_DD_CONTROL_CONFIGURATION structures ordered so that the line ids are in the same order as the line ids in the MIXER_DD_LINE_CONFIGURATION_DATA structure.

    • A per control array of MIXER_DD_CONTROL_LISTTEXT structures for each multiple select control ordered by control id. Within each per control array, the elements are ordered so that the n’th element corresponds to the n’th selection in that control.

Input

None

Output

The driver returns the current configuration data to the buffer at Irp->AssociatedIrp.SystemBuffer. Note that if the buffer supplied by the caller is too small for the amount of available configuration data, the driver can return as much data as will fit and then indicate buffer overflow in the status block. The first member of the MIXER_DD_CONFIGURATION_DATA structure, cbSize, is the total configuration data available. This can be read in order to determine the correct amount of data to request.

Status Block

The driver returns the amount of configuration data in the output buffer in the Information field. The Status field is either set to STATUS_SUCCESS or possibly STATUS_BUFFER_OVERFLOW if the output buffer is smaller than the amount of available configuration data.

IOCTL_MIX_GET_CONTROL_DATA

Operation

Returns the control data for the control line whose id is specified in the MIXER_DD_READ_DATA structure supplied as input.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer specifies the control id for which data is to be read. The Parameters.DeviceIoControl.InputLength indicates the size in bytes of the input buffer, which must be >= sizeof(MIXER_DD_READ_DATA).

Output

The driver returns the control data at Irp->AssociatedIrp.SystemBuffer.

Status Block

The Status field either is set to STATUS_SUCCESS or STATUS_BUFFER_TOO_SMALL. The Information field contains the number of bytes returned.

IOCTL_MIX_GET_LINE_DATA

Operation

Returns the line data for the line id specified in the MIXER_DD_READ_DATA structure supplied as input. The data is returned in the format described by the MIXER_DD_LINE_DATA structure. Only the MIXERLINE_LINEF_ACTIVE flag changes. If the driver sets the MIXERLINE_LINEF_DISCONNECTED flag, the line should be ignored by applications.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the line id for which data is to be read. The Parameters.DeviceIoControl.InputLength indicates the size in bytes of the input buffer, which must be >= sizeof(MIXER_DD_READ_DATA).

Output

The driver returns line data to the buffer at Irp->AssociatedIrp.SystemBuffer.

Status Block

The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL. The Information field is set to the number of bytes of returned data.

IOCTL_MIX_REQUEST_NOTIFY

Operation

The caller should supply the same buffer for input and to receive the output. The driver will keep track of which changes have been reflected to the application by reading the CurrentLogicalTime member. The driver increments this member for each change.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(MIXER_DD_REQUEST_NOTIFY). The Irp->AssociatedIrp.SystemBuffer will be a buffer of type MIXER_DD_REQUEST_NOTIFY.

Output

The driver returns changes in the buffer at Irp->AssociatedIrp.SystemBuffer. This buffer should be the same buffer supplied as input and must be >= sizeof(MIXER_DD_REQUEST_NOTIFY).

Status Block

The Status field is set to STATUS_SUCCESS, or possibly one of the following: STATUS_BUFFER_TOO_SMALL if the supplied buffer is < sizeof(MIXER_DD_REQUEST_NOTIFY), to STATUS_PENDING, or to STATUS_INVALID_PARAMETER if the time field in the input buffer is invalid.

IOCTL_SOUND_GET_CHANGED_VOLUME

Operation

Returns the actual volume of the MMAux device when its current value does not match the requested setting, provided that the device supports volume control. This request is usually asynchronous. It is completed as soon as the volume of the Aux device differs from the values that were set.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(AUX_DD_VOLUME).

Output

The driver returns the current volume setting to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field either is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_INSUFFICIENT_RESOURCES, or STATUS_NOT_SUPPORTED if the hardware does not support volume setting.


Chapter 6 I/O Requests for Disk Drivers

Any driver that sets up device objects of type FILE_DEVICE_DISK handles the same basic set of IRP_MJ_XXX, including floppy, fixed disk, removable hard disk, and SCSI class drivers for these kinds of devices.

This chapter describes the basic set of I/O requests sent to these device drivers and to intermediate drivers layered above them. SCSI class drivers with device objects of type FILE_DEVICE_DISK support certain additional I/O control requests, as described in Chapter 9.


6.1 Disk I/O Requests

Use the browse button to view these topics in sequence.

IRP_MJ_CREATE

Operation

Returns STATUS_SUCCESS, indicating that the device or partition exists. For the caller, this request opens the device for I/O.

When Called

A higher-level driver is attempting to connect or attach one of its device objects to the target device object, or a user-mode process has attempted to open a file stored on the physical device, thereby causing a file system to carry out a mount operation.

I/O Status Block

Returns STATUS_SUCCESS in the Status field and zero in the Information field.

IRP_MJ_FLUSH_BUFFERS

Operation

Transfers any data cached in the device or in the driver’s internal buffer(s) to system memory or out to the device. If the device or driver supports caching or buffering data, the driver must handle this request.

When Called

A file system or DTP application makes this request to ensure data integrity.

I/O Status Block

The Status field is set to STATUS_SUCCESS when the data has been transferred.

IRP_MJ_SHUTDOWN

Operation

Transfers any data cached in the device or in the driver’s internal buffer(s) to system memory or out to the device before the system is shut down. If the device or driver supports caching or buffering data, the driver must handle this request.

When Called

The user has decided to shut down the system.

I/O Status Block

The Status field is set to STATUS_SUCCESS when the data has been transferred.

IRP_MJ_READ

Operation

Transfers data from the device to system memory.

Output

Drivers of disk-type devices generally use direct I/O, so the lowest-level driver transfers data using DMA or PIO into the buffer described by the MDL at Irp->MdlAddress.

When Called

A user-mode application that has a handle for an opened file or a higher-level driver has requested data.

I/O Status Block

The Information field is set to the number of bytes transferred, whatever the Status field’s value. In addition to STATUS_SUCCESS, possible values are any of the following:
Device Errors
STATUS_INVALID_DEVICE_REQUEST
STATUS_NO_SUCH_DEVICE
STATUS_IO_TIMEOUT
STATUS_DEVICE_NOT_CONNECTED

Sector/Data Errors

STATUS_DEVICE_DATA_ERROR
STATUS_CRC_ERROR

and, by drivers of removable-media devices:

STATUS_NO_MEDIA_IN_DEVICE
STATUS_VERIFY_REQUIRED

IRP_MJ_WRITE

Operation

Transfers data from system memory to the device.

Input

Drivers of disk-type devices generally use direct I/O, so the lowest-level driver transfers data using DMA or PIO from the buffer described by the MDL at Irp->MdlAddress.

When Called

A user-mode application that has a handle for an opened file or a higher-level driver has requested a data transfer.

I/O Status Block

The Information field is set to the number of bytes transferred, whatever the Status field’s value. Possible values are any of those returned for read requests and STATUS_MEDIA_WRITE_PROTECTED returned by drivers of removable-media devices.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP.

When Called

A Win32 application has called DeviceIoControl, or a higher-level driver has set up such a request.

I/O Status Block

Also determined by the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine or lower-level driver and/or those chosen by the driver designer. In general, the Information field of the I/O status block is set to the number of bytes returned or transferred when the device driver completes the IRP.


6.2 Disk I/O Control Codes

The following summarizes the public I/O control codes that disk-type drivers support. However, each device driver is required to handle only those I/O control codes that its particular device(s) support.

For example, only a floppy driver would handle IOCTL_STORAGE_GET_MEDIA_TYPES requests; it would not handle IOCTL_DISK_GET/SET_PARTITION_INFO or IOCTL_GET/SET_DRIVE_LAYOUT requests. As another example, a filter driver like the system-supplied diskperf driver that monitors disk performance statistics would handle IOCTL_DISK_PERFORMANCE requests from a corresponding (user-mode) application; it would simply pass down other IOCTL_DISK_XXX requests to the underlying disk device driver.

All public I/O control codes for drivers of disk-type devices use buffered I/O. Consequently, the input or output data buffer for these requests is at Irp->AssociatedIrp.SystemBuffer.

Class drivers for SCSI disk devices handle additional public I/O control codes, along with those described in this section. See also Chapter 9 for more information about requirements for SCSI class drivers of mass-storage devices.

The IOCTL_INTERNAL_DISK_Xxx codes can be used for communication between two kernel-mode drivers.

IOCTL_DISK_FORMAT_TRACKS

Operation

Formats the specified set of contiguous tracks on the disk.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the FORMAT_PARAMETERS data. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer.

Output

The device driver returns an array of BAD_TRACK_NUMBER values to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size of the returned bad-track array when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is zero, and the Status field possibly can be set to STATUS_INVALID_PARAMETER or STATUS_MEDIA_WRITE_PROTECTED if the media is removable.

IOCTL_DISK_FORMAT_TRACKS_EX

Operation

This IOCTL is similar to IOCTL_DISK_FORMAT_TRACKS except that it allows the caller to specify several more parameters. The additional extended parameters are the format gap length, the number of sectors per track, and an array whose element size is equal to the number of sectors per track. This array represents the track layout.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the FORMAT_EX_PARAMETERS data. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer.

Output

The device driver returns an array of BAD_TRACK_NUMBER values to the buffer at Irp->AssociatedIrp.SystemBuffer.

Status Block

The driver sets the Status field to STATUS_SUCCESS. Otherwise, the driver sets the Status field to INVALID_PARAMETER if the input buffer length is < sizeof(FORMAT_EX_PARAMETERS), or if the format parameters supplied by the caller won’t work on the drive to be formatted.

IOCTL_DISK_VERIFY

Operation

Verifys the specified extent on the fixed disk.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the VERIFY_INFORMATION data specifying the starting offset and length to be verified. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(VERIFY_INFORMATION).

Output

None

I/O Status Block

The Information field is set to the number of bytes verified when the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, STATUS_INVALID_PARAMETER, STATUS_INSUFFICIENT_RESOURCES, STATUS_NONEXISTANT_SECTOR, STATUS_DEVICE_DATA_ERROR, STATUS_INVALID_DEVICE_REQUEST, STATUS_IO_TIMEOUT, or STATUS_DEVICE_NOT_CONNECTED.

IOCTL_DISK_GET_DRIVE_GEOMETRY

Operation

Returns information about the physical disk’s geometry (media type, number of cylinders, tracks per cylinder, sectors per track, and bytes per sector).

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be >= sizeof(DISK_GEOMETRY).

Output

The driver returns the DISK_GEOMETRY data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size in bytes of the returned data. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_UNRECOGNIZED_MEDIA, STATUS_INVALID_PARAMETER, STATUS_INVALID_DEVICE_REQUEST, STATUS_INFO_LENGTH_MISMATCH, STATUS_INSUFFICIENT_RESOURCES, or STATUS_BUFFER_TOO_SMALL.

IOCTL_DISK_GET_MEDIA_TYPES

Operation

This IOCTL is replaced by IOCTL_STORAGE_GET_MEDIA_TYPES. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_GET_MEDIA_TYPES

Operation

Returns information about the geometry of the floppy drives.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be at least (NumberOfSupportedMediaTypes * sizeof(DISK_GEOMETRY)).

Output

The driver returns an array of DISK_GEOMETRY records for the types of media it supports in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size in bytes of the returned data. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INVALID_DEVICE_REQUEST, STATUS_BUFFER_TOO_SMALL, or STATUS_BUFFER_OVERFLOW.

IOCTL_DISK_CHECK_VERIFY

Operation

This IOCTL is replaced by IOCTL_STORAGE_CHECK_VERIFY. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_CHECK_VERIFY

Operation

Determines whether the media has changed on a removable-media device. (For more information about handling removable media, see the Kernel-Mode Driver Design Guide.)

Input

Optionally, Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of a buffer, which must be >= sizeof(ULONG). This field is zero if the optional buffer is not specified.

Output

Optionally, Irp->AssociatedIrp.SystemBuffer points to a buffer to receive the media change count. The driver fills this buffer only if Parameters.DeviceIoControl.OutputBufferLength was nonzero and the return value is STATUS_SUCCESS. The media change count is a ULONG indicating how many times the media has changed since the driver started.

I/O Status Block

If the driver has no indication that the media has changed, the driver sets the Status field to STATUS_SUCCESS. In addition, if the optional media change buffer was specified, the driver returns the media change count in the buffer at Irp->AssociatedIrp.SystemBuffer and sets the Information field to sizeof(ULONG). If the optional media change buffer was not specified, the driver sets Information to zero.

If the driver detects that the media has changed and the volume is mounted (VPD_MOUNTED is set in the VPB), it must: set Information to zero, set Status to STATUS_VERIFY_REQUIRED, set the DO_VERIFY_VOLUME flag in the DeviceObject, and call IoCompleteRequest with the input IRP.

If the driver detects that the media has changed, but the volume is not mounted, the driver must not set the DO_VERIFY_VOLUME bit, The driver should set Status to STATUS_IO_DEVICE_ERROR, set Information to zero, and call IoCompleteRequest with the IRP.

If the driver detects an error such as STATUS_BUFFER_TOO_SMALL, STATUS_INSUFFICIENT_RESOURCES, or a device error, it sets Information to zero and sets the appropriate error value in the Status field.

IOCTL_DISK_GET_PARTITION_INFO

Operation

Returns information about the type, size, and nature of a disk partition. (Floppy drivers need not handle this request.)

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be >= sizeof(PARTITION_INFORMATION).

Output

The driver returns the PARTITION_INFORMATION data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size in bytes of the returned data. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INVALID_PARAMETER, STATUS_INFO_LENGTH_MISMATCH, STATUS_INSUFFICIENT_RESOURCES, STATUS_INVALID_DEVICE_REQUEST, or STATUS_BUFFER_TOO_SMALL.

IOCTL_DISK_SET_PARTITION_INFO

Operation

Changes the partition type of the specified disk partition. (Floppy drivers need not handle this request.)

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the SET_PARTITION_INFORMATION to be set. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the size of the buffer, which must be >= sizeof(SET_PARTITION_INFORMATION).

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INVALID_PARAMETER, STATUS_INVALID_DEVICE_REQUEST, STATUS_UNSUCCESSFUL, STATUS_INFO_LENGTH_MISMATCH, STATUS_INSUFFICIENT_RESOURCES, or STATUS_BUFFER_TOO_SMALL.

IOCTL_DISK_GET_DRIVE_LAYOUT

Operation

Returns information about the number of partitions, disk signature, and features of each partition on a disk. (Floppy drivers need not handle this request.)

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be >= sizeof(DRIVE_LAYOUT_INFORMATION). Note that this structure contains a variable-sized array of PARTITION_INFORMATION elements.

Output

The driver returns the DRIVE_LAYOUT_INFORMATION data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size in bytes of the returned data. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_INSUFFICIENT_RESOURCES, or STATUS_BUFFER_TOO_SMALL.

IOCTL_DISK_SET_DRIVE_LAYOUT

Operation

Repartitions a disk as specified. (Floppy drivers need not handle this request.)

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the DRIVE_LAYOUT_INFORMATION values to be set. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be >= sizeof(DRIVE_LAYOUT_INFORMATION).

Output

Returns updated DRIVE_LAYOUT_INFORMATION, possibly with modified partition numbers, to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size in bytes of the returned information. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INVALID_PARAMETER, STATUS_INFO_LENGTH_MISMATCH, STATUS_INSUFFICIENT_RESOURCES, or STATUS_BUFFER_TOO_SMALL.

IOCTL_DISK_IS_WRITABLE

Operation

Determines whether a disk is writable.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INSUFFICIENT_RESOURCES, STATUS_IO_DEVICE_ERROR, or STATUS_MEDIA_WRITE_PROTECTED.

IOCTL_DISK_REASSIGN_BLOCKS

Operation

Directs the device to map a block to one from its spare-block pool.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the REASSIGN_BLOCKS values to be set. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be >= sizeof(REASSIGN_BLOCKS). Note that this structure contains a variable-sized array of block numbers.

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS, or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_INSUFFICIENT_RESOURCES, or STATUS_BUFFER_TOO_SMALL.

IOCTL_DISK_FIND_NEW_DEVICES

Operation

This IOCTL is replaced by IOCTL_STORAGE_FIND_NEW_DEVICES. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_FIND_NEW_DEVICES

Operation

Determines whether another device that the driver supports has been connected to the I/O bus, either since the system was booted or since the driver last processed this request. If such a device is found, the driver sets up any necessary system objects and resources to handle I/O requests for its new device. It also initializes the device on receipt of this request dynamically: that is, without requiring the machine to be rebooted. Such a driver is assumed to support devices connected on a dynamically configurable I/O bus.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to any other value returned by the driver’s (re)initialization code.

IOCTL_DISK_PERFORMANCE

Operation

Collects disk performance statistics, such as the numbers of bytes read and written since the driver last processed this request, for a corresponding disk monitoring application.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be at least sizeof(DISK_PERFORMANCE).

Output

The driver returns the DISK_PERFORMANCE data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(DISK_PERFORMANCE) when the Status field is set to STATUS_SUCCESS. Otherwise, the Status field can be set to STATUS_INVALID_PARAMETER or STATUS_BUFFER_TOO_SMALL.

IOCTL_SCSI_GET_DUMP_POINTERS

Operation

The system supplied SCSI port driver and atdisk driver support this request. Any replacement driver that controls a disk used for the paging file, must support this request.

This request is issued during system initialization. The driver must return configuration parameters that are used if a system crash occurs to dump memory to the paging file. The DUMP_POINTERS structure describes these configuration parameters, and the driver must set all parameters appropriately to its disk. Each pointer must be a valid pointer or NULL; each size must be a valid size or zero.

Input

None.

Output

The driver returns the DUMP_POINTERS parameters in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The driver sets Irp->IoStatus.Information to sizeof(DUMP_POINTERS). The driver sets the Status field to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_INVALID_PARAMETER, or STATUS_NOT_SUPPORTED. An error return indicates that the crash dump functionality is disabled and no memory dump is produced if a system failure occurs.

IOCTL_DISK_GET_DRIVE_LETTERS

Operation

None. This IOCTL is obsolete.

SMART_GET_VERSION

Operation

Returns version information, a capabilities mask, and a bitmask for the device. This IOCTL must be handled by drivers that support Self-Monitoring Analysis and Reporting Technology (SMART).

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer, which must be >= sizeof(GETVERSIONINPARAMS).

Output

The driver returns the information to the buffer at
Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The driver sets the Information field to sizeof(GETVERSIONINPARAMS) when it sets the Status field to STATUS_SUCCESS. Otherwise, the driver sets the Information field to zero and sets the Status field to possibly STATUS_INVALID_PARAMETER.

SMART_RCV_DRIVE_DATA

Operation

Returns the ATA-2 identify data, the SMART thresholds, or the SMART attributes for the device. This IOCTL must be handled by drivers that support SMART.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains a SENDCMDINPARAMS structure that describes the request being sent to the device. The irDriveRegs.bCommandReg member specifies ID_CMD when identify data is requested and SMART_CMD when SMART data is requested. If SMART data is requested, the irDriveRegs.bFeaturesReg member specifies either READ_ATTRIBUTES or READ_THRESHOLDS.

Parameters.DeviceIoControl.InputBufferLength specifies the size in bytes of the input buffer, which must be >= (sizeof(SENDCMDINPARAMS) - 1).

Parameters.DeviceIoControl.OutputBufferLength specifies the size in bytes of the output buffer, which must be >= (sizeof(SENDCMDOUTPARAMS) - 1 + 512).

Output

The driver returns the SENDCMDOUTPARAMS structure and a 512-byte buffer of drive data to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The driver sets the Information field to (sizeof(SENDCMDOUTPARAMS) - 1 + 512) when it sets the Status field to STATUS_SUCCESS. Otherwise, the driver sets the Information field to zero and the Status field to possibly STATUS_INVALID_PARAMETER or STATUS_INSUFFICIENT_RESOURCES.

SMART_SEND_DRIVE_COMMAND

Operation

Sends one of the following SMART commands to the device:

  • Enable or disable reporting on the device

  • Enable or disable autosaving of attributes

  • Save current attributes now

  • Execute offline diagnostics

  • Get current SMART status

This IOCTL must be handled by drivers that support SMART.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains a SENDCMDINPARAMS structure that describes the command being sent to the device. The irDriveRegs.bCommandReg member must specify SMART_CMD. The irDriveRegs.bFeaturesReg member must specify one of ENABLE_SMART, DISABLE_SMART, ENABLE_DISABLE_AUTOSAVE, SAVE_ATTRIBUTE_VALUES, EXECUTE_OFFLINE_DIAG, or RETURN_SMART_STATUS.

Parameters.DeviceIoControl.InputBufferLength specifies the size in bytes of the input buffer, which must be >= (sizeof(SENDCMDINPARAMS) - 1).

Parameters.DeviceIoControl.OutputBufferLength specifies the size in bytes of the output buffer, which must be >= (sizeof(SENDCMDOUTPARAMS) - 1). If SMART status is being requested, the output buffer must be >= (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS)).

Output

The driver returns the SENDCMDOUTPARAMS structure to the buffer at
Irp->AssociatedIrp.SystemBuffer. If SMART status was requested, and successfully received from the device, the driver includes the IDEREGS structure in the output buffer.

I/O Status Block

When the driver sets the Status field to STATUS_SUCCESS, it sets the Information field is set to ((sizeof(SENDCMDOUTPARAMS) - 1) + sizeof(IDEREGS)) for returning SMART status and to (sizeof(SENDCMDOUTPARAMS) - 1) for all other commands. The driver should set the Status field to STATUS_INVALID_PARAMETER if an input parameter is incorrect or to STATUS_IO_DEVICE_ERROR if the device aborts a command it does not support. If Status is not STATUS_SUCCESS, the driver sets the Information field to zero.

IOCTL_DISK_INTERNAL_SET_VERIFY

Operation

Allows a driver to set the verify bit on a disk device object if the mode of the caller is kernel mode.

Input

None

Output

None

Status Block

The Status field is set to STATUS_SUCCESS. If the mode of the requestor is not kernel mode, the requested operation is not performed, but STATUS_SUCCESS is still returned.

IOCTL_DISK_INTERNAL_CLEAR_VERIFY

Operation

Allows a driver to clear the verify bit on a disk device object, if the mode of the caller is kernel mode.

Input

None

Output

None

Status Block

The Status field is set to STATUS_SUCCESS. If the mode of the requestor is not kernel mode, the requested operation is not performed, but STATUS_SUCCESS is still returned.


Chapter 7 I/O Requests for CD-ROM Drivers

This chapter describes the basic set of I/O requests sent to CD-ROM device drivers and to intermediate drivers layered above them.


7.1 CD-ROM I/O Requests

Any driver that sets up device objects of type FILE_DEVICE_CD_ROM is required to handle three types of requests:

IRP_MJ_CREATE
IRP_MJ_READ
IRP_MJ_DEVICE_CONTROL

IRP_MJ_CREATE

Operation

Returns STATUS_SUCCESS, indicating that the device exists. For the caller, this request opens the device for I/O.

When Called

A higher-level driver is attempting to connect one of its device objects to the target device object, or a user-mode process has attempted to open a file stored on the physical device, thereby causing a file system to carry out a mount operation.

I/O Status Block

Returns STATUS_SUCCESS in the Status field and zero in the Information field.

IRP_MJ_READ

Operation

Transfers data from the underlying device to system memory.

Output

Drivers of CD-ROM devices generally use direct I/O, so the lowest-level driver transfers data using DMA or PIO into the buffer described by the MDL at Irp->MdlAddress.

When Called

A Win32™ application with a handle for the opened file object that represents the CD-ROM device has requested a data transfer.

I/O Status Block

The Information field is set to the number of bytes transferred, whatever the Status field’s value. In addition to STATUS_SUCCESS, possible values are any of the following:
STATUS_INSUFFICIENT_RESOURCES
STATUS_DEVICE_DATA_ERROR
STATUS_INVALID_DEVICE_REQUEST
STATUS_NO_SUCH_DEVICE
STATUS_IO_TIMEOUT
STATUS_IO_DEVICE_ERROR
STATUS_DEVICE_NOT_READY
STATUS_DEVICE_BUSY
STATUS_NO_MEDIA_IN_DEVICE
STATUS_VERIFY_REQUIRED

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP.

When Called

A Win32 application with a handle for the opened file object that represents the CD-ROM device has called DeviceIoControl or a higher-level driver has set up the request.

I/O Status Block

Also determined by the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of possible STATUS_XXX values includes those propagated from a call to a support routine or lower-level driver and/or those chosen by the driver designer. In general, the Information field of the I/O status block is set to the number of bytes returned or transferred when the device driver completes the IRP.


7.2 CD-ROM I/O Control Codes

All public I/O control codes for drivers of CD-ROM devices use buffered I/O. Consequently, the input or output data for these requests is at Irp->AssociatedIrp.SystemBuffer.

Class drivers for SCSI CD-ROM devices handle additional public I/O control codes, along with those described in this section. See also Chapter 9 for more information about requirements for SCSI class drivers of mass-storage devices.

IOCTL_CDROM_GET_DRIVE_GEOMETRY

Operation

IOCTL_CDROM_GET_DRIVE_GEOMETRY returns information about the CDROM’s geometry (media type, number of cylinders, tracks per cylinder, sectors per track, and bytes per sector).

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of the buffer, which must be >= sizeof(DISK_GEOMETRY).

Output

The driver returns the DISK_GEOMETRY-type information in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the size in bytes of the returned data. The Status field is set to STATUS_SUCCESS or possibly to STATUS_UNRECOGNIZED_MEDIA, STATUS_INVALID_PARAMETER, STATUS_INFO_LENGTH_MISMATCH, or STATUS_BUFFER_TOO_SMALL.

IOCTL_CDROM_READ_TOC

Operation

Returns the table of contents of the media.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(CDROM_TOC).

Output

The driver returns the CDROM_TOC data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_NO_MEDIA_IN_DEVICE, STATUS_DEVICE_NOT_READY, STATUS_IO_TIMEOUT, STATUS_IO_DEVICE_ERROR, STATUS_DEVICE_BUSY, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_GET_LAST_SESSION

Operation

The driver determines whether a disc is multisession. It compares the first and last session and the starting track of the last session. If the first session is the same as the last, the disc is not multisession. If the disc is multisession, the driver stores session block addresses in the table of contents.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST, STATUS_DEVICE_BUSY, STATUS_DEVICE_NOT_READY, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_CHECK_VERIFY

Operation

This IOCTL is replaced by IOCTL_STORAGE_CHECK_VERIFY. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_CHECK_VERIFY

Operation

Determines whether the media has changed on a CD-ROM drive. (For more information about handling removable media, see the Kernel-Mode Driver Design Guide.)

Input

Optionally, Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location of the IRP indicates the size in bytes of a buffer, which must be >= sizeof(ULONG). This field is zero if the optional buffer is not specified.

Output

Optionally, Irp->AssociatedIrp.SystemBuffer points to a buffer to receive the media change count. The driver fills this buffer only if Parameters.DeviceIoControl.OutputBufferLength was nonzero and the return value is STATUS_SUCCESS. The media change count is a ULONG indicating how many times the media has changed since the driver started.

I/O Status Block

If the driver has no indication that the media has changed, the driver sets the Status field to STATUS_SUCCESS. In addition, if the optional media change buffer was specified, the driver returns the media change count in the buffer at Irp->AssociatedIrp.SystemBuffer and sets the Information field to sizeof(ULONG). If the optional media change buffer was not specified, the driver sets Information to zero.

If the driver detects that the media has changed and the volume is mounted (VPD_MOUNTED is set in the VPB), it must: set Information to zero, set Status to STATUS_VERIFY_REQUIRED, set the DO_VERIFY_VOLUME flag in the DeviceObject, and call IoCompleteRequest with the input IRP.

If the driver detects that the media has changed, but the volume is not mounted, the driver must not set the DO_VERIFY_VOLUME bit. The driver should set Status to STATUS_IO_DEVICE_ERROR, set Information to zero, and call IoCompleteRequest with the IRP.

If the driver detects an error such as STATUS_BUFFER_TOO_SMALL, STATUS_INSUFFICIENT_RESOURCES, or a device error, it sets Information to zero and sets the appropriate error value in the Status field.

IOCTL_CDROM_GET_CONTROL

Operation

The driver determines the current audio playback mode.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(CDROM_AUDIO_CONTROL).

Output

The driver returns the CDROM_AUDIO_CONTROL data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_DEVICE_NOT_READY, STATUS_IO_DEVICE_ERROR, STATUS_IO_TIMEOUT, STATUS_INSUFFICIENT_RESOURCES, STATUS_VERIFY_REQUIRED, or STATUS_INVALID_DEVICE_REQUEST.

IOCTL_CDROM_GET_VOLUME

Operation

The driver determines the current volume for each of its device’s audio ports.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(CDROM_VOLUME_CONTROL).

Output

The driver returns the CDROM_VOLUME_CONTROL data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to sizeof(CDROM_VOLUME_CONTROL) when the Status field is set to STATUS_SUCCESS. Otherwise, the Information field is set to zero, and the Status field possibly can be set to STATUS_BUFFER_TOO_SMALL, STATUS_DEVICE_NOT_READY, STATUS_IO_DEVICE_ERROR, STATUS_VERIFY_REQUIRED, or STATUS_IO_TIMEOUT.

IOCTL_CDROM_SET_VOLUME

Operation

The driver resets the volume for its device’s audio ports.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the CDROM_VOLUME_CONTROL value(s) to be set. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(CDROM_VOLUME_CONTROL).

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_DEVICE_NOT_READY, STATUS_IO_TIMEOUT, STATUS_IO_DEVICE_ERROR, STATUS_VERIFY_REQUIRED, or STATUS_INVALID_DEVICE_REQUEST.

IOCTL_CDROM_PLAY_AUDIO_MSF

Operation

Plays the specified range of the media.

Input

The buffer at Irp->AssociatedIrp.System buffer contains the starting and ending MSF values. Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(CDROM_PLAY_AUDIO_MSF).

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST, STATUS_BUFFER_TOO_SMALL, STATUS_DEVICE_NOT_READY, STATUS_IO_DEVICE_ERROR, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_SEEK_AUDIO_MSF

Operation

Moves the heads to the specified MSF on the media.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains the CDROM_SEEK_AUDIO_MSF specification. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_INVALID_DEVICE_REQUEST, STATUS_IO_DEVICE_ERROR, STATUS_NO_MEDIA_IN_DEVICE, STATUS_DEVICE_NOT_READY, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_STOP_AUDIO

Operation

Ends audio play.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST if audio is not currently playing, STATUS_IO_DEVICE_ERROR, STATUS_NO_MEDIA_IN_DEVICE, STATUS_DEVICE_NOT_READY, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_PAUSE_AUDIO

Operation

Suspends audio play.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST if audio is not currently playing, STATUS_IO_DEVICE_ERROR, STATUS_NO_MEDIA_IN_DEVICE, STATUS_DEVICE_NOT_READY, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_RESUME_AUDIO

Operation

Resumes a suspended audio operation.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST if audio is not currently suspended, STATUS_IO_DEVICE_ERROR, STATUS_NO_MEDIA_IN_DEVICE, STATUS_DEVICE_NOT_READY, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_READ_Q_CHANNEL

Operation

Returns the current position, media catalog, and ISRC track data.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(SUB_Q_CHANNEL_DATA).

Output

The driver returns the SUB_Q_CHANNEL_DATA information in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL, STATUS_IO_DEVICE_ERROR, STATUS_INSUFFICIENT_RESOURCES, STATUS_INVALID_DEVICE_REQUEST, STATUS_NO_MEDIA_IN_DEVICE, STATUS_DEVICE_NOT_READY, STATUS_IO_TIME_OUT, or STATUS_VERIFY_REQUIRED.

IOCTL_CDROM_FIND_NEW_DEVICES

Operation

This IOCTL is replaced by IOCTL_STORAGE_FIND_NEW_DEVICES. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_FIND_NEW_DEVICES

Operation

Determines whether another device that the driver supports has just been connected to the I/O bus. If such a device is found, the driver sets up any necessary system objects and resources to handle I/O requests for its new device. It also initializes the device on receipt of this request dynamically: that is, without requiring the machine to be rebooted. Such a driver is assumed to support devices connected on a dynamically configurable I/O bus.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to any value returned by the driver’s (re)initialization code.

IOCTL_CDROM_RAW_READ

Operation

Reads data from the CD-ROM in raw mode.

Input

If the IOCTL is from user mode, Irp->AssociatedIrp.SystemBuffer contains a structure that specifies the starting disk offset, the sector count, and the track mode (XA or CDDA) for the read. Parameters.DeviceIoControl.InputBufferLength specifies the size in bytes of the structure, which must be >= sizeof(RAW_READ_INFO). Parameters.DeviceIoControl.OutputBufferLength specifies the size of the buffer to be read, which must be >= sizeof(SectorCount * RAW_SECTOR_SIZE).

If the IOCTL is from kernel mode, Parameters.DeviceIoControl.Type3InputBuffer contains a structure that specifies the starting disk offset, the sector count, and the track mode (XA or CDDA) for the read. Parameters.DeviceIoControl.OutputBufferLength specifies the size of the buffer to be read, which must be >= sizeof(SectorCount * RAW_SECTOR_SIZE).

Output

The driver writes the requested bytes directly (using DMA or PIO) to the buffer described by the MDL at Irp->MdlAddress.

I/O Status Block

If the read is successful, the driver sets Status to STATUS_SUCCESS and Information to the number of bytes transferred. If the read is not successful, the driver sets Information to zero and Status to possibly STATUS_INVALID_PARAMETER, STATUS_INSUFFICIENT_RESOURCES, or STATUS_INVALID_DEVICE_REQUEST.

IOCTL_CDROM_CLOSE_DOOR

Operation

None. This I/O control code has been replaced by IOCTL_DeviceType_LOAD_MEDIA, described in Chapter 9.


Chapter 8 I/O Requests for Tape Drivers

This chapter describes the basic set of I/O requests sent to tape device drivers and to intermediate drivers layered above them.


8.1 Tape I/O Requests

Any driver that sets up device objects of type FILE_DEVICE_TAPE is required to handle at least four types of requests.

IRP_MJ_CREATE

Operation

Returns STATUS_SUCCESS, indicating the existence of the device.

When Called

A higher-level driver is attempting to connect one of its device objects to the target device object, or a user-mode process has attempted to open a file stored on the physical device, thereby causing a file system to carry out a mount operation.

I/O Status Block

Returns STATUS_SUCCESS in the Status field and zero in the Information field.

IRP_MJ_SHUTDOWN

Operation

Transfers data cached in the device or in the driver’s internal buffer(s) into system memory or out to the device before the system is shut down. If the device or driver supports caching or buffering data, the driver must handle this request.

When Called

The user has decided to shut down the system.

I/O Status Block

The Status field is set to STATUS_SUCCESS when the transfer is complete.

IRP_MJ_READ

Operation

Transfers data from the underlying device to system memory.

Output

Drivers of tape devices generally use direct I/O, so the lowest-level driver transfers data using DMA or PIO into the buffer described by the MDL at Irp->MdlAddress.

When Called

A Win32® application with a handle for the opened file object that represents the tape device has requested a data transfer.

I/O Status Block

The Information field is set to the number of bytes transferred, whatever the Status field’s value. In addition to STATUS_SUCCESS, possible values are any of the following:
STATUS_INSUFFICIENT_RESOURCES
STATUS_DEVICE_DATA_ERROR
STATUS_INVALID_DEVICE_REQUEST
STATUS_NO_SUCH_DEVICE
STATUS_IO_TIMEOUT
STATUS_IO_DEVICE_ERROR
STATUS_DEVICE_NOT_READY
STATUS_NO_MEDIA_IN_DEVICE
STATUS_VERIFY_REQUIRED

IRP_MJ_WRITE

Operation

Transfers data from system memory to the underlying device.

Output

Drivers of tape devices generally use direct I/O, so the lowest-level driver transfers data using DMA or PIO into the buffer described by the MDL at Irp->MdlAddress.

When Called

A Win32 application with a handle for the opened file object that represents the tape device has requested a data transfer.

I/O Status Block

The Information field is set to the number of bytes transferred, whatever the Status field’s value. Possible values are any of those returned for read requests and STATUS_MEDIA_WRITE_PROTECTED.

IRP_MJ_DEVICE_CONTROL

Operation

Determined by the I/O control code set at Parameters.DeviceIoControl.IoControlCode in the driver’s I/O stack location of the IRP.

When Called

A Win32 application with a handle for the opened file object that represents the tape device has called DeviceIoControl, or a higher-level driver has set up the request.

I/O Status Block

Also determined by the operation, either STATUS_SUCCESS or an appropriate STATUS_XXX value. For most operations, the set of STATUS_XXX values includes those propagated from a call to a support routine or lower-level driver and/or those chosen by the driver designer. In general, the Information field of the I/O status block is set to the number of bytes returned or transferred.


8.2 Tape I/O Control Codes

All public I/O control codes for drivers of tape devices use buffered I/O. Consequently, the input or output data for these requests is at Irp->AssociatedIrp.SystemBuffer.

Class drivers for SCSI tape devices handle additional public I/O control codes, along with those described in this section. See also Chapter 9 for more information about requirements for SCSI class drivers of mass-storage devices.

IOCTL_TAPE_ERASE

Operation

Erases the current tape partition, either as a TAPE_ERASE_LONG (a.k.a. “secure”) operation that overwrites data with a pattern or as a TAPE_ERASE_SHORT (a.k.a. “quick”) operation that writes an end-of-recorded-data mark at the current position.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_ERASE). If the Immediate member is TRUE, the operation should be asynchronous.

Output

None

I/O Status Block

The Information field is set to the number of bytes transferred. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_IO_DEVICE_ERROR, STATUS_MEDIA_WRITE_PROTECTED, STATUS_INSUFFICIENT_RESOURCES, STATUS_NOT_IMPLEMENTED, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_PREPARE

Operation

Loads or unloads tape, resets the tape’s tension, locks or unlocks the ejection mechanism, or formats the tape.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_PREPARE). If the Immediate member is TRUE, the operation should be asynchronous.

Output

None

I/O Status Block

The Information field is set to the number of bytes transferred. The Status field is set to STATUS_SUCCESS or possibly to STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_IO_DEVICE_ERROR, STATUS_INSUFFICIENT_RESOURCES, STATUS_DEVICE_NOT_CONNECTED, STATUS_MEDIA_WRITE_PROTECTED, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_WRITE_MARKS

Operation

Writes one of setmarks, filemarks, short filemarks, or long filemarks to tape.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_WRITE_MARKS). If the Immediate member is TRUE, the operation should be asynchronous.

Output

None

I/O Status Block

The Information field is set to the number of bytes written. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_IO_DEVICE_ERROR, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_MEDIA_WRITE_PROTECTED, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_CHECK_VERIFY

Operation

This IOCTL is replaced by IOCTL_STORAGE_CHECK_VERIFY. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_CHECK_VERIFY

Operation

Determines whether the media has changed on a tape drive.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field must be set to either of STATUS_SUCCESS or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_GET_POSITION

Operation

Returns the current absolute, logical, or pseudological partition and offset position on the tape.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_GET_POSITION).

Output

The driver returns the TAPE_GET_POSITION data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_IO_DEVICE_ERROR, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_SET_POSITION

Operation

Moves the current position on the tape to the specified partition and offset, according to the given method.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_SET_POSITION). If the Immediate member is TRUE, the operation should be asynchronous.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_IO_DEVICE_ERROR, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_GET_DRIVE_PARAMS

Operation

Returns information about the tape drive’s capabilities, such as its default block size, maximum and minimum block sizes, maximum partition count, whether the drive has EEC, compression, data padding, and report-setmark capabilities, that is, which configurable features the drive supports, including the EOT warning zone size.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_GET_DRIVE_PARAMETERS).

Output

The driver returns the TAPE_GET_DRIVE_PARAMETERS data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INFO_LENGTH_MISMATCH, STATUS_IO_DEVICE_ERROR, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, or STATUS_DEVICE_NOT_READY.

IOCTL_TAPE_SET_DRIVE_PARAMS

Operation

Adjusts a tape drive’s configurable parameters.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_SET_DRIVE_PARAMETERS). The buffer at Irp->AssociatedIrp.SystemBuffer contains the values to be set.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_IO_DEVICE_ERROR, STATUS_INVALID_DEVICE_REQUEST, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_INFO_LENGTH_MISMATCH, or STATUS_DEVICE_NOT_READY.

IOCTL_TAPE_GET_MEDIA_PARAMS

Operation

Returns information about the media’s total and remaining capacity, its block size, the number of partitions, and whether it is write-protected.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_GET_MEDIA_PARAMETERS).

Output

The driver returns the TAPE_GET_MEDIA_PARAMETERS data in the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field is set to the number of bytes returned. The Status field is set to STATUS_SUCCESS or possibly to STATUS_IO_DEVICE_ERROR, STATUS_DEVICE_DATA_ERROR, STATUS_DATA_OVERRUN, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_INFO_LENGTH_MISMATCH, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_SET_MEDIA_PARAMS

Operation

Resets the block size of the media in the drive.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_SET_MEDIA_PARAMETERS). The buffer at Irp->AssociatedIrp.SystemBuffer contains the block size to be set.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_IO_DEVICE_ERROR, STATUS_MEDIA_WRITE_PROTECTED, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_INFO_LENGTH_MISMATCH, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_GET_STATUS

Operation

Returns the current status of the drive.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_CREATE_PARTITION

Operation

Creates the specified number of fixed, select, or initiator partition(s) of the given size on the media.

Input

Parameters.DeviceIoControl.InputBufferLength in the I/O stack location indicates the size in bytes of the parameter buffer, which must be >= sizeof(TAPE_CREATE_PARTITION). The buffer at Irp->AssociatedIrp.SystemBuffer specifies the partition(s) to be created.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_IO_DEVICE_ERROR, STATUS_MEDIA_WRITE_PROTECTED, STATUS_DEVICE_DATA_ERROR, STATUS_NO_SUCH_DEVICE, STATUS_IO_TIMEOUT, STATUS_DEVICE_NOT_READY, STATUS_INFO_LENGTH_MISMATCH, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_VERIFY_REQUIRED.

IOCTL_TAPE_FIND_NEW_DEVICES

Operation

This IOCTL is replaced by IOCTL_STORAGE_FIND_NEW_DEVICES. The only difference between the two IOCTLs is the base code.

IOCTL_STORAGE_FIND_NEW_DEVICES

Operation

Determines whether another device that the driver supports has just been connected to the I/O bus. If such a device is found, the driver sets up any necessary system objects and resources to handle I/O requests for its new device. It also initializes the device on receipt of this request dynamically: that is, without requiring the machine to be rebooted. Such a driver is assumed to support devices connected on a dynamically configurable I/O bus.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field can be set to STATUS_SUCCESS or to any other value returned by the driver’s (re)initialization code.


Chapter 9 I/O Requests for SCSI Drivers

This chapter describes a superset of I/O control requests that SCSI class drivers of mass-storage devices handle.

It also describes the set of I/O control requests that the system SCSI port driver supports.


9.1 SCSI Mass-storage Device I/O Control Codes

SCSI class drivers of mass-storage devices are required to handle a superset of the I/O control codes for IRP_MJ_DEVICE_CONTROL requests that other drivers of the same types of devices handle. See the preceding chapters about the I/O requests sent to disk, CD-ROM, and tape drivers for information about the basic requirements for all drivers of these types of devices.

This section summarizes the I/O control codes that SCSI class drivers of mass-storage devices also handle, as appropriate to the device.

The IOCTL_STORAGE_XXX control codes replace the previous IOCTL_DeviceType_XXX control codes, where DeviceType was DISK, TAPE, or CDROM. For example, IOCTL_STORAGE_RESERVE replaces IOCTL_DISK_RESERVE, IOCTL_TAPE_RESERVE, and IOCTL_CDROM_RESERVE. The new storage control codes have identical values for function code, transfer method, and required access as the previous disk, tape, and CD-ROM codes. The only difference is the device type. Drivers that pass unrecognized IOCTLs to the SCSI class driver do not need to make any changes for these new IOCTLs.

IOCTL_STORAGE_RESERVE

Operation

Claims a device on a SCSI bus with multiple initiators for the exclusive use of the caller.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST, STATUS_DEVICE_NOT_CONNECTED, or STATUS_IO_TIMEOUT.

IOCTL_STORAGE_RELEASE

Operation

Releases a previously reserved device.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST.

IOCTL_STORAGE_LOAD_MEDIA

Operation

Causes media to be loaded in a drive.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST, STATUS_NO_MEDIA_IN_DEVICE, STATUS_BUFFER_TOO_SMALL, or STATUS_DEVICE_NOT_CONNECTED.

IOCTL_STORAGE_MEDIA_REMOVAL

Operation

If the driver can prevent the media from being removed while the drive is in use, it disables or enables the mechanism that ejects media.

A driver for such a removable media which can support this IOCTL must do the following:

  1. Keep a lock count in the device object extension.

  2. Keep the lock count per physical device.

  3. When called with this IOCTL, if the flag to prevent removing the media is set, increment the count; if the flag is clear, decrement the count.

  4. Prevent removal of the media unless the count is zero.

Input

The buffer at Irp->AssociatedIrp.SystemBuffer contains a Boolean value, with TRUE indicating that the driver should lock the media in the drive.

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_INVALID_DEVICE_REQUEST, STATUS_NO_MEDIA_IN_DEVICE, or STATUS_DEVICE_NOT_CONNECTED.

IOCTL_STORAGE_EJECT_MEDIA

Operation

If the device supports it, ejects the media.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field is set to STATUS_SUCCESS or possibly to STATUS_NO_MEDIA_IN_DEVICE or STATUS_INVALID_DEVICE_REQUEST.


9.2 SCSI Port I/O Control Codes

All SCSI class drivers are required to set the minor function code with IRP_MN_SCSI_CLASS in the port driver’s I/O stack location of any IRP that a class driver allocates and sets up to send on to the system port driver. For example, all SCSI class drivers must set this minor function code when they make IOCTL_SCSI_GET_INQUIRY_DATA and IOCTL_SCSI_GET_CAPABILITIES requests during initialization.

For incoming IRPs originating with a user I/O request or with a higher-level driver, SCSI class drivers should ignore the minor function code in the port driver’s I/O stack location, leaving it as is. In general, such requests can be sent directly to the system-supplied port driver only if no SCSI class driver for the target device exists. Otherwise, such requests must be directed to the SCSI class driver for the appropriate type of underlying SCSI device.

The following shows I/O control requests that the system SCSI port driver supports. All public I/O control codes for the SCSI port driver use buffered I/O. Consequently, most input or output data for these requests is at Irp->AssociatedIrp.SystemBuffer.

IOCTL_SCSI_GET_INQUIRY_DATA

Operation

Returns the SCSI inquiry data for all devices on a given SCSI bus. Usually, the caller is a SCSI class driver, but the caller can be an application that makes IOCTL_SCSI_MINIPORT, IOCTL_SCSI_PASS_THROUGH, IOCTL_SCSI_PASS_THROUGH_DIRECT, or IOCTL_SCSI_RESCAN_BUS requests to the port driver.

Input

Parameters.DeviceIoControl.OutputBufferLength indicates the size in bytes of the buffer which must be >= (sizeof(SCSI_ADAPTER_BUS_INFO) + (NumberOfBuses -1) * sizeof(SCSI_BUS_DATA)) + (InquiryDataSize * NumberOfLUs), where the InquiryDataSize is (sizeof(SCSI_INQUIRY_DATA) - 1 + INQUIRYDATABUFFERSIZE) rounded up to an alignment boundary.

Output

The buffer at Irp->AssociatedIrp.SystemBuffer contains the returned inquiry data.

I/O Status Block

The Information field contains the number of bytes returned in the output buffer. The Status field indicates the results of the operation.

IOCTL_SCSI_GET_CAPABILITIES

Operation

Returns the capabilities and limitations of the underlying SCSI HBA. The most important information is returned in the MaximumTransferLength and AlignmentMask members. Class drivers and users of IOCTL_SCSI_PASS_THROUGH and IOCTL_SCSI_PASS_THROUGH_DIRECT are required to honor these limitations.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(IO_SCSI_CAPABILITIES).

Output

Updated IO_SCSI_CAPABILITIES information is returned to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field contains the number of bytes returned in the output buffer. The Status field indicates the results of the operation.

IOCTL_SCSI_GET_ADDRESS

Operation

Returns the address information, such as the target id (TID) and the logical unit number (LUN) of a particular SCSI target. This request must be directed at a class driver.

Input

Parameters.DeviceIoControl.OutputBufferLength in the I/O stack location indicates the size in bytes of the buffer, which must be >= sizeof(SCSI_ADDRESS).

Output

Updated SCSI_ADDRESS information is returned to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field contains the number of bytes returned in the output buffer. The Status field indicates the results of the operation.

IOCTL_SCSI_MINIPORT

Operation

Sends a special control function to an HBA-specific miniport driver. Results vary, depending on the particular miniport driver to which this request is forwarded. If the caller specifies a nonzero Length, either the input or output buffer must be at least (sizeof(SRB_IO_CONTROL) + DataBufferLength)).

Input

The buffer at Irp->AssociatedIrp.SystemBuffer must contain an SRB_IO_CONTROL structure. Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer, which must be at least sizeof (SRB_IO_CONTROL), with additional storage for data if the Length field is nonzero.

Output

An updated SRB_IO_CONTROL structure is returned to the buffer at Irp->AssociatedIrp.SystemBuffer.

I/O Status Block

The Information field contains the number of bytes returned in the output buffer. The Status field indicates the results of the operation.

IOCTL_SCSI_PASS_THROUGH

Operation

Allows an application to send almost any SCSI command to a target device, with the following restrictions:

  • Multitarget commands, such as COPY, are not allowed.

  • Bidirectional data transfer operations are not supported.

  • If a class driver for the target type of device exists, the request must be sent to that class driver. Thus, an application can send this request directly to the system port driver for a target logical unit only if there is no class driver for the type of device connected to that LU.

  • This request cannot be used if the CDB might require the underlying miniport driver to access memory directly. If the caller’s CDB might require direct access to memory, use IOCTL_SCSI_PASS_THROUGH_DIRECT instead.

The calling application creates the SCSI command descriptor block, which can include a request for request-sense data if a CHECK CONDITION occurs. Any data transferred is double buffered.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer at Irp->AssociatedIrp.SystemBuffer, which must be at least (RequestSenseStorageSize + sizeof (SCSI_PASS_THROUGH)). The size of the SCSI_PASS_THROUGH structure varies, depending on its DataTransferLength specification.

This structure includes a SCSI CDB, which must be initialized by the caller. For a data-out command, the data to be transferred is included in the buffer at Irp->AssociatedIrp.SystemBuffr at DataBufferOffset in the SCSI_PASS_THROUGH structure. However, the caller must allocate additional storage, immediately following SCSI_PASS_THROUGH, if the caller asks for request-sense data.

Output

The port driver returns any request-sense data and any data transferred from the device to the buffer at Irp->AssociatedIrp.SystemBuffer. The SenseInfoLength and DataTransferLength in the SCSI_PASS_THROUGH structure are updated to indicate the amount of data transferred.

I/O Status Block

The Information field is set to the number of bytes returned in the output buffer at Irp->AssociatedIrp.SystemBuffer. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER if the input Length value in SCSI_PASS_THROUGH is improperly set.

IOCTL_SCSI_PASS_THROUGH_DIRECT

Operation

Allows an application to send almost any SCSI command to a target device, with the following restrictions:

  • Multitarget commands, such as COPY, are not allowed.

  • Bidirectional data transfer operations are not supported.

  • If a class driver for the target type of device exists, the request must be sent to that class driver. Thus, an application can send this request directly to the system port driver for a target logical unit only if there is no class driver for the type of device connected to that LU.

  • This request must be made if the input CDB might require the underlying miniport driver to access memory directly.

The calling application creates the SCSI command descriptor block, which can include a request for request-sense data if a CHECK CONDITION occurs. If the CDB requests a data transfer operation, the caller must set up a cache-aligned buffer from which or into which the miniport driver can transfer data directly.

Input

Parameters.DeviceIoControl.InputBufferLength indicates the size in bytes of the buffer at Irp->AssociatedIrp.SystemBuffer, which must be at least (RequestSenseStorageSize + sizeof (SCSI_PASS_THROUGH_DIRECT)). The size of the SCSI_PASS_THROUGH_DIRECT structure is fixed.

This structure includes a SCSI CDB, which must be initialized by the caller. For a data-out command, the data to be transferred must be in a cache-aligned buffer. The DataBuffer member of SCSI_PASS_THROUGH_DIRECT is a pointer to this cache-aligned buffer. The caller must allocate additional storage, following the SCSI_PASS_THROUGH_DIRECT structure, if the caller asks for request-sense data.

Output

The port driver returns any request-sense data and the SCSI_PASS_THROUGH_DIRECT structure to the buffer at Irp->AssociatedIrp.SystemBuffer. The SenseInfoLength and DataTransferLength are updated to indicate the amount of data transferred. The port driver returns any data transferred from the device to the supplied cache-aligned buffer at DataBuffer.

I/O Status Block

The Information field is set to the number of bytes returned in the output buffer at Irp->AssociatedIrp.SystemBuffer. The Status field is set to STATUS_SUCCESS or possibly to STATUS_BUFFER_TOO_SMALL or STATUS_INVALID_PARAMETER if the input Length value in SCSI_PASS_THROUGH is improperly set.

IOCTL_SCSI_RESCAN_BUS

Operation

The port driver rescans the LUNs on the bus(es). It collects SCSI inquiry data about all devices on the bus(es), while preserving any claims on their respective devices for SCSI class drivers.

Usually, this request originates in a system-supplied Win32 application such as Windisk or Setup when the user connects new device(s) on a SCSI bus dynamically: that is, without shutting down the system. When this request is satisfied, such an application next makes an IOCTL_SCSI_GET_INQUIRY_DATA request to the system port driver and examines the returned inquiry data. If the application finds any unclaimed devices of a given type, it then sends one or more IOCTL_DeviceType_FIND_NEW_DEVICES requests to the appropriate SCSI class drivers.

Input

None

Output

None

I/O Status Block

The Information field is set to zero. The Status field indicates the results of the operation.


Chapter 1 ScsiPort Routines

An OS-specific SCSI port driver provides ScsiPort Xxx routines to support OS-independent SCSI miniport drivers that are linked with a Microsoft-supplied, OS-specific SCSI port driver.

The Windows NT® SCSI port driver is a kernel-mode dynamic-link library.

References for the ScsiPort Xxx routines are in alphabetical order.


ScsiPortCompleteRequest

VOID
ScsiPortCompleteRequest(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN UCHAR SrbStatus
);

ScsiPortCompleteRequest completes all of the active requests for the given SCSI bus, controller, or LU, including a request being processed by the calling miniport routine.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
PathId
Identifies the SCSI bus; SP_UNTAGGED indicates all buses controlled by the HBA.
TargetId
Identifies the target controller or device on the given bus(es); SP_UNTAGGED indicates all targets on the bus.
Lun
Identifies the logical unit for the given target controller or device; SP_UNTAGGED indicates all logical units for the given target controller(s) on the given bus(es).
SrbStatus
Specifies the completion status to be set in the SrbStatus member of each SRB.

Comments

ScsiPortCompleteRequest can be called to complete outstanding requests after a bus reset, a device reset, or an abort, rather than calling ScsiPortNotification for each outstanding request individually. After calling ScsiPortCompleteRequest, do not doubly complete by also calling ScsiPortNotification individually for each request.

See Also

SCSI_REQUEST_BLOCK, ScsiPortNotification


ScsiPortConvertPhysicalAddressToUlong

ULONG
ScsiPortConvertPhysicalAddressToUlong(
IN SCSI_PHYSICAL_ADDRESS Address
);

ScsiPortConvertPhysicalAddressToUlong truncates a SCSI_PHYSICAL_ADDRESS to a ULONG.

Parameters

Address
Specifies a value of type SCSI_PHYSICAL_ADDRESS.

Return Value

ScsiPortConvertPhysicalAddressToUlong returns the low-order part of the given SCSI_PHYSICAL_ADDRESS value.

See Also

ACCESS_RANGE, ScsiPortGetDeviceBase, ScsiPortGetPhysicalAddress


ScsiPortConvertUlongToPhysicalAddress

SCSI_PHYSICAL_ADDRESS
ScsiPortConvertUlongToPhysicalAddress(
IN ULONG UlongAddress
);

ScsiPortConvertUlongToPhysicalAddress extends a given ULONG address into a value of type SCSI_PHYSICAL_ADDRESS.

Parameters

UlongAddress
Specifies a value of type ULONG.

Return Value

ScsiPortConvertUlongToPhysicalAddress returns the converted SCSI_PHYSICAL_ADDRESS.

See Also

ACCESS_RANGE, ScsiPortGetDeviceBase, ScsiPortGetVirtualAddress


ScsiPortFlushDma

VOID
ScsiPortFlushDma(
IN PVOID DeviceExtension
);

ScsiPortFlushDma flushes any data cached in the system DMA controller at the end of a transfer or terminates a system DMA transfer.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.

Comments

Only miniport drivers of slave HBAs that use a system DMA controller call this routine.

ScsiPortFlushDma must be called after a slave DMA transfer operation is completed but before the SRB is completed or the next call is made to ScsiPortIoMapTransfer for the current buffer.

ScsiPortFlushDma also must be called after ScsiPortIoMapTransfer to cancel a DMA operation, even if no transfer has occurred and the driver’s HwScsiDmaStarted routine has not yet been called.

See Also

HwScsiDmaStarted, ScsiPortIoMapTransfer


ScsiPortFreeDeviceBase

VOID
ScsiPortFreeDeviceBase(
IN PVOID HwDeviceExtension,
IN PVOID MappedAddress
);

ScsiPortFreeDeviceBase frees a range of device I/O or memory space addresses previously mapped into the system address space with ScsiPortGetDeviceBase.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
MappedAddress
Points to the base address of the range to be freed. This address must be the same as that returned by ScsiPortGetDeviceBase.

Comments

If a miniport driver’s HwScsiFindAdapter routine determines that the driver does not need a particular mapped base address for an adapter, it must release the mapping by calling this routine. For example, when a miniport determines there are no supported HBAs on a particular I/O bus, it must call ScsiPortFreeDeviceBase with each mapped address returned by ScsiPortGetDeviceBase, if any calls to this routine were made.

ScsiPortFreeDeviceBase can be called only from miniport driver’s HwScsiFindAdapter routine. Calls from other miniport routines will result in system failure or incorrect operation for the caller.

See Also

HwScsiFindAdapter, ScsiPortGetDeviceBase


ScsiPortGetBusData

ULONG
ScsiPortGetBusData(
IN PVOID DeviceExtension,
IN ULONG BusDataType,
IN ULONG SystemIoBusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
);

ScsiPortGetBusData returns bus-type-specific configuration information that a miniport’s HwScsiFindAdapter routine might use to determine whether it supports a particular adapter on a particular I/O bus and to configure the HBA if it does.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.
BusDataType
Specifies the type of bus-specific configuration data to be returned. Currently, this value can be one of the following: Cmos, EisaConfiguration, Pos, or PCIConfiguration. However, additional types of bus configuration will be supported in the future. The upper bound on the types supported is always MaximumBusDataType.
SystemIoBusNumber
Specifies the system-assigned number of the I/O bus. The miniport driver’s HwScsiFindAdapter routine obtains this value from the input PORT_CONFIGURATION_INFORMATION SystemIoBusNumber member.
SlotNumber
Specifies the logical slot number or location of the device.

If PCIConfiguration is specified as the BusDataType, this parameter must be specified as a PCI_SLOT_NUMBER-type value.

Buffer
Points to a buffer or area to which the configuration data is returned or, if the given Length is zero, points to a location to which the OS-specific port driver returns a pointer to a buffer that it allocates.
Length
Specifies the maximum number of bytes to return at Buffer, or zero if the caller requires the OS-specific port driver to allocate a buffer to contain the data.

Return Value

ScsiPortGetBusData returns the number of bytes of configuration information it stored in the buffer.

When the input BusDataType is PCIConfiguration, ScsiPortGetBusData can return either of the following values to indicate an error:
Value | Meaning
0 (zero) | The PCI bus does not exist.
2 | The PCI bus exists, but there is no device at the given PCI SlotNumber. The Buffer contains the value PCI_INVALID_VENDOR_ID at the PCI_COMMON_CONFIG VendorId member.

Comments

ScsiPortGetBusData can be called only from a miniport driver’s HwScsiFindAdapter routine. Calls from other miniport routines will result in system failure or incorrect operation for the caller.

Configuration data returned by ScsiPortGetBusData is valid only until the miniport calls ScsiPortGetBusData again. As soon as the caller’s HwScsiFindAdapter routine returns control, any returned configuration data becomes invalid.

See Also

PCI_COMMON_CONFIG, PCI_SLOT_NUMBER, PORT_CONFIGURATION_INFORMATION, HwScsiFindAdapter


ScsiPortGetDeviceBase

PVOID
ScsiPortGetDeviceBase(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
IN SCSI_PHYSICAL_ADDRESS IoAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN InIoSpace,
);

ScsiPortGetDeviceBase returns a mapped, logical base address that can be used to communicate with an HBA. Every miniport driver must pass mapped, logical access range addresses to the ScsiPort..Port Xxx and ScsiPort..Register Xxx routines to communicate with its HBA(s).

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area. Miniports usually store their mapped access ranges addresses for the HBA in this extension.
BusType
Specifies the interface type of the I/O bus on which the HBA is connected. The miniport’s HwScsiFindAdapter routine obtains the value for this parameter from the AdapterInterfaceType member of the input PORT_CONFIGURATION_INFORMATION.
SystemIoBusNumber
Specifies the system-assigned number of the I/O bus on which the HBA is connected. The HwScsiFindAdapter routine obtains the value for this parameter from the SystemIoBusNumber member of the input PORT_CONFIGURATION_INFORMATION.
IoAddress
Specifies the bus-relative base address of a range used by the HBA. The HwScsiFindAdapter routine obtains the value for this parameter from one of the AccessRanges elements in the PORT_CONFIGURATION_INFORMATION if the port driver supplies range-configuration information. Otherwise, this address can be a value returned by ScsiPortGetBusData or a miniport-supplied default value. Avoid using a base address of zero because its successful return status can conflict with the error status (NULL).
NumberOfBytes
Specifies the size in bytes of the range that the mapping should cover. The HwScsiFindAdapter routine obtains the value of this parameter from the same AccessRanges element as IoAddress if the port driver supplies range configuration information. Otherwise, this value can be returned by ScsiPortGetBusData or a miniport-supplied default. In any case, the driver must not access the hardware outside of the returned, mapped range.
InIoSpace
TRUE indicates the range to be mapped is in I/O space, and the miniport will pass mapped addresses in this range to the ScsiPort...Port Xxx to communicate with the HBA. The HwScsiFindAdapter routine obtains the value of this parameter from the same AccessRanges element as IoAddress. Note that a miniport driver must invert the value of the InMemorySpace member in an ACCESS_RANGE-type element before it is passed to ScsiPortGetDeviceBase as the InIoSpace argument.

Return Value

ScsiPortGetDeviceBase returns a mapped logical base address for the given IoAddress if it successfully mapped the given range from IoAddress to NumberOfBytes. If a given range cannot be mapped, ScsiPortGetDeviceBase returns NULL.

Comments

Windows NT platforms can have several types of I/O buses and several I/O buses of the same type. Moreover, the HAL can map I/O space to memory in some platforms.

Consequently, a miniport driver cannot use bus-relative access range addresses to communicate with its HBA. To maintain miniport driver portability across CISC- and RISC-based machines, the addresses they use to access HBAs must be translated with ScsiPortGetDeviceBase.

Every miniport driver must use system-space logical range addresses, mapped by ScsiPortGetDeviceBase, to communicate with its HBA(s). Calls to the ScsiPort...Port/Register Xxx routines require these mapped, logical addresses.

ScsiPortGetDeviceBase can be called several times, depending on how many HBAs the miniport driver supports and how many access ranges each HBA requires. Each mapped range corresponds to a range of bus-relative device addresses specified in an ACCESS_RANGE-type element of the AccessRanges array.

ScsiPortGetDeviceBase can be called only from a miniport driver’s HwScsiFindAdapter routine. Calls from other miniport routines will result in system failure or in incorrect operation for the caller.

Follow these guidelines for calling ScsiPortGetDeviceBase:

  • If HwScsiFindAdapter is using a miniport-supplied set of default bus-relative access ranges or values returned by ScsiPortGetBusData, it should call ScsiPortValidateRange before attempting to call ScsiPortGetDeviceBase.

  • If HwScsiFindAdapter determines that a particular HBA is not one that the miniport supports, it must call ScsiPortFreeDeviceBase to release the mapping(s) it set up to communicate with that HBA.

The logical address returned by ScsiPortGetDeviceBase should be used for all subsequent references made to the hardware but should not be added to any AccessRanges specification in the PORT_CONFIGURATION_INFORMATION. Miniport driver writers should make no assumptions about how many bits are used in the logical base address returned by ScsiPortGetDeviceBase.

See Also

ACCESS_RANGE, HwScsiFindAdapter, ScsiPortFreeDeviceBase, ScsiPortGetBusData, ScsiPortRead Xxx, ScsiPortValidateRange, ScsiPortWrite Xxx, PORT_CONFIGURATION_INFORMATION


ScsiPortGetLogicalUnit

PVOID
ScsiPortGetLogicalUnit(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun
);

ScsiPortGetLogicalUnit returns a pointer to the miniport’s per-LU storage area for a given peripheral.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
PathId
Identifies the SCSI bus.
TargetId
Identifies the target controller or device on the bus.
Lun
Identifies the logical unit number of the target device, for which the miniport wants access to its per-LU storage area.

Return Value

ScsiPortGetLogicalUnit returns a pointer to the miniport’s storage area for the requested logical unit. If the OS-specific port driver considers this logical unit to be nonexistent, it returns NULL.

Comments

ScsiPortGetLogicalUnit is irrelevant if the miniport’s DriverEntry routine specified zero for the LuExtensionSize in the HW_INITIALIZATION_DATA when it called ScsiPortInitialize. Otherwise, the OS-specific port driver allocates and initializes with zeros a set of LU extensions of the specified size for the miniport driver to use.

Per-LU storage can be used to store data relevant to a particular peripheral, such as saved data pointers. To access this area, the miniport calls ScsiPortGetLogicalUnit when the driver is maintaining information about the state of or current operation for any particular peripheral.

The OS-specific port driver can consider a logical unit to be nonexistent if there is no active request for that logical unit and the device has never been successfully selected.

See Also

DriverEntry, HW_INITIALIZATION_DATA, ScsiPortInitialize, ScsiPortMoveMemory


ScsiPortGetPhysicalAddress

SCSI_PHYSICAL_ADDRESS
ScsiPortGetPhysicalAddress(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb, /* optional */
IN PVOID VirtualAddress,
OUT ULONG *Length
);

ScsiPortGetPhysicalAddress converts a given virtual address range to a physical address range for a DMA operation.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
Srb
Points to the SCSI request block if the VirtualAddress to be converted comes from that SRB’s DataBuffer member or, possibly, from the SenseInfoBuffer. Otherwise, this parameter must be NULL.
VirtualAddress
Points to the base virtual address to be converted. If this virtual address falls within the range for an SRB-supplied DataBuffer, the caller also must provide the Srb pointer.
Length
Returns the number of bytes mapped, starting at the returned physical address.

Return Value

ScsiPortGetPhysicalAddress returns the corresponding physical address for a given VirtualAddress. If the given address cannot be converted, it returns NULL.

Comments

Miniport drivers of busmaster HBAs call ScsiPortGetPhysicalAddress to get mapped physical address ranges to be used during DMA operations. For example, ScsiPortGetPhysicalAddress can be used to build a scatter/gather list for data transfers that span pages. Note that the Length returned can be greater than the size of the DataBuffer in a given SRB.

If a nonNULL Srb is passed, the VirtualAddress either must be within the range of the DataBuffer of the SRB or must be a pointer from the SenseInfoBuffer. Otherwise, the given VirtualAddress must be in the miniport’s uncached extension, the SrbExtension, or the SenseInfoBuffer.

A miniport driver can call ScsiPortGetPhysicalAddress to translate an extension address only if that miniport’s DriverEntry routine set NeedPhysicalAddresses to TRUE in the HW_INITIALIZATION_DATA when it called ScsiPortInitialize.

Due to constraints on some buses, such as ISA, the address returned by this routine is not guaranteed to match the address returned by an analogous outside routine (such as MmGetPhysicalAddress). A miniport driver should call only ScsiPort Xxx routines to be portable.

See Also

HW_INITIALIZATION_DATA, DriverEntry, ScsiPortGetUncachedExtension, ScsiPortGetVirtualAddress, SCSI_REQUEST_BLOCK


ScsiPortGetSrb

PSCSI_REQUEST_BLOCK
ScsiPortGetSrb(
IN PVOID DeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN LONG QueueTag
);

ScsiPortGetSrb returns a pointer to an active SCSI request for a particular logical unit.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.
PathId
Identifies the SCSI bus.
TargetId
Identifies the target controller or device on the bus.
Lun
Identifies the logical unit number of the target device.
QueueTag
Specifies the queue tag if the miniport handles tagged requests; SP_UNTAGGED indicates that the request is not tagged.

Return Value

ScsiPortGetSrb returns a pointer to a request for the specified logical unit. If there is no outstanding request for the given peripheral or if the QueueTag value is invalid, it returns NULL.

See Also

SCSI_REQUEST_BLOCK


ScsiPortGetUncachedExtension

PVOID
ScsiPortGetUncachedExtension(
IN PVOID HwDeviceExtension,
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN ULONG NumberOfBytes
);

ScsiPortGetUncachedExtension allocates memory that can be used by both the CPU and a busmaster HBA for DMA or for shared data.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
ConfigInfo
Specifies information about the HBA’s DMA capabilities. The following members must be filled in: DmaChannel or DmaPort, DmaWidth, DmaSpeed, MaximumLength, ScatterGather, Master set to TRUE, NumberOfPageBreaks, AdapterInterfaceType, Dma32BitAddresses, SystemIoBusNumber, and AutoRequestSense.

Members that are not pertinent to the HBA, such as DmaChannel for an EISA busmaster adapter, must be left as is.

NumberOfBytes
Indicates the size in bytes of the uncached extension to be allocated.

Return Value

ScsiPortGetUncachedExtension returns a virtual address pointer to the uncached extension. If it cannot allocate the requested memory, it returns NULL.

Comments

ScsiPortGetUncachedExtension can be called only from miniport driver’s HwScsiFindAdapter routine and only for a busmaster HBA. Calls from other miniport driver routines will result in system failure or incorrect operation for the caller.

Because high-end machines have caches and large memories, any memory to be shared between an HBA and the CPU must be specially allocated. Mailboxes or I/O request queues in system memory are examples of this type of shared memory.

The HwScsiFindAdapter routine can call ScsiPortGetUncachedExtension only once for each busmaster HBA the miniport driver supports.

To obtain the physical address for the uncached extension that the HBA can use, call ScsiPortGetPhysicalAddress.

See Also

PORT_CONFIGURATION_INFORMATION, HwScsiFindAdapter, ScsiPortGetPhysicalAddress


ScsiPortGetVirtualAddress

PVOID
ScsiPortGetVirtualAddress(
IN PVOID HwDeviceExtension,
IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
);

ScsiPortGetVirtualAddress returns a physical address associated with a virtual address if the physical address was obtained by a call to ScsiPortGetPhysicalAddress.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
PhysicalAddress
Specifies the physical address to be mapped. This value must have been returned by ScsiPortGetPhysicalAddress and must be from an uncached device extension or SRB extension.

Return Value

ScsiPortGetVirtualAddress returns a mapped pointer for the given input address. If the PhysicalAddress value is invalid, it returns NULL.

See Also

ScsiPortGetUncachedExtension, ScsiPortGetPhysicalAddress


ScsiPortInitialize

ULONG
ScsiPortInitialize(
IN PVOID Argument1,
IN PVOID Argument2,
IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
IN PVOID HwContext /* optional */
);

ScsiPortInitialize sets up the PORT_CONFIGURATION_INFORMATION structure and calls a miniport driver’s HwScsiFindAdapter routine. ScsiPortInitialize also sets up system objects and resources on behalf of miniport drivers.

Parameters

Argument1
Is the first pointer with which the operating system called the miniport’s DriverEntry routine.
Argument2
Is the second pointer with which the operating system called DriverEntry.
HwInitializationData
Points to the initialization and configuration information supplied by DriverEntry.
HwContext
Is the address of a context value to be passed to the miniport’s HwScsiFindAdapter routine. Miniport drivers that scan the bus for HBAs rather than receiving configuration information from the port driver can use this parameter to store state between calls to HwScsiFindAdapter.

Return Value

ScsiPortInitialize returns a status value that is used as the return value from the miniport’s DriverEntry routine.

Comments

Every miniport driver’s DriverEntry routine must call ScsiPortInitialize after the miniport has first zeroed and then set up the HW_INITIALIZATION_DATA.

If a miniport driver can support HBAs on different types of I/O buses, such as both Isa and MicroChannel type I/O buses, the miniport should call ScsiPortInitialize for each supported interface type.

A miniport driver that calls ScsiPortInitialize more than once should check the value returned by ScsiPortInitialize at each call and save the lowest value for all its calls. The DriverEntry routine must return the lowest value when it returns control to the system. Miniport driver writers can make no assumptions about the values returned by ScsiPortInitialize.

See Also

DriverEntry, HW_INITIALIZATION_DATA, HwScsiFindAdapter


ScsiPortIoMapTransfer

VOID
ScsiPortIoMapTransfer(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN ULONG LogicalAddress,
IN ULONG Length
);

ScsiPortIoMapTransfer sets up the system DMA controller for a miniport driver to transfer data through a slave HBA.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
Srb
Point to the SCSI request block for the DMA transfer.
LogicalAddress
Specifies the starting address for the transfer operation. This value can be the base address of a buffer into which or from which data is to be transferred. This value can be an offset within such a buffer.
Length
Specifies the number of bytes to be transferred.

Comments

Only miniport drivers of HBAs that use a system DMA controller (slave DMA) call ScsiPortIoMapTransfer. This routine must be called before such a miniport sets up its HBA to transfer data. The range specified by the LogicalAddress and Length must be within the buffer described by the given SRB.

After the OS-specific port driver programs the system DMA controller, it calls the miniport driver’s HwScsiDmaStarted routine. HwScsiDmaStarted should program the HBA to begin the data transfer. Note that an HBA can interrupt between the miniport’s call to ScsiPortIoMapTransfer and the OS-specific port driver’s call to the miniport’s HwScsiDmaStarted routine.

See Also

HwScsiDmaStarted, ScsiPortFlushDma


ScsiPortLogError

VOID
ScsiPortLogError(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb, /* optional */
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN ULONG ErrorCode,
IN ULONG UniqueId
);

ScsiPortLogError logs errors to the system event log when a miniport driver or its HBA detects a SCSI error condition.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.
Srb
Points to a SCSI request block if one is associated with the error. Otherwise, this parameter is NULL.
PathId
Identifies the SCSI bus.
TargetId
Identifies the target controller or device on the bus.
Lun
Identifies the logical unit number of the target device.
ErrorCode
Specifies an error code indicating one of the following values as the type of error:

Values

Meaning

SP_PROTOCOL_ERROR

Indicates the miniport driver detected a SCSI bus protocol error.

SP_BUS_PARITY_ERROR

Indicates a SCSI bus parity error was detected.

SP_INTERNAL_ADAPTER_ERROR

Indicates an internal HBA error was detected.

SP_UNEXPECTED_DISCONNECT

Indicates that a target disconnected unexpectedly.

SP_INVALID_RESELECTION

Indicates a logical unit reselected unexpectedly or with an invalid queue tag.

SP_BUS_TIME_OUT

Indicates a SCSI bus connection to a logical unit timed out.

SP_REQUEST_TIMEOUT

Indicates an operation to the controller has timed out.

SP_IRQ_NOT_RESPONDING

Indicates the HBA is not interrupting when expected.

SP_BAD_FW_ERROR

Indicates the driver has detected bad or old firmware. The device will not be used.

SP_BAD_FW_WARNING

Indicates the driver has detected a card with old or bad firmware, which can result in reduced performance or functionality.

UniqueId
Specifies a unique identifier for the error. This value differentiates the current error from other errors with the same ErrorCode. For some miniport drivers, this identifies the line of code where the error was detected. For others, it is additional information returned by the HBA.

Comments

A miniport driver should log all real hardware errors. However, it should not log common operational errors, such as selection time outs or bus resets it detects.

See Also

ScsiPortNotification


ScsiPortMoveMemory

VOID
ScsiPortMoveMemory(
IN PVOID WriteBuffer,
IN PVOID ReadBuffer,
IN ULONG Length
);

ScsiPortMoveMemory copies data from one location to another.

Parameters

WriteBuffer
Points to the destination buffer.
ReadBuffer
Points to the source buffer.
Length
Specifies how many bytes to transfer from ReadBuffer to WriteBuffer.

Comments

ScsiPortMoveMemory can be called if a miniport driver needs to copy data from one system-allocated area to another. For example, a miniport might call ScsiPortMoveMemory to copy pertinent SRB values into the driver’s SRB extension.

Each of the given buffer areas must be at least sizeof(Length).


ScsiPortNotification

VOID
ScsiPortNotification(
IN SCSI_NOTIFICATION_TYPE NotificationType,
IN PVOID HwDeviceExtension,
... /* Additional parameters, as required by the given NotificationType */

/* for RequestComplete add: */
IN PSCSI_REQUEST_BLOCK Srb
/* for NextLuRequest add: */
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun
/* for CallEnableInterrupts or CallDisableInterrupts add: */
IN PHW_INTERRUPT HwScsiXxxInterruptsCallback
 /* for RequestTimerCall add: */
IN PHW_TIMER HwScsiTimer,
IN ULONG MiniportTimerValue
);

ScsiPortNotification tells the OS-specific port driver of certain events, such as when a miniport driver completes a request or is ready to start another SRB, as well as when the HBA indicates that certain SCSI error conditions occurred during an operation.

Parameters

NotificationType
Specifies the type of notification, which can be one of the following:

Notification type

Description

RequestComplete

Indicates the given Srb has finished. If this value is set, ScsiPortNotification requires one additional parameter: the address of the SRB. After this notification, the OS-specific port driver owns the request. The miniport driver must not access the Srb, and it must not pass the Srb to another routine (such as ScsiPortLogError).

NextRequest

Indicates the miniport driver is ready for another request to a target that is not currently busy. This notification should be sent by the miniport as soon as the driver is ready for another request. Usually, this notification is sent from the HwScsiStartIo routine but, sometimes, from the HwScsiInterrupt (or HwScsiEnableInterruptsCallback) routine.

NextLuRequest

Indicates that the HBA is ready for another request for the specified logical unit. If this value is set, ScsiPortNotification requires three additional parameters: (1) the path ID, (2) the target ID, and (3) the logical unit number. This value should be used only if the HBA can queue multiple requests and support auto request sense or tagged queuing.

ResetDetected

Indicates that the HBA has detected a reset on the SCSI bus. After this notification, the miniport driver is still responsible for completing any active requests. The SCSI port driver will manage all required bus-reset delays.

CallEnableInterrupts

Indicates that the miniport driver requires the OS-specific port driver to call the miniport’s HwScsiEnableInterruptsCallback routine. If this value is set, ScsiPortNotification requires an additional parameter: the entry point for the HwScsiEnableInterruptsCallback. The miniport’s HwScsiInterrupt routine makes this call, after disabling interrupts on the HBA, to defer some interrupt-driven I/O processing if the HBA requires polling or stalling in the ISR. While the callback runs, system interrupts remain enabled but the miniport driver’s HwScsiInterrupt routine will not be called. The HwScsiEnableInterruptsCallback is responsible for completing the deferred I/O processing and for calling ScsiPortNotification again with CallDisableInterrupts and the miniport’s HwScsiDisableInterruptsCallback entry point.

CallDisableInterrupts

Indicates that the miniport driver requires the OS-specific port driver to call the miniport’s HwScsiDisableInterruptsCallback routine. If this value is set, ScsiPortNotification requires an additional parameter: the entry point for the HwScsiDisableInterruptsCallback. While this callback runs, it cannot be preempted by an interrupt except from a device with a higher priority interrupt than the HBA. In this callback, the miniport re-enables interrupts on the HBA.

RequestTimerCall

Indicates that the miniport requires the OS-specific port driver to call the miniport’s HwScsiTimer routine in the requested number of microseconds. If this value is set, ScsiPortNotification requires two additional parameters: (1) the entry point for the miniport’s HwScsiTimer routine, and (2) a MiniportTimerValue interval, in microseconds. Note that the resolution of the system timer is approximately 10 milliseconds.

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.

Comments

Every miniport driver must call ScsiPortNotification twice for each call to the miniport’s HwScsiStartIo routine with an SRB that the miniport completes successfully. First, the miniport calls ScsiPortNotification with the NotificationType NextRequest or with NextLuRequest if the miniport supports tagged queuing or multiple requests per LU. Then, the miniport calls ScsiPortNotification with the NotificationType RequestComplete and the request that it has just satisfied.

A miniport’s HwScsiInterrupt routine is most likely to call ScsiPortNotification with the NotificationType ResetDetected.

If an HBA requires the miniport to use more than a millisecond processing interrupt-driven I/O operations, its HwScsiInterrupt routine should disable interrupts on the HBA and call ScsiPortNotification with CallEnableInterrupts and a driver-supplied HwScsiEnableInterruptsCallback routine. This routine, in turn, calls ScsiPortNotification with CallDisableInterrupts and the corresponding driver-supplied HwScsiDisableInterruptsCallback.

See Also

HwScsiTimer, HwScsiDisableInterruptsCallback, HwScsiEnableInterruptsCallback, ScsiPortCompleteRequest


ScsiPortReadPortBufferUchar

VOID
ScsiPortReadPortBufferUchar(
IN PUCHAR Port,
IN PUCHAR Buffer,
IN ULONG Count
);

ScsiPortReadPortBufferUchar transfers a given number of unsigned byte values from the HBA to a buffer.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of bytes to be read from the HBA.

Comments

ScsiPortReadPortBufferUchar ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadPortBufferUlong

VOID
ScsiPortReadPortBufferUlong(
IN PULONG Port,
IN PULONG Buffer,
IN ULONG Count
);

ScsiPortReadPortBufferUlong transfers a given number of ULONG values from the HBA to a buffer.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of ULONG values to read from the HBA.

Comments

ScsiPortReadPortBufferUlong ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadPortBufferUshort

VOID
ScsiPortReadPortBufferUshort(
IN PUSHORT Port,
IN PUSHORT Buffer,
IN ULONG Count
);

ScsiPortReadPortBufferUshort transfers a given number of USHORT values from the HBA to a buffer.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of USHORT values to read from the HBA.

Comments

ScsiPortReadPortBufferUshort ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadPortUchar

UCHAR
ScsiPortReadPortUchar(
IN PUCHAR Port
);

ScsiPortReadPortUchar reads an unsigned byte value from the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.

Return Value

ScsiPortReadPortUchar returns an unsigned byte from the HBA’s I/O port.

Comments

ScsiPortReadPortUchar ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadPortUlong

ULONG
ScsiPortReadPortUlong(
IN PULONG Port
);

ScsiPortReadPortUlong reads a ULONG value from the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.

Return Value

ScsiPortReadPortUlong returns a ULONG value from the HBA’s I/O port.

Comments

ScsiPortReadPortUlong ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadPortUshort

USHORT
ScsiPortReadPortUshort(
IN PUSHORT Port
);

ScsiPortReadPortUshort reads a USHORT value from the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.

Return Value

ScsiPortReadPortUshort returns a USHORT value from the HBA’s I/O port.

Comments

ScsiPortReadPortUshort ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadRegisterBufferUchar

VOID
ScsiPortReadRegisterBufferUchar(
IN PUCHAR Register,
IN PUCHAR Buffer,
IN ULONG Count
);

ScsiPortReadRegisterBufferUchar transfers a specified number of unsigned bytes from the HBA to a buffer.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of bytes to be read from the HBA.

Comments

ScsiPortReadRegisterBufferUchar ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadRegisterBufferUlong

VOID
ScsiPortReadRegisterBufferUlong(
IN PULONG Register,
IN PULONG Buffer,
IN ULONG Count
);

ScsiPortReadRegisterBufferUlong transfers a specified number of ULONG values from the HBA to a buffer.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of ULONG values to be read from the HBA.

Comments

ScsiPortReadRegisterBufferUlong ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadRegisterBufferUshort

VOID
ScsiPortReadRegisterBufferUshort(
IN PUSHORT Register,
IN PUSHORT Buffer,
IN ULONG Count
);

ScsiPortReadRegisterBufferUshort transfers a specified number of USHORT values from the HBA to a buffer.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of USHORT values to be read from the HBA.

Comments

ScsiPortReadRegisterBufferUshort ensures that the data has been transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadRegisterUchar

UCHAR
ScsiPortReadRegisterUchar(
IN PUCHAR Register
);

ScsiPortReadRegisterUchar reads an unsigned byte value from the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.

Return Value

ScsiPortReadRegisterUchar returns an unsigned byte from the HBA’s register.

Comments

ScsiPortReadRegisterUchar ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadRegisterUlong

ULONG
ScsiPortReadRegisterUlong(
IN PULONG Register
);

ScsiPortReadRegisterUlong reads a ULONG value from the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.

Return Value

ScsiPortReadRegisterUlong returns a ULONG value from the HBA’s register.

Comments

ScsiPortReadRegisterUlong ensures that data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortReadRegisterUshort

USHORT
ScsiPortReadRegisterUshort(
IN PUSHORT Register
);

ScsiPortReadRegisterUshort reads a USHORT value from the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.

Return Value

ScsiPortReadRegisterUshort returns a USHORT value from the HBA’s register.

Comments

ScsiPortReadRegisterUshort ensures that the data is transferred correctly.

See Also

ScsiPortGetDeviceBase


ScsiPortSetBusDataByOffset

ULONG
ScsiPortSetBusDataByOffset(
IN PVOID DeviceExtension,
IN ULONG BusDataType,
IN ULONG SystemIoBusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
);

ScsiPortSetBusDataByOffset sets bus-configuration data for an adapter on a dynamically configurable I/O bus with a published, standard interface.

Parameters

DeviceExtension
Points to the miniport driver’s device extension.
BusDataType
Specifies the type of bus data to be set. Currently, its value can be PCIConfiguration. However, additional types of standardized, dynamically configurable buses will be supported in future. The upper bound on the bus types supported is always MaximumBusDataType.
SystemIoBusNumber
Specifies the system-assigned number of the I/O bus on which the HBA is connected. The miniport driver’s HwScsiFindAdapter routine obtains this value from the input PORT_CONFIGURATION_INFORMATION SystemIoBusNumber member.
SlotNumber
Specifies the logical slot number of the HBA.

When PCIConfiguration is specified as the BusDataType, this parameter must be specified as a PCI_SLOT_NUMBER-type value.

Buffer
Points to a caller-supplied storage area with configuration information specific to BusDataType.

When PCIConfiguration is specified, the buffer contains some or all of the PCI_COMMON_CONFIG information for the given SlotNumber. The specified Offset and Length determine how much information is supplied.

Offset
Specifies the byte offset within the PCI_COMMON_CONFIG structure at which the caller-supplied configuration values begin. A miniport driver can use PCI_COMMON_HDR_LENGTH to specify the offset of the device-specific area in PCI_COMMON_CONFIG.
Length
Specifies the number of bytes in the storage area at Buffer.

Return Value

ScsiPortSetBusDataByOffset returns the number of bytes of data successfully set for the given SlotNumber. If the given BusDataType is not valid for the current platform or if the supplied information is invalid, ScsiPortSetBusDataByOffset returns zero.

Comments

Miniport drivers of HBAs on a PCI bus seldom call ScsiPortSetBusDataByOffset unless unusual circumstances or the nature of a particular driver’s HBA requires such a call. For example, a miniport driver might call ScsiPortSetBusDataByOffset to clear a bit in the PCI status register if its HBA signals a target abort during initialization or to set device-specific configuration data for the HBA.

ScsiPortSetBusDataByOffset can be called only from the miniport’s HwScsiFindAdapter routine.

See Also

HalSetBusDataByOffset, HwScsiFindAdapter, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER, PORT_CONFIGURATION_INFORMATION, ScsiPortGetBusData


ScsiPortStallExecution

VOID
ScsiPortStallExecution(
IN ULONG Delay
);

ScsiPortStallExecution stalls in the miniport driver.

Parameters

Delay
Specifies the delay interval in microseconds. The given value must be less than a full millisecond.

Comments

ScsiPortStallExecution should be called as seldom as possible and the total stall time in a miniport driver routine must be less than one millisecond. This call ties up a processor, doing no useful work while stalling in the driver.

In general, a miniport should call ScsiPortStallExecution only if the driver must wait for a state change on the HBA that cannot cause an interrupt, or if the driver must delay for a very short interval between accesses to the HBA.

If a miniport’s HwScsiInterrupt routine must stall between accesses to the HBA and the total delay time in the ISR might be more than one millisecond, HwScsiInterrupt should call ScsiPortNotification with the NotificationType CallEnableInterrupts instead of calling ScsiPortStallExecution. Such a miniport has a pair of HwScsi..InterruptsCallback routines to manage its interrupt-driven I/O processing without tying up a processor and degrading the I/O performance of other HBAs that miniport driver might support in the same machine.

A miniport-supplied HwScsiTimer routine also can be passed in calls to ScsiPortNotification with a specified interval that is not limited to one millisecond.

See Also

HwScsiTimer, HwScsiDisableInterruptsCallback, HwScsiEnableInterruptsCallback, HwScsiInterrupt, ScsiPortNotification


ScsiPortValidateRange

BOOLEAN
ScsiPortValidateRange(
IN PVOID HwDeviceExtension,
IN INTERFACE_TYPE BusType,
IN ULONG SystemIoBusNumber,
IN SCSI_PHYSICAL_ADDRESS IoAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN InIoSpace
);

ScsiPortValidateRange indicates whether the specified access range values have already been claimed in the registry by another driver.

Parameters

HwDeviceExtension
Points to the per-HBA storage the OS-specific port driver allocated, initialized with zeros, and passed in to the miniport’s HwScsiFindAdapter routine.
BusType
Is the value of the AdapterInterfaceType member in the PORT_CONFIGURATION_INFORMATION structure when HwScsiFindAdapter is called.
SystemIoBusNumber
Is the value of the SystemIoBusNumber member in the configuration information when HwScsiFindAdapter is called.
IoAddress
Is a bus-relative base address for the range of ports or device memory to be validated before the miniport’s HwScsiFindAdapter routine attempts to map the access range for the adapter at that address.
NumberOfBytes
Specifies the size in bytes or number of elements in the range.
InIoSpace
TRUE indicates the range is in I/O space, rather than in memory.

Return Value

ScsiPortValidateRange returns TRUE if the HwScsiFindAdapter routine can safely map and use the mapped range to access the adapter. ScsiPortValidateRange returns FALSE if the specified access range values have already been claimed in the registry by another driver.

Comments

ScsiPortValidateRange can be called only from a miniport driver’s HwScsiFindAdapter routine. Calls from other miniport driver routines will result in system failure or incorrect operation for the caller.

If the OS-specific port driver initializes any AccessRanges element of the PORT_CONFIGURATION_INFORMATION structure before it calls the miniport’s HwScsiFindAdapter routine, the miniport must pass the supplied values to ScsiPortGetDeviceBase and use the mapped logical addresses for the range to determine whether an HBA is one that it supports.

The port driver either fills an ACCESS_RANGE-type element with a complete description of a bus-relative address range for an adapter, or the port driver zeros all members of the element.

For input AccessRanges elements set with default zeros, the HwScsiFindAdapter routine can attempt to locate an adapter it supports on the given I/O bus. In these circumstances, a miniport driver usually has a set of driver-determined default addresses for its type(s) of HBA. However, a previously loaded driver might already be using an initialized adapter at one of this miniport’s default address ranges, particularly in x86-only systems in which some devices are initialized in x86 real mode. To prevent such a device from being inadvertantly reprogrammed, each miniport driver’s HwScsiFindAdapter routine should call ScsiPortValidateRange before it maps any driver-supplied addresses with ScsiPortGetDeviceBase and then uses the mapped logical addresses to interrogate adapters on an I/O bus.

If ScsiPortValidateRange returns FALSE, HwScsiFindAdapter must not attempt to map the input range addresses because another driver has already claimed the range in the registry.

If ScsiPortValidateRange returns TRUE, HwScsiFindAdapter can safely do the following:

  1. Map the bus-relative range addresses to system-space logical range addresses with ScsiPortGetDeviceBase.

  2. Use the mapped logical addresses with the ScsiPortRead/Write Xxx to determine whether the adapter actually is an HBA that the driver supports.

If a miniport uses a range successfully passed to ScsiPortValidateRange for an HBA it supports, that driver must invert the InIoSpace value when it sets the RangeInMemory member of an AccessRanges element in the PORT_CONFIGURATION_INFORMATION.

See Also

ACCESS_RANGE, HwScsiFindAdapter, PORT_CONFIGURATION_INFORMATION, ScsiPortGetDeviceBase, ScsiPortInitialize, ScsiPortRead Xxx, ScsiPortWrite Xxx


ScsiPortWritePortBufferUchar

VOID
ScsiPortWritePortBufferUchar(
IN PUCHAR Port,
IN PUCHAR Buffer,
IN ULONG Count
);

ScsiPortWritePortBufferUchar transfers a given number of unsigned bytes from a buffer to the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to a buffer.
Count
Specifies the number of bytes to be written to the HBA.

See Also

ScsiPortGetDeviceBase


ScsiPortWritePortBufferUlong

VOID
ScsiPortWritePortBufferUlong(
IN PULONG Port,
IN PULONG Buffer,
IN ULONG Count
);

ScsiPortWritePortBufferUlong transfers a given number of ULONG values from a buffer to the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of ULONG values to be written to the HBA.

See Also

ScsiPortGetDeviceBase


ScsiPortWritePortBufferUshort

VOID
ScsiPortWritePortBufferUshort(
IN PUSHORT Port,
IN PUSHORT Value,
IN ULONG Count
);

ScsiPortWritePortBufferUshort transfers a given number of USHORT values from a buffer to the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to the buffer.
Count
Specifies the number of USHORT values to be written to the HBA.

See Also

ScsiPortGetDeviceBase


ScsiPortWritePortUchar

VOID
ScsiPortWritePortUchar(
IN PUCHAR Port,
IN UCHAR Value
);

ScsiPortWritePortUchar transfers an unsigned byte to the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Value
Specifies the value to be written to the HBA’s I/O port.

See Also

ScsiPortGetDeviceBase


ScsiPortWritePortUlong

VOID
ScsiPortWritePortUlong(
IN PULONG Port,
IN ULONG Value
);

ScsiPortWritePortUlong transfers a ULONG value to the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Value
Specifies the value to be written to the HBA’s I/O port.

See Also

ScsiPortGetDeviceBase


ScsiPortWritePortUshort

VOID
ScsiPortWritePortUshort(
IN PUSHORT Port,
IN USHORT Value
);

ScsiPortWritePortUshort transfers a USHORT value to the HBA.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by ScsiPortGetDeviceBase.
Value
Specifies the value to be written to the HBA’s I/O port.

See Also

ScsiPortGetDeviceBase


ScsiPortWriteRegisterBufferUchar

VOID
ScsiPortWriteRegisterBufferUchar(
IN PUCHAR Register,
IN PUCHAR Buffer,
IN ULONG Count
);

ScsiPortWriteRegisterBufferUchar transfers a given number of unsigned bytes from a buffer to the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to a buffer containing the data to be written.
Count
Specifies the number of bytes to be transferred to the HBA.

See Also

ScsiPortGetDeviceBase


ScsiPortWriteRegisterBufferUlong

VOID
ScsiPortWriteRegisterBufferUlong(
IN PULONG Register,
IN PULONG Buffer,
IN ULONG Count
);

ScsiPortWriteRegisterBufferUlong transfers a given number of ULONG values from a buffer to the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to a buffer containing the data to be written.
Count
Specifies the number of ULONG values to be transferred to the HBA.

See Also

ScsiPortGetDeviceBase


ScsiPortWriteRegisterBufferUshort

VOID
ScsiPortWriteRegisterBufferUshort(
IN PUSHORT Register,
IN PUSHORT Buffer,
IN ULONG Count
);

ScsiPortWriteRegisterBufferUshort transfers a given number of USHORT values from a buffer to the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Buffer
Points to a buffer containing the data to be written.
Count
Specifies the number of USHORT values to be transferred to the HBA.

See Also

ScsiPortGetDeviceBase


ScsiPortWriteRegisterUchar

VOID
ScsiPortWriteRegisterUchar(
IN PUCHAR Register,
IN ULONG Value
);

ScsiPortWriteRegisterUchar transfers a unsigned byte value to the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Value
Specifies the value to be written to the HBA’s register.

See Also

ScsiPortGetDeviceBase


ScsiPortWriteRegisterUlong

VOID
ScsiPortWriteRegisterUlong(
IN PULONG Register,
IN ULONG Value
);

ScsiPortWriteRegisterUlong transfers a ULONG value to the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Value
Specifies the value to be written to the HBA’s register.

See Also

ScsiPortGetDeviceBase


ScsiPortWriteRegisterUshort

VOID
ScsiPortWriteRegisterUshort(
IN PUSHORT Register,
IN USHORT Value
);

ScsiPortWriteRegisterUshort transfers a USHORT value to the HBA.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by ScsiPortGetDeviceBase.
Value
Specifies the value to be written to the HBA’s register.

See Also

ScsiPortGetDeviceBase


Chapter 2 SCSI Miniport Driver Routines

All SCSI miniport drivers must have some of the routines described in this chapter. Whether any particular miniport driver has others depends on the nature of its HBA(s) or on that miniport’s designer.

This chapter describes miniport routines in alphabetical order.

A miniport’s HwScsiXxx routines can have any name chosen by the driver writer. DriverEntry is a required name.


DriverEntry of SCSI Miniport Driver

ULONG
DriverEntry(
IN PVOID Argument1,
IN PVOID Argument2
);

Each miniport driver must have a routine explicitly named DriverEntry in order to be loaded.

Parameters

Argument1
Is a pointer with which the miniport driver must call ScsiPortInitialize.
Argument2
Is a pointer with which the miniport driver must call ScsiPortInitialize.

Return Value

DriverEntry returns the value returned by ScsiPortInitialize. If it calls ScsiPortInitialize more than once, DriverEntry returns the lowest value returned by ScsiPortInitialize.

Comments

A miniport’s DriverEntry routine allocates memory on the stack and initializes a HW_INITIALIZATION_DATA structure with zeros. DriverEntry must zero all members in the HW_INITIALIZATION_DATA structure before initializing it with values appropriate to the HBA(s) the miniport supports.

DriverEntry should set the HwInitializationDataSize member to sizeof(HW_INITIALIZATION_DATA) to indicate which version of this structure it is using, as well as initializing all members appropriately for its HBA(s).

Next, DriverEntry calls ScsiPortInitialize. If a miniport driver supports HBA(s) that can be connected on more than one type of I/O bus, such as both MicroChannel and Isa type buses, it should call ScsiPortInitialize once for each type of I/O bus. Such a miniport driver must return the lowest value returned by its calls to ScsiPortInitialize from the DriverEntry routine. A miniport driver writer can make no assumptions about the values returned by ScsiPortInitialize.

See Also

HwScsiFindAdapter, HW_INITIALIZATION_DATA, ScsiPortInitialize


HwScsiAdapterState

BOOLEAN
HwScsiAdapterState(
IN PVOID HwDeviceExtension,
IN PVOID Context,
IN BOOLEAN SaveState
);

A miniport driver’s HwScsiAdapterState routine saves or restores the HBA state according to the value of the SaveState parameter. If its HBA has a BIOS and, therefore, can be initialized in x86 real mode, a miniport driver must have this routine, which is called by the x86-only, OS-dependent port driver when the system transitions from x86 real to protected processor mode.

Parameters

HwDeviceExtension
Points to the miniport driver’s storage for per-HBA data.
Context
Is reserved for system use.
SaveState
TRUE indicates the miniport should save the current state of the HBA until the HwScsiAdapterState routine is called again with SaveState set to FALSE to restore the saved state.

Return Value

HwScsiAdapterState returns TRUE if it successfully saved or restored the HBA state.

Comments

Miniport drivers of HBAs that are initialized in x86 real mode and that are sensitive to x86-specific processor mode transitions must have a HwScsiAdapterState routine. This routine, which is usually called after the miniport’s HwScsiFindAdapter routine, preserves the initialized HBA state during x86-only system transition(s) from x86 real to protected processor mode.

HwScsiAdapterState is responsible for saving the current state of the HBA in the HwDeviceExtension when it is called with SaveState set to TRUE. It must restore the HBA state when it is called subsequently with SaveState set to FALSE.

Miniport drivers of HBAs that are initialized in x86 protected mode and miniports for which it is irrelevant whether their HBAs are being initialized on a CISC-or RISC-based platform need not have this routine.

See Also

HwScsiFindAdapter, ScsiPortInitialize


HwScsiDisableInterruptsCallback

BOOLEAN
HwScsiDisableInterruptsCallback(
IN PVOID DeviceExtension
);

HwScsiDisableInterruptsCallback is called after a miniport’s HwScsiEnableInterruptsCallback routine calls ScsiPortNotification with the NotificationType CallDisableInterrupts. Miniport drivers of HBAs that require interrupt processing that takes more than 50 microseconds in the ISR should have a pair of HwScsi..InterruptsCallback routines.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.

Return Value

HwScsiDisableInterruptsCallback returns TRUE, indicating it re-enabled interrupts on the HBA.

Comments

All system interrupts with a higher hardware priority than the HBA’s are enabled when the HwScsiDisableInterruptsCallback routine is called. Consequently, a HwScsiDisableInterruptsCallback routine should be optimized to run as quickly as possible.

HwScsiDisableInterruptsCallback is responsible for re-enabling interrupts on the HBA and then returning control.

See Also

HwScsiEnableInterruptsCallback, HwScsiInterrupt, ScsiPortNotification


HwScsiDmaStarted

PVOID
HwScsiDmaStarted(
IN PVOID HwDeviceExtension
);

If the HBA is a slave DMA device, the miniport driver’s HwScsiDmaStarted routine is called after the OS-specific port driver has set up the system DMA controller for a DMA transfer.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-HBA storage area.

Comments

Only miniport drivers of slave DMA adapters must have a HwScsiDmaStarted routine.

HwScsiDmaStarted is responsible for programming the HBA for the transfer operation.

Miniport drivers of HBAs that are busmasters or use programmed I/O do not supply this routine.

See Also

ScsiPortFlushDma, ScsiPortIoMapTransfer


HwScsiEnableInterruptsCallback

BOOLEAN
HwScsiEnableInterruptsCallback(
IN PVOID DeviceExtension
);

HwScsiEnableInterruptsCallback is called after a miniport’s HwScsiInterrupt routine disables interrupts on the HBA and calls ScsiPortNotification with the NotificationType CallEnableInterrupts. Miniport drivers of HBAs that require interrupt processing that takes more than 50 microseconds in the ISR should have a pair of HwScsi..InterruptsCallback routines.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.

Return Value

HwScsiEnableInterruptsCallback returns TRUE.

Comments

All system interrupts are enabled when the HwScsiEnableInterruptsCallback routine gets control, except for interrupts from the HBA. Because the miniport’s HwScsiInterrupt routine disables interrupts on the HBA before it calls ScsiPortNotification, the HwScsiEnableInterruptsCallback routine should be optimized to run as quickly as possible.

HwScsiEnableInterruptsCallback is responsible for completing the interrupt-driven I/O processing deferred by the HwScsiInterrupt routine, using the information it saved in the DeviceExtension. That is, HwScsiEnableInterruptsCallback completes the request that caused the interrupt to occur.

Then, HwScsiEnableInterruptsCallback calls ScsiPortNotification again with the NotificationType CallDisableInterrupts before it returns control.

See Also

HwScsiDisableInterruptsCallback, HwScsiInterrupt, ScsiPortNotification


HwScsiFindAdapter

ULONG
HwScsiFindAdapter(
IN PVOID DeviceExtension,
IN PVOID HwContext,
IN PVOID BusInformation,
IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again
);

HwScsiFindAdapter uses supplied configuration information to determine whether it supports a particular HBA and, if so, returns information about its HBA in the given ConfigInfo buffer.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area. The OS-specific port driver allocates memory for and initializes this extension with zeros before it calls the miniport’s HwScsiFindAdapter routine.
HwContext
Points to a context value.

Miniports that scan for supported adapters can define the context in their DriverEntry routine and pass its address to ScsiPortInitialize. ScsiPortInitialize passes the context pointer to HwScsiFindAdapter. Such a miniport driver can use HwContext to store state between calls to HwScsiFindAdapter.

Miniports that use configuration information from the port driver rather than scanning the I/O bus should not use this pointer. If HwScsiFindAdapter receives nonzero access range values from the port driver it should not use the HwContext pointer.

BusInformation
Points to bus-type-specific information that the OS-specific port driver has gathered. The format of this information depends on the particular bus type, which the miniport driver determines by setting the AdapterInterfaceType value in the HW_INITIALIZATION_DATA structure.
ArgumentString
Points to a zero-terminated ASCII string. This string originates from the user and, if used, contains device information such as a base parameter or an interrupt level from the registry.
ConfigInfo
Points to the PORT_CONFIGURATION_INFORMATION structure. The OS-specific port driver initializes this structure with any known configuration information, such as values the miniport set in the HW_INITIALIZATION_DATA and the SystemIoBusNumber. HwScsiFindAdapter must use any supplied information to determine if the HBA it describes is one that the miniport driver supports, and, if so, to initialize and configure that HBA and to fill in any missing relevant configuration information for that HBA. If possible, each miniport driver should have a set of default configuration values for each type of HBA it supports, in case the OS-dependent port driver cannot supply additional configuration information to that provided by the miniport’s DriverEntry routine.
Again
Points to a BOOLEAN variable to be set by HwScsiFindAdapter before it returns control. TRUE indicates that the miniport driver wants the OS-specific port driver to call its HwScsiFindAdapter routine again with a new DeviceExtension and the same ConfigInfo. This member is set to TRUE by miniport drivers that can support more than one HBA on an I/O bus.

Return Value

HwScsiFindAdapter must return one of the following status values:
Value | Meaning
SP_RETURN_FOUND | Indicates a supported HBA was found and that the HBA-relevant configuration information was successfully determined and set in the PORT_CONFIGURATION_INFORMATION structure.
SP_RETURN_ERROR | Indicates an HBA was found but there was error obtaining the configuration information. If possible, such an error should be logged with ScsiPortLogError.
SP_RETURN_BAD_CONFIG | Indicates the supplied configuration information was invalid for the adapter.
SP_RETURN_NOT_FOUND | Indicates no supported HBA was found for the supplied configuration information.

Comments

ScsiPortInitialize calls the HwScsiFindAdapter routine after allocating storage according to the DeviceExtensionSize that the miniport specified in the HW_INITIALIZATION_DATA structure.

First, every HwScsiFindAdapter routine should check the value of Length in the input PORT_CONFIGURATION_INFORMATION to determine whether the port driver has allocated a structure with all the configuration information the miniport needs to find and configure its HBA(s).

Next, every HwScsiFindAdapter routine should check the AccessRanges in the PORT_CONFIGURATION_INFORMATION to determine whether the OS-specific port driver collected and set bus-relative range-configuration information for an HBA. If HwScsiFindAdapter finds nonzero access-range values, it must map the supplied values with ScsiPortGetDeviceBase and use the returned logical range addresses to access the HBA.

If the input PORT_CONFIGURATION_INFORMATION contains AccessRanges elements filled with default zeros, the HwScsiFindAdapter routine can use any appropriate search logic, such as calling ScsiPortGetBusData, or use a set of driver-supplied bus-relative default values, to fill in each such access range element and determine its validity.

HwScsiFindAdapter should call ScsiPortValidateRange before it maps any miniport-supplied access range values with ScsiPortGetDeviceBase and uses the mapped logical addresses to access an adapter. ScsiPortValidateRange verifies that the device range has not already been claimed in the registry by another driver. If ScsiPortValidateRange returns FALSE, HwScsiFindAdapter cannot map and use that range and must not attempt to do so.

HwScsiFindAdapter must check the value of NumberOfPhysicalBreaks in the input PORT_CONFIGURATION_INFORMATION. The port driver usually sets this member to a particular value. The miniport driver can set NumberOfPhysicalBreaks to a lower value if its HBA supports fewer breaks between scatter/gather lists, but the miniport must not set it to a higher value than that supplied by the port driver. If NumberOfPhysicalBreaks is set to SP_UNINITIALIZED_VALUE, the miniport must fill in the actual number of breaks between scatter/gather lists that its underlying adapter can support.

HwScsiFindAdapter should also check the input port configuration information for additional port-driver-supplied values, such as interrupt vector or level, and DMA channel or port. Usually, the OS-specific port driver supplies additional configuration information if it supplies nonzero, bus-relative values for AccessRanges elements.

A miniport driver must use any valid configuration information it finds in the input port configuration information to configure the HBA.

The port driver is responsible for doing bus-level configuration programming, particularly of dynamically configurable I/O buses with published standard interfaces, such as PCI and PCMCIA. For an HBA on such an I/O bus, the miniport driver can simply accept the interrupt configuration information provided as values reserved for system use. For an HBA on other types of I/O buses, a miniport should program its HBA to use any supplied interrupt value if its HBA supports programmable interrupt configuration. If no interrupt configuration is supplied, as indicated either by the value zero or the value SP_UNINITIALIZED_VALUE, the miniport should either query its HBA if the HBA supports interrupt selection using jumpers or should supply a nonzero default interrupt configuration unless the HBA does not use interrupts. The miniport should set the interrupt configuration value to zero if it controls its HBA in a polled mode.

A miniport’s HwScsiFindAdapter routine should return with the Again parameter set to TRUE so the OS-specific SCSI port driver will search for additional HBAs on the I/O bus.

See Also

ACCESS_RANGE, DriverEntry, HW_INITIALIZATION_DATA, PORT_CONFIGURATION_INFORMATION, ScsiPortGetBusData, ScsiPortGetDeviceBase, ScsiPortInitialize, ScsiPortLogError, ScsiPortValidateRange


HwScsiInitialize

BOOLEAN
HwScsiInitialize(
IN PVOID DeviceExtension
);

HwScsiInitialize initializes the HBA after a reboot or a power failure occurs.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.

Return Value

If the initialization succeeds, HwScsiInitialize returns TRUE.

Comments

HwScsiInitialize must initialize the HBA but should avoid resetting the SCSI bus(es) if possible. Interrupts can occur before this routine is called. If HwScsiInitialize resets a bus, it must call ScsiPortNotification to report that the bus was reset.

If HwScsiInitialize cannot initialize the HBA, this routine should call ScsiPortLogError before returning FALSE when it returns control.

See Also

ScsiPortLogError, ScsiPortNotification


HwScsiInterrupt

BOOLEAN
HwScsiInterrupt(
IN PVOID DeviceExtension
);

HwScsiInterrupt is called when the HBA generates an interrupt. Miniport drivers of HBAs that do not generate interrupts do not have this routine.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.

Return Value

If the miniport finds that its HBA did not generate the interrupt, HwScsiInterrupt should return FALSE as soon as possible.

Comments

HwScsiInterrupt is responsible for completing interrupt-driven I/O operations. This routine must clear the interrupt on the HBA before it returns TRUE.

Miniport drivers of HBAs that require interrupt processing that takes more than 50 microseconds should have a pair of HwScsi..InterruptsCallback routines. Such a driver’s HwScsiInterrupt routine should do the following, rather than calling ScsiPortStallExecution to tie up the processor while its HBA updates hardware state:

  1. Disable interrupts on the HBA.

  2. Set up the DeviceExtension with any context necessary to complete the operation.

  3. Call ScsiPortNotification with the NotificationType value CallEnableInterrupts and the entry point for the driver’s HwScsiEnableInterruptsCallback routine.

  4. Return control.

Using this technique improves overall I/O throughput, including that of any other HBAs the miniport driver supports.

If a miniport’s HwScsiInterrupt routine cannot disable interrupts on its HBA but its interrupt-driven transfer operations take more than 50 microseconds in the HwScsiInterrupt routine, the miniport should limit the maximum size for transfers that it accepts. Such a miniport’s HwScsiFindAdapter routine can set the MaximumTransferLength member of the PORT_CONFIGURATION_INFORMATION structure to a value that restricts execution time in the HwScsiInterrupt routine for transfers. The miniport writer should adjust this value during driver development so there is no user-noticeable impact on the performance of the mouse, serial, and/or parallel drivers. Otherwise, the mouse pointer can appear to “jump” or the serial/parallel throughput to degrade noticeably whenever that miniport is transferring data.

See Also

HwScsiDisableInterruptsCallback, HwScsiEnableInterruptsCallback, ScsiPortNotification, ScsiPortStallExecution


HwScsiResetBus

BOOLEAN
HwScsiResetBus(
IN PVOID DeviceExtension,
IN ULONG PathId
);

HwScsiResetBus resets a given SCSI bus.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.
PathId
Identifies the SCSI bus to be reset.

Return Value

If the bus is successfully reset, HwScsiResetBus returns TRUE.

Comments

HwScsiResetBus can be called even if the miniport driver is not ready for another request. The miniport driver should complete all pending requests and must reset the given bus.

HwScsiResetBus need not call ScsiPortNotification to indicate that the bus was reset.

See Also

ScsiPortCompleteRequest, ScsiPortNotification


HwScsiStartIo

BOOLEAN
HwScsiStartIo(
IN PVOID DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);

All miniport drivers must have a HwScsiStartIo routine. The OS-specific port driver calls HwScsiStartIo first with each incoming I/O request for a target on a SCSI bus.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.
Srb
Points to the SCSI request block to be started.

Return Value

HwScsiStartIo returns TRUE to acknowledge receipt of the SRB.

Comments

As soon as it receives the initial request for a target peripheral, the OS-specific port driver calls the HwScsiStartIo routine with an input SRB. After this call, the HBA miniport driver owns the request and is expected to complete it.

Subsequently, the OS-specific port driver calls the HwScsiStartIo routine after the port driver receives each NextRequest, NextLuRequest, or notification as the miniport driver makes calls to ScsiPortNotification and/or ScsiPortCompleteRequest.

When the HwScsiStartIo routine is called but the driver needs to defer processing of the given SRB, HwScsiStartIo should do the following:

  1. Set the SrbStatus member to SRB_STATUS_BUSY.

  2. Call ScsiPortNotification with the request to be deferred and a notification type of RequestComplete.

  3. Return TRUE.

The port driver requeues such a request and resubmits it later.

See Also

ScsiPortCompleteRequest, ScsiPortNotification, SCSI_REQUEST_BLOCK


HwScsiTimer

VOID
HwScsiTimer(
IN PVOID DeviceExtension
);

HwScsiTimer is called after the interval specified when the miniport driver called ScsiPortNotification with the RequestTimerCall NotificationType value.

Parameters

DeviceExtension
Points to the miniport driver’s per-HBA storage area.

Comments

A HwScsiTimer routine is optional.

See Also

ScsiPortNotification


Chapter 3 SCSI Tape Class Driver Routines

The system-supplied SCSI tape class driver handles generic tape operations and provides OS-specific support to individual tape miniclass drivers.

This chapter describes the routines exported by the tape class driver for use by tape miniclass drivers. The routines are listed in alphabetical order.

These routines are defined in minitape.h.

For information about the SCSI tape class/miniclass architecture and designing a tape miniclass driver, see the Kernel-Mode Driver Design Guide.


TapeClassAllocateSrbBuffer

BOOLEAN
TapeClassAllocateSrbBuffer(
IN OUT PSCSI_REQUEST_BLOCK Srb,
IN ULONG SrbBufferSize
);

TapeClassAllocateSrbBuffer allocates an Srb->DataBuffer.

Parameters

Srb
Points to the SRB.
SrbBufferSize
Specifies the size, in bytes, of the DataBuffer to be allocated.

Return Value

TapeClassAllocateSrbBuffer returns TRUE if the DataBuffer was allocated successfully.

Comments

TapeClassAllocateSrbBuffer allocates an Srb->DataBuffer from nonpaged memory and initializes the members to zero. A tape miniclass driver calls this routine to allocate a DataBuffer in a portable way.


TapeClassCompareMemory

ULONG
TapeClassCompareMemory(
IN OUT PVOID Source1,
IN OUT PVOID Source2,
IN ULONG Length
);

TapeClassCompareMemory compares two memory buffers and returns the number of bytes that are equivalent.

Parameters

Source1
Points to the first buffer to be compared.
Source2
Points to the second buffer to be compared.
Length
Specifies the number of bytes to be compared.

Return Value

TapeClassCompareMemory returns the number of bytes that are equivalent.

Comments

A tape miniclass driver uses TapeClassCompareMemory to compare memory in a portable way. For example, a miniclass driver uses TapeClassCompareMemory in its TapeMiniVerifyInquiry routine to determine whether a given product ID matches one of the devices the driver supports.

See Also

TapeMiniVerifyInquiry


TapeClassInitialize

ULONG
TapeClassInitialize(
IN PVOID Argument1,
IN PVOID Argument2,
IN PTAPE_INIT_DATA TapeInitData
);

TapeClassInitialize performs much of the driver and device initialization on behalf of a miniclass driver. TapeClassInitialize loads the tape class driver entry points for tape I/O requests into the miniclass driver data structure, locates and claims unclaimed SCSI tape devices that the miniclass driver supports, and allocates and initializes the OS resources for the miniclass driver and its devices. TapeClassInitialize uses miniclass-driver-specific information supplied in TapeInitData and calls back to the tape miniclass driver for driver-specific operations.

Parameters

Argument1
Points to driver context information that was passed to the tape miniclass driver’s DriverEntry routine. The format of the information is OS-specific and must not be interpreted by a tape miniclass driver.
Argument2
Points to the second driver context structure that was passed to the tape miniclass driver’s DriverEntry routine. The format of the information is OS-specific and must not be interpreted by a tape miniclass driver.
TapeInitData
Points to a TAPE_INIT_DATA structure containing driver-specific information such as the entry points for the tape miniclass driver’s command processing routines.

Return Value

TapeClassInitialize returns a value indicating the success or failure of the driver initialization. The tape miniclass driver passes this value, uninterpreted, as the return value from its DriverEntry routine.

Comments

A tape miniclass driver calls TapeClassInitialize from its DriverEntry routine and passes driver-specific information in TapeInitData. TapeClassInitialize performs a large part of the driver initialization on behalf of the miniclass driver and insulates the miniclass driver from OS-specific details.

TapeClassInitialize calls the tape miniclass driver for driver-specific activities required during initialization. For example, TapeClassInitialize calls the tape miniclass driver’s TapeMiniVerifyInquiry routine to determine whether the driver supports a given tape device. TapeClassInitialize also calls the tape miniclass driver’s TapeMiniExtensionInit routine to initialize the minitape extension, if the miniclass driver requested one.

A tape miniclass driver allocates a TAPE_INIT_DATA structure on the stack, clears it with TapeClassZeroMemory, fills in all the appropriate members, and passes it to TapeClassInitialize.

See Also

TAPE_INIT_DATA, DriverEntry of Tape Miniclass Driver


TapeClassLogicalBlockToPhysicalBlock

TAPE_PHYS_POSITION
TapeClassLogicalBlockToPhysicalBlock(
IN UCHAR DensityCode,
IN ULONG LogicalBlockAddress,
IN ULONG BlockLength,
IN BOOLEAN FromBOT
);

TapeClassLogicalBlockToPhysicalBlock translates a pseudological block address to a physical block address. This routine is for SCSI-I devices.

Parameters

DensityCode
Specifies the tape media density code. This routine supports tapes with the following density codes: QIC_24, QIC_120, QIC_150, QIC_525, QIC_1000, QIC_2GB, QIC_1350, and QIC_2100.
LogicalBlockAddress
Specifies a pseudological block address.
BlockLength
Specifies the logical block size, in bytes.
FromBOT
TRUE indicates that the physical block calculation should start at the beginning of the tape and account for the physical device header. FALSE indicates that the tape has two partitions, that the block address is on the directory partition, and therefore no physical device header needs to be factored into the calculation.

Return Value

TapeClassLogicalBlockToPhysicalBlock returns a structure containing the physical block address:

typedef struct _TAPE_PHYS_POSITION {
    ULONG SeekBlockAddress;
    ULONG SpaceBlockCount;
} TAPE_PHYS_POSITION, PTAPE_PHYS_POSITION;

Comments

A tape miniclass driver calls TapeClassLogicalBlockToPhysicalBlock to translate a logical block address from an application to a physical block address for a tape device. TapeClassLogicalBlockToPhysicalBlock is not necessary for SCSI-II drivers because SCSI-II devices support logical block addressing.

To position a tape to the physical block address returned by this routine a tape miniclass driver issues two SCSI commands: a LOCATE command to position the tape to the SeekBlockAddress and then a SPACE command to advance the tape SpaceBlockCount. The SpaceBlockCount value is necessary if the pseudological blocks on the tape are smaller than the physical blocks; in that case, the logical block boundary might not align with a physical block boundary.

If a tape miniclass driver calls this routine with an unsupported tape density code, TapeClassLogicalBlockToPhysicalBlock does not perform any translation. It returns the logical block address in SeekBlockAddress and returns zero in SpaceBlockCount.

See Also

TapeClassPhysicalBlockToLogicalBlock


TapeClassPhysicalBlockToLogicalBlock

ULONG
TapeClassPhysicalBlockToLogicalBlock(
IN UCHAR DensityCode,
IN ULONG PhysicalBlockAddress,
IN ULONG BlockLength,
IN BOOLEAN FromBOT
);

TapeClassPhysicalBlockToLogicalBlock translates a physical block address to a pseudological block address. This routine is for SCSI-I devices.

Parameters

DensityCode
Specifies the tape media density code. This routine supports tapes with the following density codes: QIC_24, QIC_120, QIC_150, QIC_525, QIC_1000, QIC_2GB, QIC_1350, and QIC_2100.
PhysicalBlockAddress
Specifies the physical block address obtained by a SCSI READ POSITION command.
BlockLength
Specifies the logical block size, in bytes.
FromBOT
TRUE indicates that the logical block calculation should start at the beginning of the tape and account for the physical device header. FALSE indicates that the tape has two partitions, that the block address is on the directory partition, and therefore no physical device header needs to be factored into the calculation.

Return Value

TapeClassPhysicalBlockToLogicalBlock returns the logical block address.

Comments

A tape miniclass driver calls TapeClassPhysicalBlockToLogicalBlock to translate a physical block address from a tape device to a logical block address for an application. TapeClassPhysicalBlockToLogicalBlock is not necessary for SCSI-II drivers because SCSI-II devices support logical block addressing.

If a tape miniclass driver calls this routine with an unsupported tape density code, TapeClassPhysicalBlockToLogicalBlock returns the physical block address in the return value, without performing any translation.

See Also

TapeClassLogicalBlockToPhysicalBlock


TapeClassZeroMemory

VOID
TapeClassZeroMemory(
IN OUT PVOID Buffer,
IN ULONG BufferSize
);

TapeClassZeroMemory fills a buffer with zeros.

Parameters

Buffer
Points to the buffer that needs to be cleared.
BufferSize
Specifies the size of the buffer, in bytes.

Comments

A tape miniclass driver calls TapeClassZeroMemory to zero a buffer in a portable way. A miniclass driver must use TapeClassZeroMemory to clear the TAPE_INIT_DATA structure and CDBs before it uses them.

See Also

DriverEntry of Tape Miniclass Driver


Chapter 4 SCSI Tape Miniclass Driver Routines

This chapter describes the required and optional routines in a tape miniclass driver. The routines are listed in alphabetical order.

A tape miniclass driver’s TapeMiniXxx routines can have any name chosen by the driver writer. DriverEntry is a required name.

The term “TapeMiniProcessCommand routines” refers to the set of tape miniclass driver routines that implement device-specific processing for device control requests. For example, TapeMiniErase and TapeMiniSetDriveParameters are TapeMiniProcessCommand routines.

The routines, structures, and constants required by a tape miniclass driver are declared in minitape.h.

For information about the SCSI tape class/miniclass architecture and designing a tape miniclass driver see the Kernel-Mode Driver Design Guide. For a description of the tape commands see the Win32® SDK documentation.


DriverEntry of Tape Miniclass Driver

ULONG
DriverEntry(
IN PVOID Argument1,
IN PVOID Argument2
);

DriverEntry initializes a tape miniclass driver. This routine is required.

Parameters

Argument1
Points to driver context that a tape miniclass driver passes through to TapeClassInitialize. The format of the context information is OS-specific and must not be interpreted by portable tape miniclass drivers.
Argument2
Points to a second context structure that a tape miniclass driver passes through to TapeClassInitialize. The format of the context information is OS-specific and must not be interpreted by portable tape miniclass drivers.

Return Value

DriverEntry returns the value returned by its call to TapeClassInitialize.

Comments

DriverEntry is the initial entry point for a tape miniclass driver.

Because TapeClassInitialize performs most of the required driver initialization, DriverEntry’s primary task is to fill the TAPE_INIT_DATA structure with driver-specific constants and entry points.

DriverEntry calls TapeClassZeroMemory to clear the TAPE_INIT_DATA structure. DriverEntry then sets the values and pointers in the structure.

DriverEntry calls TapeClassInitialize and passes the address of TAPE_INIT_DATA and the two pointers that were passed to DriverEntry (Argument1 and Argument2). TapeClassInitialize completes driver initialization and returns status to the tape miniclass driver’s DriverEntry routine. DriverEntry returns the status that it received from TapeClassInitialize.

See Also

TapeClassInitialize, TapeClassZeroMemory, TAPE_INIT_DATA


TapeMiniCreatePartition

TAPE_STATUS
TapeMiniCreatePartition(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniCreatePartition handles the device-specific aspects of an IOCTL_TAPE_CREATE_PARTITION request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniCreatePartition must fill in the CDB in the SRB. TapeMiniCreatePartition might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_INSUFFICIENT_RESOURCES
TAPE_STATUS_INVALID_DEVICE_REQUEST
TAPE_STATUS_INVALID_PARAMETER
TAPE_STATUS_IO_DEVICE_ERROR
TAPE_STATUS_MEDIA_WRITE_PROTECTED
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniCreatePartition builds SRBs with CDBs to create a partition on a tape. It can take one or more SRBs to complete the request. TapeMiniCreatePartition returns each SRB to the tape class driver which sends it along to the device.

TapeMiniCreatePartition must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniCreatePartition can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

The Win32 tape interface does not fully support creating more than two partitions on a tape. As a workaround, if an application creates more than two partitions, the Size member in CommandParameters is an array of ULONGs rather than a single ULONG. The Count member specifies the number of elements in the array.

See Also

TapeClassZeroMemory


TapeMiniErase

TAPE_STATUS
TapeMiniErase(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniErase handles the device-specific aspects of an IOCTL_TAPE_ERASE request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniErase must fill in the CDB in the SRB. TapeMiniErase might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniErase builds SRBs with CDBs to erase a tape. It can take one or more SRBs to complete the request. TapeMiniErase returns each SRB to the tape class driver which sends it along to the device.

TapeMiniErase must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniErase can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniExtensionInit

VOID
TapeMiniExtensionInit(
IN OUT PVOID MinitapeExtension,
IN PINQUIRYDATA InquiryData,
IN PMODE_CAPABILITIES_PAGE ModeCapabilitiesPage /* optional */
);

TapeMiniExtensionInit initializes an optional, driver-specific context area. This routine is called by TapeClassInitialize when the tape miniclass driver is loaded. This routine is optional.

Parameters

MinitapeExtension
Points to a buffer of the size requested by the tape miniclass driver.
InquiryData
Points to the SCSI inquiry data for the tape device.
ModeCapabilitiesPage
Points to a buffer that contains low-level information for the tape device. The format of this page is defined by the evolving QIC 157 standard and is subject to change. The pointer is NULL if the device does not support a mode capabilities page.

Comments

A tape miniclass driver requests a minitape extension by specifying a nonzero value for MinitapeExtensionSize in TAPE_INIT_DATA in its DriverEntry routine. A miniclass driver defines the structure and contents of the minitape extension and typically uses it to store inquiry data for the devices it supports.

The tape class driver allocates the minitape extension and supplies it as a parameter to the TapeMiniProcessCommand routines and the optional TapeMiniTapeError routine.

TapeMiniExtensionInit loads information into the minitape extension. The tape class driver passes InquiryData and a ModeCapabilitiesPage to this routine because those structures contain information that a tape miniclass driver might want to store in the minitape extension.

See Also

DriverEntry of Tape Miniclass Driver, TapeClassInitialize, TAPE_INIT_DATA


TapeMiniGetDriveParameters

TAPE_STATUS
TapeMiniGetDriveParameters(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniGetDriveParameters handles the device-specific aspects of an IOCTL_TAPE_GET_DRIVE_PARAMS request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniGetDriveParameters must fill in the CDB in the SRB. TapeMiniGetDriveParameters might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_INSUFFICIENT_RESOURCES

Comments

TapeMiniGetDriveParameters builds SRBs with CDBs to get the drive parameters for a tape device. It can take one or more SRBs to complete the request. TapeMiniGetDriveParameters returns each SRB to the tape class driver which sends it along to the device.

TapeMiniGetDriveParameters must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniGetDriveParameters can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniGetMediaParameters

TAPE_STATUS
TapeMiniGetMediaParameters(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniGetMediaParameters handles the device-specific aspects of an IOCTL_TAPE_GET_MEDIA_PARAMS request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniGetMediaParameters must fill in the CDB in the SRB. TapeMiniGetMediaParameters might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_DATA_OVERRUN
TAPE_STATUS_INSUFFICIENT_RESOURCES

Comments

TapeMiniGetMediaParameters builds SRBs with CDBs to get media parameters for a tape device. It can take one or more SRBs to complete the request. TapeMiniGetMediaParameters returns each SRB to the tape class driver which sends it along to the device.

TapeMiniGetMediaParameters must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniGetMediaParameters can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniGetPosition

TAPE_STATUS
TapeMiniGetPosition(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniGetPosition handles the device-specific aspects of an IOCTL_TAPE_GET_POSITION request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniGetPosition must fill in the CDB in the SRB. TapeMiniGetPosition might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_INSUFFICIENT_RESOURCES
TAPE_STATUS_INVALID_DEVICE_REQUEST
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniGetPosition builds SRBs with CDBs to read the position of a tape. It can take one or more SRBs to complete the request. TapeMiniGetPosition returns each SRB to the tape class driver which sends it along to the device.

TapeMiniGetPosition must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniGetPosition can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniGetStatus

TAPE_STATUS
TapeMiniGetStatus(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniGetStatus handles the device-specific aspects of an IOCTL_TAPE_GET_STATUS request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniGetStatus must fill in the CDB in the SRB. TapeMiniGetStatus might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS

Comments

TapeMiniGetStatus builds SRBs with CDBs to read the status of a tape device. It can take one or more SRBs to complete the request. TapeMiniGetStatus returns each SRB to the tape class driver which sends it along to the device.

TapeMiniGetStatus must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniGetStatus can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniPrepare

TAPE_STATUS
TapeMiniPrepare(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniPrepare handles the device-specific aspects of an IOCTL_TAPE_PREPARE request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniPrepare must fill in the CDB in the SRB. TapeMiniPrepare might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniPrepare builds SRBs with CDBs to prepare a tape. It can take one or more SRBs to complete the request. TapeMiniPrepare returns each SRB to the tape class driver which sends it along to the device.

TapeMiniPrepare must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniPrepare can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniPreProcessReadWrite

TAPE_STATUS
TapeMiniPreProcessReadWrite(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* not used */
IN OUT PVOID CommandParameters, /* not used */
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber, /* not used */
 IN TAPE_STATUS StatusOfLastCommand, /* not used */
IN OUT PULONG RetryFlags /* not used */
);

TapeMiniPreProcessReadWrite is an optional, special-purpose routine that executes device-specific operations required before read and write operations. Most tape miniclass drivers do not need this routine.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Not used
CommandParameters
Not used.
Srb
Points to the SRB for the read or write operation. This routine should not modify the SRB.
CallNumber
Not used.
StatusOfLastCommand
Not used.
RetryFlags
Not used.

Return Value

TapeMiniPreProcessReadWrite can return any TAPE_STATUS_XXX value. The tape class driver ignores the return value.

Comments

The activities of the TapeMiniPreProcessReadWrite routine are device specific. The routine can use the information passed to it by the class driver to implement special preprocessing for reads and writes. If a drive has limited capabilities, the driver may need this routine to maintain coherent state, for example.

If this routine is nonNULL in TAPE_INIT_DATA, the tape class driver calls it before each read and write operation on the tape device. The class driver does not expect any information back from this routine.


TapeMiniSetDriveParameters

TAPE_STATUS
TapeMiniSetDriveParameters(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniSetDriveParameters handles the device-specific aspects of an IOCTL_TAPE_SET_DRIVE_PARAMS request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniSetDriveParameters must fill in the CDB in the SRB. TapeMiniSetDriveParameters might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_INSUFFICIENT_RESOURCES
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniSetDriveParameters builds SRBs with CDBs to set the parameters for a tape device. It can take one or more SRBs to complete the request. TapeMiniSetDriveParameters returns each SRB to the tape class driver which sends it along to the device.

TapeMiniSetDriveParameters must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniSetDriveParameters can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniSetMediaParameters

TAPE_STATUS
TapeMiniSetMediaParameters(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniSetMediaParameters handles the device-specific aspects of an IOCTL_TAPE_SET_MEDIA_PARAMS request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniSetMediaParameters must fill in the CDB in the SRB. TapeMiniSetMediaParameters might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_INSUFFICIENT_RESOURCES
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniSetMediaParameters builds SRBs with CDBs to set the parameters for a tape. It can take one or more SRBs to complete the request. TapeMiniSetMediaParameters returns each SRB to the tape class driver which sends it along to the device.

TapeMiniSetMediaParameters must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniSetMediaParameters can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniSetPosition

TAPE_STATUS
TapeMiniSetPosition(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniSetPosition handles the device-specific aspects of an IOCTL_TAPE_SET_POSITION request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniSetPosition must fill in the CDB in the SRB. TapeMiniSetPosition might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_INVALID_DEVICE_REQUEST
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniSetPosition builds SRBs with CDBs to position a tape. It can take one or more SRBs to complete the request. TapeMiniSetPosition returns each SRB to the tape class driver which sends it along to the device.

TapeMiniSetPosition must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniSetPosition can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


TapeMiniTapeError

VOID
TapeMiniTapeError(
IN PVOID MinitapeExtension, /* optional */
IN PSCSI_REQUEST_BLOCK Srb,
IN OUT PTAPE_STATUS TapeStatus
);

TapeMiniTapeError provides device-specific error handling when a SCSI request block is completed with an error status. This routine is optional.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. NULL if the miniclass driver does not use a minitape extension.
Srb
Points to the SCSI request block for the operation that failed.
TapeStatus
Points to the TAPE_STATUS set by the tape class driver. The tape miniclass driver can change the TAPE_STATUS or leave it as is.

Comments

Most tape miniclass drivers do not provide a TapeMiniTapeError routine. The TAPE_STATUS codes set by the tape class driver are appropriate.

For some devices, however, a tape miniclass driver can identify a more appropriate error code than the tape class driver. For example, for some devices the tape class driver returns a TAPE_STATUS_BUS_RESET, but the tape miniclass driver is able to determine that the real problem is more accurately represented by TAPE_STATUS_NO_MEDIA.

A tape miniclass driver optionally specifies a TapeMiniTapeError routine in TAPE_INIT_DATA in its DriverEntry routine. When an error occurs during a read, write, or device control command the tape class driver sets an appropriate TAPE_STATUS code. If the tape miniclass driver supplied a TapeMiniTapeError routine, the tape class driver calls that routine with the minitape extension, the failed SRB, and the TAPE_STATUS code that would be returned.

TapeMiniTapeError determines whether to alter the status provided by the tape class driver based on the input parameters and its knowledge of the device. If the SRB_STATUS_AUTOSENSE_VALID bit is set in Srb->SrbStatus, then the SRB contains SCSI sense information at SenseInfoBuffer. The length of the buffer is SenseInfoBufferLength. The TapeMiniTapeError routine should examine this information along with the SrbStatus and ScsiStatus in the SRB to determine whether or not to change the TAPE_STATUS returned.

See Also

SCSI_REQUEST_BLOCK


TapeMiniVerifyInquiry

BOOLEAN
TapeMiniVerifyInquiry(
IN PINQUIRYDATA InquiryData,
IN PMODE_CAPABILITIES_PAGE ModeCapabilitiesPage /* optional */
);

TapeMiniVerifyInquiry determines whether the tape miniclass driver recognizes and supports a given device. This routine is required.

Parameters

InquiryData
Points to a structure that contains information about the device.
ModeCapabilitiesPage
Points to a structure that contains low-level information about the device. The format of this structure is defined by the evolving QIC 157 standard and is subject to change. The pointer is NULL if the tape device does not support a mode capabilities page.

Return Value

TapeMiniVerifyInquiry returns TRUE if the miniclass driver supports the device.

Comments

TapeMiniVerifyInquiry examines the InquiryData, particularly the VendorId and ProductId members, to determine whether the tape miniclass driver supports the tape device. TapeMiniVerifyInquiry uses TapeClassCompareMemory to compare ID strings against values the tape miniclass driver supports.

See Also

TapeClassCompareMemory


TapeMiniWriteMarks

TAPE_STATUS
TapeMiniWriteMarks(
IN OUT PVOID MinitapeExtension, /* optional */
IN OUT PVOID CommandExtension, /* optional */
IN OUT PVOID CommandParameters,
 IN OUT PSCSI_REQUEST_BLOCK Srb,
 IN ULONG CallNumber,
 IN TAPE_STATUS StatusOfLastCommand, /* optional */
IN OUT PULONG RetryFlags /* optional */
);

TapeMiniWriteMarks handles the device-specific aspects of an IOCTL_TAPE_WRITE_MARKS request. It is one of the TapeMiniProcessCommand routines in a miniclass driver and is called by the tape class driver. This routine is required.

Parameters

MinitapeExtension
Points to the driver-specific minitape extension. The pointer is NULL if the miniclass driver did not specify a minitape extension.
CommandExtension
Points to the command extension. The pointer is NULL if the miniclass driver did not specify a command extension.
CommandParameters
Points to a buffer containing the parameters for the command. The structure for each I/O command (IOCTL) is defined in minitape.h.
Srb
Points to the partially filled SRB. TapeMiniWriteMarks must fill in the CDB in the SRB. TapeMiniWriteMarks might fill in other SRB members, depending on the requirements of its device(s).
CallNumber
Specifies the number of times the routine has been called to process a given tape command. CallNumber is zero the first time this routine is called for a given command and is incremented until the command request is complete.
StatusOfLastCommand
Specifies the status of the last command. The tape miniclass driver can set RETURN_ERRORS in RetryFlags to have the tape class driver return errors to the miniclass driver rather than directly to the application. Zero if RETURN_ERRORS was not set.
RetryFlags
Points to a value that specifies what action the tape class driver should take when a tape device reports an error.

The low-order word specifies the number of retries to perform in the event of a SCSI command failure. The default is zero (no retries).

The high-order word contains flags:

  • By default, the RETURN_ERRORS and IGNORE_ERRORS flags are clear and the tape class driver returns a failure status to the requesting application if an error occurs.

  • If RETURN_ERRORS is set, the tape class driver returns a failure status, but returns control to the TapeMiniProcessCommand routine rather than to the application.

  • If IGNORE_ERRORS is set, the tape class driver converts a failure status to a success status, and returns control to the TapeMiniProcessCommand routine rather than the application.

Return Value

TAPE_STATUS_SEND_SRB_AND_CALLBACK
Indicates to the tape class driver that the SRB has been filled in and is ready to be given to the device. By default, the tape miniclass driver’s TapeMiniProcessCommand routine is called back if the SRB is successfully completed by the tape class driver. A miniclass driver can modify the default behavior using RetryFlags.

TAPE_STATUS_CALLBACK
Directs the tape class driver to increment CallNumber and call back to the TapeMiniProcessCommand routine, without sending an SRB to the tape device.

TAPE_STATUS_CHECK_TEST_UNIT_READY
Directs the tape class driver to fill in an SRB for the TEST UNIT READY command and send the SRB to the device.

TAPE_STATUS_XXX
Any other return code indicates to the tape class driver that the command is complete and indicates success, failure, or warning. Possible completion return values for this routine include, but are not limited to:
TAPE_STATUS_SUCCESS
TAPE_STATUS_NOT_IMPLEMENTED

Comments

TapeMiniWriteMarks builds SRBs with CDBs to write marks to a tape. It can take one or more SRBs to complete the request. TapeMiniWriteMarks returns each SRB to the tape class driver which sends it along to the device.

TapeMiniWriteMarks must fill in the following members in the SRB before returning to the tape class driver:

  • Cdb - Points to the SCSI CDB for the command. Clear the CDB with TapeClassZeroMemory before filling it in.

  • CdbLength - Specifies the number of bytes in the CDB.

TapeMiniWriteMarks can optionally fill in the following members in the SRB:

  • DataBuffer - Points to the data buffer to be transferred. Use TapeClassAllocateSrbBuffer to allocate a DataBuffer of length greater than or equal to DataTransferLength.

  • DataTransferLength - Specifies the number of bytes to be transferred for this SCSI command. This member is set by TapeClassAllocateSrbBuffer.

  • TimeOutValue - Specifies a timeout value for this comand, overriding the default timeout value.

  • SrbFlags - Specifies a flag for this command. The SRB_FLAGS_DATA_OUT flag must be set if the SCSI command is sending data to the tape drive. This member can be zero if the SCSI command is receiving data from the tape drive or if no data is being transferred by the command.

See Also

TapeClassZeroMemory


Chapter 5 SCSI Structures

This chapter describes structures used by Windows NT® SCSI drivers, including class, miniclass, and filter drivers. It also describes structures used by OS-independent, SCSI miniport drivers that are linked with a Microsoft-supplied, OS-specific SCSI port driver.

Structures described in this chapter are in alphabetical order.


ACCESS_RANGE

typedef struct _ACCESS_RANGE {
    SCSI_PHYSICAL_ADDRESS RangeStart;
    ULONG RangeLength;
    BOOLEAN RangeInMemory;
} ACCESS_RANGE, *PACCESS_RANGE;

An ACCESS_RANGE describes a memory or I/O port range used by an HBA.

Members

RangeStart
Specifies the bus-relative base address of the range. This is an address that can be passed into ScsiPortGetDeviceBase.
RangeLength
Specifies the size in bytes or number of ports in the range. A miniport driver must ensure that this value matches the range actually decoded by the adapter. For example, if the HBA uses seven registers but responds to eight, this member should be set to eight.
RangeInMemory
TRUE indicates the range is in memory, rather than in I/O space.

Comments

Each ACCESS_RANGE is an AccessRanges array element within the PORT_CONFIGURATION_INFORMATION structure that is passed to a miniport driver’s HwScsiFindAdapter routine.

If possible, the OS-specific port driver sets up each access range element with a bus-relative HBA range for the miniport driver before calling the miniport’s HwScsiFindAdapter routine. Otherwise, the port driver zeros range elements for which it cannot supply configuration information.

If the port driver does supply a range, the miniport’s HwScsiFindAdapter routine should use only the supplied addresses and should never attempt to find other HBAs on the same bus using addresses of its own devising. Attempting to access other bus-relative port or memory ranges when the port driver has supplied range information, particularly in x86-only systems in which some devices are initialized in x86 real mode, can cause other devices on the bus to fail initialization or even cause the system to fail the boot process.

Each miniport driver should have a set of bus-relative default ranges to try if the OS-specific port driver cannot supply the information. A miniport driver must call ScsiPortValidateRange to check the safety of any miniport-supplied access range before it attempts to map such a range with ScsiPortGetDeviceBase and use the returned logical addresses to access an adapter, particularly if one of its HBAs has a BIOS.

Any access range that a miniport driver fills in for the OS-specific port driver in the PORT_CONFIGURATION_INFORMATION must have the RangeStart member set to a bus-relative address, such as a value returned by ScsiPortGetBusData.

The corresponding base logical address returned by ScsiPortGetDeviceBase should be stored, usually in the miniport’s device extension, as the RangeStart address for a mapped range of I/O ports or memory addresses used to call the ScsiPortRead Xxx and ScsiPortWrite Xxx routines.

See Also

HwScsiFindAdapter, PORT_CONFIGURATION_INFORMATION, ScsiPortConvertUlongToPhysicalAddress, ScsiPortGetBusData, ScsiPortGetDeviceBase, ScsiPortRead Xxx, ScsiPortValidateRange, ScsiPortWrite Xxx


HW_INITIALIZATION_DATA

typedef struct HW_INITIALIZATION_DATA {
    ULONG HwInitializationDataSize;
    INTERFACE_TYPE AdapterInterfaceType;
    PHW_INITIALIZE HwInitialize;
    PHW_STARTIO HwStartIo;
    PHW_INTERRUPT HwInterrupt;
    PHW_FIND_ADAPTER HwFindAdapter;
    PHW_RESET_BUS_BUS HwResetBus;
    PHW_DMA_STARTED HwDmaStarted;
    PHW_ADAPTER_STATE HwAdapterState;
    ULONG DeviceExtensionSize;
    ULONG SpecificLuExtensionSize;
    ULONG SrbExtensionSize;
    ULONG NumberOfAccessRanges;
    PVOID Reserved;
    BOOLEAN MapBuffers;
    BOOLEAN NeedPhysicalAddresses;
    BOOLEAN TaggedQueuing;
    BOOLEAN AutoRequestSense;
    BOOLEAN MultipleRequestPerLu;
    BOOLEAN ReceiveEvent;
    USHORT VendorIdLength;
    PVOID VendorId;
    USHORT ReservedUshort;
    USHORT DeviceIdLength;
    PVOID DeviceId;
} HW_INITIALIZATION_DATA, *PHW_INITIALIZATION_DATA;

Each SCSI miniport’s DriverEntry routine must initialize with zeros and, then, fill in the relevant HW_INITIALIZATION_DATA information for the OS-specific port driver.

Members

HwInitializationDataSize
Specifies the size of this structure in bytes, as returned by sizeof(). In effect, this member indicates the version of this structure being used by the miniport driver. A miniport’s DriverEntry routine should set this member’s value for the port driver.
AdapterInterfaceType
Specifies the type of I/O bus to which the HBA is connected, which can be one of the following: Internal, Isa, Eisa, MicroChannel, TurboChannel, or PCIBus. However, additional types of buses will be supported in future. The upper bound on the types of buses supported is always MaximumInterfaceType.

If this is set to PCIBus, the miniport driver must supply values for the VendorIdLength, VendorId, DeviceIdLength, and DeviceId members, described later.

HwInitialize
Points to the miniport’s HwScsiInitialize routine, which is a required entry point for all miniport drivers.
HwStartIo
Points to the miniport’s HwScsiStartIo routine, which is a required entry point for all miniport drivers.
HwInterrupt
Points to the miniport’s HwScsiInterrupt routine, which is a required entry point for any miniport driver of an HBA that generates interrupts. Set this to NULL if the miniport needs no ISR.
HwFindAdapter
Points to the miniport’s HwScsiFindAdapter routine, which is a required entry point for all miniport drivers.
HwResetBus
Points to the miniport’s HwScsiResetBus routine, which is a required entry point for all miniport drivers.
HwDmaStarted
Points to the miniport’s HwScsiDmaStarted routine if its HBA uses system DMA, that is, a system DMA controller. Set this to NULL if the HBA is a busmaster or uses PIO.
HwAdapterState
Points to the miniport’s HwScsiAdapterState routine, which is a required entry point for miniport drivers of HBAs with BIOSes that are linked with an OS-dependent, x86-platform-only port driver that must switch between x86 protected and real processor modes. If the miniport needs no HwScsiAdapterState routine, set this member to NULL. A miniport without this routine for an HBA that has a BIOS is incompatible with the x86-only port driver and unportable to an x86-only OS environment.
DeviceExtensionSize
Specifies the size in bytes required by the miniport driver for its per-HBA device extension. A miniport uses its device extension as storage for driver-determined HBA information. The OS-specific port driver initializes each device extension it allocates with zeros, and passes a pointer to the HBA-specific device extension in every call to a miniport driver except to its DriverEntry routine. The given size does not include any miniport-requested per-logical-unit storage, described next.
SpecificLuExtensionSize
Specifies the size in bytes required by the miniport driver for its per-logical-unit storage, if any. A miniport can use its LU extensions as storage for driver-determined logical-unit information about SCSI peripherals on the bus. The OS-specific port driver initializes each LU extension it allocates with zeros. Leave this member set to zero if the miniport does not maintain per-LU information for which it requires storage.
SrbExtensionSize
Specifies the size in bytes required by the miniport driver for its per-request storage, if any. A miniport can use SRB extensions as storage for driver-determined, request-specific information, such as data necessary to process a particular request. The OS-specific port driver does not initialize SRB extensions, but sets a pointer to this storage in each SRB it sends to the miniport driver. An SRB extension can be safely accessed by the HBA hardware. Leave this member set to zero if the miniport driver does not maintain per-SRB information for which it requires storage.
NumberOfAccessRanges
Specifies how many access ranges the adapter uses. Each is a range either of memory addresses or I/O port addresses. A typical HBA uses two ranges, one for its I/O ports and another for its device memory range.
Reserved
This member is reserved for system use and not available for use by miniport drivers.
MapBuffers
TRUE indicates that all data buffer addresses must be mapped to virtual addresses for access by the miniport driver.
NeedPhysicalAddresses
TRUE indicates that the miniport driver needs to translate its device, any per-LU, and any per-SRB extension addresses, as well as SRB buffer addresses, to physical addresses, as required by the HBA.
TaggedQueuing
TRUE indicates that miniport driver can support SCSI-II tagged queueing.
AutoRequestSense
TRUE indicates that the HBA can perform a request-sense operation without requiring an explicit request to do so. Only miniports driving HBAs with built-in firmware to perform request-sense operations should set this member to TRUE.
MultipleRequestPerLu
TRUE indicates that the miniport driver can queue multiple requests per logical unit, in particular, within the HBA. Note that an HBA must support auto request sense for its miniport to enable this functionality. If a miniport driver sets this member to TRUE, it must use each SRB QueueTag member for requests of this type, but the SRB_FLAGS_QUEUE_ACTION_ENABLE is not set in the SrbFlags member of the SCSI_REQUEST_BLOCK structure.
ReceiveEvent
TRUE indicates the miniport drives an HBA that can support the receive-event SRB for SCSI-II asynchronous events.
VendorIdLength
Specifies the size in bytes of the VendorId string, described next.
VendorId
Points to an ASCII byte string identifying the manufacturer of the HBA.

If the given AdapterInterfaceType is PCIBus, the vendor ID is a USHORT value allocated by the PCI SIG, which must be converted into a byte string by the miniport driver. For example, if the assigned PCI vendor ID value is 1001, the miniport-supplied VendorId string would be (‘1’, ‘0’, ‘0’, ‘1’).

ReservedUshort
This member is reserved for system use and is not available for use by miniport drivers.
DeviceIdLength
Specifies the size in bytes of the DeviceId string, described next.
DeviceId
Points to an ASCII byte string identifying the HBA model(s) supported by the miniport driver.

If the given AdapterInterfaceType is PCIBus, a device ID is a USHORT value assigned by the manufacturer of the HBA. The miniport must convert any PCI device ID value(s) for the HBA(s) it can support into DeviceId byte string(s), as for the VendorId member. For example, if a miniport can support HBAs with the PCI device IDs 8040 and 8050, it might set DeviceId with a pointer to the byte string (‘8’, ‘0’).

Comments

Each miniport driver must initialize the HW_INITIALIZATION_DATA structure with zeros before it sets the values of relevant members in this structure and calls ScsiPortInitialize.

See Also

DriverEntry, HwScsiInitialize , SCSI_REQUEST_BLOCK, ScsiPortInitialize


PORT_CONFIGURATION_INFORMATION

typedef struct _PORT_CONFIGURATION_INFORMATION {
    ULONG Length;
    ULONG SystemIoBusNumber;
    INTERFACE_TYPE AdapterInterfaceType;
    ULONG BusInterruptLevel;
    ULONG BusInterruptVector;
    KINTERRUPT_MODE InterruptMode;
    ULONG MaximumTransferLength;
    ULONG NumberOfPhysicalBreaks;
    ULONG DmaChannel;
    ULONG DmaPort;
    DMA_WIDTH DmaWidth;
    DMA_SPEED DmaSpeed;
    ULONG AlignmentMask;
    ULONG NumberOfAccessRanges;
    ACCESS_RANGE (*AccessRanges)[];
    PVOID Reserved;
    UCHAR NumberOfBuses;
    UCHAR InitiatorBusId[8];
    BOOLEAN ScatterGather;
    BOOLEAN Master;
    BOOLEAN CachesData;
    BOOLEAN AdapterScansDown;
    BOOLEAN AtdiskPrimaryClaimed;
    BOOLEAN AtdiskSecondaryClaimed;
    BOOLEAN Dma32BitAddresses;
    BOOLEAN DemandMode;
    BOOLEAN MapBuffers;
    BOOLEAN NeedPhysicalAddresses;
    BOOLEAN TaggedQueuing;
    BOOLEAN AutoRequestSense;
    BOOLEAN MultipleRequestPerLu;
    BOOLEAN ReceiveEvent;
    BOOLEAN RealModeInitialized;
    BOOLEAN BufferAccessScsiPortControlled;
    UCHAR MaximumNumberOfTargets;
    UCHAR ReservedUchars[2];
    ULONG SlotNumber;
    ULONG BusInterruptLevel2;
    ULONG BusInterrutVector2;
    KINTERRUPT_MODE InterruptMode2;
    ULONG DmaChannel2;
    ULONG DmaPort2;
    DMA_WIDTH DmaWidth2;
    DMA_SPEED DmaSpeed2;
} PORT_CONFIGURATION_INFORMATION,
  *PPORT_CONFIGURATION_INFORMATION;

PORT_CONFIGURATION_INFORMATION contains configuration information for an HBA. The OS-specific port driver allocates and initializes this structure, supplies as much HBA-specific configuration information as possible, and passes the structure to the miniport driver’s HwScsiFindAdapter routine. The port driver gets some of the information for this structure from the miniport’s HARDWARE_INITIALIZATION_DATA structure. The miniport’s HwScsiFindAdapter routine is responsible for determining whether the miniport driver can support the HBA and, if so, for filling in the pertinent remaining information in the PORT_CONFIGURATION_INFORMATION structure.

Members

Length
Specifies the size of this structure in bytes, as returned by sizeof(). In effect, this member, which is always initialized by the OS-specific port driver, indicates the version of this structure used by the port driver.
SystemIoBusNumber
Specifies the system-assigned number of the I/O bus to which the HBA is connected. The OS-specific port driver always initializes this member. Its value is system-assigned because the platform might have several I/O buses of the given AdapterInterfaceType.
AdapterInterfaceType
Identifies the I/O bus interface. The OS-specific port driver always sets this member to the value specified by the miniport driver in the HW_INITIALIZATION_DATA structure.
BusInterruptLevel
Specifies the bus-relative interrupt request level. The OS-specific port driver makes no assumptions about the HBA’s interrupt usage, so the default value is zero. Depending on the given AdapterInterfaceType and HBA, the value set for this member can correspond to the IRQL for the bus, such as for Isa and MicroChannel type buses. Drivers of Eisa HBAs must set this value to the bus-relative IRQL for the HBA if the adapter is configured for level-sensitive interrupts.
BusInterruptVector
Specifies the bus-relative vector returned by the HBA. The OS-specific port driver makes no assumptions about the HBA’s interrupt usage, so the default value is zero. This member is irrelevant to drivers that set up the BusInterruptLevel member for their HBAs. It is pertinent for HBAs on types of I/O buses that use interrupt vectors, such as PCIBus.
InterruptMode
Specifies whether the HBA uses LevelSensitive or Latched interrupts. The OS-specific port driver always initializes this member to Latched (sometimes called “edge-triggered”). Drivers of Eisa HBAs must reset this value if the adapter is configured for level-sensitive interrupts, as must drivers of HBAs on I/O buses that use level-sensitive interrupts, such as MicroChannel type buses.
MaximumTransferLength
Specifies the maximum number of bytes the HBA can transfer in a single transfer operation. By default, the value of this member is SP_UNINITIALIZED_VALUE, which indicates an unlimited maximum transfer size. If its HBA has more limited transfer support, a miniport driver must reset this member according to the HBA’s transfer capacity. If a miniport’s HwScsiInterrupt routine cannot disable interrupts on the HBA, this member can be adjusted during driver development to ensure that time spent in that miniport’s ISR does not cause the mouse to “jump” or serial/parallel throughput to drop to a user-noticeable level.
NumberOfPhysicalBreaks
Specifies the maximum number of breaks between address ranges that a data buffer can have if the HBA supports scatter/gather. In other words, the number of scatter/gather lists minus one. By default, the value of this member is SP_UNINITIALIZED_VALUE, which indicates the HBA can support an unlimited number of physical discontiguities. If the port driver sets a value for this member, the miniport driver can adjust the value lower but no higher. If this member is SP_UNINITIALIZED_VALUE, the miniport driver must reset this member according to the HBA’s scatter/gather capacity, with zero representing no scatter/gather support.
DmaChannel
Specifies the DMA channel used by a slave HBA. By default, the value of this member is SP_UNINITIALIZED_VALUE. If the HBA uses a system DMA controller and the given AdapterInterfaceType is any value except MicroChannel, the miniport driver must reset this member.
DmaPort
Specifies the DMA port used by a slave HBA. By default, the value of this member is SP_UNINITIALIZED_VALUE. If the HBA uses a system DMA controller and the given AdapterInterfaceType is MicroChannel, the miniport driver must reset this member.
DmaWidth
Specifies the width of DMA transfers if the HBA uses DMA. By default, the value of this member is zero. If its HBA does DMA, the miniport must reset this member to one of the following: Width8Bits, Width16Bits, or Width32Bits.
DmaSpeed
Specifies the DMA data-transfer speed for Eisa HBAs. By default, the value of this member specifies compatibility timing. Acceptable values for this member are any of the following: Compatible, TypeA, TypeB, or TypeC.
AlignmentMask
Is a mask indicating the alignment boundary for buffers required by the HBA for transfer operations. A miniport driver should set this mask to indicate any alignment restrictions on scatter/gather buffer pointers if the HBA supports scatter/gather. For example, the value 0x00000001 indicates (two-byte) word alignment.
NumberOfAccessRanges
Specifies the number of AccessRanges elements in the array, described next. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize.
AccessRanges
Points to an array of ACCESS_RANGE-type elements. The given NumberOfAccessRanges determines how many elements must be configured with bus-relative range values. The AccessRanges pointer must be NULL if NumberOfAccessRanges is zero. By default, all values within array elements are zero. If possible, the OS-specific port driver initializes this member’s elements, possibly from the registry or possibly by interrogating an I/O bus, for an HBA it found by using the miniport’s specified HW_INITIALIZATION_DATA.

If any access range element is set to nondefault values on entry to the miniport’s HwScsiFindAdapter routine, the miniport must map the supplied addresses with ScsiPortGetDeviceBase and use the corresponding logical addresses to verify whether that HBA is one it supports. If the input AccessRanges have nondefault values supplied, a miniport should never attempt to scan the I/O bus for HBAs it supports using a set of driver-devised port/memory addresses that its HBA(s) might use.

Reserved
Reserved for system use and not available for use by miniport drivers.
NumberOfBuses
Specifies the number of SCSI buses controlled by the HBA. By default, the value of this member is zero.
InitiatorBusId
If the input InitiatorBusId[0] has the value zero, the miniport can assign a default value if its HBA does not require the use of particular value(s) determined by querying the HBA. Otherwise, the miniport should use any nonzero value assigned by the port driver if possible. Every miniport must update InitiatorBusId specifications to match what its HBA uses, if necessary querying the HBA to determine the appropriate values(s).

A miniport driver must set an entry for each SCSI bus supported by the HBA, as indicated by the value of NumberOfBuses.

ScatterGather
TRUE indicates the HBA supports scatter/gather. By default, the value of this member is FALSE.
Master
TRUE indicates the HBA is a busmaster. By default, the value of this member is FALSE.
CachesData
TRUE indicates the HBA caches data or maintains cached state on the peripherals. By default, the value of this member is FALSE. If this is reset to TRUE, the OS-specific port driver notifies the miniport driver when certain system events occur, such as file system cache flushes and system shutdown. For example, if a controller on the bus mirrors two disks, the miniport driver would normally set this member to TRUE.
AdapterScansDown
TRUE indicates the BIOS for the HBA scans for SCSI bus targets from seven to zero. By default, the value of this member is FALSE.
AtdiskPrimaryClaimed
TRUE indicates that the primary “AT” (WD1003-compatible) disk address range, 0x1F0 to 0x1FF, has been claimed in the registry by another driver for its device. The OS-specific port driver always initializes this member. If the input value of this member is TRUE, a miniport driver should assume that an already loaded driver has claimed the device range. If the input value is FALSE, a miniport driver can claim the address range for an HBA that emulates an “AT” disk controller and reset this member to TRUE, thereby preventing access to this range by the “AT” disk driver or subsequently loaded miniport drivers.
AtdiskSecondaryClaimed
TRUE indicates that the secondary AT disk address range, 0x170 to 0x17F has been claimed in the registry by another driver for its device. The OS-specific port driver always initializes this member. Miniport drivers should treat this member as for AtdiskPrimaryClaimed.
Dma32BitAddresses
TRUE indicates that the HBA has 32 address lines and can access memory with physical addresses greater than 0x00FFFFFF. By default, the value of this member is FALSE.
DemandMode
TRUE indicates the system DMA controller should be programmed for demand-mode rather than single-cycle operations. If the HBA is not a slave device, this member should be FALSE.
MapBuffers
TRUE indicates data buffers must be mapped to system virtual address ranges. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize. A miniport driver can reset this value for any particular HBA it controls. If FALSE, the miniport must not access Srb.DataBuffer directly.
NeedPhysicalAddresses
TRUE indicates the miniport driver must translate virtual addresses to physical addresses, as required by the HBA. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize. A miniport driver can reset this value for any particular HBA it controls.
TaggedQueuing
TRUE indicates the HBA supports queuing of multiple requests with SCSI-II tags. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize. A miniport driver can reset this value for any particular HBA it controls.
AutoRequestSense
TRUE indicates the HBA supports auto request sense. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize. A miniport driver can reset this value for any particular HBA it controls. This member must be set to TRUE before the driver of a busmaster HBA calls ScsiPortGetUncachedExtension.
MultipleRequestPerLu
TRUE indicates the HBA supports multiple requests per logical unit. The mechanism by which the adapter caches the requests is adapter-defined. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize. A miniport driver can reset this value for any particular HBA it controls.
ReceiveEvent
TRUE indicates the HBA supports SCSI receive-event operations. The OS-specific port driver always sets this member to the value passed in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize. A miniport driver can reset this value for any particular HBA it controls.
RealModeInitialized
TRUE indicates an (x86-only) real-mode driver has already initialized the HBA. The OS-dependent port driver always initializes this member. If this is set to TRUE, the miniport driver must not initialize its HBA if the driver’s initialization sequence is identical to real-mode HBA initialization; not reinitializing the HBA in these circumstances makes the miniport driver load much faster. The value of this member is irrelevant to any miniport that always initializes its HBA while running in protected mode on x86-based platforms and to all miniports for which it is immaterial whether they are running on a CISC- or RISC-based platform.
BufferAccessScsiPortControlled
TRUE indicates that the miniport always calls ScsiPort Xxx to access data buffers and/or request-sense information in SRBs, thereby allowing the OS-specific port driver to optimize I/O request processing. Setting this member to FALSE can degrade the I/O performance of the HBA. Calling routines other than the ScsiPort Xxx makes a miniport driver nonportable across Microsoft operating systems.
MaximumNumberOfTargets
Specifies the number of target peripherals the HBA can control. By default, the value of this member is SCSI_MAXIMUM_TARGETS, according to the SCSI-II standard. A miniport can reset this member to a lesser value if the HBA has more limited capabilities or to a greater value, indicating that the HBA has wide-SCSI capabilities.
ReservedUchars[2]
Reserved for system use and not available for use by miniport drivers.
SlotNumber
Specifies the slot number of an HBA with matching values to the VendorId and DeviceId that were specified in the HW_INITIALIZATION_DATA structure when the miniport driver called ScsiPortInitialize.
BusInterruptLevel2
Reserved for system use and not available for use by miniport drivers.
BusInterruptVector2
Reserved for system use and not available for use by miniport drivers.
InterruptMode2
Reserved for system use and not available for use by miniport drivers.
DmaChannel2
Reserved for system use and not available for use by miniport drivers.
DmaPort2
Reserved for system use and not available for use by miniport drivers.
DmaWidth2
Reserved for system use and not available for use by miniport drivers.
DmaSpeed2
Reserved for system use and not available for use by miniport drivers.

Comments

The specific members initialized depend on the HBA miniport driver and on the configuration information available to the OS-specific port driver. The OS-specific port driver sets default values in all members for which it cannot supply configuration information to the miniport’s HwScsiFindAdapter routine.

All HBA miniport drivers should have at least one set of defaults to use for relevant members if the OS-specific port driver does not pass in all initialized values.

The HwScsiFindAdapter routine must update all members relevant to an HBA that the driver supports.

Windows NT SCSI class drivers, which load later than miniport drivers, depend on the information supplied by each miniport’s HwScsiFindAdapter routine to set up their subsequent I/O requests. For example, the MaximumTransferLength and NumberOfPhysicalBreaks values supplied by each miniport driver control whether a class driver must split large transfer requests into a set of partial transfers to suit the limits of the HBA.

See Also

ACCESS_RANGE, DriverEntry, HwScsiFindAdapter, ScsiPortGetDeviceBase, ScsiPortGetUncachedExtension, ScsiPortInitialize, ScsiPortValidateRange


SCSI_REQUEST_BLOCK

typedef struct _SCSI_REQUEST_BLOCK {
    USHORT Length;
    UCHAR Function;
    UCHAR SrbStatus;
    UCHAR ScsiStatus;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR QueueTag;
    UCHAR QueueAction;
    UCHAR CdbLength;
    UCHAR SenseInfoBufferLength;
    ULONG SrbFlags;
    ULONG DataTransferLength;
    ULONG TimeOutValue;
    PVOID DataBuffer;
    PVOID SenseInfoBuffer;
    struct _SCSI_REQUEST_BLOCK *NextSrb;
    PVOID OriginalRequest;
    PVOID SrbExtension;
    ULONG QueueSortKey;
    UCHAR Cdb[16];
} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK;

Members

Length
Specifies the size in bytes of this structure.
Function
Specifies the operation to be performed, which can be one of the following values:

Operation

Meaning

SRB_FUNCTION_EXECUTE_SCSI

A SCSI device I/O request should be executed on the target logical unit.

SRB_FUNCTION_ABORT_COMMAND

A SCSIMESS_ABORT message should be sent to cancel the request pointed to by the NextSrb member. If this is a tagged-queue request, a SCSIMESS_ABORT_WITH_TAG message should be used instead. If the indicated request has been completed, this request should be completed normally. Only the SRB Function, PathId, TargetId, Lun, and NextSrb members are valid.

SRB_FUNCTION_RESET_DEVICE

The SCSI target controller should be reset using the SCSIMESS_BUS_DEVICE_RESET message. The miniport driver should complete any active requests for the target controller. Only the SRB Function, TargetId, and PathId members are valid.

SRB_FUNCTION_RESET_BUS

The SCSI bus should be reset using the SCSIMESS_BUS_DEVICE_RESET message. A miniport driver receives this request only if a given request has timed out and a subsequent request to abort the timed-out request also has timed out. Only the SRB Function and PathId members are valid.

SRB_FUNCTION_TERMINATE_IO

A SCSIMESS_TERMINATE_IO_PROCESS message should be sent to cancel the request pointed to by the NextSrb member. If the indicated request has already completed, this request should be completed normally. Only the SRB Function, PathId, TargetId, Lun, and NextSrb members are valid.

SRB_FUNCTION_RELEASE_RECOVERY

A SCSIMESS_RELEASE_RECOVERY message should be sent to the target controller. Only the SRB Function, PathId, TargetId, and Lun members are valid.

SRB_FUNCTION_RECEIVE_EVENT

The HBA should be prepared to receive an asynchronous event notification from the addressed target. The SRB DataBuffer member indicates where the data should be placed.

SRB_FUNCTION_SHUTDOWN

The system is being shut down. This request is sent to a miniport driver only if it set CachesData to TRUE in the PORT_CONFIGURATION_INFORMATION for the HBA. Such a miniport driver can receive several of these notifications before all system activity actually stops. However, the last shutdown notification will occur after the last start I/O. Only the SRB Function, PathId, TargetId, and Lun members are valid.

SRB_FUNCTION_FLUSH

The miniport should flush any cached data for the target device. This request is sent to the miniport driver only if it set CachesData to TRUE in the PORT_CONFIGURATION_INFORMATION for the HBA. Only the SRB Function, PathId, TargetId, and Lun members are valid.

SRB_FUNCTION_IO_CONTROL

The request is an I/O control request, originating in a user-mode application with a dedicated HBA. The SRB DataBuffer points to an SRB_IO_CONTROL header followed by the data area. The value in DataBuffer can be used by the driver, regardless of the value of MapBuffers. Only the SRB Function, SrbFlags, TimeOutValue, DataBuffer, and DataTransferLength members are valid, along with the SrbExtension member if the miniport driver requested SRB extensions when it initialized. If a miniport driver controls an application-dedicated HBA so it supports this request, the miniport should execute the request and notify the OS-specific port driver when the SRB has completed, using the normal mechanism of calls to ScsiPortNotification with RequestComplete and NextRequest.

SrbStatus
Returns the status of the completed request. This member should be set by the miniport driver before it notifies the OS-specific driver that the request has completed by calling ScsiPortNotification with RequestComplete. The value of this member can be one of the following:

Status

Meaning

SRB_STATUS_PENDING

Indicates the request is in progress. The OS-specific port driver initializes SrbStatus to this value. A miniport driver should never set the SrbStatus member to this value.

SRB_STATUS_SUCCESS

Indicates the request was completed successfully.

SRB_STATUS_ABORTED

Indicates the request was aborted as directed by the port driver. A miniport driver sets this status in the NextSrb for a successful SRB_FUNCTION_ABORT_COMMAND request.

SRB_STATUS_ABORT_FAILED

Indicates an attempt to abort the request failed. Return this status for an SRB_FUNCTION_ABORT_COMMAND request when the specified request cannot be located.

SRB_STATUS_ERROR

Indicates the request was completed with an error in the SCSI bus status.

SRB_STATUS_BUSY

Indicates the miniport or target device could not accept the request at this time. The OS-specific port driver will resubmit the request later.

SRB_STATUS_INVALID_REQUEST

Indicates the miniport driver does not support the given request.

SRB_STATUS_NO_DEVICE

Indicates the device did not respond.

SRB_STATUS_TIMEOUT

Indicates the request timed out.

SRB_STATUS_SELECTION_TIMEOUT

Indicates the SCSI device selection timed out.

SRB_STATUS_COMMAND_TIMEOUT

Indicates the target did not complete the command within the time limit.

SRB_STATUS_MESSAGE_REJECTED

Indicates the target rejected a message. This is normally returned only for such message-type requests as SRB_FUNCTION_TERMINATE_IO.

SRB_STATUS_BUS_RESET

Indicates a bus reset occurred while this request was being executed.

SRB_STATUS_PARITY_ERROR

Indicates a parity error occurred on the SCSI bus and that a retry failed.

SRB_STATUS_REQUEST_SENSE_FAILED

Indicates the request-sense command failed. This is returned only if the HBA performs auto request sense and the miniport driver set AutoRequestSense to TRUE in the PORT_CONFIGURATION_INFORMATION for this HBA.

SRB_STATUS_NO_HBA

Indicates the HBA does not respond.

SRB_STATUS_DATA_OVERRUN

Indicates that a data overrun or underrun error occurred. The miniport also must update the SRB’s DataTransferLength member to indicate how much data actually was transferred if an underrun occurs.

SRB_STATUS_UNEXPECTED_BUS_FREE

Indicates the target disconnected unexpectedly.

SRB_STATUS_PHASE_SEQUENCE_FAILURE

Indicates the HBA detected an illegal phase sequence failure error.

SRB_STATUS_REQUEST_FLUSHED

Indicates the request for status was stopped.

SRB_STATUS_BAD_FUNCTION

Indicates the SRB Function code is not supported.

SRB_STATUS_INVALID_PATH_ID

Indicates the PathId specified in the SRB does not exist.

SRB_STATUS_INVALID_TARGET_ID

Indicates the TargetID value in the SRB is invalid.

SRB_STATUS_INVALID_LUN

Indicates the Lun value in the SRB is invalid.

SRB_STATUS_ERROR_RECOVERY

Indicates the request was completed with an error in the SCSI bus status and that the SCSI INITIATE RECOVERY message was received.

SRB_STATUS_AUTOSENSE_VALID

Indicates information returned in the SenseInfoBuffer is valid.

SRB_STATUS_QUEUE_FROZEN

A miniport driver should never set the SrbStatus member to this value. The Windows NT port driver can set this value to inform a SCSI class driver that its queue of requests for a particular peripheral has been frozen.

ScsiStatus
Returns the SCSI status that was returned by the HBA or target device. If the status is not SUCCESS, the miniport driver should set the SrbStatus member to SRB_STATUS_ERROR.
PathId
Indicates the SCSI port or bus for the request. This value is zero-based.
TargetId
Indicates the target controller or device on the bus.
Lun
Indicates the logical unit number of the device.
QueueTag
Is the queue-tag value assigned by the OS-specific port driver. If this member is used for tagged queueing, the HBA supports internal queueing of requests to LUs and the miniport driver set TaggedQueuing to TRUE in the PORT_CONFIGURATION_INFORMATION for this HBA.
QueueAction
Indicates the tagged-queueing message to be used when the SRB_FLAGS_QUEUE_ACTION_ENABLE flag is set. The value can be one of the following: SRB_SIMPLE_TAG_REQUEST, SRB_HEAD_OF_QUEUE_TAG_REQUEST, or SRB_ORDERED_QUEUE_TAG_REQUEST, as defined according to the SCSI-II specification.
CdbLength
Indicates the size in bytes of the SCSI command descriptor block.
SenseInfoBufferLength
Indicates the size in bytes of the request-sense buffer.
SrbFlags
Indicates various parameters and options about the request. SrbFlags is read-only, except when SRB_FLAGS_UNSPECIFIED_DIRECTION is set and miniport drivers of slave DMA adapters are required to update SRB_FLAGS_DATA_IN or SRB_FLAGS_DATA_OUT. This member can have one or more of the following flags set:

Value

Meaning

SRB_FLAGS_QUEUE_ACTION_ENABLE

Indicates tagged-queue actions are to be enabled.

SRB_FLAGS_DISABLE_AUTOSENSE

Indicates that request-sense information should not be returned.

SRB_FLAGS_DATA_IN

Indicates data will be transferred from the device to the system.

SRB_FLAGS_DATA_OUT

Indicates data will be transferred from the system to the device.

SRB_FLAGS_UNSPECIFIED_DIRECTION

Defined for backward compatibility with the ASPI/CAM SCSI interfaces, this flag indicates that the transfer direction could be either of the preceding because both of the preceding flags are set. If this flag is set, a miniport driver should determine the transfer direction by examining the data phase for the target on the SCSI bus. If its HBA is a slave DMA device, such a miniport must update SRB_FLAGS_DATA_OUT or SRB_FLAGS_DATA_IN to the correct value before it calls ScsiPortIoMapTransfer.

SRB_FLAGS_NO_DATA_TRANSFER

Indicates no data transfer with this request. If this is set, the flags SRB_FLAGS_DATA_OUT, SRB_FLAGS_DATA_IN, and SRB_FLAGS_UNSPECIFIED_DIRECTION are clear.

SRB_FLAGS_DISABLE_SYNCH_TRANSFER

Indicates the HBA, if possible, should not perform a synchronous negotiation for this transfer request.

SRB_FLAGS_DISABLE_DISCONNECT

Indicates the HBA should not allow the target to disconnect from the SCSI bus during processing of this request.

SRB_FLAGS_BYPASS_FROZEN_QUEUE

Is irrelevant to miniport drivers.

SRB_FLAGS_NO_QUEUE_FREEZE

Is irrelevant to miniport drivers.

SRB_FLAGS_IS_ACTIVE

Is irrelevant to miniport drivers.

SRB_FLAGS_ALLOCATED_FROM_ZONE

Is irrelevant to miniport drivers. To a Windows NT class driver, this indicates whether the SRB was allocated from a zone buffer. If this flag is set, the class driver must call ExInterlockedFreeToZone to release the SRB; otherwise, it must call ExFreePool.

SRB_FLAGS_SGLIST_FROM_POOL

Is irrelevant to miniport drivers. To a Windows NT class driver, this indicates that memory for a scatter/gather list was allocated from nonpaged pool. If this flag is set, the class driver must call ExFreePool to release the memory after the SRB is completed.

DataTransferLength
Indicates the size in bytes of the data buffer. If an underrun occurs, the miniport must update this member to the number of bytes actually transferred.
TimeOutValue
Indicates the interval in seconds that the request can execute before the OS-specific port driver might consider it timed out. Miniport drivers are not required to time requests because the port driver already does.
DataBuffer
Points to the data buffer. Miniport drivers should not use this value as a data pointer unless the miniport set MapBuffers to TRUE in the PORT_CONFIGURATION_INFORMATION for the HBA. In the case of SRB_FUNCTION_IO_CONTROL requests, however, miniport drivers can use this value as a data pointer regardless of the value of MapBuffers.
SenseInfoBuffer
Points to the request-sense buffer. A miniport driver is not required to provide request-sense data after a CHECK CONDITION. If it does, such a driver should not use the SenseInfoBuffer value as a data pointer unless the miniport set MapBuffers to TRUE in the PORT_CONFIGURATION_INFORMATION for the HBA. The physical address of the buffer can be obtained by calling ScsiPortGetPhysicalAddress.
NextSrb
Indicates the SCSI_REQUEST_BLOCK to which this request applies. Only a small subset of requests use a second SRB, for example SRB_FUNCTION_ABORT_COMMAND.
OriginalRequest
Points to the IRP for this request.
SrbExtension
Points to the Srb extension. A miniport must not use this member if it set SrbExtensionSize to zero in the SCSI_HW_INITIALIZATION_DATA. The memory at SrbExtension is not initialized by the OS-specific port driver, and the miniport-determined data can be accessed directly by the HBA. The corresponding physical address can be obtained by calling ScsiPortGetPhysicalAddress with the SrbExtension pointer.
QueueSortKey
Specifies the offset from the start of the media or zero, depending on the type of the target device.
Cdb
Specifies the SCSI-II command descriptor block to be sent to the target device.

Comments

Windows NT SCSI class and filter drivers can send SRBs with the following Function values to the system port driver:

  • SRB_FUNCTION_CLAIM_DEVICE to indicate that the class driver supports a peripheral identified in the SRB by the PathId, TargetId, and Lun members.

  • SRB_ATTACH_DEVICE to indicate that a filter driver, layered above a class driver, wants requests for a particular peripheral to be routed first to the filter driver.

  • SRB_FUNCTION_RELEASE_DEVICE to indicate that a class driver is releasing its claim on a particular peripheral.

  • SRB_FUNCTION_FLUSH_QUEUE to request cancellation of any requests currently queued in the port driver to a particular peripheral.

  • SRB_FUNCTION_RELEASE_QUEUE to request that the port driver release a frozen queue of requests to a particular peripheral.

The preceding SRB_FUNCTION_XXX are never set in SRBs sent to SCSI miniport drivers. SRB_FUNCTION_REMOVE_DEVICE is defined for use in future versions of the system. It, too, is never set in SRBs sent to SCSI miniport drivers.

See Also

ExFreePool, ExInterlockedFreeToZone, HW_INITIALIZATION_DATA, PORT_CONFIGURATION_INFORMATION, ScsiPortGetPhysicalAddress, ScsiPortGetSrb, ScsiPortIoMapTransfer, ScsiPortNotification, SRB_IO_CONTROL


SRB_IO_CONTROL

typedef struct _SRB_IO_CONTROL {
    ULONG HeaderLength;
    UCHAR Signature[8];
    ULONG Timeout;
    ULONG ControlCode;
    ULONG ReturnCode;
    ULONG Length;
} SRB_IO_CONTROL, *PSRB_IO_CONTROL;

Members

HeaderLength
Is sizeof(SRB_IO_CONTROL).
Signature
Identifies the application-dedicated, target HBA for this request. This signature is used to prevent conflicts in ControlCode values between vendors. It should be a string of ASCII characters. If a miniport driver does not recognize the input Signature value, it must complete the request with a status of SRB_STATUS_INVALID_REQUEST.
Timeout
Indicates the interval in seconds that the request can execute before the OS-specific port driver might consider it timed out. Miniport drivers are not required to time requests because the port driver does.
ControlCode
Indicates the operation to be performed. There are no system-defined operations. Values must be defined by the driver as a set of private I/O control codes with which the application can make requests by calling the Win32 DeviceIoControl function. For more information about defining private I/O control codes for device control requests, see also Part II of this manual.
ReturnCode
Returns a status code for examination by the requesting application.
Length
Indicates the size in bytes of the immediately following data area. This area can be divided for the particular operation into input and output areas. For input requests, the contents of the DataBuffer will be copied to the requestor up to the returned value of DataTransferLength.

Comments

This structure is used by applications to send requests directly to an application-dedicated HBA. Note that such an application also must set up requests to program its dedicated HBA.

See Also

IOCTL_SCSI_PASS_THROUGH, IOCTL_SCSI_PASS_THROUGH_DIRECT, SCSI_REQUEST_BLOCK


TAPE_INIT_DATA

typedef struct _TAPE_INIT_DATA {
    TAPE_VERIFY_INQUIRY_ROUTINE     VerifyInquiry;
    BOOLEAN                         QueryModeCapabilitiesPage;
    ULONG                           MinitapeExtensionSize;
    TAPE_EXTENSION_INIT_ROUTINE     ExtensionInit;       /* optional */
    ULONG                           DefaultTimeOutValue; /* optional */
    TAPE_ERROR_ROUTINE              TapeError;           /* optional */
    ULONG                           CommandExtensionSize;
    TAPE_PROCESS_COMMAND_ROUTINE    CreatePartition;
    TAPE_PROCESS_COMMAND_ROUTINE    Erase;
    TAPE_PROCESS_COMMAND_ROUTINE    GetDriveParameters;
    TAPE_PROCESS_COMMAND_ROUTINE    GetMediaParameters;
    TAPE_PROCESS_COMMAND_ROUTINE    GetPosition;
    TAPE_PROCESS_COMMAND_ROUTINE    GetStatus;
    TAPE_PROCESS_COMMAND_ROUTINE    Prepare;
    TAPE_PROCESS_COMMAND_ROUTINE    SetDriveParameters;
    TAPE_PROCESS_COMMAND_ROUTINE    SetMediaParameters;
    TAPE_PROCESS_COMMAND_ROUTINE    SetPosition;
    TAPE_PROCESS_COMMAND_ROUTINE    WriteMarks;
    TAPE_PROCESS_COMMAND_ROUTINE    PreProcessReadWrite; /* optional */
} TAPE_INIT_DATA, *PTAPE_INIT_DATA;

TAPE_INIT_DATA defines values and routines that are specific to a tape miniclass driver. The tape miniclass DriverEntry routine passes this information to the tape class driver to complete miniclass driver initialization.

Members

VerifyInquiry
Specifies the address of a tape miniclass driver routine that determines whether the driver supports a given device. This routine is required.
QueryModeCapabilitiesPage
TRUE directs the tape class driver to pass a mode capabilities page to the tape miniclass driver’s TapeMiniVerifyInquiry and TapeMiniExtensionInit routines.
MinitapeExtensionSize
Specifies the size, in bytes, of a driver-specific context area. If this value is nonzero, ExtensionInit should not be NULL. This value is optional and must be set to zero if not used.
ExtensionInit
Specifies the address of a tape miniclass routine that initializes an optional minitape extension. This routine is optional. If one is not used ExtensionInit must be set to NULL.
DefaultTimeOutValue
Specifies the number of seconds that the tape class driver waits for an SRB request before canceling it. If this value is zero, the tape class driver sets an appropriate default value (currently 180). The tape class driver always uses the default timeout value for read and write requests. TapeMiniProcessCommand routines can override the default timeout value for device control requests by setting TimeOutValue in the SRB.
TapeError
Specifies the address of a tape miniclass driver routine that augments the error-handling activities of the tape class driver. This routine is optional. If one is not used TapeError must be set to NULL.
CommandExtensionSize
Specifies the size, in bytes, of a command extension to be allocated before the start of each tape command. A tape miniclass driver uses the command extension to store context during the processing of tape commands. Its size and internal structure are defined by the tape miniclass driver. A command extension is optional. If one is not used CommandExtensionSize must be set to zero.
CreatePartition
Specifies the address of a tape miniclass driver routine that creates a partition on a tape. This routine is required.
Erase
Specifies the address of a tape miniclass driver routine that erases a tape. This routine is required.
GetDriveParameters
Specifies the address of a tape miniclass driver routine that handles requests to get drive parameters. This routine is required.
GetMediaParameters
Specifies the address of a tape miniclass driver routine that handles requests to get media parameters. This routine is required.
GetPosition
Specifies the address of a tape miniclass driver routine that handles requests to get the position of a tape. This routine is required.
GetStatus
Specifies the address of a tape miniclass driver routine that handles requests for status. This routine is required.
Prepare
Specifies the address of a tape miniclass driver routine that prepares a tape device. This routine is required.
SetDriveParameters
Specifies the address of a tape miniclass driver routine that sets drive parameters. This routine is required.
SetMediaParameters
Specifies the address of a tape minclass driver routine that sets media parameters. This routine is required.
SetPosition
Specifies the address of a tape miniclass driver routine that positions a tape. This routine is required.
WriteMarks
Specifies the address of a tape miniclass driver routine that writes marks to tape. This routine is required.
PreProcessReadWrite
Specifies the address of a tape miniclass driver routine that executes device-specific operations before all reads and writes. This routine is optional and is not needed by most drivers. If one is not used PreProcessReadWrite must be set to NULL.

Comments

A tape miniclass driver calls TapeClassZeroMemory to clear TAPE_INIT_DATA, fills in the required members and any appropriate optional members, and passes the structure to TapeClassInitialize.

See Also

DriverEntry of Tape Miniclass Driver, TapeClassInitialize, TapeClassZeroMemory, TapeMiniCreatePartition, TapeMiniErase, TapeMiniExtensionInit, TapeMiniGetDriveParameters, TapeMiniGetMediaParameters, TapeMiniGetPosition, TapeMiniGetStatus, TapeMiniPrepare, TapeMiniSetDriveParameters, TapeMiniSetMediaParameters, TapeMiniSetPosition, TapeMiniTapeError, TapeMiniVerifyInquiry, TapeMiniWriteMarks


Chapter 1 Graphics System Overview

Microsoft® Windows NT® provides a robust graphics architecture in which third party graphics hardware companies can easily integrate their video displays and printing devices. This book provides design guidelines for writing effective graphics drivers. It is broken down as follows:

  • Part 1: Graphics Drivers

    This section describes the Windows NT graphics architecture, and the Graphics Driver Interface (GDI) and Device Driver Interface (DDI). Design and implementation details that are common to both display and printing drivers are then discussed.

  • Part 2: Display and Video Drivers

    Part 2 describes the video display environment in Windows NT, and provides design and implementation details for display and video miniport driver writers.

  • Part 3: Printer Drivers

    Part 3 describes the Windows NT printing environment, and provides design and implementation details for printer driver and/or minidriver writers. Design guidelines are also provided for third-party vendors who need to write their own print monitors.

The glossary found at the end of this book helps define terms and acronyms specific to the graphics subsystem and driver design.

Graphics driver function references are found in the Windows NT Graphics Driver Reference. For more information on the design of kernel-mode drivers, see the Windows NT Kernel-mode Driver Design Guide.

Windows NT Graphics Architecture

The Windows NT operating system provides graphics support as a part of its executive services. The graphics system architecture, from application to driver output, is shown in Figure 1.1. The printing path shown in this figure is the most common of many ways to print. Note that the spooler runs in user mode as a separate process.

Figure 1.1 Windows NT Graphics Architecture

Microsoft® Win32® application writers request graphics and print operations by calling the system-supplied Win32 GDI and WinSpool functions. The Win32 APIs allow application developers to implement device-independent code, and are described in the Win32 SDK.

The Win32 subsystem routes graphics requests to the graphics engine by way of the NT executive system services. The graphics engine, also known as GDI, processes these requests in conjunction with the hardware-specific display or printer driver. GDI communicates with these drivers through a set of functions called the DDI. Graphics hardware vendors write drivers specific to their display or printer hardware by implementing some or all of the DDI. GDI is system-supplied, and cannot be replaced.

A display driver is paired with a video miniport driver to complete video display support. Each video miniport driver provides hardware-level support for its associated display driver. Video miniport driver writers implement the video miniport driver interface described in Part 2 of this book.

The Windows NT spooler enables print jobs to be routed to local and network printers. It is sometimes referred to as the print subsystem since it is comprised of numerous components. Along with printer drivers and minidrivers, printer IHVs can develop their own print processor and/or monitors. See Part 3 of this book for more details.


Document Conventions

The font conventions used in this book are identical to those used in all other Windows NT DDK books. These conventions are described in Getting Started. Many of the DDK video and printer code samples use Hungarian notation.


Chapter 2 GDI Support for Graphics Drivers

This chapter describes the Microsoft® Windows NT® graphics device interface (GDI). It then details the support that GDI provides to graphics drivers.

References to “GDI” in this book are implicit references to kernel-mode GDI; Win32 GDI will be explicitly identified. Kernel-mode GDI is also known as the Graphics Engine.

GDI function and structure references are documented in the Windows NT DDK Graphics Driver Reference.


GDI from the Driver’s Perspective

GDI is the intermediary support between a Windows NT graphics driver and application. Applications call Win32 GDI functions to make graphics output requests. These requests are routed to GDI. GDI then sends these requests to the appropriate graphics driver, such as a display driver or printer driver. GDI is an NT system-supplied module that cannot be replaced.

GDI communicates with the graphics driver through a set of DDI functions. These functions are identified by their Drv prefix. Information is passed between GDI and the driver through the input/output parameters of these entry points. The driver must support certain DrvXxx functions for GDI to call. The driver supports GDI’s requests by performing the appropriate operations on its associated hardware before returning to GDI.

GDI includes many graphics output capabilities in itself, eliminating the need for the driver to support these capabilities and thereby making it possible to reduce the size of the driver. GDI also exports service functions that the driver can call, further reducing the amount of support the driver must provide. GDI service functions are identified by their Eng prefix, and functions that provide access to GDI-maintained structures have names in the form of XXXOBJ_Xxx.

Figure 2.1 shows this flow of communication.

Figure 2.1 Graphics Driver and GDI Interaction


GDI as an Application Graphics Language

Both the Win32 GDI and the graphics engine are completely device independent. Consequently, applications do not need to access the hardware directly. Based on an application graphics request, GDI works in conjunction with device-dependent graphics drivers to provide high quality graphics output for an array of graphics devices. The same GDI code paths are used for both printing and display devices.


GDI as a Rendering Engine

For rendering operations, the driver must first enable a surface for each PDEV (a logical representation of a physical device) that is enabled. If the hardware can set up as a GDI standard-format bitmap, GDI can be used to do all or some of the drawing to the bitmap surface. GDI can also handle advanced halftoning.

For information on enabling PDEVs and surfaces, refer to DrvEnablePDEV and DrvEnableSurface in the Graphics Driver Reference.


GDI-Managed Bitmaps

GDI manages bitmaps in all DIB formats including 1, 4, 8, 16, 24, and 32 bits-per-pixel. GDI can do all line drawing, filling, text output, and bit block transfer (bitblt) operations on these bitmaps. This makes it possible for the driver to either have GDI do all graphics rendering, or to implement functions for which its hardware offers special support.

If the device has a frame buffer in a DIB format, GDI can perform any or all graphics output directly to the device, making a frame buffer driver very small. If the device supports a nonstandard format, the driver must implement necessary drawing functions. GDI can still simulate most drawing functions, although a performance cost is incurred: the pixels must be copied into a standard format bitmap before they can be operated on by GDI, and then be copied back to the original format after drawing is complete.


GDI-Managed Lines and Curves

GDI offers improved definitions of lines and curves. Lines are not required to have integer endpoints in DEVICE coordinates, as was true for Windows 3.x. This allows the driver to transform graphics objects without gross rounding. The fundamental curve in GDI is a Bezier curve (cubic spline) rather than an ellipse. All GDI internal operations are handled with Bezier curves, which are supported by most high-end devices. For devices that do not handle Bezier curves, GDI breaks curves down into line segments before calling the driver to draw them.

GDI can download regions to be filled in the form of paths, as well as rectangles. Drivers can decompose paths into trapezoids or spans for filling.


GDI-Managed Attributes: Brushes

GDI also manages all attributes. GDI passes attributes to the driver as brushes; the driver realizes these brushes by converting them to a useful internal form. GDI maintains this converted information for the driver. GDI also maintains all states, including bounds, correlation, current position, and line style. The driver can cache information but is not assumed to maintain any state. Except for initialization and brush realization, GDI calls the driver only to draw on the device. GDI takes care of transforms, region locking, and pointer exclusion before it calls the driver.

Whenever a driver needs to use a brush not yet realized, it calls back to GDI. GDI allocates memory for the brush and calls the driver to realize it and, if necessary, dither it.


GDI Halftoning Capabilities

GDI halftoning produces a quality color-scale or gray-scale image for printing devices or display devices that do not already have a built-in color or gray scale. Color halftoning provides:

  • Highest quality color and gray-scale reproduction possible on a given device.

  • Increased visual resolution with a limited set of intensity levels.

  • Improved color correlation between the different output devices.

Traditional analog halftoning is a cellular process that uses a halftoning screen. The halftoning screen is composed of cells of equal sizes, with fixed-cell spacing center-to-center. The fixed-cell spacing accommodates the thickness of the ink, while the size of a dot within each cell can vary to produce the impression of a continuous tone.

On a computer, most printing or screen shading also uses a fixed-cell pixel size. To simulate the variable dot size, a combination of cluster pixels simulates the halftone screen. In Windows NT, GDI includes halftoning default parameters that provide a good first approximation. Additional device-specific information can be added to the system to improve output.


GDI/Driver Division of Labor

To understand graphics driver design, it is important to understand the roles of GDI and the driver, and how they negotiate. GDI, with its enhanced capabilities, can handle many operations that previously required a graphics driver. GDI also has the responsibility of managing data structures critical to graphics operations, such as surfaces, although each graphics driver must have access to them.


GDI Communication With the Driver

The driver exports only two functions to GDI: DrvEnableDriver and DrvDisableDriver. All other driver-supported functions are exposed to GDI through an array of pointers. A GDI call to DrvEnableDriver initializes the driver and passes back this list of driver-supported DDI functions. While there are some functions a driver must support, GDI will handle those operations not included in the function list received from the driver’s DrvEnableDriver routine. GDI calls DrvDisableDriver when the driver is to be unloaded. DDI functions are discussed in depth in Supporting the DDI.

GDI makes a large number of services available to the driver. These services fall into two categories: user objects and service routines.


User Objects

GDI maintains important internal data structures, but gives the driver access to the public fields of these structures by passing them down as user objects. User objects are intermediate data structures that provide an interface between GDI data structures and the drivers that need access to the information within these structures. The driver can pass the pointer to a user object back to GDI to query for information or to ask for various services. User objects with public fields provide the following advantages:

  • They eliminate problems associated with direct access to internal GDI data structures.

  • They provide a place to hold GDI data for the driver. For example, a PATHOBJ can hold all the extra data required to enumerate a complex object like a path.

The following user objects are available:
BRUSHOBJ | Defines the brush objects for graphic functions that output lines, text, or fills. Drivers can call BRUSHOBJ services to realize brushes or to find realizations previously cached by GDI.
CLIPOBJ | Provides the driver with access to a clip region for drawing or filling. This region can be enumerated as a series of rectangles.
FLOATOBJ | Allows graphics drivers to emulate floating point operations. Floating point operations are disabled for all other kernel-mode drivers.
FONTOBJ | Gives the driver access to information about a particular instance (or realization) of a font.
PALOBJ | A structure containing RGB palette colors; accessible via the PALOBJ_cGetColors and DrvSetPalette functions.
PATHOBJ | Defines a path that specifies what is to be drawn (lines or Bezier curves). PATHOBJ is passed to the driver to describe a set of lines and Bezier curves that are to be stroked or filled.
STROBJ | Enumerates a list of glyph handles and positions for the driver that describes how a text string is to be drawn.
SURFOBJ | Identifies a surface, which can be a GDI bitmap, a device-dependent bitmap, or a device-managed surface.
XFORMOBJ | Describes an arbitrary linear two-dimensional transform, such as for geometric wide lines.
XLATEOBJ | Translates color indexes that refer to a source palette into indexes for a destination palette. The resulting destination indexes identify colors that match the surface color as closely as possible.


GDI Service Routines

GDI exports many service routines, whose names have the form EngXxx. The driver dynamically links to win32k.sys to directly access these routines. GDI service routines include surface management, rendering simulations, and path, palette, font, and text services. These services are discussed in detail in GDI Support Services.


PDEV Negotiation

One of the primary responsibilities of any graphics driver is to enable a PDEV whenever a request is made by GDI. A PDEV is a logical representation of the physical device; it is defined by the driver. Refer to DrvEnablePDEV for more information on enabling PDEVs.

Through DrvEnablePDEV, the driver must provide information to GDI that describes the requested device and its capabilities. One piece of important information that the driver gives GDI is the set of GCAPS_XXX flags in flGraphicsCaps member of the DEVINFO structure.

The GCAPS flags allow GDI to determine what operations the PDEV supports. For example, GDI tests the GCAPS flags that indicate whether the PDEV can handle Bezier curves and geometric wide lines before GDI attempts to call DrvStrokePath to draw paths with these primitive types. If the GCAPS flags indicate that the PDEV cannot handle these primitive types, GDI will break down the lines or curves so it can make simpler calls to the driver.

From the driver’s side, whenever the driver gets an advanced path-related call from GDI, it can return FALSE if the path or clipping is too complex for the device to process and the function called is not one of the following that requires specific driver support:

  • DrvStrokePath

  • DrvPaint

  • DrvTextOut

  • DrvCopyBits

  • DrvBitBlt

The driver cannot return FALSE from DrvStrokePath when handling a cosmetic line because the driver must handle any complex clipping or styling for cosmetic lines. However, DrvStrokePath can return FALSE if the path has Bezier curves or geometric lines. When this occurs, GDI breaks the call down to simpler calls, just as it does if the capability bits are not set. For example, if DrvStrokePath returns FALSE when it is sent a geometric line, GDI simplifies the line and calls DrvFillPath or DrvPaint.

If DrvStrokePath is to report an error, it must return DDI_ERROR.

DrvPaint and DrvTextOut can return FALSE to report an error, but not to notify GDI that the operation is too complex.

This kind of negotiation between GDI and the driver, for functions that depend on the PDEV, permits GDI and the driver to produce high quality output without excess communication.


Surface Negotiation

Drawing and text output require a surface on which to draw. This surface is enabled through DrvEnableSurface and is referred to as the primary surface. There is only one primary surface enabled per PDEV although a driver can support several PDEVs. Drivers that support DrvCreateDeviceBitmap can create and use many bitmap surfaces. These bitmap surfaces are referred to as secondary surfaces because they must be drawn on the surface created by DrvEnableSurface. The driver is responsible for determining the type of drawing surface it supports. It does this by calling back to GDI with one of the surface-creation GDI service functions.


Surface Coordinates

A device surface is a subset of an array of 2 (28) by 2 (28) pixels. These pixels are addressed by pairs of 28-bit signed numbers, the upper leftmost pixel of the device surface is given the coordinates (0,0). The device surface lies in the lower right quadrant of this coordinate space, where both coordinates are nonnegative.


DC Origin

Application programs are required to keep their graphics within an array of 2 (27) by 2 (27) pixels. The device space has additional size at the DDI level because the Window Manager may offset the application’s coordinate system by one signed 27-bit coordinate, the DC origin. The DC origin is not visible to the driver and the driver identifies the graphics coordinates only after the offset is applied. The graphics driver can take advantage of the fact that a graphics primitive cannot extend beyond the 2 (27) pixel-width coordinate system, and can use the extra space to manage overflow.


FIX Coordinates

Fractional coordinates that can specify a location on the device surface within 1/16th of a pixel are passed over the DDI. (On vector devices, the fractional coordinates are sixteen times more accurate than the device resolution.) The fractional coordinates are represented as signed 32-bit numbers in 28.4 bit FIX notation. In this notation, the highest order 28 bits represent the integer part of the coordinate, the lowest 4 bits represent the fractional part. For example, 0x0000003C equals +3.7500, and 0xFFFFFFE8 equals -1.5000.

FIX coordinates represent control points for lines and Bezier curves. In other cases, integer coordinates are used. For cases such as rectangular clip regions, GDI uses signed 32-bit integers with no implicit binary point. Because only 28-bit coordinates are allowed, the highest 5 bits of an integer coordinate are either all cleared or all set.


Surface Types

Surface types can be viewed in context of how they are handled. These types are as follows:

  • Engine-managed surfaces (standard format bitmaps).

  • Device-managed standard bitmaps.

  • Device-managed nonstandard surfaces.

Engine-Managed Surfaces

A standard-format bitmap, called a DIB, is a single-plane, packed-pixel (where the data for each pixel is stored in a contiguous manner)format bitmap. Each scan line of the bitmap is aligned on a four-byte boundary.

Standard-format bitmaps are created and supported by GDI when the driver calls the GDI service EngCreateBitmap. An engine-managed surface must be a DIB. The frame buffer driver creates a DIB as its primary surface and lets GDI handle rendering operations.

Device-Managed Surfaces

Device-managed surfaces are controlled by the driver and they can be either DIB or nonDIB surfaces. Having a device-managed DIB surface allows the driver to call back to GDI to have it draw on the surface. A driver can get a handle to a DIB by calling EngCreateBitmap and then wrap a surface around it by calling EngCreateDeviceSurface, which requires GDI to call the driver for rendering operations. This is used in banked frame buffer display drivers. Refer also to Supporting Banked Frame Buffers.

A driver can enable a device-managed nonDIB surface by calling EngCreateDeviceSurface to have GDI create the surface and return a handle to it. GDI relies on the driver to access, to control drawing to, and to read from a device-managed surface.

A DDB, sometimes called a device-format bitmap, is another type of nonDIB device-managed surface. The DDB is supported to allow certain drivers, such as the VGA, to implement faster bitmap-to-screen block transfers. The DDB also allows drivers to draw to banked or nonDIB bitmaps in offscreen display memory. If a DDB is required, the driver can support DrvCreateDeviceBitmap and call EngCreateDeviceBitmap to have the engine return a handle to the bitmap.


Hooking versus Punting

The terms hooking and punting refer to driver decisions concerning how standard bitmap drawing operations are implemented. If the driver requests GDI to create a surface with a standard DIB format, GDI can handle all drawing and text operations. A driver can, however, hook out and implement functions if its hardware provides faster operation. A driver hooks a DrvXxx function when it has implemented that function’s operation. Punting means that the driver wants GDI to handle the operation.

There are some situations in which the operation must be implemented in the driver. For example, if you are dealing with a device-managed surface, or you are drawing on a nonstandard bitmap, certain drawing functions must be implemented in the display driver. Display drivers must also handle the management of banked memory, although they can callback for each bank and have GDI do the drawing.

Hooking

By default, when a drawing surface is an engine-managed surface, GDI handles the drawing (rendering) operation. For a driver to take advantage of hardware that offers acceleration for some or all of these drawing functions for a given surface, or to make use of special block transfer hardware, it can hook these functions from GDI. To hook calls, the driver specifies the hooks as flags of the flHook parameter of the EngAssociateSurface function.

If the driver hooks a call, it must implement that call. The driver can optimize the operation where there is hardware support; or it can implement the operation in highly optimized assembler code when portability is not an issue. Such a driver might handle only certain cases on a hooked call. For example, if complicated graphics are requested on a call that is hooked, it may still be more efficient to punt the call back to GDI, allowing GDI to handle the operation.

Another example of a driver choosing whether or not to handle a hooked call is a driver that supports hardware that can handle bit-block-transfer calls with certain ROPs, but otherwise is just a frame buffer. Such a driver will return a handle to the bitmap surface for the frame buffer as the surface for its PDEV, but it will hook the DrvBitBlt call for itself. When GDI calls DrvBitBlt, the driver can check the ROP to see if it is one of those supported by the hardware. If not, the driver can pass the operation back to GDI with a call to EngBitBlt.

Drivers that support device-managed surfaces must hook out some of the drawing functions; namely DrvPaint, DrvCopyBits, DrvTextOut, and DrvStrokePath. Although GDI simulations can handle other drawing functions, it is recommended for performance reasons that drivers of this type hook out other functions, such as DrvBitBlt and DrvRealizeBrush, because simulation requires drawing from and to the surface.

Punting

Punting the call back to GDI means to put in a call to the corresponding GDI simulation. In general, for every DrvXxx graphics output call, there is a corresponding GDI EngXxx simulation call that takes the same arguments. Object parameters, such as a SURFOBJ, PATHOBJ, or CLIPOBJ, can be passed without change to GDI simulation. When the driver punts a call back to GDI, the size of the driver is reduced (since the code for that functionality can be omitted). However, because the engine owns the call, the driver does not have control over the execution speed. For some complicated cases, there may be no real advantage to providing support in the driver.

Hookable GDI Graphics Output Functions

The graphics output functions that the driver can hook and the corresponding GDI simulations are listed in the following table.
Driver Graphics Output Function | Corresponding GDI Simulation
DrvBitBlt | EngBitBlt
DrvCopyBits | EngCopyBits
DrvStretchBlt | EngStretchBlt
DrvFillPath | EngFillPath
DrvStrokeAndFillPath | EngStrokeAndFillPath
DrvTextOut | EngTextOut
DrvPaint | EngPaint
DrvStrokePath | EngStrokePath
DrvLineTo | EngLineTo


GDI Support Services

GDI exports many service routines that can simplify driver design. The driver can call these routines directly. The names of routines that are general graphics engine services start with Eng. Service routines related to a particular object always start with the name of the object; for example, CLIPOBJ_cEnumStart is a CLIPOBJ service.

Note The service routines in which the first argument is a pointer to a user object are methods on that user object, and are called using the usual C++ conventions. Therefore, drivers written in C++ can access the service routines as methods.

These service routines fall into the following categories:

  • Surface management

  • Palette services

  • Path services

  • Rendering services

  • Font and text services

Supporting the DDI describes the DDI entry points and also explains where many of these service routines can be used to help the driver implement the entry points. For detailed descriptions of each of the service functions, see the Graphics Driver Reference.


GDI Support for Surfaces

For each PDEV, a driver must support the DrvEnableSurface function. DrvEnableSurface sets up the surface to be drawn on and associates it with the PDEV. The driver must also support DrvDisableSurface to disable created surfaces. Because GDI creates and maintains the surface, the driver relies on several GDI service functions, listed in the following table, to implement the enabling and disabling of surfaces.
Function Name | Purpose
EngAssociateSurface | Associates a surface with a PDEV and defines the drawing operations the driver wants to hook out for that surface. It assumes the PDEV’s default palette and style steps. The driver must make this call for the primary surface, during DrvEnableSurface and when enabling a secondary surface before locking the surface to write on it.
EngCheckAbort | (Printers only) Enables a printer driver to determine whether its printer job has been terminated.
EngCreateBitmap | Creates a standard format DIB bitmap. GDI can perform all drawing operations to this type of surface.
EngCreateDeviceBitmap | Creates a device-dependent bitmap which the driver is responsible for drawing on (although it can be created as a DIB, in which case the driver can call back to have GDI draw on it).
EngCreateDeviceSurface | Creates a device-managed surface. The driver is responsible for managing certain drawing operations for this surface. The function returns a handle that the driver manages.
EngDeleteSurface | Deletes a surface (DIB, device-dependent bitmap, or device-managed surface).
EngEraseSurface | Fills a specified rectangle on a surface with a given color, effectively erasing it. This should be called only to erase the surface of a GDI bitmap.
EngLockSurface | Gives the driver access to a created surface by creating a user object (SURFOBJ) for that surface. (The primary surface is not locked.)
EngMarkBandingSurface | (Printers only) Marks a surface as a banding surface.
EngUnlockSurface | Unlocks a surface when the driver has finished a drawing operation (to be called when disabling a secondary surface).

For more information on these functions, refer to the Graphics Driver Reference.


GDI Support for Palettes

GDI can do most of the work with regard to palette management. When GDI calls DrvEnablePDEV, the driver returns its default palette to GDI as part of the DEVINFO structure. The driver should create this palette with EngCreatePalette.

A palette effectively maps 32-bit color indexes into 24-bit RGB color values, which is the way GDI uses palettes. A driver specifies its palette so GDI can determine how different color indexes are to appear on the device.

The driver need not deal with most palette operations and calculations as long as it uses the XLATEOBJ provided by GDI.

If the device supports a modifiable palette, it should implement DrvSetPalette. GDI calls DrvSetPalette when an application changes the palette for a device and passes the resulting new palette to the driver. The driver should set its internal hardware palette to match the new palette as closely as possible.

A palette can be defined for GDI in either of the two different formats listed in the following table.
Palette Format | Description
Indexed | A color index is an index into an array of RGB values. The array can be small, containing, for example, 16 color indexes; or large, containing, for example, 4096 color indexes or more.
Bit Fields | Bit fields in the color index specify colors in terms of the amounts of R, G, and B in each color. For example, 5 bits could be used for each, providing a value between 0 and 31 for each color. The 5-bit value would be scaled up to cover a range of 0 to 255 for each component when converting to RGB. (The usual RGB representation itself is defined by bit fields.)

GDI typically uses the palette mapping in reverse. That is, an application specifies an RGB color for drawing and GDI must locate the color index that causes the device to display that color. As indicated in the next table, GDI provides two primary palette service functions for creating and deleting the palette, as well as some service functions related to the PALOBJ and the XLATEOBJ used to translate color indexes between two palettes.
Function | Description
EngCreatePalette | Creates a palette. The driver associates the palette with a device by returning a handle to the palette in the DEVINFO structure.
EngDeletePalette | Deletes the given palette.
PALOBJ_cGetColors | Allows a driver to download RGB colors from an indexed palette. Called by the display driver in DrvSetPalette.
XLATEOBJ_iXlate | Translates a single source color index to a destination color index.
XLATEOBJ_piVector | Retrieves a translation vector from an indexed source palette. The driver can use this vector to perform its own translation of the source indexes to destination indexes.
XLATEOBJ_cGetPalette | Retrieves the 24-bit RGB value for the colors in an indexed source palette. The driver can use this function to obtain information from the palette to perform color blending.

For more information on these functions, refer to the Graphics Driver Reference.


GDI Services for Paths

To assist vector devices in filling complex areas, their drivers can call the engine functions, listed in the following table, that create, modify, and enumerate a path. The driver has access to paths through the PATHOBJ.
GDI Path Service Function | Description
EngCreatePath | Allocates a path for the driver’s temporary use. The driver should delete this path before returning to GDI from its current drawing call.
EngDeletePath | Deletes a path allocated by EngCreatePath.
PATHOBJ_vGetBounds | Returns a bounding rectangle for the path.
PATHOBJ_vEnumStart | Notifies a PATHOBJ that the driver will begin calling PATHOBJ_bEnum to enumerate the curves in the specified path. This function must be called in case of an enumeration restart.
PATHOBJ_bEnum | Retrieves the next PATHDATA record from a path. Each record describes all or part of a subpath.
PATHOBJ_vEnumStartClipLines | Allows the driver to ask for lines to be clipped against a CLIPOBJ. This is useful when the clip region is more complex than a single rectangle.
PATHOBJ_bEnumClipLines | Enumerates clipped line segments from a path. In response to DrvPaint, a vector driver can create a path, fill it with lines to describe the brush, and pass the path to PATHOBJ_bEnumClipLines for clipping against the complex region.
PATHOBJ_bMoveTo | Changes the current position in a PATHOBJ-defined path.
PATHOBJ_bPolyLineTo | Draws lines in a PATHOBJ-defined path.
PATHOBJ_bPolyBezierTo | Draws Bezier curves (cubic splines) in a PATHOBJ-defined path.
PATHOBJ_bCloseFigure | Closes a path (for filling) by drawing a line to the starting point.

For more information on these functions, refer to the Graphics Driver Reference.


GDI Drawing Services, Other

To support the CLIPOBJ, BRUSHOBJ, and XFORMOBJ, GDI offers several drawing services, listed in the following table, in addition to the GDI simulations described earlier in Hooking versus Punting.
GDI Draw Service Function | Description
EngCreateClip | Allocates a CLIPOBJ for the driver’s temporary use. The driver should call EngDeleteClip to delete it when it is no longer needed.
EngDeleteClip | Deletes a CLIPOBJ allocated with EngCreateClip.
EngBitBlt | GDI simulation for DrvBitBlt.
EngCopyBits | GDI simulation for DrvCopyBits.
EngStretchBlt | GDI simulation for DrvStretchBlt.
EngFillPath | GDI simulation for DrvFillPath.
EngStrokeAndFillPath | GDI simulation for DrvStrokeAndFillPath.
EngPaint | GDI simulation for DrvPaint.
EngStrokePath | GDI simulation for DrvStrokePath.
EngLineTo | GDI simulation for DrvLineTo.
BRUSHOBJ_pvAllocRbrush | Allocates memory for the driver’s realization of a brush.
BRUSHOBJ_pvGetRbrush | Returns a pointer to the driver’s realization of the brush. Realizes the brush if it has not yet been realized.
CLIPOBJ_cEnumStart | Sets parameters for enumeration of the rectangles in all or part of the clipped region. (The region can be enumerated once without this call).
CLIPOBJ_bEnum | Retrieves a batch of rectangles from the clip region.
CLIPOBJ_ppoGetPath | Can be used to retrieve complicated regions as a path.

For more information on these functions, refer to the Graphics Driver Reference.


GDI Font and Text Services

GDI provides support for font management and text output. The FONTOBJ gives a driver access to a particular instance of a font. To support text output, the driver has access to the STROBJ and related services. These functions are listed in the following table.
Function | Description
EngTextOut | GDI simulation for DrvTextOut
FONTOBJ_cGetAllGlyphHandles | Allows the driver to retrieve every glyph handle of a GDI font. The driver uses this service to download an entire font.
FONTOBJ_vGetInfo | Returns information that describes the associated font.
FONTOBJ_cGetGlyphs | Translates glyph handles into pointers to the associated glyph data for the font consumer. These pointers are valid until the next call to FONTOBJ_cGetGlyphs.
FONTOBJ_pxoGetXform | Retrieves the Notional-to-Device transform for the associated font. This transform is required for a driver to realize a driver-supplied font.
FONTOBJ_pifi | Retrieves the pointer to the IFIMETRICS structure that describes the associated font.
FONTOBJ_pvTrueTypeFontFile | Retrieves a pointer to the ROM-mapped TrueType file that is associated with a TrueType font.
STROBJ_vEnumStart | Restarts the enumeration of the GLYPHPOS array for the specified STROBJ. This function should be called by the driver prior to subsequent enumerations.
STROBJ_bEnum | Enumerates glyph identities and positions in the specified STROBJ.
STROBJ_dwGetCodePage | Returns the code page associated with the specified STROBJ.

For more information on these functions, refer to the Graphics Driver Reference.


Data Types

The data types defined in the following table appear in the device driver interface. Several of the listed data types are those user objects previously described in User Objects.
DDI Data Type | Variable Name Prefix | Definition
BOOL | b | A 32 bit value that can be TRUE or FALSE.
BYTE | j | An 8 bit unsigned integer.
BRUSHOBJ* | pbo | A brush object.
CLIPLINE | cl | A clipline object.
CLIPOBJ* | pco | A clipping object.
DHPDEV | dhpdev | A 32 bit handle, defined by the device driver, that identifies a physical device.
DHSURF | dhsurf | A 32 bit handle, defined by the device driver, that identifies a device managed surface.
FIX | fix | A 32 bit signed fixed point number. 28 bits are to the left of the binary point.
FLOAT | e | A 32 bit IEEE floating point number.
FLOAT_LONG | el | A 32 bit value that is interpreted as either a LONG or FLOAT, depending on a separate flag.
FLONG | fl | A set of 32 bit flags.
FONTOBJ* | pfo | A font object.
FSHORT | fs | A set of 16 bit flags.
HBM | hbm | A 32 bit handle, defined by GDI, that identifies a bitmap.
HPAL | hpal | A 32 bit handle, defined by GDI, that identifies a palette.
HSURF | hsurf | A 32 bit handle, defined by GDI, that identifies a surface.
LONG | l | A 32 bit signed integer.
MIX | mix | A 32 bit quantity, whose lower 16 bits define foreground and background mix modes.
PALOBJ* | ppalo | A palette object.
PATHOBJ* | ppo | A path object.
POINTE | pte | A point structure that consists of {FLOAT x,y;}.
POINTFX | ptfx | A point structure that consists of {FIX x,y;}.
POINTL | ptl | A point structure that consists of {LONG x,y;}.
PWSZ | pwsz | A pointer to a zero-terminated Unicode string.
PVOID | pv | A pointer to a structure that is not defined by GDI.
RECTL | rcl | A rectangle structure that consists of {LONG xLeft,yTop,xRight,yBottom;}
RECTFX | rcfx | A rectangle structure that consists of {FIX xLeft,yTop,xRight,yBottom;}
ROP4 | rop4 | A 32 bit value that specifies how source, destination, pattern, and mask pixels are to be mixed.
SHORT | s | A 16 bit signed integer.
SIZEL | sizl | A two-dimensional size structure that consists of {ULONG cx,cy;}.
STROBJ* | pstro | A text string object.
SURFOBJ* | pso | A surface object.
ULONG | ul | A 32 bit unsigned integer.
USHORT | us | A 16 bit unsigned integer.
XFORMOBJ* | pxo | A coordinate transform object.
XLATEOBJ* | pxlo | A color translation object.

The parameter prefixes listed in the next table are used to modify variable name prefixes in accordance with their usage.
Prefix | Parameter Usage
i | An enumerated index
c | A count
p | A pointer


Porting Graphics Drivers to Windows NT4.0

The Windows NT graphics architecture has significantly changed in Release 4.0. The biggest change is the move of GDI and graphics drivers into the Windows NT executive. For this reason, all pre-4.0 graphics drivers must be ported and rebuilt to run on NT4.0. Several new support routines have been added to kernel-mode GDI, including routines for kernel-mode memory management, synchronization, and file mapping.

See Porting Graphics Drivers To Windows NT 4.0 from 3.51 for details.


Chapter 3 Supporting the DDI

In response to device-independent application calls routed through GDI, a graphics driver must ensure that its graphics device produces the required output. A graphics driver controls graphics output by implementing as much of the Device Driver Interface (DDI) as is necessary.

DDI function names have the form DrvXxx. GDI calls these DrvXxx functions to pass data to the driver. In response to a request from an application, if GDI determines that a function is supported by the driver, GDI calls that function. It is the responsibility of the driver to support the function and return to GDI upon its completion.

This chapter describes the DDI functions of which all graphics driver writers must be aware. DDI function declarations, structure definitions, and constants can be found in winddi.h. This include file is shipped with the Windows NT DDK. See the Graphics Driver Reference for detailed information on each of the DDI entry point functions, GDI service functions, and DDI structures and types.

Note When a device driver returns an error, it can call EngSetLastError to log an extended error code. The application program can then retrieve the error code.


Graphics Driver Function Support

The topics that follow describe the driver entry point functions, categorizing them as required, conditionally required, and optional. Later topics provide implementation information, in functional categories for general graphics driver entry points. Parts 2 and 3 of this document present design information specific to display drivers and printer drivers, respectively.


Required Graphics Driver Functions

All graphics drivers must support the entry points that GDI calls to enable and disable the driver, the PDEV, and the surface associated with each PDEV. The following table lists the needed functions in the order in which they are typically called.
Entry Point | Description
DrvEnableDriver | As the initial driver entry point, provides GDI with the driver version number and entry points of optional functions supported.
DrvGetModes | Lists the modes supported by a specified video hardware device. (This function is required of display drivers only.)
DrvEnablePDEV | Enables a PDEV.
DrvCompletePDEV | Informs the driver upon completion of device installation.
DrvEnableSurface | Creates a surface for a specified hardware device.
DrvDisableSurface | Informs the driver that the surface created for the current device is no longer needed.
DrvDisablePDEV | When the hardware is no longer needed, frees memory and resources used by the device and any surface created, but not yet deleted.
DrvDisableDriver | Frees all allocated resources for the driver and returns the device to its state when initially loaded.
DrvAssertMode | Resets the video mode for a specified hardware device. (This function is required of display drivers only.)


Conditionally Required Graphics Driver Functions

Depending on how a driver is implemented, other functions, such as those listed in the table that follows, are required. If the driver manages its own surface (using EngCreateDeviceSurface to get a handle to the surface), the driver must also support several drawing functions. Drivers writing to standard format DIBs usually allow GDI to manage most or all of these operations. Displays that support settable palettes must also support DrvSetPalette.

It is more common for a printer driver than a display driver to define or draw fonts. A display driver is not required to handle fonts unless it must be able to draw its own fonts or those of the video hardware on a device-managed surface. If the hardware has a resident font, the driver must supply information to GDI about this font. This information includes font metrics, mappings from Unicode to individual glyph identities, individual glyph attributes, and kerning tables.
Entry Point | When Required | Description
DrvCopyBits | Device-managed surfaces | Translates between a device-managed raster surface and a GDI standard-format bitmap.
DrvStrokePath | Device-managed surfaces | Renders a path on the display.
DrvTextOut | Device-managed surfaces or drivers that define fonts | Renders a set of character components (glyphs) at specified positions.
DrvSetPalette | Displays that support settable palettes | Realizes the palette for a specified device.
DrvPaint | Device-managed surfaces | Fills ("paints") a specified region.
DrvDescribePixelFormat | Displays that support windows with different pixel formats on a single surface | Describes a PDEV’s pixel format.
DrvSetPixelFormat | Displays that support windows with different pixel formats on a single surface | Sets a window’s pixel format.
DrvSwapBuffers | Drivers that support a pixel format with double buffering | Displays contents of a surface’s hidden buffer.
DrvResetPDEV | Devices that allow mode changes in documents | Transfers driver state from old PDEV to new PDEV.
DrvQueryFont | Drivers that draw fonts | Retrieves a GDI structure for a given font.
DrvQueryFontData | Drivers that draw fonts | Retrieves information about a realized font.
DrvQueryFontTree | Drivers that draw fonts | Queries a tree structure defining one of three types of font mapping.
DrvLoadFontFile | Font drivers | Specifies file to use for font realizations.
DrvUnloadFontFile | Font drivers | Informs driver a font file is not needed.
DrvQueryFontCaps | Font drivers | Asks driver for font driver capabilities.
DrvQueryFontFile | Font drivers | Asks driver for font file information.
DrvQueryTrueTypeTable | TrueType font drivers | Gives GDI access to TrueType font files.
DrvQueryTrueTypeOutline | TrueType font drivers | Returns TrueType glyph handles to GDI.
DrvGetTrueTypeFile | TrueType font drivers | Gives GDI access to memory-mapped TrueType font file.


Optional Graphics Driver Functions

In the interests of reducing driver size, drivers usually add only those optional functions that are well-supported in hardware. If a driver is written for hardware that supports these specialized functions, the appropriate functions in the following table can be included.
Entry Point | Description
DrvCreateDeviceBitmap | Creates and manages a bitmap with a driver-defined format.
DrvDeleteDeviceBitmap | Deletes a device-managed bitmap.
DrvDitherColor | Requests a device to create a brush dithered against a device palette.
DrvFillPath | Paints a closed path for a device-managed surface.
DrvMovePointer | Moves a pointer to a new position, and redraws it.
DrvBitBlt | Executes general bit block transfers to and from surfaces.
DrvRealizeBrush | Realizes a specified brush for a defined surface.
DrvSaveScreenBits | Saves or restores a specified rectangle of the screen (display driver only).
DrvSetPointerShape | Removes the pointer from the screen, if the driver has drawn it, and then sets a new pointer shape.
DrvStretchBlt | Allows stretching block transfers among device-managed and GDI-managed surfaces.
DrvStrokeAndFillPath | Simultaneously fills and strokes a path.
DrvSynchronize | Coordinates drawing operations between GDI and a display driver-supported coprocessor device; for engine-managed surfaces only.
DrvFree | Frees font storage associated with an indicated data structure.
DrvDestroyFont | Notifies driver that a font realization is no longer needed; driver can free allocated data structures.
DrvEscape | Queries information from a device not available in a device-independent DDI.
DrvDrawEscape | Implements draw-type escape functions.
DrvEndDoc | Sends end-of-document information. (printer driver)
DrvGetGlyphMode | Returns type of font information to be stored for a particular font. (printer driver)
DrvSendPage | Sends raw bits from a surface to the printer. (printer driver)
DrvStartDoc | Sends start-of-document control information. (printer driver)
DrvStartPage | Sends start-of-page control information. (printer driver)
DrvFontManagement | Allows access to printer functionality not directly available through GDI. (printer driver)
DrvQueryAdvanceWidths | Supplies character advance widths for a specified set of glyphs. (font driver)
DrvLineTo | Draws a single solid integer-only cosmetic line.
DrvNextBand | Realizes the contents of a surface’s just-drawn band.
DrvStartBanding | Prepares the driver for banding.


Supporting Initialization and Termination Functions

A graphics driver can support multiple devices and multiple concurrent use of each device. Therefore, initialization/termination occur in three distinct layers, each layer with its own timing. Initialization occurs in the following order, with termination in the reverse order:

  1. Driver initialization

  2. PDEV initialization

  3. Surface initialization


Driver Initialization and Cleanup

While the device driver may implement several or many functions, it exports only two of them to GDI; DrvEnableDriver, and DrvDisableDriver. The driver exposes its other supported functions through a function table. The first call GDI makes to a device driver is DrvEnableDriver. At this time, the driver fills in the passed-in DRVENABLEDATA structure so that GDI can determine which other DrvXxx functions are supported and where they are located. The driver supplies the following information in DRVENABLEDATA:

  • The DDI version number of GDI. This constant in defined as DDI_DRIVER_VERSION in winddi.h.

  • A pointer to an array of DRVFN structures that lists the supported functions their indexes.

  • The number of DRVFN structures in the array.

For GDI to call a function other than the driver’s enable and disable functions, the function’s name and location must be made available to GDI. The following is an example array of DRVFN structures for a display driver that handles many of the drawing operations and supports a hardware pointer:

DRVFN gadrvfn[] = {
    {   INDEX_DrvEnablePDEV,     (PFN) DrvEnablePDEV         },
    {   INDEX_DrvCompletePDEV,   (PFN) DrvCompletePDEV       },
    {   INDEX_DrvDisablePDEV,    (PFN) DrvDisablePDEV        },
    {   INDEX_DrvEnableSurface,  (PFN) DrvEnableSurface      },
    {   INDEX_DrvDisableSurface, (PFN) DrvDisableSurface     },
    {   INDEX_DrvAssertMode,     (PFN) DrvAssertMode         },
    {   INDEX_DrvGetModes,       (PFN) DrvGetModes           },
    {   INDEX_DrvDitherColor,    (PFN) DrvDitherColor        },
    {   INDEX_DrvSetPalette,     (PFN) DrvSetPalette         },
    {   INDEX_DrvCopyBits,       (PFN) DrvCopyBits           },
    {   INDEX_DrvSetPointerShape,(PFN) DrvSetPointerShape    },
    {   INDEX_DrvMovePointer     (PFN) DrvMovePointer        },
    {   INDEX_DrvBitBlt,         (PFN) DrvBitBlt             },
    {   INDEX_DrvTextOut,        (PFN) DrvTextOut            },
    {   INDEX_DrvStrokePath,     (PFN) DrvStrokePath         },
    {   INDEX_DrvFillPath,       (PFN) DrvFillPath           },
    {   INDEX_DrvRealizeBrush,   (PFN) DrvRealizeBrush       },
    {   INDEX_DrvStretchBlt,     (PFN) DrvStretchBlt         },
    {   INDEX_DrvPaint,          (PFN) DrvPaint              }
};

While DrvEnableDriver can also perform one-time initializations, such as the allocation of semaphores, a driver should not actually enable the hardware during DrvEnableDriver. Hardware initialization should occur in a driver’s DrvEnablePDEV routine. Likewise, a driver should enable the surface in DrvEnableSurface.

GDI calls DrvDisableDriver to notify the driver that it is about to be unloaded. In response to this call, the driver should free all resources and memory still allocated by the driver at this point. Finally, if the hardware needs to be reset, the driver will be called with DrvAssertMode.


PDEV Initialization and Cleanup

Each kernel-mode graphics driver represents a single logical device managed by GDI. In turn, a driver might manage one or more PDEVs. A PDEV is a logical representation of the physical device. It is characterized by the type of hardware, logical address, and surfaces that can be supported:

  • Type of Hardware - As an example of a driver supporting a PDEV characterized by the type of hardware, one driver could support the “LaserWhiz,” “LaserWhiz II,” and “LaserWhiz Super” printers. The device name passed by GDI specifies which logical device is requested from the total set of driver-supported devices.

  • Logical Address - A single driver could support printers attached to LPT1, COM2, and a server named \\SERVER1\PSLASER, for example. In addition, a display driver that can support more than one VGA display simultaneously might differentiate between them by port numbers, such as 0x3CE, 0x2CE, and so on. The logical address for printers and other hard copy output devices is determined by GDI; EngWritePrinter directs the output to the proper destination. Displays either know their logical address implicitly or retrieve the address from the private section of DEVMODEW.

    The DEVMODEW structure provides the driver with required environment settings, such as the name of the device and other information specific to either printer or display drivers.

  • Surfaces - Each PDEV requires a unique surface. For example, if a printer driver is to work on two print jobs simultaneously, each requiring a different page format such as the landscape and portrait formats, each print job requires a different PDEV. Similarly, a display driver might support two desktops on the same display, each desktop requiring a different PDEV and surface. For each surface required, there is a call to DrvEnablePDEV to create a different PDEV for a different surface.

In response to a call to DrvEnablePDEV, the driver returns information about the capabilities of the hardware device to GDI through several structures.

The GDIINFO structure is zero-filled before GDI calls DrvEnablePDEV. The driver fills in GDIINFO to communicate the following information to GDI:

  • Driver version number

  • Basic device technology (raster versus vector)

  • Size and resolution of printable page

  • Color palette and gray scale information

  • Font and text capabilities

  • Halftoning support

  • Style step numbers

The driver should fill only the fields it supports and ignore the rest.

The DEVINFO structure is filled in by the driver with flags that describe the graphics capabilities of this PDEV. In nearly all cases, the information from DEVINFO tells GDI the level of graphics support the driver can provide. For example, if a drawing of a treble clef is needed, information within DEVINFO tells GDI if the driver can handle Bezier curves or whether GDI must send multiple line segments instead. The driver should fill in as many fields as it supports and leave the others untouched.

Another important piece of information the driver must provide is a pointer (phsurfPatterns) to a buffer filled with handles for surfaces representing the standard fill patterns. Besides the standard fill patterns, phsurfPatterns can contain a null, which causes GDI to create the pattern surface automatically according to the device resolution and the pixel size. When GDI needs to realize a brush with a standard pattern, it calls DrvRealizeBrush to realize the brush defined for the requested pattern.

DrvEnablePDEV is passed a handle, hDriver, for the kernel driver that supports the device. For a printer driver, hDriver provides the handle to the printer and is used in calls, such as EngWritePrinter, to the spooler.

Whenever GDI calls DrvEnablePDEV, the driver must allocate the memory required to support the PDEV that is created, even if DrvEnablePDEV is called to create other PDEV structures for different modes. (A driver can have several active PDEVs enabled at the same time.) However, an actual surface is not supported until GDI calls DrvEnableSurface.

If a device surface requires the allocation of a bitmap, the allocation is not necessary until the surface is enabled (usually during the DrvEnableSurface function). Although applications often request device information before actually writing to the device, waiting to allocate a large bitmap can save valuable resources and improve driver performance during system initialization.

When the installation of the PDEV is complete, GDI calls DrvCompletePDEV to inform the driver that installation of the physical device is complete. This function also provides the driver with GDI’s logical handle to the PDEV, which the driver uses in calls to GDI functions.

A call to the driver’s DrvDisablePDEV entry point indicates that the given physical device is no longer needed. In this function, the driver should free any memory and resources used by the physical device.

Refer also to Enabling and Disabling the Surface.


Enabling and Disabling the Surface

As the final initialization stage, GDI calls DrvEnableSurface to have the driver enable a surface for an existing PDEV. DrvEnableSurface must specify the type of surface by calling the appropriate GDI service to create it. As described in GDI Support for Surfaces, and depending on the device and circumstances, the driver can call the appropriate GDI services from within DrvEnableSurface to create the surfaces:

  • For a device-managed surface, the driver should call EngCreateDeviceSurface to get a handle for the surface.

  • To create a standard-format (DIB) bitmap that GDI can manage completely, including the performance of all drawing operations, the driver should call EngCreateBitmap. The driver may hook out any drawing operations it can optimize. The driver can either have GDI allocate the space for the pixels or can provide the space itself, although the latter option is usually used only by printer drivers and framebuffer drivers.

DrvEnableSurface returns a valid surface handle as a return value, such as a bitmap handle.

Following the creation of the surface, the driver must associate that surface with a PDEV by calling the GDI service EngAssociateSurface. This call also tells GDI which drawing function a driver has hooked for that surface.

GDI calls DrvDisableSurface to inform the driver that the current surface created for the PDEV by DrvEnableSurface is no longer required. The driver must deallocate any memory and resources allocated during DrvEnableSurface. DrvDisableSurface is always called before DrvDisablePDEV, if the PDEV has an enabled surface.

Once created, a surface must be deleted when it is no longer in use. Failure to properly match surface creation with deletion can cause stray objects to accumulate and degrade system performance.


Creating Device-Dependent Bitmaps

When an application requests the creation of a bitmap, a driver can create and manage a DDB by supporting the DrvCreateDeviceBitmap function. When such a driver creates the bitmap, it can store the bitmap in any format. The driver examines the passed parameters and provides a bitmap with at least as many bits-per-pixel as requested.

Note Graphics drivers can improve performance by supporting bitmaps in offscreen memory and by drawing bitmaps using hardware, such as in the case of the S3 display driver.

Within DrvCreateDeviceBitmap, the driver calls the GDI service EngCreateDeviceBitmap to have GDI create a handle for the device bitmap.

If the driver supports DrvCreateDeviceBitmap, it creates a DDB, defines its format, and returns a handle to it. The driver controls where the bitmap is stored, and in what format. The driver should support the color format that matches its device surface most closely.

The contents of the bitmap are undefined after creation. If the application has requested a device-managed bitmap, GDI calls DrvBitBlt after DrvCreateDeviceBitmap returns control. If the driver returns zero, it does not create and manage the bitmap, instead GDI performs these tasks.

If the driver creates bitmaps, it must also be able to delete them by implementing DrvDeleteDeviceBitmap.


Supporting Graphics Output

The particular graphics output operations that a driver handles depend upon the drawing surface and the capabilities of the hardware. If the surface is a standard-format DIB, GDI will handle all rendering operations by default. The driver can hook out any of the drawing functions and implement them to take advantage of hardware support.

For a device-managed surface, a driver must, at a minimum, support the graphics output functions DrvCopyBits, DrvTextOut, and DrvStrokePath. It can support any of the other graphics output functions; supporting DrvBitBlt and DrvPaint, for example, can enhance performance. Some functions require a certain level of capability while others allow the device to indicate its capability by setting the appropriate GCAPS flags in the DEVINFO structure.

The topics that follow describe how a driver can implement the following operations:

  • Drawing lines and curves

  • Drawing and filling paths

  • Painting regions

  • Copying bitmaps

  • Halftoning


Drawing Lines and Curves

The types of lines and curves included in graphic output are geometric lines, cosmetic lines, and Bezier curves.

For line and curve output, a driver may support DrvStrokePath, DrvFillPath, DrvStrokeAndFillPath, and DrvPaint. The driver must support DrvStrokePath for drawing lines if the surface is device-managed; drivers are not required to support curves. For a DIB surface, the function is called only if the driver has hooked it through EngAssociateSurface.

When GDI draws a line or curve with any set of attributes, GDI can call DrvStrokePath. At a minimum, the DrvStrokePath function must support the drawing of solid and styled cosmetic lines with a solid color brush and arbitrary clipping. The GDI PATHOBJ and CLIPOBJ service functions make this possible by breaking down the lines into a set of lines one pixel wide with precomputed clipping. DrvStrokePath provides a pointer, plineattrs, to the LINEATTRS structure which defines the various line attributes.

When the path or clipping is too complex for the driver to process on the device, the driver can punt the call back to GDI. In this case, GDI can break the DrvStrokePath call into a set of lines one pixel wide with precomputed clipping.

By calling the CLIPOBJ services from GDI, a driver can have GDI enumerate all the lines in the path and perform all of the line clipping calculations. In addition, a driver can use the PATHOBJ, CLIPOBJ, or XFORMOBJ services to simplify the graphics operations. For example, a driver can use CLIPOBJ_cEnumStart and CLIPOBJ_bEnum to enumerate the rectangles in a clip region, send this region down to the printer, and clip to it. The driver can also use PATHOBJ_vEnumStart and PATHOBJ_bEnum to enumerate lines or curves in the path, and can then send the path to the device, and stroke it.


Cosmetic Lines

A cosmetic line is always one pixel wide and is drawn using a solid color brush. It is rendered according to the Grid Intersection Quantization (GIQ) diamond convention in which each pixel can be thought of as having a 1-pixel by 1-pixel diamond around it.

For rendering cosmetic lines, DrvStrokePath follows the GIQ diamond convention. Figure 3.1 illustrates this convention, with the shaded diamonds representing the pixels to be lit. Wherever the line touches a diamond, the corresponding pixel is lighted. Start and end pixels are determined by the diamond convention. A diamond is exclusive of its sides, except for the right and bottom vertices. These points on the diamond are inclusive, unless the slope of the line is exactly one. In this case the left and bottom vertices are inclusive. A cosmetic line is last-pixel-exclusive; that is, if the line ends inside the diamond for a pixel, that pixel is not illuminated.

Figure 3.1 Diamond Convention for Cosmetic Line

DrvLineTo is an optional entry point that a driver can supply as an optimization for application calls to the Win32 LineTo function. DrvLineTo is simpler than DrvStrokePath because it supports only integer end-points and solid cosmetic lines.

For raster devices that support the R2_NOT mix mode, the driver must use exact rendering. Rendering should also be exact for devices that need GDI to draw on some bitmaps and the driver to draw on other surfaces (unless the pixels are too small to make any visible difference) or for devices that request GDI handle complex clipping.


Geometric Wide Lines

The shape of a geometric line is determined by the width, join style, and end-cap style of the brush and the current world-to-device transform in the XFORMOBJ. The line can be drawn using either a solid or a nonsolid brush.

Drivers for more advanced hardware may support geometric wide lines in DrvStrokePath. GDI determines whether a driver can draw a path containing a geometric line by testing the GCAPS_GEOMETRICWIDE capability flag in the DEVINFO structure returned in DrvEnablePDEV. If the driver does not have the capability, or if the function refuses to handle an operation because the path or clipping is too complex for the device, GDI automatically transforms the call to the simpler DrvFillPath or DrvPaint functions.

A geometric wide line has a specific meaning to a display driver graphics function. A path containing device coordinates is transformed to world coordinates using the inverse of the current transform. A geometric construction with the specified width then obtains a widened version of the path, taking into account joins and end caps. This path is transformed to device coordinates again and filled with the specified brush.

Styling of a geometric wide line is specified by an array of floating-point values. The array has a finite length, but is used as though it repeats indefinitely. The first array entry specifies the length, in world coordinates, of the first dash; and the next entry specifies the length of the first gap. After this, lengths of dashes and gaps alternate. For example, the style array {3.0,1.0,1.0,1.0} causes a line to be drawn with alternating long and short dashes.

Styling can be thought of as the driver moving along a path before widening, “erasing” the parts of the path corresponding to the gaps. This breaks the path into many subpaths. The broken path is then widened as if it has no line style, applying end caps and joins as usual. Style arrays can be of odd length. For example, the style array {1.0} causes a line to drawn with alternating dashes. The style state (defined as the current distance into the styling array) is provided for the beginning of the first subpath in a path. It is assumed to be reset to 0.0 at the beginning of each subsequent subpath, which occurs after any Win32 MoveToEx operation.


Styled Cosmetic Lines

DrvStrokePath must support the drawing of cosmetic lines with arbitrary clipping using a solid-color brush. The driver can make a call to the GDI service PATHOBJ_vEnumStartClipLines to precompute the clipping.

Styling of a cosmetic line is similar to that for a geometric wide line because it is specified by a repeating array. For a styled cosmetic line, the array entries are LONG values that contain the lengths in style steps. The relation between style steps and pixels is defined by the xStyleStep, yStyleStep, and denStyleStep fields in the GDIINFOstructure returned by DrvEnablePDEV.

When the driver calls PATHOBJ_bEnumClipLines, to handle styled cosmetic lines through complex clipping, GDI modifies the value of the CLIPLINE structure’s iStyleState member to represent the style state. The style state is the offset back to the first pixel of the line segment; that is, the first pixel that would be rendered if the line were not clipped. The style state consists of two 16-bit values packed into a ULONG value. If HIGH and LOW are the high- and low-order 16 bits of the style state, a fractional version of the style state, referred to as style position, can be computed as:

    style position = HIGH + LOW/denStyleStep

For example, if the values in iStyleState are 1 and 2, and denStyleStep is 3, then style position is 5/3. To determine exactly where the drawing of the style begins in the style array, take the product of:

    style position * denStyleStep

In this example, with a denStyleStep value of 3, the drawing position is calculated to exclude the first five (5/3 * 3) pixels of the style array. That is, drawing begins at the sixth pixel in the style array of this clipped line.

There are y-styled cosmetic lines and x-styled cosmetic lines. If a line extends dx device units in the x-direction and dy units in the y-direction, the line is y-styled when the following is true:

    (dy * yStyleStep) ³  (dx * xStyleStep)

In this case, the style position is advanced by yStyleStep/denStyleStep for each pixel advanced in the y direction.

Conversely, a line is x-styled and the style position is advanced by xStyleStep/denStyleStep for each pixel advanced in the x direction when the following is true:

    (dx * xStyleStep)  >  (dy * yStyleStep)

When the style position advances to a new integer, the style step advances one unit in the style array.

Figure 3.2 shows several cosmetic styled lines having different slopes. In this illustration, the pixel grid shown is not square, but is shown as it would be for an EGA display in which four pixels in the x direction move as far as three pixels in the y direction. The style steps in GDIINFO ensure that styled lines appear the same at any slope on displays with nonsquare pixel coordinates. In this illustration, the styling array (defined by pStyle of LINEATTRS) is {1,1}, which is a broken line having equal-sized dots and gaps. The driver’s value of xStyleStep is 3, yStyleStep is 4, and denStyleStep is 12.

Figure 3.2 Styled Cosmetic Line Examples

To illustrate further, suppose a dot matrix printer has a 144-dpi horizontal resolution and a 72-dpi vertical resolution. In addition, suppose the dot length of the minimum dot is 1/24-inch. To support this printer, select the smallest numbers for xStyleStep and yStyleStep that can compensate for the printer’s aspect ratio, such as 1 for xStyleStep and 2 (144/72) for yStyleStep, and 6 (144/24) for denStyleStep.

If the LA_ALTERNATE bit is set in the flag in the LINEATTRS structure, a special style is used for a cosmetic line. In this case, every other pixel is on, regardless of direction or aspect ratio. Style state is returned as if the style array is {1,1} and xStyleStep, yStyleStep, and denStyleStep are all one. In other words, if lStyleState is zero, the first pixel is on; if lStyleState is one, the first pixel is off.

If the LA_STARTGAP bit is set in the LINEATTRS flag, the sense of the elements in the style array is inverted. The first array entry specifies the length of the first gap, the second entry specifies the length of the first dash, and so forth.


Bezier Curves

Some advanced hardware devices can draw paths containing Bezier curves (cubic splines), which are general-purpose curve primitives. If so, the driver can include support for these curves in DrvStrokePath.

When GDI wants to draw a Bezier curve path on a device-managed surface, it will test the GCAPS_BEZIERS flag (in the DEVINFO structure) to determine if it should call DrvStrokePath. If called, this function either performs the requested operation or decides not to handle it, just as it does for geometric wide lines. In the latter case, GDI breaks the request down into simpler operations, for example, by converting curves to line approximations.


Drawing and Filling Paths

The graphics driver considers a path to be a sequence of lines, and/or curves, defined by a path object (PATHOBJ structure). To handle the filling of closed paths, the driver supports the function DrvFillPath.

GDI can call DrvFillPath to fill a path on a device-managed surface. The interface compares the requirements of the fill with the DEVINFO flags GCAPS_BEZIERS, GCAPS_ALTERNATEFILL, and GCAPS_WINDINGFILL to decide whether to call the driver. If it does, the driver either performs the operation or returns to tell GDI that the path or clipping requested is too complex to be handled by the device. In the latter case, GDI breaks the request down into several simpler operations.

A driver can also support the optional DrvStrokeAndFillPath function to fulfill requests for path fills. This function fills and strokes a path at the same time. Many GDI primitives require this functionality. If a wide line is used for stroking, the filled area must be reduced to compensate.

When the driver returns FALSE from DrvFillPath or DrvStrokeAndFillPath, GDI converts the fill-path request to a set of simpler operations and calls the driver function again. If the device returns FALSE again on the second call to DrvFillPath, GDI converts the path to a clip object and then calls DrvPaint. For a FALSE return when DrvStrokeAndFillPath is recalled, GDI can convert the call into separate DrvStrokePath and DrvPaint calls.


Path Fill Modes

The two fill modes defined for paths are alternate and winding. Both fill modes use an even-odd rule to determine how to fill a closed path.

FP_ALTERNATEMODE applies the even-odd rule as follows: draw a line from any arbitrary start point in the closed path to some point obviously outside of the closed path. If the line crosses an odd number of path segments, the start point is inside the closed region and is therefore part of the fill area. An even number of crossings means that the point is not in an area to be filled.

FP_WINDINGMODE considers not only the number of times that the vector crosses segments of the path, but also considers the direction of each segment. The path is considered to be drawn from start to finish, with each segment’s direction implied by the order of its specified points: the first vertex of a segment is the “from” point, and the second vertex is the “to” point. Now draw the same arbitrary line described in alternate mode. Starting from zero, add one for every “forward” direction segment that the line crosses, and subtract one for every “reverse” direction segment crossed. (Forward and reverse are based on the dot product of the segment and the arbitrary line.) If the count result is nonzero, then the start point is inside the fill area; a zero count means the point is outside the fill area.

Figure 3.3 shows how to apply both rules to the more complex situation of a self-intersecting path. In alternate fill mode, point A is inside because ray 1 passes through an odd number of line segments, while points B and C are outside, because rays 2 and 3 pass through an even number of segments. In winding-fill mode, points A and C are inside, because the total forward (positive) and reverse (negative) line segments crossed by their rays, 1 and 3 respectively, does not sum to zero, while point B is outside, because the number of forward and reverse line segments that ray 2 crosses add to zero.

Figure 3.3 Alternate and Winding Mode Fills


Filling Areas (Closed Paths)

As in line drawing, pixels for filling are considered to be at integer coordinates. Each scan line in a region is bordered on the left and right by a segment of the path. Pixels that fall between the left and right borders are considered inside the fill region. Pixels that are exactly on the left border are also inside, but those exactly on the right border are excluded. If a top border is exactly horizontal, any pixels exactly on the border are inside while pixels exactly on the lower border are excluded.

Figure 3.4 shows how the pixels included in the fill region are determined relative to left and right borders of the region. Stated mathematically, the region is considered to be “closed” on the left and top, and “open” on the right and bottom.

Figure 3.4 Determining the Pixels Included in a Fill Region

The convention described above for the x-axis of the fill region also applies to the y-axis by substituting the left border with the top border and the right border with the bottom border.


Painting Regions

To a graphics driver, a clip region is a series of rectangles that can be filled by painting with a brush. A driver must support the DrvPaint function if the driver performs any drawing on a device-managed surface. When GDI calls DrvPaint, it passes a CLIPOBJ structure to define the region. The iDComplexity member of the structure classifies the painting operation as one of the following:

  • Rectangular¾A single rectangle is painted.

  • Complex¾A set of rectangles is painted.

DrvPaint performs the specified operation on the region indicated. If a complex region has been specified, the driver must first call the GDI service functions CLIPOBJ_cEnumStart and CLIPOBJ_bEnum to enumerate the rectangles that make up the region. It specifies, in the call, the type of shape it supports.

Note Vector drivers can implement this function using EngCreatePath and the PATHOBJ functions to create the hatch patterns for filling.

As indicated in Figure 3.5, the painting boundary conventions for a rectangle are similar to those for a path. The area consists of interior pixels bounded by the pixels of four lines. Two of the lines are horizontal, one designated as the top scan line and the other as the bottom scan line. The top scan line is the first line of the rectangle. The pixels of each scan line down to bottom one are included in the rectangle, but the pixels of the bottom line itself are excluded.

The two side lines of the rectangle must also be specified to completely define the shape. Pixels that fall between the left and right sides are included. Those that are exactly on the left side are also included, but those exactly on the right side are excluded.

Figure 3.5 Defining Rectangles for a Clip Region.

DrvPaint paints the area specified by the CLIPOBJ onto the surface. DrvPaint uses the pointer pbo, to indicate the BRUSHOBJ that defines the pattern and colors to fill the area.


Copying Bitmaps

Bit block transfer (bitblt) functions implemented by drivers must copy blocks of bits from one surface to another. These functions include:

· DrvBitBlt

· DrvStretchBlt

· DrvCopyBits

There is also a display-driver-specific bitblt function called DrvSaveScreenBits.

If the surface being drawn on is a device-managed surface or bitmap, the driver must support a minimum level of bit block transfer functions. If the surface is a GDI-managed standard format bitmap, GDI handles all of these operations unless the driver hooks out any of the calls in EngAssociateSurface.

DrvBitBlt provides general bit block transfer capabilities. If a driver supports a device-managed surface or bitmap, the driver must implement DrvCopyBits. At a minimum, the driver must do the following when DrvCopyBits is called:

  • Block transfer to and from a bitmap, in the device’s preferred format, and the device surface.

  • Perform the transfer with the SRCCOPY (0xCCCC) ROP.

  • Allow arbitrary clipping.

The driver can use the GDI CLIPOBJ enumeration services to reduce the clipping to a series of clip rectangles. GDI passes down a translation vector, XLATEOBJ, to assist in color index translation between source and destination surfaces.

If the surface of a device is organized as a standard-format DIB, the driver can support only simple transfers. If a call comes in with a complicated ROP, the driver can punt the block transfer request back to GDI with a call to EngCopyBits. This allows GDI to break up the call into simpler functions the driver can perform.

If a source is used, DrvBitBlt maps a geometric source rectangle exactly onto a geometric destination rectangle. If there is no source, the pptlSrc parameter is ignored. The destination rectangle is the surface to be modified and it is defined by two integer points, upper left and lower right. The rectangle is lower right exclusive; the lower and right edges of the rectangle are not part of the block transfer. DrvBitBlt cannot be called with an empty destination rectangle. The two points of the rectangle are always well ordered.

DrvBitBlt deals with different ROPs and can perform some optimizations, depending on the device. In some cases, if the ROP is a solid color, a fill rather than a bitblt can be done. For devices that do not support all ROPs, true WYSIWYG is not always possible. The PostScript driver, for example, supports no ROPs.

Optionally, a block transfer handled by DrvBitBlt can be masked and involve color index translation. A translation vector assists in color index translation for palettes. The transfer might need to be arbitrarily clipped by a display driver, using a series of clip rectangles. The required region and information are furnished by GDI.

Implementing DrvBitBlt represents a significant portion of the work involved in writing a driver for a raster display driver that does not have a standard-format frame buffer. The Microsoft VGA driver furnished with the DDK provides example code that completely supports the basic function for a planar device. Implementing DrvBitBlt for other devices may be less complex.

DrvStretchBlt can be optionally provided, even for drivers that support device-managed surfaces. This function provides capabilities for stretching block transfers between device-managed and GDI-managed surfaces. DrvStretchBlt supports only certain types of stretching, such as stretching by integer multiples.

DrvStretchBlt also allows a driver to write on GDI bitmaps, especially when the driver can do halftoning. The function also permits the same halftoning algorithm to be applied to GDI bitmaps and device surfaces.

DrvStretchBlt maps a geometric source rectangle exactly onto a geometric destination rectangle. The source is a rectangle with corners displaced by (-0.5,-0.5) from the given integer coordinates. The points specified in the function parameters lie on integer coordinates that correspond to pixel centers. A rectangle defined by two such points is considered to be geometric, with two vertices whose coordinates are the given points, but with 0.5 subtracted from each coordinate. (GDI POINTL structures use a shorthand notation for specifying these fractional coordinate vertices.) Note that the edges of any such rectangle never intersect a pixel, but go around a set of pixels. The pixels inside the rectangle are normal pixels for a “bottom right-exclusive” rectangle.

The points of the source rectangle are well ordered. DrvStretchBlt cannot be given an empty source rectangle. Unlike DrvBitBlt, DrvStretchBlt can be called with a single clipping rectangle to prevent round-off errors in clipping the output.

The destination rectangle is defined by two integer points. These points are not well ordered and the coordinates of the second point are not necessarily larger than those of the first. The source rectangle these points describe does not include the lower and right edges. Because the rectangle is not well ordered, DrvStretchBlt must sometimes perform inversions in both x and y. (The driver must not attempt to read pixels that do not lie on the source surface). DrvStretchBlt cannot be called with an empty destination rectangle.

For color translation, DrvStretchBlt provides a pointer, pxlo, to XLATEOBJ that is used to translate between the source and destination surfaces. XLATEOBJ can be queried to find the destination index for any source index. For a high-quality stretching block transfer, DrvStretchBlt is required to interpolate colors in some cases. DrvStretchBlt also uses the COLORADJUSTMENT structure to define the color adjustment values that are to be applied to the source bitmap before the bits are stretched.

DrvStretchBlt uses the iMode parameter to define how the source pixels are to be combined for output. In particular, iMode provides the HALFTONE option that permits the driver to use groups of pixels in the output surface to best approximate the color or grey level of the output. Changes to the COLORADJUSTMENT structure are passed to the driver after the next DrvStretchBlt call with an iMode of HALFTONE. In addition, if the driver wants GDI to handle halftoning for GDI bitmaps, the driver can hook out DrvStretchBlt, set the iMode parameter to HALFTONE, and return it in EngStretchBlt.

If DrvStretchBlt has hooked a call to EngStretchBlt and is asked to do something it does not support, it returns the request to GDI so that the appropriate function can handle it.

DrvCopyBits is called by GDI from its simulation operations to translate between a device-managed raster surface and a GDI standard-format bitmap. DrvCopyBits provides a fast path for SRCCOPY (0xCCCC) ROP bit-block transfers.

Required for a graphics driver with device-managed bitmaps or raster surfaces, this function must translate driver surfaces to and from any standard-format bitmap. DrvCopyBits is never called with an empty destination rectangle, and the two points of the destination rectangle are always well ordered. This call has the same requirements as DrvBitBlt.

DrvCopyBits is also called with RLE bitmaps (see the Win32 SDK) and DDBs. The bitmaps are provided to this function as a result of application program calls to several Win32 GDI routines. The optional DDB is supported only by a few specialized drivers.


Halftoning

Traditional analog halftoning uses a halftoning screen, composed of cells of equal sizes, with fixed-cell spacing center-to-center. The fixed-cell spacing accommodates the thickness of the ink, while the size of a dot within each cell can vary to produce the impression of a continuous tone.

On a computer, most printing or screen shading also uses a fixed-cell pixel size. To simulate the variable dot size, a combination of cluster pixels simulates the halftone screen. GDI includes halftoning default parameters that provide a good first approximation. Additional device-specific information can be added to the system to improve output.

The driver sends GDI the device-related specifications that GDI needs to do halftoning through the GDIINFO structure returned by DrvEnablePDEV. The driver specifies the pattern size with the ulHTPatternSize member, which defines the preferred output format for halftoning. For specific devices, halftoning relates to the halftone pattern sizes. GDI provides numerous predefined pattern sizes from 2 x 2 through 16 x 16.

For each standard pattern size, there is also a modified version. It is identified by the suffix “_M” on the standard pattern size’s name. For example, the defined name of the standard 6-by-6 pattern is HT_PATSIZE_6x6, while the name of the modified 6-by-6 pattern is HT_PATSIZE_6x6_M). The modified version gives more color resolution, but can produce a side effect of horizontal or vertical noise. In addition, because each of these pattern sizes is device resolution-dependent, the appropriate pattern size depends upon the specific device.

The tradeoff between pattern size (spatial resolution) and color resolution is determined by the pattern size. A larger halftone pattern produces better color resolution, while a smaller pattern results in the best spatial resolution. Determining the best pattern size is frequently a matter of trial and error. For more information, refer to GDIINFO in the Graphics Driver Reference.

Another of the GDIINFO structure members affecting halftoning is flHTFlags, which contains flags that describe the device resolution needed for halftoning.

GDI handles color adjustment requests from the application and passes the information down to driver functions through the DDI. If the application selects halftoning and the surface is a standard format DIB, GDI processes the bitmap using its halftoning capabilities, after which, the bitmap is sent to the device. In the PostScript driver, EngStretchBlt can send the bitmap to the printer using DrvCopyBits or DrvBitBlt (in the SRCCOPY mode).

Letting GDI perform the halftoning instead of the PostScript printer, for example, provides a faster output with better WYSIWYG quality. An interface to the PostScript driver allows the user to adjust the halftoning and provides a checkbox to turn off GDI halftoning if the printer’s built-in halftoning capabilities are preferred.

DrvDitherColor can return the DCR_HALFTONE value, which requests that GDI approximate a color using the existing device (halftone) palette. DCR_HALFTONE can be used with a display driver only when the device contains a device (halftone) palette, such as a VGA-16 adapter card, because it has a standard fixed palette. Monochrome drivers, including most raster printers, can use the iMode parameter in DrvDitherColor to obtain good gray-scale effects.

Note The current version of Windows NT does not support halftoning on 24-bit (or higher) devices.


Supporting DDI Color and Pattern Functions

DDI color and pattern functions include palette management and brush realization functions.


Managing Palettes

As described in GDI Support for Graphics Drivers, GDI handles much of the palette management work. The driver must supply its default palette to GDI in the DEVINFO structure when GDI calls DrvEnablePDEV. At this time, the driver should create the default palette with a call to the GDI service function EngCreatePalette.

Drivers that support settable palettes also must support the entry point DrvSetPalette. This function is used exclusively by display drivers.


Realizing Brushes

Graphics functions that output lines, text or fills take at least one brush as an argument. The brush defines the pattern to be used to draw the graphics object on the specified surface. Each output function that takes a brush requires a brush origin. The brush origin provides the coordinates of a pixel on the device surface to be aligned with the upper left pixel of the brush’s pattern. The brush pattern is repeated (tiled) to cover the whole device surface.

The driver can support the following functions to define brushes:

· DrvRealizeBrush

· DrvDitherColor

Drawing functions that output lines, text, or fills take at least one brush as a parameter.

A brush is always used with a mix mode that defines how the pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG value. The foreground ROP is in the lowest-order byte. The next byte contains the background ROP. For more information, see the Win32 SDK.

GDI keeps track of all logical brushes that an application has requested for use. Before asking a driver to draw something, GDI first issues a call to the driver function DrvRealizeBrush. This allows the driver to compute the optimal representation of the required pattern for its own drawing code.

DrvRealizeBrush is called to realize the brush defined by psoPattern (pattern for the brush) and by psoTarget (surface for the realized brush). A realized brush contains information and accelerators a driver needs to fill an area with a pattern. This information is defined and used only by the driver. Driver realization of a brush is written into a buffer that the driver can cause to be allocated by calling the GDI service function BRUSHOBJ_pvAllocRBrush from within DrvRealizeBrush. GDI caches all realized brushes; consequently, they seldom need to be recomputed.

In DrvRealizeBrush, the brush is represented by the BRUSHOBJ user object. The surface for which the brush is to be realized can be the physical surface for the device, a DDB, or a standard-format bitmap. For a raster device, the surface describing the brush pattern represents a bitmap; and for a vector device, it is always one of the pattern surfaces returned by DrvEnablePDEV. The transparency mask used for the brush is a one-bpp bitmap with the same extent as the pattern. A mask bit of zero means that the corresponding pattern pixel is a background brush pixel; that is, the target pixel is unaffected by that particular pattern pixel. DrvRealizeBrush uses an XLATEOBJ structure to translate the colors in the brush pattern to the device color indexes.

The driver should call the GDI service function BRUSHOBJ_pvGetRbrush when the value of the iSolidColor member of the BRUSHOBJ is 0xFFFFFFFF and the pvRbrush member is null. BRUSHOBJ_pvGetRbrush retrieves a pointer to the driver’s realization of a specified brush. If the brush has not been realized when the driver calls this function, GDI automatically calls DrvRealizeBrush for the driver’s realization of the brush.

Dithering

If necessary, GDI can request the assistance of the driver when trying to create a brush with a solid color that cannot be represented exactly on the hardware. GDI calls the driver function DrvDitherColor to request the driver to dither a brush against the reserved portion of the device palette.

Dithering uses a pattern of several colors to approximate the chosen color, and its result is an array of device color indexes. A brush created using these colors for its pattern is usually a good approximation of the given color. DrvDitherColor can also represent a color that cannot be specified exactly by a device. To do this, DrvDitherColor requests a pattern of several colors and creates a brush that approximates the given solid color.

The function DrvDitherColor is optional and is called only if the GCAPS_COLOR_DITHER or GCAPS_MONO_DITHER capability flags are set in the flGraphicsCaps member of DEVINFO. DrvDitherColor can return the values listed in the following table.
Value | Meaning
DCR_SOLID | Indicates that GDI should map the requested color to the nearest color value in the existing device palette (many to one).
DCR_DRIVER | Indicates that the dither values have been calculated by the driver. The handle to a cxDither by cyDither array of device color indexes is passed back in this case.
DCR_HALFTONE | Indicates that GDI should approximate a color using the existing device (halftone) palette. For example, GDI can use the typical palette for a printer that contains only three or four colors. DCR_HALFTONE can be used with a display driver only when the device contains a device (halftone) palette, such as VGA-16 adapter card, which has a standard fixed palette.

Monochrome drivers should support DrvDitherColor in order for GDI to obtain good gray-level patterns.


Supporting DDI Font and Text Functions

For many devices, GDI can handle all font functions. Some drivers, however, can draw their own fonts, or their device’s own fonts, on device surfaces. Other drivers are font drivers, which can provide glyph bitmaps and/or outlines, as well as glyph metrics to GDI. In these cases, the driver must support some of the available font functions.

Text output is a more general function. If the surface is a standard-format bitmap, GDI can handle all text output, unless the driver hooks out the call to enhance performance. For a device-managed surface, the driver must support text output.

The following topics provide information with regard to the support of font and text management functions.

Managing and Optimizing Fonts

Drawing Text


Managing and Optimizing Fonts

A producer is a driver that can generate fonts (produces glyph information as output, including glyph metrics, bitmaps, and outlines). A consumer is a driver that uses fonts (accepts glyph information as input for generating text output, and must draw its own fonts or those of the hardware on a device-managed surface). A driver can be both a producer and a consumer. For example, a printer driver can act as a producer while servicing a DrvQueryFontData call to provide glyph metrics and later act as a consumer while processing a DrvTextOut call.

A driver is required to handle fonts only when it is a font producer or a font consumer. If the hardware has a resident font, the driver must supply information to GDI about this font, including the font metrics in the IFIMETRICS structure, mappings from Unicode to individual glyph identities, individual glyph attributes, and kerning tables. There are also functions the driver must support. Some functions are required both by font drivers and drivers that use driver-specific or device-specific fonts. Others are required only by font drivers.

The support of font functions depends on the driver’s abilities. The general types are:

  • Metrics functions

  • Glyph functions

  • TrueType functions


Font Metric Functions

When a driver must support fonts, it must supply font information to GDI through the IFIMETRICS structure. There is a separate IFIMETRICS structure for each font. Most of the fields are expressed in terms of FWORDs, each a signed 16-bit quantity, in the design space. If the font is a raster font, the design space and device space are the same and a font unit is equivalent to the distance between pixels.

Basically, the IFIMETRIC structure is the DDI version of a text-metric structure. All distances refer to the notional coordinate system of the font designer. The notional space coordinate system is a right-handed Cartesian coordinate system in which the y coordinate increases towards the top and the x coordinate increases towards the right.

The IFIMETRICS structure is designed to be of variable length. No restriction is placed on the length of the character strings associated with the font. It is common practice to store the strings immediately following the last field of the IFIMETRICS structure.

Any driver that provides fonts must support the DrvQueryFont function. The driver also can include the function DrvQueryFontData to retrieve information about a realized font. In a call to this function, GDI provides a pointer to an array of glyphs or kerning handles. The driver returns information about associated glyphs in GDI GLYPHDATA structures. If it has been given kerning handles, it returns information about kerning pairs in the form of Win32 POINTL structures. The following table lists the font metric functions.
Function | Description
DrvQueryFont | Returns a pointer to the IFIMETRICS structure for a font. Required by all drivers that deal with fonts.
DrvQueryFontTree | Returns pointers to structures that define either the mapping from Unicode to glyph handles or the mapping of kerning pairs to kerning handles. Required by all drivers that deal with fonts.
DrvQueryFontData | Returns information about a realized font. Required (for selected iMode values) by all drivers that deal with fonts.
DrvFree | Tells the driver that the indicated data structure is no longer needed. Optional¾should be implemented only if the driver’s memory management requires this information.
DrvDestroyFont | Notifies the driver that a font realization is no longer needed so the driver can free any data structures that it allocated. GDI calls this function once for the font producer and once for the font consumer. Optional¾should be supported only if the driver must free allocated resources.

DrvQueryFontTree allows GDI to obtain pointers to tree structures that define one of the following:

  • Mapping from Unicode to glyph handles, including glyph variants (GDI FD_GLYPHSET structure)

  • Mapping of kerning pairs to kerning handles (FD_KERNINGPAIRS structure)

DrvQueryFontTree requires effort to generate the needed structures, so the driver should precompute these files if possible. The structures can be stored in a resource or in a file. If the structures are stored in a file, the ideal method for loading or reading them is to call EngMapFontFile, which maps a file to the memory. Because the file does not get added to the swap file, the memory can be made available if needed, which is more efficient than opening and reading in a file.

In particular, the driver returns an identifier in the pid parameter. GDI passes it to DrvFree, with the returned pointer, when the FD_GLYPHSET structure or an array of FD_KERNINGPAIR structures is no longer needed. Depending on how memory is managed in the driver, pid could identify the structure, the way the structure was allocated, or do nothing at all.

DrvFree and DrvDestroyFont are both optional. GDI calls DrvFree to inform the driver that the specified data structure is no longer needed. The driver does not need to implement it unless it allocates memory for the structure and needs to be informed when the corresponding data structure can be released. For example, if the data is associated with the FONTOBJ, the deletion could be deferred until a call to DrvDestroyFont, so it would not be necessary to implement DrvFree.

DrvDestroyFont notifies the driver that a font realization is no longer needed so the driver can free any data structures it allocated. GDI calls this function once for the font producer and once for the font consumer. It should be implemented only if the driver must free allocated resources when the font instance is destroyed.


Font Driver Functions

In addition to the functions described in the previous topics, the following table lists several other functions that font drivers should support.
Function | Description
DrvQueryFontFile | Depending on the mode of the query, returns the number of font faces in a font file or in a descriptive string. Required for font drivers.
DrvQueryFontCaps | Copies an array of bits defining the capabilities of a font driver to a specified buffer.
DrvLoadFontFile | Specifies a file to be used for creating font realizations; the driver must prepare the file for use. Required for font drivers.
DrvUnloadFontFile | Informs driver that a font file is no longer needed so driver can do necessary cleanup. Required for font drivers.
DrvQueryAdvanceWidths | Asks the driver to send GDI character advance widths for a specified set of glyphs.

GDI calls DrvLoadFontFile with a particular file to be used for creating font realizations. This function is required only of font drivers. When DrvLoadFontFile is called, the driver performs the conversions necessary to prepare the file for use.

DrvLoadFontFile returns a unique identifier that allows GDI to request the correct font using a GDI-maintained font usage table. Once a font is loaded, GDI does not call for the same font to be loaded again.

GDI calls DrvUnloadFontFile when the specified font file is no longer needed. DrvUnloadFontFile is required only in font drivers. DrvUnloadFontFile causes all scratch files to be deleted and all allocated system resources to be freed.

GDI calls DrvQueryFontFile to return information about a font file that was loaded by the driver. DrvQueryFontFile is required only in font drivers. The type of information to be returned is specified by iMode. If iMode is QFF_DESCRIPTION, the function returns a string that Windows NT uses to describe the font file. If iMode is QFF_NUMFACES, the function returns the number of faces in the font file. The faces are identified by an index from the range 1 to number of faces.


TrueType Font Driver Functions

TrueType font drivers must support the functions listed in the following table.
Function | Description
DrvQueryTrueTypeTable | Gives GDI access to specific files in the TrueType font file format.
DrvQueryTrueTypeOutline | Returns glyph handles in native TrueType format.
DrvGetTrueTypeFile | Gives GDI efficient access to the memory-mapped TrueType font file.

All these functions provide GDI with information about TrueType font files. DrvQueryTrueTypeTable should give GDI access to specific tables in the TrueType font-file format. DrvQueryTrueTypeOutline must send GDI glyph outlines in native TrueType format. DrvGetTrueTypeFile returns to GDI the TrueType driver’s private entry point that allows GDI efficient access to the memory mapped TrueType font file.


Drawing Text

The text output functions are called only for a device-managed surface (a device bitmap or surface), or for a GDI-managed surface if the driver has hooked the call in EngAssociateSurface. The graphic output primitives for text are:

  • DrvTextOut

  • DrvGetGlyphMode

GDI calls DrvTextOut to render a set of glyph’s pixels at specified positions for text output. Many of the DrvTextOut capabilities are defined with the GCAPS bits of the DEVINFO structure returned by DrvEnablePDEV.

The input parameters for DrvTextOut define two sets of pixels, foreground and opaque. The driver renders the surface to provide the following results:

  1. The opaque pixels are rendered first, with the opaque brush.

  2. The foreground pixels are then rendered with the foreground brush.

Each of these rendering operations is performed in a clip region. The pixels outside the clip region cannot be affected.

The driver must render the surface so opaque pixels are calculated and drawn on the surface first with an opaque brush. Then foreground pixels are calculated and rendered with a foreground brush. Each of these operations is limited by clipping.

Foreground and opaque pixels make up a mask through which color is brushed onto the surface. The glyphs of a font do not, in themselves, have color. The foreground set of pixels is defined as the union of the glyphs’ pixels and the pixels of certain extra rectangles used to simulate strikethrough or underline. Opaque pixels are defined by opaque rectangles.

DrvTextOut selects the specified font using a pointer, pfo, to query the current FONTOBJ. This process can include downloading a soft font or a font substitution, or any other font optimizations necessary for the device.

If a driver has scalable fonts, it should call FONTOBJ_pxoGetXform for the current FONTOBJ to return the notional-to-device transform for the associated font. This is required for a driver-supplied font. Notional space is the design space of the device font. For example, PostScript fonts are defined in 1000-by-1000 unit character cells. Most of the metrics returned in the IFIMETRICS structure are converted to notional space, which is why the notional-to-device transform is necessary.

The graphics engine queries the driver by calling DrvGetGlyphMode to find out how it should internally cache its font information. It can cache individual glyphs as bitmaps, cutlines, or neither (the proper choice for device fonts).


Chapter 4 Display Driver Environment

Part 1 of this document described general graphics driver design and implementation that applies to both printer and display drivers. Topics included initialization and termination, graphics output (line and path drawing, painting, and bitmap copying), and color, pattern, font, and text functions. Display driver writers should read Part 1 before proceeding with this part, which is Part 2.

Part 2 focuses on how a display driver operates, and describes issues specific to display driver design. Part 2 also provides guidelines for video miniport driver design.

This chapter of Part 2 covers the following subjects:

  • An overview of the graphics display driver.

  • Types of video display adapters.

  • Display driver design and implementation strategies.

  • The hardware interface.

The Windows NT DDK includes sample display drivers that support several standard video adapter cards. This chapter refers to some of those sample drivers.


About Windows NT® Display Drivers

A Windows NT display driver is a DLL whose primary responsibility is rendering. Applications call Win32 functions with device-independent graphics requests. GDI interprets these instructions and calls the display driver. The display driver translates device-independent graphics commands into commands the video hardware needs to draw graphics on the screen.

Each display driver is paired with a kernel-mode video miniport driver. The miniport driver normally handles operations that must interact with other NT kernel components. Figure 4.1 shows the display driver and the other components required for graphics video display.

Figure 4.1 The Display Driver and other Graphics Video Components

Because the display is among the most time-critical parts of any system, and because of the wide variety in video hardware capabilities, the display driver also accesses the hardware directly. This access and the wide scope of capabilities within GDI offer considerable latitude in implementing the various functions of a display driver:

  • By default, GDI handles drawing operations on standard format bitmaps, as in hardware that includes a frame buffer. A driver can hook out and implement any of the drawing functions for which the hardware offers special support. For less time-critical operations and more complex operations not supported by the video adapter, the driver can punt functions back to GDI and allow GDI to do the work.

  • For especially time-critical operations, the display driver designer has direct access to video hardware registers. For example, the VGA display driver for x86 systems uses optimized assembly code to implement direct access to hardware registers for some drawing and text operations.

The display driver should not perform resource management, such as hardware detection and configuration, or physical device memory mapping. These are functions of the video miniport driver.

As shown in Figure 4.1, Windows NT display drivers are concerned with two software interfaces:

  1. The DDI interface, which is the set of functions that the display driver can hook, and that GDI can call in order to process graphics commands.

  2. The GDI callbacks, generally referred to as “Eng” callbacks, which are help routines provided by GDI for the display driver’s use.

Required, conditionally required, and optional DDI function support specific to display drivers is described in DDI Support in Display Drivers. DDI function support common to both display drivers and printer drivers is described in Supporting the DDI, which is in Part 1 of this book. Part 1 also provides information on GDI operation and the service functions available to graphics drivers. Reference pages for the DDI and GDI functions can be found in the Graphics Driver Reference.


Display Adapters

For each different type of graphics adapter card that can be used with Windows NT, there must be both a display driver and a corresponding video miniport driver. The miniport driver is specific to the adapter (or a family of adapters), but a display driver can be specific to a class of adapters with a common drawing interface. This is because the display driver draws and the miniport driver performs operations such as mode sets and provides information about the hardware to the driver.

More than one display driver can work with a given miniport driver. For example, the 16- and 256-color SVGA display drivers can use the same miniport driver. A display driver can also work with more than one miniport driver if the adapter types provide the same drawing interface. One example is the VGA display driver, which can be used with either the VGA miniport driver or the ET4000.

There are four general types of video hardware:

  • Frame buffers

  • Accelerators

  • Coprocessors

  • VGA


Frame Buffers

A frame buffer is dedicated memory on a video adapter. Simple frame buffer video adapter hardware contains a frame buffer but no accelerator. If the hardware is linearly addressable and it can support standard format bitmaps (DIB format), GDI can handle the rendering operations and write directly to the frame buffer. This can reduce the size of the display driver because the driver can let GDI do all or most of the drawing. Most frame buffers today are linearly addressable, or “flat”.

Among the sample display drivers is a frame buffer driver in the directory named framebuf. Because this driver supports linearly addressable frame buffer hardware, it allows GDI to do most of the work, and so is quite small and relatively simple.


Accelerators

An accelerator contains onboard circuitry that enhances the speed of graphics operations. Such hardware can contain accelerations for specific rendering operations such as line drawing and filling, or bit block transfers. If the hardware provides support for a pointer, it is usually much faster for the driver to make use of this than to use the pointer support provided by GDI.

The S3 sample driver is a sophisticated and fairly complicated example of an accelerator that can be found in the s3 display driver source example directory. The S3 display driver is coded entirely in C so that it is portable across systems. It supports both flat and banked frame buffers. For details about banked frame buffers, see Supporting Banked Frame Buffers.


Coprocessors

A video adapter that includes a coprocessor offers another way to accelerate graphics display functions. A coprocessor is basically an accelerator with additional programmable command logic. Because the coprocessor is programmable, it enables multiprocessing, allowing the coprocessor to handle some time-consuming operations while others are occurring elsewhere.


VGA

The VGA is the standard video adapter card for x86-based computers. This hardware can operate in both text and graphics modes. A number of accelerators, such as the S3, are built on top of VGA-compatible hardware. There are three sample VGA display drivers: VGA, VGA64K, and VGA256.

The VGA display driver is the standard VGA 16-color driver, supporting any resolution up to 2K pixels across and any number of vertical scanlines. The VGA256 sample driver is designed for 256-color VGA. Both the VGA and the VGA256 use optimized assembly code to implement accelerations. The VGA256 driver does screen-to-screen scrolls, solid and pattern fills, and text with fast assembly language, using the VGA hardware for acceleration wherever possible. The VGA64K and VGA256 drivers are banked frame buffer drivers. Most other functions are punted to GDI one bank at a time. A driver built on VGA-compatible hardware could be derived from the VGA256 driver by hooking and implementing the appropriate functionality.


General Design and Implementation Strategies

To design an effective Windows NT display driver, consider the following suggestions and facts:

  • Start with a DDK sample display driver written for a video adapter similar to the adapter for which you are writing the driver. It would probably reduce driver design time to modify one of the existing sample drivers rather than write one from scratch.

  • For maximum portability, write as much of the driver as possible using the C language. Use assembly language only when necessary for time-critical operations that are not well supported by video adapter hardware. Coding in assembly, while offering the possibility of optimization, is time-consuming and removes the possibility of portability.

  • Use miniport drivers for operations that involve resource management, physical device memory mapping, ensuring that register outputs occur in close proximity, or responding to interrupts. The main purpose of miniport drivers is to handle hardware variations within a family and to minimize display driver hardware-type dependencies.

  • The display driver is in the same privileged kernel-mode address space as the rest of the Windows NT Executive. Faults in the display driver will cause GDI and the rest of the system to fault.

  • Display drivers (and video miniport drivers) are fully preemptable at any time.

  • Both the code and data sections of a display driver are entirely pageable.

  • Exported functions must execute the standard Windows NT prolog on entry and the epilog on exit. For more information, see the Win32 SDK.


The Hardware Interface

To ensure display performance, display drivers can affect hardware operation in several ways:

  • Through IOCTLs to a video miniport driver, also written specifically for the video adapter hardware.

  • Directly to the video memory (frame buffer).

  • Directly to hardware registers.


The Video Miniport Driver Interface

Certain operations, such as hardware initialization and memory mapping, require action by the Windows NT I/O subsystem. These operations are done by the video miniport driver, which must be specific to the video hardware.

To send synchronous requests to the video miniport driver, the display driver makes a call to GDI’s EngDeviceIoControl routine with an IOCTL. GDI, using one buffer for both input and output, passes the request to the Windows NT I/O subsystem. There, the video port driver and the video miniport driver that have been paired with the display driver work together to process the request as necessary.

Some IOCTL requests require the miniport driver to access video registers, and some store or retrieve information from the miniport driver’s data structures. Normally, no requests actually require the video driver to perform drawing operations.

Figure 4.2 shows the driver’s communication with the miniport driver by way of IOCTLs.

Figure 4.2 Display Driver/Video Miniport Driver Communication

In general, and unless modularity dictates otherwise, the display driver handles drawing and other time-critical operations. Sending an IOCTL to the miniport driver to perform a time-critical function can degrade system performance.

The display driver calls on the video miniport driver to support infrequently requested operations. This includes all hardware initialization and detection, memory mapping, ensuring that register outputs occur in close proximity, and responding to interrupts. The video miniport driver generally handles mode set interaction with the video adapter card. It should also be used to handle multiple hardware types and to minimize hardware-type dependency in the display driver. The video miniport can map the video register into the display driver’s address space. I/O ports are directly addressable.

These rules are not hard and fast; the balance between modularity and performance is the key. For example, the hardware pointer code for the VGA driver resides in the miniport. This promotes modularity, so the same display driver can handle both the Video Seven VRAM, which has a hardware pointer, and the ET4000, which does not.

See the Graphics Drivers Reference for descriptions of system-defined video IOCTLs. You can extend the interface between the display driver and the video miniport driver by adding private IOCTLs, which must be formatted as described in the Kernel-Mode Drivers Reference. If you need to write a new IOCTL, please contact Microsoft Technical Support.

For information on video miniport design, see Video Miniport Drivers.


Frame Buffer and Hardware Register Access

To reduce display driver size, it is desirable to implement only those functions that the display driver can do faster than GDI and have GDI perform all other operations. It is common to have GDI do much of the drawing to linear frame buffers to reduce the size of the driver. However, GDI cannot directly access banked memory. Therefore, when the frame buffer is not linearly addressable, the display driver must divide the frame buffer into a series of banks and provide a means for GDI to perform its draw operations to the appropriate bank. See Supporting Banked Frame Buffers for details.

The display driver has direct access to I/O- and memory-mapped video registers. This access allows a display driver to achieve high performance. For example, the driver might need to access video hardware registers to send line-drawing commands at high throughput.

Similarly, for video cards, such as the S3, many of the innermost loops in the graphics engine code require reads and writes of several video controller ports (for example, text output in graphics mode, bit block transfers, and line drawing). The performance in these cases would be unacceptable if the display driver were required to send an IOCTL to the miniport driver for each request, so the display driver is granted direct access to the video hardware. This access is granted by the miniport and video port drivers.


Chapter 5 DDI Support in Display Drivers

Part 1 of this document discussed general graphics driver design and summarized DDI functions and GDI support services that are common to both printer drivers and display drivers. This chapter presents information on implementing DDI entry points that are specific to display driver design. This includes discussion in the following areas:

  • Display driver initialization

  • Managing desktops

  • Controlling the mouse pointer

  • Unloading video hardware

  • Supporting coprocessor synchronization

For complete descriptions of the DDI entry points and structures, as well as GDI service functions and objects, see the Graphics Driver Reference.


Display Driver DDI Functions

A Microsoft® Windows NT® display driver must support several DDI functions. In addition, the driver should support any operation it can do more efficiently than GDI; although the driver will be smaller and easier to write if it makes maximum use of existing GDI capabilities.

At a minimum, every display driver must do the following:

  1. Enable and disable the graphics hardware.

  2. Supply GDI with information about hardware capabilities.

  3. Enable the drawing surface.

  4. If the surface is device-managed, carry out graphics operations. At a minimum, this includes transferring bitmaps, and drawing text.

Driver calls are serialized for any given surface.

Some functions are required, depending on the type of display driver, while others are optional and their inclusion in the driver depends on what functionality the driver is to optimize. The topics that follow list the required, conditionally required, and optional DDI functions for display drivers.


Required Display Driver Functions

The list of functions that all graphics drivers must implement is in Required Graphics Driver Functions. In addition to these functions, display drivers must also implement the following functions:
DrvGetModes | Lists the modes supported by a specified hardware device.
DrvAssertMode | Resets the video mode for a specified hardware device.
DrvDitherColor | Requests a device to create a brush dithered against a device palette.


Conditionally Required Display Driver Functions

Depending on how a driver is implemented and on the features of the underlying adapter, other DDI functions can be required. For example, if a driver manages its own surface (using EngCreateDeviceSurface to get a handle to the surface), that driver must also, at a minimum, support the following drawing functions:

  • DrvCopyBits

  • DrvTextOut

  • DrvStrokePath

  • DrvPaint

Drivers that write to standard format DIBs usually allow GDI to manage most or all of these operations. Displays that support settable palettes must support the DrvSetPalette function. The list of conditionally required functions for all graphics drivers is in Conditionally Required Graphics Driver Functions.

Some display drivers manage fonts, although it is more common for a printer driver to do so. A display driver is not required to handle fonts unless it must draw its own fonts or those of the video hardware on a device-managed surface. If the hardware has a resident font, the display driver must supply information to GDI about this font. This information includes font metrics, mappings from Unicode to individual glyph identities, individual glyph attributes, and kerning tables. Therefore, if your display driver supplies fonts, it must support the DDI entry points in Conditionally Required Graphics Driver Functions that support fonts.


Optional Display Driver Functions

In the interest of reducing driver size, display drivers usually add only those optional functions that are well supported in video hardware. The display driver can implement the following optional functions:
DrvCreateDeviceBitmap | Creates and manages a bitmap with a driver-defined format.
DrvDeleteDeviceBitmap | Deletes a device-managed bitmap.
DrvFillPath | Paints a closed path for a device-managed surface.
DrvMovePointer | Moves a pointer to a new position and redraws it.
DrvBitBlt | Executes general bit block transfers to and from surfaces.
DrvRealizeBrush | Realizes a specified brush for a defined surface.
DrvSaveScreenBits | Saves or restores a specified rectangle of the screen.
DrvSetPointerShape | Removes the pointer from the screen, if the driver has drawn it, and then sets a new pointer shape.
DrvStretchBlt | Allows stretching block transfers among device-managed and GDI-managed surfaces.
DrvStrokeAndFillPath | Simultaneously fills and strokes a path.
DrvSynchronize | Coordinates drawing operations between GDI and a display driver-supported coprocessor device; for engine-managed surfaces only.
DrvFree | Frees storage associated with an indicated data structure.
DrvDestroyFont | Notifies driver that a font realization is no longer needed; driver can free allocated data structures.
DrvEscape | Queries information from a device not available in a device-independent DDI.
DrvDrawEscape | Implements draw-type escape functions.


Display Driver Initialization

Although most of display driver initialization is generally the same as that for graphics drivers, there are a few details specific to display drivers. This section presents a brief synopsis of the process. See the Programmer’s Guide for more information about the boot process.

Video miniport and display driver initialization occur after the Windows NT executive and the Win32 subsystem are loaded and initialized. It is USER who opens video miniport driver(s) enabled in the registry and then determines which video miniport driver and display driver pair to use. During this determination, GDI opens all necessary display drivers, based on the information provided by Window Manager. Figure 5.1 shows the basic display driver initialization procedure.

Figure 5.1 Display Driver Initialization

  • When GDI is called to create the first DC for the video hardware, GDI calls the display driver function DrvEnableDriver. Upon return, DrvEnableDriver furnishes GDI with a DRVENABLEDATA structure that holds both the driver version number and the entry points of all functions the driver chooses to support that GDI can call (other than DrvEnableDriver and DrvDisableDriver).

  • GDI then typically asks for a physical device to be created by a call to the display driver function DrvEnablePDEV. The call uses the GDI DEVMODEW structure to identify the exact device and mode that GDI wants to access. The display driver initializes the hardware to a known state by sending requests to the video port-miniport driver pair. If GDI requests a mode that the display or underlying miniport driver does not support, the display driver must fail this call.

  • The display driver represents a logical device controlled by GDI. A single logical device can manage several physical devices, each characterized by type of hardware, logical address, and surfaces supported. The display driver allocates the memory to support the device it creates. A display driver can manage more than a single display defined as a PDEV. The display driver copies device information to a PDEV structure representing the display and its current operating state. DrvEnablePDEV can be called more than once by GDI, with each call creating another PDEV that is used with a different surface. However, an actual surface is not supported until the GDI calls DrvEnableSurface.

    If a driver is to support more than one PDEV, it must not use global variables.

  • When installation of the physical device is complete, GDI calls the display driver DrvCompletePDEV function. This function provides the driver with a physical device handle to be used when requesting GDI functions for the device.

  • As the final stage of initialization, a surface is created for the video hardware by a GDI call to DrvEnableSurface which enables graphics output to the hardware. Depending on the device and the environment, the display driver enables a surface in one of two ways:

    • The driver manages its own surface by calling the GDI function EngCreateDeviceSurface to obtain a handle for the surface. The device-managed surface method is required for hardware that does not support a standard-format bitmap and is optional for hardware that does.

    • GDI can manage the surface completely as an engine-managed surface if the hardware device has a surface organized as a standard-format bitmap. Even in this case, when it calls EngAssociateSurface, the driver can still hook any drawing functions that it can more efficiently handle.

The display driver can obtain a bitmap handle for a surface by calling EngCreateBitmap with a pointer to the device pixels. GDI collects the graphics directly in a bitmap. The display driver calls EngCreateBitmap, allowing the GDI to allocate space for the pixels.

Any existing GDI bitmap handle is a valid surface handle. Before returning a surface, the display driver must associate it with the PDEV, using a call to EngAssociateSurface, specifying which functions it wishes to hook for drawing to the surface. If the surface is engine-managed, GDI can handle any or all drawing operations. If the surface is device-managed, at a minimum, the driver must handle DrvTextOut, DrvStrokePath, and DrvCopyBits.


Desktop Management

There are two functions that display drivers must support to manage desktops: DrvAssertMode and DrvGetModes. If the display driver is palette-managed it will also receive a call to DrvSetPalette to reset its palette to the correct state.


Switching Desktops: Responding to DrvAssertMode

When switching between desktops on a single display, the Window Manager makes sure that the desktops are properly redrawn and that a mouse pointer is enabled and put in the right location. The display device driver receives a call to DrvAssertMode only when there is a desktop switch.

This function causes the driver to make sure the indicated PDEV is in the mode specified when the PDEV was created, or is in text mode. The Window Manager then selects the correct pointer shape and moves it to the current position. Thus, GDI relieves the driver of the responsibility of maintaining the mouse pointer state.

GDI calls the DrvAssertMode function in the display driver to set the mode of a specified hardware device. This function selects either the mode specified when the display driver-defined PDEV structure was created or the default mode of the hardware. The driver should keep a record of the current mode of the PDEV.

GDI also calls DrvAssertMode, with the enable parameter set to FALSE, when the user switches from a windowed application to a full-screen application in x86 applications, or when switching desktops (on all platforms). The display driver must restore the video hardware to a default mode by sending an IOCTL_VIDEO_RESET_DEVICE in an EngDeviceIoControl call to the video port-miniport drivers.


Returning Display Modes: DrvGetModes

The display driver must also support DrvGetModes. This function gives GDI a pointer to an array of DEVMODEW structures. The structures define the attributes of the display for the various modes, including information about dimension (in both pixels and millimeters), number of planes, bits per plane, color information, and so on.


Supporting Multiple PDEVs

A graphics driver enables a PDEV for the default desktop upon which applications can run. A graphics driver must also support the enabling of additional PDEVs since it is possible for an application to create a new PDEV with a new desktop. This requirement is placed on the display driver by the Display applet contained within the Control Panel. This applet allows the user to run a brief test of selected changes to such things as the size, number of colors, and refresh rate of the screen. The Display applet creates a temporary desktop to test the changes made by the user to the display settings.

Figure 5.2 Creating and Destroying a Second PDEV with the Display Applet.

Figure 5.2 shows that an application can create a new PDEV while the current PDEV is still loaded. In the second stage of this example, the Display applet temporarily disables the current PDEV and, when the user clicks the Test button, creates a second PDEV that matches the display mode selections in the dialog box. After a brief period of use, where the user views a bitmap on the display screen under the test mode, the second PDEV is destroyed and Display applet restores the original PDEV for the desktop. Without the ability to revert back to the original display settings, the system would become unusable if settings incompatible with the installed hardware and driver were chosen.

Note GDI will not call the driver to create a second PDEV if the DDI version number of the driver, returned in DRVENABLEDATA, is 1.0 since this version number indicates lack of support for a second PDEV.

A PDEV is private to a driver and contains all the information and data that represents the associated physical device. To accommodate the creation of multiple PDEVs, the graphics driver must meet a pair of requirements. The first is that the driver must not use global varibles in lieu of dereferencing members of a PDEV structure. If global variables are used, they may contain or point to random data when a new PDEV is created, or an old one restored. All state information must be saved in the PDEV. The PDEV is always passed down on any graphics operation and so is used to get or set global data.

Secondly, DrvDisableSurface and DrvDisablePDEV must be implemented in the graphics driver so that an application can create and destroy additional PDEVs, and in some cases load more than one driver.

Note In some cases with the Display applet, the test bitmap will be displayed using a different driver than the currently-loaded driver. For example, the user’s system could be running in 16-color mode with the VGA driver and test 64K-color mode with the VGA64K display driver. In this case, the VGA64K driver is loaded dynamically and unloaded when the test is completed.


Pointer Control

Every application must be able to control a pointer that moves around a windowed display in response to a pointing device, such as a mouse. The display driver, GDI, or the video miniport driver can draw the pointer. Refer also to Controlling the Pointer and Moving the Pointer.

GDI can take care of all pointer drawing directly for a display that uses a linearly addressable buffer. For a device that is not a linear frame buffer, GDI uses DrvCopyBits for pointer drawing. However, specialized pointer code, supported by hardware and implemented in the device driver is much faster.

Display drivers are allowed some freedom on the kinds of pointers they draw. For example, a device might support monochrome pointers in hardware and fail the color pointer calls, allowing GDI to handle the color pointers.

The display driver can control the pointer in situations for which the processor does not have to be owned exclusively and the pointer does not have to be drawn off an interrupt, such as the vertical synchronization interrupt. In these special cases, the miniport driver must draw and control the pointer because certain kernel-mode callbacks available only in the video miniport driver are required. This can affect performance adversely, because it requires IOCTLs to communicate with the miniport driver for each pointer operation.

In writing a display driver and miniport driver pair, IOCTLs must be included for passing pointer information between the two. This allows the miniport driver to take over drawing of any or all pointers, if necessary. VGA, SVGA, S3, frame buffer, and XGA devices all use this approach to pointer handling. See the Graphics Driver Reference for video pointer IOCTL definitions.


Pointer Drawing

GDI supports both color pointers and monochrome pointers. The shape of a monochrome pointer is defined by a single bitmap. The width of the bitmap is the same as the width of the pointer on the display, but the bitmap has twice the vertical extent as appears on the display, allowing it to contain two masks.

Calls to the pointer functions are serialized by GDI. This means two different threads in the driver cannot execute the pointer functions simultaneously. There are two possible pointer functions: DrvSetPointerShape and DrvMovePointer.


Drawing Monochrome Pointers

There are two parts to a monochrome bitmap; the first part defines the AND mask for the pointer while the second part defines the XOR mask. Taken together, these masks provide two bits of information for each pixel of the pointer image. The following truth table determines what should be drawn on the display for different values in the AND and XOR masks:
AND mask value | XOR mask value | Result displayed on screen
0 | 0 | Black.
0 | 1 | White.
1 | 0 | Transparent, pixel is unchanged
1 | 1 | Inverts the pixel color

This supplies a black and white image while allowing for transparency and inversion of the pixels that make up the pointer.


Drawing Color Pointers

Defining the color pointer this way also gives transparency and inversion. Where the color bitmap is black (index 0) and the AND mask is 1, the result is transparency. Where the color bitmap is white and the AND mask is 1, the result is inversion. If the device cannot display color, a pointer defined this way can be drawn as black and white. These conventions allow applications to use a single pointer definition for color and monochrome displays.


Controlling the Pointer: DrvSetPointerShape

If a display driver controls the pointer, the driver must support DrvSetPointerShape to allow a change in the pointer shape. A call to DrvSetPointerShape has the following effect:

  1. If there is an existing pointer that the driver has drawn on the display, the function removes it.

  2. It then sets the new shape that has been requested, declining to do so only if it cannot handle the shape.

  3. Finally, the new pointer is displayed at the position indicated by the parameters of the call.


Moving the Pointer: DrvMovePointer

If DrvSetPointerShape is included in the driver, DrvMovePointer must also be supported. This function moves a driver-managed pointer to a new position. Because GDI serializes calls to pointer functions, DrvMovePointer will never be called while any thread is drawing in the display driver, unless the GCAPS_ASYNCMOVE flag has been set in the DEVINFO structure.


Managing Display Palettes

If the video hardware supports colors that can be set, it maintains a color lookup table called a palette. GDI takes each RGB value and translates it into a device color index so that it can be displayed. GDI uses precalculated and cached tables to assist in the translation. These tables are accessible to drivers as the user object XLATEOBJ. Therefore, every GDI graphics function that takes source colors and moves them to a destination device uses a XLATEOBJ structure to translate the colors. For more information on palettes in general, and how GDI handles them, see Part 1.

If the video hardware supports palettes that can be set, GDI calls the DrvSetPalette function in the display driver when it has finished mapping colors into the device palette requested by the application. GDI passes the new palette to the display driver, and the driver queries the PALOBJ to set its internal hardware palette to match ("realize") the palette changes for the video hardware.

The DrvSetPalette function supplies a handle to a PDEV to the driver, and requests the driver to realize the palette for that device. The driver should set the hardware palette to match the entries in the given palette as closely as possible.

This entry point is required if the device supports a palette that can be set, and should not be provided otherwise. A display driver specifies that its device has a settable palette by setting the GCAPS_PALMANAGED bit in the flGraphicsCaps field of the DEVINFO structure returned in DrvEnablePDEV.

The service routine PALOBJ_cGetColors is available to display drivers. This function downloads RGB colors from an indexed palette, and should be called from within the implementation of DrvSetPalette.


Bitmaps in Display Drivers

Certain devices, such as the 16-color VGA display, can perform bit block transfers much faster from nonstandard bitmaps. To support this, a driver can choose to be given the chance to create such surfaces by hooking DrvCreateDeviceBitmap. This allows the driver to create a bitmap that a driver manages completely. When a driver creates such a bitmap, the driver can store it in any format. The driver examines the passed parameters and provides a bitmap with at least as many bits-per-pixel as requested. The contents of the bitmap are undefined after creation. If the application has requested a device-managed bitmap, GDI would call the driver for drawing functions after DrvCreateDeviceBitmap returns control. If the driver returns FALSE, the driver-managed bitmap is not created, so GDI can handle drawing operations on an engine-managed surface.

Few display drivers manage their own bitmaps. Driver-managed bitmaps are provided only for specific devices like the VGA adapter card, which can perform a DrvBitBlt much faster from a bitmap in nonstandard format.

Another function related to bit block transfers in display drivers is the DrvSaveScreenBits function. Some display drivers can move data to or from off-screen device memory much faster than an area can be redrawn or copied from a DIB. Such a driver can hook DrvSaveScreenBits, allowing the driver to be called to save or restore a specified rectangle of a displayed image more speedily when a menu or dialog box is popped up.

Note For bit block transfer calls, the driver does not have to deal with pointer exclusion or clip region locking because GDI handles this.

Drivers that implement device bitmaps in off-screen memory can significantly improve system performance. Off-screen device bitmaps improve system performance by:

  • using accelerator hardware in place of GDI to do drawing

  • improving the speed of bitmap-to-screen bit block transfers

  • reducing demands on main memory (a bitmap stored in off-screen memory isn't taking up space in main memory)

  • taking advantage of hardware to perform such operations as mask bit block transfers and double-buffering in support of OpenGL.

Drivers should implement device bitmaps in off-screen memory through DrvCreateDeviceBitmap. The sample S3 driver implements device bitmaps in off-screen memory and should be referred to for a complete example.


Supporting Banked Frame Buffers

To reduce its size, a driver can be designed to implement the functions it can perform faster than GDI and have GDI perform all other operations. GDI can be used to draw directly to all standard 1, 4, 8, 16, 24, and 32-bit-per-pixel bitmaps and it is common to have GDI do much of the drawing. This scheme allows a display driver to hook any call it can accelerate and let GDI handle all other calls. This type of GDI support works best when the entire frame buffer can be mapped linearly into the CPU’s address space. However, GDI cannot directly access banked memory associated with a banked frame buffer. Consequently, the display driver must divide the frame buffer into a series of contiguous banks and provide a means for GDI to perform its draw operations to the appropriate frame buffer banks. That is, GDI is made to write data to one bank of the frame buffer before being moved to subsequent banks as necessary to complete the draw operation through a mechanism referred to as “banked callbacks.”

Figure 5.3 shows that the accelerator’s frame buffer, a 1024-by-768 VGA display buffer, is divided into several banks. In this example, the contents of the video memory are written to the accelerator through a series of draw operations that address contiguous banks of the frame buffer. As far as GDI is concerned, each of its draw operations appear to be to the standard frame buffer and not to different banks of the accelerator’s frame buffer. The device driver for the accelerator handles the banking operations that cause GDI to draw to the accelerator’s frame buffer on a bank by bank basis.

Figure 5.3 is provided for the purpose of illustration only. The display driver does not specifically use the physical address A000 but uses a logical address passed to it by the miniport driver.

Figure 5.3 Example of Mapping Video Memory to a Banked Frame Buffer

When an accelerator that employs a banked frame buffer is in use in a system, the frame buffer is a device-managed surface and so the display driver hooks the draw function calls. When the display driver hooks a call, such as draw path, fill path, or block transfer, it determines which banks in the frame buffer are affected by the called draw function.

If the driver elects to have GDI perform the draw function, the driver calls the appropriate EngXxx function. However, before making the call, the display driver must modify the clip and surface objects it received in the hooked call and pass these modified objects in the call back to GDI. The clip and surface objects are modified to prevent GDI from drawing beyond the extents of the bank. That is, if GDI is called to draw a path that exists partially in the next bank, and if there is no modification of the clip and surface objects, GDI will write to memory beyond the extents of the current bank. Attempts by GDI to draw outside the extents of the bank will result in access violations that may be difficult to track.

The example banked frame buffer in Figure 5.4 shows how an elliptical object drawn on the display spans two banks of the banked frame buffer, BANK_1 and BANK_2. For GDI to draw this object, it must be made to draw the top portion of the ellipse (appearing in BANK_1) to the standard frame buffer and then the lower portion of the ellipse to the same standard buffer. It is up to the device driver to map these two successive writes by GDI to BANK_1 and BANK_2 of the banked frame buffer for display and also prevent GDI from writing beyond the limits of each bank.

When performing banked frame buffering, the display driver can determine the bounds of the object (the size of the destination rectangle) by checking the parameters of the call or by calling back to GDI. From the bounds of the object, the driver can determine how many banks are spanned by the object. For every bank that the bounding rectangle touches, the display driver calls back to the appropriate GDI draw function, altering values for each call.

One set of values that need to be altered are members of the CLIPOBJ originally passed by GDI. Changes to this structure inform GDI of the bounds of the bank. That is, the top and bottom scan values that GDI must draw to are redefined so that it doesn’t attempt to draw beyond the limits of the bank. The bank manager (bankmgr.c of the VGA64K driver) takes the original CLIPOBJ data obtained from GDI, saves the values away for later restoration, and alters the bounds values. The bounds are changed to provide new rclBounds.top and rclBounds.bottom values that describe the extents of the bank being drawn to. That is, it is necessary during banking that GDI perform clipping to a size that avoids drawing the entire path and overwriting the limits of the current bank in the process.

Figure 5.4 Drawn Objects Span Multiple Banks in the Frame Buffer

If the original CLIPOBJ passed by GDI was defined as null or DC_TRIVIAL, the display driver passes a subsititute CLIPOBJ, created via EngCreateClip, that is modified to define a clip window and cause GDI to clip to the extents of a single bank. If the CLIPOBJ is complex, such as with a triangular-shaped clip object on top of the ellipse as shown in Figure 5.4, the display driver modifies the complex CLIPOBJ with the rclBounds.top and rclBounds.bottom values so that there is an additive effect between the two clip objects. The net result is that GDI is prevented from writing off the end of the bank. The driver must also restore the original bounds of the CLIPOBJ data previously obtained from GDI.

In addition to altering the bounds values, the display driver sets the OC_BANK_CLIP flag in the clip object to inform GDI that this is a banked callback. The example code for this clipping operation is located in bankmgr.c of the VGA64K sample driver.

Not only does GDI need to be informed of the clipping required when drawing to the banked frame buffer, but it must also be made to draw with reference to the beginning of the standard frame buffer. When called to draw, GDI simply gets a pointer to a SURFOBJ which includes the pvScan0, lDelta, and iBitmapFormat members. GDI calculates where to draw on the surface by using these values as follows:

start_draw_point = pvScan0 + (y*lDelta) + (x*PixelSize(iBitmapFormat))

where x and y are coordinates at which drawing is to begin and start_draw_point is the address at which the address of the first pixel to be drawn. GDI performs this calculation on every drawing call and always references the SURFOBJ for pvScan0, which is the logical address for the start of the standard frame buffer.

To further illustrate, if it is the case that GDI needs to “draw” the entire contents of a 8 bits-per-pixel 64K frame buffer, beginning at a logical address of pvScan0 = 0x100000, it would end the draw operation at 0x10FFFF (0x100000 + (63*1024)+(1023)), where y is 63, lDelta is 1024, and x is 1023 (the position of the last pixel in the last scan line).

When the display driver next calls GDI to draw that part of the object that falls within the next bank of the banked frame buffer, the value of y, as far as GDI is concerned, is 64. With a value of 0x100000 for pvScan0 and and 64 for y, GDI would begin to write data at 0x110000 if allowed to. However, 0x110000 is beyond the 0x10FFFF extent of the 64K frame buffer and must not be written to by GDI during this operation.

Consequently, when the display driver wants GDI to write the data that is to appear in the second and subsequent banks of the frame buffer, the driver must decrement the value of pvScan0 so that the starting point calculated by GDI is still referenced to the example address of 0x100000. Continuing in the example, this means decrementing the value of pvScan0 to a value of 0x090000 when drawing to the second bank of the frame buffer. The net result of this pvScan0 alteration is that GDI still draws with reference to address 0x100000. That is, 0x090000 + (64*1024) + 0 is equal to 0x100000, which is where GDI is required to draw in order for the data to be mapped into the second bank of the frame buffer. The adjustment of pvScan0 can be found in vga64k\bankmgr.c.


Tracking Window Changes

Changes to a window can be tracked by a device driver through a WNDOBJ. A WNDOBJ is a driver level window object that contains information about the position, size, and the visible client region of a window. That is, by creating a WNDOBJ corresponding to an application's window, the driver can track the size, position, and client region changes in that window.

An application uses the Win32 API to access the WNDOBJ_SETUP functionality implemented by the device driver. Access is gained through the Win32 ExtEscape function. GDI passes this escape call to the device driver with DrvEscape, implemented by the device driver with WNDOBJ_SETUP for the value of iEsc.

An application calls ExtEscape(hdc, WNDOBJ_SETUP,...) and passes a handle to the application-created window (created by means of CreateWindow or some equivalent Win32 function) via the input buffer to the driver. If the driver is to keep track of the window, it calls EngCreateWnd, within the context of the ExtEscape call, to create a WNDOBJ structure for the given window. From that point on, any changes to that window will pass down to the driver.

The driver should handle the ExtEscape call in a manner similar to the following.

ULONG DrvEscape(
SURFOBJ *pso,
ULONG    iEsc,
ULONG    cjIn,
PVOID    pvIn,
ULONG    cjOut,
PVOID    pvOut)
{
    WNDOBJ *pwo;
    WNDDATA *pwd;

    if (iEsc == WNDOBJ_SETUP)
    {
        pwo = EngCreateWnd(pso,*((HWND *)pvIn),&DrvVideo,
                           WO_RGN_CLIENT, 0);

    // Allocate space for caching client rects. Remember the pointer
    // in the pvConsumer field.

        pwd = EngAllocMem(0, sizeof(WNDDATA), DRIVER_TAG);
        WNDOBJ_vSetConsumer(pwo,pwd);

    // Update the rectangle list for this wndobj.

        vUpdateRects(pwo);
        return(1);
    }

}

Since creating a window object involves locking special window resources, EngCreateWnd should be called only in the context of the WNDOBJ_SETUP escape in DrvEscape or DrvSetPixelFormat.

The EngCreateWnd function supports window tracking by multiple drivers. Through EngCreateWnd, each driver identifies its own callback routine that GDI is to call for changes to the corresponding window. This feature allows, for example, a live video driver to track changes to live video windows while an OpenGL driver is tracking changes to OpenGL windows.

GDI will call back to the driver with the most recent window states if a new WNDOBJ is created in DrvSetPixelFormat or ExtEscape. GDI will also callback to the driver when a window referenced by a WNDOBJ is destroyed.

As an accelerator, the driver may access public fields of the WNDOBJ, defined as follows.

    typedef struct {
        CLIPOBJ    coClient;
        PVOID        pvConsumer;
        RECTL       rclClient;
    } WNDOBJ;

where coClient is a CLIPOBJ which describes the region of the window, pvConsumer is a driver-defined value for identifying this particular WNDOBJ, and rclClient is a lower-right exclusive rectangle of the client area of the window in screen coordinates.

Note These public fields are guaranteed to remain unchanged only in the context of the driver callback routine supplied to GDI in the EngCreateWnd function call or the DDI functions where a WNDOBJ is given.

Tracking window changes involves the use of three callback functions provided to support the WNDOBJ. The visible client region may be enumerated by calling the WNDOBJ_cEnumStart and WNDOBJ_bEnum callback functions. A driver may associate its own data with a WNDOBJ by calling the WNDOBJ_vSetConsumer callback function.


Unloading Video Hardware

When a surface is no longer needed, a GDI call to DrvDisableSurface informs the display driver that the surface created for the current hardware device by DrvEnableSurface is to be disabled. If the surface is tying up any resources, the driver must free them.

After the surface is disabled, GDI calls DrvDisablePDEV to inform the driver that the hardware device is no longer needed. The driver then frees any memory and resources that were allocated during the processing of DrvEnablePDEV.

Figure 5.5 shows GDI’s calling sequence for disabling the video hardware.

Figure 5.5 Disabling the Video Hardware


Supporting Coprocessor Synchronization

To support video controllers with coprocessors, a display driver can implement the function DrvSynchronize. A device with coprocessors can start a long drawing operation and return to GDI while the operation continues. If the display driver does not do all drawing to the surface, it is possible that a subsequent drawing operation will be handled by GDI. In this case, it is necessary for GDI to wait for the coprocessor to complete its work before drawing on the surface.

DrvSynchronize is called for a surface only if the driver hooks it in a call to EngAssociateSurface. GDI calls it just before drawing directly to the device surface. DrvSynchronize returns when it is safe for GDI to begin drawing.


Supporting the DitherOnRealize Flag

In earlier verisions of GDI/DDI, two calls by GDI to display driver functions were required to dither a specified color and then realize a brush for that color. For example, when an application requests that a rectangle be filled with a dithered color, GDI typically calls DrvBitBlt, passing the extents of the rectangle and the brush object to use. The display driver then checks the brush, finds that it has not been realized, and calls back to GDI with BRUSHOBJ_pvGetRbrush for GDI’s realization of the brush. Since GDI doesn’t perform the dithering of a brush, but the display driver does, GDI passes the RGB that the application originally supplied for dithering in a DrvDitherColor call back to the display driver.

DrvDitherColor returns a pointer to an array of color indexes that describe the dither for the supplied color back to GDI. GDI immediately passes this dither information back to the display driver in a call to DrvRealizeBrush. With the BRUSHOBJ realized, control returns back to GDI and subsequently back to the original DrvBitBlt function.

To accomplish dithering using the above technique, GDI has to call DrvDitherColor followed immediately by a call DrvRealizeBrush; two separate function calls. Setting a GCAPS_DITHERONREALIZE flag in the DEVINFO structure and a modification to DrvRealizeBrush to effectively combine these two functions eliminates the need for the separate call to DrvDitherColor and also saves some memory allocation. Under this scheme, if the display driver sets GCAPS_DITHERONREALIZE, GDI calls DrvRealizeBrush with the RGB to be dithered and with the RB_DITHERCOLOR flag set in iHatch. (The RB_DITHERCOLOR flag is set in the high byte of iHatch while the RGB color to be dithered is contained in the three low-order bytes.) The need to call DrvDitherColor is eliminated in this situation since the functionality of both calls are put into one.

For example code, refer to \s3\brush.c.


Chapter 6 Supporting the DirectDraw Hardware Interface

Windows NT provides support for a subset of the DirectDraw hardware interface. This topic identifies what portion of the DirectDraw hardware interface is supported.

For function and structure descriptions, see the reference pages in the Graphics Driver Reference.


About the DirectDraw Hardware Interface

The DirectDraw hardware interface provides hardware-specific DirectDraw support. It can be an integral part of the display driver or a separate DLL that communicates with the display driver through a private interface defined by the driver’s creator. On Windows NT, a DirectDraw hardware interface must be a 32-bit implementation.

The DirectDraw hardware interface is implemented by the chip manufacturer, board producer, or OEM. The hardware interface should not perform any software validation. If a function is not performed by the hardware, the hardware interface should not report it as a hardware capability. The hardware interface should also never do any parameter validation, as DirectDraw does this before invoking the hardware interface.


DirectDraw Hardware Interface Support on Windows NT

The following table lists the DirectDraw hardware interface functions that are supported on Windows NT:
Function Name | Description
Blt | Performs a bit-block transfer.
CanCreateSurface | Queries whether the driver can support a surface that is different from the primary surface.
CreateSurface | Creates a DirectDraw surface object.
DestroySurface | Destroys a DirectDraw surface object.
DrvDisableDirectDraw | DDI routine that disables the hardware for DirectDraw use.
DrvEnableDirectDraw | DDI routine that enables the hardware for DirectDraw use.
DrvGetDirectDrawInfo | DDI routine that gets the capabilities of the graphics hardware.
Flip | Allows driver to perform multi-buffering.
GetBltStatus | Queries the driver for its blt status.
GetFlipStatus | Queries the driver for its flip status.
Lock | Provides access to block of memory associated with a surface.
MapMemory | Maps or unmaps application-modifiable portionsn of the frame buffer into or from user-mode address space.
SetColorKey | Sets the color key value.
SetOverlayPosition | Sets the position for an overlay.
Unlock | Releases the lock held on a block of memory.
UpdateOverlay | Repositions or modifies the visual attributes of an overlay surface.
WaitForVerticalBlank | Returns the vertical blank status of the device.


Chapter 7 Video Miniport Drivers

Windows NT® video miniport drivers are adapter-specific, kernel-mode drivers. Each miniport driver links itself with the system-supplied video port driver, which is a kernel-mode dynamic-link library. A video miniport driver calls only the port driver’s VideoPort Xxx functions to communicate with the system and with its hardware.

NT video miniports include the following system-supplied header files:

  • miniport.h with the basic types, constants, and structures for both SCSI and video miniport drivers

  • video.h with the video-specific structures, such as the VIDEO_REQUEST_PACKET, miniport function prototypes, and VideoPort Xxx declarations

  • ntddvdeo.h with the system-defined I/O control codes and corresponding structures that are sent in VRPs (video request packets) to video miniport drivers

  • devioctl.h with the macros and constants used to define I/O control codes

  • dderror.h with the Win32® status constants that miniport drivers return to the video port driver, which are also returned to the miniport’s corresponding kernel-mode display driver.

In this document, all miniport HwXxx routines are referred to as HwVidXxx routines for the purpose of clarity. In the header files and programmatically, these routines will not have the Vid prefix. The routines, whose addresses will be stored in a dispatch table, can have any name. For the purposes of this document, these routines will have names beginning with HwVidXxx.

For more detailed information about the system-defined I/O control codes, constants, structures, and function prototypes in these header files, see the Graphics Driver Reference.


Support for Kernel Mode Graphics Drivers

Each video miniport driver provides hardware-level support for a kernel-mode display driver written to the GDI/DDI interface. Such a display driver calls the graphics engine EngDeviceIoControl function to request support from the underlying video miniport driver. EngDeviceIoControl, in turn, calls an I/O system service to send the request through the video port driver to the miniport driver. See Figure 7.1 for an architectural overview of the graphics system.

Figure 7.1 Windows NT Graphics Architecture

In most circumstances, the display driver carries out time-critical operations that are visible to the user, while the underlying miniport driver provides support for infrequently requested operations or for truly time-critical operations that cannot be preempted by an interrupt or a context switch to another process.

A display driver cannot handle device interrupts, and only the underlying kernel-mode miniport driver can set up device memory and map it into a display driver’s virtual address space.

For more information about Windows NT display drivers, see Display Driver Environment and DDI Support in Display Drivers.


Video Requests

The system-supplied video port driver sends all requests that originate in the display driver’s call to EngDeviceIoControl to the corresponding miniport driver as VRPs. The video port driver maps incoming IRPs to VRPs and calls the miniport driver with a pointer to each VIDEO_REQUEST_PACKET structure that it sets up.

The video port driver also manages the synchronization of incoming requests for all video miniport drivers by sending each miniport only one VRP for processing at a time. Until a miniport driver completes the current VRP, the video port driver holds on to any outstanding IRPs that the I/O Manager sent in response to the corresponding display driver’s call to EngDeviceIoControl.


Platform-Specific Requirements

In x86-based Windows NT platforms, there are two kinds of video miniport drivers: VGA-compatible miniports and miniports that rely on having the system-supplied VGA miniport driver or another VGA-compatible SVGA miniport driver loaded concurrently.

Miniport drivers on RISC-based Windows NT platforms rely on the system-supplied VGA support, if it is necessary.


VGA-Compatible, x86-Based Miniports

Self-declared “VGA-compatible” miniport drivers are set up to configure themselves in the registry with VgaCompatible set to one (TRUE). VGA-compatible miniport drivers have the following features:

  • Such a miniport driver, usually of an SVGA adapter, is based on the system-supplied VGA miniport driver, with code modified to support adapter-specific features. The system-supplied VGA display drivers use the kernel-mode support provided by VGA-compatible miniport drivers, so the developer of a new miniport for a VGA-compatible adapter need not write a new display driver.

  • Such a miniport driver both provides support for full-screen MS-DOS® applications to do I/O directly to the adapter registers and also functions as a video validator to prevent such an application from issuing any sequence of instructions that would hang the machine.

Miniport drivers of most SVGA adapters fall in this category, but any new miniport driver for a nonSVGA adapter can provide VGA compatibility support at the discretion of the driver designer.


Other x86-Based Miniports

Miniport drivers that are set up to configure themselves in the registry with VgaCompatible set to zero (FALSE) have the following features:

  • Such a miniport driver provides no special support for full-screen MS-DOS applications in x86-based machines. Instead, it is loaded along with a system-supplied VGA (or, possibly, with a VGA-compatible SVGA) miniport driver, which provides this support for full-screen MS-DOS applications.

  • In most cases, such a miniport driver either is written for an adapter that has no VGA compatibility mode or for an accelerator that works independently of the VGA.

If a new miniport driver cannot provide support for an existing display driver a corresponding display driver must be written together with the new miniport driver.


RISC-Based Miniports

In all RISC-based Windows NT platforms, video miniport drivers need not supply any special support for full-screen MS-DOS applications.


Required and Optional Miniport Routines

Every video miniport driver must have at least the following system-defined functions:

  • DriverEntry to initialize the miniport driver

  • HwVidFindAdapter to detect whether driver-supported video adapter(s) are in the machine, to claim hardware resources for supported video adapter(s) in the registry, and to set up communication with those adapter(s)

  • HwVidInitialize, which is called in response to a request to open the adapter, to perform one-time initialization of the adapter for the corresponding display driver

  • HwVidStartIO to begin processing for each incoming VRP

Depending on a given adapter’s features and on the driver designer, video miniport drivers also can or must have the following system-defined functions:

  • HwVidQueryDeviceCallback and/or HwVidQueryNamedValueCallback to process configuration information stored in the registry, which are optional if the miniport’s HwVidFindAdapter function uses I/O bus-type-specific configuration information or driver-supplied defaults

  • HwVidInterrupt to handle adapter-generated interrupts, which is required if the miniport’s adapter generates interrupts

  • HwVidSynchronizeExecutionCallback for critical sections of code to be run at a higher than normal software or hardware priority (IRQL, with some device and system interrupts masked off), which is required if other miniport functions share state with a HwVidInterrupt function

  • HwVidResetHw to reset the adapter to character mode so the HAL can display system shutdown information in case of a controlled shutdown (crash) of the machine and initialization information on a soft reboot of the machine, which is required unless the adapter’s registers can be automatically reset by an Int10

  • HwVidTimer to be called periodically at approximately one-second intervals by the port driver, which is optional

  • SvgaHwIoPortXxx functions in VGA-compatible SVGA miniport drivers to validate accesses to I/O ports by full-screen MS-DOS applications in x86-based machines. See VGA-Compatible Miniport’s SvgaHwIoPortXxx for more information.

Except for DriverEntry, this documentation uses pseudonames for standard miniport functions that suggest the required functionality of each. In other words, the name given to each standard miniport function, except DriverEntry, can be anything the miniport driver writer chooses.

The following sections describe the requirements for and functionality of each of these standard miniport driver functions.


Video Miniport’s DriverEntry

A DriverEntry function is the initial entry point for each video miniport driver. DriverEntry is the required name for this function.

A miniport’s DriverEntry function is called by the I/O manager with two input pointers and must do the following:

  1. Call VideoPortZeroMemory to initialize a VIDEO_HW_INITIALIZATION_DATA structure on the stack.

  2. Set driver-specific and adapter-specific values in the VIDEO_HW_INITIALIZATION_DATA fields, including the miniport driver’s entry points. The following entry points must be set to a driver supplied routine:

    • HwVidFindAdapter

    • HwVidInitialize

    • HwVidStartIO

    The HwVidInterrupt, HwVidResetHw, and HwVidTimer entry points also must be set to a driver-supplied function or to NULL.

  3. Initialize the StartingDeviceNumber to zero. If a miniport driver supports more than one adapter, the video port driver uses a different key in the registry for each adapter and gives each adapter-specific key a name derived from the zero-based StartingDeviceNumber.

  4. Set up any driver-determined context data that the miniport’s DriverEntry function passes to the miniport’s HwVidFindAdapter function.

  5. Call VideoPortInitialize with the pointers that were input to the DriverEntry function and with pointers to the filled-in VIDEO_HW_INITIALIZATION_DATA and miniport-supplied context data, if any.


Calling VideoPortInitialize

A miniport driver might have a set of bus-type-specific HwVidFindAdapter functions if its adapter can be connected on various types of I/O bus, or it can call VideoPortInitialize with the same HwVidFindAdapter function for each type of bus and possibly use driver-supplied HwVid..Callback functions to get adapter configuration information from the registry.

A miniport driver must modify the AdapterInterfaceType member in VIDEO_HW_INITIALIZATION_DATA after each call to VideoPortInitialize, possibly modify the context data (and HwFindAdapter member, if the driver writer implements separate HwVidFindAdapter functions) for the new bus type, and call VideoPortInitialize once for each type of I/O bus on which a supported adapter could be connected.

VideoPortInitialize does the following:

  • Checks the validity of the miniport’s VIDEO_HW_INITIALIZATION_DATA specifications

  • Collects and stores pertinent information in the device extension of a device object that it creates to represent the adapter

  • Allocates memory for and zeros a VIDEO_PORT_CONFIG_INFO-type buffer

  • Fills in as much configuration information as it can in this buffer

  • Allocates memory for a device extension to contain the miniport’s per-adapter state information

  • Calls the miniport driver’s HwVidFindAdapter function, described later in HwVidFindAdapter Functionality.

If the miniport driver sets an AdapterInterfaceType value in VIDEO_HW_INITIALIZATION_DATA but there is no I/O bus of that type in the machine, the port driver returns STATUS_NO_SUCH_DEVICE. VideoPortInitialize does not call the miniport-supplied HwVidFindAdapter function for that bus type. The miniport driver does not remain loaded if there are no I/O buses of the corresponding AdapterInterfaceType(s) for its adapter(s) in the machine.

Note that VideoPortInitialize also is responsible for the following before it calls the miniport’s HwFindAdapter routine:

  • Setting up all necessary NT objects for the miniport driver

  • Getting requested configuration information from and setting configuration information in the registry

  • Allocating system resources on behalf of the miniport driver, including memory of the miniport-specified HwDeviceExtensionSize for the miniport’s device extension

  • Initializing the miniport’s device extension with zeros before it calls HwVidFindAdapter with a pointer to the device extension it allocated.

VideoPortInitialize can be called only from a miniport driver’s DriverEntry function.


Video Miniport’s Device Extension

A device extension is each miniport’s primary and only global storage area for adapter-specific state information.

Each miniport driver defines the size, internal structure, and contents of its device extension. A pointer to the device extension is an input parameter to every system-defined miniport driver function except DriverEntry and the HwVidSynchronizeExecutionCallback and SvgaHwIoPortXxx functions, if any. Many VideoPort Xxx functions require this pointer as an argument.

The device extension should also be used to maintain the state information for a single adapter. Each adapter would have separate state information maintained in a separate device extension.

For example, VideoPortInitialize calls the miniport’s HwVidFindAdapter function with a pointer to the memory it allocated for the device extension. HwVidFindAdapter is responsible for determining whether an adapter it can support is present in the machine and for setting up whatever context the miniport maintains about that adapter in its device extension.


Return from Video Miniport’s DriverEntry

When VideoPortInitialize returns control, the DriverEntry function propagates the return value of VideoPortInitialize. If a miniport driver calls VideoPortInitialize more than once, its DriverEntry function should propagate the lowest return value.

Miniport driver writers can make no assumptions about the values returned by VideoPortInitialize.


HwVidFindAdapter Functionality

Every video miniport driver must have at least one HwVidFindAdapter routine. It is possible to have a different HwVidFindAdapter routine for each different I/O bus type. If a driver implements bus specific HwVidFindAdapter routines, the HwFindAdapter member of VIDEO_HW_INITIALIZATION_DATA, in addition to the AdapterInterfaceType of VIDEO_PORT_CONFIG_INFO, must be changed before each call to VideoPortInitialize.

The video port driver fills in as much VIDEO_PORT_CONFIG_INFO as it can from the miniport’s VIDEO_HW_INITIALIZATION_DATA before calling its HwVidFindAdapter routine with a pointer to the configuration information buffer. In particular, VideoPortInitialize always sets the following:

  • Length to sizeof(VIDEO_PORT_CONFIG_INFO)

  • AdapterInterfaceType to the miniport’s VIDEO_HW_INITIALIZATION_DATA specification

  • SystemIoBusNumber to the system-assigned number of the I/O bus on which the adapter might be attached

    This value specifies a particular I/O bus in multibus machines with more than one bus of the given AdapterInterfaceType.

  • StartingDeviceNumber to the miniport’s VIDEO_HW_INITIALIZATION_DATA specification.

In general, the HwVidFindAdapter function is responsible for the following:

  • Determining whether one or more adapters on the given I/O bus are supported by the miniport driver

  • Claiming bus-relative access ranges of device memory and other hardware resources (such as interrupt vector, if any) for supported adapter(s) on the I/O bus in the registry

  • Mapping bus-relative access ranges to system-space logical addresses to communicate with the adapter(s)

  • Filling in all relevant configuration information for each supported adapter in the VIDEO_PORT_CONFIG_INFO buffer

  • Setting the registry entries to indicate the chip type, DAC type, memory size, and adapter string for the adapter. See Setting Hardware Information in the Registry for more information.

First, a HwVidFindAdapter function should check whether the Length of the input ConfigInfo buffer has enough room for data that the miniport driver needs to find, configure, and initialize its video adapter. In effect, this is a check on the version of the VIDEO_PORT_CONFIG_INFO structure being used by the port driver.


Setting Hardware Information in the Registry

It is recommended that miniport drivers set certain hardware information in the registry to display useful information to the user and for assistance in debugging. A miniport driver can set a chip type, DAC type, memory size (of the adapter), and a string to identify the adapter. This information is displayed by the control panel display applet.

The driver sets this information by calling VideoPortSetRegistryParameters. The following table outlines for the two variable parameters to VideoPortSetRegistryParameters and what data should be passed.
Information for Entry | ValueName | ValueData
Chip type | HardwareInformation.ChipType | Null terminated string containing the chip name.
DAC type | HardwareInformation.DacType | Null terminated string containing the DAC name or ID.
Memory size | HardwareInformation.MemorySize | ULONG containing, in MB, the amount of video memory on the adapter.
ID String | HardwareInformation.AdapterString | Null-terminated string containing the name of the adapter.


Calling VideoPortXxx from HwVidFindAdapter

Certain VideoPort Xxx functions should be called only from the miniport driver’s HwVidFindAdapter function(s). A HwVidFindAdapter function would never call all of the following, but every HwVidFindAdapter function must call some of the following VideoPort Xxx:

  • VideoPortGetBusData to get bus-type-specific configuration information about an adapter on a particular I/O bus

  • VideoPortSetBusData to set bus-specific configuration data for an adapter on a dynamically configurable I/O bus with a published standard interface, such as a PCI bus, if necessary

  • VideoPortGetDeviceData to get VIDEO_DEVICE_DATA_TYPE-specific information from the registry and call a miniport-supplied HwVidQueryDeviceCallback function to process the information, as described in HwVidQueryDeviceCallback Routine.

  • VideoPortGetAccessRanges to get bus-relative access ranges addresses, and possibly other hardware configuration values, and to claim them in the registry for use by the driver of an adapter, usually one on a configurable I/O bus

  • VideoPortVerifyAccessRanges to determine whether bus-relative video memory and/or I/O ports for the adapter can be claimed by the miniport driver; otherwise, a previously loaded driver has already claimed the resource in the registry and the HwVidFindAdapter function must try to claim other access ranges if possible or return control and propagate the status value returned by VideoPortVerifyAccessRanges

  • VideoPortGetDeviceBase to map each successfully claimed bus-relative base address and range size, as described in each VIDEO_ACCESS_RANGE-type element for the adapter, to a system-space logical address range

    Each such (bus-relative) physical address range must be mapped with VideoPortGetDeviceBase before it is used to access an adapter. With mapped logical range addresses, the driver can call the VideoPortRead Xxx and VideoPortWrite Xxx functions to read from or write to an adapter. For a mapped range in I/O space, the miniport driver calls the VideoPortReadPort Xxx and VideoPortWritePort Xxx functions. For a mapped range in memory, the miniport driver calls the VideoPortReadRegister Xxx and VideoPortWriteRegister Xxx functions.

  • VideoPortFreeDeviceBase to release such a mapped range if the HwVidFindAdapter or HwVidQueryDeviceCallback function does not find an adapter it can support on a given I/O bus, as indicated by the VIDEO_PORT_CONFIG_INFO SystemIoBusNumber value

    If this occurs, the miniport also must call VideoPortVerifyAccessRanges to release its claims in the registry on the bus-relative access ranges for that adapter.

  • VideoPortScanRom to determine whether a specific signature matches that of the machine ROM; in effect, this determines whether the miniport finds a specific adapter with a video chip it can support.

A HwVidFindAdapter routine also can call the VideoPortGetRegistryParameters and VideoPortSetRegistryParameters functions to get and set configuration information in the registry. For example, HwVidFindAdapter might call VideoPortSetRegistryParameters to set up nonvolatile configuration information in the registry for the next boot. It might call VideoPortGetRegistryParameters to get adapter-specific, bus-relative configuration parameters written into the registry by an installation program.

The HwVidInitialize function, described later in HwVidInitialize Functionality, also can call VideoPortGetRegistryParameters or VideoPortSetRegistryParameters.


Setting up Video Adapter Access Ranges

An array of VIDEO_ACCESS_RANGE-type elements describes one or more ranges of I/O ports and/or memory used by a video adapter. Each element specifies a set of registers or I/O ports the adapter will respond to and that the HwVidFindAdapter routine will attempt to communicate with. Each access range element in this access ranges array contains bus-relative “physical” address values. The driver must claim, and have an entry for, any port or range of ports that the adapter will respond to.

A miniport driver also must map each bus-relative physical address range, that the software must communicate with, in its access ranges array to a system logical address range with VideoPortGetDeviceBase in order to communicate with an adapter. The HAL can remap bus-relative access range values to system-space logical address ranges, particularly in multiple bus machines.

Every video miniport driver must define the bus-relative access ranges for its device, either as statically allocated memory in the driver’s code or on the stack. Except for miniport drivers, such as VGA-compatible SVGA miniports, that use PC standard address ranges for video memory, most miniport drivers set up their video access ranges on the stack.

If HwVidFindAdapter cannot get bus-relative access ranges information by calling VideoPortGetBusData, VideoPortGetAccessRanges, or from the registry with VideoPortGetDeviceData or VideoPortGetRegistryParameters, the miniport should have a set of bus-relative default values for access ranges.

If a miniport’s access ranges are externally configurable, the installation program sets up access ranges for the adapter in the registry. Such a miniport’s HwVidFindAdapter routine can call VideoPortGetRegistryParameters with a miniport-supplied HwVidQueryValueCallback routine that will process information retrieved from the registry. For more information about registry functions, see Registry HwVidXxx Callbacks.

If possible, video miniport drivers should try to auto-detect their adapters, rather than depending on configuration utilities. Doing this is required for the autodetection of adapter features during installation using a setup script.

The HwVidFindAdapter function must always call VideoPortVerifyAccessRanges or VideoPortGetAccessRanges successfully before it calls VideoPortGetDeviceBase.

  • Any successful call to VideoPortVerifyAccessRanges or VideoPortGetAccessRanges establishes a miniport driver’s claim on bus-specific video memory and register addresses or I/O ports for its adapter in the registry. It is critical to note, however, any subsequent call to VideoPortVerifyAccessRanges or VideoPortGetAccessRanges will cause the previous claimed resources by that driver to be erased an replaced with the ranges specified to those functions. If a driver claims ranges by separate calls to these functions, then the range arrays must be cumulative.

  • To claim other types of hardware resources, such as an interrupt vector, a miniport driver should set appropriate values in the VIDEO_PORT_CONFIG_INFO and call VideoPortVerifyAccessRanges, or it should call VideoPortGetAccessRanges.

  • Calling VideoPortGetAccessRanges or VideoPortVerifyAccessRanges successfully ensures that a miniport driver does not try to use register or device memory addresses already in use by another driver.

  • Claiming an adapter’s bus-relative hardware resources in the registry prevents drivers that load later from attempting to use the same access ranges (and other hardware resources) for their adapters. It also prevents a subsequently loaded driver from changing the initialized state of the video adapter.

Note that HwVidFindAdapter must call either VideoPortVerifyAccessRanges or VideoPortGetAccessRanges before the miniport attempts to initialize an adapter on the I/O bus specified by the SystemIoBusNumber in the input VIDEO_PORT_CONFIG_INFO. If VideoPortVerifyAccessRanges or VideoPortGetAccessRanges returns an error status, which indicates a resource conflict, the HwVidFindAdapter function can call again with another set of access ranges if the adapter can be configured at different addresses. Otherwise, it should propagate the returned status and return control immediately.

Note also that the HwVidFindAdapter function can claim a small set of access ranges for an adapter, use this set to determine whether the adapter is one that the miniport supports, and claim a full set of access ranges for a supported adapter with another call to VideoPortGetAccessRanges or VideoPortVerifyAccessRanges. Each successful call to these VideoPort..AccessRanges routines for a particular adapter overwrites the caller’s previous claims in the registry.

Every HwVidFindAdapter function must map claimed bus-relative device memory ranges with VideoPortGetDeviceBase before the miniport attempts to communicate with an adapter on a given I/O bus. VideoPortGetDeviceBase returns a system-space, logical base address for a mapped range that the driver can pass in calls to the VideoPortRead Xxx and VideoPortWrite Xxx to determine whether an adapter it supports is present in the machine. Such a returned logical address also can be passed to VideoPortCompareMemory, VideoPortMoveMemory, VideoPortZeroDeviceMemory, and/or VideoPortZeroMemory.


Setting up NumEmulatorAccessEntries

In x86-based Windows NT machines, nonVGA-compatible miniports always set the NumEmulatorAccessEntries to zero in the VIDEO_PORT_CONFIG_INFO buffer, because a system-supplied VGA or VGA-compatible SVGA miniport driver provides this support for full-screen MS-DOS applications. In other Windows NT platforms, video miniport drivers need not support emulator access entries and should set this member to zero.

A VGA-compatible SVGA miniport driver must set up emulator access entries in the VIDEO_PORT_CONFIG_INFO buffer. For all RISC-based Windows NT platforms, this member of the VIDEO_PORT_CONFIG_INFO structure is ignored.


Determining Supported Video Adapters

As already mentioned in Setting up Video Adapter Access Ranges, the HwVidFindAdapter or HwVidQueryDeviceCallback function calls a VideoPortRead Xxx or, possibly, VideoPortWrite Xxx function to identify its adapter.

However, the HwVidFindAdapter (or HwVidQueryDeviceCallback) routine should not change the state of any video adapter unnecessarily and must not leave any adapter in a changed state.

While the HwVidFindAdapter function runs, the HAL has control of the video adapter so it can write information to the screen in the early stages of the system boot process. If a HwVidFindAdapter routine’s attempt to identify its adapter might have affected an adapter’s state, this routine should restore the original state immediately so that on return from HwVidFindAdapter the HAL can continue to display boot-up messages.

As its name suggests, a HwVidFindAdapter function is responsible for detecting any adapters that the miniport driver supports. This function should not change the state of any video adapter permanently.

The HwVidInitialize routine should actually initialize the adapter because the HAL does not relinquish control of the video adapter to a video miniport driver until its HwVidInitialize function is called. For example, HwVidFindAdapter should defer determining the DAC type of an adapter to the HwVidInitialze function, because making this determination does not affect whether the miniport will be loaded but does change the state of the adapter permanently.


Configuring a Supported Video Adapter

In addition to setting up bus-relative access ranges, claiming these hardware resources (and any others necessary) for its adapter, and mapping access ranges to logical addresses, HwFindAdapter must fill in any missing configuration information in the VIDEO_PORT_CONFIG_INFO structure for an adapter it supports.

After filling in the VIDEO_PORT_CONFIG_INFO structure for a supported adapter, HwVidFindAdapter returns control with the status code NO_ERROR.

VideoPortInitialize sets up necessary system objects, such as a device object for the adapter and an interrupt object if the adapter generates interrupts, on behalf of the miniport driver.

The miniport’s HwVidInitialize function, is called in response to the opening of a video miniport driver by the kernel-mode graphics engine.

Note that the mapped logical addresses passed to the VideoPortRead Xxx and VideoPortWrite Xxx functions are kernel-mode addresses. After a miniport driver is loaded and its HwVidInitialize function is run, the miniport’s HwVidStartIO function, is called to map any access range of video memory that the miniport driver makes visible to its corresponding display driver. For more information on HwVidStartIO, see HwVidStartIO Functionality .


Return from HwVidFindAdapter

If the miniport’s HwVidFindAdapter function cannot find an adapter it supports, it should set Again to FALSE and return ERROR_DEV_NOT_EXIST. If the HwVidFindAdapter function finds a supported adapter but the driver cannot find valid configuration data for its adapter on a particular I/O bus, it should set Again to FALSE and return ERROR_INVALID_PARAMETER.

Note that setting Again to FALSE and returning control with ERROR_DEV_NOT_EXIST or ERROR_INVALID_PARAMETER indicates that a given I/O bus, identified by the SystemIoBusNumber in the input VIDEO_PORT_CONFIG_INFO buffer, has no video adapters that the miniport driver can support. It does not prevent the video port driver from calling HwVidFindAdapter again with an updated SystemIoBusNumber so that HwVidFindAdapter can look for its video adapter on another I/O bus of the same AdapterInterfaceType.

When the HwVidFindAdapter function finds an adapter it can support, this function must fill in pertinent fields, as appropriate for its adapter and the given AdapterInterfaceType, in the input VIDEO_PORT_CONFIG_INFO buffer, and return NO_ERROR. The HwVidFindAdapter function can set Again to TRUE to be called again with some of the same input VIDEO_PORT_CONFIG_INFO values and a new device extension if there might be another of its adapters on the same I/O bus. Usually, such a miniport driver’s adapter has relocatable I/O ports or device memory.

If a miniport driver’s HwVidFindAdapter function finds that the video adapter does not generate interrupts or that it cannot determine a valid interrupt vector/level for the adapter, HwVidFindAdapter should set both InterruptLevel and InterruptVector in the VIDEO_PORT_CONFIG_INFO structure to zero.

When HwVidFindAdapter returns control, the video port driver checks the interrupt configuration members in VIDEO_PORT_CONFIG_INFO and, if both are zero, does not connect an interrupt for the miniport. Explicitly setting both interrupt configuration members to zero in the HwVidFindAdapter function disables the HwVidInterrupt entry point, if any, that was set up by the miniport’s DriverEntry function.

When a miniport driver’s HwVidFindAdapter routine returns control, VideoPortInitialize returns to the DriverEntry routine if the call(s) to HwVidFindAdapter indicated that the miniport driver could not support an adapter. If a miniport’s HwVidFindAdapter function does not return NO_ERROR for at least one adapter, the driver does not remain loaded after its DriverEntry function returns control. If a miniport’s HwVidFindAdapter function returns anything other than NO_ERROR for a particular adapter, and no other miniport driver returns NO_ERROR from its HwVidFindAdapter routine, then that adapter will be unavailable to any display drivers.


Registry HwVidXxx Callbacks

As mentioned in Calling VideoPortXxx from HwVidFindAdapter, a miniport driver’s HwVidFindAdapter function can request that configuration information be retrieved from the registry by calling either (or both) VideoPortGetDeviceData or VideoPortGetRegistryParameters with a driver-supplied HwVid..Callback function.

Each of the preceding VideoPortGet Xxx functions retrieves information from a different key in the registry. Consequently, each requires the miniport driver to supply a system-defined HwVid..Callback function with different formal parameters. However, neither of the HwVid..Callbacks can retrieve configuration information about display driver defaults from the registry.

For more detailed information about parameters for the HwVidQueryDeviceCallback and HwVidQueryNamedValueCallback functions, see the Graphics Driver Reference.

A miniport driver also can call VideoPortSetRegistryParameters to store information, such as information about the DAC type, chip information, memory on adapter, and adapter identification information, in the registry under the key ..\CurrentControlSet\Services\DriverName\Device Number.. For more information on storing hardware information in the registry, see Setting Hardware Information in the Registry. To retrieve the information from the registry later, the miniport calls VideoPortGetRegistryParameters.

Note that the registry information made available by calls to VideoPortGetDeviceData is volatile: it is re-created every time the system is loaded. Note also that the port driver makes this information and the configuration information returned by VideoPortGetRegistryParameters available only to video miniport drivers. A miniport driver cannot call VideoPortSetRegistryParameters to pass configuration information to its corresponding display driver.


HwVidQueryDeviceCallback Routine

When the system bootstrap loader runs, as much information as possible is stored in the registry about the I/O buses and peripherals that can be found in the machine in the \Registry\Machine\Hardware\Description tree. A HwVidQueryDeviceCallback routine can be used to retrieve this information.

A video miniport driver can attempt to find an adapter it supports by calling VideoPortGetDeviceData with a driver-supplied HwVidQueryDeviceCallback function, a pointer to the miniport’s device extension, and a pointer to any driver-supplied context data (which can be the VIDEO_PORT_CONFIG_INFO), requesting one of the following types of data:

  • VpBusData for bus-type-specific configuration data if the AdapterInterfaceType in the VIDEO_PORT_CONFIG_INFO was set to Eisa or MicroChannel

  • VpControllerData for bus-relative configuration information about a video adapter on the I/O bus in ARC-compliant (RISC-based) machines

  • VpMonitorData for configuration information about the video monitor in ARC-compliant machines

  • VpMachineData for information on the identifier, specific resource configuration data, and component information about the specific machine

  • VpCmosData to return a copy of the CMOS and extended CMOS memory.

If they call VideoPortGetDeviceData, video miniport drivers usually request VpBusData-type configuration information.

VideoPortGetDeviceData calls the HwVidQueryDeviceCallback function with pointers to the input device extension and context data, and with a pointer to a buffer containing the requested information and the length in bytes of the returned data if any is available in the registry.

As already mentioned in Setting up Video Adapter Access Ranges, any bus-relative addresses retrieved from the registry for VIDEO_ACCESS_RANGEs must be mapped to logical address ranges by calling VideoPortGetDeviceBase before they are stored in the miniport’s device extension and before they are passed to a VideoPortRead Xxx or VideoPortWrite Xxx function to communicate with the device.


HwVidQueryNamedValueCallback Routine

As an alternative or in addition, the miniport driver might get configuration data it needs for its adapter from the registry key ..\CurrentControlSet\Services\DriverName\Device Number by calling VideoPortGetRegistryParameters. A HwVidQueryNamedValueCallback can be used to retrieve this information.

A HwVidFindAdapter (or HwVidInitialize) function calls VideoPortGetRegistryParameters with a driver-supplied HwVidQueryNamedValueCallback function, pointers to the device extension and any driver-supplied context data (which can be the VIDEO_PORT_CONFIG_INFO buffer), and a pointer to a Unicode string naming the value to be retrieved from the registry.

A HwVidFindAdapter or HwVidInitialize function can set the IsFileNameParameter argument to TRUE if the driver-supplied Unicode string is a named registry entry whose value is a file name. For this specification, the returned data will be the contents of the named file.

When VideoPortGetRegistryParameters calls the HwVidQueryNamedValueCallback function, it processes whatever configuration data is made available. For example, a HwVidQueryNamedValueCallback function might use retrieved data to determine the miniport’s access ranges and to set up its emulator access ranges (if any), interrupt vector or interrupt IRQL (if any), and so forth in the VIDEO_PORT_CONFIG_INFO buffer, as well as in the miniport’s device extension, before returning control to the HwVidFindAdapter function.

A miniport’s HwVidInitialize function, described in HwVidInitialize Functionality, might also call VideoPortGetRegistryParameters with IsFileNameParameter set to TRUE so that the miniport-supplied HwVidQueryNamedValueCallback function could use the buffered contents of a named file to set up microcode on the adapter.


HwVidInitialize Functionality

For each adapter found and successfully configured by the miniport driver, the miniport HwVidInitialize function is called when the corresponding display driver is loaded. The HwVidInitialize routine can initialize software state information, but it should not set up visible state on the adapter. On return from HwVidInitialize, the adapter should be set to the same state as on return from the miniport’s HwVidResetHw routine. For more information on HwVidResetHw, see HwVidResetHw Functionality.

If necessary, a miniport’s HwVidInitialize function can carry out a one-time initialization operation on the adapter that was postponed by its HwVidFindAdapter function. For example, a miniport might postpone loading microcode on the adapter and have the HwVidInitialize function call VideoPortGetRegistryParameters.

When the HwVidInitialize function returns control, the graphics engine has a handle for the miniport’s adapter. The corresponding display driver then can call the engine EngDeviceIoControl function to request access to mapped video memory or to request any other operation. The video port driver sends such a request on to the miniport’s HwVidStartIO function, as a VRP. For more information on HwVidStartIO see HwVidStartIO Functionality.

Usually, a display driver controls the display the end user sees, except occasionally when a full-screen MS-DOS application is run in an x86-based Windows NT machine. For more information about supporting this feature in VGA-compatible miniport drivers, see VGA-Compatible Miniports SvgaHwIoPortXxx.


HwVidStartIO Functionality

As its name suggests, a HwVidStartIO function is the entry point for incoming video requests for the miniport driver to process on the adapter. HwVidStartIO is called with pointers to a VRP and to the miniport’s per-adapter device extension.

After the corresponding display driver has a handle for the miniport’s adapter, all of the VRPs sent to the HwVidStartIO function have the IoControlCode field set to an IOCTL_VIDEO_XXX. On receipt of such a request, the HwVidStartIO function does the following:

  1. Determines what operation is requested by switching on the IoControlCode value

  2. Checks that the VRP InputBufferLength and/or OutputBufferLength indicates a buffer that is large enough to satisfy the request and returns an error if either buffer is too small

  3. Either calls an internal function to satisfy the request or satisfies it in the HwVidStartIO function

  4. Sets the Status and Information fields in the VRP and returns TRUE when it returns control.

The video port driver sets up VRPs and serializes calls to the HwVidStartIO function, which owns each input VRP until the miniport driver completes the requested operation and returns control. Every HwVidStartIO function must set status in each VRP and return a Boolean TRUE, indicating that each input request was processed.


System-Defined IOCTL_VIDEO_XXX Requests

All video miniport drivers are required to support the following requests:

  • IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES

  • IOCTL_VIDEO_QUERY_AVAIL_MODES

  • IOCTL_VIDEO_QUERY_CURRENT_MODE

  • IOCTL_VIDEO_SET_CURRENT_MODE

  • IOCTL_VIDEO_RESET_DEVICE

  • IOCTL_VIDEO_MAP_VIDEO_MEMORY

  • IOCTL_VIDEO_UNMAP_VIDEO_MEMORY

  • IOCTL_VIDEO_SHARE_VIDEO_MEMORY

  • IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY

  • IOCTL_VIDEO_GET_PUBLIC_ACCESS_RANGES

  • IOCTL_VIDEO_FREE_PUBLIC_ACCESS_RANGES

Depending on the adapter’s features, video miniport drivers can support the following additional requests:

  • IOCTL_VIDEO_GET_POWER_MANAGEMENT

  • IOCTL_VIDEO_SET_POWER_MANAGEMENT

  • IOCTL_QUERY_COLOR_CAPABILITIES

  • IOCTL_VIDEO_SET_COLOR_REGISTERS (required if the device has a palette)

  • IOCTL_VIDEO_DISABLE_POINTER

  • IOCTL_VIDEO_ENABLE_POINTER

  • IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES

  • IOCTL_VIDEO_QUERY_POINTER_ATTR

  • IOCTL_VIDEO_SET_POINTER_ATTR

  • IOCTL_VIDEO_QUERY_POINTER_POSITION

  • IOCTL_VIDEO_SET_POINTER_POSITION

VGA-compatible SVGA miniport drivers are required to support the following additional requests:

  • IOCTL_VIDEO_SAVE_HARDWARE_STATE

  • IOCTL_VIDEO_RESTORE_HARDWARE_STATE

  • IOCTL_VIDEO_DISABLE_CURSOR

  • IOCTL_VIDEO_ENABLE_CURSOR

  • IOCTL_VIDEO_QUERY_CURSOR_ATTR

  • IOCTL_VIDEO_SET_CURSOR_ATTR

  • IOCTL_VIDEO_QUERY_CURSOR_POSITION

  • IOCTL_VIDEO_SET_CURSOR_POSITION

  • IOCTL_VIDEO_GET_BANK_SELECT_CODE

  • IOCTL_VIDEO_SET_PALETTE_REGISTERS

  • IOCTL_VIDEO_LOAD_AND_SET_FONT

The following requests are obsolete but defined for compatibility with existing VGA-compatible miniport drivers:

  • IOCTL_VIDEO_QUERY_NUM_AVAIL_FONTS

  • IOCTL_VIDEO_QUERY_AVAIL_FONTS

  • IOCTL_VIDEO_QUERY_CURRENT_FONT

  • IOCTL_VIDEO_SET_CURRENT_FONT


Display-Miniport IOCTL_VIDEO_XXX Requests

A miniport driver might define one or more private I/O control codes for its corresponding display driver.

However, only a specific display-and-miniport driver pair can use privately defined I/O control codes. That is, a miniport driver designed to run under an existing display driver should not define private I/O control codes because the existing display driver cannot make new I/O control requests without being rewritten and, possibly, without breaking existing miniport drivers it already uses. An existing or generic display driver layered over many different models of adapters, such as SVGA adapters, also cannot rely on a privately defined I/O control code to have the same effects in every underlying miniport driver.

For more information on defining private I/O control codes, see the Kernel-Mode Driver Design Guide.


Handling Unsupported IOCTL_VIDEO_XXX Requests

Every HwVidStartIO function also must handle the receipt of an unsupported IOCTL_VIDEO_XXX, as follows:

  1. Set the input VRP’s Status field to ERROR_INVALID_FUNCTION.

  2. Set the input VRP’s Information field to zero.

  3. Return TRUE to indicate the request was processed.


HwVidInterrupt Functionality

On entry, a HwVidInterrupt function should determine if its adapter actually generated an interrupt. The HwVidInterrupt function must return FALSE as soon as possible if it is called for a spurious interrupt. Then, the ISR for the device that generated the interrupt can be called quickly.

Otherwise, a miniport’s HwVidInterrupt function is generally responsible for completing the I/O operation that caused the interrupt. Depending on the adapter and the design of the miniport, a HwVidInterrupt function does the following:

  • Dismisses the interrupt on the adapter (required)

  • Possibly, completes the requested operation that caused the interrupt

  • Returns control as quickly as possible (required)

HwVidInterrupt cannot call certain VideoPort Xxx without causing a system check condition. However, a HwVidInterrupt routine can call the following functions safely:

  • VideoPortRead Xxx and VideoPortWrite Xxx

  • VideoPortZeroDeviceMemory and VideoPortZeroMemory

  • VideoPortLogError

  • VideoPortStallExecution (for no more than a very few microseconds)

  • VideoPortDisableInterrupt and VideoPortEnableInterrupt

A HwVidInterrupt routine cannot be pageable, nor can any function or routine that it calls.

Note that the HwVidInterrupt function can access the miniport’s device extension. Depending on the design of the miniport, it might be impossible for other driver functions to share the device extension or a particular area of the device extension with the HwVidInterrupt function safely in SMP machines.

For example, suppose the miniport’s HwVidStartIO function is accessing the device extension when the adapter interrupts, the HwVidInterrupt function is run on another processor, and the HwVidInterrupt function also accesses the device extension. If such a situation might occur, the miniport’s HwVidStartIO function should call VideoPortSynchronizeExecution with a driver-supplied HwVidSynchronizeExecutionCallback function.

Any mapped access ranges that a HwVidInterrupt routine uses cannot be accessed by the corresponding display driver because display driver and miniport driver functions cannot be synchronized.


HwVidSynchronizeExecutionCallback Functionality

Miniport drivers for adapters that do not generate interrupts seldom call VideoPortSynchronizeExecution with a HwVidSynchronizeExecutionCallback function.

In fact, even miniport drivers that have a HwVidInterrupt function do not necessarily have a HwVidSynchronizeExecutionCallback function. Because the video port driver does not send a request to a miniport’s HwVidStartIO function until it completes processing of the preceding request (see HwVidStartIO Functionality for more information), miniport drivers rarely call VideoPortSynchronizeExecution.

There are two possible uses for a miniport’s HwVidSynchronizeExecutionCallback function:

  • To access the adapter registers using the miniport’s device extension for a driver function other than the HwVidInterrupt function

    When the HwVidSynchronizeExecutionCallback function is given control, interrupts from the adapter are masked off so the miniport’s HwVidInterrupt function cannot change state in the device extension while the HwVidSynchronizeExecutionCallback function is running in an SMP machine.

  • To write commands to the adapter registers or ports very quickly if the adapter requires it

    When the HwVidSynchronizeExecutionCallback function is given control, almost all system interrupts are masked off, so the HwVidSynchronizeExecutionCallback function cannot be preempted by a device (or even, a clock) interrupt.

    A HwVidSynchronizeExecutionCallback function must return control as quickly as possible.

With the first type of HwVidSynchronizeExecutionCallback function, the miniport calls VideoPortSynchronizeExecution with the Priority set to VpMediumPriority. With the second type of HwVidSynchronizeExecutionCallback function, the miniport also makes this call with the Priority set to VpMediumPriority if the driver has no HwVidInterrupt function. Otherwise, such a miniport makes this call with the Priority set to VpHighPriority.

In general, a miniport driver should not call VideoPortSynchronizeExecution with the second type of HwVidSynchronizeExecutionCallback function unless the driver designer has no other alternative: that is, unless the adapter is such that it must be programmed with system interrupts masked off. Otherwise, the miniport should call VideoPortSynchronizeExecution with the Priority set to VpLowPriority.

A HwVidSynchronizeExecutionCallback function, like a HwVidInterrupt function, cannot be pageable and cannot call certain VideoPort Xxx functions without bringing down the system. For a summary of VideoPort Xxx that the HwVidSynchronizeExecutionCallback function can call safely, see HwVidInterrupt Functionality.


HwVidTimer Functionality

Any miniport driver can have a HwVidTimer function at the discretion of the driver writer. A HwVidTimer function allows the miniport to time out operations or to monitor state changes over a coarser-grained interval than is possible by calling VideoPortStallExecution. HwVidTimer also does not prevent other system operations for occurring as VideoPortStallExecution does.

For example, a miniport driver for an adapter that emulates VGA functionality might have a HwVidTimer function that monitors the status of its adapter’s “VGA” registers periodically so the driver can emulate VGA-style graphics.

After a call to VideoPortStartTimer, the port driver calls HwVidTimer once every second until the miniport calls VideoPortStopTimer. A miniport driver can enable and disable calls to the HwVidTimer function repeatedly.

Note that a HwVidTimer function cannot disable calls to itself with VideoPortStopTimer. Another miniport function must control the enabling or disabling of calls to a HwVidTimer function through the use of VideoPortStartTimer and VideoPortStopTimer.


HwVidResetHw Functionality

Every miniport driver must have a HwVidResetHw function if its adapter cannot be reset to an initialized state without a hard reboot of the machine.

HwVidResetHw is called by the HAL if the machine is about to crash or if the user initiates a soft reboot of the machine. HwVidResetHw resets the adapter to a specified character mode, so the HAL can display crash-dump information as it shuts down the system or initialization information during a soft reboot.

HwVidResetHw cannot call the BIOS, call any pageable code, nor may it be made pageable. If possible, it should call only the VideoPortRead Xxx and VideoPortWrite Xxx functions, but it also can call any of the following:

  • VideoPortStallExecution

  • VideoPortZeroDeviceMemory

  • VideoPortZeroMemory


VGA-Compatible Miniport’s SvgaHwIoPortXxx

VGA-compatible miniport drivers in x86-based machines replace the system-supplied VGA miniport driver. Therefore, VGA-compatible miniports must have a set of SvgaHwIoPortXxx functions to support full-screen MS-DOS applications as the system-supplied VGA miniport driver does.

The designer of a new VGA-compatible SVGA miniport driver should adapt one of the system-supplied SVGA miniport’s SvgaHwIoPortXxx functions to the adapter’s features. As already mentioned in VGA-compatible x86 Based Miniports, miniport drivers for other types of adapters in x86-based machines can have a set of SvgaHwIoPortXxx routines and provide the same support at the discretion of the miniport designer or if the miniport cannot be loaded while the system VGA miniport is loaded.


Windowed VDMs in x86-Based Machines

Each MS-DOS application runs as a Windows VDM, which in turn, runs as a Console Manager application in the Win32 protected subsystem.

In Windows NT platforms, a kernel-mode component called the V86 emulator traps I/O instructions issued by MS-DOS applications. As long as such an application runs within a window, its attempts to access video adapter ports are trapped and reflected back to the system-supplied video VDD, which emulates the behavior of the adapter for the application.

In other words, the display driver retains control of the video adapter while a VDM runs in a window.


Full-Screen VDMs in x86-based Machines

For performance reasons, when the user switches an MS-DOS application to full-screen mode in an x86-based machine, the display driver yields control of the adapter. The system VGA or a VGA-compatible miniport driver then hooks out from the V86 emulator all I/O instructions, such as application-issued IN, REP INSB/INSW/INSD, OUT, and REP OUTSB/OUTSW/OUTSD instructions, to the video I/O ports. These hooked I/O operations are forwarded to the VGA-compatible miniport’s SvgaHwIoPortXxx functions.

However, for faster performance, a miniport can call VideoPortSetTrappedEmulatorPorts to allow some I/O ports to be accessed directly by the application. The miniport continues to hook other I/O ports with its SvgaHwIoPortXxx to validate the application-issued instruction stream to those ports.

To prevent a full-screen application from issuing a sequence of instructions that might hang the machine, the SvgaHwIoPortXxx functions monitor the application instruction stream to a driver-determined set of adapter registers. A miniport driver must enable direct access only to I/O ports that are completely safe. For example, ports for the sequencer and miscellaneous output registers should always be hooked by the V86 emulator and trapped to the miniport-supplied SvgaHwIoPortXxx functions for validation.

Direct access to I/O ports for the application is determined by the IOPM (named for the x86 I/O permission map) that the VGA-compatible miniport sets by calling VideoPortSetTrappedEmulatorPorts. Note that the miniport can adjust the IOPM by calling this function to have access ranges, describing I/O ports, released for direct access by the application or re-trapped to an SvgaHwIoPortXxx. The current IOPM determines which ports can be accessed directly by the application and which remain hooked by the V86 emulator and trapped to an SvgaHwIoPortXxx function for validation.

By default, all I/O ports set up in such a miniport’s emulator access ranges array are trapped to the corresponding SvgaHwIoPortXxx. However, VGA-compatible miniport drivers usually call VideoPortSetTrappedEmulatorPorts on receipt of an IOCTL_VIDEO_ENABLE_VDM request to reset the IOPM for the VDM to allow direct access to some of these I/O ports. Usually, such a driver allows direct access to all video adapter registers except the VGA sequencer registers and the miscellaneous output register, plus any SVGA adapter-specific registers that the driver writer has determined should always be validated by an SvgaHwIoPortXxx.


VGA-Compatible Miniport’s HwVidFindAdapter

A VGA-compatible miniport’s HwVidFindAdapter function (or registry HwVid..Callback) must set up the following in the VIDEO_PORT_CONFIG_INFO buffer:

  • NumEmulatorAccessEntries, indicating the number of entries in the EmulatorAccessEntries array

  • EmulatorAccessEntries, pointing to a static array containing the given number of EMULATOR_ACCESS_ENTRY-type elements, each describing a range of I/O ports hooked from the V86 emulator and, by default, forwarded to an SvgaHwIoPortXxx

    Each entry includes a starting I/O address, a range length, the size of access to be trapped (UCHAR, USHORT, or ULONG), whether the miniport supports input or output of string data through the I/O port(s), and the miniport-supplied SvgaHwIoPortXxx that actually validates and, possibly, transfers the data. Each SvgaHwIoPortXxx function handles read (IN or REP INSB/INSW/INSD) and/or write (OUT or REP OUTSB/OUTSW/OUTSD) transfers of UCHAR-, USHORT-, or ULONG-sized data.

  • EmulatorAccessEntriesContext, a pointer to storage, such as an area in the miniport’s device extension, in which the miniport’s SvgaHwIoPortXxx can batch a sequence of application-issued instructions that require validation

  • VdmPhysicalVideoMemoryAddress and VdmPhysicalVideoMemoryLength, describing a range of video memory that must be mapped into the VDM address space to support BIOS INT10 calls from full-screen MS-DOS applications

    The miniport driver can call the VideoPortInt10 function when such an application changes the video mode to one that the miniport driver’s adapter can support.

  • HardwareStateSize, describing the minimum number of bytes required to store the hardware state for the adapter in response to an IOCTL_VIDEO_SAVE_HARDWARE_STATE request

    When the user switches a full-screen MS-DOS application to run in a window, the miniport driver must save the adapter state before the display driver regains control of the video adapter. Note that a VGA-compatible miniport driver also must support the reciprocal IOCTL_VIDEO_RESTORE_HARDWARE_STATE request because the user might switch the windowed application back to full-screen mode.

A VGA-compatible miniport’s emulator access entries specify subsets of its access ranges array for the adapter. The emulator access entries can be and usually are all I/O ports in the mapped access ranges array set up by its HwVidFindAdapter function. The access ranges it passes in calls to VideoPortSetTrappedEmulatorPorts, defining the current IOPM and determining the I/O ports that are directly accessible by a full-screen MS-DOS application, specify subsets of the miniport’s emulator access entries.


Validating Instructions in SvgaHwIoPortXxx

As already mentioned in VGA-Compatible Miniports HwFindAdapter, the IOPM set for directly accessible I/O ports usually includes all SVGA registers except the sequencer registers and the miscellaneous output register, which the VGA-compatible miniport continues to monitor with its SvgaHwIoPortXxx functions. The sequencer registers control internal chip timing on VGA-compatible video adapters. If a full-screen MS-DOS application touches other adapter registers during a synchronous reset, the machine can hang. Likewise, if the miscellaneous output register is set to select a nonexistent clock, the machine can hang.

VGA-compatible miniport drivers must ensure that full-screen MS-DOS applications do not issue instructions that cause the machine to hang. Each such miniport driver must supply SvgaHwIoPortXxx function that monitor application-issued instructions to the I/O ports for the adapter sequencer registers and miscellaneous output register. Each new VGA-compatible miniport driver for an adapter with special features also must monitor and continue to validate any I/O ports to which an application might send any instruction sequence that could hang the machine.

Whenever an application attempts to access the sequencer clock register, the SvgaHwIoPortXxx must change the IOPM in order to trap all instructions coming in during a synchronous reset. As soon as an application sends an instruction that affects the sequencer or attempts to write to the miscellaneous output register, the SvgaHwIoPortXxx should adjust the IOPM by calling VideoPortSetTrappedEmulatorPorts to disable direct access to all adapter registers.

The miniport-supplied SvgaHwIoPortXxx functions should buffer subsequent IN (or INSB/INSW/INSD) and/or OUT (or OUTSB/OUTSW/OUTSD) instructions in the EmulatorAccessEntriesContext area it set up in the VIDEO_PORT_CONFIG_INFO (see VGA-Compatible Miniports HwFindAdapter ) until the synchronous reset is done, or until the application either restores the miscellaneous output register or resets it to a “safe” clock.

Then, the miniport driver is responsible for checking that the buffered instructions cannot hang the machine. If not, the miniport should process the buffered instructions, usually by calling VideoPortSynchronizeExecution with a driver-supplied HwVidSynchronizeExecutionCallback function. Otherwise, the miniport driver should discard the buffered instructions.


VGA-Compatible Miniport’s HwVidStartIO

When the user switches a full-screen MS-DOS application back to running in a window, a VGA-compatible miniport’s HwVidStartIO function is sent a VRP with the I/O control code IOCTL_VIDEO_SAVE_HARDWARE_STATE. The miniport must store the state of the adapter in case the user switches the application to full-screen mode again.

Note that the miniport’s SvgaHwIoPortXxx might have buffered a sequence of application INs and/or OUTs, as described in Validating Instructions in SvgaHwIoPortXxx, when its HwVidStartIO routine is called to save the adapter state. In these circumstances, the miniport should save the current state, including the buffered instructions, so that the SvgaHwIoPortXxx can resume validation operations exactly where they left off if the user switches the application to full-screen mode again.

When the miniport completes a save operation, the port driver automatically disables the current IOPM for VDMs and the miniport’s SvgaHwIoPortXxx functions. The port driver restores the IOPM automatically if the application is switched to full-screen mode again. It also resumes calling the miniport’s SvgaHwIoPortXxx, after it calls the miniport’s HwVidStartIO routine with the IOCTL_VIDEO_RESTORE_HARDWARE_STATE request.


Chapter 8 Printing Environment

The Windows NT® printing architecture was designed to be modular, allowing third-party vendors to customize select components in order to integrate their specialized hardware into the system. Figure 8.1 shows the Windows NT printing environment.

Figure 8.1 Printing Environment on Windows NT

To print, an application makes Win32® printing and GDI calls. These functions, found in winspool.dll and gdi32.dll, are device-independent, allowing applications to easily print on a variety of devices, such as laser printers, vector plotters, raster printers, and fax machines.

The Win32 calls are passed to kernel-mode GDI. The printer driver, in conjunction with the graphics engine, converts the input and passes the resulting raw device data to the spooler. The spooler further processes the data and sends it to the serial, parallel, or network driver to which the target printer is connected.

The remaining chapters in this part of the book discuss the various Windows NT print components:

  • Printer Drivers

  • Printer Minidrivers and RasDD

  • The Spooler

  • Print Processors

  • Print Monitors


Chapter 9 Printer Drivers

To support device-independent applications consistently across the full range of possible printing devices, Win32® accesses printer features through a printer driver. A printer driver is an intermediate software module that provides device-specific implementations for printer functions.

Hardware vendors can develop and easily install printer drivers in Windows NT®. This chapter defines a printer driver’s role in the Windows NT print environment and outlines the components required by all Windows NT printer drivers. It then presents general design issues and describes how printer drivers support Win32 printer administration and setup. Finally, it briefly describes the Common Property Sheet User Interface with which a printer interface driver must communicate.

Part 1 of this manual contains information crucial to the design of all graphics drivers, which includes both printer and display drivers. It describes the general capabilities of GDI and provides information on implementing driver entry points common to all graphics drivers. Printer-specific DDI support is discussed in Supporting DDI Printing and User Interface Functions. Detailed descriptions of DDI entry points, GDI support functions, and data structures, can be found in the Graphics Driver Reference.


Printer Drivers in Windows NT

Figure 9.1 shows where the printer driver exists in the Windows NT printing architecture.

Figure 9.1 The Printer Driver in Windows NT

It is only through GDI that a Windows NT printer driver communicates with the application and, by way of the spooler, sends printer instructions to the device. That is, a printer driver is driven by requests from GDI. If the driver determines that a print-related request can be adequately processed by GDI services, it can pass the call back for the graphics engine to handle. This is referred to as punting the call. This method of deferring function calls to GDI, when no special processing is required for the particular printer device, allows the driver to be smaller and can significantly shorten its development time.

Since the graphics engine can perform much of the rendering and printing functionality, and always works in conjunction with a printer driver, it is recommended that you refer to the GDI Support for Graphics Drivers and its subsequent topics before proceeding with the printer driver topics that follow. The GDI overview topics provide information on topics such as:

  • GDI rendering capabilities

  • The division of labor between GDI and the printer driver

  • GDI halftoning capabilities

  • How the driver exports its functions

  • GDI support services

  • Rendering surfaces

  • PDEVs (logical representations of physical devices)

  • Other GDI/driver issues


Components of a Printer Driver

A Windows NT printer driver consists of the following components:

  • The printer graphics driver DLL implements the DDI to the degree necessary to support printer rendering features and capabilities that cannot be adequately handled by the graphics engine.

  • The printer interface driver DLL creates an interface that allows users to set the physical properties of the printer and the logical properties of the document. This interface is also responsible for interacting with the Common Property Sheet User Interface to provide device- and document-specific property sheet page(s) in response to an application request.

  • The printer device data file supplies printer-specific information to both the printer graphics and interface components. The file type (and contents) depends on the printer with which it is associated.

A printer driver is specific to a particular type of printer or family of printers. Three types of printer drivers shipped with Windows NT are the raster device driver (RasDD), PostScript driver, and plotter driver. The components that comprise each of these drivers are shown in the following table.
Type of Printer Driver | Printer Graphics Driver DLL | Printer UI DLL | Data File
RasDD | rasdd.dll | rasddui.dll | Raster minidriver .DLL
PostScript | pscript.dll | psui.dll | .PPD
Plotter | plotter.dll | plotui.dll | .PCD

For example, the three components that comprise the printer driver for an HP IIIsi PostScript printer are:

  • pscript.dll

  • PSUI.dll

  • HP3SI<ver>.PPD

Printer driver files are installed on a Windows NT system when the corresponding printer is installed.

These three driver types provide support for many of the printing devices on the market today. You need only write a printer driver if your printing device is not compatible with the appropriate system-supplied one.


Printer Graphics DLL

To print a document, an application calls a variety of Win32 functions. Many of these functions depend on the printer graphics component of the printer driver to perform rendering actions that are specific to the particular type of printer device being used, such as:

  • Drawing/rendering text and graphics

  • Returning device-specific information

  • Marking the starting and ending points of documents and pages

  • Creating device-compatible realizations of graphics engine objects such as fonts, brushes, and palettes

In addition, the printer graphics driver portion of the printer driver always supports requests to enable and disable the driver, the PDEV, and the rendering surface. If the surface is device-managed, requests to implement rendering operations are also supported. Requests for the correct device-dependent data and behavior come to the appropriate driver from GDI through the DDI entry points.

Driver functions that return printer-specific information, such as DrvQueryAdvanceWidths, are often used even when no document is actually being printed. This is the case when an application must maintain a WYSIWYG display on the screen, so that what the user arranges on the display will be replicated by the printer whenever a printed copy is requested. For example, to display the line breaks in text properly, a word processor must base line-fitting calculations on character widths and other font metrics from the specific printer’s realization of a font. Without information on how the printer realizes a chosen font, the display could not accurately present the printed document.

Sample code for the printer graphics component of the PostScript printer driver can be found in the print\psprint\pscript directory of the Windows NT DDK.

For DDI functions and implementation details that are common to all graphics drivers, see Supporting the DDI in Part 1. Printer-specific DDI support is described in Supporting DDI Printing and User Interface Functions.

Raster Device Drivers

Microsoft Windows NT includes RasDD to simplify the task of building a raster printer driver. RasDD is capable of carrying out print-related requests ¾ such as printing text, rendering bitmaps, and advancing a page ¾ on most types of printers, which means that the driver writer doesn’t have to write this code. Instead, driver writers have only to build a minidriver for their particular printer. The Unitool is provided with the Windows NT DDK for this purpose. For more information on RasDD and minidrivers, see Printer Minidrivers and RasDD.

RasDD is a data-driven printer driver that uses the minidriver to provide essential data about a printer. For example, RasDD retrieves required information from the minidriver to determine the current resolution of the printer, to identify which fonts are available, and so on.

Note that RasDD does not support plotters, Adobe PostScript printers, or printers that use printer-control languages similar to PostScript. For these devices, a vendor might be able to use the Microsoft-provided drivers, or might have to implement the functions required by GDI for these types of printers.


Printer Interface DLL

Printer drivers differ from the typical display driver in that device-specific information can be obtained and altered via the user interface. Through the Win32 Printer Folder in the Windows NT interface, the user has access to view and alter certain printer properties such as the name of the printer, which port it connects to, and which paper trays are used. Furthermore, many applications allow modifications to the printer setup for a particular document.

To accommodate this functionality, printer drivers must provide a printer interface DLL that includes functions for setting printer properties and document properties by the adminstrator/end-user. This printer interface component is in addition to the printer graphics DLL required for rendering the image to be printed.

Note that where the printer driver is loaded and executed in Windows NT system memory, the printer interface driver is a user-mode module that is loaded and executed in the context of the spooler.

Sample code for the printer interface component of the printer driver can be found in the psui directory of the Windows NT DDK.


Printer Driver Data Files

Printer driver data files provide information to the printer graphics and printer interface components that indicate the printer’s capabilities. These capabilities include the printer’s memory configuration, the number and type of cartridges, and the number and size of the paper trays. Driver data files can be either minidriver, PPD, or PCD files, depending on the printer type.

Minidrivers

A printer minidriver is the GPC data file that RasDD uses to print to a raster printing device. It contains a collection of data structures referred to as the printer’s data table. A data table contains data such as the printer’s resolution, color capabilities, metrics for device fonts, and the strings that correspond to various printer commands. It also specifies printer model-specific aspects of how primitive bitmap and text operations are to be performed.

To create a printer description file for a new printer, use the Unitool program, supplied with the Windows NT DDK. Unitool automatically combines the printer description file with a layer of RasDD interface code to create a printer driver DLL, called a minidriver. For more information about using Unitool, refer to Printer Minidrivers and RasDD or the Unitool online Help.

PPD Files

The PostScript driver uses .PPD files. Each PPD file contains PostScript keywords that provide resource data for a PostScript printing device. For example, the PPD file contains the printer model name, nickname, default resolution, page size, printable area of the page, and a list of device fonts. Unlike the minidriver’s GPC file, which is converted to a DLL when defined, a PPD file is an ASCII text file that must be read by the PostScript driver as part of the device initialization during DrvEnablePDEV.

The PostScript driver can support any PostScript printer as long as it has access to a PPD file for that printer. There must be a PPD file for each PostScript printer supported.

The Windows NT PostScript driver supports Adobe version 4.0-compatible .PPD files.

PCD Files

The plotter driver uses .PCD data files. A PCD data file is generated from an ASCII text file that contains the capabilities of a plotter device. The Windows NT DDK supplies two sample text files; it is recommended that you copy and edit one of these files to define your device. The DDK also supplies the plotgpc program to convert the text file into a .PCD file.


Printer Driver Design and Implementation Strategies

Windows NT includes RasDD for raster printing devices, a PostScript printer driver, and a plotter driver. Most currently available printers belong to the families supported by RasDD, the PostScript driver, or the plotter driver. Exceptions include devices that contain hardware drawing accelerators controlled by proprietary command sequences.

Each of these drivers have data-driven designs, allowing them to support broad classes of devices. Because these drivers customize their operation for specific printer models by interpreting printer data files, support can often be extended to a new printer simply by creating a new description file, rather than creating a new printer driver.

If a printing device is not supported in Windows NT, it should first be determined whether the device can be provided with minimum support using a minidriver and RasDD. There is no source for RasDD in the DDK, but if it is necessary to write a new raster printer driver, the designer can use a combination of the PostScript driver and the framebuffer (framebuf) display driver source as a model for such a driver.

The DDK includes sample source for a PostScript print driver in the print\psprint directory. The pscript subtree contains source code for the PostScript graphics driver, and the pscriptui subtree contains source for the PostScript interface driver. If it is necessary to create a new PostScript driver, modifying this driver should greatly reduce design time and effort.

The PostScript interface driver can provide a basis for a raster printer interface driver, with modification for the hardware. A major difference between PostScript and raster drivers is that a PostScript graphics driver writes to a device-managed surface, and so must implement certain rendering entry points. A raster driver, on the other hand, enables a GDI standard bitmap, allowing GDI to handle most of the rendering. The framebuffer display driver handles rendering in the same way. Some of the design areas that require close attention are the following:

  • If the device has hardware fonts, the driver should support the DrvTextOut and the font query calls, including: DrvQueryFont, DrvQueryFontTree, and DrvQueryFontData. For more information, see Part 1 of this manual and the function descriptions in the Graphics Driver Reference.

  • If the device supports scalable fonts, this must be indicated in the IFIMETRICS structure. GDI uses XFORMOBJ to determine the size of the fonts, which, in turn, determines the commands that must be provided to the printer. The PostScript driver uses scalable (transformable) fonts only, so review the textout.c file in the pscript sample driver as an example of implementing scalable fonts.

  • It is important to be aware that the printer driver is in the same privileged kernel-mode address space as the rest of the Windows NT Executive. Faults in the printer driver will cause GDI and the rest of the system to fault. Driver writers can optionally implement a user-mode printer driver to perform rendering in user mode.

Supporting DDI Printing and User Interface Functions contains information on printer-specific driver entry points for the printer graphics and printer interface portions of a printer driver. It also lists the functions that comprise the user-mode printer driver module.


Common Property Sheet User Interface

The CPSUI is a system-supplied module that manages all aspects of a property sheet for an application, including the addition of driver-defined pages. Specifically, the CPSUI is responsible for creating and destroying an application’s property sheet pages plus any device-specific pages that an application might request. The CPSUI is also responsible for the display and update of all dialog boxes that define a page.

The system-supplied Printer Folder is an example of an application that uses the CPSUI to manage and display property sheets. When a user selects a printer’s “Printer®Properties” menu item, the CPSUI displays the property sheet shown in Figure 9.2. The tab titled “Device Settings” is obtained from the printer driver by the CPSUI through a call to DrvDevicePropertySheets; the other pages are provided to the CPSUI by Printer Folder. A printer interface driver’s implementations of DrvDevicePropertySheets and DrvDocumentPropertySheets must be able to respond to CPSUI requests for property sheet information.

Figure 9.2 Printer Folder Property Sheet

CPSUI functions and structures are declared in compstui.h, which is provided with this Windows NT DDK. The DDK also provides a sample application that uses the CPSUI to manage and display a property sheet.

Reference material for the CPSUI can be found in the Graphics Drivers Reference.


Chapter 10 Supporting DDI Printing and User Interface Functions

A Windows NT® printer driver ensures that application print requests, sent to GDI, write correctly to the specified printer. In addition, the printer driver must also supply and support some user interface functions that allow the administrator to set printer characteristics through the Print Folder and allow the user of print-aware applications to set device-specific document printing characteristics. To ensure this, a printer driver must have two DLLs:

  • The printer graphics driver

  • The printer interface driver

This chapter provides information about implementing the DDI entry points specific to both of these parts of printer drivers. This chapter also describes the DDI entry points that comprise the user-mode printer driver interface.


Supporting Printer Graphics Driver-Specific Functions

The general categories of DDI functions that any graphics driver can support are initialization and termination functions, graphics output functions, color and pattern functions, and font and text functions. Use of functions in these categories are described in Supporting the DDI. This section describes functions that are specific to printer graphics drivers. There are two types:

  • Page and document support functions

  • Device-specific functions

Function declarations and structure definitions can be found in winddi.h, which is shipped with the Windows NT DDK.


Supporting Pages and Documents

Printer drivers are required to support the following entry points, which support page and document printing:

  • DrvStartDoc

  • DrvStartPage

  • DrvSendPage

  • DrvEndDoc

Printing a document begins with a call from GDI to the driver function DrvStartDoc. This function requests that the driver supply any control information needed to start the printing of a document. DrvStartDoc also sends the document name to the printer driver.

The driver calls EngWritePrinter to send data to the printer by way of the spooler. Along with a buffer of data, the driver should also supply the printer handle received when GDI called DrvEnablePDEV.

DrvStartPage is called to give the driver a chance to send any control codes needed to make sure the page is in the correct format. DrvStartPage should also erase the surface each time it is called.

DrvSendPage requests the driver to process and send the page to the printer by calling EngWritePrinter. If the surface is a bitmap on which the drawing is accumulated, the driver should access the raw bits by using SURFOBJ_Xxx service functions. Drivers that use a device-managed surface and send the bits to the printer as the image is rendered do not send out the drawing in DrvSendPage. During DrvSendPage, the driver should also send the appropriate printer sequences to eject the page from the printer.

If DrvSendPage is slow, the user might want to stop the print job while in this call. In this case, the driver should call EngCheckAbort at least every five seconds to determine whether printing should be terminated. If EngCheckAbort returns TRUE, the processing of the page should be stopped and this function should return FALSE.

DrvEndDoc requests that the driver send any control information needed at the end of a document. The control codes should be sent by way of EngWritePrinter.


Supporting Device-specific Functions

Optional functions that support device-dependent functions in the printer not covered by regular GDI operations are the following:

  • DrvEscape

  • DrvDrawEscape

These entry points enable the driver to provide escape functions used to “tunnel” through GDI, essentially turning GDI into a dumb communications line. The PostScript driver is an example of a driver that supports these.

DrvEscape is used to query information from a device when the information is not available in the device-independent DDI. For example, information that could be obtained directly from a device might include the phone number for a FAX machine. Drawing on a surface is allowed, but, because no CLIPOBJ is passed in, as it is in DrvDrawEscape, drawing in DrvEscape is limited. The escape depends on the value of the iEsc parameter

The driver should support DrvDrawEscape when the device allows device-specific drawing operations not available in a device-independent DDI. DrvDrawEscape gets a CLIPOBJ as an input parameter. For example, the PostScript driver implements this call to support the printing of encapsulated PostScript (EPS) data. The escapes that are supported are determined by DrvEscape.


Supporting Printer Interface Driver Functions

A Windows NT printer interface driver must support several DDI functions. In addition, the printer interface driver can optionally hook GDI printer management functions such that it can perform pre- and/or post-processing on data in user mode. Function declarations and structure definitions can be found in winddiui.h, which is shipped with the Windows NT DDK.

The topics that follow list the required and optional DDI functions for printer interface drivers.


Required Printer Interface Driver Functions

A printer interface driver must implement the following functions:
DevQueryPrintEx | Determines whether or not the driver can print the specified job on a printer.
DrvConvertDevMode | Converts the specified DEVMODE.
DrvDeviceCapabilities | Provides information about the specified device and its capabilities.
DrvDevicePropertySheets | Adds the driver’s printer properties page(s) to the specified property sheet.
DrvDocumentPropertySheets | Adds the driver’s document properties page(s) to the specified property sheet.
DrvPrinterEvent | Responds to the print subsystem’s request for action in response to an event that the subsystem has received.

DrvDocumentPropertySheets is called when a user has requested that a printer’s document properties be updated and/or displayed.

When DrvDocumentPropertySheets is called with a null pPSUIInfo parameter, the spooler is the caller. A user interface is not required in this situation; instead, DrvDocumentPropertySheets should perform the spooler-requested operation that is specified in the fMode member of the DOCUMENTPROPERTYHEADER structure.

When DrvDocumentPropertySheets is called with a non-null pPSUIInfo parameter, the Common Property Sheet User Interface is the caller. The printer interface driver has the option of supplying the CPSUI with driver-specific document property page(s) for display on the CPSUI-managed property sheet. See the Common Property Sheet User Interface for more details.

The Print Folder calls DrvDevicePropertySheets when a user has requested to see the Printer Properties property sheet. The printer interface driver has the option of supplying the CPSUI with a property sheet page that displays and permits configuration of device-specific properties.

Because virtually all printer drivers have registry data, the printer interface driver must implement the PRINT_EVENT_INITIALIZE event for DrvPrinterEvent. All other events can be optionally implemented.


Optional Printer Interface Driver Functions

The printer interface driver can optionally implement the following functions:
DrvDocumentEvent | Allows the printer interface driver to hook certain printer management calls.
DrvUpgradePrinter | Upgrades a printer’s registry device settings.


User-Mode Printer Drivers

Windows NT provides printer driver extensions that allow you to optionally implement a user-mode printer driver. These extensions allow a printer driver to implement rendering functionality in user mode by allowing the driver to alter the data stream both before it is sent to the kernel-mode printer driver and after it is returned from the kernel-mode printer driver.

If implemented, the user-mode printer driver should exist as a separate module from the printer interface driver. In this situation, the user-mode printer driver must link to spoolss.lib, as opposed to winspool.lib.

When the spooler calls DrvPrinterEvent with PRINTER_EVENT_INITIALIZE, the printer interface driver should call the Win32 SetPrinterData function with a value of SPLPRINTER_USER_MODE_PRINTER_DRIVER and a REG_SZ value that is the name of the DLL. The spooler will then know which DLL to load during printing.

Sample code for a simple user-mode printer driver is provided with this Windows NT DDK. Function declarations can be found in winddiui.h, which is also provided with the DDK. The following table lists the functions that comprise this interface, and identifies those functions that must be implemented.
DrvSplAbort | Aborts the printing job. Optional.
DrvSplClose | Releases the user-mode printer driver handle and its associated resources. Required.
DrvSplEndDoc | Ends the printing job. Required.
DrvSplEndPage | Provides end-of-page notification. Optional.
DrvSplStartDoc | Starts the identified print job. Required.
DrvSplStartPage | Prepares the printer to accept data. Optional.
DrvSplWritePrinter | Writes data to the printer. Required.


Chapter 11 Printer Minidrivers and RasDD

A printer minidriver is an executable data file that the raster printer device driver (RasDD) uses to print to a raster printing device. This GPC file describes the capabilities, commands, and resident fonts of a printer or class of printers.

RasDD is the Windows NT system-supplied device driver that, with a minidriver, provides printer support for raster printing devices. RasDD uses a GDI standard bitmap, allowing GDI to handle most of the rendering. So, instead of implementing all the DrvXxx functions that GDI requires, a driver writer need only create a minidriver that provides information about the printer. This greatly simplies the task of developing a Windows NT raster printer driver.

Other reasons for using minidrivers include:

  • They achieve portability across CPU types.

  • They provide improvements to the minidriver and printer driver software.

  • They receive updates and changes to the graphics engine.

  • They obtain high quality color rendering and halftoning.

  • They support 1, 4, and 8 bits-per-pixel color.

This chapter provides design guidelines for those vendors who need to develop their own raster device drivers. For those vendors who need only develop a minidriver, this chapter provides a detailed description on how to use Unitool.


Windows NT Raster Printing Architecture

Figure 11.1 shows the system components required to print to a raster printing device on Windows NT.

Figure 11.1 Raster Printer Components in Windows NT

This figure identifies the components that comprise a raster device driver as:

  • The rasdd.dll graphics driver.

  • The rasddui.dll printer interface driver.

  • A minidriver that contains printer-specific information for RasDD and its user interface.

RasDD customizes its operation for specific printer models through minidriver files. In many cases, support can be extended to a new printer simply by creating a new minidriver rather than creating an entirely new printer driver.

Printer drivers and their components are discussed in Printer Drivers.


Developing a Raster Printer Device Driver

The system-supplied RasDD supports most raster printers. The raster printing model designed for Windows NT uses the following general assumptions:

  • The number of pins for the printer must either be one or a multiple of eight.

  • Windows NT currently supports only Hewlett-Packard PCL-scalable fonts.

Other printers with scalable fonts, such as an Epson printer, can be supported by creating a minidriver with a range of font sizes rather than designing an entirely new driver.

If a printing device is not supported in Windows NT, vendors should first determine whether their device can be provided with minumum support through development of a minidriver to use with RasDD.

If vendors determine that they need to write a new raster printer driver, they can use a combination of the DDK-supplied PostScript driver and the framebuffer display driver source as a model for such a driver. There is no source code for RasDD in the DDK.

The PostScript interface driver can provide a basis for a raster printer interface driver, with modification for the hardware. A major difference between PostScript and raster drivers is that a PostScript graphics driver writes to a device-managed surface, and so must implement certain rendering entry points. A raster driver, on the other hand, enables a GDI standard bitmap, allowing GDI to handle most of the rendering. The framebuffer display driver handles rendering the latter way.

Areas that require close attention when designing a raster printer device driver include:

  • If the device has hardware fonts, the driver should support the DrvTextOut and the font query calls, such as DrvQueryFont, DrvQueryFontTree, and DrvQueryFontData. For more information, see Part 1 of this manual and the function descriptions in the Graphics Driver Reference.

  • If the device supports scalable fonts, this must be indicated in the IFIMETRICS structure. GDI uses XFORMOBJ to determine the size of the fonts, which, in turn, determines the commands that must be provided to the printer. The PostScript driver uses scalable (transformable) fonts, so its source code can be used as an example of implementing scalable fonts.

Supporting the DDI contains information on printer-specific driver entry points for the printer graphics and printer interface portions of a printer driver.


Developing a Minidriver with Unitool

The Windows NT Unitool is a development tool that allows driver writers to easily create a minidriver for their raster printer. It permits GPC data to be organized and defined into a minidriver for a single raster printer or a group of similar raster printers. Unitool automatically combines the printer description file with a layer of RasDD interface code to create a minidriver. The GPC data that Unitool creates in the minidriver provides the resources that RasDD requires to run a printing device. This eliminates the need to create a complex driver for every printer within a family of similar printing devices.

Unitool, sample GPC data, and other related files and information required to build a minidriver are provided in the Windows NT DDK. Sample GPC data, used to create actual Windows NT minidrivers, is provided for the following printer drivers:

  • Digital Equipment Corporation driver for DEClaser 3200 printers

  • Epson 9-pin driver

  • Epson 24-pin driver

  • Hewlett Packard driver for printers that use HPPCL

  • HP PaintJet driver

  • A text-only base driver

To save development time, driver writers can start with the supplied GPC data set that most closely resembles the characteristics of their raster printer, and customize it to fit their device.

The GPC resource data in Unitool enables you to collect comprehensive information about the related printer driver in the minidriver. Each minidriver supports RasDD and the RasDD user interface. For the user interface, the minidriver data defines general information about the printer, such as the number of available paper trays, and what form is contained in which tray.

The subsequent topics:

  • Contain information related to using Unitool.

  • Describe the general procedure for building a Windows NT minidriver.

Subsequent topics provide detailed descriptions of each type of information contained in Unitool, including:

  • Character-translation table used to remap characters from the printer’s native character and symbol set to the Windows ANSI set.

  • Minidriver data and the specification for master units.

  • Control information, including the page-control and cursor-movement commands.

  • Paper information, including the paper size, paper source and paper destination commands.

  • Graphics information, including the printer resolution, compression, color and rectangle fill commands

  • Font information, including text font cartridge, font simulation and downloadable fonts

  • Printer model data, including model-specific data for the concerned minidriver.

  • Minidriver callback functions.

  • Unitool error messages.

Note Unitool creates data files (including PFM and CTT) that are compatible with Windows 95 and Windows 3.1. You must convert these files to the necessary format to work on Windows NT. For more information, see Converting a Minidriver to Windows NT.

Many topics contain examples of minidriver data for the following printer types:

  • Kodak Diconix 150 Plus

  • Hewlett-Packard LaserJet IIP

  • Hewlett-Packard PaintJet

The Kodak Diconix printer is an ink-jet printer that uses a command set similar to the Epson and IBM dot-matrix printer command set. The HP LaserJet IIP printer is a laser printer that uses HPPCL. The HP PaintJet is an ink-jet color printer that is used as an example to describe color support for the minidriver.


Unitool Data Structures

Using Unitool, a developer who understands the hardware and command set for a printer can develop a minidriver by initializing its component data structures using the minidriver's Printer Data window. The minidriver's data structures are identified as follows:
Structure | Description
COMPRESSMODE | Contains data compression mode commands.
CURSORMOVE | Indicates the limitations of the printer cursor-movement commands and contains command strings that move the printer's cursor.
DEVCOLOR | Describes the way in which a device handles color for both graphics and text. The DEVCOLOR structure can support devices that process color data by plane or by pixel.
DOWNLOADINFO | Contains command strings that download fonts.
FONTCART | Contains font cartridge commands and command strings that select the specified cartridge and identify the fonts on that cartridge.
FONTSIMULATION | Contains command strings that enable and disable bold, italic, underline, and strikethrough font simulations.
MODELDATA | Indicates the characteristics of a printer model (s).
PAGECONTROL | Contains command strings that initialize the printer, select the paper orientation, and so on.
PAPERDEST | Specifies the paper output bin that collects the pages and contains command strings that select the specified bin.
PAPERSIZE | Specifies the size dimensions of the printer paper and contains command strings that configure the printer for the specified paper size.
PAPERSOURCE | Contains command strings that identify the paper bin or feeding mechanism.
RECTFILL | Indicates the printer's rectangle fill capabilities and contains command strings that fill rectangular regions.
RESOLUTION | Indicates the horizontal and vertical raster graphics resolution that the printer supports and contains command strings that select the specified resolution.


Minidriver Printer Data Window

Each data structure in the minidriver's Printer Data window corresponds to one or more of the dialog boxes in the Unitool application. Setting the controls in the Unitool dialog boxes actually initializes the members of these structures. Unitool provides an organized means of entering in printer data and command strings that become part of a minidriver's Printer Data window. In turn, Unitool interprets the data that is already available and generates GPC data as requested.

The minidriver's Printer Data window contains four key types of GPC data for each category of printer:

  • Printer data resources

  • Font data resources

  • Glyph encoding data resources

  • String-table resources

The printer data resources provide most of the data contained in the minidriver data structures. Each minidriver's Printer Data window contains a host of information about the printer, such as resolution, text clipping, underlining, and strikethrough. Unitool also describes, for example, compression capabilities, available paper sizes, sources, destinations, how to move the print position on the page, font simulations such as underscore or italic, rectangular fill commands, and how to download fonts.


Fonts

Some printers have many fonts, others have none. The minidriver font data resources define which fonts are available depending on the current configuration of the printer. For example, dot matrix printers have no fonts when printing in landscape mode. Fonts can also be cached in the printer. The Laserjet printer enables fonts to be downloaded from disk, including TrueType fonts. In this case, the bitmap information is taken from the TrueType fonts and is downloaded to the printer to create a device font that can be processed quickly, thereby reducing the size of the data stream.

To render text with device fonts and retrieve font data in response to application queries, RasDD requires PFM and font selection commands. The character data for device fonts is stored in the printer or in a font cartridge. Printers render text faster with device fonts because the bitmap or vector data is device resident and does not require downloading from the disk to the printer.

A CTT for 16-bit drivers and RLE format for 32-bit drivers are used by RasDD to map characters from a printer's native character and symbol set to the Windows ANSI character set to identify the printable characters. The current version of the Windows ANSI character set contains 244 entries. RasDD provides support for some of the common code pages and symbol sets to eliminate redundancy in a large number of individual printer drivers and permit individual drivers to support the less common ones. The font data resources and the CTT-data resources differ from the resources used in the Windows NT minidriver.


String-Table Resources

The GPC data refers to string-table resources by indices. String resources are specified in a number of the Unitool dialog boxes. For example, strings indicate the printer models the driver supports. In the user interface, strings define how to format the resolution for printing and how to display the information for the user. Other strings define information, such as the names of the paper trays. The string indices track the actual IDs in the .rc file. The strings are stored in that file and can be translated into other languages if necessary.


Minidriver Callbacks

In addition, a minidriver callback function is required when the printer provides functionality that RasDD does not support. For example, suppose the bit order in the printhead is in reverse to the common convention where the most significant bit is the uppermost print pin. The only function currently supported by Windows NT RasDD is the FilterGraphics function.


Commonly Used Minidriver Values

Unitool separates the minidriver data into a set of related application functions. These functions can be selected using the cascading menu near the top or from the individual lists shown in the minidriver's Printer Data window. When a function is selected, a corresponding dialog box is displayed to permit minidriver data to be entered or updated. Each dialog box can contain two type of values: selectable values and user-defined values.

Selectable values are listed as a set of values commonly used by the minidriver, such as the predefined values for standard paper sizes, paper sources, and paper destinations. Unitool dialog boxes also request user-defined values such as a name, a printer command string, or a control value. In addition, defaults are provided for the user-defined values commonly entered for the source driver.


Minidriver Commands

Unitool can provide commands that enable or disable certain features of the printer. For the Kodak Diconix 150 Plus printer, the string ESCx3 defines the Select String command. In Unitool, ESCx3 is represented as \x1Bx\x03. The string ESC is replaced by the ASCII representation (\x1B), the argument x is not altered, and the decimal value 3 is replaced by the hexadecimal equivalent (\x03).

Commands can also contain parameters supplied by RasDD at runtime, when the command is used. For example, in the CURSORMOVE dialog box, the CM_OCD_YM_REL command advances the cursor in the positive y direction, relative to the current position. For the Diconix 150 plus printer, the Immediate Line Feed string, ESCJn defines the command, where n is the number of 1/288th inch increments that the cursor advances. In this case, the ESC string is replaced by its ASCII representation (\x1B), the argument J is not altered, and the variable n is replaced by the conversion character to convert the parameter to a byte value (%c).

Unitool stores each printer command in a CD data structure. In cases where RasDD supplies an argument to a particular command, such as the Immediate Line Feed command, Unitool stores the additional data in an Extended Command-Descriptor (EXTCD) data structure. This data structure contains values by which RasDD divides, multiplies, increases, or limits command arguments.


Printer Commands

Printer commands and control values can be entered in any combination of the following formats:
Format | Description
ASCII | Enter literal ASCII characters
Hexadecimal | Hexadecimal values are preceded by the backslash character: \x. For example, the hexadecimal value 0xFF is typed as \xFF.
Converted parameters | Convert the minidriver parameters to a special format and send these parameters to the printer using a conversion character. Each conversion character is preceded by the % (percent) sign. The current conversion characters are as follows:
Character | Description
%d | Decimal value. For example, 12 is sent as 1 (31h) followed by 2 (32h).
%D | Signed decimal value. This is the same as the %d character except it is preceded by a plus or minus sign.
%c | Byte value. For example, 12 is sent as formfeed (0Ch).
%C | Byte value with a zero added. For example, 6 is sent as \x36.
%l | 16-bit value with the least significant byte first. For example, 12 is sent as Form Feed (0Ch) followed by null (00h).
%m | 16-bit value with the most significant byte first. This is the same as %l except the order of the bytes is reversed.
%q | Unique hexadecimal value formatted for Toshiba and Qume devices.


Coordinate System

RasDD uses a single, common horizontal and vertical (X, Y) coordinate system. The units in this coordinate system are called master units. Master units are the least common multiple of the various horizontal and vertical resolutions supported by the printer. RasDD can add, multiply, or divide a value by the specified master units to calculate the arguments for printer commands that use these coordinates.

Note Many of the Units options that appear next to specified values are not in use at this time and do not require entries.

In cases where the master units are required, a special Units dialog box is provided for each printer command to specify how to convert from master units to the relevant printer units. For example, if the master units are 432 units/inch and the printer opens at 144 dpi, the master units must be divided by three to obtain the appropriate values for the printer.


Developing Windows NT Minidrivers

Unitool, version 3.06 is compatible with RasDD, although a number of file conversions are required to prepare the minidriver for use with it. The requirement for these conversions results from the major optimizations that have been incorporated to enhance the performance of RasDD as follows:

  • Handles rendering. RasDD retrieves the bitmap image the graphics engine creates and sends the bitmap to the printer.

  • Scans for rules. Rules are rectangular areas that the LaserJet printer can handle. Scanning for rules substantially reduces the volume of data sent to the printer. In some instances, this means a page that would otherwise be too large to fit into the printer's memory can be printed. It also permits a grid, such as in a spreadsheet, to be sent to the printer as a number of rules, thereby eliminating most of the graphics.

  • For the font metrics, RasDD uses the IFIMETRICS structure to provide the Windows NT version of the font metrics information which is preferred to the PFM files.

  • The Windows NT GPC extensions provide Windows NT-specific information about the printer that is later passed to GDI. In particular, the Windows NT GPC extensions data consists of color rendering (halftoning) information. The Windows NT GPC data also characterizes the printing mechanism. This data defines both the effective resolution of the printer and the default halftone pattern size to use. The NTGPC structure also identifies minidriver version, a size field and a field to indicate whether the minidriver uses IFIMETRICS or PFM.

    Note If the NTGPC structure is not included, the driver is assumed to be using the Windows 3.1 format.

  • Instead of the Windows 3.1 CTT, Windows NT uses the RLE format for glyph encoding. RLE fully supports Unicode because RLE does not have the 256-character limitation CTT contains. This is particularly important for the handling of non-latin based ideographic languages. RasDD can still use the old format (for compatibility with 16-bit minidrivers), but the RLE format caters to the wide characters of Unicode and speeds up Win32 CreateDC calls by having much of the font information precomputed in the format that is required by GDISRV.

  • .Inf files are required for installation and setup. The Windows NT DDK contains sample code for Setup including the \setup\printer\oemsetup.inf file.


NT RLE Character Mapping

To fully support Unicode, Windows NT has changed the format of the Windows 3.1 CTT to the new RLE format. RasDD can still use the old format (for compatibility with 16-bit minidrivers), but the RLE format supports the wide characters of Unicode and speeds up CreateDC calls by having much of the font information precomputed in the format required by GDISrv.

The CTT file contents can be saved into one of three formats. Unitool determines which one is to be used to save the CTT file based on the information that was entered as follows:

  • 1-to-1 (byte to byte) translation (effectively a translation table). Each character code consists of a single byte. For simple remappings, RasDD sends the character value entered in the edit control each time the ASCII value is shown.

  • 2-to-1 (two bytes to one byte) translation. Each character code consists of either one or two characters. RasDD sends the first character value entered in the edit control, then uses either a backspace or x-movement command (as defined in the DI_BKSP_OK control) to move from the current position back to the original position. Then RasDD sends the second character to overprint the first. If null, the second character is not sent. This is used to overstrike characters, such as the slash and zero to produce a slashed zero.

  • Variable-length (byte to byte) string translation. Each character code consists of a variable length string containing printer commands that can change printer modes to gain access to a particular character, such as symbol set switching in the LaserJet family. RasDD sends the commands entered in the edit control when requested.

The differences between these formats is transparent to the user. Unitool creates the CTT in a format that results in the smallest file size depending on the actual translation. Printer commands sent from the CTT do not change the permanent state of the printer.

The Window NT equivalent of these is the RLE table. Because Unicode is a somewhat sparse mapping and real fonts are even sparser, there is a need to have the capability to return information about which glyphs are available in the font to GDISrv. This is done using the WCRUN structures defined in the winddi.h header file. To speed up CreateDC, RasDD uses these same structures in the minidriver. This means no time is spent building up tables that are static for a given printer.

The Windows NT DDK provides the ctt2rle tool to convert an existing Windows 3.1 CTT file to an NT RLE file. The conversion is done assuming that the Windows 3.1 character set provides the input mapping. The ctt2rle executable requires only a single parameter: the file containing the CTT data. The output is written to a file with the same prefix, but with a .rle extension. Similar to Unitool, ctt2rle uses the shortest format available to encode this information. The sample drivers in the DDK contain a batch file showing how ctt2rle.exe is used to convert existing files.


Using the RLE File Format

The ctt2rle.exe program assumes a Latin-based language. To handle Unicode - and especially non-latin, ideographic languages - a more complex format is needed. The file layout is defined by the structures located in the ntrle.h header file.

Note GDISrv allows the driver a 32-bit handle to each glyph in the font. Basically, the FD_GLYPHSET structure contains the header information that describes how to reach into the glyph handle array of data returned by the driver to GDISrv. The driver is free to set the value within these 32 bits.

RasDD can use five different formats as follows:

#define RLE_DIRECT    10     /* Index + 1 or 2 data bytes       */
#define RLE_PAIRED    11     /* Index plus 2 bytes,  overstruck */
#define RLE_L_OFFSET  12     /* Length + 3 byte offset to data  */
#define RLE_LI_OFFSET 13     /* Length + Index + 2 byte Offset  */
#define RLE_OFFSET    14     /* Offset to (length; data)        */

These values specify the different methods used to pack data into the glyph handles and are taken from the ntrle.h header file.

The glyph handles contain two or three pieces of data depending upon the particular format. Each glyph handle contains a width table index and at least one byte of data to send to the printer. If only one or two bytes are to be sent to the printer, the RLE_DIRECT or RLE_PAIRED format is used. This format has a 2-byte index and one or two bytes of data. RLE_DIRECT contains one or two bytes of data to send. If there are two bytes of data, and the second one is not zero, the second one is sent immediately following the first. One use for this format is SHift JIS, where many of the glyphs require two bytes to print. This format is defined by the RD structure in the ntrle.h header file. The RLE_PAIRED format also contains one or two bytes. If RLE_PAIRED contains two bytes RasDD is forced to backup after the first byte and print the second one. This corresponds to the overprinting mode of Windows 3.1.

If any of the glyphs require more than two bytes, one of the offset modes is required. There are three offset modes that are designed to reduce the size of the output file: RLE_L_OFFSET, RLE_LI_OFFSET and RLE_OFFSET.

The most compact of these offsets is the RLE_LI_OFFSET mode. The data format is defined by the RLIC structure in the ntrle.h header file. In this format, there is a one-byte length field, a one-byte width table index, and two bytes used either as an offset OR as one or two bytes to send to the printer. If the length field is <= 2, the data is contained in the least significant one or two bytes. Otherwise, the least significant two bytes (considered a WORD) contains an offset to the data to send. This offset is relative to NTRLE structure located at the beginning of the file/resource. There is one obvious limitation to this mode: There can be no more than 256 glyphs in the font (due to the width table index size).

The RLE_L_OFFSET format is used when the above limitations rule out use of RLE_LI_OFFSET. This format consists of a length byte and a three-byte offset field. The offset is in the least significant three bytes of the handle, while the length field occupies the most significant byte. The offset is relative to the NTRLE structure at the start of the file/resource. The data at the offset must be WORD aligned (the least significant bit of the offset must be zero). The offset points to a WORD that is the width table index for this particular glyph, followed by the bytes to send to the printer. The number of bytes is contained in the most significant byte of the glyph handle.

The RLE_OFFSET format is the largest of the offsets. RLE_OFFSET simply consists of an offset into the file. This offset is relative to the NTRLE structure at the start of the file/resource. The offset must be WORD aligned and point at two words, followed by the bytes to send to the printer. The first word contains the length of the data string to send, the second WORD is the width table index.


NTGPC Data Extensions

The Windows NTGPC extensions provide Windows NT-specific information about the printer that is later passed to GDI. In particular, the Windows NTGPC extensions data consists of color rendering (halftoning) information made up of two major components. First, the COLORINFO structure is defined to characterize the printer’s color rendering. The COLORINFO structure specifies information such as the purity of the printer’s inks (how much cyan and magenta is in the yellow ink), the actual color of the inks, and the gamma of the printer (how the intensity of the color changes as the amount of color is increased).

The other major component of the Windows NTGPC data characterizes the printing mechanism. This data defines both the effective resolution of the printer and the default halftone pattern size to use. In practice, the printer’s stated resolution, such as 300 dpi, actually indicates only the positioning ability of the printer. To print correctly, the printer’s effective resolution is also required because the actual dots the printer creates on the paper are often larger than the printer’s stated resolution. For example, the LaserJet Series II has a stated positioning ability of 1/300th of an inch, but the actual dots are a slightly less than 1/200th of an inch. In this case, if every second dot is printed, more than 50 percent of the page appears to be covered in ink.

The effective resolution allows the halftoning code to compensate for these large dots and the image comes out at about the expected intensity. The halftone pattern size is set on a subjective basis to make the output look good. As the pattern size increases, the number of grey levels in the image increases (for a monochrome printer). The image resolution decreases as the halftoning operation effectively averages over larger chunks of the image.

The Windows NT DDK contains the ntgpcedt.exe program that specifies the NTGPC extensions data. The ntgpcedt.exe program reads data from a file and generates the required data structures in the desired file format. The structures used in the output file are described in the file ntres.h, that is also included in the DDK. Because RasDD accepts this file format, consider the ntgpcedt.exe program as the best means to produce GPC extensions data.


Character-Translation Table File Formats

A character-translation table is used by RasDD to remap characters from the printer’s native character and symbol set to the Windows ANSI set. RasDD will provide support for some of the common code pages and symbol sets, so that this data will not need to be redundant in a large number of individual printer drivers. It is intended that printer drivers supporting devices with less common code pages and symbol sets will contain their own character-translation table resources. Any given driver can mix and match either of these types of character-translation tables as needed. Character-translation tables provided by RasDD are referenced from the minidriver through negative numbers that correspond to the resource ID values (from RasDD), and character-translation tables provided by the minidriver are referenced by positive numbers corresponding to the resource ID values (from the minidriver).

Note For a Windows NT minidriver, the RLE format for glyph encoding is provided instead of the characterization tables to better support Unicode because there is no 256 entry limitation. This is particularly important for that translation of non-Latin based languages. The Windows NT DDK provides the CCT2RLE program that converts the Windows 3.1 CCT format to the preferred RLE format. This enables you to generate the RLE using the current CCT structure.

Predefined Translation Tables

RasDD will provide support for some of the common code pages and symbol sets, and allow drivers to support less common ones. The list of character-translation tables currently supported by RasDD is as follows:
Translation Table | Description
CTT_CP437 | Translate using IBM Code Page 437.
CTT_CP850 | Translate using IBM Code Page 850.
CTT_WTYPE_BIG5 | Use for Big 5 to Big 5 conversions.
CTT_WTYPE_CPJIS78 | Use to convert a range of single byte or double byte characters into JIS78.
CTT_WTYPE_CPJIS83 | Use to convert a range of single byte or double byte characters into JIS83.
CTT_WTYPE_CPJIS78_ANK | Use to convert a range of double byte characters into JIS78.
CTT_WTYPE_CPJIS83_ANK | Use to convert a range of double byte characters into JIS83.
CTT_WTYPE_NS86 | Use to convert Chinese Big 5 to Industrial Standard Code.

TRANSTAB Structure

Three formats of character-translation tables are available. The first is a single byte-to-byte translation used for simple remappings. The second is a single byte-to-two bytes translation, where the second character is used to overstrike the first. The DI_BKSP_OK flag determines how the cursor should be repositioned to overstrike the second character. The third allows a single byte-to-variable length string that may contain commands to change symbol sets, or whatever else is necessary, to create the desired character. This difference between these formats is transparent to the user, but UniTool creates the character-translation table in the format resulting in the smallest file size depending upon the actual translations specified by the user. Any printer commands sent from the CTT cannot change the permanent state of the printer.

typedef struct {
    WORD    wType;
    BYTE    chFirstChar;
    BYTE    chLastChar;
    union
        {
        short    psCode[1];
        BYTE     bCode[1];
        BYTE     bPairs[2][1];
        } uCode;
} TRANSTAB;

Members

wType
Translation table type. It can be one of these values:

Value

Meaning

CTT_WTYPE_COMPOSE

uCode is an array of 16-bit offsets from the beginning of the file pointing to the strings to use for translation. The length of the translated string is the difference between the next offset and the current offset. Please note that an additional offset is needed (dfLastChar + 1) to provide a means to read the length of the string for the translation of the last character.

CTT_WTYPE_DIRECT

uCode is a byte array of one-to-one translation table from chFirstChar to chLastChar.

CTT_WTYPE_PAIRED

uCode contains an array of paired, unsigned bytes. If only one character is needed to do the translation, then the second byte is zero, otherwise the second byte is struck over the first byte.

chFirstChar
First character in character-translation table.
chLastChar
Last character in character-translation table.


Creating a Minidriver

The version of Unitool shipped with this DDK is a 16-bit application. Consequently, Unitool does not operate satisfactorily without some preparation of the resource files. The sample.RC files provided with the DDK cannot be read by Unitool, so if you want to use the sample files as a template for a new minidriver, you must use the .W31 files. When you have finished using Unitool to enter pertinent printer and font data, you must convert the .PFM files to .IFI files (refer to “Converting a Minidriver to Windows NT” for additional information) and also convert the .CTT files to .RLE files (see “NT RLE Character Mapping” and “Using the RLE File Format” for information).

You will also need to run ntgpcedt.exe, a program that specifies the NTGPC extensions data. Refer to “NTGPC Data Extensions.”

The following steps outline the procedure for creating a new minidriver while using one of the samples as a template.

Note The following procedure uses the epson9 sample files as an example template.

  1. Create a destination directory for the new minidriver files.

  2. Copy the following directories from the DDK sample files to the destination directory created in step 1:

    • \src\print\epson9\ifi

    • \src\print\epson9\pfm

    • \src\print\epson9\ctt

    • \src\print\epson9\rle

    That is, if your destination directory is named MYDRIVER, you will now have the following directories:

    • ...\MYDRIVER\ifi

    • ...\MYDRIVER\pfm

    • ...\MYDRIVER\ctt

    • ...\MYDRIVER\rle

  3. Delete the .IFI files from your ......\MYDRIVER\ifi directory and the .RLE files from the ......\MYDRIVER\rle directory created in step 2.

  4. Start Unitool and choose File/Open. Enter ...\src\print\epson9\epson9.w31 in the File Name: text box and click OK. Unitool opens the Printer Data, Fonts, and CTT windows which contain information extracted from the epson9.w31 file.

    You may get a message box stating that the file is read-only; click OK since you won’t need to overwrite the epson9.w31 file.

    At this point you can do a File/SaveAs to create a new file for your minidriver. Enter the pathname of the destination directory (created in step 1) and name of the new driver (using the form <driver_name>.GPC) in the File Name: list box.

    Unitool writes the following files to the \MYDRIVER directory:

    • <driver_name>.RC

    • <driver_name>.GPC

    • <driver_name>.RC$

  5. Set up the minidriver's Printer Data window. You can start by opening the Model Data dialog box (choose Printer Data/ModelData) and deleting the unsupported models provided by the sample .W31 file you opened in step 3. For example, you could delete all but the “Epson Compatible 9 Pin” model and use this as the template for the models included in your minidriver.

    Make the changes in each of the following dialog boxes accessed under the Printer Data menu, as appropriate for your minidriver:

    • MiniDriver Data dialog box

    • Page Control dialog box

    • Cursor Movement dialog box

    • Paper Sizes dialog box

    • Paper Sources dialog box

    • Paper Destinations dialog box

    • RESOLUTION dialog box

    • Compression dialog box

    • Color Models dialog box

    • Rectangle Fill dialog box

    • Font Cartridges dialog box

    • Font Simulation dialog box

    • Download Information dialog box

    The Validate Dialog Boxes function on the Options menu can be selected to automatically validate each entry. When this function is active, a dialog box is selected, and OK, Next, or Previous is selected (a choice that causes the current dialog box contents to be saved rather than ignored. Unitool performs limited error checking on the contents of that dialog box and if any potential problems are found, a dialog box warning of problems is displayed. These warnings can be ignored or the dialog box can be displayed to correct the errors. If the Validate Dialog Boxes command is not selected, error checking does not occur. This command is enabled by default. For more information, see Unitool Error Messages.

    In addition, Validate Now! on the Options menu can be selected to perform limited error checking on the contents of the currently loaded minidriver file. If any potential problems are found, a dialog box listing them is displayed.

    Note Subsequent topics provide information relating to the setting of the minidriver's Printer Data window. Each topic provides a specific, detailed description for each category of information that can be set up in the minidriver's Printer Data window. Each topic also offers recommended values that can be entered based on the type of source minidriver to be built. For example, the page-control commands section recommends values to enter for the Diconix 150 Plus printer and for the HP LaserJet IIP.

  6. When all the Printer Data entries have been made, you can edit the .PFM files in the FONTS window and the CCT files in the CCT window. You only need to edit those files that relate to fonts and character translation tables supported by your printer.

    You may need to close your <driver_name>.RC file in Unitool and edit this file with your text editor to add/delete the names of .PFM files as appropriate for your driver (these will be converted to .IFI files later). You may also need to name the .CTT files you are using and delete any .RLE filenames (the .CTT files willl be converted to .RLE files later).

  7. When all changes are complete in the FONTS and CCT windows. exit Unitool and proceeed with “Converting a Minidriver to Windows NT” to convert the minidriver to Windows NT format.


Converting a Minidriver to Windows NT

Converting a minidriver to the Windows NT format requires the pfm2ifi.exe, ctt2rle.exe, and ntgpcedt.exe programs (available in the Windows NT DDK) to generate the NTGPC data extensions.

To convert the minidriver:

  1. Open a command window and move to the directory containing the binary files provided with the DDK and run the pfm2ifi.exe program to convert the PFM files you crated with Unitool and generate the files in the IFIMETRICS format.

    The command to convert a .PFM file is:

    pfm2ifi <pathname>.pfm <filename>.ifi [-v]

    The pathname is the path and filename of the .PFM file to be converted. The -v option prints the converted metrics to the standard output device.

  2. Run the ctt2rle.exe program to convert the CTT files you created with Unitool and generate these files in the RLE format.

    The command to convert a .CTT file is:

    ctt2rle <pathname>.ctt

    The pathname is the path and filename of the .CTT file to be converted. The created .RLE file will be written to the same directory as the .CCT file.

  3. Move the newly created .RLE files to your ...\rle directory.

  4. Open the MYDRIVER.RC file with a text editor. Immediately under the

    RC_TABLES    PRELOAD  MOVEABLE    MYDRIVER.RC

    line, add the following line and close the file.
    RC_TABLES    PRELOAD  MOVEABLE    NT.GPC

  5. Run the ntgpcedt.exe program and open your .RC file (MYDRIVER.RC in this example).

  6. Make any appropriate changes to the color calibration data, halftone, and pixel values displayed in the NT GPC Editor dialog box and select Close. Choose Yes in response to the “Save updated NT GPC data to ‘NT.GPC’?” message box and the data is saved to the NT.GPC file for your driver.

  7. Type Build to build the minidriver DLL file. Refer to Setting up the Environment, Creating Your Driver, and Compiling Your Driver for details on the build environment and process.

  8. Test and implement the minidriver. Refer also to “Debugging and Tuning Your Driver” and “Testing Your Driver.”

Note Sample minidrivers are provided in the Windows DDK to demonstrate the input and output files the minidrivers produce during conversion.


Setting up the Environment

The graphical Microsoft® Windows NT® DDK Setup program creates a program group called Windows NT Device Driver Kit. Among other items, this program group contains items called Free Build Environment and Checked Build Environment. These items call setenv.bat to create a build environment with correct environment variables.

Manual installation does not create this program group. You can call setenv.bat manually, using the following syntax:

setenv <destination> [free | checked]

The <destination> argument specifies the directory where the DDK was installed. The optional second argument specifies the target build environment; the default type is free.


Creating Your Driver

The DDK includes sample source code for many Windows NT drivers that you can use as a base for your own driver development. Choose the driver that most closely matches your device and spend some time analyzing and experimenting with it. The Win32 SDK provides many tools, including a customizable text editor, that you can use to modify sample code, as well as to write your own drivers from scratch. See the Win32 SDK documentation for more details on the text editor and other development tools.


Compiling Your Driver

Windows NT DDK drivers are built using the build utility, which uses a set of rules and project files which specify how drivers should be created.

The dirs file specifies which directories in the subtrees contain source code files which are to be built. The sources file specifies which source files are required to build the current driver.

If your driver will consist of multiple binaries, or the source will be kept in multiple directories, you may need to create a dirs file specifying which directories are to be built and in what order. Each separate binary will require a sources file describing which files are to be compiled/linked to create the driver.

Each driver directory also contains a makefile. As build spawns the nmake utility for each source file listed in sources, nmake uses the makefile to generate dependency and command lists. The standard makefile in a driver source code directory directs nmake to the master nmake macro definition file, makefile.def. This file defines the flags for the build tools such as the compiler and linker. makefile.def simplifies the creation of platform-independent driver projects and is similar to the ntwin32.mak file in the Win32 SDK.

Once you have your dirs, sources and makefile files created, the next step is to run build. Build will parse the sources file and spawn nmake for each source file. Nmake will evaluate the macros in makefile.def and spawn the C compiler with the proper switches. After the compile stages are completed, build will spawn nmake again to link the objects and complete the driver building phase.

If your sources file is correct, running build is all that is required to compile and link your driver. Using build and makefile.def removes the guesswork from such things as which compiler switches are required, and what arguments the linker requires. By adding the appropriate defines to the sources file, it is possible to control the build options in a platform-independent fashion.

During driver development you will need to build free and checked versions of your driver. This is controlled by environment variable settings which are interpreted by build and nmake. These variables are set by setenv.bat, as discussed previously. To build a free driver, run build from the free environment. To build a checked driver, run build from the checked environment.

Template files (.tpl) for dirs, sources, makefile, and other files can be found in the \<destination>\doc directory. Working versions of these files can be found in driver source subdirectories of the DDK. Examining these files along with makefile.def (located in the \<destination>\inc directory) will provide additional information.

For more information about the build utility, see the Windows NT DDK Programmer’s Guide .


Debugging and Tuning Your Driver

During driver development and testing, you will need to adjust the registry of your Windows NT development and test machines. You will need to enable/disable driver loading, add/modify registry keys and values, and so on.

Windows NT provides an application for this purpose called Registry Editor. Registry Editor (regedt2.exe) is documented in the Windows NT documentation.

Additionally, the DDK includes two registry tools, regini and regdmp, which are script-driven. These tools provide a “-?” option, which provides help from the command line. Regdmp dumps the contents of the registry to a script file. Regini creates registry entries according to the specified script. Sample regini script files can be found in several driver source directories in the DDK.

The methods available for driver debugging center around the kernel debugger and the checked build, both of which have already been discussed in some detail. Additional information about the kernel debugger is available in the Win32 SDK documentation and the Windows NT DDK Programmer’s Guide.

Some debugger extensions have also been included for the Windows NT STREAMS (strmexts.dll), Windows NT GDI (gdiexts.dll) and Windows NT USER (userexts.dll) components. These are installed and placed in your path by DDK Setup, to be used while debugging.

Performance tuning is covered in the Win32 SDK documentation and in the Programmer’s Guide topic of the DDK Help online documentation. The DDK includes a sample illustrating performance counters that is based on the VGA display driver; this sample is found in the src\perf directory of the DDK.

Also included are the utilities that are useful for quickly loading performance counter information into the registry, lodctrs and unlodctrs. The tools are script-driven and are documented in the Windows NT DDK Programmer’s Guide.

Other useful tools include the ObjDir and Drivers tools, which provide information about objects in the system and what resources drivers have created, own, or are using. The ObjDir tool is similar to the WinObj tool that is included with the Win32 SDK, except it is console based. The Drivers tool provides a listing of what drivers are loaded in the system and their resource usage.


Testing Your Driver

The DDK includes several groups of tests from the Microsoft Hardware Compatibility Test (HCT) Suite. These tests are found in the \hct directory of the DDK CD-ROM. They can be installed by running setup.exe in that directory.

Setup installs the HCT testing shell and, optionally, several groups of tests that will exercise the system while loading your driver to help you uncover problem areas.

Many of the HCT tests are documented in the Programmer’s Guide topic of the DDK Help online documentation. A readme file in the \hct directory provides additional details about disk and hardware requirements, as well as more information on Setup and Install.

Note You should use the -ddk switch when using Setup to install the HCT tests. These tests should first be run using the checked build to aid driver debugging. They should be run again for final verification on the free build.

Microsoft Compatibility Labs (MCL) uses a superset of these tests to certify Windows NT-compliant drivers. Passing the included DDK HCT tests is a positive sign that your driver is stabilizing and nearly ready for certification.


Master Units and Minidriver Data

RasDD uses a single, common horizontal and vertical (x,y) coordinate system. The units in this coordinate system are called master units and are the least common multiple of the various horizontal and vertical resolutions supported by the printer. The minidrivers add, multiply, or divide a value by the specified master units to calculate the arguments for printer commands that use these coordinates.


MiniDriver Data Dialog Box

Select MiniDriver Data from the Printer Data menu to use the MiniDriver Data dialog box to specify a GPC specification version number, a minidriver version number, the horizontal and vertical master units, the printer’s technology, and the absence or presence of a private help file as needed.

Major Revision (GPC Specification Version Number)

Major Revision contains the version number of the GPC specification that applies to the minidriver. The GPC specification refers to the format of data resources contained in the minidriver. For current minidrivers, including the Diconix 150 Plus and the HP LaserJet IIP, the version number is 3.

Minor Revision (Minidriver Version Number)

Minor Revision contains the version number of the minidriver. This value can be any integer between 0 and 255.

If the driver is installed, changing this value and rebuilding the minidriver causes RasDD to rebuild the driver’s font-summary file. Unitool builds a font-summary file for each printer that can download soft fonts using a font installer (as with the HP LaserJets and HP DeskJets). The font-summary file contains a list of fonts currently available for a particular printer model, including the internal and downloadable fonts.

Diconix 150 Plus: Use 1 for the first version of the minidriver.

HP LaserJet IIP: Use 41 for the forty-first version of the minidriver.

X Master Units

X Master Units specifies the horizontal master unit, which is the least-common multiple of the selected horizontal cursor-movement command and the supported graphics resolutions.

For the Diconix printer, the horizontal master unit is 960 dpi (dots per inch). This value is the least-common multiple of the following graphics and cursor-movement resolutions:

  • 80-dpi (normal/expanded-mode graphics)

  • 160-dpi (normal/expanded-mode graphics)

  • 320-dpi (normal/expanded-mode graphics)

  • 12-cpi (default font-character width)

A minidriver typically supports three graphics resolutions: low, medium, and high. For the Diconix printer, there are four horizontal graphics resolutions: 80, 96, 160, and 320. 96 dpi is not used because the resolution does not factor evenly into any of the others, but the remaining three values (80, 160, and 320) support horizontal graphics resolutions. For the HP LaserJet IIP printer, each of the supported graphics resolutions (75, 150, and 300) factor evenly into the specified least-common multiple of 300.

Many dot-matrix and laser printers support commands that move the printhead or cursor horizontally across the page. For the dot-matrix printers that do not, RasDD uses the space character from the narrowest printer font to move the printhead or cursor horizontally across the page. For more information, see Control Information.

Because Diconix printer falls into the latter category, the space character from the narrowest printer font (12 pitch font) is selected as the horizontal space command. This means the resolution of the horizontal cursor-movement command for the Diconix printer is 1/12th of an inch.

Diconix 150 Plus: Use 960.

HP LaserJet IIP: Use 300.

Y Master Units

Y Master Units specifies the vertical master unit. This value is the least-common multiple for the line spacing command and the vertical resolution for graphics output.

For the Diconix printer, the vertical master unit is 576 dpi. This value is the least common multiple of the following line spacing and graphics resolutions:

  • 1/288-inch (line spacing)

  • 1/96-inch (normal-mode graphics)

A minidriver for a dot-matrix printer typically supports three graphics resolutions: low, medium, and high. For the Diconix printer, there are only two vertical graphics resolutions. When possible, select the lower resolution (96 dpi). The higher resolution requires the factory settings to be altered and results in slower printer (requires interlaced graphic output in the y-direction). Minidrivers ideally support the default switch settings for printer, although this is not always possible.

The Diconix printer supports six line-spacing commands that provide the following vertical resolutions:

  • 1/8-inch (line spacing)

  • 7/72-inch (line spacing)

  • 7/96-inch (line spacing)

  • 1/6-inch (line spacing)

  • n/216-inch (line spacing)

  • n/288-inch (line spacing)

Of these resolutions, the finest (288 dpi) was chosen for the Diconix.

Diconix 150 Plus: Use 576. This is the least common multiple of the 1/288 inch Line Spacing command and the 1/96 inch vertical graphics resolution.

HP LaserJet IIP: Use 300.

For the HP LaserJet IIP, the resolution for the line-spacing command and the vertical resolution for graphics output are equivalent at 300 dpi (the aspect ration is 1:1).

Note A printer’s aspect ratio is the ratio formed by the x- and the y-printer resolution values. For many printers, the aspect ratio is 1. For example, the HP LaserJet IIP supports a 300 dpi resolution in both the vertical and horizontal directions. For the Diconix printer, the aspect ratio is 3.333 (320/96) for the highest resolution. To produce the highest quality, avoid using aspect ratios greater than 2.

Update References

Update References indicates whether Unitool is to update each of the measurement values (graphics resolutions, paper sizes, unpintable-region dimensions, line-feed values, and so on) that are specified in master units, in each of the related data structures. Check this control and click OK for UniTool to update all measurements. If left blank, UniTool leaves the original measurement values. If some of the measurements do not factor evenly into the new master units and the Update References box is checked, UniTool issues an error message that identifies the faulty value.

fTechnology (Printer Technology)

fTechnology contains a list of values that identify device technologies used by the printer:
Value | Meaning
GPC_TECH_DEFAULT | Printer does not use any of the other technologies in the list.
GPC_TECH_PCL4 | HP PCL Level 4 or above
GPC_TECH_CAPSL | CaPSL Level 3 and above
GPC_TECH_PPDS | PPDS printer language
GPC_TECH_TTY | TTY-type printer
GPC_TECH_DBCS | FE (Far East) page printer

Select the value from the list that corresponds to the device technology for your printer.

Diconix 150 Plus: The GPC_TECH_PCL4 box is not checked.

HP LaserJet IIP: The GPC_TECH_PCL4 box is checked.

fGeneral

(Not supported; no boxes should be checked.)


Control Information

RasDD sends page-control commands to the printer to begin a document, to begin a page, to end a document, to end a page, to cancel a print job, and so on. RasDD sends cursor-movement commands to the printer to move the cursor or print head horizontally across the page, vertically down (and sometimes up) the page, and so on.

A number of sets of page-control and cursor-control commands can be set up for the minidriver. To specify the page-control commands, select Control Info from the Printer Data menu. then select PageControl to display the initial Page Control dialog box. To specify cursor-movement commands, select Control Info from the Printer Data menu. Then select CursorMove to display the initial Cursor Movement dialog box.


Page-Control Dialog Box

Select Control Info/PageControl... from the Printer Data menu to access the Page Control dialog box. The Page Control dialog box specifies the page-control commands and the priority order RasDD uses to send the commands to the printer.

Note The fGeneral dialog box accessed from the Page-Control dialog box is not used in the current implementation of Unitool. You can ignore it.

When you finish entering data in the Page Control dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

The page-control commands appear in the text boxes near the center of the Page Control dialog box. Use the scrollbar to access the list boxes and locate the required commands and define them. When complete, indicate the priority order for sending commands numerically in the Order group box near the top of the dialog box.

PC_OCD_BEGINDOC

The PC_OCD_BEGINDOC command is issued to begin printing a document. For example, commands sent at the beginning of a document the cursor or print head to the upper-left corner of a page, clear the printer’s buffers, initialize the printer configuration to a known state, and perform any related tasks. Some common PC_OCD_BEGINDOC commands are the printer reset, carriage return, buffer clear, top-of-form, character set selection, and so on. In addition, less-common (and less-obvious) commands for particular printers are also sometimes set. For example, a raster printer may require unique dip switch settings for foreign character sets. In this case, additional PC_OCD_BEGINDOC commands are inserted to take these settings into account.

Diconix 150 Plus: Use the Reset command (ESC@) to return the printer to the default conditions specified by the current dip switch settings. Use the Cancel command (ESCM) to delete all data from the print buffer.

To specify the Reset and Cancel combination, type the following in the PC_OCD_BEGINDOC box:

\x1B@\x1BM

HP LaserJet IIP: Use the Reset command (ESCE) to return the printer to its default settings and the Perforation Skip command (ESC&l0L) to disable the printer’s perforation skip feature.

To specify the Reset and Perforation Skip combination, type the following in the PC_OCD_BEGINDOC box:

\x1BE\x1B&l0L

PC_OCD_BEGINPAGE

The PC_OCD_BEGINPAGE box command is issued before printing each page of a document. For some printers, the PC_OCD_BEGINDOC command can be a single carriage return. For other printers, other commands must also be sent.

Diconix 150 Plus: Use the Carriage Return command (0x0d) to return the cursor or print head to the left edge of the page.

To specify the command, type the following in the PC_OCD_BEGINPAGE box:

\x0D

HP LaserJet IIP: No command is required at the beginning of a new page.

PC_OCD_DUPLEX_ON

The PC_OCD_DUPLEX_ON command enables duplex (double-sided) printing.

Note RasDD does not process the duplex-on command unless the MD_DUPLEX box is checked in the fGeneral dialog box on the Printer Model Data dialog box.

Diconix 150 Plus: Does not support duplex printer. The PC_OCD_DUPLEX_ON box is empty.

HP LaserJet IIP: The PC_OCD_DUPLEX_ON box is empty.

Two separate commands are required to enable duplex printing; the PC_OCD_DUPLEX_ON and PC_OCD_DUPLEX_HORZ commands. One command enables duplex printing in portrait mode. The other enables it in landscape mode.

PC_OCD_ENDDOC

The PC_OCD_ENDDOC box command ends a document. The carriage return is a common end-document command that positions the cursor in the left margin. Other end-document commands vary from printer to printer, although the printer is typically returned to its original (or default) state.

Diconix 150 Plus: Use the Carriage Return (0x0d) command.

To specify the Carriage Return command, type the following in the PC_OCD_ENDDOC box:

\x0D

HP LaserJet IIP: Use the Reset command (\x1BE) to reset the printer to its default settings.

To specify the Reset command, type the following in the PC_OCD_ENDDOC box:

\x1BE

PC_OCD_ENDPAGE

The PC_OCD_ENDPAGE command ends a page.

Diconix 150 Plus: No command is required. The PC_OCD_ENDPAGE box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

PC_OCD_DUPLEX_OFF

The PC_OCD_DUPLEX_OFF command disables duplex (double-sided) printing.

Diconix 150 Plus: Does not support duplex printing. The PC_OCD_DUPLEX_OFF box is empty.

HP LaserJet IIP: Use the Duplex Off command (ESC&l0S).

To specify the command, type the following in the PC_OCD_DUPLEX_OFF box:

\x1B&l0S

PC_OCD_ABORT

The PC_OCD_ABORT command cancels the current document.

Diconix 150 Plus: Use the Reset command (ESC@) to return the printer to its default settings and the Cancel command (ESCM) to delete all data from the print buffer.

To specify the Reset and Cancel combination, type the following in the PC_OCD_ABORT box:

\x1B@\x18

HP LaserJet IIP: Use the Reset command (ESCE) to return the printer to its default settings.

To specify the Reset command, type the following in the PC_OCD_ABORT box:

\x1BE

PC_OCD_PORTRAIT

The PC_OCD_PORTRAIT command enables the printer to render output in portrait mode. A printer supports the portrait-mode command only if the printer also supports device fonts for both portrait and landscape modes.

Diconix 150 Plus: Does not support this command.. The PC_OCD_PORTRAIT box is empty.

HP LaserJet IIP: Use the Portrait Orientation command (ESC&l0O).

To specify the command, type the following in the PC_OCD_PORTRAIT box:

\x1B&l0O

PC_OCD_LANDSCAPE

The PC_OCD_LANDSCAPE command enables the printer to print output in landscape mode. A printer supports the landscape-mode command only if the printer also supports device fonts for both portrait and landscape modes.

Diconix 150 Plus: Does not support commands that set the page orientation. The PC_OCD_LANDSCAPE box is empty.

HP LaserJet IIP: Use the Landscape Orientation command (ESC&l1O).

To specify the command, type the following in the PC_OCD_LANDSCAPE box:

\x1B&l1O

PC_OCD_MULT_COPIES

The PC_OCD_MULT_COPIES command enables multiple copies of a document to be printed.

Note RasDD does not process the multiple-copies command unless the MD_COPIES box is checked in the fGeneral dialog box on the Printer Model Data dialog box.)

Diconix 150 Plus: Does not support a command that enables multiple-copy printing. The PC_OCD_MULT_COPIES box is empty.

HP LaserJet IIP: Use the Number of Copies command (ESC&lnX) to print n copies of a document.

To specify the command, type the following in the PC_OCD_MULT_COPIES box:

\x1B&l%dX

PC_OCD_DUPLEX_VERT

The PC_OCD_DUPLEX_VERT command enables vertical-duplex printing. When pages are printed in vertical-duplex mode, each is printed to permit the top and bottom edges of consecutive pages to be bound. When pages are printed in horizontal-duplex mode, each is printed to permit the left and right edges of consecutive pages to be bound.

Diconix 150 Plus: Does not support this command. The PC_OCD_DUPLEX_VERT box is empty.

HP LaserJet IIP: Use the Vertical Duplex command (ESC&l1S).

To specify the command, type the following in the PC_OCD_DUPLEX_VERT box:

\x1B&l1S

PC_OCD_DUPLEX_HORZ

The PC_OCD_DUPLEX_HORZ command enables horizontal-duplex printing.

Diconix 150 Plus: Does not support duplex printing. The PC_OCD_DUPLEX_HORZ box is empty.

HP LaserJet IIP: Use the Horizontal Duplex command (ESC&l2S).

To specify the command, type the following in the PC_OCD_DUPLEX_HORZ box:

\x1B&l2S

PC_OCD_PRN_DIRECTION

The PC_OCD_PRN_DIRECTION command sets the direction of the text relative to the current graphic orientation at 90 degree increments.

Diconix 150 Plus: Does not support the text direction. The PC_OCD_PRN_DIRECTION box is empty.

HP LaserJet IIP (HPPCL5 only): Use the text rotation command (ESC&a%dP).

To specify the command, type the following in the PC_OCD_PRN_DIRECTION box:

\x1B&a%dP

PC_OCD_JOB_SEPARATION

(Not currently supported)

Maximum Number of Copies

Max # Copies specifies the maximum number of copies that the user can request.

Diconix 150 Plus: If a printer does not support multiple copies, this box can be left empty. For clarity, however, this box contains 1 for the Diconix minidriver.

HP LaserJet IIP: Allows a maximum of 99 copies (99 is the default).

Order Group Box

The Order group box contains 16 controls that specify the order of the page-control commands. Some printers, such as the Hewlett-Packard printers that use PCL, require a specific ordering of the page-control commands to process pages correctly. Other printers, such as the Diconix, work satisfactorily using the default ordering. The default values begin with 1 in the upper-left corner and proceed down the left column, to the top of the right column, and then down the right column.

Diconix 150 Plus: Use the default values.

HP LaserJet IIP: Requires the unique ordering of the commands as specified in the following list:

  1. PC_ORD_BEGINDOC

  2. PC_ORD_PAPER_SOURCE

  3. PC_ORD_PAPER_SIZE

  4. PC_ORD_ORIENTATION

  5. PC_ORD_RESOLUTION

  6. PC_ORD_MULT_COPIES

  7. PC_ORD_DUPLEX

  8. PC_ORD_DUPLEX_TYPE

  9. PC_ORD_BEGINPAGE

General Page Control Dialog Box

This dialog box is not used in the current implementation of Unitool.


Cursor-Movement Dialog Box

Each printer provides a unique set of commands that control the vertical and horizontal movement of the cursor or print head across the page. These commands are called cursor-movement commands. Some of these commands move the cursor with respect to the current position, or with respect to the page origin, and require information about the distance to be moved. Some of these commands move the cursor a fixed distance. These commands include the carriage return, linefeed, and formfeed. On some printers, the cursor-movement commands are somewhat primitive; for example, horizontal movement can only be achieved by sending a space character from a particular font. On other printers, a single command is available to move the cursor in both horizontal and vertical direction. The coordinate system used by cursor movement commands is based on the origin at the upper-left corner of the page. “Positive” movement is considered to be moving down the page (for y-movement commands), or moving to the right (for x-movement commands).

Select Control Info/CursorMove... from the Printer Data menu to access the Cursor Movement dialog box. The Cursor Movement dialog box contains controls in which you specify the cursor-movement commands and other related information that the Universal Printer Driver requires.

Define the cursor-movement commands in the list box at the top of the Cursor Movement dialog box. Use the scroll bar to scroll through the list boxes and locate the needed commands. When complete, indicate the printer horizontal and vertical movement capabilities using the fXMove and fYMove capability bitwords.

Note For some of the following commands, the corresponding Units dialog box contains values that RasDD uses to calculate the argument(s) for the related command as specified.

CM_OCD_XM_ABS

The CM_OCD_XM_ABS command defines the horizontally cursor movement with respect to the page origin.

Diconix 150 Plus: Does not support this command. The CM_OCD_XM_ABS box is empty.

HP LaserJet IIP: Use the Horizontal Cursor Positioning command (ESC*pnX). The variable n specifies a horizontal distance in 1/300 inch units.

To specify the command, type the following in the CM_OCD_XM_ABS box:

\x1B*p%dX

CM_OCD_XM_REL

The CM_OCD_XM_REL command defines the horizontal cursor movement with respect to the current position. By default, RasDD assumes that the CM_OCD_XM_REL command moves the cursor to the right of the current position.

If your printer uses a command that allows movement to either the left or right of the current position, set the CM_XM_REL_LEFT bit in the CURSORMOVE fXMove dialog box.

Diconix 150 Plus: Does not support this command. The CM_OCD_XM_REL box is empty.

HP LaserJet IIP: Use the Horizontal Cursor Positioning command (ESC*p+nX). The plus sign (+) indicates that the movement is in the positive x-direction relative to the current cursor location. The variable n specifies a distance in 1/300 inch units.

To specify the command, type the following in the CM_OCD_XM_ABS box:

\x1B*p+%dX

CM_OCD_XM_RELLEFT

The CM_OCD_XM_RELLEFT command defines the horizontal cursor movement in the negative x-direction relative to the current cursor position. This command is provided for printers that support different cursor-movement commands.

Diconix 150 Plus: Does not support this command. The CM_OCD_XM_RELLEFT box is empty.

HP LaserJet IIP: Use the Horizontal Cursor Positioning command (ESC*p-nX). The minus sign (-) indicates that the movement is in the negative x-direction relative to the current cursor location. The variable n specifies the distance of movement in 1/300 inch units.

To specify the command, type the following in the CM_OCD_XM_RELLEFT box:

\x1B*p-%dX

CM_OCD_YM_ABS

The CM_OCD_YM_ABS command defines the vertical cursor movement with respect to the page origin.

Diconix 150 Plus: Does not support this command. The CM_OCD_YM_ABS box is empty.

HP LaserJet IIP: Use the Vertical Cursor Positioning command (ESC*pnY). The variable n specifies the distance of movement in 1/300 inch units.

To specify the command, type the following in the CM_OCD_YM_ABS box:

\x1B*p%dY

CM_OCD_YM_REL

The CM_OCD_YM_REL command defines the cursor movement in the positive y-direction with respect to the current position.

Diconix 150 Plus: Use the Immediate Line Feed command (ESCJn) to move the cursor by n units in the positive y-direction with respect to the current cursor location. The variable n specifies the distance of movement in 1/288 inch units.

To specify the command, type the following in the CMD_OCD_YM_REL box:

\x1BJ%c

HP LaserJet IIP: Use the relative Vertical Cursor Positioning command (ESC*p+nY) to move the cursor by n units in the positive y-direction with respect to the current cursor location. The variable n specifies the distance of movement in 1/300 inch units.

To specify the command, type the following in the CMD_OCD_YM_REL box:

\x1B*p+%dY

CM_OCD_YM_RELUP

The CM_OCD_YM_RELUP command defines the cursor movement in the negative y-direction with respect to the current position.

Note The minidriver cannot use the CM_OCD_YM_RELUP cursor movement command with a nonlaser printer.

Diconix 150 Plus: Does not support this command. The CM_OCD_YM_RELUP box is empty.

HP LaserJet IIP: Use the relative Vertical Cursor Positioning command (ESC*p-nY) to move the cursor by n units in the negative y-direction with respect to the current cursor location. The variable n specifies the distance of movement in 1/300 inch units.

To specify the command, type the following into the CMD_OCD_YM_REL box:

\x1B*p-%dY

CM_OCD_YM_LINESPACING

The CM_OCD_YM_LINESPACING command sets the line spacing increment value that occurs at each linefeed.

In addition, click the Units button to display the Units dialog box and add the sMax (maximum) values and sMin (minimum) values for command arguments in 1/288 inch increments.

Diconix 150 Plus: Use the Line Spacing command (ESC3n). The variable n specifies a line spacing value in 1/288 inch increments.

To specify the command, type the following in the CM_OCD_YM_LINESPACING box:

\x1B3%c

HP LaserJet IIP: Does not support this command. The CM_OCD_YM_LINESPACING box is empty.

CM_OCD_XY_REL

(This command is not supported by RasDD.)

CM_OCD_XY_ABS

The CM_OCD_XY_ABS box contains the printer command or commands that move the cursor vertically and horizontally with respect to the page origin. Click the Units button to display the Units dialog box, which contains values that must be added to command arguments, maximum values for command arguments, and minimum values for command arguments. This command is not supported by RasDD.

CM_OCD_CR

The CM_OCD_CR command defines a carriage return.

Diconix 150 Plus: Use the Carriage Return command (0x0D).

To specify the command, type the following in the CM_OCD_CR box:

\x0D

HP LaserJet IIP: Use the Carriage Return command (0x0D).

To specify the command, type the following in the CM_OCD_CR box:

\x0D

CM_OCD_LF

The CM_OCD_LF command defines a linefeed.

Diconix 150 Plus: Use the Line Feed command (0x0A).

To specify the command, type the following in the CM_OCD_LF box:

\x0A

HP LaserJet IIP: Use the Line Feed command (0x0A).

To specify the command, type the following in the CM_OCD_LF box:

\x0A

CM_OCD_FF

The CM_OCD_FF command defines a formfeed.

Diconix 150 Plus: Use the Form Feed command (0x0C).

To specify the command, type the following in the CM_OCD_FF box:

\x0C

HP LaserJet IIP: Use the Form Feed command (0x0C).

To specify the command, type the following in the CM_OCD_FF box:

\x0C

CM_OCD_BS

The CM_OCD_BS command defines a backspace. This command is only required for printers that support device fonts using byte-paired CTTs. For more information about CTTs, see Character-Translation Table.

Diconix 150 Plus: Use the Backspace command (0x08) to move the cursor in the negative x-direction by the width of the current font’s space character.

To specify the command, type the following in the CM_OCD_BS box:

\x08

HP LaserJet IIP: Use the Backspace command (0x08) to move the cursor in the negative x-direction by the width of the current font’s space character.

To specify the command, type the following in the CM_OCD_BS box:

\x08

CM_OCD_UNI_DIR

The CM_OCD_UNI_DIR command enables unidirectional printing.

Note The RES_BO_UNIDIR box in the fBlockOut dialog box must be checked. If this box is not checked, RasDD ignores these commands. The fBlockOut dialog box is contained in the RESOLUTION dialog box.

Diconix 150 Plus: Use the Unidirectional Printing On command (ESCU1).

To specify the command, type the following in the CM_OCD_UNI_DIR box:

\x1BU\x01

HP LaserJet IIP: Does not support this command. The CM_OCD_UNI_DIR box is empty.

CM_OCD_UNI_DIR_OFF

The CM_OCD_UNI_DIR_OFF command disables unidirectional printing.

Note The RES_BO_UNIDIR box in the fBlockOut dialog box must be checked. If this box is not checked, RasDD ignores these commands. The fBlockOut dialog box is contained in the RESOLUTION dialog box.

Diconix 150 Plus: Use the Unidirectional Printing Off command (ESCU0).

To specify the command, type the following in the CM_OCD_UNI_DIR_OFF box:

\x1BU\x00

HP LaserJet IIP: Does not support this command. The CM_OCD_UNI_DIR_OFF box is empty.

CM_OCD_PUSH_POS

The CM_OCD_PUSH_POS command pushes the current position onto the printer’s internal stack.

Diconix 150 Plus: Does not provide an internal stack for printer commands. The CM_OCD_PUSH_POS box is empty.

HP LaserJet IIP: Use the Push Cursor Position command (ESC&f0S).

To specify the command, type the following string in the CM_OCD_PUSH_POS box:

\x1B&f0S

CM_OCD_POP_POS

The CM_OCD_POP_POS command pops the current position off of the printer’s internal stack.

Diconix 150 Plus: Does not provide an internal stack for printer commands. The CM_OCD_POP_POS box is empty.

HP LaserJet IIP: Use the Pop Cursor Position command (ESC&f1S).

To specify the command, type the following in the CM_OCD_POP_POS box:

\x1B&f1S

General Cursor Movement Dialog Box

(Not currently supported.)

Horizontal Movement Dialog Box

(Not currently supported.)

Vertical Movement Dialog Box

Click the fYMove button to display the fYMove from Cursor Movement dialog box.The fYMove dialog box contains controls that you use to specify the vertical cursor-movement capabilities for a printer as follows:
Control | Description
CM_YM_FAV_ABS | RasDD sends absolute rather than relative y-movement commands.
CM_YM_REL_UP | The printer’s relative y-movement command moves the print head in both the positive and the negative y-direction.
CM_YM_NO_POR_GRX | Printer should exit portrait grx-mode before doing y-movement.
CM_YM_NO_LAN_GRX | Printer should exit landscape grx-mode before doing y-movement.
CM_YM_CR | RasDD sends a carriage-return command to the printer before sending any y-movement commands.
CM_YM_LINESPACING | RasDD uses the line spacing command for vertical movement.
CM_YM_TRUNCATE | (not supported in Windows NT)
CM_YM_RES_DEPENDENT | The Y movement command in the CURSORMOVE structure specifies movement in units of the current resolution. (Most commands are in master units, but some devices can only move in terms of the current resolution.)

Diconix 150 Plus: Check the CM_YM_CR box and the CM_YM_LINESPACING box.

HP LaserJet IIP: Check the CM_YM_FAV_ABS box and the CM_YM_NO_LAN_GRX box.


Paper Information

The following topics describe how to specify paper-size, paper-source, and paper-destination commands and data for your printer.


Paper Sizes Dialog Box

A number of sets of paper-size commands can be set up for the minidriver. To specify paper-size commands, select Paper Info/Size... from the Printer Data menu and display the initial Paper Sizes dialog box. Alternately, you can select an existing set of paper-size commands by highlighting the desired set of sizes in the Paper Sizes list box in the minidriver’s Printer Data window (scroll to display the paper sizes set on the list if necessary) and double clicking the selection to display the Paper Sizes dialog box.

The Paper Sizes dialog box specifies printer data, such as whether a particular paper size is driver-defined or predefined, the paper dimensions, the cursor origin, the unprintable-region dimensions, and so on.

The Diconix 150 Plus printer supports two paper sizes. The first is 8.5 x 11 and the second is defined by the user.

The HP LaserJet IIP printer supports the following four paper sizes and four envelope sizes:

  • 8.5 x 11 inches

  • Legal (8.5 x 14 inches)

  • Executive (7.25 x 10.5 inches)

  • A4 (210 x 297 millimeters)

  • Envelope #10 (4 1/8 x 9 1/2 inches)

  • Envelope DL (110 x 220 millimeters)

  • Envelope C5 (162 x 229 millimeters)

  • Envelope C3 (324 x 458 inches)

Note The examples in the following topics are based on the first paper size initialized for each minidriver. In both instances, this is the standard letter size (8.5 x 11) paper.

When you finish entering data in the Paper Sizes dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Predefined (Paper Sizes)

The Predefined button, when set, allows selection of a single paper size from the Name listbox of predefined names supplied by RasDD or the minidriver. Click the adjacent Name list box and select a single paper size. This information will be presented to the user.

Diconix 150 Plus: Select the name: Letter 8.5 x 11 in.

HP LaserJet IIP: Same as Diconix 150 Plus.

Driver Defined (Paper Sizes)

The Driver Defined button identifies a driver defined paper size. If the Driver Defined button is set, enter the name of the paper size in the adjacent Name box. This information will be presented to the user.

Diconix 150 Plus: The paper size is predefined. This button is not selected.

HP LaserJet IIP: Same as Diconix 150 Plus.

X Size (Paper Width)

The X Size control specifies the page width. If the paper size is Predefined, UniTool provides this value. If the paper size is defined by the user, specify this value in master units.

Diconix 150 Plus: The X Size box contains 8160 (8.5 inches x 960 dots per inch).

HP LaserJet IIP: The X Size box contains 2550 (8.5 inches x 300 dots per inch).

Y Size (Paper Height)

The Y Size box control specifies the page height. If the paper size is Predefined, UniTool provides this value. If the paper size is defined by the user, specify this value in master units.

Diconix 150 Plus: The Y Size box contains 6336 (11 inches x 576 dots per inch).

HP LaserJet IIP: The Y Size box contains 3300 (11 inches x 300 dots per inch).

Portrait Mode and Landscape Mode

The Paper Sizes dialog box contains separate group boxes for configuring the printer for portrait and landscape modes. Both group boxes contain a Select box, an Unprintable Regions box, a Cursor Origin box, and Vector Offset box. The following topics describe the boxes in more detail.

Select Paper Size

The Select String box command selects the paper size identified in Name. Laser printers usually require a device command to select a page size.. For nonlaser printers, use a device command to set the page size if the command is available. In either case, if a page size selection command is used, check the PS_EJECTFF box in the fGeneral dialog box.

Diconix 150 Plus: The default paper size is 8.5 x 11 inches. No command is required.

HP LaserJet IIP: These printer-specific commands prevent problems on particular printers as follows:

  • Page Size command (ESC&l2A) selects the 8.5 x 11 inch paper size.

  • Line Spacing command (ESC&l4D) selects a vertical line spacing of four lines per inch.

  • Top Margin command (ESC&l1E) specifies that the top margin is the height of one line.

  • Text Length command (ESC&l42F) specifies that 42 lines per page.

To specify a combination of these commands, type the following in the Select String box:

\x1B&l2a4d1e42F

Unprintable-Region Dimensions

Many printers cannot print text and graphics on some portion of the page. This area of the page is referred to as the unprintable region. For example, most laser printers have an unprintable region that varies with different page sizes. Many narrow carriage dot matrix printers can only print eight inches of text or graphics. Additionally, the use of sheet feeders on many nonlaser printers can introduce an unprintable region on the top and/or bottom of the page.

To obtain correct output for the different combinations of printer models, paper sizes, and paper sources, the minidriver must specify information about the size and placement of the unprintable regions of a printer.

For more information about limitations imposed by a specific printer model, see Model Data.

For more information about limitations imposed by a paper-feed method, see Paper Sources Dialog Box.

The Unprintable Regions control specifies the size of the unprintable regions at the top, bottom, left, and right side of a particular paper size. The unprintable region for a paper size is that region where output is clipped as a result of physical limitations imposed by the printer’s hardware.

To generally determine the location of the printer’s physical boundaries:

  1. Create an ASCII text file using a line width that exceeds the paper width and a page length that exceeds the paper height.

  2. Copy the file to the printer.

  3. Measure the distance from the left edge of the paper to the left edge of the text.

  4. Measure the distance from the right edge of the paper to the right edge of the text.

  5. Measure the distance from the top of the paper to the first line of text.

  6. Measure the distance from the bottom of the paper to the last line of text.

To determine the unprintable region:

  1. Convert the four values obtained above into master units.

  2. Enter these values in the unprintable-region controls.

After your minidriver is installed, test the accuracy of the values that you entered in the Unprintable Regions boxes. To do this, use PRNTTEST.EXE, which is included in the Printer Compatibility Test Suite, and run the printable region test. The other method requires a Windows graphics or desktop publishing package as follows:

  1. Open a Windows graphics or desktop publishing application that renders straight lines around the entire length and width of the page.

  2. Render four lines: two horizontal lines and two vertical lines as follows:

    • Top horizontal line one inch below the top of the page

    • Bottom horizontal line one inch above the bottom of the page

    • Left vertical line one inch from the left edge of the page

    • Right vertical line one inch from the right edge of the page

  3. Print the page.

  4. Examine the intersections of the vertical and horizontal lines.

The lines in the upper-left corner must intersect one inch below the top of the page and one inch from the left edge of the page. If not, adjust the cursor origin. For example, if the horizontal line at the top of the page is 1.125 inches from the top, and the horizontal line at the bottom of the page is .875 inches from the bottom, increment the Y cursor origin by .125 inches.

Note If the cursor origin is set in the Paper Sizes dialog box, check the MD_USE_CURSOR_ORIG box in the fGeneral dialog box of the ModelData dialog box.)

Diconix 150 Plus: The hardware limits the printable page width to 6.875 inches. Set the left, right, top, and bottom unprintable regions to 0. The size of the unprintable top and bottom margins can vary depending on the paper source. The sizes of the unprintable top and bottom regions are expressed in the paper-source data structure.

Because this limit is imposed by the printer model, not by the paper size, list the Page Size Limit (width) in the ModelData data structure and not in the PAPERSIZE data structure. (This same limit applies to any paper size and is not specific to letter-sized paper.)

HP LaserJet IIP: The hardware limits the printable-page width to 8.047 inches. This means that the left and right unprintable regions on this printer measure ((8.5 - 8.047)/2) inches or .2267 inches. Converted to horizontal-master units, this value is expressed as (.21875 * 300) or 68. Enter 68 in the Left and Right boxes of the Unprintable Regions control.

The printer hardware also limits the printable page height to 10.995 inches. This means that the top and bottom unprintable regions on this printer measure ((11 - 10.5)/2) inches or .25 inches. Converted to vertical master units, this value is expressed as (.25 * 300) or 75. Enter 75 in the Top and Bottom boxes of the Unprintable Regions control.

Cursor Origin

(Not currently supported; values should be set to zero.)

Vector Offset

(Not currently supported; values should be set to zero.)

Page Protection

The Page Protection commands enable or disable page protection. The KB Required box specifies the amount of memory, in kilobytes, to allocate for page protection when protection is enabled.

Diconix 150 Plus: Does not support page protection. The Enable and Disable boxes are empty, and the KB Required box contains zero.

HP LaserJet IIP: Same as Diconix 150 Plus.

wYSizeUnit

(Not supported by Windows NT.)

General Paper-Sizes Dialog Box

Click the fGeneral button to display the fGeneral dialog box. The fGeneral dialog box contains the following check boxes:
Control | Description
PS_CENTER | RasDD centers the printable region on the page.
PS_ROTATE | RasDD swaps the x- and y- paper-size dimensions. If this control is set, RasDD does not swap the margin widths.
PS_EJECTFF | RasDD can eject a page of this paper size using the form-feed command instead of y-movement commands.

Diconix 150 Plus: None of the boxes are checked.

HP LaserJet IIP: Same as Diconix 150 Plus.

Paper Type Dialog Box

Click the fPaperType button to display the fPaperType dialog box. The fPaperType dialog box matches paper sizes (legal, letter, and so on) to paper sources (trays, bins, or other paper-feed mechanisms).

Note The corresponding bit must be set in both the PAPERSIZE and PAPERSOURCE data structures for the combination to be valid. The name assigned to each bit is provided for convenience.

The controls in this dialog box specify the paper size that corresponds to the paper source as follows:
Control | Description
PS_T_STD | Standard paper
PS_T_LARGE | Large paper
PS_T_ENV | Envelope
PS_T_LRGENV | Large envelope
PS_T_ROLL | Paper roll

Diconix 150 Plus: Check the PS_T_STD for the 8.5 by 11-inch paper size. This control is also selected for the tractor-feed and sheet-feed paper sources in the Paper Sources dialog box.

HP LaserJet IIP: Check the PS_T_STD is selected for the 8.5 by 11-inch paper size. This control is also selected for the upper paper tray, lower paper tray, and manual feed paper sources in the Paper Sources dialog box.


Paper Quality Dialog Box

(Not currently supported.)


Paper Sources Dialog Box

A number of sets of paper-source commands can be set up for the minidriver. To specify the paper source commands, select Paper Info/Source... from the Printer Data menu on the minidriver’s Printer Data window to access the initial Paper Sources dialog box. Alternately, you can select an existing set of paper source commands by highlighting the desired set of Paper Sources entries to edit on the minidriver’s Printer Data window. Then double click on the selection to display the Paper Sources dialog box.

The Paper Sources dialog box is provided as a means to specify paper sources.

The Diconix 150 Plus printer supports two paper sources as follows:
Constant | Description
DMBIN_TRACTOR | Specifies tractor-feed capabilities.
DMBIN_MANUAL | Specifies manual-feed capabilities.

The HP LaserJet IIP printer supports one of the following paper sources:
Constant | Description
DMBIN_UPPER/ONLY | Specifies upper-paper tray capabilities.
DMBIN_ENVMANUAL | Specifies manual envelope-feed capabilities.
DMBIN_MANUAL | Specifies manual-feed capabilities.
DMBIN_LOWER | Specifies lower-paper tray capabilities.

The examples contained in the following topics are based on the first paper source that was initialized for each minidriver. For the Diconix printer, the first paper source specifies tractor feed. For the HP LaserJet IIP, the first paper source specifies the upper paper tray.

When you finish entering data in the Paper Sources dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Predefined/Driver-Defined Paper Sources

The Predefined and Driver Defined options specify whether the data and commands in the Paper Sources dialog box correspond to predefined or driver-defined paper qualities. These options also specify whether the single paper source description presented to the user is selected from the predefined names supplied by RasDD or by the minidriver.

Diconix 150 Plus: Set the Predefined button

HP LaserJet IIP: Same as Diconix 150 Plus.

Predefined Paper Source

If the Predefined button is set, click the adjacent Source Name dropdown list box and select the name of a single paper source as follows:
Paper Source | Description
DMBIN_UPPER/ONLY | Upper paper tray
DMBIN_LOWER | Lower paper tray
DMBIN_MIDDLE | Middle paper tray
DMBIN_MANUAL | Manual feed
DMBIN_ENVELOPE | Envelope feed
DMBIN_ENVMANUAL | Manual envelope feed
DMBIN_AUTO | Automatic sheet feeder
DMBIN_TRACTOR | Tractor feed
DMBIN_SMALLFMT | Small format paper
DMBIN_LARGEFMT | Large format paper
DMBIN_LARGECAPACITY | Large capacity paper tray
DMBIN_CASSETTE | Paper cassette
DMBIN_ROLL | Paper roll
DMBIN_USER | User-defined paper source

Diconix 150 Plus: Select DMBIN_TRACTOR. The primary paper source is the tractor-feed mechanism.

HP LaserJet IIP: Select DMBIN_UPPER/ONLY. The primary paper source is the upper-paper tray.

Note When DMBIN_UPPER/ONLY is selected, click the fPaperType button to display the Paper Types dialog box and check the appropriate box for the paper size.

Driver-Defined Paper Source

The Driver Defined Source Name textbox is provided for a printer that uses a driver-defined paper source not identified in the list of predefined selections. If the Driver Defined button is set, enter the adjacent Source Name to describe the name of the paper source.

Diconix 150 Plus: The paper source is predefined. This box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

Paper Source Select String

The Select String command selects the paper source identified in the Paper Sources dialog box.

Diconix 150 Plus: The default paper source uses the tractor-feed mechanism and does not require selection.

HP LaserJet IIP: Use the Paper Source command (ESC&l1H) to designates that the upper-paper tray is the paper source.

To specify the command, type the following in the Select String box:

\x1B&l1H

Unprintable Regions

The Unprintable Regions control specifies the height of the unprintable regions at the top and bottom of a page of paper. These values specify the amount of space that becomes unprintable due to the paper-feed mechanism. Typically, sheet feeders are the only type of paper source that introduces an unprintable region. (These values are not the same values entered in the Paper Sizes dialog box.)

Diconix 150 Plus: Use -1 for the Top and Bottom boxes. The paper feed mechanism does not create a unique unprintable region.

HP LaserJet IIP: Same as Diconix 150 Plus.

Bin Adjustment

(Not currently supported)

General Paper-Source Dialog Box

Click the fGeneral button to display the fGeneral from Paper Sources dialog box. The controls in this dialog box specify general information relating to the paper-source selection as follows:
Control | Description
PSRC_EJECTFF | Check the box if the printer ejects a page of paper by sending a form-feed command. The check box is not selected if the printer ejects the page by sending y-movement commands.
PRSC_MAN_PROMPT | Check the box if the user is to be prompted to insert a new piece of paper before printing each page.

Diconix 150 Plus: Check the PSRC_EJECTFF box.

HP LaserJet IIP: Same as Diconix 150 Plus.

Paper Types Dialog Box

Click the fPaperTypes button to display the fPaperTypes from Paper Sources dialog box. This dialog box matches sizes paper sizes (legal, letter, and so on) to paper sources (trays, bins, or other paper feed mechanisms).

The corresponding bit must be set in both the PAPERSOURCE data structure and the PAPERSIZE data structure for the combination to be valid. The name assigned to each bit is provided for convenience.

The four controls in this dialog box specify the paper size according to the paper source as follows:
Control | Description
PS_T_STD | Standard paper
PS_T_LARGE | Large paper
PS_T_ENV | Envelope
PS_T_LRGENV | Large envelope
PS_T_ROLL | Paper roll

Diconix 150 Plus: Check the PS_T_STD box for the 8.5 by 11 inch paper size. (This control is also selected for the tractor feed and sheet feed paper sources in the Paper Sources dialog box.)

HP LaserJet IIP: Same as Diconix 150 Plus.


Paper Destinations Dialog Box

(Not currently supported)


Graphics Information

The following topics describe how you should specify resolution, graphics-data compression, color, and rectangle-fill commands and data for your printer.


Resolution Dialog Box

A number of sets of graphics resolution commands can be set up for the minidriver. To specify graphics resolution commands, select Graphics Info from the Printer Data menu on the minidriver’s Printer Data window. Then select Resolution to display the initial RESOLUTION dialog box.

The RESOLUTION dialog box specifies resolution data such as the text resolution, graphics resolution, color capabilities at a given resolution, methods of sending blocks of graphics data, and so on.

Printers support one or more horizontal and vertical resolutions for raster graphics output. A resolution is an integer value specified in dots-per-inch (dpi), which indicates the number of distinct dots that the print head or inkjet can apply along the x- or the y-axis.

In some instances, the sample Diconix 150 Plus and Hewlett-Packard LaserJet IIP minidrivers support multiple sets of commands and data. For example, both minidrivers contain three RESOLUTION dialog boxes. In this topic, the examples use the first dialog box, which supports the highest graphics resolution.

The Diconix 150 Plus supports the following x-y resolutions:

  • 80 x 96

  • 160 x 96

  • 320 x 96

The HP LaserJet IIP supports the following x-y resolutions:

  • 75 x 75

  • 150 x 150

  • 300 x 300

When you finish entering data in the RESOLUTION dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Display Format

The Display Format box contains the conversion specification for the resolution string. The most common conversion specification is %d x %d; such as for 320 x 96. In cases where two parameters are supplied in the display format string, the x-resolution is enumerated first.

Diconix 150 Plus: The Display Format box contains:

%d x %d

HP LaserJet IIP: The Display Format box contains:

%d dots per inch

Scale Factors

The Scale Factors specify horizontal and vertical scale factors for raster graphics and text output using device fonts. Unlike other values specified in the minidriver, the scale factors are expressed in dpi rather than master units. The values specified must factor evenly into master units.

Text scale factors indicate the resolution for digitizing device fonts. Graphic scale factors describe the resolution required to place individual pixels on the page. Because graphic scale factors are tracked internally as logarithmic ratios to text scale factors, graphic scale factors must either be equal to the text scale factors or equal to a text scale factor divided by a power of 2. For example, a text scale factor of 300 dots per inch can use a graphic scale factor of 300, 150 (300/2), of 75 (300/4), but not a graphic scale factor of 100.

For most laser printers, text and graphics are sent to the printer in separate bands. The scale factors determine the coordinates where each object is placed in a given band on the page. For example, the HP LaserJet IIP supports one of several resolutions with an x and y text scale factor of 300 dpi, and a graphics scale factor of 75 dpi. When using this resolution, text is printed using device fonts that are output at 300 dpi accuracy and raster graphic objects output at 75 dpi accuracy.

For most dot-matrix and inkjet printers, text and graphics are sent to the printer in the same band. The graphic scale factors determine the coordinates for the placement in a given band. For these printers, the text and graphic scale factors are the same. Attempting to set different scale factors does not change output quality and causes Windows to not enumerate TrueType fonts when this resolution is selected.

Note TrueType fonts are not available for those resolutions where the text scale factor does not equal the graphic scale factor unless the driver supports TrueType font downloading. For more information, see Download Information Dialog Box.

Diconix 150 Plus: The text and graphics resolutions are identical. Use 320 for the X DPI boxes and 96 for the Y DPI boxes.

HP LaserJet IIP: The device fonts are digitized at the highest printer resolution (300 dpi). At the highest graphics resolution, use 300 for each Scale Factors box.

Graphics Cursor Dialog Box

Click the fCursor button to display the fCursor dialog box. The fCursor dialog box controls describe how the printer positions the cursor after rendering a block of raster data. (A block of raster data is a row of bits in a bitmap produced by one pass of the print head.) Use any of the following controls as needed:
Control | Description
RES_CUR_X_POS_ORG | Aligns the cursor with the left edge of the original block of raster data after printing a block of data.
RES_CUR_X_POS_AT_0 | Aligning the cursor with the left edge of the printable region (page).
RES_CUR_Y_POS_AUTO | Moves the cursor in the positive y-direction to the start of the next row of data after printing a lock of data.
RES_CUR_CR_GRX_ORG | Resets the left-edge coordinates of a block of graphics data when internal blanks are removed (for HPPCL printers only).

If your printer uses the Hewlett-Packard Printer Control Language (PCL), and you have checked the RES_BO_ENCLOSED_BLNKS box in the fBlockOut dialog box, you should also check the RES_CUR_CR_GRX_ORG box in the fCursor dialog box. (This is because PCL printers redefine the left edge of a block of graphics data when internal blanks are removed. By setting the RES_CUR_CR_GRX_ORG box, you set a flag that informs RasDD that it must reset the left-edge coordinates.)

Diconix 150 Plus: None of the boxes are checked.

HP LaserJet IIP: Check the RES_CUR_X_POS_ORG box , the RES_CUR_Y_POS_AUTO box, and the RES_CUR_GRX_ORG box.

Graphics Dump Data Dialog Box

(Not currently supported.)

Graphics Block Data Dialog Box

Click the fBlockOut button to display the fBlockOut dialog box. the fBlockOut dialog box controls specify how RasDD formats each block of raster-graphic (bitmap) data to be sent to the printer. A block is defined as having a height of n scan lines where n is equal to the sNPins control. For more information, see “Number of Pins” below.

The controls in the BlockOut dialog box specify if RasDD is to perform the following:
Control | Description
RES_BO_LEADING_BLNKS | Strip leading, non-printing graphic data from the beginning of each block of raster data (bitmap) sent to the printer.
RES_BO_TRAILING_BLNKS | Strip trailing, nonprinting graphic data from the end of each block of raster data (bitmap) sent to the printer.
RES_BO_ENCLOSED_BLNKS | Strip enclosed, nonprinting graphic data from within each block of raster data (bitmap) sent to the printer, if the number of bytes of nonprinting raster data exceeds that value listed in the sMinBlankSkip control in the RESOLUTION dialog box.
RES_BO_RESET_FONT | Selected device font needs to be reset after printing grx.
RES_BO_UNIDIR | Enable unidirectional printing using the data in the Cursor Movement dialog box with this resolution. Some printers require this setting for correct horizontal alignment of output.
RES_BO_NO_ADJACENT | Inidcate that vertically adjacent pins on the print head are not fired simultaneously.
RES_BO_OEMGRXFILTER | Minidriver provides a callback function to alter raster graphic data.
RES_BO_MULTIPLE_ROWS | Send multiple scanlines of data with each RES_OCD_SENDBLOCK command; otherwise, the minidriver sends only one at a time. This option affects only laser printers for which sPinsPerPass is set to 1.

Note The term strip raster graphics data means that RasDD substitutes available cursor-movement commands in place of nonprinting raster graphic data prior to sending the data to the printer. This usually results in a performance gain because cursor-movement commands typically require less data when sent to the printer. Most printers benefit from using the RES_BO_TRAILING_BLNKS bit. Due to differences in the resolutions of cursor-movement commands and graphics resolutions, use care with the RES_BO_LEADING_BLNKS and RES_BO_ENCLOSED_BLNKS bits to ensure that horizontal misalignment of graphic output does not occur.

Diconix 150 Plus: Check the RES_BO_TRAILING_BLNKS box. The RES_BO_LEADING_BLNKS and RES_BO_ENCLOSED_BLNKS boxes are not checked because the available methods of moving the cursor do provide a reasonable degree of accuracy.

HP LaserJet IIP: Check the RES_BO_ENCLOSED_BLNKS box and the RES_BO_TRAILING_BLNKS box. The RES_BO_LEADING_BLNKS box is not checked. (The performance of HPPCL minidrivers is significantly reduced when this box is checked and RasDD removes leading blanks from a bitmap.)

Number of Pins

The sNPins box in the RESOLUTION dialog box specifies the minimum height (in pixels) of lines of raster data that RasDD evaluates at one time. This is typically the number of pins or jets used by the printer for each horizontal slice of graphic output produced by one pass of the print head.

Note When the value of sNPins is equal to the value of sPinsPerPass, this is referred to as noninterlaced graphics output.

Diconix 150 Plus: Use 8 for the sNPins box. This is the number of jets used by the printer for noninterlaced graphics. Use 16 for interlaced graphics.

HP LaserJet IIP: Use 1 for the sNPins box.

Interlaced Graphics

In some cases, a higher resolution than the one native to the printer can be created by vertically interlacing graphics (rendering each block of graphics data in two passes) using the sPinsPerPass. The sPinsPerPass control is a vertical cursor-movement command that uses a resolution at least twice as fine as the native vertical-graphics resolution. RasDD accomplishes this by sending the first block of raster data as normal. During the second pass, the printhead position is incremented in the y-direction by a value that is less than or equal to the height of a single pin or jet. To render bitmaps correctly using interlaced graphics, RasDD requires the number of pins to be fired by the printer as defined in the sNPins box. If the height of a block of graphics data is less than the height of the specified pins, RasDD adds null graphics data to the block.

For example, many 9-pin dot-matrix drivers typically support a 240 x 72 dpi resolution as their highest native mode. These printers also have a vertical-movement command that operates in 1/144 inch. RasDD can support 240 x 144 dpi resolutions if the driver sets sPinsPerPass to 8 (the number of pins fired in one pass), sets sNPins to 16 (the number of pins fired in two passes), and supports the 1/144-inch vertical command.

Diconix 150 Plus: Use 8 for the sPinsPerPass box. This is the number of jets used by the printer for noninterlaced graphics.

HP LaserJet IIP: Use 1 for the sPinsPerPass box.

Vertical Text Offset

The sTextYOffset box specifies the difference between the baseline of printer fonts and the baseline of GDI fonts. If the baseline of device fonts differs from the baseline of GDI fonts and an application mixes the two fonts on a line, the text appears uneven. To prevent this, RasDD specifies the difference between the two baselines in master units.

Diconix 150 Plus: Use 0 for the sTextYOffset box . The baseline of printer fonts is identical to the baseline of GDI fonts.

HP LaserJet IIP: Same as Diconix 150 Plus.

Minimum Number of Blanks

The sMinBlankSkip box specifies the minimum number of bytes of null data (blanks) that must occur before RasDD begins removing embedded blanks from the block of bitmap data. If RasDD removes most of the null raster data sent to the printer to reduce data transmission, printing and performance time can increase. For example, if this value is too small due to printhead movement when switching from graphics mode to cursor movement commands. Dot-matrix minidrivers are recommended to use values no smaller than the number of bytes of raster data required for two inches of output.

Diconix 150 Plus: Use 0 for the sMinBlankSip box.

HP LaserJet IIP: Use 32 for the sMinBlankSkip box . In this case, RasDD strips blank data from a bitmap unless there are at least 32 consecutive null bytes.

Spot Diameter

The sSpotDiameter box specifies a special value RasDD uses to rasterize graphic data and compensate for overlapping pixels. The value entered affects the lightness or darkness of printed raster-graphic images. The spot diameters for common resolutions supported by 9-pin, 24-pin, and ink-jet printers are as follows:
Printer | Resolution | Diameter
9-pin | 240 x 144 | 170
 | 120 x 144 | 140
 | 120 x 100 | 100
24-pin | 360 x 180 | 170
 | 180 x 180 | 140
 | 120 x 180 | 120
Ink-Jet | 360 x 360 | 100
 | 180 x 180 | 100
 | 120 x 180 | 100

Diconix 150 Plus: Use 100 for the sSpotDiameter box.

HP LaserJet IIP: Same as Diconix 150 Plus.

Default Dither Brush

The Default Dither Brush box specifies the default dithering technique for the selected resolution. Select the technique that looks the best for the given printer model and resolution. These choices are available:
Technique | Description
None | No dithering.
Coarse | The dither pattern is clustered.
Fine | The dither pattern is evenly dispersed.
LineArt | The dither pattern is optimized for color graphics images with sharp separation. This technique is not recommended as the default, and it is not supported for color graphics.

Diconix 150 Plus: The Coarse button is selected.

HP LaserJet IIP: The Fine button is selected.

Select Resolution

The Select Resolution command selects the designated printer resolution.

Diconix 150 Plus: Integrates the selection of the resolution with the Send Block command. This box is empty.

HP LaserJet IIP: Use the Raster Graphics Resolution command (ESC*tnR) to select the resolution for graphics output where n specifies a resolution of 75, 100, 150, or 300 dpi.

To specify the command for 300 dpi graphics, type the following in the Select Resolution box:

\x1B*t300R

Begin Graphics

The Begin Graphics command is sent to the printer to enable graphics output. (Printers that provide a specific command to select a graphics resolution can also provide a command to enter graphics mode.)

Diconix 150 Plus: Automatically begins rendering graphics when a Send Block command is received. The Begin Graphics box is empty.

HP LaserJet IIP: Use the Start Raster Graphics command (ESC*rnA) to signal the start of raster graphics and specify the location of the left margin. If n is 1, the current cursor location defines the left margin for the graphics output; if n is 0, the left edge of the page defines the left margin for the graphics output.

To specify the command using the current cursor location as the left margin, type the following in the Begin Graphics box:

\x1B*r1A

End Graphics

The End Graphics command is sent to the printer to disable graphics output. (Printers that provide a specific command to disable a graphics resolution can also provide a command to end the processing of graphics data.)

Diconix 150 Plus: Automatically ends graphics output after processing the last byte of graphics data. This control is not initialized.

HP LaserJet IIP: Use the End Raster Graphics command (ESC*rB) to disable raster graphics output.

To specify the command, type the following in the End Graphics box:

\x1B*rB

End Block

The End Block command terminates or ends the processing of a block of graphics data.

Diconix 150 Plus: Stops processing graphics data at the conclusion of the command specified in the Send Block box. The End Block box is empty.

HP LaserJet IIP: Processes a single block (or row) of data at a time. A command to terminate the processing of graphics data is not required. The End Block box is empty.

Send Block

The Send Block command processes or sends one or more blocks (or rows) of graphics data.

Diconix 150 Plus: Use the Quadruple-Density 8-Bit Graphics command (ESCZn1n2d1...dn) to process multiple blocks (that is, an entire bitmap) at the 320 x 96 dpi resolution. The arguments n1 and n2 specify the total number of bytes of graphics data: n1 is the remainder when this number is divided by 256, and n2 is the quotient. The arguments d1 thru dn represent the actual bytes of graphics data.

To specify the command, type the following in the Send Block box:

\x1BZ%l

HP LaserJet IIP: Use the Transfer Raster Data command (ESC*bnW) to process a single block of raster data at the specified resolution. The argument n specifies the count of bytes of raster data.

To specify the command, type the following in the Send Block box:

\x1B%dW


Compression Dialog Box

A number of sets of graphics compression commands can be set up for the minidriver. To specify graphics compression commands, select Graphics Info from the Printer Data menu on the minidriver’s Printer Data window. Then select Compression to display the initial Compression dialog box.

To select an existing set of graphics compression commands, highlight the desired set of Compression commands to edit on the minidriver’s Printer Data window. Then, double click on the selection to display the Compression Modes dialog box.

Some printers can receive raster graphic data in a compressed format that typically results in increased performance. The increased performance is due to the reduced amount of data sent to the printer to print bitmaps. If your printer supports compression of graphics data (bitmaps) using run-length encoding (RLE), or Tag Information File Format (TIFF) version 4.0, use the Compression Modes dialog box.

The Diconix 150 Plus printer does not support data compression. The HP LaserJet IIP printer supports both run-length encoding and TIFF. The HP LaserJet examples are based on the dialog box that contains commands for TIFF encoding.

When you finish entering data in the Compression dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Compression Type

Click the Compression Type box and select the type of compressionas follows:

  • Run Length Encoding

  • Tiff Encoding (4.0)

(Delta Row compressions is not currently supported)

Diconix 150 Plus: Does not support compression. The Compression Type box is empty.

HP LaserJet IIP: Supports RLE as specified in the Compression Type box.

Begin Compression

The command in the Begin textbox enables the compression of graphics data.

Diconix 150 Plus: The Begin box is empty.

HP LaserJet IIP: Use the Set Compression Mode command (ESC*bnM) to enable and disable the various types of graphics compression. If n is 0, graphics compression is disabled; if n is 1, run-length encoding is enabled; if n is 2, TIFF encoding is enabled; and if n is 3, delta-row compression is enabled.

To specify the command to enable run-length encoding, type the following in the Begin box:

\x1B*b1M

End Compression

The command in the End listbox disables the compression of graphics data.

Diconix 150 Plus: The End box is empty.

HP LaserJet IIP: Use the Set Compression Mode command (ESC*bnM) to enable and disable various types of graphics compression. If n is 0, graphics compression is disabled; if n is 1, run-length encoding is enabled; if n is 2, TIFF encoding is enabled; and if n is 3, delta-row compression is enabled.

To specify the command to disable TIFF encoding, type the following in the Begin box:

\x1B*b0M


Color Models Dialog Box

A number of sets of color commands can be set up for the minidriver. To specify color commands, select Graphics Info from the Printer Data menu on the minidriver’s Printer Data window. Then select Color to display the initial Color Models dialog box.

To select an existing set of color commands, highlight the desired set of Color Models commands to edit on the minidriver’s Printer Data window. Then, double click on the selection to display the Color Models dialog box.

Most printers support one of two color methods: planar or pixel. Devices that use the pixel method send all of the RGB data for a single pixel at once, typically using at least two bytes per single pixel. RasDD does not support color on printers that require color-raster graphic data using the pixel method.

Printers that use the planar method send the color data for a single pixel in several passes (planes). The value that results from combining the data for one pixel in each plane determines the color that pixel assumes. Devices that use the planar method typically use three or four color planes. A three-plane model represents cyan data in one plane, yellow data in a second plane, and magenta data in a third plane. By combining inks from these three basic colors (cyan, yellow, and magenta), the device can render images of up to eight colors. A four-plane model is identical to the three-plane model except for the addition of a fourth plane that represents black data. (In the three-plane model, cyan, yellow, and magenta ink is combined in equal parts to create black output.)

Until recently, most color printers used planar models to represent color images. For this reason, the current version of the DMCOLOR library and the UniTool application support this model (rather than the pixel model). You can provide a callback function to convert planar color data into pixel color data. In some instances, the converted data does not take advantage of a printer’s color-output capabilities. For example, DMCOLOR allows a maximum of eight colors (cyan, blue, magenta, red, yellow, green, white, and black). This means that DMCOLOR converts any 256-color pixel image into an 8-color planar image. If a printer supports 256 colors, it can not display the original image, even if the minidriver provides the callback function. Obviously, some printers are better supported by the original Windows driver interface.

The Color Models dialog box controls specify the color capabilities of printers that render color output. For example, these controls specify values such as the number of color planes supported by printers that use a color-plane format, the number of bits per pixel supported by printers that use a bit-per-pixel format, the device commands that select colors, and so on.

Note Because neither the Diconix 150 Plus nor the HP LaserJet IIP printers provide color support, this section contains information for an additional printer, the Hewlett-Packard PaintJet.

When you finish entering data in the Color Models dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Number of Color Planes

The sPlanes box specifies the number of color planes supported by the printer.

HP PaintJet: Use 3 for the sPlanes box.

Number of Bits per Pixel

The sBitsPixel box specifies the number of bits of data required for each color pixel.

HP PaintJet: Use 1 for the sBitsPixel box. One bit in each plane corresponds to a single pixel.

General Color Data Dialog Box

Click the fGeneral button to display the fGeneral dialog box. This dialog box contains boxes that specify general color capabilities for a printer as follows:
Control | Description
DC_PRIMARY_RGB | If this bit is set, sPlanes must equal three with RasDD. RasDD first sends data for the red plane, followed by the blue and green planes. The printer then converts this data into an index into its color palette to obtain the eight basic colors. If this bit is not set, RasDD sends black data first (only if the DC_EXTRACT_BLACK bit is set, followed by data for the cyan, magenta and yellow planes.)
DC_EXTRACT_BLK | If this bit is set, sPlanes must equal four. This control specifies that the printer renders black using a separate ink or ribbon. If this control is not set, RasDD assumes that the printer renders black by mixing the other colors.
DC_SEND_CR | RasDD sends a carriage return to clear the output buffer before processing the next plane of color-raster data.
DC_SEND_ALL_PLANES | RasDD sends all color plane data to the printer; otherwise, the driver may send only non-null data. This flag applies only to color-plane devices.
DC_SEND_PAGE_FRAME | RasDD sends color plane data one page at a time. This flag applies only to color-plane devices.
DC_EXPLICIT_COLOR | RasDD sends a command to select a given color; the selection is not implied by the RES_OCD_SENDBLOCK command. This flag applies only to color-plane devices.
DC_SEND_PALETTE | RasDD downloads the color palette. This flag applies only to color plane devices.

HP PaintJet: Check the DC_PRIMARY_RGB box because the printer uses the RGB color model.

Color-Planes Selection

The Color Planes control specifies the commands that render the color graphics output. Select a plane from the Plane # box, enter the command for rendering that plane in the Command box, and select an identifier for the plane from the Plane ID box.

HP PaintJet: The Command box for planes 1 and 2 contain the Transfer Raster Data by Plane command (ESC*bnV) where n specifies the number of bytes of raster data.

To specify the command, type the following in the Command box for planes 1 and 2:

\x1B*b%dV

The Command box for the plane 3 contains the Transfer Raster Data by Row command (ESC*bnW). This command differs from the previous command in that it moves the current active position (CAP) to the next row of data.

To specify the command, type the following in the Command box for plane 3:

\x1B*b%dW

Because the PaintJet printer uses three-plane technology, the Command box for plane 4 is empty.

Color Plane Identifier

The Plane ID box indicates the order in which the color plane data is sent to the printer.

HP PaintJet: Contains DC_PLANE_RED for plane 1, DC_PLANE_BLUE for plane 2 blue, and DC_PLANE_GREEN for plane 3.

Note If the DC_PRIMARY_RGB bit is set in the fGeneral dialog box, RasDD uses the device commands to send a color plane of graphics data instead of sending block commands from the resolution section when color is enabled.

DC_OCD_TC_XXX Text-Color Commands

The Commands controls specify commands that render the color text output. The Commands controls includes the following:
Command | Description
DC_OCD_TC_BLACK | Sets the text color to black.
DC_OCD_TC_RED | Sets the text color to red.
DC_OCD_TC_GREEN | Sets the text color to green.
DC_OCD_TC_YELLOW | Sets the text color to yellow.
DC_OCD_TC_BLUE | Sets the text color to blue.
DC_OCD_TC_MAGENTA | Sets the text color to magenta.
DC_OCD_TC_CYAN | Sets the text color to cyan.
DC_OCD_TC_WHITE | Sets the text color to white.
DC_OCD_SETCOLORMODE | Selects the printer model. This command is required only for pixel color models.
DC_OCD_PC_SELECTINDEX | Selects the palette index to be used as the foreground color for text.

HP PaintJet: The DC_OCD_TC_BLACK, DC_OCD_TC_RED, DC_OCD_TC_GREEN, and DC_OCD_TC_YELLOW boxes contain the corresponding Text Color command (ESC&vnS) that renders text using the specified color. If n is 0, the text color is black; if n is 1, the text color is red; if n is 2, the text color is green; if n is 3, the text color is yellow, and so on.

To select black text, type the following command in the DC_OCD_TC_BLACK box:

\x1B&v0S

To select red text, type the following command in the DC_OCD_TC_RED box:

\x1B&v1S

To select green text, type the following command in the DC_OCD_TC_GREEEN box:

\x1B&v2S

To select yellow text, type the following command in the DC_OCD_TC_YELLOW box:

\x1B&v3S

To select blue text, type the following command in the DC_OCD_TC_BLUE box:

\x1B&v4S

To select magenta text, type the following command in the DC_OCD_TC_MAGENTA box:

\x1B&v5S

To select cyan text, type the following command in the DC_OCD_TC_CYAN box:

\x1B&v6S

To select white text, type the following command in the DC_OCD_TC_WHITE box:

\x1B&v7S

DC_OCD_SETCOLORMODE

The DC_OCD_SETCOLORMODE command initializes a printer to process color data.

HP PaintJet: Use the Set Number of Planes Per Row command (ESC*rnU) to initialize the printer for color data processing. The argument n specifies the number of planes of color data.

To specify the command, type the following in the DC_OCD_SETCOLORMODE box:

\x1B*r3U


Rectangle Fill Dialog Box

A number of sets of rectangle-fill commands can be set up for the minidriver. To specify rectangle-fill commands, select Graphics Info from the Printer Data menu on the minidriver’s Printer Data window. Then select RectFill to display the initial Rectangle Fill dialog box.

To select an existing set of rectangle-fill commands, highlight the desired set of Rectangle Rill commands to edit on the minidriver’s Printer Data window. Then, double click on the selection to display the Rectangle Fill dialog box.

The Rectangle Fill dialog box controls specify rectangle-fill data and commands for printers with rectangle-fill capabilities. Typically, the only printers that provide these capabilities are printers that support a page-control language such as the HPPCL. The data supplied in the Rectangle Fill dialog box describes the printer’s minimum and maximum gray levels, the cursor position at the conclusion of a rectangle-fill operation, and so on. The commands supplied in the Rectangle Fill dialog box specify the rectangle’s dimensions and fill pattern.

When you finish entering data in the Rectangle Fill dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Minimum Gray Level

The wMinGray box contains the minimum percentage value that RasDD can pass as an argument in the printer’s gray-fill command.

Diconix 150 Plus: The wMinGray box is empty.

HP LaserJet IIP: Use 1 for the minimum percentage of black pixels. (This means that there is 1 black pixel for every 99 white pixels in the minimum shade or fill pattern.)

Maximum Gray Level

The wMaxGray box contains the maximum value that RasDD can pass as an argument in the printer’s gray-fill command.

Diconix 150 Plus: The wMaxGray box is empty.

HP LaserJet IIP: Use 100 for the maximum percentage of black pixels. (This means that there are 100 black pixels and 0 white pixels in the maximum shade or fill pattern.)

General Rectangle-Fill Dialog Box

Click the fGeneral button to display the fGeneral dialog box. The fGeneral dialog box controls specify general rectangle-fill data as follows:
Box | Description
RF_WHITE_ABLE | (not supported by Windows NT)
RF_MIN_IS_WHITE | (not supported by Windows NT)
RF_CUR_X_END | The cursor is aligned with the right edge of the rectangle after a rectangle-fill operation. If this box is not checked, the cursor is positioned at the rectangle origin after the fill operation.
RF_CUR_Y_END | The cursor is aligned with the bottom edge of the rectangle after a rectangle-fill operation. If this box is not checked, the cursor is positioned at the rectangle origin after the fill operation.

Diconix 150 Plus: None of the boxes are checked.

HP LaserJet IIP: Check the RF_WHITE_ABLE box to indicate that the HP LaserJet IIP can use the White-fill command.

Rectangle Width

The X Size command that sets the rectangle width.

Diconix 150 Plus: The X Size box is empty.

HP LaserJet IIP: Use the Horizontal Rectangle Size command (ESC*cnA) to specify the rectangle’s width in 1/300 inch units. The argument n specifies the number of units.

To specify the command, type the following in the X Size box:

\x1B*c%dA

Rectangle Height

The Y Size command sets the rectangle height.

Diconix 150 Plus: The Y Size box is empty.

HP LaserJet IIP: Use the Vertical Rectangle Size command (ESC*cnB) to specify the rectangle’s height in 1/300 inch units. The argument n specifies the number of units.

To specify the command, type the following in the Y Size box:

\x1B*c%dB

Gray Fill

The Gray Fill command fills a rectangle with a shade of gray.

Diconix 150 Plus: The Gray Fill box is empty.

HP LaserJet IIP: The Area Fill ID and the Fill Rectangular Area commands are combined into a single command. The Area Fill ID command (ESC*cnG) specifies the shade of gray, and the Fill Rectangular Area command (ESC*cnP) fills a rectangular area.

For the Area Fill ID command, n specifies the percent of shading. For the Fill Rectangular Area command, n specifies the type of fill and can be one of these values:
Value | Fill
0 | Black
1 | White
2 | Shading
3 | Cross-hatch
5 | Current pattern

To specify the combination, type the following in the Gray Fill box:

\x1B*c%dG2P

White Fill

(not supported by Windows NT)

Hatch Fill

(not supported by Windows NT)


Image Control Dialog Box

(Not currently supported)


Print Density Dialog Box

(Not currently supported)


Font Information

The following topics describe how to specify font information for your printer, including the Printer Font Metric (PFM) file, the Character-Translation Table (CTT), the font-cartridge commands and data, the font-simulation commands and data, and the downloadable-font commands and data.


Text Qualities Dialog Box

This feature is not currently supported.


Font Cartridges Dialog Box

A number of font cartridges can be set up for the minidriver. To specify font cartridges for the minidriver, select Font Info from the Printer Data menu on the minidriver’s Printer Data window. Then select FontCart to display the initial Font Cartridge dialog box.

To select an existing font cartridge, highlight the desired set of Font Cartridges commands to edit on the minidriver’s Printer Data window. Then double click on the selection to display the Font Cartridge dialog box.

Some printers provide a slot for removable cartridges that contain device fonts. The Font Cartridge dialog box contains controls that you use to identify supported cartridges and the fonts that each contains.

Note The examples for the HP LaserJet IIP printer assume the printer uses the Microsoft 1A cartridge, one of 33 cartridges supported by the HP LaserJet printer.

Note The fGeneral button on the Font Cartridge dialog box is not in use at this time and does not require entry.

When you finish entering data in the Font Cartridge dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Name of Cartridge

The Name of Cartridge box specifies the selected font-cartridge name.

HP LaserJet IIP: Contains the string, “Z: Microsoft 1A”, which identifies the Microsoft 1A font cartridge.

Portrait Fonts

The Portrait Fonts box specifies the names of portrait fonts supported by a cartridge. Unitool copies the names of all the PFM font files used by the minidriver into this control. Specify which fonts are supported on a particular cartridge by selecting the correct names with the cursor and clicking the mouse button to highlight the selection.

HP LaserJet IIP: Sixteen of the 303 device fonts are supported on the Microsoft 1A cartridge. The strings that identify these fonts (numbered 184 - 191 and 193 - 200) are highlighted.

Landscape Fonts

The LandScape Fonts box specifies the names of landscape fonts supported by a cartridge. Unitool copies the names of all the PFM font files used by the minidriver into this control. Specify which fonts are supported on a particular cartridge by selecting the correct names with the cursor and clicking the mouse button to highlight the selection.

HP LaserJet IIP: One of the 303 device fonts is supported in landscape mode on the Microsoft 1A cartridge. The string that identifies this font (numbered 192) is highlighted.


Font Simulation Dialog Box

A number of sets of font simulation commands can be set up for the minidriver. To specify font simulation commands, select Font Info from the Printer Data menu on the minidriver’s Printer Data window. Then select FontSim to display the initial Font Simulation dialog box.

To select an existing set of font simulation commands, highlight the desired set of Font Simulation commands to edit on the minidriver’s Printer Data window. Then double click on the selection to display the Font Simulation dialog box.

A font-simulation is a capability of simulating italic, bold, underlined, strikeout, or white text from a normal device font. The Font Simulation dialog box identifies the font-simulation commands and data that your printer supports. Most printers provide at least some font simulations for device fonts. The font-simulation commands described in this topic are assumed to be applicable to all device fonts. There are two exceptions:

  • A separate PFM file for fonts is defined in the minidriver.

  • The fGeneral dialog box in the PFM file inhibits font simulations.

All font simulation is performed by the Windows NT GDI. As a result, none of the font simulation, except FS_OCD_WHITE_TEXT_ON and FS_OCD_WHITE_TEXT_OFF are in use.

To enable the bold, italic, strikeout, and white font simulations for a single printer model, set the corresponding bits in the fGeneral dialog box in the MODELDATA data structure for each printer model.

The fGeneral button on the Font Simulation dialog box is not in use at this time and does not require entry.

When you finish entering data in the Font Simulation dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

FS_OCD_BOLD_ON

The FS_OCD_BOLD_ON command enables bold simulations for a printer.

Diconix 150 Plus: Use the Emphasized Mode On command (ESCE).

To specify the command, type the following in the FS_OCD_BOLD_ON box:

\x1BE

HP LaserJet IIP: Use the Stroke Weight command (ESC(snB) to specify the thickness of the strokes that compose a character. If n is -3, the stroke is thin; if n is 0, the stroke is normal; if n is 3, the stroke is bold.

Note When the TC_EA_DOUBLE box in the fText dialog box of the Model Data dialog box is not checked, RasDD does not perform bold simulations. The FS_OCD_BOLD_ON command is used to override the TC_EA_DOUBLE check box.

To specify the command that enables bold output, type the following in the FS_OCD_BOLD_ON box:

\x1B(s3B

FS_OCD_BOLD_OFF

The FS_OCD_BOLD_OFF command disables bold simulations for a printer.

Diconix 150 Plus: Use the Emphasized Mode Off command (ESCF).

To specify the command, type the following in the FS_OCD_BOLD_OFF box:

\x1BF

HP LaserJet IIP: Use the Stroke Weight command (ESC(snB) to specify the thickness of the strokes that compose a character. If n is -3, the stroke is thin; if n is 0, the stroke is normal; if n is 3, the stroke is bold.

To specify the command that disables bold output, type the following in the FS_OCD_BOLD_OFF box:

\x1B(s0B

FS_OCD_ITALIC_ON

The FS_OCD_ITALIC_ON command enables italic simulations for a printer.

Diconix 150 Plus: Use the Italic Mode On command (ESC4) to enable italic simulations.

To specify the command, type the following in the FS_OCD_ITALIC_ON box:

\x1B4

HP LaserJet IIP: Use the font-style command (ESC(snS). If n is 1, the characters are italicized; if n is 0, the characters are plain text.

Note When the TC_IA_ABLE box in the fText dialog box of the Model Data dialog box is not checked, RasDD does not perform italic simulations. The FS_OCD_ITALIC_ON command is used to override the TC_IA_ABLE check box.

To specify the command, type the following in the FS_OCD_ITALIC_ON box:

\x1B(s1S

FS_OCD_ITALIC_OFF

The FS_OCD_ITALIC_OFF command disables italic simulations for a printer.

Diconix 150 Plus: Use the Italic Mode Off command (ESC5).

To specify the command, type the following in the FS_OCD_ITALIC_OFF box:

\x1B5

HP LaserJet IIP: Use the font-style command (ESC(snS) to specify if the font is italicized or plain text. If n is 1, the characters are italicized; if n is 0, they are plain text.

To specify plain text, type the following in the FS_OCD_ITALIC_OFF box:

\x1B(s0S

FS_OCD_UNDERLINE_ON

The FS_OCD_UNDERLINE_ON command enables underline simulations for a printer.

Diconix 150 Plus: Use the Underline Mode command (ESC-n) to enable and disable the underline font simulation. If n is 1, the font simulation is enabled; if n is 0, the font simulation is disabled.

To specify the command, type the following in the FS_OCD_UNDERLINE_ON box:

\x1B-\x01

HP LaserJet IIP: Use the Underline command (ESC&dnD) to enable fixed- and floating-position underlining. If n is 0, fixed-position underlining is enabled; if n is 1, floating-position underlining is enabled. RasDD performs underline simulations.

To specify the command, type the following in the FS_OCD_UNDERLINE_ON box:

\x1B&dD

FS_OCD_UNDERLINE_OFF

The FS_OCD_UNDERLINE_OFF command disables underline simulations for a printer.

Diconix 150 Plus: Use the Underline Mode command (ESC-n) to enable and disable the underline font simulation. If n is 1, the font simulation is enabled; if n is 0, the font simulation is disabled.

To specify the command, type the following in the FS_OCD_UNDERLINE_OFF box:

\x1B-\x00

HP LaserJet IIP: Use the Disable Underline command (ESC&d@) to disable fixed- and floating-position underlining.

To specify the command, type the following in the FS_OCD_UNDERLINE_OFF box:

\x1B&d@

FS_OCD_DOUBLEUNDERLINE_ON

The FS_OCD_DOUBLEUNDERLINE_ON command enables double-underline simulations for a printer.

Diconix 150 Plus: Does not support double-underlining. The FS_OCD_DOUBLEUNDERLINE_ON box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

FS_OCD_DOUBLEUNDERLINE_OFF

The FS_OCD_DOUBLEUNDERLINE_OFF command disables double-underline simulations for a printer.

Diconix 150 Plus: Des not support double-underlining. The FS_OCD_DOUBLEUNDERLINE_OFF box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

FS_OCD_STRIKETHRU_ON

The FS_OCD_STRIKETHRU_ON command enables the strikethrough simulations for a printer.

Diconix 150 Plus: Does not support strikethrough text. The FS_OCD_STRIKETHRU_ON box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

FS_OCD_STRIKETHRU_OFF

The FS_OCD_STRIKETHRU_OFF command disables the strikethrough simulations for a printer.

Diconix 150 Plus: Does not support strikethrough text. The FS_OCD_STRIKETHRU_OFF box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

FS_OCD_WHITE_TEXT_ON

The FS_OCD_WHITE_TEXT_ON command enables the white text simulations for a printer.

Diconix 150 Plus: Does not support white text. The FS_OCD_WHITE_TEXT_ON box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

FS_OCD_WHITE_TEXT_OFF

The FS_OCD_WHITE_TEXT_OFF box command disables the white text simulations for a printer.

Diconix 150 Plus: Does not support white text. The FS_OCD_WHITE_TEXT_OFF box is empty.

HP LaserJet IIP: Same as Diconix 150 Plus.

FS_OCD_SINGLE_BYTE

The FS_OCD_SINGLE_BYTE command ends KANJI mode.

FS_OCD_DOUBLE_BYTE

The FS_OCD_DOUBLE_BYTE command starts KANJI mode.

FS_OCD_VERT_ON

The FS_OCD_VERT_ON command starts KANJI vertical mode.

FS_OCD_VERT_OFF

The FS_OCD_VERT_OFF command ends KANJI vertical mode.

sBoldExtra

(not supported by Windows NT)

sItalicExtra

(not supported by Windows NT)

sBoldItalicExtra

(not supported by Windows NT)


Download Information Dialog Box

A number of sets of downloadable font commands can be set up for the minidriver. To specify downloadable font commands, select Font Info from the Printer Data menu on the minidriver’s Printer Data window. Then select DownloadInfo to display the initial Download Info dialog box.

To select an existing set of downloadable font commands, highlight the desired set of DownLoad Info commands to edit on the minidriver’s Printer Data window. Then double click on the selection to display the Download Info dialog box.

The Download Info dialog box supports the commands and data required to download soft fonts for Hewlett Packard printers. These printer operate using HPPCL. Downloading fonts to other types of printer models is not supported by RasDD. A soft font is a special font stored on a disk and copied or downloaded to the printer’s RAM before it can be used.

A soft font has two components: the font-descriptor component and the character-descriptor component. The font-descriptor component specifies the font’s metrics (measurements such as the character-cell dimensions, pitch, x-height, stroke weight, and so on). The character-component contains bitmaps for individual characters in a soft font.

Soft fonts are temporary as opposed to permanent cartridge- or ROM-resident fonts. When a printer is reset, soft fonts are deleted. This information is needed to download TrueType fonts to the HP LaserJet Series II, and later HP LaserJet printers. RasDD supports downloading TrueType fonts as bitmapped fonts, but not as downloaded scalable fonts.

When you finish entering data in the Download Info dialog box:

  • Click the OK button to accept the additions or changes.

  • Click the Add button to add an additional set of commands.

  • Use the Previous or Next buttons to page to the preceding or next set of commands.

  • Click the Delete button to delete a set of commands.

Font ID Value Limits

The HPPCL requires a special identifier to be assigned to a soft font before downloading. The Minimum ID and Maximum ID boxes specify the minimum and maximum allowable font ID values.

Note The ID values listed apply only to downloaded TrueType fonts. Other downloaded fonts installed with the font installer use ID values supplied to the font installer.

HP LaserJet IIP: 1 is used to specify the minimum allowable font ID for the Minimum ID box. The Maximum ID box specifies the maximum allowable font ID value.

Bitmap Font Information

The Bitmap Font Info box contains the cbBitMapFontDescriptor and cbBitMapCharDescriptor controls that specify the font size and character-size, in bytes. They are descriptors for a bitmapped font.

HP LaserJet IIP: Use 64 for the cbBitMapFontDescriptor box and 16 for the cbBitMapCharDescriptor box. The character descriptor for each symbol in the font requires 16 bytes.

Maximum Font Count

The sMaxFontCount box specifies the maximum number of fonts that can be downloaded at any given point in time.

HP LaserJet IIP: Use -1 for the sMaxFontCount box. This means the number of fonts that can be downloaded to the printer is unlimited (except by available printer memory).

General Download Information Dialog Box

Click the fGeneral button to display the fGeneral dialog box. The fGeneral dialog box contains boxes that specify printer limitations and features related to downloaded fonts as follows:
Control | Description
DLI_GEN_MEMORY | The number of downloaded fonts is limited by available memory.
DLI_GEN_DIJOB | The printer downloads fonts on a per-job basis.
DLI_GEN_DLPAGE | The printer downloads fonts on a per-page basis.
DLI_GEN_PT_IDS | The printer requires a font ID value in order to make the font temporary or permanent.
DLI_GEN_FNTDEL_ ANYWHERE | Fonts can be deleted anywhere on the page. By default, fonts may be deleted only at page boundaries.

HP LaserJet IIP: Check the DLI_GEN_MEMORY box and the DLI_GEN_PT_IDS box.

Format Information Dialog Box

Click the fFormat button to display the fFormat dialog box. The fFormat dialog box provides information that RasDD uses to support TrueType on Hewlett-Packard printers that use HPPCL. The fFormat dialog box has these controls:
Control | Description
DLI_FMT_PCL | The printer uses HPPCL.
DLI_FMT_INCREMENT | The printer downloads only those characters required to print a single job. If this box is not checked, RasDD downloads the entire character set to the printer for a given font.
DLI_FMT_PCL_RESSPEC | The printer supports HPPCL bitmap fonts in which the x and y resolutions are specified at the end of the download header.
DLI_FMT_OUTLINE | (not supported by Windows NT)
DLI_FMT_CAPSL | The printer supports CaPSL style font downloading.
DLI_FMT_PPDS | The printer supports PPDS style font downloading.
DLI_FMT_CALLBACK | Downloading is supported by a callback function. For more information about the callback function, see Minidriver Callback Functions.

HP LaserJet IIP: Check the DLI_FMT_PCL and the DLI_FMT_INCREMENT boxes.

DLI_OCD_BEGIN_DL_JOB

The DLI_OCD_BEGIN_DL_JOB command begins downloading.

HP LaserJet IIP: The DLI_OCD_BEGIN_DL_JOB box is empty.

DLI_OCD_BEGIN_FONT_DL

The DLI_OCD_BEGIN_FONT_DL command begins downloading fonts.

HP LaserJet IIP: The DLI_OCD_BEGIN_FONT_DL box is empty.

DLI_OCD_SET_FONT_ID

The DLI_OCD_SET_FONT_ID command sets the identifier of the font. This command assigns an integer value to a downloadable font. This integer value is the value that RasDD uses to refer to the font.

HP LaserJet IIP: Use the Assign Font ID command (ESC*cnD) to assign an ID value to a font. This value can be used to select or delete the font after it is downloaded.

To specify the command, type the following in the DLI_OCD_SET_FONT_ID box:

\x1B*c%dD

DLI_OCD_SEND_FONT_DESCRIPTOR

The DLI_OCD_SEND_FONT_DESCRIPTOR command downloads the font-descriptor data to a printer.

HP LaserJet IIP: Use the Font Descriptor command (ESC)snW) to download font descriptor data to the printer for the font whose ID is specified by the argument n. Font Descriptor data is data that is common to all characters in a font; this data includes: the dimensions of the character cell, the orientation of the characters, the x-height, the stroke weight, the first character code, the last character code, and so on.

To specify the command, type the following string in the DLI_OCD_SEND_FONT_DESCRIPTOR box:

\x1B)s%dW

DLI_OCD_SELECT_FONT_ID

The DLI_OCD_SELECT_FONT_ID command selects the downloadable font identified by a particular integer value.

HP LaserJet IIP: Use the Font Selection by ID command (ESC(nX) to select the downloaded font with the ID value of n.

To specify the command, type the following string in the DLI_OCD_SELECT_FONT_ID box:

\x1B(%dX

DLI_OCD_SET_CHAR_CODE

The DLI_OCD_SET_CHAR_CODE command identifies the next character to be printed.

HP LaserJet IIP: Use the Character Code command (ESC*cnE) to designate the character code associated with the next character to be downloaded.

To specify the command, type the following in the DLI_OCD_SET_CHAR_CODE box:

\x1B*c%dE

DLI_OCD_SEND_CHAR_DESCRIPTOR

The DLI_OCD_SEND_CHAR_DESCRIPTOR command downloads the character-descriptor and character-data components for a font.

HP LaserJet IIP: Use the Character Descriptor and Data command (ESC(snW) to download n bytes of character descriptors and character data for a font.

To specify the command, type the following in the DLI_OCD_SEND_CHAR_DESCRIPTOR box:

\x1B(s%dW

DLI_OCD_END_FONT_DL

The DLI_OCD_END_FONT_DL command signals the completion of the download-font process.

HP LaserJet IIP: The DLI_OCD_END_FONT_DL box is empty.

DLI_OCD_MAKE_PERM

The DLI_OCD_MAKE_PERM command makes a downloadable font permanent.

HP LaserJet IIP: Use the Font Control command (ESC*cnF) to delete downloaded fonts, make downloaded fonts temporary, make downloaded fonts permanent, and so on. If n is 5, the font is made permanent.

To specify the command that makes a font permanent, type the following in the DLI_OCD_MAKE_PERM box:

\x1B*c5F

DLI_OCD_MAKE_TEMP

The DLI_OCD_MAKE_TEMP command makes a downloadable font temporary.

HP LaserJet IIP: Use the Font Control command (ESC*cnF) to delete downloaded fonts, make downloaded fonts temporary, make downloaded fonts permanent, and so on. If n is 4, the font is made temporary.

To specify the command that makes a font temporary, type following in the DLI_OCD_MAKE_TEMP box:

\x1B*c4F

DLI_OCD_END_DL_JOB

The DLI_OCD_END_DL_JOB command signals the completion of a print-job that used downloaded fonts.

HP LaserJet IIP: Not required. The DLI_OCD_END_DL_JOB box is empty.

DLI_OCD_DEL_FONT

The DLI_OCD_DEL_FONT command deletes a single downloaded font from the printer’s memory.

HP LaserJet IIP: Use the Font Control command (ESC*cnF) to delete downloaded fonts, make downloaded fonts temporary, make downloaded fonts permanent, and so on. If n is 2, the font whose ID was last specified is deleted.

To specify the command that deletes the last identified downloaded font, type the following in the DLI_OCD_DEL_FONT box:

\x1B*c2F

DLI_OCD_DEL_ALL_FONTS

The DLI_OCD_DEL_ALL_FONTS command deletes all downloaded fonts from the printer’s memory.

HP LaserJet IIP: Use the Font Control command (ESC*cnF) to delete downloaded fonts, make downloaded fonts temporary, make downloaded fonts permanent, and so on. If n is 0, all downloaded fonts are deleted.

To specify the command that deletes all downloaded fonts, type the following in the DLI_OCD_DEL_ALL_FONTS box:

\x1B*c0F

DLI_OCD_SET_SECOND_FONT_ID

The DLI_OCD_SET_SECOND_FONT_ID command sends printer control codes that designate a font identifier. For languages that only support 128 characters per downloaded font (for example, CaPSL), the identifier is that of the secondary character set (128-255).The printing engine or font installer supplies font identifiers.

HP LaserJet IIP: The DLI_OCD_SET_SECOND_FONT_ID box is empty.

DLI_OCD_SELECT_SECOND_FONT_ID

The DLI_OCD_SELECT_SECOND_FONT_ID command selects the specified downloaded font. For languages that only support 128 characters per downloaded font (for example, CaPSL), the command selects the secondary character set (128-255).

HP LaserJet IIP: The DLI_OCD_SELECT_SECOND_FONT_ID box is empty.


Model Data

After you specify master units, control information, paper information, graphics information, and font information for your printer, you need to enter model-specific data. The following topic provides information on entering model-specific data.


Model Data Dialog Box

A number of sets of printer model commands can be set up for the minidriver. To specify printer model commands, select ModelData from the Printer Data menu on the minidriver’s Printer Data window. Then, select Resolution to display the initial ModelData dialog box.

To select an existing set of printer model commands, highlight the desired set of Printer Models to edit on the minidriver’s Printer Data window. Then, double click on the selection to display the ModelData dialog box. Most of the printer model-specific data is entered in the ModelData dialog box.

When you finish entering data into the ModelData dialog box:

  • Click the OK button to accept additions or changes.

  • Click the Add Model button to add an additional set of printer model commands.

  • Use the Cut Model or Paste Model buttons to cut or paste a set of printer model commands to another minidriver.

  • Click the Delete Model button to delete a set of printer model commands.

Name of Model

The Name of Model control specifies a printer’s model name. Click the Next Model or Previous Model buttons as needed to display the appropriate printer name or enter a new model name and select the Add button.

If a driver supports two models that are identical except for the name, the names are separated by a percent sign (%). For example, HP LaserJet%HP LaserJet IIP Plus for the HP LaserJet IIP and HP LaserJet IIP Plus printers.

Page-Size Limits

The Page Size Limits controls specify the minimum allowable physical page size and the maximum printable region widths and heights in master units for user-defined paper sizes. If the minidriver supports user-defined paper sizes and the appropriate controls in the Paper Sizes dialog box are initialized, set the Page Size Limits controls.

Diconix 150 Plus: The maximum allowable printable width is imposed by the model and is set to 6624 (6.9 inches as expressed in master units). The Diconix printer does not impose any paper-length constraints. For example, an arbitrary length of 24 inches was chosen that translates into 13,824 vertical master units (24 * 576).

The minimum allowable width for the Diconix printer is imposed by the rubber rollers on the bail arm. These rollers can be adjusted from 6 to 8 inches apart imposing the smallest allowable paper width of 6 inches, which translates into 1,924 horizontal master units (6 * 320). The minimum allowable height is the distance from the entrance of the paper-feed slot to the top bail arm. This distance is 3.5 inches or 2,016 vertical master units (3.5 * 576).

HP LaserJet IIP: The maximum allowable printable width is 8 inches. This value translates into 2400 horizontal master units (8 * 300). The maximum allowable printable height is 13 inches. This value translates into 3900 master units (13 * 300). The minimum allowable page width and height is one inch. This value translates into 300 master units (1 * 300).

Supported Attributes

The Supported Attributes list box contains the names of each of the related dialog boxes. These dialog boxes correspond to the Control Info, Paper Info, Graphics Info, Vector Info, and Font Info selections accessible by using the Printer Data menu. To select a name from the Supported Attributes list, scroll to and highlight the desired name. Then double click the name to display the dialog box containing a list of the current setups. Each dialog box contains the same list of current setups that is shown in the separate box listings on the minidriver’s Printer Data window.

When the requested dialog list box is displayed, select the default setting from the Default list box. Next, highlight the appropriate items in the list and click one of the following buttons as needed:
Control | Description
Enable All | Enable all selected items.
Inhibit All | Disable all selected items.
Cancel | Cancel the dialog box.
OK | Accept the selected items and the default setting.

Capability Bitfields

The Capability Bitfields list box contains the names of dialog boxes that specify the capabilities of your printer. You select a dialog box by double-clicking its name.

The fCurves, fLines, fPolygonals, and fClip dialog boxes describe the vector graphics capabilities of the printer. If your driver must support a particular vector graphics capability, it must set the appropriate bit indicating it supports this capability and must contain code to handle the device driver interface (DDI) call for this capability without calling UNIDRV.DLL. For example, if the CC_CIRCLES bit in fCurves is set, your driver must handle the DDI Output function when the output style is OS_CIRCLE. If your minidriver does not contain this code, or if your printer does not have vector capabilities, you should not set any of the vector graphics capability bits. For vector-capable devices, these bits should be set so that they correspond to the vector commands that you defined through the Printer Data menu.

General Capabilities Dialog Box

The fGeneral dialog box identifies printer capabilities such as double-sided printing, multiple-copy printing, font rotation capabilities, bitmap rotation capabilities, and so on. The fGeneral dialog box has these controls:
Control | Description
MD_SERIAL | Output must be sorted sequentially in the y-direction, because the printer cannot scroll backwards.
MD_ROTATE_FONT_ABLE | The printer can rotate hardware fonts to match the current paper orientation. Fonts supplied by Windows (TrueType, bitmap, and vector) are always available in both orientations.
MD_COPIES | The printer supports multiple-copy printing.
MD_DUPLEX | The printer supports double-sided printing.
MD_NO_ADJACENT | The printer cannot fire adjacent pins on the print head in all resolutions. If adjacent pins can be fired in some resolutions but not others, this should be indicated for each resolution.
MD_LANDSCAPE_GRX_ABLE | The printer can rotate a graphic image (or bitmap) to match the current paper orientation. If the printer is unable to rotate graphics, the Universal Printer Driver will perform the graphic rotation.
MD_ALIGN_BASELINE | The printer aligns text output on the baseline rather than the top of the character bounding-box. (The default alignment is the top of the bounding box.)
MD_FONT_MEMCFG | (not supported by Windows NT)
MD_LANDSCAPE_RT90 | The printer rotates 90 degrees in the counterclockwise direction for landscape output (this means that the right side of a page or image is printed first). If this control is not set, the default rotation of 270 degrees in the counterclockwise direction is assumed. Only set this control if the printer supports a set-orientation command.
MD_USE_CURSOR_ORIG | The cursor origin is calculated using the values entered in the cursor origin controls of the Paper Sizes dialog box. If the MD_USE_CURSOR_ORIG is not set, the cursor origin is the upper-left corner of the printable region.
MD_WHITE_TEXT | The printer can print white text on a black background. The Universal Printer Driver uses the commands you entered in the Font Simulation dialog box to turn white text printing on and off.
MD_PCL_PAGEPROTECT | The printer supports HP PCL5-style page protection. The Universal Printer Driver uses the commands you entered in the Paper Sizes dialog box to set the page protection mode.
MD_MARGINS | (not supported by Windows NT)
MD_CMD_CALLBACK | (not supported by Windows NT)
MD_MEMRES | (not supported by Windows NT)

Diconix 150 Plus: Check the MD_SERIAL box.

HP LaserJet IIP: Check the MD_PARAMETERIZE, MD_COPIES, MD_ALIGN_BASELINE, MD_LANDSCAPE_RT90, and MD_USE_CURSOR_ORIG boxes.

Curve Capabilities Dialog Box

(Not currently supported)

Line Capabilities Dialog Box

(Not currently supported)

Polygon Capabilities Dialog Box

(Not currently supported)

Text Capabilities Dialog Box

The fText and fLText dialog boxes identify text-rendering capabilities in portrait and landscape mode when the selected font is a device font. (The fLText dialog box is identical to the fText dialog box; the fText dialog box specifies text rendering capabilities in portrait mode, and the fLText dialog box specifies text rendering capabilities in landscape mode.) The fText and fLText dialog boxes have these controls:
Control | Description
TC_OP_CHARACTER | Device supports OutputPrecision CHARACTER. This bit should be set if the device has any hardware fonts.
TC_OP_STROKE | Device supports OutputPrecision STROKE. (Not used.)
TC_CP_STROKE | Device supports ClipPrecision STROKE. (Not used.)
TC_CR_90 | Device fonts may be rotated at 90-degree increments independent of current page orientation. (Not used.)
TC_CR_ANY | Device supports character rotation (variable) independent of current page orientation. (Not used.)
TC_SF_X_YINDEP | Device supports scaling independent of X and Y.
TC_SA_DOUBLE | Device supports doubled character for scaling.
TC_SA_INTEGER | Device supports integer multiples for scaling.
TC_SA_CONTIN | Device supports any multiples for scaling.
TC_EA_DOUBLE | Device supports bold characters.
TC_IA_ABLE | Device supports italicized characters.
TC_UA_ABLE | Device supports underlined characters.
TC_SO_ABLE | Device supports strikeout characters.
TC_RA_ABLE | Device can printer raster fonts. Indicates that GDI should enumerate any raster fonts available for this device in response to the EnumFonts function. If this bit is not set, GDI supplied raster fonts will not be enumerated in response to EnumFonts, although device fonts, TrueType fonts, and GDI vector fonts will continue to be enumerated.
TC_VA_ABLE | Device can print vector fonts. (Not used.)

Diconix 150 Plus: Check the TC_OP_CHARACTER, TC_EA_DOUBLE, TC_IA_ABLE, TC_UA_ABLE, and TC_RA_ABLE boxes.

HP LaserJet IIP: Check the TC_UA_ABLE box.

Clipping Capabilities Dialog Box

(Not currently supported)

Raster Capabilities Dialog Box

(Not currently supported)

Left Margin Width

The sLeftMargin box specifies the width of the unprintable region on the left edge of the page. Typically, this value is set only for dot-matrix printers and is specified in master units.

Diconix 150 Plus: Use 768. This value is converted from horizontal master units (using the Xdpi value from the master-units dialog box). The maximum printable page width is 6.9 inches. Subtracting this value (6.9 inches) from the width of a paper page (8.5 inches) leaves 1.6 inches for the borders of the page. Dividing 1.6 by 2 leaves .8 inches. This is the width of the left and right borders.

HP LaserJet IIP: Use 50. The unprintable left-margin measures .166 inches. Convert this value to master units (300 * .166 = 50).

Maximum Physical Page Width

The sMaxPhysWidth box specifies the maximum allowable physical page width in master units.

Diconix 150 Plus: Use 8160. This value is converted from horizontal master-units (using the Xdpi value from the master-units dialog box). The maximum physical page width the printer can handle is 8.5 inches.

HP LaserJet IIP:Use 2550. This value is converted from horizontal master units (300 * 8.5 = 2550).

Maximum Number of Fonts per Page

The sMaxFontsPage box specifies the maximum number of device fonts on a single page. If there is no limit to the number of device fonts allowed on a printer, this value is -1.

Diconix 150 Plus: Use -1.

HP LaserJet IIP: Use -1.

Number of Cartridge Slots

The sCartSlots box specifies the number of font-cartridge slots on the printer.

Diconix 150 Plus: Use 0.

HP LaserJet IIP: Use 1.

Default Character-Translation Table

The Default CTT ID box identifies the default CTT. If a single CTT is used for all device fonts, this value can be entered in the Default CTT control in the ModelData dialog box. If the CTT for the fonts is provided by RasDD, the Default CTT is that CTT ID multiplied by -1. If RasDD uses a driver-supplied CTT, the Default CTT value is a positive integer. The Default CTT is zero if RasDD does not use a single CTT.

Diconix 150 Plus: Use 1.

HP LaserJet IIP: Use 0.

Default Font ID

The Default Font ID box identifies the default font that RasDD uses for text output when an application fails to request a specific font set. The default font for a fixed pitch font is 10 point and 10 characters per inch, if possible.

Diconix 150 Plus: Use 1.

HP LaserJet IIP: Use 225.

LookAhead Region

The LookAhead Region box specifies the length of the LookAhead Region in master units. This value is 0 for most printer models. A nonzero value is required for HP DeskJet printers.

Diconix 150 Plus: Use 0.

HP LaserJet IIP: Use 0.

Resident Fonts Dialog Box

Click the Resident Fonts button to display the Resident Fonts dialog box. The Resident Fonts dialog box contains two list boxes for device fonts. The list box on the left side contains the names of Portrait Fonts. The list box on the right side contains the names of Landscape Fonts. The fonts considered to be resident on the device are highlighted.

To select fonts in either list, highlight each of the desired fonts or click the Select All button to select all of the device fonts. Click Clear All to clear all of the current selections. When complete, click OK to accept the selections.

Diconix 150 Plus: The only resident font is 10-point Times Roman for portrait mode.

HP LaserJet IIP: Several fonts are available in both portrait and landscape orientation and are selected in each control.

Memory Configuration Dialog Box

Click the Memory Config button to display the Memory Configuration dialog box. This dialog box contains a list of possible printer memory configurations ranging from 65K/128K to 5820K/17M.

The value in the left column specifies the amount of memory available to RasDD to store downloaded font data and bitmap raster data. The value in the right column specifies the amount of memory to be displayed as available. Because the printer typically reserves some memory for its own use, these two values are rarely the same.

If the printer supports HPPCL and the driver supports downloadable fonts, select the memory configurations for the device. A memory configuration is defined as the amount of RAM available on the printer to store raster bitmap graphics and download font information.

To select memory configurations, highlight each of the necessary configurations or click the Enable All button to select all of the device fonts. (Click Inhibit All to clear all of the current selections). When complete, click OK to accept the selections.

Diconix 150 Plus: No memory configurations are selected.

HP LaserJet IIP: The 369/512K, 2243K/2.5M, and 4491K/4.5M configurations are selected. The HP LaserJet IIP comes with 512K of memory.


Printer Font Metrics (PFM)

To access the PFM dialog box and specify the PFM commands, scroll through the FONTS window and select the desired font. Then, double click the selection to display the PFM dialog box for the selected font file.

To render text with device fonts and to retrieve font data for application queries, RasDD requires special font data or printer font metrics. The PFM dialog box specifies a printer’s font metrics and font selection commands. Printers that render text using device fonts store the character data in the printer or in a font cartridge. (The character data for a Windows font is stored on disk.) Printers render text faster with device fonts because the bitmap or vector data is device resident and does not require downloading from the disk to the printer.

Note The sample in this section displays a Roman 12-point device font file for the Diconix 150 Plus printer. No sample is provided for the HP LaserJet IIP.

When you finish entering data in the PFM dialog box:

  • Click the Save button to accept changes or additions to the PFM dialog box.

  • Click the SaveAs button to save using another name or storage location.

Font Type

The Type control contains the decimal representation of a 16-bit flag. The following list describes the significance of the bits in this flag. Bit 15 is the high-order bit of the high byte; bit 0 is the low-order bit of the low-order byte.
Bit | Description
0 | Specifies a scalable font, if set. Otherwise, the font is a bitmap font.
1 | Reserved; must be zero.
2 | Specifies that character data is stored at the location specified BitsOffset if set. Otherwise, the character data appears immediately after the header information. (This bit is only valid for font files that contain bitmap data.)
3-6 | Reserved; must be zero.
7 | Specifies a device font, if set. Otherwise, the font is a GDI font.
8-15 | Reserved; must be zero.

Note RasDD does not support scalable device fonts, so the Type value must be set to 128 for all device fonts.

Diconix 150 Plus: Set the Type value to 128 for all device fonts. The only bit set for any of the Diconix device fonts is bit 7, whose value is 128. The device fonts are bitmapped fonts; bit 0 is not set. Bit 1 is reserved and is set to 0. Because the PFM file describes a device font and bitmap character data is not in the PFM file, bit 2 is not set. Bits 3 through 6 are reserved and are set to 0.

Point Size

The Points control contains the point size for the font. The point size is determined by measuring the vertical distance from the bottom edge to the top edge of a character cell for a font. Specify the value in printer points; each printer point is equivalent to approximately 1/72 of an inch.

Note Most applications do not use this box to display point sizes to the user because fractional point sizes cannot be expressed. Most applications calculate point size as the sum of the value of the PixHeight and Internal Leading controls in the PFM dialog box.

Diconix 150 Plus: Use 10 for the point size.

Vertical Resolution

Most printer manufacturers digitize their fonts at the highest resolution supported by the printer. The VertRes control contains the vertical resolution that the printer manufacturer used to digitize a font. Specified this value in dpi.

Diconix 150 Plus: The fonts are digitized at a vertical resolution of 96 dpi. Use 576 to prevent confusion with calculations between master units for the minidriver, and the units for values in the PFM files.

Horizontal Resolution

The HorzRes control contains a horizontal resolution value that the printer manufacture used to digitize a font.

Diconix 150 Plus: The fonts are digitized at a horizontal resolution of 320 dpi.

Ascent

The Ascent control specifies the distance from the baseline to the top of a character cell. Specified the value in vertical-resolution units.

Diconix 150 Plus: This distance is 7.25 printers points or 58 dpi.

Internal Leading

The Internal Leading control specifies an amount of space reserved in each character cell for accent characters and so on. For most 9- and 24-pin printers, this value is 0. (The point size specified by most applications is the sum of the PixHeight control value and the Internal Leading value.)

Note Some of the PFM files supplied by Microsoft reflect values compatible with earlier versions of Windows-based printer drivers. For example, Epson and IBM printers have values that are not listed here. However, for recently created drivers, font metric values should reflect how the printer produces device fonts.

Diconix 150 Plus: Use 0 for this value. The device fonts for the Diconix do not support internal leading.

External Leading

The External Leading control specifies the amount of space placed between two rows of text printed using a particular font. Most dot-matrix printer manufacturers suggest printing device fonts at 6 lines to the inch. The External Leading value is increased or decreased to the sum of PixHeight control value added to the External Leading meet value.

Diconix 150 Plus: Use 2 printer points or 16 master units.

Italic Fonts

The Italic control indicates whether the PFM file contains data for an italic font. Use 1 if the font is italic or 0 if it is not.

Diconix 150 Plus: Use 0 (the example font is not italicized).

Underline Fonts

The Underline control indicates whether the PFM file contains data for an underlined font. Use 1 if the font is underlined or 0 if it is not..

Diconix 150 Plus: Use 0 (the example font is not underlined).

Strikeout Fonts

The Strikeout control indicates whether the PFM file contains data for a strikeout font. Use 1 if the font is a strikeout font or 0 if it is not.

Diconix 150 Plus: Use 0 (the example font is not a strikeout font).

Font Weight

The Weight control specifies the relative density or weight of characters rendered using the font. Valid weights can range from 100 to 1000 using increments of the value 100. Specify 400 for normal weight, 700 for bold, and so on. Weights must be a multiple of 100 to be valid.

Diconix 150 Plus: Use 400. The bitmaps for the example font render normal characters.

Character Set

The Char Set control identifies the character set associated with a font. This value is:

  • 0 if the font uses the Windows ANSI character set

  • 1 if the font is an IBM PC hardware font (code page 437)

  • 2 if the font uses a special symbol set

Whenever possible, a minidriver is recommended to support the Windows ANSI character set.

Diconix 150 Plus: Use 0 because the Diconix device fonts use the Windows ANSI character set.

Pixel Width

The PixWidth control specifies the character width for fixed-pitch raster fonts. Specify this value using the horizontal-resolution units (for a variable-width device font, this value is 0).

Diconix 150 Plus: Use 57.

Pixel Height

The PixHeight control specifies the character height of fixed-pitch device fonts. Specify this value using vertical-resolution units.

Diconix 150 Plus: Use 80.

Pitch and Family

The Pitch and Family control contains an integer value that corresponds to a 16-bit binary value. To set this value, click the ? button to display the Font Pitch and Family dialog box. Indicate whether the font is fixed pitch or variable (proportional) spaced. Then, click the Family value to indicate which screen font Windows displays for this device font and click OK.

Diconix 150 Plus: 16 is displayed if the Pitch is Fixed and the Family is Roman.

Average Width

The Average Width control specifies the average character width for proportionally spaced and fixed-pitch fonts. If the font is proportionally spaced, Unitool calculates this value based on the information entered in the font-width table. For a fixed-pitch font, enter this value (equivalent to the width of each character).

Diconix 150 Plus: Use 57.

Maximum Width

The Max. Width control specifies the width of the widest character in a font. For fixed-pitch fonts, this is equivalent to the value specified in the Average Width control. For proportionally spaced fonts, Unitool calculates and inserts this value using values from the character-width table.

Diconix 150 Plus: Use 57.

First Character

The First Char control identifies the ASCII decimal value for the first character in a font’s character set.

Diconix 150 Plus: Use 32, which corresponds to the space character.

Last Character

The Last Char control identifies the ASCII decimal value for the last character in a font’s character set.

Diconix 150 Plus: Use 126, which corresponds to the tilde (~).

Default Character

The Default Char control identifies the character that Windows renders when a character outside the range defined for First Char through Last Char is requested. This value is a zero-based offset from the first character identified in the Default Char control.

Diconix 150 Plus: Use 14, which corresponds to the period (.).

Break Character

The Break Char control identifies the character used to define word breaks. This value is a zero-based offset from the first character identified in the First Char control.

Diconix 150 Plus: Use 0, which corresponds to the space character.

Bitmap Width

The Width Bytes control specifies the width in bytes of each row of pixels for characters in a raster font. This value is only required for PFM files that contain bitmapped font data.

Diconix 150 Plus: Use 0.

Device Name

The Device Name control specifies the name of the printer.

Diconix 150 Plus: Use the string, Diconix 150 Plus.

Face Name

The Face Name control specifies the font name. If a printer supports multiple fonts with the same font name and point size, but with different pitches, the string also contains the pitch value. Pitch values should be expressed in terms of characters-per-inch (cpi). The font name in a minidriver must be unique.

Diconix 150 Plus: Use the string, Roman(12cpi).

Device Font Character Widths Dialog Box

If the Pitch and Family value indicates that the PFM file for a variable-pitch device font is to be edited, click the FontWidth button to display the Device Font Character Widths dialog box. (If fixed-pitch fonts are used, this box is not active.) Then, enter the values for the widths of each character that falls between the First Char and Last Char ASCII character values. The widths are expressed in horizontal resolution units. If the font uses a CTT, the widths entered reflect the widths of the characters the printer actually produces. Click OK when complete to return to the PFM dialog box.

Extended Text Metrics Dialog Box

This information is used only by applications that specifically request it from the minidriver. To edit values for extended text metrics:

  • Click the ExtTextMetrics button to display the Extended Text Metrics dialog box and to enter or change these values.

  • Click OK to accept these values.

  • Click the Delete button to delete information.

Extent Table Dialog Box

The Extent Table dialog box specifies the width of each character in a scalable font. To edit the extent table values:

  • Click the ExtentTable button to display the Extent Table dialog box and to enter or change these values.

  • Click OK to accept these values.

Kern Pairs Dialog Box

This information is used only by applications that specifically request it from the minidriver. To edit kerning-pair values:

  • Click the KernPair button to display the Kern Pairs dialog box.

  • Click Add to add values for the kern pairs and enter these values.

  • Click Change to change a kern pair and edit the current values.

  • Click Del to delete a kern pair.

Kern Tracks Dialog Box

This information is used only by applications that specifically request it from the minidriver as follows:

  • Click the KernTrack button to display the Kern Tracks dialog box.

  • Click Add to add values for the kern pairs and enter these values.

  • Click Change to change a kern pair and edit the current values.

  • Click Del to delete a kern pair.

Driver Information

The DriverInfo group box at the bottom of the PFM dialog box contains the printer commands that select and cancel the selection of the font as well as additional data that some printers require to render text with a specific font.

Font Selection

The Font Select command selects a particular font.

Diconix 150 Plus: Use the Elite Mode On command (ESCM) to select 10-point and 12-pitch Roman font.

To specify the command, type the following in the Font Select box:

\x1BM

Note Depending on the printer model, font selection commands can require a combination of printer commands to select the desired font.

Canceling Font Selection

The Font UnSelect command cancels a font selection. For printers that have no x-cursor movement command and rely on the width of the space character to perform horizontal movement, this command selects the font or pitch that causes the printer to move the desired amount for a space character. This command is also required if a printer’s font selection command enables attributes that cannot be disabled by a subsequent font select command or inhibit the use of other commands used in the driver.

Diconix 150 Plus: There is no command that cancels the selection of the default font when the printer’s switches are set to the factory default. The Font UnSelect box is empty.

Note Depending on the printer model, font selection commands can require a combination of printer commands to select the desired font.

Vertical Adjustments

The Y Moved and the Y Adjust controls contain vertical offsets required to align a font baseline with the GDI baseline. If these baselines differ, Windows shifts the font baseline by the Y Adjust value prior to rendering text. After the text is rendered, Windows shifts the font baseline back to the proper position using the Y Moved value. Typically, it is not necessary to adjust the Y position for device fonts.

Diconix 150 Plus: Use 0.

Private Font Data

The Private Data control provides a means of storing 16-bits of private data. If a minidriver provides a private function (or private functions) for processing device fonts, it can store data used by these functions using this control.

Diconix 150 Plus: Use 0.

Font Type

The Font Type control specifies the type of scalable font technology the printer uses. It can be one of these values:
Value | Meaning
DF_TYPE_HPINTELLIFONT | HP Intellifont fonts. This is the default value.
DF_TYPE_TRUETYPE | TrueType fonts
DF_TYPE_PST1 | not currently supported
DF_TYPE_CAPSL | not currently supported
DF_TYPE_OEM1 | not currently supported
DF_TYPE_OEM2 | not currently supported

Character Shift Amount

The Shift control specifies a horizontal distance in horizontal-resolution units. This distance is an offset that RasDD uses to shift individual characters from a particular font. This distance is required for fonts with characters that are not centered in the bounding box. RasDD uses a negative shift value to shift characters to the right, and a positive shift value to shift characters to the left. Shift amounts are used when characters do not fall in the center of the bounding box defined by font metrics.

Diconix 150 Plus: Use 0.

Character-Translation Table

The Trans Tab control identifies the CTT associated with a font. A CTT provides a method of remapping the extended characters used for device fonts to the Windows ANSI set. In the Trans Tab control, negative values identify predefined translation-tables, and positive values identify minidriver-defined translation tables. A value of zero indicates that the CTT is identified in the minidriver’s model data. For more information about character-translation tables (CTTs), see Character-Translation Table.

Diconix 150 Plus: Use 0 because the font uses the CTT specified in the ModelData dialog box.

Font Simulation Capabilities (fCaps) Dialog Box

This dialog box is not currently used since the Windows NT GDI performs font simulation.

Diconix 150 Plus:

For the Diconix 150 Plus printer, all font simulations are supported; therefore, none of the checkboxes were selected.


Character-Translation Table

After you have created PFM files for your printer’s fonts, you should create a character-translation table (CTT). To create a CTT for a font, scroll through the CTT window and highlight the desired character-translation. Then, double click the selection to display the CTT dialog box for the selected font file.

A CTT is used by RasDD to map characters from the Windows ANSI character set to the printer character set to identify the printable characters. The current version of this set contains 256 entries.

RasDD provides support for some of the common code pages and symbol sets to eliminate redundancy in a large number of individual printer drivers and permits individual driver to support the less common ones. RasDD currently supports the following predefined CTTs:
CTT ID | CTT (Code Page)
-1 | IBM Code Page 437
-2 | IBM Code Page 850
-3 | IBM Code Page 863

If the device font uses extended characters that match either code page 437, 850, or 863, a minidriver can use one of the predefined CTTs provided by RasDD. In this case, enter the CTT ID for the CTT in the Trans Tab control of the PFM dialog box. CTTs provided by RasDD are referenced from the minidriver through negative numbers that correspond to the resource ID values (from RasDD).

When the printer uses the same extended characters for all device font, such as most dot-matrix printers, a minidriver can define the CTT as follows:

  1. Specify a CTT through the Default CTT in the ModelData dialog box and set the Trans Tab value in the PFM dialog box in each PFM file to zero.

  2. Set the Trans Tab values in each PFM file to the appropriate CTT ID value.

For more information, see Printer Font Metric Structure.

Note If a single CTT is used for the font set, the value can be entered in the Default CTT control of the ModelData dialog box. If RasDD uses a driver-supplied CTT, the Default CTT value is a positive integer. The Default CTT is zero if RasDD does not use a single CTT. For more information, see Model Data.

Printer drivers supporting device with less common code pages and symbol sets contain their own CTT resources. A given minidriver can mix and match these types of CTTs as needed. CTTs provided by the minidriver are referenced by positive numbers corresponding to the resource ID values (from the minidriver).

When the printer uses a different set of extended characters for each font, such as the HP LaserJet IIP, the minidriver sets each Trans Tab value in each PFM file to the CTT ID value that is appropriate for that font. After the extended character set(s) for the known device fonts is determined, the CTT dialog box is used to create unique CTTs for the device’s remaining characters to specify which character codes map to which printer symbols.

Note The sample in this section displays the CTT dialog box with the EPSONXTA.CTT file for the Diconix 150 Plus printer. No sample is provided for the HP LaserJet IIP.

If the font does not use extended characters (either does not print them, or the characters are italicized versions of the characters below ASCII 127), a CTT is not needed. For these printers, set the Last Char in the PFM dialog box to 127 (or to the last printing ASCII value). In this case, the Default Char in the PFM file is used when an extended character is requested.

When you finish entering data in the CTT dialog box:

  • Click the Save button to accept additions or changes to the CTT file.

  • Click the Save As button to save the CTT using another name or storage location.

First Character

The First Char control identifies the first character in the CTT. The first character in the translation table is 127. Characters below the First Char do not use the CTT and are sent directly to the printer.

Note The extended characters used by the HP LaserJet IIP printer vary depending on the individual font.

Last Character

The Last Char control identifies the last character in the CTT. Because the CTT for the EPSONXTA.CTT file character set supports characters 127 through 255, this value is 255.

Character Codes

The remaining controls in the CTT dialog box specify a character code or device-command/character-code sequence and maps it to a particular entry in the Windows ANSI set. For example, suppose character code 161 in the Windows ANSI character set is an inverted exclamation point. If this character is available on the printer, place the required printer commands in box 161 in the CTT. If the character is not available, place the default character for that font in that location in the CTT.

There are three formats for the contents of a CTT file. Unitool determines which one is to be used to save a CTT file based on the information entered:

  • 1-to-1 (byte to byte) translation (effectively a translation table). Each character code consists of a single byte. For simple remappings, RasDD sends the character value entered in the edit control each time the ASCII value is shown.

  • 2-to-1 (two bytes to one byte) translation. Each character code consists of either one of two characters. RasDD sends the first character value that was entered in the edit control, and then use either a backspace character or x-movement command (as defined in the DI_BKSP_OK control) to move the current position back to the original position. Then, RasDD sends the second character to overprint the first. If null, the second character is not set. This is used to overstrike characters, such as the slash and zero to produce a slashed zero.

  • Variable-length translation. Each character code consists of a variable length string containing printer commands that change printer modes to gain access to a particular character, such as symbol set switching in the LaserJet family. RasDD sends the commands entered in the edit control when requested.

The differences between these formats is transparent to the user. Unitool creates the CTT in a format that results in the smallest file size depending on the actual translation. Printer commands sent from the CTT do not change the permanent state of the printer.


Minidriver Callback Functions

You can provide callback functions that the Microsoft Windows Universal Printer Driver calls at particular points in its execution. The following topics provide information on these callback functions.


CBFilterGraphics

WORD FAR PASCAL  CBFilterGraphics(LPBYTE lpdv, LPSTR lpBuf, WORD wLen)

Alters the format of raster graphic data before sending the data to the output device. This is useful if a specific device addresses pins on the print head in an unusual way (such as older Okidata dot matrix printers), or if the device requires raster graphics data in an unusual format (such as the "24 raster bits in 4 bytes" format common among Toshiba dot matrix printers).

  • Returns the number of bytes of processed raster data.The number of bytes may be the same as wLen, but not necessarily.
lpdv
Address of private data used by RasDD. The CBFilterGraphics function should not change this data.
lpBuf
Address of a buffer that holds raster graphic data.
wLen
Size, in bytes, of the buffer specified by lpBuf.

After CBFilterGraphics has finished changing the raster graphic data, it should call the WriteSpoolBuf function to pass the data to RasDD.

To implement this function, you must:

  1. Set the RES_BO_OEMGRXFILTER bit flag in fBlockOut dialog box that is accessible through the RESOLUTION dialog box.

  2. Export the CBFilterGraphics function as ordinal 300 in the EXPORTS section of the module definition (.DEF) file for your minidriver.

  3. Import the WriteSpoolBuf function as ordinal 300 in the IMPORTS section of the module definition (.DEF) file for your minidriver.

  4. In the drvname.C file for your minidriver, add the function to perform the changes upon the raster data needed by the device. Define this function as described under “WriteSpoolBuf.”


WriteSpoolBuf

short FAR PASCAL WriteSpoolBuf(LPBYTE lpdv, LPSTR lpBuf, WORD wLen);

Passes data to RasDD that has been changed by the CBFilterGraphics callback function.

  • Returns the number of bytes written if successful, C1 otherwise.
lpdv
Address of private data used by RasDD. The data should be that same as that specified in the CBFilterGraphics function.
lpBuf
Address of the buffer that receives changed raster graphic data.
wLen
Size, in bytes, of the buffer specified by lpBuf.


Unitool Error Messages

Unitool provides two ways of checking the data described in the minidriver for possible problems and reporting that information back to you. From the Options menu, you can choose two commands: Validate Dialog Boxes and Validate Now!.

For message descriptions, refer to:
MiniDriver Data Dialog Box Messages

ModelData Dialog Box Messages

Resolution Dialog Box Messages

Paper Sizes Dialog Box Messages

Font Cartridge Dialog Box Messages

Page Control Dialog Box Messages

Cursor Movement Dialog Box Messages

Font Simulation Dialog Box Messages

Color Models Dialog Box Messages

DownLoad Info Dialog Box Messages

Printer Font Metric Dialog Box Messages

CTT Dialog Box Messages

General Error Messages

Validate Dialog Boxes

The Validate Dialog Boxes command may be chosen or canceled at your discretion. When it is chosen, whenever you display a dialog box and choose OK, Next, or Previous (that is, any choice that will cause the current dialog box contents to be saved rather than ignored), Unitool will perform some limited error checking on the contents of that dialog box. If any potential problems are found, a new dialog box listing all the potential problems will be displayed, and you may choose to ignore these warnings or return to the dialog box to correct them. All of the error messages, along with a corresponding explanation, are listed at the end of this appendix. If the Validate Dialog Boxes command is not chosen, error checking will not occur. This command is enabled by default.

Validate Now

The Validate Now command on the Options menu performs some limited error checking on the contents of the entire minidriver file currently loaded. If any potential problems are found, a new dialog box listing all the potential problems is displayed. All of the error messages, along with a corresponding explanation, are listed at the end of this appendix.


MiniDriver Data Dialog Box Messages

Illegal GPC Major Version Number, must be 0<=n<=1
The value in the Major box is not 0 or 1.
Illegal GPC Major Version Number, must be 0<=n<=1
The value in the Minor box is within the specified range (0 - 255).
Structure-Name Conversion error
The new master units resulted in a round-off error after each was divided into a specified member in the MODELDATA, RESOLUTION, PAPER-SIZE, PAPER-SOURCE, CURSOR-MOVEMENT, or RECTANGLE-FILL structures.

For example, if the sLeftMargin box of the ModelData dialog box contains 179, and the horizontal master unit was changed from 360 to 180, the conversion error message appears because 89.5/180 cannot be expressed as an integer. Unitool also replaces the structure identified by Structure-Name with the appropriate string.


ModelData Dialog Box Messages

Default Font ID must refer to a valid font
An invalid value is entered in the Default Font ID box.
Default CTT ID must refer to a valid CTT
The value entered in the Default CTT ID box does not identify a valid character-translation table.
Every Model must have a name
The string entered in the Name of Model box is invalid.
Look Ahead Region must be non-negative
The value entered in the LookAhead Region box is negative.
Max Page Width must be > 0 or equal -1
The value entered in the Max-Width page size limits box is equal to 0 or less than -1.
Max Page Height must be > 0 or equal -1
The value entered in the Max-Width page size limits box is equal to 0 or less than -1.
minidriver name supports Font Cartridges, but has no Cartridge slots
Zero is entered in the sCartSlots box, and one or more font cartridges is selected in the supported attributes Font Cartridges dialog box. The Font Cartridges dialog box is associated with the ModelData dialog box.
minidriver name has no PageControl
A set of supported page-control commands is not identified in the ModelData dialog box.
minidriver name has no CursorMove
A set of supported cursor-movement commands is not identified in the ModelData dialog box.
minidriver name has no Resolutions
A set of supported resolution commands is not identified in the ModelData dialog box.
minidriver name has no Paper Sizes
A set of supported paper-size commands is not identified in the ModelData dialog box.
minidriver name: Memory Values not used for non-HPPCL devices
One of the listed memory configurations in the ModelData dialog box is for a printer that does not use HPPCL.
Min Page Width must be > 0
The value entered in the Min-Width page size limits box is less than or equal to 0.
MODELDATA Min Page Height must always be > 0
The value entered in the Min-Height page size limits box is less than or equal to 0.
sCartSlots must be non-negative
The value entered in the sCartSlots box is negative.
sLeftMargin must be non-negative
The value entered in the sLeftMargin box is negative.
sMaxPhysWidth must be > 0
The value entered in the sMaxPhysWidth box is not greater than or equal to zero.


Resolution Dialog Box Messages

Every RESOLUTION must have a format string
The a format string for the selected resolution is not entered.
minidriver name Illegal Text X DPI value
An invalid horizontal text resolution is entered.
minidriver name Illegal Text Y DPI value
an invalid vertical text resolution is entered.
minidriver name Illegal Graphics X DPI value
An invalid horizontal graphics resolution is entered.
minidriver name Illegal Graphics Y DPI value
An invalid vertical graphics resolution is entered.
minidriver name: sMinBlankSkip must be >= 0
An invalid value is entered in the sMinBlankSkip box.
minidriver name :sMinBlankSkip should be > 0
An invalid value is entered in the sMinBlankSkip box and the RES_BO_ENCLOSED_BLNKS box is checked in the fBlockOut dialog box.
minidriver name: sNPins must be 1,8,16,32,40,48...
An invalid value is entered in the sNPins box.
minidriver name: sPinsPerPass must be 1,8,16,32,40,48...
An invalid value is entered in the sPinsPerPass box.
minidriver name: Spot Diameters must be > 0
A negative value is entered in the sSpotDiameter box.
minidriver name: RES_DM_GDI conflicts with sNPins or sPinsPerPass
A value greater than 1 is entered for sNPins or sPinsPerPass and the RES_DM_GDI box is checked in the fBlockOut dialog box.
minidriver name: Unknown iDitherBrush
An undefined value is entered in the iDitherBrush box.


Paper Sizes Dialog Box Messages

minidriver name :Illegal Bottom Margin
An illegal bottom-margin height is entered.
minidriver name :Illegal Left Margin
An illegal left-margin width is entered.
minidriver name :Illegal fPaperType bit(s) set
An illegal paper-type box is selected.
minidriver name :Illegal Right Margin
An illegal right-margin width is entered.
minidriver name :Illegal Top Margin
An illegal top-margin height is entered.
minidriver name :Illegal X Size
An illegal paper width is entered.
minidriver name :Illegal Y Size
An illegal paper height is entered.


Font Cartridge Dialog Box Messages

Font Cartridge Must have a Name
A string identifying the selected font cartridge is not entered.


Page Control Dialog Box Messages

minidriver name :Max Copy Count must be >= 0
A value less than zero is entered for the maximum copy count.


Cursor Movement Dialog Box Messages

minidriver name :sMult =0 or 1 for abs. Y move
The value entered in the sUnitMult box of the associated Units dialog box is not 0 or 1.
minidriver name :sMult =0 or 1 for linespacing Y move
The value entered in the sUnitMult box of the associated Units dialog box is not 0 or 1.
minidriver name :sMult =0 or 1 for rel. Y move
The value entered in the sUnitMult box of the associated Units dialog box is not 0 or 1.
minidriver name :sMult =0 or 1 for rel. up Y move
The value entered in the sUnitMult box of the associated Units dialog box is not 0 or 1.


Font Simulation Dialog Box Messages

minidriver name :sBoldExtra must be >= 0
The value entered in the sBoldExtra box is negative.
minidriver name :sBoldItalicExtra must be >= 0
The value entered in the sBoldItalicExtra box is negative.
minidriver name :sItalicExtra must be >= 0
The value entered in the sItalicExtra box is negative.


Color Models Dialog Box Messages

minidriver name :DC_EXTRACT_BLACK conflicts w/ number of planes
The selection contradicts the specified number of planes.
minidriver name :DC_PRIMARY_RGB conflicts w/ number of planes
The selection contradicts the specified number of planes.
minidriver name :sBitsPerPixel must = 1
The value entered in the sBitsPerPixel box is not equal to 1.
minidriver name :sPlanes must = 3 or 4
The value entered in the sPlanes box is not equal to 3 or 4.


DownLoad Info Dialog Box Messages

minidriver name :Bitmap Font Descriptor must be > 0
The bitmap font-descriptor value entered is negative.
minidriver name :Bitmap Char Descriptor must be > 0
The bitmap character-descriptor value entered is negative.
minidriver name :fFormat DLI_FMT_PCL not set
The DLI_FMT_PCL box is not checked for a HPPCL printer.


Printer Font Metric Dialog Box Messages

Kerning Info will be lost Continue?
Kerning information will be lost.
minidriver name :AvgWidth must be > 0
The value entered in the Average Width box is negative.
minidriver name :Ascent must be > 0
The value entered in the Ascent box is less than or equal to zero.
minidriver name :CharSet must be 0,1, or 2
The value entered in the Char Set box is not equal to 0, 1, or 2.
minidriver name :HorizRes must be > 0
The horizontal resolution value entered is less than or equal to zero.
minidriver name :Points must be > 0
The point size value entered is less than or equal to zero.
minidriver name :Type must = 128
The value in the Type box does not equal 128.
minidriver name :VertRes must be > 0
The vertical resolution value entered is less than or equal to zero.
minidriver name :Weight should be 400 or 700
The value entered in the weight box is not equal to either 400 or 700.


CTT Dialog Box Messages

Can’t have LastChar < FirstChar
The specified character code for the first character is greater than the specified character code for the last character.
Must be 0 < n < 255
The specified character code is not an integer value between 1 and 255.


General Error Messages

Bad format Font file: minidriver name
The format of the specified font file is invalid.
Bad Format GPC file
A GPC file with an invalid format is requested for loading.
Bad Format RC File
The specified RC file uses an invalid format.
Can’t add atom for minidriver name
The PC cannot process a string. If this error occurs, exit Unitool and Windows.
Can’t change drives: minidriver name
The PC cannot switch to the specified driver.
Can’t create directory:minidriver name
The PC cannot create the specified directory.
Can’t Find File minidriver name
The requested file cannot be found.
Can’t find minidriver name in UNITOOL.INF
The PC cannot find an entry for the specified minidriver in the UNITOOL.INF file.
Can’t find string with requested ID
The GPC file contains an entry in its string table that is not present in the corresponding RC file.
Can’t find UNITOOL.INF in Windows directory
The PC cannot locate the file named UNITOOL.INF.
Can’t read font file: minidriver name
The specified font file cannot be read.
Can’t read PFMEXTENSION, unable to access DRIVERINFO
The PFMEXTENSION structure in the PFM file cannot be parsed. (If Unitool cannot parse the PFMEXTENSION structure, Unitool cannot read the DRIVERINFO structure.)
Can’t read RC File: minidriver name
The specified font file cannot be read.
Can’t save CTT reference change in: minidriver name
After a reference to a CTT is changed, the font file cannot be changed.
Can’t Save File minidriver name
The file cannot be saved using the specified filename.
Can’t save RC File: minidriver name
The specified resource file cannot be saved.
Can’t update Read Only file: minidriver name
A font file saved with read-only permission cannot be saved.
Default Font ID in: minidriver name Is no longer valid
The font file containing the minidriver default font was deleted.
Global Memory Allocation failed
The required memory cannot be allocated. If this error occurs, exit Unitool and Windows without attempting to save data.
GPC and/or RC file is read only,Save will be disabled:minidriver name
Either the specified resource file or the associated GPC file was saved with read-only permission.
Illegal CTT file entry:minidriver name
The entry for a CTT file in the specified resource file cannot be parsed.
Illegal Font file entry:minidriver name
The entry for a font file in the specified resource file cannot be parsed.
Illegal GPC file entry:minidriver name
The entry for the GPC file in the resource file cannot be parsed.
Illegal DRV name, must be between 1-8 chars
The entered minidriver name exceeds 8 characters.
Illegal string
The entered formatted string is invalid.
Illegal STRINGTABLE entry in:minidriver name
The specified resource file contains an invalid string table entry.
Must Provide a Destination Directory
The directory name (specified as part of the minidriver name) is invalid.
Newer version of Unitool needed to read this file
A newer version of Unitool must be loaded in order to edit the specified GPC file.
No errors were found
No errors were found after the Validate Now command is selected.
No structures of this type have been defined
A modification is attempted on data that in not defined.
Not a device font PFM file: minidriver name
The specified font files do not correspond to a device font.
Numeric Value required
A non-numeric value is entered instead of a numeric value.
OverWrite the file: minidriver name
An existing minidriver file is about to be overwritten.
RC file doesn’t reference GPC file:minidriver name
The specified resource file does not contain a reference to the listed GPC file.
RC file lists duplicate font files
The specified resource file contains duplicate font file entries.
RC file lists duplicate CTT files
The specified resource file contains duplicate CTT entries.
RC file has non-contiguously numbered font files
The specified resource file contains noncontiguous identifiers for minidriver font files.
Requested CTT ID value already used by: minidriver name
The specified CTT identifier is already in use.
Replacement CTT ID doesn’t exist
The specified replacement CTT does not exist.
Save Failed, restoring original files
The GPC file cannot be saved and the original files must be restored.
STRINGTABLE missing END statement
The specified resource file contains a string table that is missing an END statement.
Target Directory doesn’t exist, Create?
The specified directory does not exist.
Unable to retrieve CTT file names, Can’t save file
The specified CTT files cannot be retrieved and the corresponding resource file cannot be saved.
Unable to retrieve font file names, Can’t save file
The specified font files cannot be retrieved and the corresponding resource file cannot be saved.
Unable to retrieve RC strings, Can’t save file
The minidriver’s resource strings cannot be retrieved and the file cannot be saved.
Unresolved STRINGTABLE reference for ID:minidriver name
The specified string-table reference cannot be resolved.


Printer Font Metric Structure

The following structure is used by all Printer Font Metrics (PFM) files used by drivers supported by the Microsoft Windows Universal Driver.

typedef struct
{
    short   sSize;
    short   iVersion;
    WORD    fCaps;
    short   iFontID;
    short   sYAdjust;
    short   sYMoved;
    short   iTransTab;
    short   sUnderlinePos;
    short   sDoubleUnderlinePos;
    short   sStrikeThruPos;
    LOCD    locdSelect;
    LOCD    locdUnSelect;
    WORD    wPrivateData;
    short   sShift;
} DRIVERINFO;

Members

sSize
Size, in bytes, of the structure.
iVersion
Version number of the structure. This documentation describes version 0x0301 (implied decimal for version 3.01).
fCaps
Bit flags describing capabilities. Currently, cCaps can only be DF_BKSP_OK to indicate that this fonts uses a CTT_WTYPE_PAIRED character-translation table to determine how to overstrike characters. If it is set, it is assumed that a single backspace character can be used to move the cursor to the start of the bounding box of the previous character prior to overstriking. If it is not set, it is assumed that explicit x-movement commands are required to position the cursor for an overstrike. If the font does not use a CTT_WTYPE_PAIRED translation table, this flag is ignored.
iFontID
Reserved; do not use.
sYAdjust
Distance to move the current y position, as expressed in master units, before printing characters in this font and size. This member is primarily used for double-height characters, and the specified distance to align the baseline accurately.
sYMoved
Distance to move the y position, as expressed in master units, after printing with this font and size. This member is also used to compensate for fonts that do not restore the cursor to the correct y position. It is primarily used for double-height characters.
iTransTab
Index of character-translation table resource for this font. This may zero, a positive, or negative number. If this is zero, use the character-translation table referred to in the ModelData dialog box for this model. If this is a negative number, the Universal Printer Driver will take the absolute value and use its own character-translation table resource with this identifier. If this is a positive value, the Universal Printer Driver will use the driver-defined character-translation table resource with this identifier value.
sUnderlinePos
Position of the hardware underline created by FS_OCD_ST_UNDERLINE_ON from the bottom of the character-bounding box. This member is measured in master units.
sDoubleUnderlinePos
Position of the hardware double underline created by FS_OCD_ST_DOUBLEUNDERLINE_ON from the bottom of the character-bounding box. This member is measured in master units.
sStrikeThruPos
Position of the hardware strikethrough created by FS_OCD_ST_STRIKETHRU from the bottom of the character-bounding box. This member is measured in master units.
locdSelect
Offset to the command descriptor to select this font.
locdUnSelect
Offset to the command descriptor to cancel the selection of this font. This member is NOOCD if not used.
wPrivateData
Private data to be used by the minidriver.
sShift
Number of pixels to shift each character to the left or right. Negative numbers shift to the right; positive numbers shift to the left.


Chapter 12 The Spooler

The Windows NT spooler is the component of the print subsystem that enables print jobs to be routed to local and network printers. It is sometimes referred to as the print subsystem since it is comprised of numerous components. The spooler’s responsibilities include:

  • Retrieving the location of the correct printer driver and loading it.

  • Accepting a data stream prepared by GDI and/or a printer driver for output on a particular type of printer.

  • Ensuring that the driver instructions go to the first available physical printer in a logical printer queue. The spooler supports printing over a network.

  • Spooling the data to a file if the printer is not available to print it. If necessary, the spooler converts the high-level function calls to EMFs before storing the print job to disk.

  • Delivering data to printers for printing, either directly or by playing back spooled files.

  • Providing print-related database management. The spooler maintains a forms database. It also manages a dynamic environment of printers, printer drivers, print providers, monitors, ports, and print jobs. The spooler maintains Registry information from which it can derive how many of these components are in the print subsystem at any time and the unique name of each component. The spooler also keeps track of the current status of each component.

The print spooler is loaded at system startup and continues to run until the operating system is shut down.


Spooler Components and IHV Development Opportunities

The components of the Windows NT spooler are outlined in the following table.
Component/Description | Default Executable | IHV Development Requirement s
Spooler Server Process: entrance to the spooler subsystem for both local and remote print requests. | spoolss.exe | None: the Microsoft-provided service cannot be replaced.
Print Request Router (PRR): routes a print request’s data stream to the local or network print provider. | spoolss.dll | None: the Microsoft-provided PRR cannot be replaced.
Local Print Provider: responsible for the local playback of spooled print jobs. This includes putting print jobs into spool files, managing despooling, and performing administrative tasks such as print queue management. | localspl.dll | None: the Microsoft-provided local print provider cannot be replaced.
Network Print Provider: responsible for translating print requests from Win32 printing function calls to a vendor-specific form that can be used by the network. | win32spl.dll | Printer IHVs typically do not need to develop a network print provider. Network IHVs must develop one.
Print Processors: perform despooling. | winprint.dll | Winprint handles three spool file formats: raw, EMF, and text; it is the default print processor. Print processors and printer drivers work together, so printer vendors may wish to develop another print processor if the vendor supports a different spool file format.
Port Monitors: Responsible for communication between spooler and printing device. Typically communicates with base I/O drivers (i.e. serial and parallel drivers) for device I/O, but may also call different interfaces, such as Windows Sockets, SCSI, etc. | localmon.dll | Printers that use parallel or serial ports can use localmon.dll. IHVs with printers that connect to other types of ports, such as SCSI or Ethernet LAN cards) need to develop a port monitor. See Print Monitors for more information.
Language Monitors: allow the spooler to configure and monitor the status of a bidirectional printer. | none | If the printer manufacturer’s device is bidirectional, vendors can develop a language monitor for it. See Print Monitors for more information.


Chapter 13 Print Processors

Once the print provider determines that a job can be printed, it calls the print processor. A print processor is the DLL that interprets a print job’s data type for the spooler. It works in conjunction with the printer driver to despool data types for the spooler during spool file playback.

The print processor returns the data it converts to the spooler. The spooler then passes this converted data to the print monitor.

Windows NT supplies the following two print processors:
Print Processor | Platform Support
winprint.dl | Windows NT systems
sfmpsprt.dll | Macintosh

Printer vendors can implement a print processor if they support a custom data type. Sample code for winprint is shipped with the Windows NT DDK.


Document Data Types

The spooler stores a data type attribute with each document that specifies the format of the data. This can be, for example, raw data to be sent directly to the printer port, or a set of EMF files to be passed to the printer driver as they are despooled. An example of a print processor is a PostScript RIP, which reads PostScript code and rasterizes it into a bitmap for output.

A print processor might recognize one or more data types. For example, winprint handles three spool file formats: raw, text, and enhanced metafiles (EMFs).

Raw Data

The raw data type indicates that the print job has already been fully rendered by GDI/DDI, and it does not need any more processing. Raw data streams can be printed directly or can be put into spool files.

Most Windows NT printing clients send raw jobs. Raw spool files are device-dependent; that is, the spooled data is destined and formatted for a particular device and does not need to be printable on any other device. An example of a raw spool file is an encapsulated PostScript file, which is formatted to be understood by the PostScript printer for which it is destined. Another example of raw data is PCL.

Text Data

When a print job’s data type is text, the print processor sends the incoming job the graphics engine. The graphics engine returns a print job which, when sent to the print device, prints the original text using the print device’s default paper source, with the default font, orientation, margins, and duplexing. This achieves the same result as saving the incoming job to a file, opening that file with Notepad, and then printing the job.

EMF Data

EMF spool files are used to greatly reduce the time duration between when an application-initiated print request occurs and when control is returned to the application by the operating system. This is done by recording the GDI function calls that produce the application’s graphic object on the specified printer in an EMF-format file, called a print spool file. The spool file can be built quickly this way and then control is returned to the application. Later, using a background thread, the Windows NT print spooler performs the more time-consuming task of executing the GDI calls so that the graphic object comes out on the printer.

A second advantage to the use of EMF spool files is device independence. The bulk of an EMF is an array of variable-sized records that encode the GDI function calls necessary to reproduce the picture when the EMF is “played back”. EMF spool files encode graphics information in such a way as to maintain device independence.

On Windows NT, EMF files are spooled locally and despooled by the server. One EMF file is created per print job. GDI puts font information into the EMF file if the server does not have this font information.


Print Processor Design and Implementation Notes

Function declarations and structure definitions can be found in winsplp.h.

Print processors must implement and export the following functions:
Function | Meaning
EnumPrintProcessorDatatypes | Enumerates the data types supported by a print processor.
OpenPrintProcessor | Opens the print processor for printing.
PrintDocumentOnPrintProcessor | Prints a document on the print processor.
ControlPrintProcessor | Provides control over printing a document.
ClosePrintProcessor | Closes the print processor.

See the Graphics Driver Reference for details.


Chapter 14 Print Monitors

Windows NT supports two types of print monitors: port monitors and language monitors. This chapter describes both monitor types, and provides design and implementation guidelines.

The Windows NT DDK supplies sample code for each type of monitor:
Directory Name | Print Monitor Type and Comments
localmon | Port monitor. The resultant executable, localmon.exe, is the default port monitor shipped with Windows NT.
pjlmon | Language monitor for bidirectional printers that understand PJL.


Port Monitors

A port monitor is responsible for the communication channel between the spooler and the printer device. It supports one or more types of communication channels. For example, the Windows NT default port monitor, localmon, supports parallel, serial, and file ports.

To be able to print to a physical printer, Windows NT requires that the printer be connected to one or more ports that are controlled by port monitors. A port represents the destination for the data being sent to the printer.

Windows NT supplies five port monitors:
Port Monitor (DLL) | Supported Communication Channels
Local (localmon.dll) | Parallel and serial ports; files.
Macintosh (sfmmon.dll) | Network adapter card, via AppleTalk protocol, by which an AppleTalk print server or print device is accessible.
Digital (decpsmon.dll) | Network adapter card, via DECNet or TCP/IP, by which a DEC print device is accessible.
HP (hpmon.dll) | Network adapter card, via DLC, by which an HP JetDirect DLC print device is accessible.
LPR (lprmon.dll) | Network adapter card, via TCP/IP, by which an LPD print server or print device is accessible.

The Windows NT Resource Kit contains a high level overview of the supplied monitors. Vendors need to develop a port monitor if their printing device connects to a different type of I/O port, such as a SCSI or Ethernet LAN port.


Language Monitors

A language monitor allows the spooler to configure and monitor the status of a bidirectional printer. A bidirectional printer supports two-way communication between itself and the print spooler running on the computer to which the printer is connected.

Using a language monitor, the spooler can request configuration and status information from the printer, and/or the printer can send unsolicited status information to the spooler whenever certain events occur on the printer. For example, a language monitor could be written for a printer that can detect and communicate events such as paper jams, no paper, low or no toner, and/or the fact that the printer is warming up and not yet ready to print.

Windows NT supplies the following language monitor:
Language Monitor (DLL) | Printer Language Supported
PJL monitor (pjlmon.dll) | Printer Job Language (PJL)

If a printer manufacturer’s device is bidirectional and uses a language other than PJL, vendors can develop a language monitor for it. A language monitor can also be developed when a vendor wishes to add data, such as printer-specific control information, to the print stream going to a unidirectional printer. Language monitors are completely optional in the Windows NT print architecture.

For the printer to understand requests from the computer and for the computer to understand solicited or unsolicited responses from the printer, a vendor must define a language. For example, the HP LaserJet 4Si is a bidirectional printer device and Printer Job Language (PJL) is the language that implements its bidirectional communication.


Port and Language Monitor Interaction

Figure 14.1 shows the path taken by printer data from the print processor to a printer that a) has a language monitor associated with it; and b) does not have a language monitor associated with it.

Figure 14.1 Printing data flow (a) without a language monitor; (b) with a language monitor.

When a language monitor is associated with a printer driver during the driver’s installation, all print data that flows from the printer driver to the printer goes through the language monitor before being passed to the port monitor. That is, all calls that the spooler makes to print a job go through a language monitor if one is associated with the printer.

A language monitor should then make the appropriate calls to the port monitor with which it is working. For example, a language monitor’s StartDocPort implementation might call the associated port monitor’s StartDocPort implementation to do generic print job setup and then set up its own language-specific job control information for the first port write.

Note that a language monitor never communicates directly with a printer port; it relies on the port monitor to send and receive printer data for it. Note also that, regardless of printer type, the print data stream should always be handed to the printer’s port by a port monitor.

Language monitor writers should gracefully handle attempts to read from printers and files that cannot be read back from. For example, a language monitor written for a driver class that contains both bidirectional and unidirectional printers should handle ill-fated read attempts on unidirectional printers.

Because language monitors and port monitors are discrete architectural components, it is possible for vendors of bidirectional printers to develop only a language monitor and use an existing port monitor. Or, if vendors also require special port monitor features that will only be used in conjunction with their language monitor, they can develop a combined language monitor and port monitor (LMPM). An LMPM can work with other port monitors.


Print Monitor Installation

A language monitor is associated with a printer driver when the Win32 AddPrinterDriver function is called with the language monitor’s name specified in the pMonitorName field of the DRIVER_INFO_3 structure. This parameter information is specified in a printer driver INF file using the LanguageMonitor keyword.

Default Windows NT printer driver installation information can be found in the system-supplied ntprint.inf file. OEMs can supply an INF file that associates a printer driver with a custom language monitor or a Microsoft-supplied one. See the Programmer’s Guide for driver installation details.


The Print Monitor API

Port and language monitor function prototypes and structure definitions can be found in the Windows NT DDK winsplp.h include file.

The following table contains the print monitor API and implementation requirements on a per-monitor basis. InitializePrintMonitor and DllEntryPoint are the only two functions that a monitor’s DLL must export; the spooler accesses all other functions through the function table returned by InitializePrintMonitor. For this reason, except for InitializePrintMonitor and DllEntryPoint, the names of all other required functions are pseudonames that can, in fact, have any name that the monitor developer wishes.
 |  | Implement?* |  |
Function Name | Description | PM | LM | LMPM
InitializePrintMonitor | Initializes the print monitor. | X | X | X
DllEntryPoint | Entry function; called when the spooler loads a monitor’s DLL with the Win32 LoadLibrary function. | X | X | X
OpenPort | Opens a port for a newly connected printer. | X | - | X
OpenPortEx | Opens a port for a newly connected printer. | - | X | X
ClosePort | Closes a port when there are no printers connected to it. | X | X | X
StartDocPort | Performs the tasks required to start a print job on a port. | X | X | X
WritePort | Writes data to a printer port. | X | X | X
ReadPort | Reads data from a printer port. | X | X | X
EndDocPort | Performs end-of-print-job tasks on a port. | X | X | X
AddPort or AddPortEx | Creates a port. | X | - | X
DeletePort | Deletes a port. | X | - | X
EnumPorts | Enumerates the ports available for printing on a server. | X | - | X
ConfigurePort | Performs port configuration. | X | - | X
SetPortTimeOuts | Sets a time-out value on an open port. | o | - | o
GetPrinterDataFromPort | Gets data from a bidirectional printer by providing IEEE 1284 support. | o | o | o
* X = required; - = do not implement, o = optional |  |  |

To perform the basic requirements of monitoring printers connected to ports and sending print job data through ports, all print monitors must implement the following functions:
InitializePrintMonitor | StartDocPort
DllEntryPoint | WritePort
OpenPort or OpenPortEx | ReadPort
ClosePort | EndDocPort

For information on DllEntryPoint, see the Win32 SDK.

Port monitors and LMPMs are required to implement the print monitor functions that perform port management. These functions are:
AddPort or AddPortEx | EnumPorts
DeletePort | ConfigurePort

Because port management is the port monitor’s responsibility, a pure language monitor should not implement any of these routines.

The remaining functions of the print monitor API, SetPortTimeouts and GetPrinterDataFromPort, can be optionally implemented by port monitors and LMPMs. Pure language monitors can implement GetPrinterDataFromPort, but should not implement SetPortTimeouts because it is a port management function.


Print Monitor Initialization

Both port and language monitors implement InitializePrintMonitor functions that provide MONITOREX structures. As a result, a function’s parameter list, return type, and purpose are the same for those function members that both monitors are required to implement. An LMPM should supply only one InitializePrintMonitor and DllEntryPoint implementation and one MONITOREX structure.

The spooler calls OpenPortEx when a language monitor has been associated with a printer; otherwise, it calls OpenPort.


Initializing Monitors at Spooler Start-up

During its initialization, the spooler does the following for every port and language monitor it finds in the registry:

  • Loads the monitor’s DLL.

  • Calls the monitor’s InitializePrintMonitor function to obtain its function table. The table is returned as a set of function pointers in a MONITOREX structure. The monitor must initialize pointers to the routines listed in the must-implement section of The Print Monitor API, or the spooler fails the monitor’s creation.

  • If the monitor is a port monitor or LMPM, the spooler calls the monitor’s EnumPorts function to obtain the list of ports available on the local machine for printing that this monitor supports.

  • For every printer to be added, the spooler calls the associated monitor’s OpenPort or OpenPortEx function to acquire a port handle for every port that has been assigned to that printer.

Once initialization is complete, the spooler has a record of which print monitors and printers exist, and which ports are associated with each monitor and printer. Note that port setup is performed before printers are associated with ports.


Print Monitor Design and Implementation Notes

This section provides information pertinent to the design and implementation of port and language monitors.


Opening and Closing a Port

The OpenPort function acquires the specified port and returns a handle to it. The spooler uses this handle to print a job with subsequent calls to StartDocPort, WritePort, ReadPort, and EndDocPort.

For best performance, a port monitor’s implementation of OpenPort should perform all one-time port initializations. For example, localmon’s OpenPort function determines whether the logical port is a file, or a serial or parallel port, and then acquires and/or sets the port’s state as needed in order to print to it. If the port is a serial port, localmon acquires and stores its current control settings and its time-out parameters for later read and write operations by calling the Win32 GetCommState and GetCommTimeouts functions.

If necessary, the monitor should lock down the specified port so that other processes cannot use it.

Port names are unique on a per machine basis, and only one monitor can have a port at any time. For this reason, the spooler never makes two back-to-back OpenPort/OpenPortEx calls on a port with which two logical printers are associated. If the spooler needs to send a print job through the monitor that doesn’t hold the port, and if the port is not being used by the monitor that holds it, the spooler calls ClosePort on the holding monitor before calling OpenPort/OpenPortEx on the needed monitor. This would be true, for example, when a physical printer receives PostScript jobs through one monitor and PCL jobs through a different monitor.

The spooler calls ClosePort when a print job needs to be printed through a different language monitor, when a printer is is no longer associated with the port, and at spooler shutdown. ClosePort should release the port handle and clean up any resources associated with the closed port, such as memory that the monitor allocated for data structures.

OpenPortEx

Language monitors and LMPMs implement OpenPortEx. The spooler passes two additional parameters to this routine: the port monitor’s function pointers and the printer name. A language monitor should validate the port monitor by checking the function pointers for all the port monitor routines it requires. A language monitor acquires a handle to the port monitor when its OpenPortEx routine calls the OpenPort function of the specified port monitor.


Printing a Print Job

When the spooler determines that a particular spooled job can be printed, it calls the monitor’s implementations of StartDocPort, WritePort, and EndDocPort to print the job.

On Windows NT, a printer is actually a printer queue, or logical printer, that can dispatch print jobs to one of several ports associated with it. The spooler can use a printer queue of several associated ports to optimize printing. It can do this by making efficient use of the physical printers connected to the logical printer’s ports.

For example, the ports LPT1, LPT2, and LPT3 could all be associated with a single logical printer in one call to the Win32 AddPrinter routine. Then, when the spooler has a job that needs printing, it might attempt to dispatch the job to the physical printer connected to LPT1 by calling through the associated language monitor’s StartDocPort routine. Note that the language monitors associated with these three ports could all be different.

If the physical printer is busy with another job, or if it has problems such as an empty paper tray, it can return an error condition to the monitor. The language monitor’s StartDocPort function should then call the Win32 SetPort routine with the appropriate error code to notify the spooler of the problem. The spooler can then try sending the job to the next physical printer, attached to LPT2, in the queue. If this fails, the spooler can finally try printing to LPT3. The spooler will not send any more print jobs to a port that signals a failure until the error condition is cleared on it.

Language monitors might call WritePort (and/or ReadPort) on a port monitor outside of a StartDocPort/EndDocPort pair, for example, to check status on a printer. Port monitors that do not allow this can fail the call, and language monitors should be written to handle such a failure.

StartDocPort

The StartDocPort routine performs whatever tasks are necessary to start a print job on the specified port. A port monitor’s implementation might include tasks such as:

  • Obtaining a handle to the printer to be used by calling the Win32 OpenPrinter function.

  • Opening the port and performing any port setup required, such as initializing its end-of-file position for writing, by calling the Win32 CreateFile and SetEndOfFile functions.

  • Allocating any resources required on a per print-job basis.

The language monitor’s StartDocPort routine might perform tasks such as:

  • Validating the port monitor and printer with which the language monitor will communicate.

  • Calling the port monitor’s StartDocPort function to perform any generic per print-job initializations.

  • Allocating any resources required by the language monitor on a per print-job basis.

  • Determining whether the printer understands the language monitor’s supported language and, if so, starting a thread in which to listen for unsolicited status information from the printer.

If a monitor’s StartDocPort routine cannot perform the setup required, it should clean up as necessary and return FALSE. The spooler retries StartDocPort as often as the application retries.

WritePort

The WritePort routine sends a block of print data to the printer. A WritePort implementation could send the data by calling the Win32 WriteFile function. The spooler calls WritePort as often as is necessary to complete sending the entire print job. The spooler determines the size of the data block sent in each WritePort call, so a monitor should make no assumptions about the block size.

A language monitor’s WritePort routine can add printer job control data to print data before sending it to the port monitor. When the language monitor returns to the spooler after adding bytes, such as printer control information, to the print data stream, it must not increment the byte count of data sent to the printer. For example, if your language monitor adds .25K of information to a 1K data stream before sending it to the port monitor, it must report sending only the original 1K of data when it returns.

EndDocPort

The EndDocPort function performs end-of-print-job tasks on the port. The spooler calls EndDocPort when there is no more data to send for the current print job.

A port monitor’s EndDocPort implementation should call the Win32 SetJob routine with dwCommand set to JOB_CONTROL_SENT_TO_PRINTER to notify the spooler that the entire print job has been sent to the printer. Note that a pure port monitor cannot guarantee that a job has actually been printed; it can only guarantee that it has sent the entire job to the printer.

If a print job has been sent to a bidirectional printer, a language monitor’s EndDocPort can start a thread that listens for true end-of-job notification; that is, notification from a bidirectional printer that it has printed the entire job. When the language monitor receives this notification, it should call the Win32 SetJob routine with dwCommand set to JOB_CONTROL_LAST_PAGE_EJECTED to provide the spooler with true end-of-job notification.

Other tasks that a port monitor’s EndDocPort implementation might perform include:

  • Clearing any data that the port has cached by calling, for example, the Win32 FlushFileBuffers routine.

  • Closing the printer by calling the Win32 ClosePrinter function.

  • Freeing any per print-job resources allocated by StartDocPort.


Reading from a Port

A language monitor’s ReadPort implementation can call the port monitor’s ReadPort routine to get information from a bidirectional printer that it supports. The port monitor should simply return the raw data it receives from the printer to the calling language monitor.

Language monitors set time-outs on port monitors, and port monitors set time-outs on the physical device. A port monitor’s ReadPort implementation should time-out and log an error code if it cannot read data from the printer associated with the specified port or if it receives no response from the printer within some appropriate time interval. A port might return zero bytes on a read; in this case, ReadPort should return success.

Note that a port monitor must implement ReadPort, because a language monitor’s only method of communication with a bidirectional printer is through a port monitor.

Only language monitors are able to parse the strings of data read from a port to which a bidirectional printer is attached. A language monitor’s ReadPort or GetPrinterDataFromPort implementation calls the port monitor’s corresponding function to get data from a bidirectional printer. It is the port monitor’s ReadPort or GetPrinterDataFromPort function that reads any data available from the printer and passes that data back to the language monitor, but the port monitor should not change this data in any way. It is up to the language monitor to interpret the meaning of the data read from the printer.

The language monitor can call the port monitor’s GetPrinterDataFromPort or ReadPort from a background thread on a periodic basis, for example, every few seconds, to read unsolicited status information from a bidirectional printer. The PJLMON source code shows how to read unsolicited printer status information in this way.

A language monitor can support GetPrinterDataFromPort to query information from a bidirectional printer that supports the IEEE 1284 standard. A vendor defines the status request strings passed from the language monitor to the associated bidirectional printer.

A port monitor can implement GetPrinterDataFromPort to accommodate communication between a language monitor and bidirectional printer that support the IEEE 1284 specification. A port monitor’s GetPrinterDataFromPort routine makes supported IOCTL calls to IEEE 1284-compliant bidirectional printers and returns any data received from the printer to the calling language monitor. GetPrinterDataFromPort should use the Win32 DeviceIoControl function to send the IOCTL to the printing device.

All monitors that support GetPrinterDataFromPort should fail immediately with ERROR_INVALID_PARAMETER for registry keys they do not understand. This forces the spooler to obtain the key value itself from the registry.

A port monitor can implement GetPrinterDataFromPort to make IEEE 1284 specification IOCTL calls to bidirectional printers that support this specification. GetPrinterDataFromPort should call the Win32 DeviceIoControl function to send the IOCTL to the printing device.


Managing a Port

Except for remote port deletion, the spooler does not support remote port configuration.

Configuring a Port

A port monitor’s ConfigurePort implementation depends on the type of port(s) that the monitor supports. For example, if the port is a serial port, ConfigurePort could allow serial port settings, such as baud rate and parity, to be configured. The configuration dialog box from the Control Panel’s Ports applet can be used for serial port configuration. Likewise, a parallel port could allow the transmission retry time-out to be configured, and a network port could allow an address to be configured.

Adding a Port

The spooler calls a port monitor’s AddPort or AddPortEx function to create a port and add it to the spooler environment. When the monitor successfully adds a port, the spooler knows that the port is supported by and available for printing through that monitor. Then, when a printer has been associated with this port, the spooler can ask the monitor to open the port for printing.

AddPort allows a port to be added interactively, so it should prompt users to enter the port name in a dialog box on the window associated with hWnd.

A port monitor should ensure that no duplicate port names are added to the spooler environment by validating that the supplied port name is unique for all ports on the machine. It can obtain a list of the machine’s ports by calling the Win32 EnumPorts function. Port names are unique on a per machine basis. A port monitor should also verify that the port is one that it supports.

Deleting a Port

As stated previously, a port’s setup is done before printers can be associated with it. Likewise, all printers must be disassociated from a port before it can be deleted. An application that attempts to delete a port with which a printer is still associated gets an error.

Setting Port Timeouts

Port timeouts can be established with the one-time port initializations performed by OpenPort, or by calling a port monitor’s SetPortTimeOuts implementation. To avoid hung or unreasonably delayed port responses, port monitors should always establish reasonable time-outs before actually calling a port.

SetPortTimeOuts can use the Win32 GetCommTimeouts and SetCommTimeouts functions to establish the time-out parameters for a port.


Porting Graphics Drivers To Windows NT 4.0 from 3.51

A significant change to be introduced in Windows NT™ version 4.0 is the move of GDI and graphics drivers from the Windows NT Win32 subsystem to the Windows NT executive. The benefits of this move include improved graphics performance and memory savings. The move also means that existing Windows NT Win32 display and print drivers will not run on Windows NT 4.0.

This document briefly describes why and how the Windows NT graphics architecture has changed, and why the change makes pre-4.0 drivers incompatible in Release 4.0. It then provides steps for porting Windows NT Win32 subsystem graphics drivers into the Windows NT executive. The explanation sections do not provide a comprehensive look at the new architecture; they provide just enough detail, with a few pros and cons, for background. If you want to skip these sections and begin your port, go to Porting Steps for Graphics Drivers.

Note that your new Windows NT 4.0 graphics driver will not run on pre-4.0 systems.

Note to Video Miniport Driver Writers: Because the video miniports and the video port were already running in the kernel, they did not change at all. All video miniports that worked in Windows NT 3.51 will work in Windows NT 4.0. If you only had a miniport driver that worked with framebuf.dll, or vga.dll, vga256.dll and vga64k.dll, then you do not have to port your driver. These miniport drivers still run with the new versions of their display drivers.


Snapshot of the Windows NT 3.51 and 4.0Graphics Architectures

The Windows NT 3.51 and 4.0 graphics architectures are shown in Figures 1 and 2, respectively. Figure 2 shows the redesign of GDI as a kernel-mode component.

Although there are many different ways to print, the printing path in Figure 2 shows the most common way. The print driver sends data to the spooler via the Graphics Engine, and then the spooler sends data to the parallel/serial driver. Note that the spooler still runs in user mode as a separate process.

Figure 1 Windows NT 3.51 Graphics System Architecture

Figure 2 Windows NT 4.0 Graphics System Architecture


Why Change the Graphics Architecture?

In all previous Windows NT releases, GDI (and USER) were the Graphics Subsystem, which was a user-mode process like every other application. The kernel gave this process special security privileges to perform various operations that normal processes could not perform, such as communicating directly with the hardware. With this architecture, every GDI call was basically an inter-process call. In order to execute a graphics operation requested by an application, such as a bit-block transfer, the system marshalled the operation in the context of the calling process and sent it to the Graphics Subsystem process using an inter-process communication mechanism. The Graphics Subsystem unmarshalled it and performed the operation. This was a complicated and often slow task.

The pre-4.0 architecture did have its advantages. GDI (and USER) ran in a separate process (address space), protected from all other applications and processes. Because it was in user mode, GDI interacted with the kernel using well defined user-mode APIs, and was guaranteed to not affect the kernel.

In order to increase performance, reduce memory usage, and decrease the complexity of the code in Windows NT 4.0, GDI (and USER) have been moved into the kernel address space. This means that GDI (and USER) are now in the same privileged kernel-mode address space as the Windows NT executive (the kernel, memory manager, and I/O system), the file system, and all other drivers.

The advantages of the new architecture include eliminating LPC calls from applications to the Graphics Subsystem, reducing the number of threads in the system, and the ability to access data directly from the address space of the calling process. The new architecture also means that GDI and graphics drivers can overwrite data structures belonging to the file system, disk driver, or memory manager, possibly causing data corruption. Because of this, the quality of graphics drivers becomes even more critical to the overall reliability of the system.


Why Change the Graphics Drivers?

Windows NT 3.51 graphics drivers had access to all user-mode system functions provided in user-mode DLLs such as kernel32.dll and advapi32.dll, and to all the GDI helper routines exported by winsrv.dll. In Windows NT 4.0, graphics drivers have access to functions exported by win32k.sys only. Except for a few obsolete functions, win32k.sys exports all the original exports that were found in winsrv.dll, such as EngBitBlt. The win32k.sys library also exports a new set of functions that provide basic operating system services, such as memory allocations and synchronization.

So why do all the graphics drivers need to change? One could argue that the existing graphics drivers should just be loaded in the kernel, with Microsoft’s assurance that they work.

The first step to this approach would be to write stubs for all the Win32 functions called by the driver. These stubs would map the Win32 functions to their kernel-mode equivalents, letting drivers run with no modifications. Many of the functions, such as the memory allocators, would be easy to stub. However, some of the drivers used a very large number of user-mode functions, and some of these functions were almost impossible, technically, to implement in kernel mode. Since all of the required stubs could not be implemented, this meant that at least a few of the 3.51 drivers would not run.

Special Features of a User-Mode Process

It was also discovered that some Windows NT 3.51 drivers depended on certain special features of a user-mode process:

  • Some drivers assumed that they were always running in the context of the same process, with a consistent view of all data. This is not true in the kernel. Kernel-mode code executes in the context of the calling application, which means that the kernel-mode code has a different view of the user-mode address space with each process.

  • Some drivers were using stack space in very large amounts. This can no longer be done because stack space in the kernel is very limited. In the kernel, stacks are limited to 12K or 16K, depending on the platform. All of the user calls, GDI calls, display calls, page faults and interrupts must be handled within that stack space, which means each component has only a small subset of that total amount.

  • Some drivers were using floating operations. Floating point operations are not enabled by default in the kernel. Unless specifically set up by some other component, no floating point operations are allowed.

Any driver relying on these features in user-mode would cause Windows NT to crash when running in kernel mode.

For all of these reasons, it was decided that graphics drivers should be moved to the executive and revised.


Porting Steps for Graphics Drivers

This section provides step-by-step porting details that pertain to both display and print drivers.

Most functions that were originally exported by winsrv.dll are now exported by win32k.sys, so a simple relink of the driver resolves references to these functions. However, all other functions imported by your driver (from kernel32.dll, advapi32.dll, or any other DLL) must be replaced or eliminated because they are no longer available. If your driver directly references functions from any image other than win32k.sys, the system will not load your image. Therefore, the basic requirement for enabling a graphics driver to run under Windows NT 4.0 is to remove any calls to Win32 functions, and replace them with the calls exported by win32k.sys.

An easy way to determine exactly which functions your driver calls is to dump the list of imports, or external references, in your driver. One way to obtain this list is to run the following command:
link -dump -imports your_driver.dll

Here are some common changes to make to your graphics drivers to port them from Windows NT 3.51 to 4.0.

Step 1. Clean out the files used to compile your driver.

  • Delete the .def file that described the exports from your graphics driver in 3.51; it is no longer necessary.

  • Modify the sources files, which contain the list of files compiled to generate your driver. Update the file list from your 3.51 sources file, if necessary, and then use this list to define SOURCES in one of the sources files shipped with the sample Windows NT 4.0 drivers. This ensures that you pick up the new TARGETTYPE and other fields, enables warnings as errors, and so forth.

  • Change your main header file so that it does not include windows.h. This will help you track down the now obsolete Win32 function calls in your 3.51 source code. Your main header file should look something like the following:

    #include <stddef.h>
#include <stdarg.h>
#include <limits.h>
#include <windef.h>
#include <wingdi.h>
#include <winddi.h>
//These include files are for display drivers only.
#include <devioctl.h>
#include <ntddvdeo.h>
#include <ioaccess.h>
//These include files are for print drivers only.
#include <gdispool.h>
//Private defines
#include <my_headers>

    Note Due to problems with the spooler API header file, you might encounter compiler warnings in the pre-beta Windows NT 4.0 release. These problems will be fixed by the official Windows 4.0 release.

Step 2. Replace Win32 calls with new kernel-mode counterparts.

  • Critical sections have been replaced with semaphores:

    CRITICAL_SECTION

    HSEMAPHORE

    InitializeCriticalSection

    EngCreateSemaphore

    EnterCriticalSection

    EngAcquireSemaphore

    LeaveCriticalSection

    EngReleaseSemaphore

    DeleteCriticalSection

    EngDeleteSemaphore

    Note that where storage for a critical section was provided by the caller (the functions took a pointer to the critical section), the semaphore routines always use a handle (storage is allocated and destroyed within the semaphore routines).

  • Calls to Win32 debugging functions must be replaced with their new GDI equivalents:

    Win32 Debug Function

    GDI Debug Function

    DebugBreak

    EngDebugBreak

    OutputDebugString

    EngDebugPrint

    The debug output routine can be copied from the sample drivers. This routine supports debug levels, and automatically appends the driver name to the debug message. The following is the code sample for the debug output routine:
    VOID DebugPrint(LONG DebugPrintLevel, PCHAR DebugMessage, ...)
{
    #define STANDARD_DEBUG_PREFIX "MyDisplay:"
    va_list ap;

    va_start(ap, Debugmessage);
    if (DebugPrintLevel <= DebugLevel)
    {
        EngDebugPrint(STANDARD_DEBUG_PREFIX, DebugMessage, ap);
        // The following line appends the newline
        // automatically.
        // EngDebugPrint("", "\n", ap);
    }
    va_end(ap);
}

  • Memory allocators must be replaced with the newly provided kernel-mode GDI equivalents. The following are mappings for the most common memory functions:

    Win32 Memory Function

    KM GDI Memory Function

    LocalAlloc, GlobalAlloc, VirtualAlloc

    EngAllocMem

    VirtualFree, GlobalFree

    EngFreeMem

    LocalLock, GlobalLock, LocalUnlock, GlobalUnlock

    none (not used)

    There are some enhancements to EngAllocMem. It supports only the zero memory option (FL_ZERO_MEMORY) because the other options are not useful. Also, a third parameter, tag, was added to EngAllocMem. This tag is used by the system to provide information about resources. Both the kernel debugger !pool command and the user-mode poolmon utility can be used to track the amount of memory associated with each tag. The tag should be specified in reverse order, with single quotes. It is 4 bytes in size, and is stored in the pool header as 1 DWORD. The following is a sample call:
    ppdef = (PPDEF) EngAllocMem(FL_ZERO_MEMORY,
              sizeof(PDEV),'agxD');

    Note Memory is often referred to as pool in the kernel. The kernel memory functions are actually called ExAllocatePool and ExFreePool.

    Note Heaps are not supported in kernel mode, so you cannot destroy the heap to clean up memory.

  • There are no process/thread creation routines available to your driver. Your driver must be redesigned to work without them.

  • There is no support for registry functions that pass data to kernel-mode drivers. Your driver must be redesigned to work without them.

Any remaining Win32 functions in the original Windows NT 3.51 driver must be removed. If your driver absolutely requires a function that is not currently exposed, then Microsoft should be made aware of this requirement so the function can be exposed or an appropriate alternative can be found.

Step 3. Clean out calls to C-runtime routines where necessary.

String routines, such as strcmp, are not exported from win32k.sys. Calls to these routines should be removed if possible. Limited C-runtime functionality is available in libcntpr.lib, which statically links to the driver.

Step 4. Replace floating point operations with FLOATOBJs.

Don’t use native floating point operations. Windows NT does not support kernel-mode floating point operations on some systems. Specifically, the Windows NT kernel does not preserve floating point registers on kernel-mode stack switches, so using floating point registers in the driver corrupts an application’s floating point registers.

GDI has introduced a new set of services that allows graphics drivers to emulate floating point arithmetic. See the reference pages for the FLOATOBJ_Xxx services for more information.

Step 5. Be frugal with the stack.

Each thread is allocated a limited amount of stack space for all function calls and interrupts. The Windows NT kernel allocates an initial 12K on x86 and MIPS platforms, and 16K on ALPHA and PowerPC platforms. The stack can grow to accommodate callbacks from the kernel to user mode and back; the maximum stack size is 60K/64K in the kernel. Only 12K/16K of the stack is visible at any point in time. The only exception to these policies is OpenGL, which is allocated the entire 60K/64K stack at once.

In Microsoft’s driver design, a guideline of 1K of stack space per function is used. This heuristic was arrived at by studying the stack needs of a typical graphics driver. The first few kilobytes on the stack contain the functions called to get from the application to the driver, such as the system service functions. The last 2-3 kilobytes are reserved for page faults and interrupts. The fact that some driver calls spawn several nested function calls is also accounted for. For example, the S3’s implementation of DrvStretchBlt calls EngStretchBlt, which calls DrvBitBlt, which calls another internal S3 fill function. Using the 1K per function guideline means that only up to 4K of the stack is used for these four functions.

You should also look at the size of your functions’ parameters. Only small structures and very small arrays of data should be stored on the stack. Arrays of 1024 or 512 elements and arrays the size of the screen’s width should not be passed. Consider the following: if each array element is a DWORD, 1024 elements uses 4K of stack space.

Memory beyond the stack pointer should not be touched in assembly routines because interrupts can occur at any time.

A routine is guaranteed to be using too much stack space if you get an undefined external for something such as _chkstk.

Step 6. Update your driver with other Windows NT 4.0 driver changes.

Because all Windows NT 3.51 drivers are incompatible in Windows NT 4.0, a few things have been cleaned up in the drivers:

  • In DrvEnablePDEV, the second PWSTR parameter was converted to an HDEV.

  • The DrvAssertMode function now returns a BOOL. The status of the operation should be reported.

Step 7. Examine the “Special Features” section of this appendix.

The section entitled Special Features of a User-Mode Process, described earlier in this document, should be examined. Drivers depending on any of these features must be changed to avoid these problems.

Step 8. Read the porting section of this appendix specific to your graphics driver type.

The sections that follow this one are Porting Steps for Display Drivers and Porting Steps for Printer Drivers. Read the section that pertains to your driver type for additional type-specific porting hints.

Step 9. Install your new driver.

Install your new driver, and test. Use the kernel-mode kd debugger or windbg to debug your driver. NTSD can no longer be used for debugging graphics drivers.


Porting Steps for Display Drivers

This section contains porting notes that pertain to display drivers only.

Replace DeviceIoControl

The Win32 DeviceIoControl function has been replaced with EngDeviceIoControl. The return value is the inverse of what it used to be: 0 (ERROR_SUCCESS) for success, and non-zero for failure. The new function also takes one less parameter; DeviceIoControl’s last parameter, lpoverlapped, is no longer necessary.

Remove DCI code

DCI support has been permanently removed from the system. Therefore, all DCI code used in your driver must be removed.

Remove VFLATD

VFLATD functionality is not supported for kernel-mode threads. This means that your driver can no longer use this functionality to map a banked frame buffer in the miniport for GDI to write to.

DEC ALPHA Drivers

For drivers that work on the DEC ALPHA only: all calls to the special TRANSLATE_ADDR access macros should be removed. Calls to the WRITE_FAST access macros should be changed to use the WRITE_REGISTER access macros. On the ALPHA, the WRITE_REGISTER and WRITE_PORT macros handle all required address translation by calling into the HAL.

Note The GDI engine is still not equipped to handle sparse frame buffer access.

Other Windows NT 4.0 Display Driver Changes

Because all Windows NT 3.51 drivers are incompatible in Windows NT 4.0, a few things have been cleaned up in the display drivers:

  • The default patterns are now provided by the engine. A display driver no longer needs to initialize those patterns. The static array of patterns and the pattern enable/disable routines should be completely removed.

  • During driver initialization, DM_INTERLACED should no longer be checked during mode selection.

  • Some of the fields in the DEVMODE structure returned by DrvGetModes have changed slightly:

    1. The DM_INTERLACED flag in the dmFlag field is obsolete. Its usage must be completely removed.

    2. The name of the driver must be stored in the dmDeviceName field.

    3. The dmFields field must be filled in with the appropriate bits.

    The following is a copy of the sample code found in every Microsoft display driver:

    // Zero the entire structure to start off with.
memset(pdm, 0, sizeof(DEVMODEW));

// Set the name of the device to the name of the DLL.
memcpy(pdm->dmDeviceName, DLL_NAME, sizeof(DLL_NAME));

pdm->dmSpecVersion      = DM_SPECVERSION;
pdm->dmDriverVersion    = DM_SPECVERSION;
pdm->dmSize             = sizeof(DEVMODEW);
pdm->dmDriverExtra      = DRIVER_EXTRA_SIZE;

pdm->dmBitsPerPel       = pVideoTemp->NumberOfPlanes *
                          pVideoTemp->BitsPerPlane;
pdm->dmPelsWidth        = pVideoTemp->VisScreenWidth;
pdm->dmPelsHeight       = pVideoTemp->VisScreenHeight;
pdm->dmDisplayFrequency = pVideoTemp->Frequency;
pdm->dmDisplayFlags     = 0;

pdm->dmFields           = DM_BITSPERPEL       |
                          DM_PELSWIDTH        |
                          DM_PELSHEIGHT       |
                          DM_DISPLAYFREQUENCY |
                          DM_DISPLAYFLAGS     ;

Update Your .inf File

In order for the applet to install the new driver, the OptionType = VIDEO line in the driver's .inf file must be changed to:

OptionType = VIDEO40BETA


Porting Steps for Printer Drivers

This section contains porting notes that pertain to print drivers only.

In general, print drivers have become more complex than display drivers, largely due to fonts. However, Windows NT kernel-mode drivers should be as simple as possible, dealing only with the tasks required to render an image. Tasks unrelated to rendering should be moved, if possible, into the User Interface (UI) portion of the driver or into the print processor. The UI portion of the driver still runs in user mode. If you have a library that is shared by both DLLs, you need to write it so that it can be built and run in both kernel- and user-mode.

EMF Printing

Journaling has been replaced by metafile spooling, making the Windows NT model more similar to Windows 95. Metafile spooling also allows GDI to have a single recording model. See the reference pages for EngMarkBandingSurface, DrvStartBanding, and DrvNextBand for more details.

More on Replacing Calls to the Win32 API

New kernel-mode GDI functions have been implemented to replace some of the Win32 functions commonly used by print drivers. References pages for these functions are provided in this package. Parameters and usage should be checked in all cases, as the new GDI functions are not exact replacement functions.The following table shows the old Win32 functions and their corresponding new kernel-mode engine functions:
Win32 Function | Kernel-mode Engine Function
GetLastError | EngGetLastError
EnumForms, GetForm, GetPrinter, GetPrinterData, SetPrinterData, WritePrinterData | EngEnumForms, EngGetForm, EngGetPrinter, EngGetPrinterData, EngSetPrinterData, EngWritePrinter
FindResource | EngFindResource
LoadLibrary, GetProcAddress | EngLoadImage, EngFindImageProcAddress
GetFileTime, GetLocalTime | EngGetFileChangeTime, EngQueryLocalTime
QueryPerformanceCounter, QueryPerformanceFrequency | EngQueryPerformanceCounter, EngQueryPerformanceFrequency
MultiByteToWideChar, WideCharToMultiByte | EngMultiByteToWideChar, EngWideCharToMultiByte
MulDiv | EngMulDiv

Version Marking

To ensure that the driver’s correct version is run on the corresponding version of Windows NT, each print driver must be marked with a version number in the resource file; for example:
#include <ntverp.h>
#define VER_FILEVERSION 0,2,0,0

Version 2 drivers runs in kernel mode. The spooler ensures that the driver is kept separate from the older, non-kernel-mode drivers.

UNICODE Conversion

Limited ANSI-to UNICODE and UNICODE-to ANSI translation is available through GDI. See the EngMultiByteToUnicodeN and EngUnicodeToMultiByteN reference pages for more details.

Spooler Functions

The set of spooler functions is limited to UNICODE and those from a driver that make sense.

Impersonation

Impersonate client and RevertToSelf are handled automatically by spooler functions.

Registry

There are no plans to support registry functions passing data to kernel-mode drivers. Settings from the driver should be retrieved using EngGetPrinterData.

File Operations

GDI now supplies file mapping support. See the reference pages for EngLoadModule, EngLoadModuleForWrite, EngMapFontFile, EngMapModule, and EngUnmapFontFile for more information.

There are no plans to support file read/write/create, file path, or file find functions.

Resource Functions

See the reference page for EngFindResource for more details.


New GDI Service Routines

The following table lists all engine functions that are new in the Windows NT 4.0 release, and provides a summary description for each function. The functions are ordered alphabetically. See the reference pages for detailed information.
New Engine Function | Summary Description
EngAcquireSemaphore | Acquires the resource associated with a semaphore for exclusive access by the calling thread.
EngAllocMem | Allocates a block of memory from the system’s paged pool.
EngAllocUserMem | Allocates a block of memory from the address space of the current process.
EngComputeGlyphSet | Computes the glyph set of a code page supported on a device.
EngCreateSemaphore | Creates a semaphore object.
EngDebugBreak | Causes a breakpoint in the current process to occur.
EngDebugPrint | Prints a debug message to the kernel debugger.
EngDeleteSemaphore | Deletes a semaphore object from the system’s resource list.
EngDeviceIoControl | Sends a control code to the video miniport driver, causing the device to perform the requested operation.
EngEnumForms | Enumerates the forms supported by a printer.
EngFindImageProcAddress | Returns the address of a function found within an executable module.
EngFindResource | Determines the location of a resource in a module.
EngFreeMem | Deallocates a block of system memory.
EngFreeModule | Unmaps a file from system memory.
EngFreeUserMem | Deallocates a block of user memory.
EngGetCurrentCodePage | Returns the system’s default OEM and ANSI code pages.
EngGetDriverName | Returns the name of the driver’s DLL.
EngGetFileChangeTime | Returns the specified file’s last write time.
EngGetForm | Gets FORM_INFO_1 details for a form.
EngGetLastError | Gets the last error code logged by GDI for the calling thread.
EngGetPrinter | Retrieves information about a printer.
EngGetPrinterData | Retrieves configuration data for a printer.
EngGetPrinterDataFileName | Retrieves the string name of a printer’s data file.
EngGetProcessHandle | Retrieves a handle to the current client process.
EngGetType1FontList | Retrieves a list of PostScript Type1 font
EngLineTo | Draws a single solid integer-only cosmetic line.
EngLoadImage | Loads an executable image into system memory.
EngLoadModule | Loads an executable module into system memory as read-only.
EngLoadModuleForWrite | Loads an executable module into system memory for writing.
EngMapFontFile | Returns a pointer to the base location of the font data in a font file.
EngMapModule | Returns the address and size of an executable file that was memory-mapped by EngLoadModule.
EngMarkBandingSurface | Marks a surface as a banding surface.
EngMulDiv | Performs a precision-preserved 32-bit multiplication and division.
EngMultiByteToUnicodeN | Converts an ANSI string to a Unicode string.
EngProbeForRead | Probes a structure for read accessibility.
EngProbeForReadAndWrite | Probes a structure for read and write accessibility.
EngQueryLocalTime | Queries the local time.
EngQueryPerformanceCounter | Queries the performance counter.
EngQueryPerformanceFrequency | Queries the frequency of the performance counter.
EngReleaseSemaphore | Releases a semaphore.
EngSecureMem | Secures an address range in memory.
EngSetPrinterData | Sets the configuration data for a printer.
EngSort | Performs a quick sort on a list.
EngUnicodeToMultiByteN | Converts a Unicode string to an ANSI string.
EngUnloadImage | Unloads an image loaded by EngLoadModule.
EngUnmapFontFile | Unmaps a font file from system memory.
EngUnsecureMem | Unlocks an address range that is locked down in memory.
EngWideCharToMultiByte | Converts a wide character string into an ANSI source string using the specified code page.
EngWritePrinter | Provides the spooler with data that needs to be written to a printer.
FLOATOBJ_Xxx, XFORMOBJ_iGetFloatObjXform | Allow graphics drivers to emulate floating point arithmetic. These services must be used on Windows NT systems that do not support kernel-mode floating point operations.
STROBJ_dwGetCodePage | Returns the code page associated with the specified STROBJ.


Obsolete GDI/DDI Routines

The following is a list of GDI and DDI functions that are obsolete in Windows NT 4.0:
DrvGetResourceId | EngCreateSurface
DrvPlgBlt | EngPlayJournal
DrvQueryObjectData | EngPlgBlt
DrvRestartPDEV | EngStartBandPage
EngCreateJournal |

You need to remove calls to the obsolete Eng Xxx functions from your driver, or your driver will not compile and/or link.

The function indexes for the obsolete Drv Xxx functions have been removed from GDI, and GDI no longer makes calls to these functions. Leaving these functions in your driver does no harm, but they won’t be used by GDI.


Glossary

animation

The display of a series of graphic images, simulating motion. Animation can be frame-based or cast-based.

applet

A small (usually single-purpose) application that is launched by some other application. For example, the different options of the Program Manager Control Panel are referred to as applets.

bitmap

See “standard format bitmap.”

brush origin

The coordinate of a pixel on a device’s surface aligned with the upper left pixel of the brush’s pattern.

color index

An index into an array of RGBs.

CPSUI (Common Property Sheet User Interface)

The CPSUI is a system-supplied module that manages all aspects of a property sheet for an application, including the addition of driver-defined pages. Specifically, the CPSUI is responsible for creating and destroying an application’s property sheet pages plus any device-specific pages that an application might request. The CPSUI is also responsible for the display and update of all dialog boxes that define a page.

CTT (character translation table)

Used in Windows NT miniprint drivers and translated in Windows NT to RLE files.

DDB (device dependent bitmap)

A data structure describing a bit image, specifying width and height of a rectangular region in pixels, the width of the array that maps entries from the device palette to pixels, and the device’s color format in terms of color planes and bits per pixel.

DDI (device driver interface)

A set of functions implemented by graphics drivers and used by GDI to communicate with the drivers.

despooling

The conversion of spooled device-independent records into device-specific records.

DIB (device independent bitmap)

Information on the bitmap is stored in a data structure containing information such as color format, resolution and color palette for the device on which the image appears, an array of bits that maps RGB triplets to pixels, and a data-compression identifier which indicates which if any data compression scheme was used to reduce the size of the array of bits. A GDI bitmap that can be rendered accurately on different devices because it contains a color table that can be read by the rendering device driver. These are standard format bitmaps created and supported by GDI through EngCreateBitmap.

dithering

A technique used to create the illusion of varying shades of gray on a monochrome display or printer, or additional colors on a color display or printer. Dithering relies on treating areas of an image as groups of dots that are colored in different patterns. Similar to halftones in photography.

EMF (enhanced metafiles)

EMFs are one type of spool file used the Windows NT print spooler. These spool files reduce the time spent between when the application makes a print request and when control is returned back to it. This is possible because the GDI functions calls that produce the application’s graphics object are stored directly in the EMF. The time-consuming execution of these stored GDI functions is carried out later, in the background, when the spool file is played back.

EMF spool files also promote printer device independence. For example, a picture measuring 2 inches by 4 inches on a VGA display and stored in an EMF maintains these original dimensions regardless of the DPI of the printer on which it is printed.

GCAPS (graphics capabilities bits)

A driver specifies its graphics capabilities by setting GCAPS flags in the flGraphicsCaps member of the DEVINFO structure. GDI obtains the GCAPS when it calls the driver’s DrvEnablePDEV routine.

GDI (graphics device interface)

GDI is split into two pieces. Win32 GDI is a user mode API used by Win32 applications that require graphics support. Kernel-mode GDI interfaces directly with kernel-mode graphics drivers, as well as the window manager.Kernel-mode GDI exports several services and functions that can be called by device drivers to perform a host of drawing and graphics-related operations. This eliminates the need for graphics drivers to implement much of the required graphics functionality.

The Win32 GDI library that is directly assessable to Win32 applications can be found in GDI32.DLL. It fields calls to the functions listed in wingdi.h and passes the application-supplied information to kernel-mode GDI by way of NT’s executive system services. The kernel-mode GDI library is found in WIN32K.SYS. Kernel-mode GDI communicates with a graphics driver by calling the driver’s implementations of the DDI functions listed in winddi.h.

Glyph

The actual shape (bit pattern, outline, etc.) of a character image. For example, an italic ‘a’ and a roman ‘a’ are two different glyphs representing the same underlying character. In this usage, “glyph” is a synonym for “character image” or simply “image.”

GPC (generic printer characterization)

Data files for the RasDD printer driver.

Graphics Engine

See the kernel-mode GDI definition under “GDI.”

HAL (hardware abstraction layer)

A Windows NT executive component providing platform-specific support for the Windows NT Kernel, I/O Manager, kernel-mode debuggers, and lowest-level device drivers. The HAL exports routines that abstract platform-specific hardware details about caches, I/O buses, interrupts, and so forth, and provides an interface between the platform’s hardware and the system software. For example, the HAL implements a routine to map each device driver’s bus-relative device interrupt vector to a system-assigned vector with a corresponding platform-specific hardware priority (DIRQL), as well as several routines that provide DMA-transfer support.

The DirectDraw HAL is the hardware-specific code of a display driver that provides DirectDraw support.

hook

A callback function that handles I/O port and board memory accesses. Synonym for handler.

HS (horizontal minimum feature size)

IEEE 1284 Bi-Directional Parallel Peripheral Interface

Interface standard that adds bidirectional communication capabilities to peripheral devices that are connected to a computer’s parallel port.

IOCTL (input/output control)

Functions that handle control of I/O within a device driver module.

IRP (I/O request packet)

An IRP is the basic I/O Manager structure used to communicate with drivers and to allow drivers to communicate with each other. A packet consists of two different parts:

1. Header, or fixed part of the packet ¾ Used by the I/O Manager to store information about the original request, such as the caller’s parameters and the address of the device object upon which the file is open. It is also used by drivers for storing information such as the final status of the request. See also I/O status block and device object.

2. I/O stack locations ¾ Following the header is a set of I/O stack locations, one per driver in the chain of layered drivers for which the request is bound. Each stack location contains the parameters, function codes, and context used by the corresponding driver to determine what it is supposed to be doing.

kernel mode

The privileged processor mode in which Windows NT executive code runs. A driver or thread running in kernel mode has access to system memory and hardware. See also user mode.

language monitor

Spooler component that allows the spooler to configure and monitor the status of a bidirectional printer. Printer vendors define the language used by a language monitor.

NTVDM or VDM

Windows NT virtual DOS machine.

palette

A color lookup table.

PCD (Plotter Characterization Data)

A DLL that provides device-specific information to a graphics plotter driver.

PDEV

A logical representation of a phyiscal device. A PDEV is private to a driver and contains all the information and data that represents the associated physical device. As part of enabling a PDEV, a driver provides information to GDI that describes the requested device and its capabilities. Important information that the driver gives GDI is a set of flags that allow GDI to determine what kinds of operations the PDEV (and the requested device) supports. For example, GDI can test the flags returned as part of the PDEV to determine whether or not the requested device can handle Bezier curves and geometric wide lines.

PFM (printer font metrics)

Files that describe fonts for a printer.

PJL (printer job language)

Language that implements communication between a bidirectional printer device and a computer. PJL was defined and implemented by Hewlett-Packard. For a full definition of the PJL language, contact H-P.

port monitor

Spooler component that controls the I/O port to which the printer is connected.

PPD (PostScript printer description)

A file read by the generic PostScript driver as part of device initialization. PPD files conform to specifications published by Adobe Corporation and are supplied by PostScript printer manufacturers.

preimaging

The process of building a movie frame in a memory buffer before it is displayed.

RasDD

Raster printer device driver. This driver supports raster (bitmap) graphics printing.

realized brush

A data structure containing information and accelerators that the display driver needs in order to fill an area with a pattern.

resolution

For joysticks, resolution refers to the minimum and maximum intervals between joystick messages sent for a captured joystick.

For timers, resolution refers to the accuracy of the timer event. A resolution value of zero means the event must occur at the exact time requested, while a resolution value of ten means the event must occur within ten milliseconds of the requested time.

RIFF chunk

A chunk with chunk ID RIFF that includes an identifying code and zero or more sub-chunks, the contents of which depend on the form type.

RIFF file

A file whose format complies with one of the published RIFF forms. Examples of RIFF files include WAVE files for waveform audio data, RMID files for MIDI sequences, and RDIB files for bitmaps.

RIFF form

A file-format specification based on the RIFF standard.

RIFF (resource interchange file format)

A tagged-file specification used to define standard formats for multimedia files. Tagged-file structure helps prevent compatibility problems that often occur when file-format definitions change over time. Because each piece of data in the file is identified by a standard header, an application that does not recognize a given data element can skip over the unknown information.

RLE bitmaps (run-length encoded bitmaps)

Bitmaps are compressed to reduce disk and memory storage required for the bitmap. Encoding format is specified in the biCompression member of the BITMAPINFO structure.

ROP (raster operation)

A bitwise operation applied to the bits of color data for the replicated brush and the bits of color data for the target rectangle on the display. There are 256 ROPs in Windows.

standard format bitmap

Single plane, packed-pixel (where the data for each pixel is stored in a contiguous manner) format bitmaps created and supported by GDI through EngCreateBitmap. Each scan line of the bitmap is aligned on a four-byte boundary. Standard format bitmaps code information at a rate of 1, 4, 8, 16, or 32 bits per pixel.

streaming

The process of transferring information from a storage device, such as a hard disk or CD-ROM, to a device driver. Rather than transferring all the information in a single data copy, the information is transferred in smaller parts over a period of time, typically while the application is performing other tasks.

surface

Drawing and text output require a surface, associated with the requested device, on which to draw. Such a device surface is a subset of an array of 2 (28) by 2 (28) pixels. These pixels are addressed by pairs of 28-bit signed numbers; the upper leftmost pixel of the device surface is given the coordinates (0,0). The device surface lies in the lower right quadrant of this coordinate space, where both coordinates are nonnegative. A surface is associated with a particular PDEV.

tagged file format

A file format in which data is tagged by using standard headers that identify information type and length.

threshold

For the joystick interface, the movement threshold is the distance in device units the coordinates must change before the application is notified of the movement. Setting a high threshold reduces the number of joystick messages sent to your application, however, it also reduces the sensitivity of the joystick.

Tile

Repetition of a brush pattern, to cover an entire device surface.

UMB (upper memory block)

Unitool

Development tool that allows raster printer driver writers to easily develop a minidriver. Unitool is shipped with the Windows NT DDK.

user mode

The nonprivileged processor mode in which application code, including protected subsystem code, runs. User-mode applications cannot gain access to system data except by calling subsystem-supplied functions, which, in turn, call system resources. See also kernel mode.

VDD (virtual device driver)

A device driver that operates in user mode and communicates with a corresponding device driver in kernel mode. A VDD supports only special-purpose hardware devices from an MS-DOS application. An instruction emulator that converts x86 instructions to MIPS instructions. The virtual device driver acts as a layer between MS-DOS applications and the hardware attached to the Windows NT machine.

VDM (virtual DOS machine)

VS (vertical minimum feature size)

Windows NT virtual DOS machine

A protected environment subsystem that emulates MS-DOS and 16-bit Windows within Windows NT. A VDM is created whenever a user starts an MS-DOS application on Windows NT.

WOW (Windows on Win32)

A protected subsystem that runs within a VDM process. It provides a 16-bit Windows environment capable of running any number of 16-bit Windows applications on Windows NT.

xlate object (translate object)

Tables that assist in palette management, to translate an application’s color index into a graphics index.


Chapter 1 Graphics Driver Functions

This chapter provides information on the following function groups:

  • Drv Xxx functions. These functions are implemented by graphics drivers, and are called by GDI.

  • Eng Xxx functions. These functions are system-supplied GDI services that can be called by graphics drivers.

  • XXXOBJ_Xxx functions. These specialized service routines are implemented within GDI. They are available to be called by graphics drivers.

The Graphics Driver Design Guide describes which Drv Xxx functions are required, conditionally required, or optional in a graphics driver’s implementation.


BRUSHOBJ_pvAllocRbrush

PVOID BRUSHOBJ_pvAllocRbrush(

IN BRUSHOBJ *pbo,

IN ULONG cj

);

BRUSHOBJ_pvAllocRbrush allocates memory for the driver’s realization of a specified brush.

Parameters

pbo
Points to the BRUSHOBJ structure for which the realization is to be allocated.
cj
Specifies the size, in bytes, required for the realization.

Return Value

The return value is a pointer to the allocated memory if the function is successful. Otherwise, it is null, and an error code is logged.

Comments

BRUSHOBJ_pvAllocRbrush allocates memory for the brush realization. GDI manages the memory and discards it when the brush is no longer needed.

This function should be called only by an implementation of a brush realization following a call to DrvRealizeBrush.

See Also

BRUSHOBJ, DrvRealizeBrush


BRUSHOBJ_pvGetRbrush

PVOID BRUSHOBJ_pvGetRbrush(

IN BRUSHOBJ *pbo

);

BRUSHOBJ_pvGetRbrush retrieves a pointer to the driver’s realization of a specified brush.

Parameters

pbo
Points to the BRUSHOBJ structure whose realization is requested.

Return Value

The return value is a pointer to the realized brush if the function is successful. If the brush cannot be realized, the return value is null and an error code is logged.

Comments

BRUSHOBJ_pvGetRbrush is called when the brush is a pattern brush that has not yet been realized; that is, it is called when the iSolidColor member of the BRUSHOBJ structure is 0xFFFFFFFF and the pvRbrush member is null.

If the brush has not been realized when BRUSHOBJ_pvGetRbrush is called, GDI calls the driver-supplied DrvRealizeBrush function to obtain the driver’s realization of the brush. As an acceleration, GDI caches this realization in pvRbrush. Then, when an application reuses this brush for another drawing operation, the driver doesn’t have to call BRUSHOBJ_pvGetRbrush again.

See Also

BRUSHOBJ, BRUSHOBJ_pvAllocRbrush, DrvRealizeBrush


CLIPOBJ_bEnum

BOOL CLIPOBJ_bEnum(

IN CLIPOBJ *pco,

IN ULONG cj,

OUT ULONG *pv

);

CLIPOBJ_bEnum enumerates a batch of rectangles from a specified clip region. A prior call to CLIPOBJ_cEnumStart determines the order of enumeration.

Parameters

pco
Points to a CLIPOBJ structure describing the clipping region that is to be enumerated.
cj
Specifies the size, in bytes, of the buffer pointed to by pv.
pv
Points to the buffer that will receive data about the clip region in an ENUMRECTS structure.

Return Value

The return value is TRUE if the driver must call this function again for more enumeration data, or FALSE if the enumeration is complete. It is possible for CLIPOBJ_bEnum to return TRUE with the number of clipping rectangles equal to zero. In such cases, the driver should call CLIPOBJ_bEnum again without taking any action.

Comments

A possible loop structure for calling this function follows:

do {
    bMore = CLIPOBJ_bEnum(pco, sizeof(buffer), &buffer.c);
    for (i = 0; i < buffer.c; i++) {
        .
        .
        .
    }
} while (bMore);

The count of objects written to the buffer is written to the buffer itself.

See Also

CLIPOBJ, CLIPOBJ_cEnumStart, ENUMRECTS


CLIPOBJ_cEnumStart

ULONG CLIPOBJ_cEnumStart(

IN CLIPOBJ *pco,

IN BOOL bAll,

IN ULONG iType,

IN ULONG iDir,

IN ULONG cLimit

);

CLIPOBJ_cEnumStart sets parameters for enumerating rectangles in a specified clip region.

Parameters

pco
Points to the CLIPOBJ structure that defines the clip region to be enumerated.
bAll
Specifies whether the entire region should be enumerated. This parameter is TRUE if the whole region should be enumerated. It is FALSE if only the parts relevant to the present drawing operation should be enumerated.

A driver that caches clip regions must enumerate the entire region.

iType
Specifies the data structures that are to be written by CLIPOBJ_bEnum. This parameter currently must be CT_RECTANGLES, indicating that the region is to be enumerated as a list of rectangles.
iDir
Determines the order in which the rectangles are to be enumerated. This order can be essential if a DrvBitBlt operation is executing concurrently on the same surface. If the order is not relevant to the device driver, CD_ANY should be specified for complex regions, allowing GDI to optimize the enumeration. This value can be one of the following:

Value

Meaning

CD_RIGHTDOWN

Left to right, top to bottom.

CD_LEFTDOWN

Right to left, top to bottom.

CD_RIGHTUP

Left to right, bottom to top.

CD_LEFTUP

Right to left, bottom to top.

CD_ANY

Any order convenient for GDI.

cLimit
Specifies the maximum number of rectangles to be enumerated. If this parameter is zero, counting is omitted.

Return Value

The return value is the count of enumerated rectangles. If the count exceeds cLimit, the return value is 0xFFFFFFFF.

Comments

A region can be enumerated whether or not this function is called. By default, the driver only enumerates relevant rectangles, starting at the upper left.

The driver can restart enumeration by calling this function again.

See Also

CLIPOBJ, CLIPOBJ_bEnum, DrvBitBlt


CLIPOBJ_ppoGetPath

PATHOBJ *CLIPOBJ_ppoGetPath(

CLIPOBJ *pco

);

CLIPOBJ_ppoGetPath creates a PATHOBJ structure that contains the outline of the specified clip region.

Parameters

pco
Pointer to a CLIPOBJ structure that defines the specified clip region.

Return Value

The return value is a pointer to a PATHOBJ structure if the function is successful. Otherwise, it is null, and an error code is logged.

Comments

The returned PATHOBJ should be deleted using EngDeletePath when the driver no longer needs it.

A driver for a device that can download a clipping path might prefer this function for defining complex regions.

See Also

CLIPOBJ, EngDeletePath, PATHOBJ


DrvAssertMode

VOID DrvAssertMode(

IN DHPDEV dhpdev,

IN BOOL bEnable

);

DrvAssertMode sets the mode of the specified physical device to either the mode specified when the PDEV was initialized or to the default mode of the hardware.

Parameters

dhpdev
Identifies the PDEV describing the hardware mode that should be set.
bEnable
Specifies the mode to which the hardware is to be set. If this parameter is TRUE, then the hardware is set to the original mode specified by the initialized PDEV. Otherwise, the hardware is set to its default mode so the video miniport driver can assume control.

Comments

GDI calls DrvAssertMode when it is required to switch among multiple desktops on a single display surface. To switch from one PDEV to another, GDI calls DrvAssertMode with the bEnable parameter set to FALSE for one PDEV, and TRUE for the other. To revert to the original PDEV, DrvAssertMode is called with bEnable set to FALSE, followed by another call to DrvAssertMode, with bEnable set to TRUE and dhpdev set to the original PDEV.

If the physical device is palette-managed, GDI should call DrvSetPalette to reset the device’s palette. The driver does not then need to keep track of the current pointer state because the Window Manager selects the correct pointer shape and moves it to the current position. The Console Manager ensures that desktops are properly redrawn.

DrvAssertMode is required for display drivers.

See Also

DrvGetModes


DrvBitBlt

BOOL DrvBitBlt(

IN SURFOBJ *psoDst,

IN SURFOBJ *psoSrc,

IN SURFOBJ *psoMask,

IN CLIPOBJ *pco,

IN XLATEOBJ *pxlo,

IN RECTL *prclDst,

IN POINTL *pptlSrc,

IN POINTL *pptlMask,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrush,

IN ROP4 rop4

);

DrvBitBlt provides general bit-block transfer capabilities between device-managed surfaces, between GDI-managed standard-format bitmaps, or between a device-managed surface and a GDI-managed standard-format bitmap.

Parameters

psoDst
Points to the SURFOBJ structure that describes the surface on which to draw.
psoSrc
Points to a SURFOBJ structure that describes the source for the bit-block transfer operation, if required by the rop4 parameter.
psoMask
Points to a SURFOBJ structure that describes a surface to be used as a mask for the rop4 parameter. The mask is a bitmap with 1 bit per pixel. Typically, a mask is used to limit the area to be modified in the destination surface. Masking is selected by setting the rop4 parameter to the value 0xAACC. The destination surface is unaffected if the mask is 0x0000.

The mask will be large enough to cover the destination rectangle.

If this parameter is null and a mask is required by the rop4 parameter, the implicit mask in the brush is used.

pco
Points to a CLIPOBJ structure that limits the area to be modified. GDI services (CLIPOBJ_Xxx) that enumerate the clip region as a set of rectangles are provided. Whenever possible, GDI simplifies the clipping involved; for example, this function is never called with a single clipping rectangle. GDI clips the destination rectangle before calling this function, making additional clipping unnecessary.
pxlo
Points to a XLATEOBJ structure that specifies how color indices should be translated between the source and destination surfaces.

If the source surface is palette-managed, its colors are represented by indices into a lookup table of RGB values. The XLATEOBJ structure can be queried for a translate vector that will allow the device driver to translate any source index into a color index for the destination.

The situation is more complicated when, for example, the source is RGB, but the destination is palette-managed. In this case, the closest match to each source RGB value must be found in the destination palette. The driver can call the XLATEOBJ_iXlate service to perform this operation.

Optionally, the device driver can match colors when the target palette is the default device palette.

prclDst
Points to a RECTL structure that defines the area to be modified. This structure uses the coordinate system of the destination surface. The lower and right edges of this rectangle are not part of the bit-block transfer, meaning the rectangle is lower right exclusive.

DrvBitBlt is never called with an empty destination rectangle. The two points that define the rectangle are always well-ordered.

pptlSrc
Points to a POINTL structure that defines the upper left corner of the source rectangle, if a source exists. This parameter is ignored if there is no source.
pptlMask
Points to a POINTL structure that defines which pixel in the mask corresponds to the upper left corner of the source rectangle, if a source exists. This parameter is ignored if the psoMask parameter is null.
pbo
Points to the brush object that defines the pattern for the bit-block transfer. GDI’s BRUSHOBJ_pvGetRbrush service can be used to retrieve the device’s realization of the brush. This parameter is ignored if the rop4 parameter does not require a pattern.
pptlBrush
Points to a POINTL structure that defines the origin of the brush in the destination surface. The upper left pixel of the brush is aligned at this point, and the brush repeats according to its dimensions. This parameter is ignored if the rop4 parameter does not require a pattern.
rop4
Specifies a raster operation that defines how the mask, pattern, source, and destination pixels are combined to write to the destination surface.

This is a quaternary raster operation, which is an extension of the ternary Rop3 operation. A Rop4 has 16 relevant bits, which are similar to the 8 defining bits of a Rop3. The simplest way to implement a Rop4 is to consider its 2 bytes separately: The low byte specifies a Rop3 that should be calculated if the mask is one; the high byte specifies a Rop3 that can be calculated and applied if the mask is 0.

Return Value

The return value is TRUE if the bit-block transfer operation is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

DrvBitBlt is optional if the device surface is a standard-format bitmap created using EngCreateBitmap. It is required if the device’s surface was created using EngCreateDeviceSurface. If a device’s surface is a standard-format bitmap, DrvBitBlt will not be called unless the driver hooks the call with DrvSynchronize. The driver then has the option to pass complicated transfers back to GDI by calling EngBitBlt. The EngBitBlt function divides the transfer into simpler calls for the driver.

A driver must implement DrvBitBlt if drawing is done on device-managed surfaces. At a minimum, DrvBitBlt must support the following:

  • Bit-bit-block transfer between a standard format in the device’s preferred format and the device surface.

  • The SRCCOPY (0xCCCC) ROP.

  • Arbitrary clipping.

GDI’s CLIPOBJ_Xxx services allow the clipping to be reduced to a series of clipping rectangles. A translation vector assists in color index translation for palettes.

Note Do not dereference parameter pointers unless the ROP indicates they are needed. For example, never unnecessarily dereference pbo->iSolidColor because doing so for a ROP such as BLACKNESS can cause an access violation. (This rule also applies to any function that includes a MIX parameter.)

DrvBitBlt is a large and complex function that performs most of the work required to write a driver for a raster display device that does not have a standard format frame buffer. Sample code is provided for the Microsoft VGA driver that illustrates the basic functionality of a planar device.

For more information on raster operations, see the Win32 SDK.

See Also

BRUSHOBJ, BRUSHOBJ_pvGetRbrush, CLIPOBJ, DrvSynchronize, EngAssociateSurface, EngBitBlt, EngCreateBitmap, EngCreateDeviceSurface, SURFOBJ, XLATEOBJ, XLATEOBJ_iXlate


DrvCompletePDEV

VOID DrvCompletePDEV(

IN DHPDEV dhpdev,

IN HDEV hdev

);

DrvCompletePDEV stores the GDI handle (hdev) of the physical device in dhpdev. The driver should retain this handle for use when calling GDI services.

Parameters

dhpdev
Identifies the physical device by its handle, which was returned to GDI when it called DrvEnablePDEV.
hdev
Identifies the physical device that has been installed. This is the GDI handle for the physical device being created. The driver should use this handle when calling GDI functions.

Comments

DrvCompletePDEV is called by GDI when its installation of the physical device is complete. It also provides the driver with a handle to the PDEV to be used when requesting GDI services for the device. This function is required for graphics drivers; when GDI calls DrvCompletePDEV, it cannot fail.


DrvCopyBits

BOOL DrvCopyBits(

OUT SURFOBJ *psoDst,

IN SURFOBJ *psoSrc,

IN CLIPOBJ *pco,

IN XLATEOBJ *pxlo,

IN RECTL *prclDst,

IN POINTL *pptlSrc

);

DrvCopyBits translates between device-managed raster surfaces and GDI standard-format bitmaps.

Parameters

psoDst
Points to the destination surface for the copy operation.
psoSrc
Points to the source surface for the copy operation.
pco
Points to a CLIPOBJ structure that defines a clipping region on the destination surface.
pxlo
Points to a XLATEOBJ structure that defines the translation of color indices between the source and target surfaces.
prclDst
Points to a RECTL structure that defines the area to be modified. This structure uses the coordinate system of the destination surface. The lower and right edges of this rectangle are not part of the bit-block transfer, meaning the rectangle is lower right exclusive.

DrvCopyBits is never called with an empty destination rectangle. The two points that define the rectangle are always well-ordered.

pptlSrc
Points to a POINTL structure that defines the upper-left corner of the source rectangle.

Return Value

The return value is TRUE if the source surface is successfully copied to the destination surface.

Comments

This function is required for a device driver that has device-managed bitmaps or raster surfaces. The implementation in the driver must translate driver surfaces to and from any standard-format bitmap.

Standard-format bitmaps are single-plane, packed-pixel format. Each scan line is aligned on a 4-byte boundary. These bitmaps have 1, 4, 8, 16, 24, 32, or 64 bits per pixel.

This function should ideally be able to deal with RLE and device-dependent bitmaps (see the Win32 SDK). The device-dependent format is optional; only a few specialized drivers need to support it. These bitmaps can be sent to this function as a result of the following Win32 GDI functions: SetDIBits, SetDIBitsToDevice, GetDIBits, SetBitmapBits, and GetBitmapBits.

Kernel-mode GDI calls this function from its simulations.

See Also

CLIPOBJ, XLATEOBJ


DrvCreateDeviceBitmap

HBITMAP DrvCreateDeviceBitmap(

IN DHPDEV dhpdev,

IN SIZEL sizl,

IN ULONG iFormat

);

DrvCreateDeviceBitmap creates and manages bitmaps.

Parameters

dhpdev
Identifies the PDEV that describes the physical device that an application has designated as the primary target for a bitmap. The format of the bitmap must be compatible with this physical device.
sizl
Specifies the height and width of the desired bitmap, in pixels.
iFormat
Specifies the bitmap format, which indicates the required number of bits of color information per pixel. This value can be one of the following:

Value

Meaning

BMF_1BPP

Monochrome.

BMF_4BPP

4 bits per pixel.

BMF_8BPP

8 bits per pixel.

BMF_16BPP

16 bits per pixel.

BMF_24BPP

24 bits per pixel.

BMF_32BPP

32 bits per pixel.

BMF_4RLE

4 bits per pixel; run length encoded.

BMF_8RLE

8 bits per pixel; run length encoded.

Return Value

The return value is a handle that identifies the created bitmap if the function is successful. If the driver chooses to let GDI create and manage the bitmap, the return value is zero. If an error occurs, the return value is 0xFFFFFFFF, and GDI logs an error code.

Comments

If the driver creates the bitmap, it can store it anywhere and in any format. It is assumed that the driver will take into account the specifications of the parameters and provide a bitmap with at least as many bits per pixel as requested.

It is neither required nor recommended that drivers manage bitmaps. This capability is provided for devices such as the VGA adapter card that are able to perform bit-block transfers (DrvBitBlt) faster using nonstandard-format bitmaps.

The contents of the created bitmap are undefined. If an application requests initialization of the bitmap, GDI calls DrvBitBlt. If the display driver returns zero, GDI creates and manages the bitmap.

This function is optional.

See Also

DrvDeleteDeviceBitmap


DrvDeleteDeviceBitmap

VOID DrvDeleteDeviceBitmap(

IN DHSURF dhsurf

);

DrvDeleteDeviceBitmap deletes a device bitmap created by DrvCreateDeviceBitmap.

Parameters

dhsurf
Identifies the bitmap to be deleted. This handle identifies the bitmap created by DrvCreateDeviceBitmap.

Comments

A display driver must supply DrvDeleteDeviceBitmap if it supplies DrvCreateDeviceBitmap.


DrvDescribePixelFormat

LONG DrvDescribePixelFormat(

IN DHPDEV dhpdev,

IN LONG iPixelFormat,

IN ULONG cjpfd,

OUT PIXELFORMATDESCRIPTOR *ppfd

);

DrvDescribePixelFormat describes the pixel format for a device-specified PDEV by writing a pixel format description to a PIXELFORMATDESCRIPTOR structure.

Parameters

dhpdev
Identifies the device for which pixel format information is requested.
iPixelFormat
Specifies the index number of the requested pixel format.
cjpfd
Specifies the maximum number of bytes that can be written to the structure pointed to by ppfd.
ppfd
Points to a PIXELFORMATDESCRIPTOR structure that is to receive information about the pixel format. This parameter can be null.

Return Value

The return value is the maximum pixel format index if the function is successful. Otherwise, it is zero, and an error code is logged.

Comments

A display driver that supports 3D graphics hardware can support windows with different pixel formats on a single display surface. The pixel format must correspond to a configuration supported by the graphics hardware.

DrvDescribePixelFormat fills in the structure pointed to by ppfd if this parameter is not null.

The returned maximum pixel format index can be used by applications that need to obtain a device context’s maximum pixel format index. The pixel formats that a device supports are identified by positive one-based integer indices.

The pixel format functions are used in conjunction with the window object services functions to track and update pixel formats of windows on a display surface.


DrvDestroyFont

VOID DrvDestroyFont(

IN FONTOBJ *pfo

);

DrvDestroyFont notifies the driver that a font realization is no longer needed and that the driver can now free any associated data structures it has allocated.

Parameters

pfo
Points to the FONTOBJ structure that identifies the font instance.

Comments

If the DEVICE_FONTTYPE flag is set in the flFontType member of the FONTOBJ structure, the driver should release any resources or memory identified with both the pvConsumer and pvProducer members of FONTOBJ. Otherwise, it should release only memory and resources identified with pvConsumer.

The driver must reset the pvConsumer and pvProducer members to null if it uses them.

GDI calls DrvDestroyFont once for the font producer and once again for the font consumer.

GDI guarantees that DrvDestroyFont and DrvTextOut never overlap; consequently, the driver can rely on cached information while processing a DrvTextOut call.

This function must be implemented if the driver allocates resources that must be released when the font instance is destroyed.

See Also

DrvTextOut, FONTOBJ


DrvDisableDriver

VOID DrvDisableDriver(VOID)

DrvDisableDriver is used by GDI to notify a driver that it no longer requires the driver and is ready to unload it.

Comments

The driver should free all allocated resources and return the device to the state it was in before the driver loaded.

DrvDisableDriver is required for graphics drivers.

See Also

DrvEnableDriver


DrvDisablePDEV

VOID DrvDisablePDEV(

IN DHPDEV dhpdev

);

DrvDisablePDEV is used by GDI to notify a driver that the specified PDEV is no longer needed.

Parameters

dhpdev
Pointer to the PDEV that describes the physical device to be disabled. This value is the handle returned by DrvEnablePDEV.

Comments

If the driver has been disabled by a call to DrvAssertMode, the driver cannot access the hardware during DrvDisableSurface because another active surface might be in use. Any necessary hardware changes should have been performed during the call to DrvAssertMode. A driver should keep track of whether or not it has been disabled by DrvAssertMode so that it can perform proper cleanup operations in DrvDisableSurface.

If the physical device has an enabled surface, GDI calls DrvDisablePDEV after calling DrvDisableSurface. The driver should free any memory and resources used by the PDEV.

DrvDisablePDEV is required for graphics drivers.

See Also

DrvAssertMode, DrvEnablePDEV


DrvDisableSurface

VOID DrvDisableSurface(

IN DHPDEV dhpdev

);

DrvDisableSurface is used by GDI to notify a driver that the surface created by DrvEnableSurface for the current device is no longer needed.

Parameters

dhpdev
Handle to the PDEV that describes the physical device whose surface is to be released.

Comments

The driver should free any memory and resources used by the surface associated with the PDEV as soon as the physical device is disabled.

If the driver has been disabled by a call to DrvAssertMode, the driver cannot access the hardware during DrvDisablePDEV because another active PDEV might be in use. Any necessary hardware changes should have been performed during the call to DrvAssertMode. A driver should keep track of whether or not it has been disabled by DrvAssertMode so that it can perform proper cleanup operations in DrvDisablePDEV.

If the physical device has an enabled surface, GDI calls DrvDisableSurface before calling DrvDisablePDEV.

DrvDisableSurface is required for graphics drivers.

See Also

DrvAssertMode, DrvDisableDriver, DrvDisablePDEV, DrvEnableSurface


DrvDitherColor

ULONG DrvDitherColor(

IN DHPDEV dhpdev,

IN ULONG iMode,

IN ULONG rgb,

OUT ULONG *pul

);

DrvDitherColor requests the device to create a brush dithered against a device palette.

Parameters

dhpdev
Pointer to the PDEV that describes the physical device against whose palettes the specified color should be dithered.
iMode
Determines the palette to dither against. The currently defined values are DM_DEFAULT and DM_MONOCHROME.
rgb
Specifies the RGB color that is to be dithered.
pul
Points to the memory location in which the dithering information is to be recorded. Memory must have been allocated for a standard-format bitmap with dithered brush dimensions cxDither by cyDither. These dimensions are members of the DEVINFO structure. The bitmap format is determined by the iDitherFormat member of the DEVINFO structure.

Return Value

The return value is DCR_DRIVER if the dither values have been calculated by the driver, DCR_SOLID if the engine should use the best solid color approximation of the color, or DCR_HALFTONE if the engine should create a halftone approximation for the driver.

Comments

The result of the dither is a set of device color indices stored in pul. A brush created using these colors for its pattern should be a good approximation of the given color rgb.

DrvDitherColor is an optional function that is called only if cxDither and cyDither are nonzero. Monochrome device drivers, including most raster printers, should use the iMode parameter to tell GDI how to get good gray-scale patterns.

See Also

DEVINFO, DrvEnablePDEV


DrvDrawEscape

ULONG DrvDrawEscape(

IN SURFOBJ *pso,

IN ULONG iEsc,

IN CLIPOBJ *pco,

IN RECTL *prcl,

IN ULONG cjIn,

IN PVOID *pvIn

);

This entry point serves more than one function call; the particular function depends on the value of the iEsc parameter. The escapes that a device supports are determined by a call to DrvEscape.

Parameters

pso
Identifies the surface to which the call is directed.
iEsc
Specifies the operation to be performed. The meanings of the remaining parameters depend on this parameter. This parameter can be the following value.

Value

Meaning

ESC_PASSTHROUGH

Passes raw device data to the device driver. The number of bytes of raw data is indicated by cjIn. The data is pointed to by pvIn. The return value is the number of bytes written if the function is successful. Otherwise, it is zero, and an error code is logged.

pco
Points to a CLIPOBJ structure that can be queried to find the area on the surface that the caller can overwrite.
prcl
Points to a RECTL structure that defines the window rectangle on the surface. The application does not know the position of the window on the surface. GDI supplies this rectangle and holds a lock that ensures the rectangle is stable for the duration of the call. Coordinates received from the application are relative to the upper left corner of the window rectangle.
cjIn
The size, in bytes, of the buffer pointed to by pvIn.
pvIn
The input data for the call. The format of the input data depends on the function specified by iEsc.

Return Value

The return value depends on the function specified by iEsc. The driver should return 0xFFFFFFFF if an unsupported function is called.

Comments

This entry point differs from DrvEscape in that a CLIPOBJ structure is provided. This allows a driver to implement its own drawing functions in a windowed environment.

DrvDrawEscape is optional for all drivers.

See Also

CLIPOBJ, DrvEnableDriver, DrvEscape


DrvEnableDriver

BOOL DrvEnableDriver(

IN ULONG iEngineVersion,

IN ULONG cj,

OUT DRVENABLEDATA *pded

);

DrvEnableDriver is the initial driver entry point exported by the driver DLL. It fills a DRVENABLEDATA structure with the driver version number and calling addresses of functions supported by the driver.

Parameters

iEngineVersion
Specifies the DDI version number that GDI was written for. This value was 0x00010000 for the first version of Windows NT. DDI_DRIVER_VERSION is always the current version; drivers should use this manifest constant, defined in winddi.h.
cj
Specifies the size, in bytes, in the DRVENABLEDATA structure. If the structure is larger than expected, extra members should be left unmodified.
pded
Points to a DRVENABLEDATA structure. GDI zero-initializes cj bytes before the call. The driver fills in its own data.

Return Value

The return value is TRUE if the specified driver is enabled. Otherwise, it is FALSE, and an error code is logged.

Comments

One-time initializations, such as allocating semaphores, can be performed by this function. The driver should wait until DrvEnablePDEV is called before enabling hardware, such as a display.

DrvEnableDriver is required for graphics drivers.

See Also

DrvDisableDriver, DRVENABLEDATA, DrvEnablePDEV, DRVFN


DrvEnablePDEV

DHPDEV DrvEnablePDEV(

IN DEVMODEW *pdm,

IN LPWSTR pwszLogAddress,

IN ULONG cPat,

OUT HSURF *phsurfPatterns,

IN ULONG cjCaps,

OUT ULONG *pdevcaps,

IN ULONG cjDevInfo,

OUT DEVINFO *pdi,

IN HDEV hdev,

IN LPWSTR pwszDeviceName,

IN HANDLE hDriver

);

DrvEnablePDEV creates a logical representation of a physical device upon which the driver can associate a drawing surface.

Parameters

pdm
Points to a DEVMODEW structure that contains driver data.
pwszLogAddress
Points to the logical address string that is the user’s name for the location to which the driver is writing. Examples include “LPT1” or “My Printer.”
cPat
Specifies the number of standard patterns, equal to the number of HSURF structures in the buffer pointed to by phsurfPatterns. The driver cannot access memory beyond the end of the buffer.
phsurfPatterns
Points to a buffer that the driver will fill with surfaces representing the standard fill patterns. The following patterns must be defined in order:

Pattern

Description

HS_HORIZONTAL

Horizontal hatch.

HS_VERTICAL

Vertical hatch.

HS_FDIAGONAL

45-degree upward hatch (left to right).

HS_BDIAGONAL

45-degree downward hatch (left to right).

HS_CROSS

Horizontal and vertical cross hatch.

HS_DIAGCROSS

45-degree crosshatch.

Note The number of default hatch patterns that require driver support was reduced in a previous version of the DDK. Consequently, HS_DDI_MAX, typically used by drivers to declare the size of the pattern array, was reduced.

GDI calls DrvRealizeBrush with one of these surfaces to realize a brush with a standard pattern.

Each of these surfaces must be a monochrome (1 bit per pixel) GDI bitmap for raster devices. The device driver should choose patterns that will look most like standard patterns when written on the device surface.

GDI is never required to use these brushes in support routines for a vector device. Therefore, surfaces can be device-supported surfaces that DrvRealizeBrush recognizes as standard patterns.

cjCaps
Specifies the size of the buffer pointed to by pdevcaps. The driver must not access memory beyond the end of the buffer.
pdevcaps
Points to a GDIINFO structure that will be used to describe device capabilities. GDI zero-initializes this structure before the application calls DrvEnablePDEV.
cjDevInfo
Specifies the number of bytes in the DEVINFO structure pointed to by pdi. The driver should modify no more than this number of bytes in the DEVINFO.
pdi
Points to the DEVINFO structure, which describes the driver and the physical device. The driver should only alter the members it understands. GDI fills this structure with zeros before a call to DrvEnablePDEV.
hdev
Points to a fully-qualified path name of a data file that the driver examines to determine rendering methods for the device. This parameter is ignored for display drivers.
pwszDeviceName
Points to a zero-terminated string that is the user-readable name of the device.
hDriver
Identifies the kernel-mode driver that supports the device. For a printer driver this parameter should be used as a handle to the printer when calling the spooler.

Return Value

The return value is a handle to the PDEV that identifies the enabled device if the function is successful. Otherwise, it is zero, and an error code is logged.

Comments

The device driver represents a logical device, called a PDEV. The PDEV is managed by GDI. A single logical device can manage several PDEVs that can be differentiated by the following:

  1. Type of hardware. A single device driver might support “LaserWhiz,” “LaserWhiz II,” and “LaserWhiz Super.”

  2. Logical address. A single device driver can support printers attached to “LPT1,” “COM2,” “\SERVER1\PSLAZER,” and so forth. A display driver that can support more than one VGA display simultaneously might differentiate them according to port numbers; for example, 0x3CE or 0x2CE.

  3. Surfaces. A printer driver can process two print jobs simultaneously. The two surfaces represent two pages that will be printed. Similarly, a display device driver might support two desktops on the same device.

Each call to DrvEnablePDEV creates another device that is used with a distinct surface. The DEVMODEW structure indicates the type of hardware requested. GDI manages the logical address for printers and other hard copy output devices. The spooler service WritePrinter (a Win32 function) directs output to the proper destination. Displays and input devices either know their logical address implicitly or get it from the private section of the DEVMODEW structure. For more information, see the Win32 SDK.

When receiving a call to this function, the driver must allocate the memory to support the PDEV. However, the actual surface need not be supported until GDI calls DrvEnableSurface.

If a device surface requires a bitmap to be allocated, these allocations need not be made until needed. Although applications often request device information long before actually writing to the device, waiting to allocate resources, such as large bitmaps, can conserve memory.

GDI zero-initializes the buffer pointed to by phsurfPatterns before calling this function.

DrvEnablePDEV is required for graphics drivers.

See Also

DEVINFO, DEVMODEW, DrvEnableSurface, DrvRealizeBrush, EngCreatePalette, GDIINFO


DrvEnableSurface

HSURF DrvEnableSurface(

IN DHPDEV dhpdev

);

DrvEnableSurface sets up a surface to be drawn on and associates it with a given PDEV.

Parameters

dhpdev
Identifies a handle to a PDEV. This value is the return value of DrvEnablePDEV. The PDEV describes the physical device for which a surface is to be created.

Return Value

The return value is a handle that identifies the newly created surface. Otherwise, it is zero, and an error code is logged.

Comments

Depending on the device and circumstances, the driver can do any of the following to enable the surface:

  • If the driver manages its own surface, the driver can call EngCreateDeviceSurface to get a handle for the surface.

  • GDI can manage the surface completely if the device has a surface that resembles a standard-format bitmap. The driver can obtain a bitmap handle for the surface by calling EngCreateBitmap with a pointer to the buffer for the bitmap.

  • GDI can collect the graphics directly onto a GDI bitmap. The driver should call EngCreateBitmap, allowing GDI to allocate memory for the bitmap. This function is generally used only by printer devices.

Any existing GDI bitmap handle is a valid surface handle.

Before defining and returning a surface, a graphics driver must associate the surface with the physical device using EngAssociateSurface. This GDI function allows the driver to specify which graphics output routines are supported for standard-format bitmaps. A call to this function can only be made when no surface exists for the given physical device.

DrvEnableSurface is required for graphics drivers.

See Also

DrvDisableSurface, DrvEnablePDEV, EngCreateBitmap, EngCreateDeviceSurface


DrvEndDoc

BOOL DrvEndDoc(

IN SURFOBJ *pso,

IN FLONG fl

);

DrvEndDoc causes the driver to send any control information needed at the end of a document.

Parameters

pso
Points to a SURFOBJ structure that defines the surface object. The structure can be queried to find its type and what PDEV it is associated with.
fl
Specifies a set of flags. If the ED_ABORTDOC flag is set, the print job has been canceled or aborted.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

DrvEndDoc is required for printer drivers.


DrvEscape

ULONG DrvEscape(

IN SURFOBJ *pso,

IN ULONG iEsc,

IN ULONG cjIn,

IN PVOID *pvIn,

IN ULONG cjOut,

OUT PVOID *pvOut

);

DrvEscape is used for retrieving information from a device that is not available in a device-independent device driver interface. The particular query depends on the value of the iEsc parameter. Drawing on the device is not allowed in this function. DrvDrawEscape is to be used for specialized drawing support.

Parameters

pso
Points to a SURFOBJ structure that describes the surface to which the call is directed.
iEsc
Specifies a query. The meaning of the other parameters depends on this value. ESC_QUERYESCSUPPORT is the only predefined value; it queries whether the driver supports a particular escape function. In this case, pvIn points to an escape function number; cjOut and pvOut are ignored. If the specified function is supported, the return value is nonzero.
cjIn
Specifies the size, in bytes, of the buffer pointed to by pvIn.
pvIn
Points to the input data for the call. The format of the input data depends on the query specified by the iEsc parameter.
cjOut
Specifies the size, in bytes, of the buffer pointed to by pvOut.
pvOut
Points to the output buffer. The format of the output data depends on the query specified by the iEsc parameter.

Return Value

The return value is dependent on the query specified by the iEsc parameter. If the function specified in the query is not supported, the return value is zero.

Comments

DrvEscape is optional for all drivers.

See Also

DrvDrawEscape, SURFOBJ


DrvFillPath

BOOL DrvFillPath(

IN SURFOBJ *pso,

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrushOrg,

IN MIX mix,

IN FLONG flOptions

);

DrvFillPath is an optional entry point to handle the filling of closed paths.

Parameters

pso
Points to a SURFOBJ structure that defines the surface on which to draw.
ppo
Points to a PATHOBJ structure that defines the path to be filled. The PATHOBJ_Xxx service routines are provided to enumerate the lines, Bezier curves, and other data that make up the path.
pco
Points to a CLIPOBJ structure. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles.
pbo
Points to a BRUSHOBJ structure that defines the pattern and colors to fill with.
pptlBrushOrg
Points to a POINTL structure that defines the brush origin, which is used to align the brush pattern on the device.
mix
Defines the foreground and background raster operations to use for the brush.
flOptions
Specifies either FP_WINDINGMODE, indicating that a winding mode fill should be performed, or FP_ALTERNATEMODE, indicating that an alternating mode fill should be performed. All other flags should be ignored.

Return Value

The return value is TRUE if the driver is able to fill the path. If the path or clipping is too complex to be handled by the driver and should be handled by GDI, the return value is FALSE, and an error code is not logged. If the driver encounters an unexpected error, such as not being able to realize the brush, the return value is DDI_ERROR, and an error code is logged.

Comments

GDI can call DrvFillPath to fill a path on a device-managed surface. When deciding whether to call this function, GDI compares the fill requirements with the following flags in the flGraphicsCaps member of the DEVINFO structure: GCAPS_BEZIERS, GCAPS_ALTERNATEFILL, and GCAPS_WINDINGFILL.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

BRUSHOBJ, CLIPOBJ, DEVINFO, DrvStrokeAndFillPath, PATHOBJ, SURFOBJ


DrvFontManagement

ULONG DrvFontManagement(

IN SURFOBJ *pso,

IN FONTOBJ *pfo,

IN ULONG iMode,

IN ULONG cjIn,

IN PVOID pvIn,

IN ULONG cjOut,

OUT PVOID pvOut

);

DrvFontManagement is an optional entry point provided for PostScript devices. If this function is hooked by the device driver then GDI will pass calls made by an application to ExtEscape for escape numbers 0x100 through 0x3fe, or for the QUERYESCSUPPORT escape when the first DWORD pointed to by pvIn is in the range 0x100 through 0x3fe.

Parameters

pso
Points to a SURFOBJ.
pfo
Points to a FONTOBJ.
iMode
Specifies the escape number to be perfomed. This must either be equal to QUERYESCSUPPORT, or in the range 0x100 through 0x3FE.
cjIn
Specifies the size, in bytes, of the buffer pointed to by pvIn.
pvIn
Points to an input buffer. If the iMode parameter is QUERYESCSUPPORT, pvIn points to a ULONG value in the range 0x100 through 0x3FE.
cjOut
Specifies the size, in bytes, of the output buffer.
pvOut
Points to the output data buffer.

Return Value

The return value is a value in the range 0x00000001 through 0x7FFFFFFF if the function is successful. If the escape is not implemented, the return value is zero. If the function fails, the return value is a value in the range 0x80000000 through 0xFFFFFFFF.

See Also

FONTOBJ, SURFOBJ


DrvFree

VOID DrvFree(

IN PVOID pv,

IN ULONG id

);

DrvFree is used to notify the driver that the specified structure is no longer needed.

Parameters

pv
Points to a structure.
id
The identifier that was returned with the structure.

Comments

DrvFree is an optional function that should be supported only if the driver must be informed when memory associated with structures can be freed. For example, if a FONTOBJ structure is in use, deletion can be deferred until DrvDestroyFont has been called, eliminating the need for the driver to implement DrvFree.

A driver can use id in different ways. It can specify an object handle or it can indicate the way the structure is allocated. For example, it can differentiate between loaded resources and memory allocated from a heap. The driver can ignore this parameter if the structure pointed to by pv contains enough information.

See Also

DrvQueryFont, DrvQueryFontTree


DrvGetGlyphMode

ULONG DrvGetGlyphMode(

IN DHPDEV dhpdev,

IN FONTOBJ *pfo

);

DrvGetGlyphMode tells GDI how to cache glyph information.

Parameters

dhpdev
Identifies a PDEV.
pfo
Points to a FONTOBJ structure that can be queried to find the font size, transform, and so on.

Comments

GDI calls a driver’s DrvGetGlyphMode routine to determine the range of font information that should be cached for a particular font; that is, DrvGetGlyphMode determines what GDI stores in its font cache. A device that caches fonts on its own should return FO_HGLYPHS to minimize the storage requirements for the font.

GDI calls DrvGetGlyphMode for each font realization. For example, a driver might want to download outlines for point sizes larger than 12 point, but raster images for smaller fonts. However, GDI reserves the right to refuse this request.

The driver must check the RASTER_FONTTYPE bit of the flFontType member of the FONTOBJ structure to determine the actual form of the glyphs. If this bit is set, GDI is sending bitmaps; otherwise it is sending PATHOBJ structures.

At the time of the call to DrvGetGlyphMode, the associated FONTOBJ is not fully functional. GDI guarantees only that the IFIMETRICS structure and the notional-to-device transform are correct.

DrvGetGlyphMode is an optional driver function. If this function is not provided, GDI will store raster fonts by default.

Return Value

DrvGetGlyphMode returns one of the following values:
Value | Meaning
FO_GLYPHBITS | GDI should cache all glyph data for this font.
FO_HGLYPHS | The device caches fonts on its own, so GDI should cache only glyph handles for this font.
FO_PATHOBJ | GDI should cache PATHOBJs for this font.

See Also

FONTOBJ, FONTOBJ_cGetGlyphs, GLYPHDEF, GLYPHPOS, IFIMETRICS, PATHOBJ, STROBJ


DrvGetModes

ULONG DrvGetModes(

IN HANDLE hDriver,

IN ULONG cjSize,

OUT DEVMODEW *pdm

);

DrvGetModes lists the modes supported by a given device.

Parameters

hDriver
Specifies the handle to the kernel driver for which the modes must be enumerated. This is the handle that is passed in the hDriver parameter of the DrvEnablePDEV function.
cjSize
Specifies the size, in bytes, of the buffer pointed to by pdm.
pdm
Points to the buffer in which DEVMODEW structures will be written.

Return Value

The return value is the count of bytes written to the buffer, or, if pdm is null, the number of bytes required to hold all mode data. If an error occurs, the return value is zero, and an error code is logged.

Comments

This function is required for display drivers. It is called indirectly by the Display applet contained within the Control Panel to get the list of supported modes.

The Window Manager (user.exe) dynamically loads all display drivers associated with a miniport (based on the InstalledDisplayDrivers key in the registry). Each display driver is called to retrieve the list of modes supported by that combination of loaded drivers. For example, the VGA64K display driver only returns the 64K color modes that were returned in the list of modes obtained from the miniport driver.

The DrvGetModes function can be called even when the display driver has not been loaded.

See Also

DEVMODEW, DrvAssertMode, DrvEnablePDEV


DrvGetTrueTypeFile

PVOID DrvGetTrueTypeFile(

IN ULONG iFile,

IN ULONG *pcj

);

DrvGetTrueTypeFile accesses a memory-mapped TrueType font file.

Parameters

iFile
Identifies a driver TrueType font file.
pcj
Points to a ULONG value that specifies the size, in bytes, of the font file. This parameter cannot be null.

Return Value

The return value is a pointer to the memory-mapped TrueType font file if the function is successful. Otherwise, it is null.

Comments

This private entry point is provided by the TrueType driver to allow GDI efficient access to the memory-mapped TrueType font file.

DrvGetTrueTypeFile is required for TrueType font drivers.

See Also

DrvLoadFontFile


DrvLineTo

BOOL DrvLineTo(

SURFOBJ *pso,

CLIPOBJ *pco,

BRUSHOBJ *pbo,

LONG x1,

LONG y1,

LONG x2,

LONG y2,

RECTL *prclBounds,

MIX mix

);

DrvLineTo draws a single solid integer-only cosmetic line.

Parameters

pso
Pointer to a SURFOBJ structure that describes the surface on which to draw.
pco
Pointer to a CLIPOBJ structure that defines the clip region in which the rendering must be done. No pixels can be affected outside this clip region.
pbo
Pointer to a BRUSHOBJ structure that specifies the brush to use when drawing the line.
x1, y1
Specify the integer x- and y-coodinates of the line’s beginning point.
x2, y2
Specify the integer x- and y-coordinates of the line’s end point.
prclBounds
Pointer to an integer rectangle that bounds the unclipped line. Drivers that support hardware line drawing can use this rectangle to quickly determine whether the line fits in a coordinate space small enough to be rendered by the hardware.
mix
Defines how the incoming pattern should be mixed with the data already on the device surface. The low-order byte defines the raster operation. For more information about raster operation codes, see the Win32 SDK.

Return Value

DrvLineTo returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

DrvLineTo is an optional entry point that a driver can supply as an optimization for application calls to the Win32 LineTo function. If the driver doesn’t hook DrvLineTo, or if the driver returns FALSE from it, GDI will automatically call DrvStrokePath instead. A driver that has hooked DrvLineTo can call EngLineTo when the rendering surface is a DIB.

This function is simpler than DrvStrokePath because it supports only integer end-points and solid cosmetic lines. GDI has less overhead when calling DrvLineTo instead of DrvStrokePath; consequently, DrvLineTo is intended to be used as a simple optimization by drivers than can accelerate nominal width lines in hardware.

See Also

BRUSHOBJ, CLIPOBJ, EngLineTo, SURFOBJ


DrvLoadFontFile

ULONG DrvLoadFontFile(

ULONG iFile,

PVOID pvView,

ULONG cjView,

ULONG ulLangID

);

DrvLoadFontFile identifies the file used to create fonts.

Parameters

iFile
Handle to the file that contains font data. The driver should retain this handle for subsequent calls to EngMapFontFile, as the font file may be unmapped until it is actually used.
pvView
Pointer to the memory-mapped font file.
cjView
Size, in bytes, of the view pointed to by pvView.
ulLangID
Specifies the language ID used when returning a locale-dependent string or name information.

Return Value

The return value is a unique identifier for a font file if the function is successful. Otherwise, it is FD_ERROR.

Comments

When DrvLoadFontFile is called, the driver should do any conversions necessary to prepare the file for use. If a font driver uses other DLLs, then it is responsible for ensuring that the DLL addresses to be accessed are valid.

Each font driver could obtain the parameters pvView and cjView by passing iFile to EngMapFontFile. However, for performance reasons, GDI calls EngMapFontFile once when loading fonts to obtain pvView and cjView, determines which font driver supports the font data in the file, and then calls that driver’s DrvLoadFontFile implementation with pvView and cjView. Font files can be unmapped until they are actually going to be used, so drivers should record iFile to do the remapping later.

DrvLoadFontFile returns a unique identifier that allows the graphics engine to request the correct font. The graphics engine maintains a font usage table. After a font is loaded, the graphics engine will not try to load the same font again.

DrvLoadFontFile is required for font drivers.

See Also

DrvUnloadFontFile, EngMapFontFile


DrvMovePointer

VOID DrvMovePointer(

IN SURFOBJ *pso,

IN LONG x,

IN LONG y,

IN RECTL *prcl

);

DrvMovePointer moves the pointer to a new position and ensures that GDI does not interfere with the display of the pointer.

Parameters

pso
Points to a SURFOBJ structure that describes the surface of a display device.
x,y
Specify the x and y coordinates on the display where the hot spot of the pointer should be positioned. An x value of -1 indicates that the pointer should be removed from the display because drawing is about to occur where it is presently located. If the pointer has been removed from the display and the x value is nonnegative, the pointer should be restored.
prcl
Points to a RECTL structure defining an area that bounds all pixels affected by the pointer on the display. GDI will not draw in this rectangle without first removing the pointer from the screen. This parameter can be null.

Comments

DrvMovePointer will not be called while any thread is drawing in the display driver unless the GCAPS_ASYNCMOVE flag is set in the flGraphicsCaps member of the DEVINFO structure.

DrvMovePointer is required for display drivers only if DrvSetPointerShape is also supported.

If a driver has registered the specified pointer using DrvSetPointerShape, DrvMovePointer must not fail.

See Also

DEVINFO, SURFOBJ


DrvNextBand

BOOL DrvNextBand(

IN SURFOBJ *pso,

IN POINTL *pptl

);

DrvNextBand realizes the contents of the band just drawn on the specified surface and returns the origin of the next band.

Parameters

pso
Points to a SURFOBJ structure that describes the banding surface.
pptl
Pointer to POINTL structure in which the origin of the next band is returned.

Return Value

DrvNextBand returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

Calls to DrvNextBand should only be made after an initial call to DrvStartBanding. DrvNextBand returns (-1,-1) in pptl when the end of a page or document is encountered.

The supplied surface must have been previously marked as a banding surface in EngMarkBandingSurface.

See Also

SURFOBJ


DrvPaint

BOOL DrvPaint(

IN SURFOBJ *pso,

IN CLIPOBJ *pco,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrushOrg,

IN MIX mix

);

DrvPaint paints a specified region. This function is required if any drawing is to be done on a device-managed surface.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to draw.
pco
Points to a CLIPOBJ structure that describes the area to be painted. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles.
pbo
Points to a BRUSHOBJ structure that defines the pattern and colors with which to fill.
pptlBrushOrg
Points to a POINTL structure that defines the brush origin, which is used to align the brush pattern on the device.
mix
Defines the foreground and background raster operations to use for the brush.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

Vector drivers can support this function with the help of EngCreatePath and the PATHOBJ_Xxx service routines.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

BRUSHOBJ, CLIPOBJ, EngCreatePath, PATHOBJ, SURFOBJ


DrvQueryAdvanceWidths

BOOL DrvQueryAdvanceWidths(

DHPDEV dhpdev,

FONTOBJ *pfo,

ULONG iMode,

HGLYPH *phg,

PVOID *pvWidths,

ULONG cGlyphs

);

DrvQueryAdvanceWidths returns character advance widths for a specified set of glyphs.

Parameters

dhpdev
Identifies a PDEV previously returned by DrvEnablePDEV.
pfo
Points to a FONTOBJ structure that identifies the font instance.
iMode
Specifies the type of information to be provided. This parameter can be one of the following values:

Value

Meaning

QAW_GETWIDTHS

The character advance widths are recorded as an array of 12.4 fixed-point numbers. This mode will not be used if the widths exceed the range of the 12.4 representation. The function returns TRUE if successful.

QAW_GETEASYWIDTHS

The character advance widths are returned as an array of 12.4 fixed-point numbers. This mode will not be used if the widths exceed the range of the 12.4 representation. This routine should compute widths as quickly as possible. If the computation of a glyph’s character advance width cannot be accomplished efficiently, the driver should write 0xFFFF into the buffer for that glyph. The function returns DDI_ERROR if an error occurs, FALSE if not all widths can be efficiently computed for this mode, or TRUE in all other cases.

phg
Points to an array of HGLYPH structures that specify the glyphs for which the driver will return character advance widths.
pvWidths
Points to a buffer where the driver will record data.
cGlyphs
Specifies the number of glyphs in the input buffer pointed to by phg.

Return Value

The return value is dependent on the value of the iMode parameter.

See Also

DrvEnablePDEV, FONTOBJ


DrvQueryFont

PIFIMETRICS DrvQueryFont(

IN DHPDEV dhpdev,

IN ULONG iFile,

IN ULONG iFace,

IN ULONG *pid

);

DrvQueryFont is used by GDI to get the IFIMETRICS structure for a given font. The driver fills the IFIMETRICS structure.

Parameters

dhpdev
Pointer to a PDEV handle that identifies a physical device. The PDEV was returned from a previous call to DrvEnablePDEV.
iFile
Identifies a driver font file. This identifier is returned by DrvLoadFontFile. This parameter is zero for device-resident fonts.
iFace
Specifies the one-based index of the driver font. GDI can query the number of fonts from the DEVINFO structure.
pid
Points to an identifier that GDI will pass to DrvFree when the IFIMETRICS structure is no longer needed. Depending on how the driver manages memory, this identifier can identify the structure, identify the way it was allocated, or do nothing at all.

Return Value

The return value is a pointer to the IFIMETRICS structure that describes the given font if the function is successful. Otherwise, it is null, and an error code is logged.

Comments

The IFIMETRICS structure must remain unchanged during the scope of the associated PDEV.

If the number of fonts in DEVINFO is -1 and iFace is zero, the driver should return the number of fonts it supports.

DrvQueryFont is required for font drivers and drivers that use driver-specific or device-specific fonts.

See Also

DEVINFO, DrvEnablePDEV, DrvLoadFontFile, IFIMETRICS


DrvQueryFontCaps

LONG DrvQueryFontCaps(

ULONG culCaps,

ULONG *pulCaps

);

DrvQueryFontCaps defines the capabilities of the font driver.

Parameters

culCaps
Specifies the number of ULONG values that can be written by the font driver.
pulCaps
Points to a buffer that will receive the ULONG values. The elements of the array have the following meanings:

Element

Meaning

pulCaps[0]

A count of the number of ULONG values in the complete array. If culCaps is less than this value, then the caller will receive an incomplete array.

pulCaps[1]

A set of flags that can be one or more of the following values:

QC_OUTLINES
If this flag is set, the font driver can return an outline description of the glyphs. Moreover, the fonts are assumed to be scalable. Bitmap fonts do not have this flag set.

QC_2_LEVEL
Indicates that the font driver is capable of returning monochrome bitmaps of each glyph. QC_2_LEVEL is equal to the previous QC_1_BIT value.

QC_4_LEVEL
Indicates that the font driver is capable of returning antialiased 8-bit-per-pixel bitmaps, where each pixel in the bitmap is represented by an unsigned 8-bit value in the range 0 through 3. The values contained in each BYTE indicate how much of the foreground color should be mixed into the background. For example, a value of 0 indicates that the pixel will take the color of the background, while a value of 3 indicates that the pixel will take the foreground color of the text.

QC_16_LEVEL
Indicates that the font driver is capable of returning antialiased 4-bit-per-pixel bitmaps where each pixel in the bitmap is represented by an unsigned 4-bit value in the range 0 through 15. The values contained in each nibble indicate how much of the foreground color should be mixed into the background. For example, a value of 0 indicates that the pixel will take the color of the background, while a value of 15 indicates that the pixel will take the foreground color of the text.

Return Value

The return value is the number of ULONG values written to the destination buffer if the function is successful. Otherwise, it is FD_ERROR.

Comments

A driver might report that it supports more than one format for a font. For example, a driver could, in principle, return with both QC_2_LEVEL and QC_16_LEVEL set, indicating that it is capable of returning both monochrome and antialiased versions of the font. All bitmaps generated by a font driver must start and end on 32-bit boundaries. If necessary, the end of a bitmap should be “padded” to satisfy this requirement.

See Also

DrvQueryFont, DrvQueryFontTree


DrvQueryFontData

LONG DrvQueryFontData(

IN DHPDEV dhpdev,

IN FONTOBJ *pfo,

IN ULONG iMode,

HGLYPH hg,

GLYPHDATA pgd,

IN PVOID pv,

ULONG cjSize

);

DrvQueryFontData retrieves information about a realized font. GDI provides a pointer to an array of glyph or kerning handles, and the driver returns information about the glyphs or kerning pairs. The driver can assume that all handles in the array are valid.

Parameters

dhpdev
Identifies a PDEV returned from a prior call to DrvEnablePDEV.
pfo
Points to a FONTOBJ structure that defines the font realization.
iMode
Specifies the type of information requested. This parameter can be one of the following values:

Value

Meaning

QFD_GLYPHANDBITMAP

If pgd is not null, then the driver should fill in the GLYPHDATA structure with the metrics of the glyph specified by hg.

If pv is not null, a GLYPHBITS structure should be written at this address. The driver should copy the glyph bitmap corresponding to the glyph specified by hg into this structure. The size of the structure is specified by cjSize.

If glyph bitmaps are not supported by the driver, this function will only be called with pv set to null.

If the driver supports glyph bitmaps, the return value is the size, in bytes, of the glyph bitmap. Otherwise, it is zero.

This mode must be supported.

QFD_GLYPHANDOUTLINE

If pgd is not null, then the driver should fill in the GLYPHDATA structure with the metrics of the glyph specified by hg.

If pv is not null, a PATHOBJ structure should be written at this address. The driver passes this PATHOBJ to the PATHOBJ Xxx services to create the outline for the glyph specified by hg. The cjSize parameter should be ignored.

The return value is zero if the function is successful. Otherwise, it is FD_ERROR.

Only font drivers that provide glyph outlines need to support this mode.

QFD_MAXEXTENTS

If pv is not null, the driver should write an FD_DEVICEMETRICS structure to the buffer pointed to by pv.

The return value is the size, in bytes, needed for the buffer if pv is null.

If pgd is nonzero, the font driver should copy an FD_REALIZEEXTRA structure to this address that corresponds to this realization of the font, as identified by the FONTOBJ.

QFD_MAXGLYPHBITMAP

The return value is the size, in bytes, of the largest RASTERGLYPH structure required for the font realization specified by pfo. The driver should ignore the hg, pv, pgd, and cjSize parameters.

Only font drivers that provide glyph bitmaps need to support this mode.

hg
Specifies the glyph.
pgd
Points to GLYPHDATA structure. This parameter can be null.
pv
Points to a data buffer. The type of data written to this buffer is dependent on iMode. This parameter can be null.
cjSize
Specifies the size of the buffer pointed to by pv.

Return Value

The return value depends on the value of the iMode parameter. If an error occurs, the return value is FD_ERROR, and an error code is logged.

Comments

DrvQueryFontData is required for font drivers and drivers that use device-specific or driver-specific fonts.

See Also

DrvEnablePDEV, DrvQueryFontFile, FD_DEVICEMETRICS, FONTOBJ, GLYPHBITS, PATHOBJ


DrvQueryFontFile

LONG DrvQueryFontFile(

IN ULONG iFile,

IN ULONG ulMode,

IN ULONG cjBuf,

IN ULONG *pulBuf

);

DrvQueryFontFile provides font file information.

Parameters

iFile
Identifies the driver font file. This identifier is returned by a prior call to DrvLoadFontFile.
ulMode
Specifies the type of information to be written. This parameter can be one of the following values:

Value

Meaning

QFF_DESCRIPTION

The function provides a string that Windows NT will use to describe the font file. A zero-terminated Unicode string is written to the buffer pointed to by pulBuffer.

QFF_NUMFACES

The function returns the number of typefaces in the font file; the cjBuf and pulBuf parameters are ignored. Typefaces are identified by an index ranging from one through the number of typefaces.

cjBuf
Specifies the size, in bytes, of the return buffer.
pulBuf
Points to the return buffer.

Return Value

If ulMode is QFF_NUMFACES, then the return value is the number of faces in the font file. If pulBuf is null, it is the number of bytes of data that would be written to pulBuf; otherwise, it is the number of bytes written to pulBuf. If an error occurs, the return value is FD_ERROR.

Comments

DrvQueryFontFile is required for font drivers.

See Also

DrvLoadFontFile


DrvQueryFontTree

PVOID DrvQueryFontTree(

IN DHPDEV dhpdev,

IN ULONG iFile,

IN ULONG iFace,

IN ULONG iMode,

IN ULONG *pid

);

When GDI calls DrvQueryFontTree, the driver supplies pointers to structures that define one of the following:

  • Mapping from Unicode to glyph handles, including glyph variants

  • Mapping of kerning pairs to kerning handles

Parameters

dhpdev
Identifies a device by a handle to its PDEV, returned from a prior call to DrvEnablePDEV.
iFile
Identifies the driver font file. This value is returned by DrvLoadFontFile.
iFace
Specifies the one-based index of the driver font.
iMode
Specifies the type of information to be provided. This can be one of the following values:

Value

Meaning

QFT_KERNPAIRS

GDI requests a pointer to a sorted, null-terminated array of FD_KERNINGPAIR structures.

The kerning pairs should be stored in increasing order. The primary key is the second Unicode character; the secondary key is the first Unicode character in the kerning pair.

QFT_GLYPHSET

GDI requests a pointer to an FD_GLYPHSET structure that defines the mappings from single Unicode characters to glyph handles.

pid
The driver writes an identifier at this address that GDI will pass to DrvFree, along with the returned pointer, when the FD_GLYPHSET structure or array of FD_KERNINGPAIR structures are no longer needed. Depending on how memory is managed in the driver, this identifier can identify the structure, identify the way it was allocated, or do nothing at all.

Return Value

The return value is a pointer to the requested structure if the function is successful. Otherwise, it is null, and an error code is logged.

Comments

The returned structure must remain unmodified until GDI calls DrvFree with the address of the structure.

DrvQueryFontTree is required for font drivers and drivers that use device-specific fonts.

See Also

DEVINFO, DrvEnablePDEV, DrvFree, DrvLoadFontFile, DrvQueryFontData, DrvQueryFontTree, FD_GLYPHSET, FD_KERNINGPAIR, IFIMETRICS


DrvQuerySpoolType

LONG DrvQuerySpoolType(

IN DHPDEV dhpdev,

IN OUT LPWSTR pwszDatatype

);

DrvQuerySpoolType retrieves the data type that the driver will use to spool a print job.

Parameters

dhpdev
Handle to this instance of the driver.
pwszDatatype
As input, pwszDatatype is a pointer to a null-terminated string specifying the data type that the spooler should use to record the print job. The input string is obtained from an application call to the Win32 StartDoc function; this string is empty if StartDoc has not been called. As output, pwszDatatype is a pointer to the data type that this driver will use to write the job to the spooler via EngWritePrinter. The data type returned by the driver overrides the type requested by the application.

Return Value

DrvQuerySpoolType returns TRUE upon success; it returns FALSE if it fails.

Comments

DrvQuerySpoolType allows a driver to specify the data type that it will use to write a print job. If a driver does not export DrvQuerySpoolType, and an application does not otherwise request a certain data type, the default data type of RAW is used to spool the job.

The size of the buffer to which pwszDatatype points is 80 characters.

Printer drivers can optionally implement this function.


DrvQueryTrueTypeOutline

LONG DrvQueryTrueTypeOutline(

IN DHPDEV dhpdev,

IN FONTOBJ *pfo,

IN HGLYPH hglyph,

IN BOOL bMetricsOnly,

IN GLYPHDATA *pgldt,

IN ULONG cjBuf,

IN TTPOLYGONHEADER *ppoly

);

DrvQueryTrueTypeOutline retrieves glyph outlines in native TrueType format.

Parameters

dhpdev
Specifies a handle to a PDEV returned from a call to DrvEnablePDEV.
pfo
Points to a FONTOBJ structure. Details of the font realization can be queried from this structure.
hglyph
Specifies the HGLYPH structure for which the outline is being queried.
bMetricsOnly
Specifies whether the TrueType outline should not be provided; if this parameter is TRUE, the TrueType outline is not provided. If it is FALSE and ppoly is not null, the TrueType outline is provided.
pgldt
Points to the buffer where the GLYPHDATA structure for this glyph should be written. If pgldt is null, this parameter is ignored.
cjBuf
Specifies the size, in bytes, of the buffer that will contain the TrueType outline.
ppoly
Points to the buffer where the TrueType outline should be written. The format of the data is in native TrueType format, stored in a TTPOLYGONHEADER structure.

Return Value

The return value is the size, in bytes, required for the ppoly buffer if pgldt is null. If pgldt is not null, the return value is the number of bytes copied into the ppoly buffer. If an error occurs, the return value is FD_ERROR.

Comments

DrvQueryTrueTypeOutline is required for TrueType font drivers.

See Also

DrvEnablePDEV, FONTOBJ


DrvQueryTrueTypeTable

LONG DrvQueryTrueTypeTable(

IN ULONG iFile,

IN ULONG ulFont,

IN ULONG ulTag,

IN PTRDIFF dpStart,

IN ULONG cjBuf,

OUT BYTE *pjBuf

);

DrvQueryTrueTypeTable accesses specific tables in a TrueType font-description file.

Parameters

iFile
Identifies the driver-provided TrueType font file.
ulFont
Specifies the one-based index of the driver font.
ulTag
Specifies the table to access. If ulTag is zero, access is to the entire file.
dpStart
Specifies the offset from the beginning of the table to begin access. If ulTag is zero, dpStart is the offset from the beginning of the file.
cjBuf
Specifies the size of the buffer pointed to by pjBuf.
pjBuf
Points to the buffer in which to copy the table or file data.

Return Value

If pfBuf is null, the return value is the size, in bytes, required for this buffer. If pfBuf is not null, the return value is the number of bytes copied. If an error occurs, the return value is FD_ERROR.

Comments

DrvQueryTrueTypeTable is required for TrueType font drivers.


DrvRealizeBrush

BOOL DrvRealizeBrush(

IN BRUSHOBJ *pbo,

IN SURFOBJ *psoTarget,

IN SURFOBJ *psoPattern,

IN SURFOBJ *psoMask,

IN XLATEOBJ *pxlo,

ULONG iHatch

);

DrvRealizeBrush requests that the driver realize a specified brush for a specified surface.

Parameters

pbo
Points to the BRUSHOBJ that is to be realized. All other parameters, except for psoTarget, can be queried from this object. Parameter specifications are provided as an optimization. This parameter is best used only as a parameter for BRUSHOBJ_pvAllocRBrush, which allocates the memory for the realized brush.
psoTarget
Points to the surface for which the brush is to be realized. This surface can be the physical surface for the device, a device format bitmap, or a standard format bitmap.
psoPattern
Points to the surface that describes the pattern for the brush. For a raster device, this is a bitmap. For a vector device, this is one of the pattern surfaces provided by DrvEnablePDEV.
psoMask
Points to a transparency mask for the brush. This is a 1 bit per pixel bitmap that has the same extent as the pattern. A mask of zero means the pixel is considered a background pixel for the brush. (In transparent background mode, the background pixels are unaffected in a fill.) Plotters can ignore this parameter because they never draw background information.
pxlo
Points to a XLATEOBJ that defines the interpretration of colors in the pattern. A XLATEOBJ Xxx service routine can be called to translate the colors to device color indices. Vector devices should translate color zero through the XLATEOBJ to get the foreground color for the brush.
iHatch
Specifies whether psoPattern is one of the hatch brushes returned by DrvEnablePDEV. This is true if the value of this parameter is less than HS_API_MAX.

Return Value

The return value is TRUE if the brush was successfully realized. Otherwise, it is FALSE, and an error code is logged.

Comments

To realize a brush, the driver converts a GDI brush into a form that can be used internally. A realized brush contains information and accelerators the driver needs to fill an area with a pattern; information that is defined by the driver and used only by the driver.

The driver’s realization of a brush is written into the buffer allocated by a call to BRUSHOBJ_pvAllocRbrush.

DrvRealizeBrush is required for a driver that does any drawing to any surface.

See Also

BRUSHOBJ, BRUSHOBJ_pvAllocRBrush, DrvEnablePDEV, XLATEOBJ


DrvResetPDEV

BOOL DrvResetPDEV (

DHPDEV dhpdevOld,

DHPDEV dhpdevNew

);

DrvResetPDEV transfers the state of the driver from the old PDEV to the new PDEV when a Win32 application calls ResetDC.

Parameters

dhpdevOld
Identifies the original device PDEV.
dhpdevNew
Identifies the new device PDEV.

Return Value

This return value is TRUE if the function is successful. Otherwise it is FALSE, and an error code is logged.

Comments

This function can set a flag so that when DrvDisablePDEV is called, the page is not ejected in the case of double-sided printing. The new PDEV should also be marked so that DrvStartDoc on the new PDEV does not cause a new header to be downloaded, as in PostScript printing.

If the driver caches fonts that are transferable between the two modes, they should be transferred during this function.

If DrvStartDoc was called before ResetDC, the driver receives the following sequence of calls:

    dhpdevNew = DrvEnablePDEV();
    DrvResetPDEV(dhpdevOld, dhpdevNew);
    DrvDisableSurface(dhpdevOld);
    DrvDisablePDEV(dhpdevOld);
    DrvEnableSurface(dhpdevNew);
    DrvStartDoc(dhpdevNew);

If ResetDC is called between documents there is no surface associated with the PDEV, so only the following sequence of calls is made:

    dhpdevNew = DrvEnablePDEV();
    DrvResetPDEV(dhpdevOld,dhpdevNew);
    DrvDisablePDEV(dhpdevOld);

If the same fonts can be used by both the old and new PDEVs, then the cached font data should be moved into the new PDEV at the time of this call. The font data associated with the original PDEV will disappear when the original PDEV is deleted.

Note that no surface has been created for the new PDEV when DrvResetPDEV is called.

DrvResetPDEV is required for output devices to allow mode changes in documents.

See Also

DrvDisablePDEV, DrvDisableSurface, DrvEnablePDEV, DrvEnableSurface, DrvStartDoc


DrvSaveScreenBits

ULONG DrvSaveScreenBits(

IN SURFOBJ *pso,

IN ULONG iMode,

IN ULONG ident,

IN RECTL *prcl

);

DrvSaveScreenBits causes a display driver to save or restore a given rectangle of the displayed image.

Parameters

pso
Points to a SURFOBJ structure that describes the surface.
iMode
Specifies the operation to perform. The iMode parameter can be one of the following values:

Value

Meaning

SS_SAVE

Saves data from the rectangle defined by prcl. The driver is responsible for managing this data in its off-screen memory. An identifier is returned that identifies this saved data. (The driver can return a handle or even a pointer to its off-screen memory.) The ident parameter is ignored.

The return value is an identifier, which can be a handle or a pointer, if the function is successful. Otherwise, it is zero.

SS_RESTORE

Restores the data identified by ident to the rectangle prcl on the display. The driver can assume that the rectangle at prcl has exactly the same size as the rectangle that was saved. The data should be discarded after this call.

The return value is TRUE if the data has been restored to the display, or FALSE if the data cannot be restored.

SS_FREE

Indicates that the data identified by ident is no longer needed and can be freed. The rectangle at prcl has the same size as the rectangle that was saved.

The return value is TRUE.

ident
Specifies an identifier returned by a previous call to DrvSaveScreenBits, if iMode is SS_RESTORE or SS_FREE. Otherwise, this parameter is ignored.
prcl
Points to a RECTL structure that defines the portion of the screen to be saved or restored.

Return Value

The return value is dependent on the value of the iMode parameter.

Comments

Some display drivers might be able to move data to or from off-screen device memory much faster than the area can be redrawn. This might be useful when the window manager must display a menu or dialog box.

DrvSaveScreenBits is optional for graphics drivers.

See Also

SURFOBJ


DrvSendPage

BOOL DrvSendPage(

IN SURFOBJ *pso

);

DrvSendPage requests that the raw bits from the indicated surface be sent to the printer.

Parameters

pso
Points to the surface object on which the drawing has been accumulated. The object can be queried to find its type and which PDEV it is associated with.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

Typically, DrvSendPage calls EngWritePrinter to request that the data be sent to the printer. However, if the hardcopy device is accessed via I/O ports, then a base driver should be used instead to gain access to the needed ports.

If the surface is a bitmap on which the drawing has been accumulated, the driver should access the bits via SURFOBJ Xxx service routines. If a driver has used a device-managed surface and sent the bits to the printer as the drawing orders came in, this call does not send out the drawing.

The control code that causes a page to be ejected from the printer should be sent as a result of this call.

If this function is slow, the driver should allow the user to abort the print job. In this case, the driver should call EngCheckAbort at least once every ten seconds to check whether printing should be terminated. If EngCheckAbort returns TRUE, processing of the page should be stopped and DrvSendPage should return FALSE.

DrvSendPage is required for printer drivers.

See Also

EngCheckAbort, SURFOBJ


DrvSetPalette

BOOL DrvSetPalette(

IN DHPDEV dhpdev,

IN PALOBJ *ppalo,

IN FLONG fl,

IN ULONG iStart,

IN ULONG cColors

);

DrvSetPalette requests that the driver realize the palette for a specified device. The driver sets the hardware palette to match the entries in the given palette as closely as possible.

Parameters

dhpdev
Identifies the device whose palette is to be realized. This parameter is the device handle returned to GDI by DrvEnablePDEV.
ppalo
Points to the PALOBJ from which the colors (RGB values) should be queried.
fl
A set of flags that provides hints and options. This parameter can be the following value:

Value

Meaning

SP_DEFAULT

The palette is the device’s complete default palette. The PALOBJ can be ignored, but contains the correct contents.

iStart
Specifies the first palette index to overwrite.
cColors
Specifies the number of colors to change in the hardware palette. Extra colors, beyond the number available in the hardware, can be ignored. If cColors is smaller than the size of the hardware palette, set only cColor entries and leave the remaining colors as they are.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

Only indexed palettes are realizeable. The RC_PALETTE bit of the flRasterCaps member of the GDIINFO structure specifies whether a device has a realizeable palette.

DrvSetPalette is required for display drivers that support realizeable palettes.

See Also

DrvEnablePDEV, EngCreatePalette, EngDeletePalette


DrvSetPixelFormat

BOOL DrvSetPixelFormat(

IN SURFOBJ *pso,

IN LONG iPixelFormat,

IN ULONG hwnd

);

DrvSetPixelFormat sets the pixel format of a window.

Parameters

pso
Points to the SURFOBJ with which the window is associated.
iPixelFormat
Index that specifies the device format to which the pixel format is to be set. The pixel formats that a device supports are identified by positive one-based integer indices starting at 1.
hwnd
Identifies the window whose pixel format is to be set.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

Setting the pixel format more than once can result in complications for the Window Manager and for multi-threaded applications. Consequently, the pixel format of a window can be set only once and must remain unchanged.

See Also

DrvDescribePixelFormat, SURFOBJ


DrvSetPointerShape

ULONG DrvSetPointerShape(

IN SURFOBJ *pso,

IN SURFOBJ *psoMask,

IN SURFOBJ *psoColor,

IN XLATEOBJ *pxlo,

IN LONG xHot,

IN LONG yHot,

IN LONG x,

IN LONG y,

OUT RECTL *prcl,

IN FLONG fl

);

DrvSetPointerShape is used to request the driver to:

  1. Take the pointer off the display, if the driver has drawn it there.

  2. Attempt to set a new pointer shape.

  3. Put the new pointer on the display at a specified position.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to draw.
psoMask
Points to a SURFOBJ structure that defines the AND-XOR mask. (The AND-XOR mask is described in Drawing Monochrome Pointers.) The dimensions of this bitmap determine the size of the pointer. There are no implicit constraints on pointer sizes, but optimal pointer sizes are 32 x 32, 48 x 48, and 64 x 64 pixels. If this parameter is null, the pointer is transparent.
psoColor
Points to a SURFOBJ structure that defines the colors for a color pointer. If this parameter is null, the pointer is monochrome. The pointer bitmap has the same width as psoMask and half the height.
pxlo
Points to a XLATEOBJ structure that defines the colors in psoColor.
xHot, yHot
Specify the x and y positions of the pointer’s hot spot relative to its upper-left pixel. The pixel indicated by the hot spot should be positioned at the new pointer position.
x, y
Specify the new pointer position.
prcl
If nonnull, the driver should write a rectangle that bounds all pixels affected by the pointer on the display. GDI avoids drawing on this rectangle without first moving the pointer out of the way.
fl
Specifies an extensible set of flags. The driver should decline the call if any flags are set that it does not understand. This parameter can be one or more of the following predefined values, and one or more driver-defined values:

Flag

Meaning

SPS_CHANGE

The driver is requested to change the pointer shape.

SPS_ASYNCCHANGE

The driver should accept the change only if it is capable of changing the pointer shape in the hardware while other drawing is underway on the device. GDI uses this option only if the GCAPS_ASYNCCHANGE flag is set in the flGraphicsCaps member of the DEVINFO structure.

SPS_ANIMATESTART

The driver should be prepared to receive a series of similarly-sized pointer shapes that will comprise an animated pointer effect.

SPS_ANIMATEUPDATE

The driver should draw the next pointer shape in the animated series.

Return Value

The return value can be one of the following values:
Value | Meaning
SPS_ERROR | The driver normally supports this shape, but failed for unusual reasons.
SPS_DECLINE | The driver does not support the shape, so GDI must simulate it.
SPS_ACCEPT_NOEXCLUDE | The driver accepts the shape. The shape is supported in hardware and GDI is not concerned about other drawings overwriting the pointer.
SPS_ACCEPT_EXCLUDE | The driver accepts the shape. GDI does not read from or write to the rectangle written at prcl without first moving the pointer out of the way.

Comments

DrvSetPointerShape is optional for display drivers.

See Also

SURFOBJ, XLATEOBJ


DrvStartBanding

BOOL DrvStartBanding(

IN SURFOBJ *pso,

IN POINTL *pptl

);

DrvStartBanding prepares the driver for banding and returns the origin of the first band.

Parameters

pso
Points to a SURFOBJ structure that describes the banding surface.
pptl
Pointer to the origin of the first band.

Return Value

DrvStartBanding returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

To actually realize the band contents rendered to the surface identified by pso, DrvNextBand must be called.

The supplied surface must previously have been marked as a banding surface in EngMarkBandingSurface. The size of the band is determined by device and/or driver limitations, and is known only to the driver.

See Also

SURFOBJ


DrvStartDoc

BOOL DrvStartDoc(

IN SURFOBJ *pso,

IN PWSTR pwszDocName,

IN DWORD dwJobId

);

DrvStartDoc sends any control information needed at the start of a document.

Parameters

pso
Points to the surface object. The object can be queried to find its type and which PDEV it is associated with.
pwszDocName
Points to a zero-terminated Unicode string specifying the user-readable name of the document being printed.
dwJobId
Identifies the print job. This value is returned to GDI from the spooler in a call to StartDocPrinter.

Return Value

The return value is TRUE if the control information is received. Otherwise, it is FALSE, and an error code is logged.

Comments

The control codes should be sent using WritePrinter. See the Win32 SDK.

DrvStartDoc is required for printer drivers.


DrvStartPage

BOOL DrvStartPage(

IN SURFOBJ *pso

);

DrvStartPage requests the driver send any control information needed at the start of a page. The control codes should be sent using EngWritePrinter.

Parameters

pso
Points to the surface object. The object can be queried to find its type and which PDEV it is associated with.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

DrvStartPage is required for printer drivers.

See Also

EngWritePrinter


DrvStretchBlt

BOOL DrvStretchBlt(

IN SURFOBJ *psoDest,

IN SURFOBJ *psoSrc,

IN SURFOBJ *psoMask,

IN CLIPOBJ *pco,

IN XLATEOBJ *pxlo,

IN COLORADJUSTMENT *pca,

IN POINTL *pptlHTOrg,

IN RECTL *prclDest,

IN RECTL *prclSrc,

IN POINTL *pptlMask,

IN ULONG iMode

);

DrvStretchBlt provides stretching bit-block transfer capabilities between any combination of device-managed and GDI-managed surfaces. This function enables the device driver to write to GDI bitmaps, especially when the driver can do halftoning. This function allows the same halftoning algorithm to be applied to GDI bitmaps and device surfaces.

Parameters

psoDest
Points to a SURFOBJ that identifies the surface on which to draw.
psoSrc
Points to a SURFOBJ that defines the source for the bit-block transfer operation.
psoMask
This optional parameter points to a surface that provides a mask for the source. The mask is defined by a logic map, which is a bitmap with 1 bit per pixel.

The mask limits the area of the source that is copied. If this parameter is specified, it has an implicit rop4 of 0xCCAA, meaning the source should be copied wherever the mask is one, but the destination should be left alone wherever the mask is zero.

When this parameter is null there is an implicit rop4 of 0xCCCC, which means that the source should be copied everywhere in the source rectangle.

The mask will always be large enough to contain the relevant source; tiling is unnecessary.

pco
Points to a CLIPOBJ that limits the area to be modified in the destination. GDI services are provided to enumerate the clip region as a set of rectangles.

Whenever possible, GDI simplifies the clipping involved. However, unlike DrvBitBlt, DrvStretchBlt can be called with a single clipping rectangle. This prevents rounding errors in clipping the output.

pxlo
Points to a XLATEOBJ that specifies how color indices are to be translated between the source and target surfaces.

The XLATEOBJ can also be queried to find the RGB color for any source index. A high quality stretching bit-block transfer will need to interpolate colors in some cases.

pca
Points to a COLORADJUSTMENT structure that defines the color adjustment values to be applied to the source bitmap before stretching the bits. (See the Win32 SDK.)
pptlHTOrg
Specifies the origin of the halftone brush. Device drivers that use halftone brushes should align the upper left pixel of the brush’s pattern with this point on the device surface.
prclDest
Points to a RECTL structure that defines the area to be modified in the coordinate system of the destination surface. This rectangle is defined by two points that are not necessarily well ordered, meaning the coordinates of the second point are not necessarily larger than those of the first point. The rectangle they describe does not include the lower and right edges. This function is never called with an empty destination rectangle.

DrvStretchBlt can do inversions of x and y when the destination rectangle is not well ordered.

prclSrc
Points to a RECTL that defines the area that will be copied in the coordinate system of the source surface. The rectangle is defined by two points, and will map onto the rectangle defined by prclDest. The points of the source rectangle are well ordered. This function is never given an empty source rectangle.

The mapping is defined by prclSrc and prclDest. The points specified in prclDest and prclSrc lie on integer coordinates, which correspond to pixel centers. A rectangle defined by two such points is considered to be a geometric rectangle with two vertices whose coordinates are the given points, but with 0.5 subtracted from each coordinate. (POINTL structures should be considered a shorthand notation for specifying these fractional coordinate vertices.)

The edges of any rectangle never intersect a pixel, but go around a set of pixels. The pixels inside the rectangle are those expected for a “bottom-right exclusive” rectangle. DrvStretchBlt will map the geometric source rectangle exactly onto the geometric destination rectangle.

pptlMask
Points to a POINTL structure that specifies which pixel in the given mask corresponds to the upper left pixel in the source rectangle. Ignore this parameter if no mask is specified.
iMode
Specifies how source pixels are combined to get output pixels. The HALFTONE mode is slower than the other modes, but produces higher quality images.

Value

Meaning

WHITEONBLACK

On a shrinking bit-block transfer, pixels should be combined with a Boolean OR operation. On a stretching bit-block transfer, pixels should be replicated.

BLACKONWHITE

On a shrinking bit-block transfer, pixels should be combined with a Boolean AND operation. On a stretching bit-block transfer, pixels should be replicated.

COLORONCOLOR

On a shrinking bit-block transfer, enough pixels should be ignored so that pixels don’t need to be combined. On a stretching bit-block transfer, pixels should be replicated.

HALFTONE

The driver can use groups of pixels in the output surface to best approximate the color or gray level of the input.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

This function can be provided to handle only certain forms of stretching, such as by integer multiples. If the driver has hooked the call and is asked to perform an operation it does not support, the driver should forward the data to EngStretchBlt for GDI to handle.

If the driver wants GDI to handle halftoning, and wants to ensure the proper iMode value, the driver can hook DrvStretchBlt, set iMode to HALFTONE, and call back to GDI with EngStretchBlt with the set iMode value.

DrvStretchBlt is optional for display drivers.

See Also

CLIPOBJ, DrvBitBlt, EngStretchBlt, SURFOBJ, XLATEOBJ


DrvStrokeAndFillPath

BOOL DrvStrokeAndFillPath(

IN SURFOBJ *pso,

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN XFORMOBJ *pxo,

IN BRUSHOBJ *pboStroke,

IN LINEATTRS *plineattrs,

IN BRUSHOBJ *pboFill,

IN POINTL *pptlBrushOrg,

IN MIX mixFill,

IN FLONG flOptions

);

DrvStrokeAndFillPath fills and strokes a path concurrently.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to draw.
ppo
Points to a PATHOBJ structure that describes the path to be filled. The PATHOBJ_Xxx service routines are provided to enumerate the lines, Bezier curves, and other data that make up the path.
pco
Points to a CLIPOBJ structure. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles.
pxo
Points to a XFORMOBJ structure that is required when a geometric wide line is drawn. It specifies the transform that takes world coordinates to device coordinates. This is needed because the path is provided in device coordinates but a geometric wide line is actually widened in world coordinates. The XFORMOBJ can be queried to find out what the transform is.
pboStroke
Specifies the brush to use when stroking the path.
plineattrs
Points to the LINEATTRS structure that describes the attributes of the line to be drawn.
pboFill
Specifies the brush to use when filling the path.
pptlBrushOrg
Specifies the brush origin for both brushes.
mixFill
Specifies the foreground and background raster operations to use for the fill brush.
flOptions
Specifies either FP_WINDINGMODE, meaning that a winding mode fill should be performed, or FP_ALTERNATEMODE, meaning that an alternating mode fill should be performed. All other flags should be ignored.

Return Value

The return value is TRUE if the driver can fill the path. Otherwise, if GDI should fill the path, the return value is FALSE. If an error occurs, the return value is DDI_ERROR, and an error code is logged.

Comments

If a wide line is used for stroking, the filled area must be reduced to compensate.

The driver can return FALSE if the path or the clipping is too complex for the device to handle; in that case, GDI converts to a simpler call. For example, if the device driver has set the GCAPS_BEZIERS flag in the flGraphicsCaps member of the DEVINFO structure and then receives a path with Bezier curves, it can return FALSE; GDI will then convert the Bezier curves to lines and call DrvStrokeAndFillPath again. If the device driver returns FALSE again, GDI can convert the call into separate DrvStrokePath and DrvPaint calls.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

CLIPOBJ, DrvFillPath, DrvPaint, DrvStrokePath, LINEATTRS, PATHOBJ, SURFOBJ, XFORMOBJ


DrvStrokePath

BOOL DrvStrokePath(

IN SURFOBJ *pso,

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN XFORMOBJ *pxo,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrushOrg,

IN LINEATTRS *plineattrs,

IN MIX mix

);

DrvStrokePath strokes a path when called by GDI. If the driver has hooked the function, and if the appropriate GCAPs are set, GDI calls DrvStrokePath when GDI draws a line or curve with any set of attributes.

Parameters

pso
Identifies the surface on which to draw.
ppo
Points to a PATHOBJ structure. GDI PATHOBJ_Xxx service routines are provided to enumerate the lines, Bezier curves, and other data that make up the path. This indicates what is to be drawn.
pco
Points to a CLIPOBJ structure. GDI CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles. Optionally, all the lines in the path may be enumerated preclipped by CLIPOBJ. This means that drivers can have all their line clipping calculations done for them.
pxo
Points to a XFORMOBJ. This is only needed when a geometric wide line is to be drawn. It specifies the transform that maps world coordinates to device coordinates. This is needed because the path is provided in device coordinates but a geometric wide line is actually widened in world coordinates.

The XFORMOBJ can be queried to find the transform.

pbo
Specifies the brush to be used when drawing the path.
pptlBrushOrg
Points to the brush origin used to align the brush pattern on the device.
plineattrs
Points to a LINEATTRS structure. Note that the elStyleState member must be updated as part of this function if the line is styled. Also note that the ptlLastPel member must be updated if a single pixel width cosmetic line is being drawn.
mix
Specifies how to combine the brush with the destination.

Return Value

The return value is TRUE if the driver is able to stroke the path. If GDI should stroke the path, the return value is FALSE, and an error code is not logged. If the driver encounters an error, the return value is DDI_ERROR, and an error code is logged.

Comments

If a driver supports this entry point, it should also support the drawing of cosmetic wide lines with arbitrary clipping. Using the provided GDI functions, the call can be broken down into a set of single-pixel-width lines with precomputed clipping.

This function is required if any drawing is to be done on a device-managed surface.

Drivers for advanced devices can optionally receive this call to draw paths containing Bezier curves and geometric wide lines. GDI will test the GCAPS_BEZIERS and GCAPS_GEOMETRICWIDE flags of the flGraphicsCaps member of the DEVINFO structure to decide whether it should call. (The four combinations of the bits determine the four levels of functionality for this call.) If the driver gets an advanced call containing Bezier curves or geometric wide lines, it can decide not to handle the call, returning FALSE. This might happen if the path or clipping is too complex for the device to process. If the call does return FALSE, GDI breaks the call down into simpler calls that can be handled easily.

For device-managed surfaces, the function must minimally support single-pixel-wide solid and styled cosmetic lines using a solid-colored brush. The device can return FALSE if the line is geometric and the engine will convert those calls to DrvFillPath or DrvPaint calls.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

CLIPOBJ, DEVINFO, DrvFillPath, DrvPaint, LINEATTRS, PATHOBJ, XFORMOBJ


DrvSwapBuffers

BOOL DrvSwapBuffers(

IN SURFOBJ *pso,

IN WNDOBJ *pwo

);

DrvSwapBuffers displays the contents of the window’s associated hidden buffer on the specified surface.

Parameters

pso
Points to the target surface that will be modified for display.
pwo
Points to the window object that defines the region on the target surface with which the back buffer will be swapped.

Return Value

The return value is TRUE if the function is successful; it is FALSE upon failure.

Comments

DrvSwapBuffers can affect the display only if the pixel format for the window specified by pwo is double-buffered. The content of the hidden buffer is undefined after the swap occurs.

This function is required if the driver supports a pixel format with double buffering; that is, if PFD_DOUBLEBUFFER is set in the dwFlags member of the PIXELFORMATDESCRIPTOR structure.

See Also

DrvDescribePixelFormat


DrvSynchronize

VOID DrvSynchronize(

IN DHPDEV dhpdev,

IN RECTL *prcl

);

DrvSynchronize supports drawing operations by coordinating coprocessor operations with GDI.

Parameters

dhpdev
Identifies the device to be synchronized with GDI. This parameter is the device handle returned to GDI by DrvEnablePDEV.
prcl
If nonnull, this parameter specifies a rectangle on the surface that GDI will draw into. If this does not collide with the drawing operation in progress, the driver can elect to let GDI draw without waiting for the coprocessor to complete.

Comments

DrvSynchronize is not an output function, but is intended to support devices that use a coprocessor for drawing. Such a device can start a long drawing operation and return to GDI while the operation continues. If the device driver does not perform all drawing operations to the surface, it is possible that a subsequent drawing operation will be handled by GDI. In this case, it is necessary for GDI to wait for the coprocessor to complete its work before drawing on the surface.

This function is only called if hooked by EngAssociateSurface. GDI calls it just before drawing directly onto the device surface.

The function should return when it is safe for GDI to draw on the surface.

See Also

DrvEnablePDEV


DrvTextOut

BOOL DrvTextOut(

IN SURFOBJ *pso,

IN STROBJ *pstro,

IN FONTOBJ *pfo,

IN CLIPOBJ *pco,

IN RECTL *prclExtra,

IN RECTL *prclOpaque,

IN BRUSHOBJ *pboFore,

IN BRUSHOBJ *pboOpaque,

IN POINTL *pptlOrg,

IN MIX mix

);

DrvTextOut is the entry point from GDI that calls for the driver to render a set of glyphs at specified positions.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to write.
pstro
Points to a STROBJ structure that defines the glyphs to be rendered and the positions in which to place them.
pfo
Points to a FONTOBJ structure from which to retrieve information about the font and its glyphs.
pco
Points to a CLIPOBJ structure that defines the clip region through which all rendering must be done. The driver cannot affect any pixels outside the clip region.
prclExtra
Points to a null-terminated array of rectangles. These rectangles are bottom-right exclusive. The pixels of the rectangles are to be combined with the pixels of the glyphs to produce the foreground pixels. The extra rectangles are used to simulate underlining or strike out.

If prclExtra is null then there are no extra rectangles to be rendered. If the prclExtra is not null then the rectangles are read until a null rectangle is reached. A null rectangle has both coordinates of both points set to zero.

prclOpaque
Points to a single opaque rectangle. This rectangle is bottom-right exclusive. Pixels within this rectangle (those that are not foreground and not clipped) are to be rendered with the opaque brush. This rectangle always bounds the text to be drawn. If this parameter is null, no opaque pixels are to be rendered.
pboFore
Points to the brush object to be used for the foreground pixels. This brush will always be a solid color brush.
pboOpaque
Points to the brush object for the opaque pixels. Both the foreground and background mix modes for this brush are assumed to be R2_COPYPEN. Unless the driver sets the GCAPS_ARBRUSHOPAQUE capabilities bit in the flGraphicsCaps member of the DEVINFO structure, it will always be called with a solid color brush.
pptlOrg
Points to a POINTL structure that defines the brush origin for both brushes.
mix
Specifies foreground and background raster operations (mix modes) for pboFore. The driver will only be called with both mix modes equal to COPYPEN unless it sets the GCAPS_ARBMIXTXT capabilities bit in the flGraphicsCaps member of the DEVINFO structure.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

The input parameters to DrvTextOut define two sets of pixels: foreground and opaque. The driver must render the surface so that the result is identical to a process where the opaque pixels are rendered first with the opaque brush, and then the foreground pixels are rendered with the foreground brush. Each of these operations is limited by clipping.

The foreground set of pixels is defined to be the union of the pixels of the glyphs with the pixels of the extra rectangles at prclExtra. These extra rectangles are used to simulate strike-through or underlines. The opaque pixels are defined by the opaque rectangle at prclOpaque.

The foreground and opaque pixels are regarded as a screen through which color is brushed onto the surface. The glyphs of the font do not have color in themselves.

The input parameters to DrvTextOut define the set of glyph pixels, the set of extra rectangles, the opaque rectangle, and the clip region. It is the driver’s responsibility to calculate and then render the set of foreground and opaque pixels.

GDI guarantees that DrvTextOut and DrvDestroyFont never overlap; consequently, the driver can rely on cached information while processing a DrvTextOut call.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

This is a required function.

See Also

CLIPOBJ, DEVINFO, FONTOBJ, STROBJ, SURFOBJ


DrvUnloadFontFile

BOOL DrvUnloadFontFile(

IN ULONG iFile

);

DrvUnloadFontFile informs a font driver that the specified font file is no longer needed.

Parameters

iFile
Identifies the font file to be removed. The iFile parameter is a value returned by DrvLoadFontFile.

Return Value

The return value is TRUE if the function is successful.

Comments

The driver should delete all scratch files, unload all DLLs that were loaded, and free all allocated system resources at this time.

This function is required for font drivers.


EngAcquireSemaphore

VOID EngAcquireSemaphore(

HSEMAPHORE hsem

);

EngAcquireSemaphore acquires the resource associated with the semaphore for exclusive access by the calling thread.

Parameters

hsem
Handle to the semaphore associated with the resource to be acquired.

Comments

EngAcquireSemaphore allows exclusive access to the driver resource associated with the semaphore by locking out all other threads from accessing the semaphore’s resource.

A call to this routine should be followed with a call to EngReleaseSemaphore as quickly as possible.

See Also

EngCreateSemaphore, EngDeleteSemaphore, EngReleaseSemaphore


EngAllocMem

PVOID EngAllocMem(

ULONG fl,

ULONG cj,

ULONG tag

);

EngAllocMem allocates a block of memory from the system’s paged pool, and inserts a caller-supplied tag before the allocation.

Parameters

fl
Specifies how to allocate memory. The FL_ZERO_MEMORY flag requests that the allocated memory’s contents be zero-initialized; if this flag is not set, the memory is returned uninitialized.
cj
Specifies the number of bytes to allocate.
tag
Is a four-byte allocation tag that uniquely identifies the driver doing the memory allocation.

Return Value

EngAllocMem returns a pointer to the allocated memory if the system has enough memory to satisfy the request; otherwise, it returns a null pointer.

Comments

The tag string should be specified in byte reversed order. The first letter must be ‘D’; the other three should be indicative of the driver name. For example, the tag string ‘ 3sD’ appears as ‘Ds3 ‘ if pool is dumped. The tag appears in any crash dump of the system that occurs.

See Also

EngFreeMem


EngAllocUserMem

PVOID EngAllocUserMem(

ULONG cj,

ULONG tag

);

EngAllocUserMem allocates a block of memory from the address space of the current process, and inserts a caller-supplied tag before the allocation.

Parameters

cj
Specifies the number of bytes to allocate.
tag
A 4-byte allocation tag that uniquely identifies the driver doing the memory allocation.

Return Value

EngAllocUserMem returns a pointer to the allocated memory if there is enough user-mode memory to satisfy the request; otherwise, it returns a null pointer.

Comments

A Windows NT process has 4 GB of virtual address space. The upper 2 GB is system memory that is accessible only to kernel-mode threads; this space is identical across all processes. The lower 2 GB is user memory that is accessible to both user-mode and kernel-mode threads; this space is unique to its process. The memory allocated by EngAllocUserMem is allocated from the unique 2 GB of user memory, and is thus accessible only when the graphics driver is called in the context of the thread in which the memory was allocated. Graphics drivers always execute in the context of the caller; that is, graphics drivers cannot switch process contexts.

EngAllocUserMem is particularly useful to a printer driver with large bitmaps that will only be used by the current process. Rather than allocating from the system pool, this driver can instead allocate space from the current process’s address space. Drivers need to exercise care with memory allocated by EngAllocUserMem, as it is possible for the application to step on this memory. EngAllocUserMem should only be used to allocate relatively large chunks of memory, as each allocation takes at least 64K of virtual address space. Sensitive data structures should never be allocated using this function. Also, user memory allocated by this function cannot be passed to EngWritePrinter by the printer driver.

The tag string should be specified in byte reversed order. The first letter must be ‘D’; the other three should be indicative of the driver name. For example, the tag string ‘ 3sD’ appears as ‘Ds3 ‘ if pool is dumped. The tag appears in any crash dump of the system that occurs.

See Also

EngFreeUserMem


EngAssociateSurface

BOOL EngAssociateSurface(

IN HSURF hsurf,

IN HDEV hdev,

IN FLONG flHooks

);

EngAssociateSurface marks a given surface as belonging to a specified device.

Parameters

hsurf
Identifies the surface or bitmap to be associated with hdev.
hdev
Identifies the device with which the surface is to be associated. This is the handle that was passed to DrvCompletePDEV.
flHooks
Specifies a set of flags that control the functions the driver can hook. The flags that can be set are as follows:

Flag

Function to be hooked

HOOK_BITBLT

DrvBitBlt

HOOK_STRETCHBLT

DrvStretchBlt

HOOK_TEXTOUT

DrvTextOut

HOOK_PAINT

DrvPaint

HOOK_STROKEPATH

DrvStrokePath

HOOK_FILLPATH

DrvFillPath

HOOK_STROKEANDFILLPATH

DrvStrokeAndFillPath

HOOK_LINETO

DrvLineTo

HOOK_COPYBITS

DrvCopyBits

HOOK_SYNCHRONIZE

DrvSynchronize

HOOK_SYNCHRONIZEACCESS

See Comments below.

When a standard format bitmap is being drawn on, GDI automatically manages the operation. Setting bits in this parameter, except for HOOK_SYNCHRONIZEACCESS, causes particular output functions to be sent to the driver instead. This is referred to as hooking.

EngAssociateSurface can be used by printer drivers to implement “rules” or device fonts, or by frame buffer display drivers to make use of special blt hardware.

Return Value

The return value is TRUE if the function is successful. Otherwise, the driver should send the information to the GDI function it is implementing, and return GDI’s return value.

Comments

If the specified surface is a standard format bitmap, the driver can specify which output functions to the surface it will handle.

When the surface is associated, it assumes the default palette and style steps of the PDEV. A surface must be associated before it is returned by DrvEnableSurface.

The HOOK_SYNCHRONIZEACCESS flag can be set by the driver to ensure that all drawing calls to the surface are synchronized, or single-threaded.

By default, when a driver supports device bitmaps by implementing DrvCreateDeviceBitmap/DrvDeleteDeviceBitmap, GDI does not automatically synchronize drawing calls to the device bitmap and to the primary surface. For example, GDI can call the driver's DrvBitBlt function to draw to a device bitmap, while another thread is drawing to the primary surface by executing the driver's implementation of DrvTextOut. The driver can even be called to draw to multiple device bitmaps at the same time.

A display driver can use the HOOK_SYNCHRONIZEACCESS value to have GDI synchronize access for drawing on this surface while drawing occurs on the main surface; or on any other surface that the driver supports. Such single-threading can be necessary if:

  • The driver creates device-managed bitmaps in the same memory block that the primary display surface is in, and;

  • The driver can’t have two calls accessing the memory block, or its hardware drawing accelerators, at the same time.

Most hardware must be used synchronously and such synchronization is managed by GDI when the driver sets the HOOK_SYNCHRONIZEACCESS flag. This flag causes GDI to synchronize all accesses to that device bitmap with all accesses to the primary surface, or to any other synchronized device bitmap. That is, if the driver sets this flag for all device bitmaps, it will be guaranteed to be single-threaded for any drawing operation.

Note GDI only allows one drawing thread to access a surface at a time. If a device driver supports device format bitmaps, then many threads might be in the driver at the same time drawing on different surfaces. The driver does not, by default, single-thread access all device surfaces, but if the HOOK_SYNCHRONIZEACCESS bit is specified in EngAssociateSurface for all a driver's device bitmaps, then GDI will single-thread all drawing calls to the driver.

See Also

DrvBitBlt, DrvCompletePDEV, DrvCopyBits, DrvCreateDeviceBitmap, DrvDeleteDeviceBitmap, DrvEnableSurface, DrvFillPath, DrvLineTo, DrvPaint, DrvStretchBlt, DrvStrokeAndFillPath, DrvStrokePath, DrvSynchronize, DrvTextOut


EngBitBlt

BOOL EngBitBlt(

IN SURFOBJ *psoDst,

IN SURFOBJ *psoSrc,

IN SURFOBJ *psoMask,

IN CLIPOBJ *pco,

IN XLATEOBJ *pxlo,

IN RECTL *prclDst,

IN POINTL *pptlSrc,

IN POINTL *pptlMask,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrush,

IN ROP4 rop4

);

EngBitBlt provides general bit-block transfer capabilities either between device-managed surfaces, or between a device-managed surface and a GDI-managed standard format bitmap.

Parameters

psoDst
Points to the SURFOBJ that identifies the surface on which to draw.
psoSrc
If the rop4 requires it, points to a SURFOBJ that defines the source for the bit-block transfer operation.
psoMask
Points to a SURFOBJ that defines a surface to be used as a mask. The mask is defined as a bitmap with 1 bit per pixel. Typically, a mask limits the area that is to be modified in the destination surface. Masking is selected by a rop4 with the value 0xAACC. The destination surface is unaffected when the mask is zero.

The mask is large enough to cover the destination rectangle.

If the value of this parameter is null and a mask is required by the rop4, then the implicit mask in the brush is used. If a mask is required, then psoMask overrides the implicit mask in the brush.

pco
Points to a CLIPOBJ structure. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles. This enumeration limits the area of the destination that will be modified. Whenever possible, GDI simplifies the clipping involved; for example, this function is never called with a single clipping rectangle. GDI clips the destination rectangle before calling this function, making additional clipping unnecessary.
pxlo
Points to a XLATEOBJ that tells how color indices should be translated between the source and target surfaces.
prclDst
Points to a RECTL structure in the coordinate system of the destination surface that defines the area to be modified. The rectangle is defined by two points; upper left and lower right. The lower and right edges of this rectangle are not part of the bit-block transfer, meaning the rectangle is lower right exclusive.

EngBitBlt is never called with an empty destination rectangle. The two points that define the rectangle are always well ordered.

pptlSrc
Points to a POINTL structure that defines the upper left corner of the source rectangle, if a source exists. If there is no source, the driver should ignore this parameter.
pptlMask
Points to a POINTL structure that defines which pixel in the mask corresponds to the upper left corner of the destination rectangle. If no mask is specified in psoMask the driver should ignore this parameter.
pbo
Points to the brush object to be used to define the pattern for the bit-block transfer. GDI’s BRUSHOBJ_pvGetRbrush service routine retrieves the device’s realization of the brush. The driver can ignore this parameter if the rop4 parameter does not require a pattern.
pptlBrush
Points to a POINTL structure that defines the origin of the brush in the destination surface. The upper left pixel of the brush is aligned at this point and the brush repeats according to its dimensions. Ignore this parameter if the rop4 parameter does not require a pattern.
rop4
Represents a raster operation that defines how the mask, pattern, source, and destination pixels are combined to write an output pixel to the destination surface.

This is a quaternary raster operation, which is a natural extension of the usual ternary Rop3 operation. A Rop4 has 16 relevant bits, which are similar to the 8 defining bits of a Rop3. (The other, redundant bits of the Rop3 are ignored.) The simplest way to implement a Rop4 is to consider its 2 bytes separately. The lower byte specifies a Rop3 that should be computed wherever the mask is 1. The high byte specifies a Rop3 that can be computed and applied wherever the mask is 0.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

If a device’s surface is organized as a standard-format bitmap, the driver can request that GDI perform the bit-block transfer by calling EngBitBlt. A driver might do this if it has special hardware to handle simple transfers quickly, but doesn’t want to handle calls with complicated transfers.

GDI services reduce clipping to a series of clip rectangles. A translation vector assists in color index translation for palettes.

If the source surface is palette-managed, its colors are represented by indices into an array of RGB colors. The XLATEOBJ can be queried for a translate vector that allows the driver to quickly translate any source index into a color index for the destination.

The situation is more complicated when, for example, the source is RGB, but the destination is palette-managed. In this case, the closest match for each source RGB value must be found in the destination palette. The XLATEOBJ contains the address of a service routine to perform this operation.

Optionally, the driver can match colors when the target palette is the default device palette.

See the Win32 SDK for more information on raster operations.

See Also

BRUSHOBJ, BRUSHOBJ_pvGetRbrush, CLIPOBJ, DrvBitBlt, EngAssociateSurface, SURFOBJ, XLATEOBJ


EngCheckAbort

BOOL EngCheckAbort(

IN SURFOBJ *pso

);

EngCheckAbort enables a printer driver to check whether a print job has been terminated.

Parameters

pso
Points to a SURFOBJ that describes the print job. This object is passed to DrvSendPage or DrvStartPage.

Return Value

The return value is TRUE if the specified print job has been aborted. FALSE is returned if pso points to an invalid SURFOBJ, or if the print job has not been aborted.

Comments

The printer driver should call EngCheckAbort from any operation that takes more than five seconds.

If the specified print job has been aborted, the driver terminates its current operation and returns to GDI.

See Also

DrvSendPage, DrvStartPage, SURFOBJ


EngComputeGlyphSet

FD_GLYPHSET * EngComputeGlyphSet(

INT nCodePage,

INT nFirstChar,

INT cChars

);

EngComputeGlyphSet computes the glyph set supported on a device.

Parameters

nCodePage
Specifies the code page supported.
nFirstChar
Character code of the first supported ANSI character.
cChars
Number of ANSI characters supported.

Return Value

If the glyph set is computed successfully, a pointer to an FD_GLYPHSET structure is returned. null is returned if an error occurred.

Comments

A driver can use EngComputeGlyphSet to compute the glyph set for a font that contains only glyphs in the code page described by nCodePage.

The driver must call EngFreeMem to free memory when it is done using the FD_GLYPHSET structure returned by EngComputeGlyphSet.

See Also

EngFreeMem


EngCopyBits

BOOL EngCopyBits(

IN SURFOBJ *psoDst,

OUT SURFOBJ *psoSrc,

IN CLIPOBJ *pco,

IN XLATEOBJ *pxlo,

IN RECTL *prclDst,

IN POINTL *pptlSrc

);

EngCopyBits translates between device-managed raster surfaces and GDI standard-format bitmaps.

Standard-format bitmaps are single-plane, packed-pixel format. Each scan line is aligned on a 4-byte boundary. These bitmaps have 1, 4, 8, 16, 24, 32, or 64 bits per pixel.

GDI calls this function from its simulations.

Parameters

psoDst
Points to a SURFOBJ structure that describes the destination surface for the copy operation.
psoSrc
Points to a SURFOBJ structure that describes the source surface for the copy operation.
pco
Points to a CLIPOBJ structure that restricts the area of the destination surface that will be affected. This parameter can be null.
pxlo
Points to a XLATEOBJ structure that defines the translation of color indices between the source and target surfaces.
prclDst
Points to a RECTL structure that defines the area in the coordinate system of the destination surface that will be modified. The rectangle is lower-right exclusive, meaning the lower and right edges of this rectangle are not part of the copy.
pptlSrc
Points to a POINTL structure that defines the upper left corner of the source rectangle.

Return Value

The return value is TRUE if the function is successful.

Comments

EngCopyBits should not be called with an empty destination rectangle, and the two points of the destination rectangle must be well-ordered.

See Also

CLIPOBJ, SURFOBJ, XLATEOBJ


EngCreateBitmap

HBITMAP EngCreateBitmap(

IN SIZEL sizl,

IN LONG lWidth,

IN ULONG iFormat,

IN FLONG fl,

IN PVOID pvBits

);

EngCreateBitmap requests that GDI create and manage a bitmap. Storage for the bitmap can optionally be provided by the driver.

Parameters

sizl
Specifies the size, in pixels, of the bitmap to be created. If pvBits is nonnull, this value should represent all pixels visible on the device, allowing the device to keep off-screen memory.
lWidth
Specifies the allocation width of the bitmap, which is the number of bytes that must be added to a pointer to move down one scan line.
iFormat
Specifies the format of the bitmap in terms of the number of bits of color information per pixel that are required. This parameter can be one of the following values:

Value

Meaning

BMF_1BPP

Monochrome.

BMF_4BPP

4 bits per pixel.

BMF_8BPP

8 bits per pixel.

BMF_16BPP

16 bits per pixel.

BMF_24BPP

24 bits per pixel.

BMF_32BPP

32 bits per pixel.

BMF_4RLE

4 bits per pixel; run length encoded.

BMF_8RLE

8 bits per pixel; run length encoded.

fl
Bitmask that specifies properties about the bitmap to be created. This parameter can be zero, or any combination of the following values:

Value

Meaning

BMF_TOPDOWN

The first scan line represents the top of the bitmap. Note that standard-format bitmaps have the first scan line at the bottom by default.

BMF_NOZEROINIT

GDI will not zero-initialize the bitmap when allocating it. This flag is checked only when pvBits is null.

BMF_USERMEM

GDI will allocate the memory for the bitmap from user memory. By default, the memory is allocated from the kernel’s address space. This flag should be specified only when the bitmap being created will not be used by other processes. User memory cannot be passed to EngWritePrinter by the printer driver.

pvBits
Points to the first scan line of the bitmap that is to be created. If this parameter is null, GDI allocates the storage space for the pixels of the bitmap. If pvBits is nonnull, it is a pointer to the buffer for the bitmap.

Return Value

If the function completes successfully, the return value is a handle that identifies the created bitmap. Otherwise, it is zero, and an error code is logged.

Comments

The driver should associate the created bitmap as a surface by calling EngAssociateSurface before returning from DrvEnableSurface.

The bitmap should be deleted by using EngDeleteSurface when it is no longer needed.

Frame buffer display drivers should use the pvBits parameter, allowing GDI to do most drawing directly to the display.

See Also

DrvCreateDeviceBitmap, EngAllocUserMem


EngCreateClip

CLIPOBJ *EngCreateClip(VOID)

EngCreateClip creates a CLIPOBJ structure that the driver uses in callbacks. This CLIPOBJ allows GDI to directly access banked frame buffers. The structure must be initialized by the driver so that CLIPOBJ.iDComplexity is set to DC_TRIVIAL or DC_RECT. If CLIPOBJ.iDComplexity is set to DC_RECT, the driver can set rclBounds to be the extent of the frame buffer bank. The driver must delete this CLIPOBJ using EngDeleteClip.

Return Value

The return value is a pointer to the newly-created CLIPOBJ if the function succeeds. Otherwise, it is null.


EngCreateDeviceBitmap

HBITMAP EngCreateDeviceBitmap(

IN DHSURF dhsurf,

IN SIZEL sizl,

IN ULONG iFormatCompat

);

EngCreateDeviceBitmap requests GDI to create a handle for a device bitmap.

Parameters

dhsurf
Identifies the device bitmap being created.
sizl
Specifies the size, in pixels, of the bitmap to be created.
iFormatCompat
Specifies the compatible engine format of the device surface being created. This is used by GDI if a temporary buffer is needed to simulate a complicated drawing call. The allowable values for iFormatCompat are BMF_1BPP, BMF_4BPP, BMF_8BPP, BMF_16BPP, BMF_24BPP, and BMF_32BPP.

Return Value

The return value is a handle that identifies the bitmap if the function is successful. Otherwise, it is zero, and an error code is logged.

Comments

The storage space for the bitmap can optionally be provided by the driver. The surface should be associated by using EngAssociateSurface. The bitmap should be deleted by calling EngDeleteSurface when it is no longer needed.

See Also

EngCreateBitmap


EngCreateDeviceSurface

HSURF EngCreateDeviceSurface(

DHSURF dhsurf,

SIZEL sizl,

ULONG iFormatCompat

);

EngCreateDeviceSurface creates and returns a handle for a device surface that the driver will manage.

Parameters

dhsurf
Identifies the surface to be managed by the device. This handle is passed to the driver when a SURFOBJ is passed for input or output.
sizl
Specifies the size, in pixels, of the surface to be created.
iFormatCompat
Specifies the compatible engine format of the device surface being created. This is used by GDI if a temporary buffer is needed to simulate a complicated drawing call.

Return Value

The return value is a handle that identifies the surface if the function is successful. Otherwise, it is zero, and an error code is logged.

Comments

The storage space for the surface can optionally be provided by the driver. The surface should be associated by using EngAssociateSurface. The bitmap should be deleted when it is no longer needed by using EngDeleteSurface.

See Also

SURFOBJ


EngCreateDriverObj

HDRVOBJ EngCreateDriverObj(

PVOID pvObj,

FREEOBJPROC pFreeObjProc,

HDEV hdev

);

EngCreateDriverObj creates a DRIVEROBJ structure. This structure is used to track a driver-managed resource that must be released if the resource-allocating process terminates without first cleaning it up.

Parameters

pvObj
Points to the driver resource that will be tracked by the DRIVEROBJ. The resource is associated with the current client process.
pFreeObjProc
Points to a driver-supplied callback function that frees the resource pointed to by pvObj. The callback function should be defined as follows, where pDriverObj points to the DRIVEROBJ structure:
BOOL CALLBACK DrvobjFreeObjProc(DRIVEROBJ *pDriverObj);

hdev
The GDI handle of the physical device associated with the object.

Return Value

The return value is a handle that identifies the newly-created DRIVEROBJ structure if the function is successful. Otherwise, it is zero.

Comments

The driver can explicitly delete the DRIVEROBJ structure by calling EngDeleteDriverObj. Otherwise, the engine frees the resource by calling the function pointed to by pFreeObjProc when the process that created the DRIVEROBJ terminates.

See Also

DRIVEROBJ


EngCreatePalette

HPALETTE EngCreatePalette(

IN ULONG iMode,

IN ULONG cColors,

IN PULONG *pulColors,

IN FLONG flRed,

IN FLONG flGreen,

IN FLONG flBlue

);

EngCreatePalette sends a request to GDI to create a palette. The driver associates the new palette with a device by returning a pointer to the palette in the DEVINFO structure.

Parameters

iMode
Specifies how the palette will be defined. This parameter can be one of the following values:

Value

Meaning

PAL_INDEXED

An array of RGB colors is provided with cColors and pulColors.

PAL_BITFIELDS

The palette is defined by the flRed, flGreen, and flBlue parameters.

PAL_RGB

The device accepts RGB colors directly, with R as the least significant byte.

PAL_BGR

The device accepts RGB colors directly, with B as the least significant byte.

cColors
If the iMode parameter is PAL_INDEXED, cColors specifies the number of colors provided in the array pointed to by pulColors. Otherwise, this parameter should be zero.
pulColors
Points to an array of ULONG values if iMode is PAL_INDEXED. The low-order 3 bytes of each ULONG define the RGB colors in the palette.
flRed, flGreen, and flBlue
If the iMode parameter is PAL_BITFIELDS, these parameters are masks that show which bits correspond to red, green, and blue. Each mask must consist of contiguous bits and should not overlap other masks. All combinations of bitfields are supported by GDI.

Return Value

The return value is a handle to the new palette if the function is successful. Otherwise, it is zero, and an error code is logged.

See Also

DEVINFO, DrvSetPalette, EngDeletePalette


EngCreatePath

PATHOBJ *EngCreatePath(VOID)

EngCreatePath allocates a path for the driver’s temporary use. The driver should delete this path before returning to GDI from its current drawing call.

Return Value

The return value is a pointer to a PATHOBJ structure if the function is successful. Otherwise, it is null, and an error code is logged.

Comments

Functions that create and modify paths are provided to assist vector devices in filling complex areas. In response to a call to DrvPaint, a vector driver can create a path, fill it with lines to describe its brush, and pass the path to PATHOBJ_bEnumClipLines for clipping against the complex region.

A PATHOBJ is a locked object, and thus should not be locked for a long time by the driver.

If the driver uses EngCreatePath to create a PATHOBJ, it should be deleted by using EngDeletePath as soon as the driver finishes with it.

The returned PATHOBJ is used in calls to PATHOBJ_bMoveTo, PATHOBJ_bPolyLineTo, PATHOBJ_vEnumStartClipLines, and PATHOBJ_bEnumClipLines

See Also

DrvPaint, PATHOBJ, PATHOBJ_bEnumClipLines


EngCreateSemaphore

HSEMAPHORE EngCreateSemaphore(VOID)

EngCreateSemaphore creates a semaphore object.

Return Value

If the function succeeds, the return value is a handle to the semaphore object. A null pointer is returned if the function fails.

Comments

Graphics drivers can create and use a semaphore object for resource synchronization. For example:

  • The S3 display driver uses a semaphore when an asynchronous pointer requires access to the CRTC registers, because these registers are shared by both the asynchronous hardware pointers and the synchronous activities of the device.

  • Multiple printer drivers sharing global data, such as font data on a print server, need to synchronize access to this data.

See Also

EngAcquireSemaphore, EngDeleteSemaphore, EngReleaseSemaphore


EngCreateWnd

WNDOBJ *EngCreateWnd(

SURFOBJ *pso,

HWND hwnd,

WNDOBJCHANGEPROC pfn,

FLONG fl,

int iPixelFormat

);

EngCreateWnd creates a WNDOBJ for the window referenced by hwnd.

Parameters

pso
Identifies the device surface.
hwnd
Handle that identifies a window created by an application’s call to CreateWindow or an equivalent function.
pfn
Points to a driver-defined callback function that GDI calls for changes to the window identified by hwnd. In the context of this function, the WNDOBJ values will be unchanged. The prototype for this callback function is defined in the header file as follows:
VOID (CALLBACK * WNDOBJCHANGEPROC)(WNDOBJ *pwo, FLONG fl);

The callback’s pwo parameter points to the window object that is currently changing and that contains the new size and position of the window. This parameter is null if fl is WOC_CHANGED.

The callback’s fl parameter can be one of the values listed in the following table:

Flag

Meaning

WOC_RGN_CLIENT_DELTA

The window object contains a delta client region. The delta region is valid for this call only.

WOC_RGN_CLIENT

The window object contains a new visible client region.

WOC_RGN_SURFACE_DELTA

The window object contains a delta surface region. The pvConsumer field of the WINOBJ contains 0. The delta region is valid for this call only.

WOC_RGN_SURFACE

The window object refers to a surface region created by GDI. The pvConsumer field of the WINOBJ contains 0.

WOC_CHANGED

Indicates that all WNDOBJ callbacks have been made for the current desktop update. GDI always notifies the driver at the end of a desktop update. Note that pwo is null here.

WOC_DELETE

The window object is deleted as a result of the deletion of the window.

fl
Bitmask that specifies the type of changes to track. This value must be consistent through all WNDOBJ requests made by the driver. This parameter can be one or more of the following bit field values:

Bit field

Meaning

WO_RGN_CLIENT_DELTA

GDI should track changes in the delta client region of the window object, and notify the driver when the window’s visible region changes. The region enumerated in the callback function is a non-empty delta area that is in the new region but not in the old region. The delta region is valid during the callback only.

WO_RGN_CLIENT

GDI should track changes in the client region of the window object, and notify the driver when the window’s visible client region changes. The region enumerated in the callback function is the new visible client area of the window.

WO_RGN_SURFACE_DELTA

GDI should track changes in the delta surface region of the window object, and notify the driver when the surface region changes. The region enumerated in the callback function is a non-empty delta area that is in the new surface region but not in the old surface region. The delta surface region is valid during the callback only.

WO_RGN_SURFACE

GDI should track changes in the surface region of the window object, and notify the driver when the surface region changes. The surface region is the display surface area excluding all visible client regions of the windows being tracked by the driver.

WO_RGN_UPDATE_ALL

GDI should notify the driver for all windows it tracks when any of its windows’ visible regions change. This flag must be used in conjunction with the WO_RGN_CLIENT flag.

iPixelFormat
Specifies the pixel format associated with the window object. The pixel format of a window object is fixed. This parameter can be zero if there is no associated pixel format.

Return Value

The return value is a pointer to a WNDOBJ if the function is successful. Otherwise, the return value is -1 if the same window is being tracked by the driver, or zero if the driver is not tracking the same window.

Comments

Because creating a window object involves locking window resources, EngCreateWnd should be called only in the context of the WNDOBJ_SETUP escape in DrvEscape.

EngCreateWnd supports window tracking by multiple drivers. Each driver is identified by a unique pfn function pointer. For example, a live video driver can track changes to live video windows while an OpenGL driver is tracking changes to OpenGL windows.

GDI will call the function pointed to by pfn with the most recent window state if a new WNDOBJ is created by DrvEscape. GDI will also notify the pfn function when a window described by a WNDOBJ is destroyed.

See Also

DrvEscape, WNDOBJ


EngDebugBreak

VOID APIENTRY EngDebugBreak(VOID)

EngDebugBreak causes a breakpoint in the current process to occur.

Comments

EngDebugBreak is useful for debugging drivers that are under development.

See Also

EngDebugPrint


EngDebugPrint

VOID EngDebugPrint(

PCHAR StandardPrefix,

PCHAR DebugMessage,

va_list ap

);

EngDebugPrint prints the specified debug message to the kernel debugger.

Parameters

StandardPrefix
Points to a string that is prepended to DebugMessage.
DebugMessage
Points to a string containing the debug message to be printed.
ap
Variable argument list.

Comments

EngDebugPrint is useful for debugging drivers that are under development. It prints StandardPrefix, followed by DebugMessage, to the kernel debugger.

The StandardPrefix parameter acts as a unique identifier of the driver executing the debug statement; therefore, the same string should be used for all calls to EngDebugPrint by a single driver.

The DebugMessage parameter is a variable argument ASCII C string; that is, it can contain both ordinary characters and C-style conversion specifications. The argument list contained in ap can have any number of variable arguments in it.

An example use of EngDebugPrint follows:

#define STANDARD_DEBUG_PREFIX     "S3: "
LONG bank;
LONG width;
...
EngDebugPrint(STANDARD_DEBUG_PREFIX, “Bank: %lx; Width: %li”, bank, width);

See Also

EngDebugBreak


EngDeleteClip

VOID EngDeleteClip (

CLIPOBJ *pco

);

EngDeleteClip deletes a CLIPOBJ structure allocated by EngCreateClip.

Parameters

pco
Points to the CLIPOBJ to delete.


EngDeleteDriverObj

BOOL EngDeleteDriverObj(

IN HDRVOBJ hdo,

IN BOOL bCallBack,

IN BOOL bLocked

);

EngDeleteDriverObj frees the handle used for tracking a driver-managed resource.

Parameters

hdo
Identifies the GDI handle that is to be deleted. This handle was obtained from EngCreateDriverObj.
bCallBack
Specifies whether or not the cleanup callback should be called. If TRUE, GDI will invoke the cleanup callback before removing the DRIVEROBJ from the handle manager. If the callback function returns failure, EngDeleteDriverObj will fail.
bLocked
Specifies whether the object was locked by the driver (through EngLockDriverObj) before EngDeleteDriverObj was called.

Return Value

The return value is TRUE if the function is successful and the handle is freed; it is FALSE if the handle has not been freed. If the pFreeObjProc driver function specified in EngCreateDriverObj returns FALSE, then EngDeleteDriverObj will fail and the handle won’t be freed. This could happen if the cleanup callback needs to lock another DRIVEROBJ (in order to free the current DRIVEROBJ) and fails because the other DRIVEROBJ is in use by another thread.

Comments

After the handle is freed, the associated driver resource is no longer tracked by GDI and the function pointed to by the pFreeObjProc parameter of EngCreateDriverObj will not be called upon process termination. It is the responsibility of the driver to ensure that the resource is freed.

Most drivers should be consistent in how objects are cleaned up at termination time. Consequently, they will pass TRUE for bCallback, indicating to GDI that it should call the driver’s cleanup function to free this driver resource. Passing FALSE prevents GDI from calling the driver’s cleanup function.

The bCallBack parameter indicates to GDI whether or not the callback function needs to be called. Most drivers pass in TRUE to ensure that objects are cleaned up consistently when an application terminates. Passing TRUE for bCallBack tells GDI to call the driver’s cleanup function back to free this driver resource. Passing FALSE prevents GDI from calling the cleanup function. If pFreeObjProc returns FALSE, EngDeleteDriverObj fails and the handle won’t be freed. For example, this could happen if the pFreeObjProc needed to lock down another DRIVEROBJ to free the current DRIVEROBJ and failed because it was being used by another thread. The pFreeObjProc should never fail at cleanup time, because no threads, other than the cleanup thread, are running, so locks of other objects won’t fail.

The bLocked parameter indicates to GDI that the object has already been locked once by the driver. Often, before an object is deleted, the driver might have locked it down to use first. This allows the driver to call GDI without first having to unlock the object, thus eliminating the possibility that another thread could enter the driver and lock it down before the handle is freed.

See Also

DRIVEROBJ, EngCreateDriverObj


EngDeletePalette

BOOL EngDeletePalette(

IN HPALETTE hpal

);

EngDeletePalette sends a request to GDI to delete the specified palette.

Parameters

hpal
Identifies the palette to be deleted. The handle is supplied by EngCreatePalette.

Return Value

The return value is TRUE if the function is successful; otherwise, it returns FALSE.


EngDeletePath

VOID EngDeletePath(

IN PATHOBJ *ppo

);

EngDeletePath deletes a path previously allocated by EngCreatePath.

Parameters

ppo
Points to the PATHOBJ to be deleted.


EngDeleteSemaphore

VOID EngDeleteSemaphore(

HSEMAPHORE hsem

);

EngDeleteSemaphore deletes a semaphore object from the system’s resource list.

Parameters

hsem
Handle of the semaphore to be deleted. The semaphore was created in EngCreateSemaphore.

See Also

EngAcquireSemaphore, EngCreateSemaphore, EngReleaseSemaphore


EngDeleteSurface

BOOL EngDeleteSurface(

IN HSURF hsurf

);

EngDeleteSurface deletes a given surface.

Parameters

hsurf
Identifies the surface to delete. This handle can be an HSURF or HBM.

Return Value

The return value is TRUE if the function is successful.


EngDeviceIoControl

DWORD EngDeviceIoControl(

HANDLE hDevice,

DWORD dwIoControlCode,

LPVOID lpInBuffer,

DWORD nInBufferSize,

LPVOID lpOutBuffer,

DWORD nOutBufferSize,

LPDWORD lpBytesReturned

);

EngDeviceIoControl sends a control code to the specified video miniport driver, causing the device to perform the specified operation.

Parameters

hDevice
Handle to the device that is to perform the operation.
dwIoControlCode
Specifies the control code for the operation. The I/O controls are listed and described in full in Video I/O Control Codes.
lpInBuffer
Pointer to a buffer containing data required to perform the operation. This parameter can be null if the control code specifies an operation that does not require input data.
nInBufferSize
Specifies the size, in bytes, of lpInBuffer.
lpOutBuffer
Pointer to a buffer in which the operation’s output data is returned. This parameter can be null if the control code specifies an operation that does not produce output data.
nOutBufferSize
Specifies the size, in bytes, of lpOutBuffer.
lpBytesReturned
Pointer to a long word that specifies the actual size, in bytes, of the data returned in lpOutBuffer.

Return Value

The return value is a 32-bit Win32 API-defined error code.

Comments

EngDeviceIoControl is used by a display driver to communicate I/O requests to its corresponding miniport driver. This function provides the only communication channel between a display and video miniport driver.


EngEnumForms

BOOL EngEnumForms(

HANDLE hPrinter,

DWORD dwLevel,

LPBYTE lpbForms,

DWORD cbBuf,

LPDWORD pcbNeeded,

LPDWORD pcbReturned

);

EngEnumForms enumerates the forms supported by the specified printer.

Parameters

hPrinter
PDEV handle of the printer for which the forms should be enumerated. This is the handle that is passed as the hDriver parameter of DrvEnablePDEV.
dwLevel
Specifies the version of the structure pointed to by lpbForms. This value must be 1, which indicates that the enumerated forms are to be returned in FORM_1_INFO structures.
lpbForms
Pointer to an array of bytes in which the enumerated FORM_INFO_1 structures are written.
cbBuf
Specifies the size, in bytes, of lpbForms.
pcbNeeded
Address of the value that specifies the number of bytes copied into lpbForm if the copy is completed successfully. If lpbForms is too small to contain all the enumerated forms’ data, this parameter specifies the number of bytes required.
pcbReturned
Address of the value that specifies the number of FORM_INFO_1 structures copied into lpbForms.

Return Value

EngEnumForms returns TRUE if all parameters are valid and the enumerated form data is successfully copied into lpbForms. Otherwise, it returns FALSE and an error message is logged. To get error information, call EngGetLastError.

Comments

A printer driver can call EngEnumForms to have GDI obtain the list of forms supported by a particular printer. The enumerated information is returned as an array of FORM_INFO_1 structures pointed to by lpbForms. If the array pointed to by lpbForms is not large enough to hold the enumerated data, the requisite array size is instead returned in pcbNeeded.

See Also

DrvEnablePDEV, EngGetLastError


EngEraseSurface

BOOL EngEraseSurface(

IN SURFOBJ *pso,

IN RECTL *prcl,

IN ULONG iColor

);

EngEraseSurface calls GDI to erase the surface. A given rectangle on the surface will be filled with the given color.

Parameters

pso
Points to the surface to erase.
prcl
Points to a rectangle defining which pixels to erase on the surface. This rectangle is exclusive of the bottom and right edges.
iColor
Specifies a color index. This is an index to the value that will be written into each pixel.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.


EngFillPath

BOOL EngFillPath(

IN SURFOBJ *pso,

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrushOrg,

IN MIX mix,

IN FLONG flOptions

);

EngFillPath is an optional entry point that fills a path. Whenever GDI fills a path on a device-managed surface, it can call this entry point depending on a comparison of the fill requirements and the following GCAPS bits: GCAPS_BEZIERS, GCAPS_ALTERNATEFILL, and GCAPS_WINDINGFILL.

The driver can return FALSE if the path or the clipping is too complex for the device to handle.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to draw.
ppo
Points to a PATHOBJ structure that defines the path to be filled. Use the PATHOBJ_Xxx service routines to enumerate the lines, Bezier curves, and other data that make up the path.
pco
Points to a CLIPOBJ structure. Use the CLIPOBJ_Xxx service routines to enumerate the clip region as a set of rectangles.
pbo
Points to a BRUSHOBJ structure that defines the pattern and colors with which to fill.
pptlBrushOrg
Points to a POINTL structure defining the brush origin to use to align the brush pattern on the device.
mix
Defines the foreground and background raster operations to use for the brush.
flOptions
Specifies the mode to use when filling the path. This value should be FP_WINDINGMODE or FP_ALTERNATEMODE. All other flags should be ignored.

Return Value

The return value is TRUE if GDI is able to fill the path. Otherwise, it is FALSE, and an error code is not logged. If an error is encountered, the return value is FALSE, and an error code is logged.

Comments

The driver can return FALSE if the path or the clipping is too complex to be handled by the device.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

BRUSHOBJ, CLIPOBJ, PATHOBJ, SURFOBJ


EngFindImageProcAddress

PVOID EngFindImageProcAddress(

HANDLE hModule,

LPSTR lpProcName

);

EngFindImageProcAddress returns the address of a function within an executable module.

Parameters

hModule
Handle to the image in which the function can be found. This handle was obtained by calling EngLoadImage.
lpProcName
Pointer to the string that specifies the name of the function to be located.

Return Value

EngFindImageProcAddress returns the base address of the function’s executable code if it successfully locates the function. Otherwise, it returns null.

Comments

A driver must previously have loaded the image into kernel-mode through a call to EngLoadImage.

The function identified by lpProcName must be exported by the loaded module. This is accomplished by using the EXPORTS key in the module’s .DEF file.


EngFindResource

PVOID EngFindResource(

HANDLE h,

int iName,

int iType,

PULONG pulSize

);

EngFindResource determines the location of a resource in a module.

Parameters

h
Handle to the module that contains the resource. This handle is obtained from EngLoadModule.
iName
Integer identifier representing the name of the resource being looked up.
iType
Integer identifier representing the type of the resource being looked up.
pulSize
Pointer to a ULONG in which the resource’s size, in bytes, is returned.

Return Value

The return value is a pointer to the address of the specified resource. null is returned if an error occurs.

Comments

The size of a successfully located resource is returned in pulSize.

See Also

EngLoadModule, EngMapModule


EngFreeMem

VOID EngFreeMem(

PVOID pv

);

EngFreeMem deallocates a block of system memory.

Parameters

pv
Pointer to the block of paged pool memory being deallocated.

Comments

This routine releases memory allocated by EngAllocMem. The memory block must not be accessed after it is freed.


EngFreeModule

VOID EngFreeModule(

HANDLE h

);

EngFreeModule unmaps a file from system memory.

Parameters

h
Handle that identifies the memory-mapped file to be freed. This handle was obtained from EngLoadModule.

See Also

EngLoadModule, EngMapModule


EngFreeUserMem

VOID EngFreeUserMem(

PVOID pv

);

EngFreeUserMem deallocates a block of user memory.

Parameters

pv
Pointer to the block of user memory being deallocated.

Comments

This routine releases memory allocated by EngAllocUserMem. The memory block must not be accessed after it is freed.


EngGetCurrentCodePage

VOID EngGetCurrentCodePage(

PUSHORT OemCodePage,

PUSHORT AnsiCodePage

);

EngGetCurrentCodePage returns the system’s default OEM and ANSI code pages.

Parameters

OemCodePage
Pointer to a USHORT in which the system’s default OEM code page is returned.
AnsiCodePage
Pointer to a USHORT in which the system’s default ANSI code page is returned.

Comments

EngGetCurrentCodePage returns the default code pages that are used by the system to translate from ANSI to Unicode. These values are set at boot time according to locale settings.

See Also

STROBJ_dwGetCodePage


EngGetDriverName

LPWSTR EngGetDriverName(

HDEV hdev

);

EngGetDriverName returns the name of the driver’s DLL.

Parameters

hdev
Handle that identifies the installed physical device whose driver’s name is being queried.

Return Value

EngGetDriverName returns a pointer to the null-terminated string buffer in which the name of the driver’s DLL is specified. The system obtains and stores the driver’s name from the DRIVER_INFO_2 structure when the driver is first installed through the Win32 AddPrinterDriver routine.

See Also

EngGetPrinterDataFileName


EngGetFileChangeTime

BOOL EngGetFileChangeTime(

HANDLE h,

LARGE_INTEGER *pChangeTime

);

EngGetFileChangeTime returns a file’s last write time.

Parameters

h
Handle that identifies the file whose change time is to be queried.
pChangeTime
Pointer to a LARGE_INTEGER in which the change time is returned.

Return Value

EngGetFileChangeTime returns TRUE if it succeeds in returning the timestamp; otherwise it returns FALSE.

Comments

The time returned is specified in system-time format. Absolute system time is the number of 100-nanosecond intervals since the start of 1601.


EngGetForm

BOOL EngGetForm(

HANDLE hPrinter,

LPWSTR pFormName,

DWORD dwLevel,

LPBYTE lpbForm,

DWORD cbBuf,

LPDWORD pcbNeeded

);

EngGetForm gets the FORM_INFO_1 details for the specified form.

Parameters

hPrinter
Handle that identifies the printer for which the form is being specified.
pFormName
Pointer to a string that specifies the name of the form.
dwLevel
Specifies the version of the form structure to which lpbForm points. This value must be 1, which indicates that the form information will be returned in a FORM_INFO_1 structure.
lpbForm
Pointer to an array of bytes in which the initialized FORM_INFO_1 structure will be written.
cbBuf
Specifies the size, in bytes, of lpbForm.
pcbNeeded
Points to a value that specifies the number of bytes copied into lpbForm if the function succeeds. The value is the number of bytes required to perform the copy if cbBuf is too small.

Return Value

EngGetForm returns TRUE if the form structure is successfully copied into lpbForm. Otherwise, it logs an error message and returns FALSE. To get the error information, call EngGetLastError.

Comments

EngGetForm returns a FORM_INFO_1 structure containing the form data associated with pFormName. The written data and its size are returned to the caller via lpbForm and pcbNeeded, respectively. If the array pointed to by lpbForm is not large enough to hold the form data, the requisite array size is instead returned in pcbNeeded.

To get a list of all supported forms, the printer driver should call EngEnumForms.

See Also

EngGetLastError


EngGetLastError

ULONG EngGetLastError(VOID)

EngGetLastError returns the last error code logged by GDI for the calling thread.

Return Value

The return value is the last error code set by EngSetLastError.

Comments

EngGetLastError facilitates communication of GDI and/or driver error conditions to an application.

See Also

EngSetLastError


EngGetPrinter

BOOL EngGetPrinter(

HANDLE hPrinter,

DWORD dwLevel,

LPBYTE pPrinter,

DWORD cbBuf,

LPDWORD pcbNeeded

);

EngGetPrinter retrieves information about the specified printer.

Parameters

hPrinter
Identifies the printer for which data should be retrieved. This is the handle that is passed as the hDriver parameter of DrvEnablePDEV.
dwLevel
Specifies the version of the structure to which pPrinter points. This parameter can have any of the following values:

Value

Structure Returned Via pPrinter

1

PRINTER_INFO_1

2

PRINTER_INFO_2

3

PRINTER_INFO_3

4

PRINTER_INFO_4

5

PRINTER_INFO_5

pPrinter
Pointer to the data space in which the printer information structure, identified by dwLevel, is loaded.
cbBuf
Specifies the size, in bytes, of the data space pointed to by pPrinter.
pcbNeeded
Return address in which the number of bytes copied is returned if the function succeeds, or in which the number of required bytes is returned if cbBuf is too small.

Return Value

EngGetPrinter returns TRUE upon success; otherwise, it logs an error and returns FALSE. To get error information, call EngGetLastError.

See Also

DrvEnablePDEV


EngGetPrinterData

DWORD EngGetPrinterData(

HANDLE hPrinter,

LPWSTR pValueName,

LPDWORD pType,

LPBYTE lpbData,

DWORD cbBuf,

LPDWord pcbNeeded

);

EngGetPrinterData retrieves configuration data for the specified printer.

Parameters

hPrinter
Handle that identifies the printer for which configuration data should be retrieved. This is the handle that is passed as the hDriver parameter of DrvEnablePDEV.
pValueName
Pointer to a null-terminated string that specifies the type of data to be retrieved.
pType
Points to a variable that receives the data type stored by EngSetPrinterData. This parameter can be null.
lpbData
Pointer to an array of bytes in which the configuration data will be written.
cbBuf
Specifies the size, in bytes, of lpbData.
pcbNeeded
Address that receives the number of bytes copied into lpbData if the function succeeds. This parameter receives the number of bytes required if cbBuf is too small.

Return Value

EngGetPrinterData returns the last logged error message.

See Also

DrvEnablePDEV, EngSetPrinterData


EngGetPrinterDataFileName

LPWSTR EngGetPrinterDataFileName(

HDEV hdev

);

EngGetPrinterDataFileName retrieves the string name of the printer’s data file.

Parameters

hdev
Handle that identifies the installed physical printing device whose data file name is being queried.

Return Value

EngGetPrinterDataFileName returns a pointer to the null-terminated string buffer in which the name of the printer’s data file is specified. The system obtains and stores the printer’s data file name from the DRIVER_INFO_2 structure when the driver is first installed through the Win32 AddPrinterDriver routine.

See Also

EngGetDriverName


EngGetProcessHandle

HANDLE EngGetProcessHandle(VOID)

EngGetProcessHandle retrieves a handle to the current client process.

Return Value

If the function succeeds, the return value is a handle that identifies the current client process. Otherwise, it is zero.

Comments

EngGetProcessHandle is intended to aid drivers in communicating with client processes efficiently. The returned handle allows a driver to access the resources of the client process.


EngGetType1FontList

BOOL EngGetType1FontList(

HDEV hdev,

TYPE1_FONT *pType1Buffer,

ULONG cjType1Buffer,

PULONG pulLocalFonts,

PULONG pulRemoteFonts,

LARGE_INTEGER *pLastModified

);

EngGetType1FontList retrieves a list of PostScript Type 1 fonts that are installed both locally and remotely.

Parameters

hdev
Handle to the device for which Type 1 font information will be returned.
pType1Buffer
Pointer to an array of TYPE1_FONT structures in which to return the Type 1 font list. This parameter can be null.
cjType1Buffer
Specifies the size, in bytes, of pType1Buffer.
pulLocalFonts
Address in which the number of Type 1 fonts on the local system will be written.
pulRemoteFonts
Address in which the number of Type 1 fonts on the remote system will be written.
pLastModified
Address in which the time stamp corresponding to the last time a Type 1 font was added or removed from the local system will be written.

Return Value

EngGetType1FontList returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

PostScript printer drivers can call EngGetType1FontList to obtain a list of Type 1 fonts available to them. These fonts can then be accessed through the handles returned in the TYPE1_FONT structure.

If pType1Buffer is null, EngGetType1FontList returns only the number of local and remote Type 1 fonts, as well as the time stamp corresponding to the last time a Type 1 font was added or removed locally from the system.

See Also

TYPE1_FONT


EngLineTo

BOOL EngLineTo(

SURFOBJ *pso,

CLIPOBJ *pco,

BRUSHOBJ *pbo,

LONG x1,

LONG y1,

LONG x2,

LONG y2,

RECTL *prclBounds,

MIX mix

);

EngLineTo draws a single solid integer-only cosmetic line.

Parameters

pso
Pointer to a SURFOBJ structure that describes the surface on which to draw.
pco
Pointer to a CLIPOBJ structure that defines the clip region in which the rendering must be done. No pixels can be affected outside this clip region.
pbo
Pointer to a BRUSHOBJ structure that specifies the brush to use when drawing the line.
x1, y1
Specify the integer x- and y-coodinates of the line’s beginning point.
x2, y2
Specify the integer x- and y-coordinates of the line’s end point.
prclBounds
Pointer to an integer rectangle that bounds the unclipped line. Drivers that support hardware line drawing can use this rectangle to quickly determine whether the line fits in a coordinate space small enough to be rendered by the hardware.
mix
Defines how the incoming pattern should be mixed with the data already on the device surface. The low-order byte defines the raster operation. For more information about raster operation codes, see the Win32 SDK.

Return Value

EngLineTo returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

The driver that has hooked DrvLineTo can call EngLineTo when the rendering surface is a DIB.

See Also

BRUSHOBJ, CLIPOBJ, DrvLineTo, SURFOBJ


EngLoadImage

HANDLE EngLoadImage(

LPWSTR pwszDriver

);

EngLoadImage loads the specified executable image into kernel-mode memory.

Parameters

pwszDriver
Pointer to a null-terminated string that names the file containing the executable image to be loaded.

Return Value

If EngLoadImage succeeds, the return value is a handle to the image that was loaded. Otherwise, this function returns null.

Comments

A driver can use EngLoadImage to map an executable image into kernel-mode memory. For example, a printer driver can call EngLoadImage to load a minidriver.

EngLoadImage requires that the image file to be loaded have a .dll suffix. The driver must include this suffix in the pwszDriver string.

To execute a section of code within the loaded image, the driver should obtain the function address from EngFindImageProcAddress.

Drivers that need to load a module as data only should call EngLoadModule instead of this function.

See Also

EngUnloadImage


EngLoadModule

HANDLE EngLoadModule(

LPWSTR pwsz

);

EngLoadModule loads the specified executable module into system memory for reading.

Parameters

pwsz
Pointer to a null-terminated string that contains the name of the file to be loaded.

Return Value

If EngLoadModule succeeds, the return value is a handle to the module that was loaded. Otherwise, null is returned.

Comments

EngLoadModule loads a data file into system memory read-only permission. To access the loaded module, the driver should call EngMapModule with the handle returned by this function. To load a writeable module, the driver should call EngLoadModuleForWrite.

Drivers that need to load an image as executable code should call EngLoadImage instead of this function.

See Also

EngFreeModule


EngLoadModuleForWrite

HANDLE EngLoadModuleForWrite(

LPWSTR pwsz

ULONG cjSizeOfModule

);

EngLoadModuleForWrite loads the specified executable module into system memory for writing.

Parameters

pwsz
Pointer to a null-terminated string that contains the name of the file to be loaded.
cjSizeOfModule
Specifies the size, in bytes, of the module to be loaded.

Return Value

If EngLoadModuleForWrite succeeds, the return value is a handle to the module that was loaded. Otherwise, null is returned.

Comments

EngLoadModuleForWrite loads a data file into system memory with write permission. To access the loaded module, the driver should call EngMapModule with the handle returned by this function.

EndLoadModuleForWrite loads the file into memory that is the same size as the file when cjSizeOfModule is zero. If cjSizeOfModule is greater than zero, GDI extends or truncates the file to be exactly cjSizeOfModule bytes in size before loading it. No assumptions should be made about the contents of memory that extend beyond the file when cjSizeOfModule is greater than the file’s original size.

Drivers that need to load an image as executable code should call EngLoadImage instead of this function.

See Also

EngLoadModule


EngLockDriverObj

DRIVEROBJ *EngLockDriverObj(

IN HDRVOBJ hdo

);

EngLockDriverObj creates an exclusive lock on this object for the calling thread. This function will fail if the handle is invalid, if the object is already locked by another thread, or if the handle isn’t owned by the calling process.

Parameters

hdo
Handle to the DRIVEROBJ structure to be locked by GDI.

Return Value

The return value is a pointer to a DRIVEROBJ structure if the function is successful. Otherwise, it is null.

EngLockDriverObj returns a DRIVEROBJ. If the operation fails, it returns . . .


EngLockSurface

SURFOBJ *EngLockSurface(

IN HSURF hsurf

);

EngLockSurface creates a user object for a given surface. This function gives drivers access to surfaces they create.

Parameters

hsurf
Handle that identifies the surface to lock.

Return Value

The return value is a pointer to a SURFOBJ structure if the function is successful. Otherwise, it is null.

Comments

The driver is responsible for unlocking the surface when it no longer needs it. Surfaces should be locked only for very short periods of time.

Use the EngUnlockSurface function to unlock the surface.


EngMapFontFile

BOOL EngMapFontFile(

ULONG iFile,

PULONG *ppjBuf,

ULONG *pcjBuf

);

EngMapFontFile returns a pointer to the base location of the font data in a font file.

Parameters

iFile
Identifies the font file that contains the font data. This identifier is the same one sent to DrvLoadFontFile by the graphics engine.
ppjBuf
Pointer to a pointer in which the base address of the font data is returned.
pcjBuf
Pointer to a ULONG in which the font’s data size, in bytes, is returned.

Return Value

EngMapFontFile returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

Font drivers call this function when they need to access the raw font data in the file identified by iFile. If the font data has not yet been memory-mapped, EngMapFontFile loads the font into system memory before returning ppjBuf and pcjBuf to the driver.

See Also

DrvLoadFontFile


EngMapModule

PVOID EngMapModule(

HANDLE h,

PULONG pSize

);

EngMapModule returns the address and size of an executable file that was loaded by EngLoadModule.

Parameters

h
Handle to the file to be memory-mapped.
pSize
Pointer to a ULONG in which the memory-mapped file’s size, in bytes, is returned.

Return Value

EngMapModule returns a pointer to the view of the file identified by h. The file’s size is returned in pSize.

Comments

A driver can open and read a file using EngLoadModule and EngMapModule.

See Also

EngFreeModule, EngLoadModule


EngMarkBandingSurface

BOOL EngMarkBandingSurface(

IN HSURF hsurf

);

EngMarkBandingSurface marks the specified surface as a banding surface.

Parameters

hsurf
Handle that identifies the surface to mark as a banding surface.

Return Value

The return value is always TRUE.

Comments

A driver should mark a surface as a banding surface when enabling it in DrvEnableSurface.

GDI will capture all of a banding surface’s output in a metafile. This output is subsequently rendered when GDI calls the driver’s implementations of DrvStartBanding and DrvNextBand.


EngMulDiv

int EngMulDiv(

int a,

int b,

int c

);

EngMulDiv multiplies two 32-bit values and then divides the 64-bit result by a third 32-bit value. The return value is rounded up or down to the nearest integer.

Parameters

a
32-bit signed multiplicand
b
32-bit signed multiplier
c
32-bit signed divisor by which the result of a*b is to be divided.

Return Value

EngMulDiv returns the signed 32-bit result of the multiplication and division.

Comments

Drivers should not pass a zero divisor to EngMulDiv.


EngMultiByteToUnicodeN

VOID EngMultiByteToUnicodeN(

LPWSTR UnicodeString,

ULONG MaxBytesInUnicodeString,

PULONG BytesInUnicodeString,

PCHAR MultiByteString,

ULONG BytesInMultiByteString

);

EngMultiByteToUnicodeN converts the specified ANSI source string into a Unicode string using the current ANSI code page.

Parameters

UnicodeString
Pointer to the buffer in which the resultant Unicode string is returned.
MaxBytesInUnicodeString
Supplies the maximum number of bytes to be written to UnicodeString. If this value is too small, causing UnicodeString to be a truncated equivalent of MultiByteString, no error condition results.
BytesInUnicodeString
Pointer to a ULONG in which the number of bytes written to UnicodeString is returned.
MultiByteString
Pointer to the ANSI source string that is to be converted to Unicode.
BytesInMultiByteString
The number of bytes in MultiByteString.

See Also

EngMultiByteToWideChar, EngUnicodeToMultiByteN


EngMultiByteToWideChar

INT EngMultiByteToWideChar(

UINT CodePage,

LPWSTR WideCharString,

INT BytesInWideCharString,

LPSTR MultiByteString,

INT BytesInMultiByteString

);

EngMultiByteToWideChar converts an ANSI source string into a wide character string using the specified code page.

Parameters

CodePage
Specifies the code page to use to perform the translation.
WideCharString
Buffer into which the character string is translated.
BytesInWideCharString
Specifies the size, in bytes, of WideCharString. If WideCharString is not large enough to contain the translation, EngMultiByteToWideChar truncates the string, and does not report an error.
MultiByteString
Buffer containing the multibyte string to be translated.
BytesInMultiByteString
Specifies the number of bytes in MultiByteString.

See Also

EngUnicodeToMultiByteN, EngWideCharToMultiByte


EngPaint

BOOL EngPaint(

IN SURFOBJ *pso,

IN CLIPOBJ *pco,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrushOrg,

IN MIX mix

);

EngPaint causes GDI to paint a specified region.

Parameters

pso
Points to a SURFOBJ that describes the surface on which to draw.
pco
Points to a CLIPOBJ that defines the area to be painted. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles.
pbo
Points to a BRUSHOBJ that defines the pattern and colors with which to fill.
pptlBrushOrg
Points to a POINTL structure that defines the brush origin used to align the brush pattern on the device.
mix
Defines the foreground and background raster operations to use for the brush.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

Vector device drivers can implement this function with the help of EngCreatePath and PATHOBJ_Xxx service routines.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

BRUSHOBJ, CLIPOBJ, DrvPaint, EngCreatePath, PATHOBJ, SURFOBJ


EngProbeForRead

VOID EngProbeForRead(

PVOID Address,

ULONG Length,

ULONG Alignment

);

EngProbeForRead probes a structure for read accessibility.

Parameters

Address
Pointer to the structure to be probed.
Length
Length, in bytes, of the structure to be probed.
Alignment
Supplies the required alignment of the structure. This parameter is expressed as the number of bytes in the base data type. For example, an alignment of 1 specifies that Address be aligned on a character boundary, 2 specifies alignment on a short boundary, and 4 specifies alignment on a long boundary.

Comments

EngProbeForRead causes an exception to be raised if the structure pointed to by Address:

  • Does not have a base address that begins on an alignment-byte boundary.

  • Is not read-accessible.

See Also

EngProbeForReadAndWrite


EngProbeForReadAndWrite

VOID EngProbeForReadAndWrite(

PVOID Address,

ULONG Length,

ULONG Alignment

);

EngProbeForReadAndWrite probes a structure for read and write accessibility.

Parameters

Address
Pointer to the structure to be probed.
Length
Length, in bytes, of the structure to be probed.
Alignment
Supplies the required alignment of the structure. This parameter is expressed as the number of bytes in the base data type. For example, an alignment of 1 specifies that Address be aligned on a character boundary, 2 specifies alignment on a short boundary, and 4 specifies alignment on a long boundary.

Comments

EngProbeForReadAndWrite causes an exception to be raised if the structure pointed to by Address:

  • Does not have a base address that begins on an alignment-byte boundary.

  • Is not both read- and write-accessible.

See Also

EngProbeForRead


EngQueryLocalTime

VOID EngQueryLocalTime(

PENG_TIME_FIELDS ptf

);

EngQueryLocalTime queries the local time.

Parameters

ptf
Pointer to the ENG_TIME_FIELDS structure in which the local time is returned.

Comments

EngQueryLocalTime returns the time at the current locale in the ENG_TIME_FIELDS structure.


EngQueryPerformanceCounter

VOID EngQueryPerformanceCounter(

LONGLONG *pPerformanceCount

);

EngQueryPerformanceCounter queries the performance counter.

Parameters

pPerformanceCount
Pointer to a location in which the performance counter value, in hertz, is returned.

Comments

EngQueryPerformanceCounter always returns a 64-bit integer that represents the number of ticks per second. The count begins accumulating when the system is booted.

A driver should call this routine sparingly. Frequent calls to EngQueryPerformanceCounter can degrade the I/O performance for the calling driver and for the system as a whole.

See Also

EngQueryPerformanceFrequency


EngQueryPerformanceFrequency

VOID EngQueryPerformanceFrequency(

LONGLONG *pFrequency

);

EngQueryPerformanceFrequency queries the frequency of the performance counter.

Parameters

pFrequency
Pointer to a location in which the performance counter frequency is returned.

Comments

The resolution of the timer is 1/x, where x is the value returned in the location to which pFrequency points. For example, if the value returned in pFrequency is 2 million, each tick is 1/2 millionth of a second. Each 1/x millionth increment of the count corresponds to one second of elapsed time.

A driver should call this routine sparingly. Frequent calls to EngQueryPerformanceFrequency can degrade the I/O performance for the calling driver and for the system as a whole.

See Also

EngQueryPerformanceCounter


EngReleaseSemaphore

VOID EngReleaseSemaphore(

HSEMAPHORE hsem

);

EngReleaseSemaphore releases the specified semaphore.

Parameters

hsem
Handle to the semaphore to be released.

Comments

EngReleaseSemaphore releases the semaphore’s exclusive lock on a driver’s resource and re-enables the delivery of special kernel asychronous procedure calls.

The lock and asynchronous procedure call suspension were acquired in a call to EngAcquireSemaphore.

See Also

EngAcquireSemaphore, EngDeleteSemaphore


EngSecureMem

HANDLE EngSecureMem(

PVOID Address,

ULONG Length

);

EngSecureMem locks down the specified address range in memory.

Parameters

Address
Pointer to the base address of the memory to be secured.
Length
Specifies the size of the memory range to be secured.

Return Value

EngSecureMem returns a handle to the secured address range upon successful completion; otherwise, it returns null.

Comments

The address range locked down by EngSecureMem will not be deallocated until it is unlocked by EngUnsecureMem.


EngSetLastError

VOID EngSetLastError(

IN ULONG iError

);

EngSetLastError causes GDI to log an error code. This error code can be retrieved by an application.

Parameters

iError
Specifies the 32-bit error code to set.

Comments

EngSetLastError sets the last error code for the calling thread. This function allows a driver to communicate error conditions to an application. To facilitate this communication, a driver should use the Win32 application error codes defined in winerror.h.

Only the last error code to be set is retrievable; that is, consecutive calls to EngSetLastError will cause the last-error code field to be overwritten.

See Also

EngGetLastError


EngSetPrinterData

DWORD EngSetPrinterData(

IN HANDLE hPrinter,

IN LPWSTR pType,

IN DWORD dwType,

IN LPBYTE lpbPrinterData,

IN DWORD cjPrinterData

);

EngSetPrinterData sets the configuration data for the specified printer.

Parameters

hPrinter
Handle that identifies the printer for which configuration data should be set. This is the handle that is passed as the hDriver parameter of DrvEnablePDEV.
pType
Pointer to a null-terminated string that specifies the type of data to be set.
dwType
Flag that specifies the type of information to be set. This parameter can be one of the following values:

Value

Meaning

REG_BINARY

Binary data in any form.

REG_DWORD

A 32-bit number.

REG_DWORD_LITTLE_ENDIAN

A 32-bit number in little-endian format (same as REG_DWORD), meaning that the most significant byte of a word is the high-order byte

REG_DWORD_BIG_ENDIAN

A 32-bit number in big-endian format, meaning that the most significant byte of a word is the low-order byte.

REG_EXPAND_SZ

A null-terminated string that contains unexpanded references to environment variables (for example, “%PATH%”). It will be a UNICODE or ANSI string depending on whether UNICODE or ANSI functions are used.

REG_LINK

A UNICODE symbolic link.

REG_MULTI_SZ

An array of null-terminated strings, terminated by two null characters.

REG_NONE

No defined value type.

REG_RESOURCE_LIST

A device-driver resource list.

REG_SZ

A null-terminated string. It will be a UNICODE or ANSI string depending on whether you use the UNICODE or ANSI functions.

lpbPrinterData
Byte pointer to the printer configuration data that is to be set. The type of data pointed to is determined by dwType.
cjPrinterData
Specifies the size, in bytes, of lpbPrinterData.

Return Value

EngSetPrinterData returns the last logged error message.

See Also

DrvEnablePDEV, EngGetPrinterData


EngSort

VOID EngSort(

PBYTE pjBuf,

ULONG c,

ULONG cjElem,

SORTCOMP pfnComp

);

EngSort performs a quick-sort on the specified list.

Parameters

pjBuf
Pointer to the data array to be sorted.
c
Size, in bytes, of each element in pjBuf.
cjElem
Number of elements in pjBuf to be sorted.
pfnComp
Pointer to a function that implements the element comparison to be used for the sort.

Comments

EngSort implements a quick-sort algorithm to sort cjElem elements in pjBuf, where each element is of size c. The sorted elements are returned in pjBuf; that is, the original contents of the buffer are overwritten with the sorted results.

The basis for comparing two elements is defined in the function that pfnComp points to. This function is defined as follows:

INT Compare((VOID *)element1, (VOID *)element2);

where element1 and element2 are the two elements to be compared, and the return value is the result of the comparison defined as follows:
Return Value | Meaning
Negative integer | element1 < element2
Zero | element1 = element2
Positive integer | element1 > element2

The array is sorted in increasing order, which is defined by the pfnComp parameter.


EngStretchBlt

BOOL EngStretchBlt(

IN SURFOBJ *psoDest,

IN SURFOBJ *psoSrc,

IN SURFOBJ *psoMask,

IN CLIPOBJ *pco,

IN XLATEOBJ *pxlo,

IN COLORADJUSTMENT *pca,

IN POINTL pptlHTOrg,

IN RECTL *prclDest,

IN RECTL *prclSrc,

IN POINTL *pptlMask,

IN ULONG iMode

);

EngStretchBlt causes GDI to do a stretching bit-block transfer. This function allows the same halftoning algorithm to be applied to GDI bitmaps and device surfaces.

Parameters

psoDest
Points to a SURFOBJ that describes the surface on which to draw.
psoSrc
Points to a SURFOBJ that describes the source surface for the bit-block transfer.
psoMask
Points to a SURFOBJ structure that defines a mask for the source. The mask is defined by a logic map, which is a bitmap with 1 bit per pixel.

The mask limits the area of the source that is copied. If this parameter is specified, it has an implicit rop4 of 0xCCAA, meaning the source should be copied wherever the mask is 1, but the destination should be left alone wherever the mask is 0.

If this parameter is null, the rop4 is implicitly 0xCCCC, which means the source should be copied everywhere in the source rectangle.

pco
Points to a CLIPOBJ that limits the area to be modified in the destination. GDI services are provided to enumerate the clip region as a set of rectangles.

Whenever possible, GDI simplifies the clipping involved. However, unlike DrvBitBlt, EngStretchBlt can be called with a single clipping rectangle. This prevents rounding errors in clipping the output.

pxlo
Points to a XLATEOBJ that specifies how color indices are to be translated between the source and target surfaces.

This XLATEOBJ can also be queried to find the RGB color for any source index. A high quality stretching bit-block transfer will need to interpolate colors in some cases.

pca
Points to a COLORADJUSTMENT structure that defines the color adjustment values to be applied to the source bitmap before stretching the bits. For more information see the Win32 SDK.
pptlHTOrg
Points to a POINTL structure that defines the origin of the halftone brush. Drivers that use halftone brushes should align the upper left pixel of the brush’s pattern with this point on the device surface.
prclDest
Points to a RECTL structure that defines the area to be modified in the coordinate system of the destination surface. This rectangle is defined by two points that are not well-ordered, meaning the coordinates of the second point are not necessarily larger than those of the first point. The rectangle described does not include the lower and right edges. This function is never called with an empty destination rectangle.

EngStretchBlt inverts x and y if the destination rectangle is not well ordered.

prclSrc
Points to a RECTL structure that defines the area to be copied, in the coordinate system of the source surface. The rectangle will map to the rectangle defined by prclDest. This function is never given an empty source rectangle, and the points of the source rectangle are always well-ordered.

The mapping is defined by prclSrc and prclDest. The points specified in prclDest and prclSrc lie on integer coordinates, which correspond to pixel centers. A rectangle defined by two such points is considered to be a geometric rectangle with two vertices whose coordinates are the given points, but with 0.5 subtracted from each coordinate. (POINTL structures are shorthand notation for specifying these fractional coordinate vertices.)

The edges of any rectangle never intersect a pixel, but go around a set of pixels. The pixels that are inside the rectangle are those expected for a bottom-right exclusive rectangle. EngStretchBlt maps the geometric source rectangle exactly onto the geometric destination rectangle.

pptlMask
Points to a POINTL structure that defines the pixel in the given mask that corresponds to the upper left pixel in the source rectangle. This parameter is ignored if no mask is specified.
iMode
Specifies how source pixels are combined to get output pixels. The HALFTONE mode is slower than the other modes, but produces higher quality images.

Value

Meaning

WHITEONBLACK

On a shrinking bit-block transfer, pixels should be combined with a Boolean OR operation. On a stretching bit-block transfer, pixels should be replicated.

BLACKONWHITE

On a shrinking bit-block transfer, pixels should be combined with a Boolean AND operation. On a stretching bit-block transfer, pixels should be replicated.

COLORONCOLOR

On a shrinking bit-block transfer, enough pixels should be ignored so that pixels don’t need to be combined. On a stretching bit-block transfer, pixels should be replicated.

HALFTONE

The driver can use groups of pixels in the output surface to best approximate the color or gray level of the input.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

The driver should call EngStretchBlt if it has hooked DrvStretchBlt and is called to do something the driver does not support.

See Also

CLIPOBJ, DrvBitBlt, SURFOBJ, XLATEOBJ


EngStrokeAndFillPath

BOOL EngStrokeAndFillPath(

IN SURFOBJ *pso,

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN XFORMOBJ *pxo,

IN BRUSHOBJ *pboStroke,

IN LINEATTRS *plineattrs,

IN BRUSHOBJ *pboFill,

IN POINTL *pptlBrushOrg,

IN MIX mix,

IN FLONG flOptions

);

EngStrokeAndFillPath causes GDI to fill a path and stroke it at the same time.

Parameters

pso
Points to a SURFOBJ structure that defines the drawing surface.
ppo
Points to a PATHOBJ structure that defines the path to be filled. The PATHOBJ_Xxx service routines are provided to enumerate the lines, Bezier curves, and other data that make up the path.
pco
Points to a CLIPOBJ structure. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles.
pxo
Points to a XFORMOBJ structure that is only needed when a geometric wide line is to be drawn and specifies the transform that converts world coordinates to device coordinates. The path is provided in device coordinates but a geometric wide line is actually widened in world coordinates.

The driver can use the XFORMOBJ_Xxx service routines to determine the transform.

pboStroke
Points to a BRUSHOBJ structure that describes the brush to use when stroking the path.
plineattrs
Points to a LINEATTRS structure.
pboFill
Points to a BRUSHOBJ structure that describes the brush to use when filling the path.
pptlBrushOrg
Points to a POINTL structure that defines the brush origin for both brushes.
mix
Defines the foreground and background raster operations to use for the fill brush.
flOptions
Specifies which fill mode to use. This parameter can be FP_WINDINGMODE or FP_ALTERNATEMODE; all other bits should be ignored.

Return Value

The return value is TRUE if GDI fills the path. If the driver should fill the path, the return value is FALSE, and an error code is not logged. If GDI encounters an unexpected error, such as not being able to realize the brush, the return value is DDI_ERROR, and an error code is logged.

Comments

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

BRUSHOBJ, CLIPOBJ, DrvStrokeAndFillPath, LINEATTRS, PATHOBJ, SURFOBJ, XFORMOBJ


EngStrokePath

BOOL EngStrokePath(

IN SURFOBJ *pso,

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN XFORMOBJ *pxo,

IN BRUSHOBJ *pbo,

IN POINTL *pptlBrushOrg,

IN LINEATTRS *plineattrs,

IN MIX mix

);

EngStrokePath requests that GDI stroke a specified path.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to draw.
ppo
Points to a PATHOBJ structure. The PATHOBJ_Xxx service routines are provided to enumerate the lines, Bezier curves, and other data that make up the path. This indicates what is to be drawn.
pco
Points to a CLIPOBJ structure. The CLIPOBJ_Xxx service routines are provided to enumerate the clip region as a set of rectangles. Optionally, all the lines in the path can be enumerated preclipped by this CLIPOBJ. This means that drivers can have all their line clipping calculations done for them.
pxo
Points to a XFORMOBJ structure. This is only needed when a geometric wide line is to be drawn. It specifies the transform that converts world coordinates to device coordinates. This is needed because the path is provided in device coordinates but a geometric wide line is actually widened in world coordinates.

The driver can use the XFORMOBJ_Xxx service routines to determine the transform.

pbo
Specifies the brush to be used when drawing the path.
pptlBrushOrg
Points to the brush origin used to align the brush pattern on the device.
plineattrs
Points to a LINEATTRS structure. Note that the elStyleState member of the structure must be updated as part of this function if the line is styled. Also note the ptlLastPel member must be updated if a single-pixel-width cosmetic line is being drawn.
mix
Specifies how to combine the brush with the destination.

Return Value

The return value is TRUE if GDI strokes the path. If the driver should stroke the path, the return value is FALSE, and no error is logged. If GDI encounters an error, the return value is DDI_ERROR, and an error code is logged.

Comments

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

CLIPOBJ, DrvStrokePath, LINEATTRS, PATHOBJ, SURFOBJ, XFORMOBJ


EngTextOut

BOOL EngTextOut(

IN SURFOBJ *pso,

IN STROBJ *pstro,

IN FONTOBJ *pfo,

IN CLIPOBJ *pco,

IN RECTL *prclExtra,

IN RECTL *prclOpaque,

IN BRUSHOBJ *pboFore,

IN BRUSHOBJ *pboOpaque,

IN POINTL pptlOrg,

IN MIX mix

);

EngTextOut causes GDI to render a set of glyphs at specified positions.

Parameters

pso
Points to a SURFOBJ structure that describes the surface on which to write.
pstro
Points to a STROBJ structure that defines the glyphs to be rendered and the positions where they are to be placed.
pfo
Points to a FONTOBJ structure that is used to retrieve information about the font and its glyphs.
pco
Points to a CLIPOBJ structure that defines the clip region through which rendering must be done. No pixels can be affected outside this clip region.
prclExtra
Points to a null-terminated array of rectangles. These rectangles are bottom-right exclusive. The pixels of the rectangles are to be combined with the pixels of the glyphs to produce the foreground pixels. The extra rectangles are used to simulate underlining or strike-out.

A null rectangle has both coordinates of both points set to zero.

prclOpaque
Points to a single opaque rectangle that is bottom right exclusive. Pixels within this rectangle (those that are not foreground and not clipped) are to be rendered with the opaque brush. This rectangle always bounds the text to be drawn. If this parameter is null, no opaque pixels are to be rendered.
pboFore
Points to the brush object to be used for the foreground pixels. This brush will always be a solid color brush.
pboOpaque
Points to the brush object for the opaque pixels. Both the foreground and background mix modes for this brush are assumed to be R2_COPYPEN. Unless the driver sets the GCAPS_ARBRUSHOPAQUE capabilities bit in the flGraphicsCaps member of the DEVINFO structure, it will always be called with a solid color brush.
pptlOrg
Points to a POINTL structure that defines the brush origin for both brushes.
mix
Specifies foreground and background raster operations (mix modes) for pboFore.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

The driver should call EngTextOut when it has hooked DrvTextOut and cannot render the glyphs.

Note The driver cannot punt to EngTextOut if it has hooked DrvTextOut for a device managed surface.

The input parameters to EngTextOut define two sets of pixels: foreground and opaque. The driver must render the surface so the result is identical to a process where the opaque pixels are rendered first with the opaque brush, and then the foreground pixels are rendered with the foreground brush. Each of these operations is limited by clipping.

The foreground set of pixels is defined to be the union of the pixels of the glyphs and the pixels of the extra rectangles pointed to by prclExtra. These extra rectangles are used to simulate strike-through or underlines. The opaque pixels are defined by the opaque rectangle at prclOpaque.

The foreground and opaque pixels are regarded as a screen through which color is brushed onto the surface. The glyphs of the font do not have color in themselves.

The input parameters to EngTextOut define the set of glyph pixels, the set of extra rectangles, the opaque rectangle, and the clip region. The driver must calculate and then render the set of foreground and opaque pixels.

The mix mode defines how the incoming pattern should be mixed with the data already on the device surface. The MIX data type consists of two ROP2 values packed into a single ULONG. The low-order byte defines the foreground raster operation; the next byte defines the background raster operation. For more information about raster operation codes, see the Win32 SDK.

See Also

CLIPOBJ, DEVINFO, DrvTextOut, FONTOBJ, STROBJ, SURFOBJ


EngUnicodeToMultiByteN

VOID EngUnicodeToMultiByteN(

PCHAR MultiByteString,

ULONG MaxBytesInMultiByteString,

PULONG BytesInMultiByteString,

PWSTR UnicodeString,

ULONG BytesInUnicodeString

);

EngUnicodeToMultiByteN converts the specified Unicode string into an ANSI string using the current ANSI code page.

Parameters

MultiByteString
Pointer to the buffer in which the resultant ANSI string is returned.
MaxBytesInMultiByteString
Supplies the maximum number of bytes to be written to MultiByteString. If this value is too small, causing MultiByteString to be a truncated equivalent of UnicodeString, then no error condition results.
BytesInMultiByteString
Pointer to a ULONG in which the number of bytes written to MultiByteString is returned.
UnicodeString
Pointer to the Unicode source string that is to be converted to ANSI.
BytesInUnicodeString
The number of bytes in UnicodeString.

See Also

EngMultiByteToUnicodeN, EngWideCharToMultiByte


EngUnloadImage

VOID EngUnloadImage(

HANDLE hModule

);

EngUnloadImage unloads an image loaded by EngLoadModule.

Parameters

hModule
Handle of the image to be unloaded from system memory.


EngUnlockDriverObj

BOOL EngUnlockDriverObj(

IN HDRVOBJ hdo

);

EngUnlockDriverObj causes GDI to unlock the driver object.

Parameters

hdo
Identifies the object to be unlocked.

Return Value

The return value is TRUE if the function is successful; otherwise, it is FALSE.

Comments

The specified driver object must have been previously locked by a call to EngLockDriverObj. The object is not unlockable by another thread while it is locked down.


EngUnlockSurface

VOID EngUnlockSurface(

IN SURFOBJ *pso

);

EngUnlockSurface causes GDI to unlock the surface.

Parameters

pso
Points to a SURFOBJ structure that describes the surface to be unlocked.

Comments

The specified surface must previously have been locked by a call to EngLockSurface. The pointer to the SURFOBJ must not be used after this call.


EngUnmapFontFile

VOID EngUnmapFontFile(

ULONG iFile

);

EngUnmapFontFile unmaps the specified font file from system memory.

Parameters

iFile
Identifies the font file to be unmapped. This identifier is obtained from DrvLoadFontFile.

Comments

EngUnmapFontFile is called by font drivers to unmap a font file that was mapped in a call to EngMapFontFile.


EngUnsecureMem

VOID EngUnsecureMem(

HANDLE SecureHandle

);

EngUnsecureMem unlocks an address range that is locked down in memory.

Parameters

SecureHandle
Handle to the address range that is locked down. This handle is returned by EngSecureMem.


EngWideCharToMultiByte

INT EngWideCharToMultiByte(

UINT CodePage,

LPWSTR WideCharString,

INT BytesInWideCharString,

LPSTR MultiByteString,

INT BytesInMultiByteString

);

EngWideCharToMultiByte converts a wide character string into an ANSI source string using the specified code page.

Parameters

CodePage
Specifies the code page to use to perform the translation.
WideCharString
Buffer containing the wide character string to be translated.
BytesInWideCharString
Specifies the size, in bytes, of WideCharString.
MultiByteString
Buffer into which the character string is translated
BytesInMultiByteString
Specifies the number of bytes in MultiByteString. If MultiByteString is not large enough to contain the translation, EngWideCharToMultiByte truncates the string, and does not report an error.

See Also

EngMultiByteToWideChar , EngUnicodeToMultiByteN


EngWritePrinter

BOOL EngWritePrinter(

HANDLE hPrinter,

LPVOID pBuf,

DWORD cbBuf,

LPDWORD pcWritten

);

EngWritePrinter provides the print spooler with data that needs to be written to the specified printer.

Parameters

hPrinter
Handle that identifies the printer to use.
pBuf
Pointer to data to be written to the printer.
cbBuf
Specifies the size, in bytes, of pBuf.
pcWritten
Pointer to a DWORD in which the number of bytes actually written to the printer is returned.

Return Value

The return value is TRUE if the function succeeds; otherwise it is FALSE.

Comments

Printer drivers call EngWritePrinter when they have data to output. Control is returned to the printer driver immediately after the data is spooled, so that the driver can continue to process print requests.

The data pointed to by pBuf cannot be user memory; that is, pBuf cannot point to memory allocated by EngCreateBitmap with the BMF_USERMEM flag set or by EngAllocUserMem.


FLOATOBJ_Add

VOID FLOATOBJ_Add(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_Add sums the two FLOATOBJs, and returns the result in the first FLOATOBJ parameter.

Parameters

pf
Pointer to the first FLOATOBJ operand. The sum of pf and pf1 is returned in this parameter.
pf1
Pointer to the second FLOATOBJ operand.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_AddFloat

VOID FLOATOBJ_AddFloat(

PFLOATOBJ pf,

FLOAT f

);

FLOATOBJ_AddFloat adds the value of type FLOAT to the FLOATOBJ, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The sum of pf and f is returned in this parameter.
f
The FLOAT operand. This value is converted to a FLOATOBJ for the summation.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_AddLong

VOID FLOATOBJ_AddLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_AddLong adds the value of type LONG to the FLOATOBJ, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The sum of pf and f is returned in this parameter.
l
The LONG operand. This value is converted to a FLOATOBJ for the summation.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_Div

VOID FLOATOBJ_Div(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_Div divides the two FLOATOBJs, and returns the result in the first FLOATOBJ parameter.

Parameters

pf
Pointer to the first FLOATOBJ operand. The division of pf by pf1 is returned in this parameter.
pf1
Pointer to the second FLOATOBJ operand.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_DivFloat

VOID FLOATOBJ_DivFloat(

PFLOATOBJ pf,

FLOAT f

);

FLOATOBJ_DivFloat divides the FLOATOBJ by the value of type FLOAT, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The division of pf by f is returned in this parameter.
f
The FLOAT operand. This value is converted to a FLOATOBJ for the division.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_DivLong

VOID FLOATOBJ_DivLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_DivLong divides the FLOATOBJ by the value of type LONG, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The division of pf by l is returned in this parameter.
l
The LONG operand. This value is converted to a FLOATOBJ for the division.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_Equal

BOOL FLOATOBJ_Equal(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_Equal determines whether the two FLOATOBJs are equal.

Parameters

pf
Pointer to the first FLOATOBJ operand.
pf1
Pointer to the second FLOATOBJ operand.

Return Value

FLOATOBJ_Equal returns TRUE if pf and pf1 are equal; otherwise it returns FALSE.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_EqualLong

BOOL FLOATOBJ_EqualLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_EqualLong determines whether the FLOATOBJ and the value of type LONG are equal.

Parameters

pf
Pointer to the FLOATOBJ.
l
The LONG value. This value is converted to a FLOATOBJ for the comparison.

Return Value

FLOATOBJ_EqualLong returns TRUE if pf and the FLOATOBJ-equivalent value of l are equal; otherwise it returns FALSE.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_GetFloat

LONG FLOATOBJ_GetFloat(

PFLOATOBJ pf

);

FLOATOBJ_GetFloat calculates and returns the FLOAT-equivalent value of the specified FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ to be converted to a FLOAT.

Return Value

FLOATOBJ_GetFloat returns the FLOAT-equivalent value of pf. The return value is of type LONG, and needs to be typecast to a FLOAT by the driver.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_GetLong

LONG FLOATOBJ_GetLong(

PFLOATOBJ pf

);

FLOATOBJ_GetLong calculates and returns the LONG-equivalent value of the specified FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ to be converted to a LONG.

Return Value

FLOATOBJ_GetFloat returns the LONG-equivalent value of pf.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_GreaterThan

BOOL FLOATOBJ_GreaterThan(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_GreaterThan determines whether the first FLOATOBJ is greater than the second FLOATOBJ.

Parameters

pf
Pointer to the first FLOATOBJ.
pf1
Pointer to the second FLOATOBJ.

Return Value

FLOATOBJ_GreaterThan returns TRUE if pf is greater than pf1; otherwise it returns FALSE.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_GreaterThanLong

BOOL FLOATOBJ_GreaterThanLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_GreaterThanLong determines whether the FLOATOBJ is greater than the value of type LONG.

Parameters

pf
Pointer to the FLOATOBJ.
l
The LONG value. This value is converted to a FLOATOBJ for the comparison.

Return Value

FLOATOBJ_GreaterThanLong returns TRUE if pf is greater than the FLOATOBJ-equivalent of l; otherwise it returns FALSE.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_LessThan

BOOL FLOATOBJ_LessThan(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_LessThan determines whether the first FLOATOBJ is less than the second FLOATOBJ.

Parameters

pf
Pointer to the first FLOATOBJ.
pf1
Pointer to the second FLOATOBJ.

Return Value

FLOATOBJ_LessThan returns TRUE if pf is less than pf1; otherwise it returns FALSE.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_LessThanLong

BOOL FLOATOBJ_LessThanLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_LessThanLong determines whether the FLOATOBJ is less than the value of type LONG.

Parameters

pf
Pointer to the FLOATOBJ.
l
The LONG value. This value is converted to a FLOATOBJ for the comparison.

Return Value

FLOATOBJ_LessThanLong returns TRUE if pf is less than the FLOATOBJ-equivalent of l; otherwise it returns FALSE.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_Mul

VOID FLOATOBJ_Mul(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_Mul multiplies the two FLOATOBJs, and returns the result in the first FLOATOBJ.

Parameters

pf
Pointer to the first FLOATOBJ operand. The result of pf by pf1 is returned in this parameter.
pf1
Pointer to the second FLOATOBJ operand.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_MulFloat

VOID FLOATOBJ_MulFloat(

PFLOATOBJ pf,

FLOAT f

);

FLOATOBJ_MulFloat multiplies the FLOATOBJ by the value of type FLOAT, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The result of pf by f is returned in this parameter.
f
The FLOAT operand. This value is converted to a FLOATOBJ for the multiplication.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_MulLong

VOID FLOATOBJ_MulLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_MulLong multiplies the FLOATOBJ by the value of type LONG, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The result of pf by l is returned in this parameter.
l
The LONG operand. This value is converted to a FLOATOBJ for the multiplication.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_Neg

VOID FLOATOBJ_Neg(

PFLOATOBJ pf

);

FLOATOBJ_Neg negates the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ to be negated. The negated FLOATOBJ is returned in this parameter.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_SetFloat

VOID FLOATOBJ_SetFloat(

PFLOATOBJ pf,

FLOAT f

);

FLOATOBJ_SetFloat assigns the value of type FLOAT to the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ that receives the value of f.
f
The FLOAT value. This value is converted to a FLOATOBJ for the assignment.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_SetLong

VOID FLOATOBJ_SetLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_SetLong assigns the value of type LONG to the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ that receives the value of l.
l
The LONG value. This value is converted to a FLOATOBJ for the assignment.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_Sub

VOID FLOATOBJ_Sub(

PFLOATOBJ pf,

PFLOATOBJ pf1

);

FLOATOBJ_Sub subtracts the second FLOATOBJ from the first, and returns the result in the first FLOATOBJ.

Parameters

pf
Pointer to the first FLOATOBJ operand. The result of pf minus pf1 is returned in this parameter.
pf1
Pointer to the second FLOATOBJ operand.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_SubFloat

VOID FLOATOBJ_SubFloat(

PFLOATOBJ pf,

FLOAT f

);

FLOATOBJ_SubFloat subtracts the value of type FLOAT from the FLOATOBJ, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The result of pf minus f is returned in this parameter.
f
The FLOAT operand. This value is converted to a FLOATOBJ for the subtraction.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FLOATOBJ_SubLong

VOID FLOATOBJ_SubLong(

PFLOATOBJ pf,

LONG l

);

FLOATOBJ_SubLong subtracts the value of type LONG from the FLOATOBJ, and returns the result in the FLOATOBJ.

Parameters

pf
Pointer to the FLOATOBJ operand. The result of pf minus l is returned in this parameter.
l
The LONG operand. This value is converted to a FLOATOBJ for the subtraction.

Comments

The FLOATOBJ_Xxx services allow graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ


FONTOBJ_cGetAllGlyphHandles

ULONG FONTOBJ_cGetAllGlyphHandles(

IN FONTOBJ *pfo,

OUT HGLYPH *phg

);

FONTOBJ_cGetAllGlyphHandles allows the device driver to find every glyph handle of a GDI font. A driver uses this service to download an entire font.

Parameters

pfo
Points to the FONTOBJ that is to be downloaded.
phg
Points to a buffer large enough to hold all the glyph handles in the font. This parameter can be null.

Return Value

The return value is the number of glyph handles supported by the font.

Comments

The driver must provide a buffer large enough to contain the output. GDI copies all glyph handles belonging to the associated font to this buffer.

The number of glyphs in the font can be determined by calling FONTOBJ_vGetInfo, or by calling FONTOBJ_cGetAllGlyphHandles with the phg parameter set to null.

See Also

FONTOBJ


FONTOBJ_cGetGlyphs

ULONG FONTOBJ_cGetGlyphs(

IN FONTOBJ *pfo,

IN ULONG iMode,

IN ULONG cGlyph,

IN HGLYPH *phg,

OUT PVOID **ppvGlyph

);

FONTOBJ_cGetGlyphs is a service to the font consumer that translates glyph handles into pointers to glyph data. These pointers are valid until the next call to FONTOBJ_cGetGlyphs.

Parameters

pfo
Points to a FONTOBJ structure containing the glyph handles to be translated.
iMode
Specifies whether data will be written as bitmaps or as outline objects. This parameter can be one of the following values:

Value

Meaning

FO_GLYPHBITS

Data will consist of GLYPHBITS structures that define the bitmaps of the glyphs.

FO_PATHOBJ

Data will consist of PATHOBJ structures that define the outlines of the glyphs.

To determine whether the path should be filled or stroked, the font consumer should check the flInfo member of the IFIMETRICS structure. If the FM_INFO_RETURNS_STROKES flag is set, the path should be stroked; otherwise, the path should be filled.

cGlyph
Specifies the number of glyphs to be translated. This number of glyph handles is available in the array starting at the address specified by phg. There must be enough space allocated in the buffer pointed to by ppvGlyph to store this number of pointers.
phg
Points to an array of cGlyph HGLYPH structures supplied by the driver.
ppvGlyph
Points to a buffer that is to receive cGlyph pointers to GLYPHDATA structures, corresponding to the handles passed in by the driver.

Return Value

The return value is the count of pointers passed to the driver if the function is successful. Otherwise, it is zero, and an error code is logged.

Comments

This function should be used if the driver is caching fonts.

See Also

DrvGetGlyphMode, FONTOBJ, FONTOBJ_cGetAllGlyphHandles, GLYPHBITS, IFIMETRICS, PATHOBJ


FONTOBJ_pifi

PIFIMETRICS FONTOBJ_pifi(

IN FONTOBJ *pfo

);

FONTOBJ_pifi retrieves the pointer to the IFIMETRICS structure associated with a specified font.

Parameters

pfo
Points to the FONTOBJ for which the associated IFIMETRICS structure is to be retrieved.

Return Value

The return value is a pointer to the IFIMETRICS structure associated with the specified font if the function is successful. Otherwise, it is null.


FONTOBJ_pvTrueTypeFontFile

PVOID FONTOBJ_pvTrueTypeFontFile(

IN FONTOBJ *pfo,

OUT ULONG *pcjFile

);

FONTOBJ_pvTrueTypeFontFile retrieves a pointer to the ROM-mapped TrueType file that is associated with the TrueType font.

Parameters

pfo
Points to a FONTOBJ.
pcjFile
Points to a TrueType file whose size in bytes is returned.

Comments

FONTOBJ_pvTrueTypeFontFile should only be called for TrueType fonts, and should be called only once per value of the iTTUniq member of the FONTOBJ structure.


FONTOBJ_pxoGetXform

XFORMOBJ FONTOBJ_pxoGetXform(

IN FONTOBJ *pfo

);

FONTOBJ_pxoGetXform retrieves the notional-to-device transform for the specified font.

Parameters

pfo
Points to the FONTOBJ for which the transform is to be retrieved.

Return Value

The return value is a XFORMOBJ structure that can be used by the XFORMOBJ Xxx service routines. The returned XFORMOBJ assumes that:

  1. The distance between the pixels is in device space units.

  2. Both notional and device space have positive values of y in the top-to-bottom direction.

If the font is a raster font, the return value is null.

Comments

The driver needs the notional-to-device transform to realize a driver-supplied font.

See Also

FONTOBJ, XFORMOBJ


FONTOBJ_vGetInfo

VOID FONTOBJ_vGetInfo(

IN FONTOBJ *pfo,

IN ULONG cjSize,

OUT FONTINFO *pfi

);

FONTOBJ_vGetInfo retrieves information about an associated font.

Parameters

pfo
Points to the FONTOBJ structure to be queried.
cjSize
Specifies the size in bytes of the buffer pointed to by pfi.
pfi
Points to a buffer previously allocated by the driver. GDI writes a FONTINFO structure to this buffer.


HT_ComputeRGBGammaTable

LONG HT_ComputeRGBGammaTable(

WORD GammaTableEntries,

WORD GammaTableType,

USHORT RedGamma,

USHORT GreenGamma,

USHORT BlueGamma,

LPBYTE pGammaTable

);

HT_ComputeRGBGammaTable is a halftone-related GDI service that causes GDI to compute device red, green, and blue intensities based on gamma numbers.

Parameters

GammaTableEntries
Specifies the total number of steps in the table for each of red, green, and blue intensities. This value must greater than 1 and less than or equal to 256. (for example, 2 <= GammaTableEntries <= 256). For example, a value of 256 means there are 256 red entries, 256 green entries, and 256 blue entries in the gamma table.
GammaTableType
Specifies pGammaTable’s organization. Valid table types are:

GammaTableType

Meaning

0

The gamma table’s red, green, and blue values are interleaved together. Each gamma step is 3 bytes; 1 byte each for red, green, and blue.

1

The red, green, and blue tables are packed separately; that is, the entire red table is followed by the entire green table, which is followed by the entire blue table. Individual entries are 1 byte each, making each sub-table a total of GammaTableEntries bytes in length.

RedGamma, GreenGamma, BlueGamma
Specify the red, green, and blue gamma values. These USHORT values are interpreted as real numbers whose four least-significant digits are decimal points. For example, a gamma value of 10000 represents the real number 1.0000, and 12345 represents 1.2345. The minimum gamma value allowed is 0.0000, and the maximum allowable value is 6.5535.
pGammaTable
Points to the array of bytes in which the gamma table’s red, green, and blue intensities will be written. The returned table is organized as specified by the GammaTableType parameter.

Return Value

The return value is the number of gamma entries written to pGammaTable; upon success, this value is equal to GammaTableEntries. If GammaTableEntries is less than 2 or greater than 256, the return value is 0.

The red, green, and blue intensities returned in pGammaTable range from 0 to 255.

Comments

GDI halftone service routines use a special palette to do halftoning. If the device selects an 8-bit per pixel palette from a pool of 24-bit device colors for a 16-bit or 24-bit type surface, GDI assumes red, green, and blue color steps; each has equal brightness.

GDI provides this service so that the driver can query the 8-bit per pixel halftone palette used by GDI or compute gamma corrected and equalized RGB color intensities for the device.


HT_Get8BPPFormatPalette

LONG HT_Get8BPPFormatPalette(

LPPALETTEENTRY pPaletteEntry,

USHORT RedGamma,

USHORT GreenGamma,

USHORT BlueGamma

);

HT_Get8BPPFormatPalette returns a halftone palette for use on standard 8-bits per pixel device types.

Parameters

pPaletteEntry
Points to an array of PALETTEENTRY structures. When this pointer is not null, GDI assumes that it points to valid memory space in which GDI can place the 8-bits per pixel halftone palette.
RedGamma, GreenGamma, BlueGamma
Specify the red, green, and blue gamma values. These USHORT values are interpreted as real numbers whose four least-significant digits are decimal points. For example, a gamma value of 10000 represents the real number 1.0000, and 12345 represents 1.2345. The minimum gamma value allowed is 0.0000, and the maximum allowable value is 6.5535.

Return Value

If pPaletteEntry is not null, the return value is the number of PALETTEENTRY structures written to the memory location pointed to by pPaletteEntry. If pPaletteEntry is null, the return value is the total count of PALETTEENTRY structures required to store the 8-bits per pixel halftone palette.

Comments

HT_Get8BPPFormatPalette is a halftone-related GDI service that drivers can use to acquire the system’s standard 8-bits per pixel halftone palette.


PALOBJ_cGetColors

ULONG PALOBJ_cGetColors(

IN PALOBJ *ppo,

IN ULONG iStart,

IN ULONG cColors,

OUT ULONG *ppalentry

);

PALOBJ_cGetColors copies RGB colors from an indexed palette.

Parameters

ppo
Points to the PALOBJ structure that contains the RGB colors to be copied.
iStart
Specifies the starting color index.
cColors
Specifies the number of colors to be written.
ppalentry
Points to the buffer in which the colors are to be written.

Return Value

The return value is the number of colors written if the function is successful. Otherwise, it is zero.

Comments

A graphics driver can call this function in its implementation of DrvSetPalette.


PATHOBJ_bCloseFigure

BOOL PATHOBJ_bCloseFigure (

PATHOBJ *ppo

);

PATHOBJ_bCloseFigure closes an open figure in a path by drawing a line from the current position to the first point of the figure.

Parameters

ppo
Points to a PATHOBJ structure that identifies the path to be closed.

Return Value

The return value is TRUE if the function is successful.


PATHOBJ_bEnum

BOOL PATHOBJ_bEnum(

IN PATHOBJ *ppo,

OUT PATHDATA *ppd

);

PATHOBJ_bEnum retrieves the next PATHDATA record from a specified path and enumerates the curves in the path.

Parameters

ppo
Points to a PATHOBJ whose curves and/or lines are to be enumerated.
ppd
Points to a PATHDATA structure that is to be filled.

Return Value

The return value is TRUE if the specified path contains more PATHDATA records, indicating that this service should be called again. Otherwise, if the output is the last PATHDATA record in the path, the return value is FALSE.

Comments

PATHOBJ_bEnum can be called only after a call to PATHOBJ_vEnumStart has been made.

A PATHDATA record describes all or part of a subpath (a connected part of a path). For example, a MoveTo call by the application within a path begins a new subpath.

See Also

PATHDATA, PATHOBJ, PATHOBJ_vEnumStart, PATHOBJ_vEnumStartClipLines


PATHOBJ_bEnumClipLines

BOOL PATHOBJ_bEnumClipLines(

IN PATHOBJ *ppo,

IN ULONG cj,

OUT CLIPLINE *pcl

);

PATHOBJ_bEnumClipLines enumerates clipped line segments from a given path. The enumeration must be started with PATHOBJ_vEnumStartClipLines before the driver makes this call.

Parameters

ppo
Points to the path containing the clipped line segments that are to be enumerated.
cj
Specifies the size of the output buffer, in bytes. GDI does not write beyond this point in the buffer. The value of this parameter must be large enough to hold a CLIPLINE structure with at least one RUN structured. The driver should allocate space for several RUNs.
pcl
Points to the buffer in which a CLIPLINE structure is returned. The structure contains the original unclipped control points for a line segment. (The correct pixels for the line cannot be computed without the original points.) RUNs, which describe sets of pixels along the line that are not clipped away, are written to this buffer.

If a clip region is complex, a single line segment can be broken into many RUNs. A segment is returned as many times as necessary to list all of its RUNs.

The CLIPLINE structure contains the starting and ending points of the original unclipped line and the line segments, or RUNs, of that line that are to appear on the display.

Return Value

The return value is TRUE if more line segments are to be enumerated, indicating that this service should be called again. Otherwise, it is FALSE, indicating that the returned segment is the last segment in the path.

See Also

CLIPLINE, PATHOBJ_vEnumStartClipLines, RUN


PATHOBJ_bMoveTo

BOOL PATHOBJ_bMoveTo(

IN PATHOBJ *ppo,

IN POINTFIX ptfx

);

PATHOBJ_bMoveTo sets the current position in a given path.

Parameters

ppo
Points to a PATHOBJ structure created by the driver.
ptfx
Points to a POINTFIX structure that specifies the new position.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

This function should only be called with PATHOBJ objects created by EngCreatePath.


PATHOBJ_bPolyBezierTo

BOOL PATHOBJ_bPolyBezierTo(

IN PATHOBJ *ppo,

IN POINTFIX *pptfx,

IN ULONG cptfx

);

PATHOBJ_bPolyBezierTo draws Bezier curves on a path.

Parameters

ppo
Points to the PATHOBJ created by the driver.
pptfx
Points to the array of control points. Each set of three control points, along with the preceding control point, or current position, determines a Bezier.
cptfx
Specifies the count of points in pptfx. Must be a multiple of three.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

PATHOBJ_bPolyBezierTo must be called only with a PATHOBJ created by EngCreatePath.

See Also

PATHOBJ, PATHOBJ_bPolylineTo


PATHOBJ_bPolyLineTo

BOOL PATHOBJ_bPolyLineTo(

IN PATHOBJ *ppo,

IN POINTFIX *pptfx,

IN ULONG cptfx

);

PATHOBJ_bPolyLineTo draws lines from the current position in a path through the specified points.

Parameters

ppo
Points to the PATHOBJ created by the driver.
pptfx
Points to an array of POINTFIX structures that define control points. The first line is drawn from the current position to the first point in this array; lines are then drawn to each subsequent point in the array.
cptfx
Specifies the count of points in pptfx. This is also the number of lines that will be added to the path.

Return Value

The return value is TRUE if the function is successful. Otherwise, it is FALSE, and an error code is logged.

Comments

PATHOBJ_bPolyLineTo should only be called with PATHOBJ objects created by EngCreatePath.

See Also

PATHOBJ, PATHOBJ_bPolyBezierTo


PATHOBJ_vEnumStart

VOID PATHOBJ_vEnumStart(

IN PATHOBJ *ppo

);

PATHOBJ_vEnumStart notifies a given PATHOBJ that the driver will be calling PATHOBJ_bEnum to enumerate lines and/or curves in the path.

Parameters

ppo
Points to a PATHOBJ structure whose lines and/or curves are to be enumerated.

Comments

PATHOBJ_vEnumStart can be called at any time to restart an enumeration.


PATHOBJ_vEnumStartClipLines

VOID PATHOBJ_vEnumStartClipLines(

IN PATHOBJ *ppo,

IN CLIPOBJ *pco,

IN SURFOBJ *pso,

IN LINEATTRS *pla

);

PATHOBJ_vEnumStartClipLines allows the driver to request lines to be clipped against a specified clipping region.

Parameters

ppo
Points to the PATHOBJ structure that describes the specified clipping object.
pco
Points to a CLIPOBJ structure that describes the clipping region.
pso
Points to a SURFOBJ structure that GDI queries to retrieve information about styling steps.
pla
Points to a LINEATTRS structure that GDI queries to retrieve line width and styling information.

Comments

This function is useful when the clip region is more complex than a simple rectangle.

PATHOBJ_vEnumStartClipLines performs calculations for cosmetic wide lines. If the LINEATTRS structure needs a cosmetic wide line, the enumeration walks the given path as many times as needed to complete the widened figure.

This function should not be called for geometric wide lines or paths that contain Bezier curves.

Once begun, this enumeration process should not be restarted.

See Also

CLIPOBJ, LINEATTRS, PATHOBJ, SURFOBJ


PATHOBJ_vGetBounds

VOID PATHOBJ_vGetBounds(

IN PATHOBJ *ppo,

OUT PRECTFX *prectfx

);

PATHOBJ_vGetBounds retrieves the bounding rectangle for the specified path.

Parameters

ppo
Points to a PATHOBJ structure that describes the path for which a bounding rectangle is to be calculated.
prectfx
Points to the address where the RECTFX structure is to be written. The returned rectangle is exclusive of the bottom and right edges. An empty rectangle is specified by setting all four RECTFX members to zero.


STROBJ_bEnum

BOOL STROBJ_bEnum(

IN STROBJ *pstro,

OUT ULONG *pc,

OUT PGLYPHOS **ppgpos

);

STROBJ_bEnum enumerates glyph identities and positions.

Parameters

pstro
Points to the STROBJ structure containing the GLYPHPOS information.
pc
Points to the count, returned by GDI, of GLYPHPOS structures.
ppgpos
Points to the array in which GDI will write the GLYPHPOS structures.

Return Value

The return value is TRUE if more glyphs remain to be enumerated, or FALSE if the enumeration is complete. The return value is DDI_ERROR if the glyphs cannot be enumerated, and an error code is logged.

Comments

A driver should download only the glyph handles if it caches fonts itself.

The information returned depends on the driver’s return value for DrvGetGlyphMode.

Bitmaps or outlines can also be obtained from FONTOBJ objects.

See Also

FONTOBJ_cGetGlyphs, GLYPHPOS, STROBJ, STROBJ_vEnumStart


STROBJ_dwGetCodePage

DWORD STROBJ_dwGetCodePage(

IN STROBJ *pstro

);

STROBJ_dwGetCodePage returns the code page associated with the specified STROBJ.

Parameters

pstro
Pointer to a STROBJ structure with which the code page is associated.

Return Value

STROBJ_dwGetCodePage returns a DWORD value that identifies the code page associated with the font used in the text output call at the Win32 API level.


STROBJ_vEnumStart

VOID STROBJ_vEnumStart(

IN STROBJ *pstro

);

STROBJ_vEnumStart defines the form, or type, for data that will be returned from GDI in subsequent calls to STROBJ_bEnum. This function also restarts the enumeration of the GLYPHPOS array.

Parameters

pstro
Points to the STROBJ structure whose data form is to be defined.

Comments

This function should be called by the driver prior to calling STROBJ_bEnum.


WNDOBJ_bEnum

BOOL WNDOBJ_bEnum(

WNDOBJ *pwo,

ULONG cj,

ULONG *pul

);

The WNDOBJ_bEnum callback function gets a batch of rectangles from the visible region of a window.

Parameters

pwo
Points to a WNDOBJ structure created by a call to EngCreateWnd.
cj
Specifies the size, in bytes, of the buffer pointed to by pul. GDI will not write beyond this limit.
pul
Points to the buffer in which a structure of the following form is to be written, where c is a count of the rectangles returned, and arcl is an array of rectangles:
typedef struct _ENUMRECTS{
    ULONG c;
    RECTL arcl[]
} ENUMRECTS;

Return Value

The return value is TRUE if there is more data to be enumerated and the driver should repeat the call. It is FALSE if the enumeration is complete.

Comments

The order of enumeration is determined by the call to WNDOBJ_cEnumStart.

A possible loop structure for calling this function follows.
do {
    bMore = WNDOBJ_bEnum(pwo, sizeof(buffer), &buffer.c);
    for (i = 0; i < buffer.c; i++) {
        //  Process the data
    }
} while (bMore);

WNDOBJ_bEnum should be called only by the callback function provided to GDI by the EngCreateWnd function, or by the DDI functions that are given a WNDOBJ.

See Also

EngCreateWnd, WNDOBJ, WNDOBJ_cEnumStart


WNDOBJ_cEnumStart

ULONG WNDOBJ_cEnumStart(

WNDOBJ *pwo,

ULONG iType,

ULONG iDirection,

ULONG cLimit

);

WNDOBJ_cEnumStart is a callback function that sets parameters for enumeration of rectangles in the visible region of a window.

Parameters

pwo
Points to a WNDOBJ structure created by a call to EngCreateWnd.
iType
Specifies the type of structures to be returned by WNDOBJ_bEnum. This parameter can be CT_RECTANGLES, meaning that the region is to be enumerated as a list of rectangles.
iDirection
Determines the order in which the rectangles are returned. This order can be essential when an overlapping DrvBitBlt is being performed on the same surface. If the order is not relevant to the device driver, then CD_ANY should be specified. This allows GDI to optimize its enumeration for complex regions. This parameter can be one of the following values:

Value

Meaning

CD_RIGHTDOWN

Left to right, top to bottom.

CD_LEFTDOWN

Right to left, top to bottom.

CD_RIGHTUP

Left to right, bottom to top.

CD_LEFTUP

Right to left, bottom to top.

CD_LEFTWARDS

Left to right, vertical direction is not defined.

CD_UPWARDS

Bottom to top, horizontal direction is not defined.

CD_ANY

Any order convenient for GDI.

cLimit
An indication of how many objects the driver is interested in caching. This is only used to decide when to stop counting rectangles while GDI is calculating the return value for this function. If cLimit is zero, counting is not done.

Return Value

The return value is a count of the number of objects that would be enumerated, provided this value is less than or equal to cLimit. If the count is greater than cLimit, the return value is 0xFFFFFFFF.

Comments

Enumeration can be restarted by calling this function again.

WNDOBJ_cEnumStart should be called only:

  • In the context of the driver callback function supplied to GDI in the EngCreateWnd function, or the DDI functions where a WNDOBJ is given.

  • When the calling thread has the device lock to ensure that no client region changes occur.

See Also

DrvBitBlt, EngCreateWnd, EngLockDriverObj, WNDOBJ, WNDOBJ_bEnum


WNDOBJ_vSetConsumer

VOID WNDOBJ_vSetConsumer(

WNDOBJ *pwo,

PVOID pvConsumer

);

The WNDOBJ_vSetConsumer callback function sets a driver-defined value in the pvConsumer field of the specified WNDOBJ.

Parameters

pwo
Points to a WNDOBJ structure created by a prior call to EngCreateWnd.
pvConsumer
A driver-defined value for identifying this particular WNDOBJ. This value overrides the previous value stored in the WNDOBJ.

Comments

WNDOBJ_vSetConsumer should be called only by the following functions:

  • DDI functions where a WNDOBJ is provided.

  • The callback provided to GDI by a driver’s call to EngCreateWnd.

  • The WNDOBJ_SETUP escape defined by DrvEscape after a new WNDOBJ is created.

See Also

EngCreateWnd, WNDOBJ


WNDOBJCHANGEPROC

VOID CALLBACK WNDOBJCHANGEPROC(

WNDOBJ *pwo,

FLONG fl

);

WNDOBJCHANGEPROC is a driver-defined callback function that GDI uses to notify the driver of changes to the window in question. The pfn parameter of the EngCreateWnd function points to this function.

Parameters

pwo
Points to a WNDOBJ structure that defines the currently changing window object. The window object contains the new size and position of the window. If the fl parameter is WOC_CHANGED, this parameter is null.
fl
This parameter can be one of the following values:

Value

Meaning

WOC_RGN_CLIENT_DELTA

The window object contains a delta client region. The delta region is valid for this call only.

WOC_RGN_CLIENT

The window object contains a new client region.

WOC_RGN_SURFACE_DELTA

The window object contains a delta surface region. The pvConsumer member of the WNDOBJ structure is zero. The delta region is valid for this call only.

WOC_RGN_SURFACE

The window object refers to a surface region created by GDI. The pvConsumer member of the WNDOBJ structure is zero.

WOC_CHANGED

All windows have been updated. GDI notifies the driver at the end of a desktop update.

WOC_DELETE

The window object is deleted as a result of the deletion of the window.

See Also

EngCreateWnd, WNDOBJ


XFORMOBJ_bApplyXform

BOOL XFORMOBJ_bApplyXform(

IN XFORMOBJ *pxo,

IN ULONG iMode,

IN ULONG cPoints,

IN PVOID pvIn,

OUT PVOID pvOut

);

XFORMOBJ_bApplyXform applies the given transform or its inverse to the given array of points.

Parameters

pxo
Points to a XFORMOBJ structure that defines the transform to be applied to the pvIn array.
iMode
Identifies the transform and the input and output data types. This parameter can be one of the following:

Value

Meaning

XF_LTOL

Applies the transform to POINTL structures to get POINTL structures.

XF_INV_LTOL

Applies the inverse of the transform to POINTL structures to get POINTL structures.

XF_LTOFX

Applies the transform to POINTL structures to get POINTFX structures.

XF_INV_FXTOL

Applies the inverse of the transform to POINTFX structures to get POINTL structures.

cPoints
Specifies the count of points in pvIn to be transformed.
pvIn
Points to an array of input points. The format of the points is specified by the iMode parameter.
pvOut
Points to the buffer that is to receive the transformed points. The iMode parameter specifies the format of the points.

Return Value

The return value is TRUE if all points were transformed without overflow. FALSE is returned if pxo, pvIn, or pvOut are null, or if overflow occurs during the transformation.

See Also

XFORMOBJ


XFORMOBJ_iGetFloatObjXform

ULONG XFORMOBJ_iGetFloatObjXform(

IN XFORMOBJ *pxo,

OUT FLOATOBJ_XFORM *pxform

);

XFORMOBJ_iGetFloatObjXform downloads a FLOATOBJ transform to the driver.

Parameters

pxo
Points to the XFORMOBJ structure that defines the transform to be downloaded.
pxform
Points to the buffer that is to receive the FLOATOBJ_XFORM structure. This parameter can be null.

Return Value

If an error occurs, the return value is DDI_ERROR. Otherwise, the return value is a complexity hint about the transform object. The value of this transform characterization can be one of the following:
Return Value | Meaning
GX_IDENTITY | Identity matrix; no translation offset.
GX_OFFSET | Identity matrix; there is a translation offset.
GX_SCALE | Off-diagonal matrix elements are zero.
GX_GENERAL | Arbitrary 2 x 2 matrix and offset.

Comments

If pxform is not null, XFORMOBJ_iGetFloatObjXform loads a FLOATOBJ_XFORM into it. This function allows graphics drivers to emulate floating point arithmetic. Windows NT does not support kernel-mode floating point operations on some systems.

See Also

FLOATOBJ, XFORMOBJ


XFORMOBJ_iGetXform

ULONG XFORMOBJ_iGetXform(

IN XFORMOBJ *pxo,

OUT XFORM *pxform

);

XFORMOBJ_iGetXform downloads a transform to the driver.

Parameters

pxo
Points to the XFORMOBJ structure that defines the transform to be downloaded to the driver.
pxform
Points to the buffer that is to receive the XFORM structure. This parameter can be null.

Return Value

If an error occurs, the return value is DDI_ERROR. Otherwise, the return value is a complexity hint about the transform object. The value of this transform characterization can be one of the following:
Value | Meaning
GX_IDENTITY | Identity matrix; no translation offset.
GX_OFFSET | Identity matrix; there is a translation offset.
GX_SCALE | Off-diagonal matrix elements are zero.
GX_GENERAL | Arbitrary 2 x 2 matrix and offset.

See Also

XFORMOBJ, XFORMOBJ_bApplyXform


XLATEOBJ_cGetPalette

ULONG XLATEOBJ_cGetPalette(

IN XLATEOBJ *pxlo,

IN ULONG iPal,

IN ULONG cPal,

IN ULONG *pPal

);

XLATEOBJ_cGetPalette retrieves RGB colors from the specified PAL_INDEXED palette.

Parameters

pxlo
Points to a XLATEOBJ structure.
iPal
Identifies the palette to be written. This parameter can be one of the following values:

Value

Meaning

XO_SRCPALETTE

Source palette

XO_DESTPALETTE

Destination palette

cPal
Specifies the number of entries in the buffer pointed to by pPal. This can be smaller than the total size of the palette.
pPal
Points to a buffer where the palette entries are to be written. Each entry is a 24-bit RGB value. Each RGB value is relative to the calibration data for the palette that specifies how a palette RGB value is related to a device-independent color space.

Return Value

XLATEOBJ_cGetPalette returns the number of palette entries written if pPal is not null. If pPal is null, the return value is the total number of entries in the palette. A value of zero is returned if XLATEOBJ is null or its palette is invalid.

Comments

The driver needs to know the palette to perform some methods of color blending.

See Also

XLATEOBJ


XLATEOBJ_iXlate

ULONG XLATEOBJ_iXlate(

IN XLATEOBJ *pxlo,

IN ULONG iColor

);

XLATEOBJ_iXlate translates a color index of the source palette to the closest index in the destination palette.

Parameters

pxlo
Points to a XLATEOBJ structure that defines the source palette.
iColor
Specifies the color index to be translated.

Return Value

The return value is an index into the destination palette if the function is successful. If the function fails, -1 is returned.


XLATEOBJ_piVector

ULONG *XLATEOBJ_piVector(

IN XLATEOBJ *pxlo

);

XLATEOBJ_piVector retrieves a translation vector that the driver can use to translate source indices to destination indices.

Parameters

pxlo
Points to a XLATEOBJ structure that defines the indexed source object.

Return Value

The return value is a pointer to a vector of translation entries if the function is successful. Otherwise, it is null, and an error code is logged.

Comments

This function can be used only if the source palette is an indexed palette.


Chapter 2 Graphic Driver Structures

Chapter 2 Graphics Driver Structures

Both GDI and the graphics driver create and maintain several structures as a means of defining objects, passing data, storing flag settings, etc. This topic describes these structures.


BRUSHOBJ

typedef struct _BRUSHOBJ {
    ULONG iSolidColor;
    PVOID pvRbrush;
} BRUSHOBJ;

A BRUSHOBJ structure contains two public members that describe a brush object. Brush objects are used to define fill patterns.

Members

iSolidColor
Specifies the color index of a solid brush. This index has been translated to the target surface’s palette. Drawing can proceed without realization of the brush. A value of 0xFFFFFFFF indicates that a nonsolid brush must be realized.
pvRbrush
Points to the driver’s realized brush.

Comments

Drivers can call BRUSHOBJ Xxx service routines to realize brushes or to find previously realized brushes.

If the iSolidColor member is 0xFFFFFFFF or the pvRbrush member is null, the driver must call the BRUSHOBJ_pvGetRbrush function to realize the brush.


CIECHROMA

typedef struct _CIECHROMA {
    LDECI4 x;
    LDECI4 y;
    LDECI4 Y;
} CIECHROMA;

The CIECHROMA structure is used to describe the chromaticity coordinates, x and y, and the luminance, Y in CIE color space. This structure is used by the COLORINFO structure to define colors for GDIINFO.

Members

x
Specifies the x coordinate in CIE chromaticity space.
y
Specifies the y coordinate in CIE chromaticity space.
Y
Specifies the luminance.

Comments

The LDECI4 type is used to represent real numbers to four decimal points. For example, (LDECI4) 10000 represents real number 1.0000, and (LDECI4) -12345 represents -1.2345.

Both x and y should be in the range zero to one. The Y member should be in the range 0 to 100.

See Also

COLORINFO, DrvEnablePDEV, GDIINFO


CLIPLINE

typedef struct _CLIPLINE {
    POINTFIX ptfxA;
    POINTFIX ptfxB;
    LONG lStyleState;
    ULONG c;
    RUN arun[1];
} CLIPLINE, *PCLIPLINE;

The CLIPLINE structure gives the driver access to a portion of a line between two clip regions used for drawing.

Members

ptfxA
Specifies the starting point of the line.
ptfxB
Specifies the end point of the line.
lStyleState
A pair of 16-bit values supplied by GDI whenever the driver calls PATHOBJ_bEnumClipLines. These two values are packed into a LONG and specify the style offset back to the first pixel of the line segment. This is the first pixel that would be rendered if the line were not clipped. This value allows the styling for the remainder of the line to be computed. Refer to Styled Cosmetic Lines for additional information.
c
Specifies the number of RUN structures in the arun array.
arun
An array of RUN structures. The RUN structures describe the start and stop portions of the clip line.

Comments

The CLIPLINE structure is used by PATHOBJ_bEnumCliplines. The CLIPLINE structure contains the original, unclipped control points of the line segment.

See Also

CLIPOBJ, GDIINFO, RUN


CLIPOBJ

typedef struct _CLIPOBJ {
    ULONG iUniq;
    RECTL rclBounds;
    BYTE iDComplexity;
    BYTE iFComplexity;
    BYTE iMode;
    BYTE fjOptions;
} CLIPOBJ;

The CLIPOBJ structure describes the clip region used when drawing. This region can be enumerated as a series of rectangles.

Members

iUniq
Specifies a value that uniquely identifies the clip region. If iUniq is nonzero, the driver uses it as a cache identifier. This allows the driver to recognize a region after downloading and caching it. If the value is zero, the driver should not cache the region because the region will not be used again.
rclBounds
Specifies a rectangle that bounds the part of the region that intersects the drawing. If iDComplexity is DC_RECT, then this is the clipping rectangle to be considered.
iDComplexity
Specifies the complexity of the part of the region that intersects with the drawing. This member must be one of the following values:

Value

Meaning

DC_TRIVIAL

Clipping need not be considered; draw the whole figure.

DC_RECT

Clip to a single rectangle.

DC_COMPLEX

The clip region must be enumerated.

iFComplexity
Specifies the complexity of the whole region. This value is used by the driver in deciding whether or not to cache the region. CLIPOBJ_cEnumStart can be called to determine the exact number of rectangles in the region. This member can be one of the following values:

Value

Meaning

FC_RECT

The region is a single rectangle.

FC_RECT4

The region consists of, at most, four rectangles.

FC_COMPLEX

The region consists of more than four rectangles.

iMode
Specifies how the region is stored by GDI. This can help the driver determine how to enumerate the region. This member can be one of the following values:

Value

Meaning

TC_RECTANGLES

The region is stored as rectangles.

TC_PATHOBJ

The region is stored as a path.

fjOptions
Specifies clipping options. This member can be the following value:

Value

Meaning

OC_BANK_CLIP

Indicates an engine callback for a banked device.

See Also

CLIPOBJ_bEnum, CLIPOBJ_cEnumStart


COLORINFO

typedef struct _COLORINFO {
    CIECHROMA Red;
    CIECHROMA Green;
    CIECHROMA Blue;
    CIECHROMA Cyan;
    CIECHROMA Magenta;
    CIECHROMA Yellow;
    CIECHROMA AlignmentWhite;
    LDECI4 RedGamma;
    LDECI4 GreenGamma;
    LDECI4 BlueGamma;
    LDECI4 MagentaInCyanDye;
    LDECI4 YellowInCyanDye;
    LDECI4 CyanInMagentaDye;
    LDECI4 YellowInMagentaDye;
    LDECI4 CyanInYellowDye;
    LDECI4 MagentaInYellowDye;
} COLORINFO, *PCOLORINFO;

Members

Red, Green, Blue, Cyan, Magenta, Yellow, AlignmentWhite
CIECHROMA structures defining the x, y, and Y (luminance) coordinates of the named color.
RedGamma, GreenGamma, BlueGamma
The gamma correction of display devices that permits the display device to display colors between the primary colors with accuracy. Their values should be between 0 and 6.5535.
MagentaInCyanDye, YellowInCyanDye, CyanInMagentaDye, YellowInMagentaDye, CyanInYellowDye, MagentaInYellowDye
Used for printing devices to describe color purity and concentration. Values should be between zero and one.

Comments

The LDECI4 type is used to represent real numbers to four decimal points. For example, (LDECI4) 10000 represents real number 1.0000, and (LDECI4) -12345 represents -1.2345.

Any values in the COLORINFO structure that are out of the specified range default to the NTSC values.

See Also

CIECHROMA, DrvEnablePDEV, GDIINFO


DEVINFO

typedef struct tagDEVINFO {
    FLONG flGraphicsCaps;
    LOGFONTW lfDefaultFont;
    LOGFONTW lfAnsiVarFont;
    LOGFONTW lfAnsiFixFont;
    ULONG cFonts;
    ULONG iDitherFormat;
    USHORT cxDither;
    USHORT cyDither;
    HPALETTE hpalDefault;
} DEVINFO, *PDEVINFO;

The DEVINFO structure is used by the DrvEnablePDEV driver-supplied function to describe the driver and the PDEV. The driver should set only the members that are relevant to it. This structure is zero-initialized by GDI before DrvEnablePDEV is called. Applications do not have direct access to this structure.

Members

flGraphicsCaps
Flags that describe graphics capabilities of the device driver. These flags are defined as follows:

Flag

Meaning

GCAPS_BEZIERS

Handles Bezier curves.

GCAPS_GEOMETRICWIDE

Handles geometric widening.

GCAPS_ALTERNATEFILL

Handles alternating fills.

GCAPS_WINDINGFILL

Handles winding mode fills.

GCAPS_HALFTONE

Handles halftoning.

GCAPS_COLOR_DITHER

Handles color dithering to a PDEV-comptible surface.

GCAPS_HORIZSTRIKE

Handles horizontal strikeouts in DrvTextOut.

GCAPS_VERTSTRIKE

Handles vertical strikeouts in DrvTextOut.

GCAPS_OPAQUERECT

Handles opaque rectangles in DrvTextOut.

GCAPS_VECTORFONT

Handles stroking of vector fonts in DrvTextOut.

GCAPS_MONO_DITHER

Handles monochrome dithering.

GCAPS_ASYNCCHANGE

Handles changes to the hardware pointer shape.

GCAPS_ASYNCMOVE

Moves the hardware pointer when a thread is performing graphical operations.

GCAPS_DONTJOURNAL

Disallows journaling to this driver. This is only valid for printer DCs and will generally result in slower return-to-application time when printing.

GCAPS_ARBRUSHOPAQUE

Supports arbitrary brush for text opaque rectangle.

GCAPS_HIGHRESTEXT

Indicates that the driver is requesting glyph positions as returned by the STROBJ in FIX point coordinates.

GCAPS_PALMANAGED

Supports palette management.

GCAPS_DITHERONREALIZE

Specifies that GDI can call DrvRealizeBrush with the RGB to be dithered directly.

GCAPS_FORCEDITHER

Allows dithering on all geometric pens.

lfDefaultFont
An Extended Logical Font structure that specifies the default font for a device.
lfAnsiVarFont
An Extended Logical Font structure that specifies the default variable-pitch font for a device.
lfAnsiFixFont
An Extended Logical Font structure that specifies the default fixed-pitch (monospaced) font for a device.
cFonts
Specifies the number of device fonts. GDI assumes that the device can draw text with this number of fonts on its own surfaces and that the driver can provide metrics information about the fonts. If the driver sets cFonts to -1, GDI will wait until fonts are needed to query the driver for the actual number of fonts it supports in a call to DrvQueryFont.
iDitherFormat
Specifies the format of the bitmap. This parameter indicates how many bits of color information per pixel are requested, and must be one of the following values:

Value

Meaning

BMF_1BPP

Monochrome.

BMF_4BPP

4 bits per pixel.

BMF_8BPP

8 bits per pixel.

BMF_16BPP

16 bits per pixel.

BMF_24BPP

24 bits per pixel.

BMF_32BPP

32 bits per pixel.

BMF_4RLE

4 bits per pixel, run length encoded.

BMF_8RLE

8 bits per pixel, run length encoded.

cxDither and cyDither
Specifies the dimensions of a dithered brush. If these members are nonzero, then the device can create a dithered brush for a given RGB color.
hpalDefault
Specifies the default palette for the device. The palette should be created by calling EngCreatePalette. The driver associates a palette with a device by returning this handle to GDI.

See Also

DrvEnablePDEV, DrvQueryFont, DrvRealizeBrush, DrvTextOut


DEVMODEW

typedef struct _DEVMODEW {
    WCHAR  dmDeviceName[CCHDEVICENAME];
    WORD   dmSpecVersion;
    WORD   dmDriverVersion;
    WORD   dmSize;
    WORD   dmDriverExtra;
    DWORD  dmFields;
    short  dmOrientation;
    short  dmPaperSize;
    short  dmPaperLength;
    short  dmPaperWidth;
    short  dmScale;
    short  dmCopies;
    short  dmDefaultSource;
    short  dmPrintQuality;
    short  dmColor;
    short  dmDuplex;
    short  dmYResolution;
    short  dmTTOption;
    short  dmCollate;
    WCHAR  dmFormName[CCHFORMNAME];
    WORD   dmLogPixels;
    DWORD  dmBitsPerPel;
    DWORD  dmPelsWidth;
    DWORD  dmPelsHeight;
    DWORD  dmDisplayFlags;
    DWORD  dmDisplayFrequency;
} DEVMODEW;

The DEVMODEW structure contains information about the device initialization and environment of a printer.

Members

dmDeviceName
Specifies the name of the device the driver supports; for example, “PCL/HP LaserJet” in the case of PCL/HP LaserJet®. If the name is greater than CCHDEVICENAME characters in length, the spooler truncates it to fit in the array.
dmSpecVersion
Specifies the version number of the initialization data specification on which the structure is based.
dmDriverVersion
Specifies the printer driver version number assigned by the printer driver developer.
dmSize
Specifies the size, in bytes, of the DEVMODEW structure minus the device-specific dmDriverData member. If an application manipulates only the driver-independent portion of the data, it can use this member to determine the size of the structure without having to account for different versions.
dmDriverExtra
Specifies the number of bytes of private driver data that follow this structure. If a device driver does not use device-specific information, this member should be set to zero.
dmFields
Specifies which of the remaining members in the DEVMODEW structure have been initialized. Bit zero (defined as DM_ORIENTATION) specifies dmOrientation; bit one (defined as DM_PAPERSIZE) specifies dmPaperSize, and so on. A printer driver should support only those members that are appropriate for the printer technology.
dmOrientation
Specifies the paper orientation. This member can be either DMORIENT_PORTRAIT or DMORIENT_LANDSCAPE.
dmPaperSize
Specifies the size of the paper to be printed on. This member can be zero if the length and width of the paper are both specified by the dmPaperLength and dmPaperWidth members. Otherwise, the dmPaperSize member can be set to one of the following predefined values:

Value

Meaning

DMPAPER_LETTER

Letter, 8-1/2 by 11 in.

DMPAPER_LEGAL

Legal, 8-1/2 by 14 in.

DMPAPER_A4

A4 Sheet, 210 by 297 mm.

DMPAPER_CSHEET

C Sheet, 17 by 22 in.

DMPAPER_DSHEET

D Sheet, 22 by 34 in.

DMPAPER_ESHEET

E Sheet, 34 by 44 in.

DMPAPER_LETTERSMALL

Letter Small, 8-1/2 by 11 in.

DMPAPER_TABLOID

Tabloid, 11 by 17 in.

DMPAPER_LEDGER

Ledger, 17 by 11 in.

DMPAPER_STATEMENT

Statement, 5-1/2 by 8-1/2 in.

DMPAPER_EXECUTIVE

Executive, 7-1/4 by 10-1/2 in.

DMPAPER_A3

A3 sheet, 297 by 420 mm.

DMPAPER_A4SMALL

A4 small sheet, 210 by 297 mm.

DMPAPER_A5

A5 sheet, 148 by 210 mm.

DMPAPER_B4

B4 sheet, 250 by 354 mm.

DMPAPER_B5

B5 sheet, 182 by 257 mm.

DMPAPER_FOLIO

Folio, 8-1/2 by 13 in.

DMPAPER_QUARTO

Quarto, 215 by 275 mm.

DMPAPER_10X14

10 by 14 in.

DMPAPER_11X17

11 by 17 in.

DMPAPER_NOTE

Note, 8-1/2 by 11 in.

DMPAPER_ENV_9

#9 Envelope, 3-7/8 by 8-7/8 in.

DMPAPER_ENV_10

#10 Envelope, 4-1/8 by 9-1/2 in.

DMPAPER_ENV_11

#11 Envelope, 4-1/2 by 10-3/8 in.

DMPAPER_ENV_12

#12 Envelope, 4-3/4 by 11 in.

DMPAPER_ENV_14

#14 Envelope, 5 by 11-1/2 in.

DMPAPER_ENV_DL

DL Envelope, 110 by 220 mm.

DMPAPER_ENV_C5

C5 Envelope, 162 by 229 mm.

DMPAPER_ENV_C3

C3 Envelope, 324 by 458 mm.

DMPAPER_ENV_C4

C4 Envelope, 229 by 324 mm.

DMPAPER_ENV_C6

C6 Envelope, 114 by 162 mm.

DMPAPER_ENV_C65

C65 Envelope, 114 by 229 mm.

DMPAPER_ENV_B4

B4 Envelope, 250 by 353 mm.

DMPAPER_ENV_B5

B5 Envelope, 176 by 250 mm.

DMPAPER_ENV_B6

B6 Envelope, 176 by 125 mm.

DMPAPER_ENV_ITALY

Italy Envelope, 110 by 230 mm.

DMPAPER_ENV_MONARCH

Monarch Envelope, 3-7/8 by 7-1/2 in.

DMPAPER_ENV_PERSONAL

6-3/4 Envelope, 3-5/8 by 6-1/2 in.

DMPAPER_FANFOLD_US

US Std Fanfold, 14-7/8 by 11 in.

DMPAPER_FANFOLD_STD_GERMAN

German Std Fanfold, 8-1/2 by 12 in.

DMPAPER_FANFOLD_LGL_GERMAN

German Legal Fanfold, 8-1/2 by 13 in.

dmPaperLength
Specifies the length of the paper. This value overrides the length of the paper specified by the dmPaperSize member. This might be necessary if the paper is of a custom size, or the device is a dot matrix printer, which can print a page of arbitrary length. This value, along with all values in this structure that specify a physical length, is specified in units of 1/10 of a millimeter.
dmPaperWidth
Specifies the width of the paper. This value overrides the width of the paper specified by the dmPaperSize member. This value is specified in units of 1/10 of a millimeter.
dmScale
Specifies the factor by which the printed output is to be scaled. The apparent page size is scaled from the physical page size by a factor of dmScale/100. For example, a letter-sized page with a dmScale value of 50 contains as much data as a page of 17 by 22 inches because the output text and graphics are half their original height and width.
dmCopies
Specifies the number of copies printed if the device supports multiple copies.
dmDefaultSource
Reserved; must be zero.
dmPrintQuality
Specifies the printer resolution. There are four predefined device-independent values:
DMRES_HIGH
DMRES_MEDIUM
DMRES_LOW
DMRES_DRAFT
If a positive value is given, it specifies the number of dots per inch (DPI) and is therefore device-dependent.
dmColor
Specifies whether a color printer should print color or monochrome. This member can be one of the following values:

DMCOLOR_COLOR
DMCOLOR_MONOCHROME

dmDuplex
Specifies duplex (double-sided) printing for duplex-capable printers. This member can be one of the following values:

DMDUP_SIMPLEX
DMDUP_HORIZONTAL
DMDUP_VERTICAL

dmYResolution
Specifies the y resolution of the printer, in DPI. If the printer initializes this member, the dmPrintQuality member specifies the x resolution of the printer, in DPI.
dmTTOption
Specifies how TrueType® fonts should be printed. This member can be one of the following values:

Value

Meaning

DMTT_BITMAP

Prints TrueType fonts as graphics. This is the default for dot-matrix printers.

DMTT_DOWNLOAD

Downloads TrueType fonts as soft fonts. This is the default for Hewlett-Packard printers that use Printer Control Language (PCL).

DMTT_SUBDEV

Substitute device fonts for TrueType fonts. This is the default for PostScript® printers.

dmCollate
Specifies whether collation should be used when printing multiple copies. This member can be be one of the following values:

Value

Meaning

DMCOLLATE_TRUE

Collate when printing multiple copies.

DMCOLLATE_FALSE

Do not collate when printing multiple copies.

Specifying DMCOLLATE_FALSE provides faster output because the data is sent to a printer only once, no matter how many copies are required.

dmFormName
Specifies the name of the form to use; for example, “Letter” or “Legal”. A complete set of names can be retrieved using the EnumForms function.
dmLogPixels
Specifies the number of log pixels in the x direction. This member should be equal to the ulLogPixels member of the GDIINFO structure.
dmBitsPerPel
Specifies the color resolution of the display device, in bits per pixel. For example: 4 for 16 colors, 8 for 256 colors, or 16 for 65536 colors.
dmPelsWidth
Specifies the width in pixels of the visible device surface.
dmPelsHeight
Specifies the height in pixels of the visible device surface.
dmDisplayFlags
Specifies the device’s display mode. The following are valid flags:

Value

Meaning

DM_GRAYSCALE

Specifies that the display is not a color device. If this flag is not set, color is assumed.

DM_INTERLACED

Specifies that the display mode is interlaced. If the flag is not set, noninterlaced is assumed.

dmDisplayFrequency
Specifies the frequency, in hertz, of the display device in its current mode.

Comments

This structure is an extension of the Windows 3.1 DEVMODE structure.

A device driver’s private data follows the dmDisplayMode member. The number of bytes of private data is specified by the dmDriverExtra member.

An application can set several of these members, like dmCopies and dmOrientation, that are common among several drivers. This allows an application to make simple mode selections without calling the driver-supplied DrvDeviceModes function.

The DEVMODEW structure is followed immediately by dmDriverExtra bytes of driver data. This data is set only by DrvGetModes, and is therefore driver-defined. The driver should not assume that it can calculate the size of the DEVMODEW structure, but instead should use the size specified by the dmSize member.

The driver data begins at the address pointed to by:

PVOID pvDriverData = (PVOID) (((BYTE *) pdm) + (pdm->dmSize));

See Also

DrvGetModes, GDIINFO


DRIVEROBJ

typedef struct _DRIVEROBJ {
    PVOID pvObj;
    FREEOBJPROC pFreeProc;
    HDEV hdev;
} DRIVEROBJ;

The DRIVEROBJ structure is used to track a resource allocated by a driver that wants to use GDI services. A DRIVEROBJ allows a display driver to request the GDI service in managing per-process resources. By creating a DRIVEROBJ, a display driver can insure that resources will be released when an application terminates.

Members

pvObj
Points to the driver resource that will be tracked by the DRIVEROBJ. The resource is associated with the current client process.
pFreeProc
Points to a driver-supplied callback function that frees the resource pointed to by pvObj.
hdev
The GDI handle of the physical device associated with the object.

Comments

Some drivers, in their Escape support, allocate resources on behalf of applications. In such cases, the DRIVEROBJ structure provides a means for the application to notify the driver when it terminates. GDI will call the driver’s cleanup function for each DRIVEROBJ allocated in an application’s context that is not deleted before the application terminates.

This structure provides a locking mechanism for exclusive access to the associated resource.

See Also

EngCreateDriverObj, EngDeleteDriverObj, EngLockDriverObj, EngUnlockDriverObj


DRVENABLEDATA

typedef struct tagDRVENABLEDATA {
    ULONG iDriverVersion;
    ULONG c;
    DRVFN *pdrvfn;
} DRVENABLEDATA, *PDRVENABLEDATA;

The DRVENABLEDATA structure contains a pointer to an array of DRVFN structures and the DDI version number of the structures.

Members

iDriverVersion
Specifies the DDI version number for which the driver is targeted, such as 0x00010000.
c
Specifies the number of DRVFN structures in the buffer pointed to by the pdrvfn member.
pdrvfn
Points to a buffer containing an array of DRVFN structures.

See Also

DrvEnableDriver


DRVFN

typedef struct _DRVFN {
    ULONG iFunc;
    PFN pfn;
} DRVFN, *PDRVFN;

The DRVFN structure is used to provide an index to a function’s address. It is used by calls to the driver-supplied DrvEnableDriver function.

Members

iFunc
Specifies the function’s index. Indices are defined in winddi.h; the index names reflect the related function names. For example, the index that specifies the DrvEnablePDEV function is INDEX_DrvEnablePDEV.
pfn
Specifies the calling address of the function.

See Also

DrvEnableDriver


ENG_TIME_FIELDS

typedef struct _ENG_TIME_FIELDS {
    USHORT		usYear;
    USHORT		usMonth;
    USHORT		usDay;
    USHORT		usHour;
    USHORT		usMinute;
    USHORT		usSecond;
    USHORT		usMilliseconds;
    USHORT		usWeekday;
} ENG_TIME_FIELDS, *PENG_TIME_FIELDS;

ENG_TIME_FIELDS is the structure in which EngQueryLocalTime returns the local time. The driver is responsible for allocating the structure and passing its pointer to EngQueryLocalTime.

Members

usYear
Specifies the current calendar year. The range is [1601,…].
usMonth
Specifies the current calendar month. The range is [1,12].
usDay
Specifies the current calendar day. The range is [1,31].
usHour
Specifies the current hour. The range is [0,23].
usMinute
Specifies the current minute. The range is [0,59].
usSecond
Specifies the current second. The range is [0,59].
usMilliseconds
Specifies the current millisecond. The range is [0,999].
usWeekday
Specifies the current day. The range is [0,6], where 0 is Sunday and 6 is Saturday.


ENUMRECTS

typedef struct _ENUMRECTS {
    ULONG c;
    RECTL arcl[1];
} ENUMRECTS;

The ENUMRECTS structure is used by the CLIPOBJ_cEnumStart function to provide information about rectangles in a clipping region for the CLIPOBJ_bEnum function.

Members

c
Specifies the number of RECTL structures in the arcl array.
arcl
An array of RECTL structures that specify the coordinates of rectangles in the clipping region.


FD_DEVICEMETRICS

typedef struct _FD_DEVICEMETRICS {
    FLONG flRealizedType;
    POINTE pteBase;
    POINTE pteSide;
    LONG lD;
    FIX fxMaxAscender;
    FIX fxMaxDescender;
    POINTL ptlUnderline1;
    POINTL ptlStrikeout;
    POINTL ptlULThickness;
    POINTL ptlSOThickness;
    ULONG cxMax;
    ULONG cyMax;
    ULONG cjGlyphMax;
    FD_XFORM fdxQuantized;
    LONG lNonLinearExtLeading;
    LONG lNonLinearIntLeading;
    LONG lNonLinearMaxCharWidth;
    LONG lNonLinearAvgCharWidth;
    LONG alReserved[4];
} FD_DEVICEMETRICS, *PFD_DEVICEMETRICS;

The FD_DEVICEMETRICS structure is used to provide device-specific font information to GDI if the iMode parameter of the driver-supplied DrvQueryFontData function is QFD_MAXEXTENTS.

Members

flRealizedType
A combination of accelerator flags. This value can be a combination of the following values:

Flag

Meaning

FDM_TYPE_BM_SIDE_CONST

An accelerator for horizontal and vertical writing. If this flag is set, the font has constant height for all bitmaps. In the horizontal case, this means that the cy dimension is constant; in the vertical case, this means that the cx dimension is constant. This accelerator is not used for outlines.

FDM_TYPE_MAXEXT_EQUAL_BM_SIDE

This flag can be set only if FDM_TYPE_BM_SIDE_CONST is also set. If set, the font height (as defined above for horizontal and vertical writing) is equal to the sum of max ascender and max descender. This accelerator is not used for outlines.

FDM_TYPE_CHAR_INC_EQUAL_BM_BASE

An accelerator for horizontal and vertical writing. In the horizontal case, if this flag is set, each glyph’s advance width is equal to the cx dimension of the glyph bitmap; in the vertical case, if this flag is set, each glyph’s advance width is equal to the cy dimension of the glyph bitmap. This accelerator is not used for outlines.

FDM_TYPE_ZERO_BEARINGS

If set, the a and c spacing is zero for all glyphs.

FDM_TYPE_CONST_BEARINGS

If set, the a and c spacing is constant for all glyphs.

pteBase
Specifies the notional space unit vector along the font’s baseline, transformed to device space and then normalized.
pteSide
Specifies a notional space unit vector perpendicular to the font’s baseline in the direction of the ascender, transformed to device space and then normalized. In notional space, baseline and ascender directions must be orthogonal, but in device space, pteBase and pteSide might or might not be orthogonal, depending on the Notional to Device space transform.
lD
Specifies the advance width if the font is a fixed pitch (monospaced) font. If the font is a variable pitch font, this member should be set to zero.
fxMaxAscender
Specifies the hinted maximum ascender height for this font instance, measured along pteSide.
fxMaxDescender
Specifies the hinted maximum descender height for this font instance, measured along pteSide.
ptlUnderline
Specifies the hinted underline position for this font instance, relative to the glyph’s character origin.
ptlStrikeout
Specifies the hinted strikeout position for this font instance, relative to the glyph’s character origin.
ptlULThickness
Specifies the hinted underline thickness for this font instance. This vector defines the side of the rectangle used to draw the underline. The base is implicitly defined by the baseline.
ptlSOThickness
Specifies the hinted strikeout thickness for this font instance. This vector defines the side of the rectangle used to draw the strikeout. The base is implicitly defined by the baseline.
cxMax
Specifies the hinted maximum glyph bitmap width, in pixels, for this font instance. Not used for outlines.
cyMax
Specifies the hinted maximum glyph bitmap height, in pixels, for this font instance. Not used for outlines.
cyGlyphMax
Specifies the hinted maximum size of a glyph, in pixels, for this font instance.
fdxQuantized
The font driver fills in the font transfomation that is actually used in the realization of the font. This may differ from the transformation requested by GDI as defined by the FONTOBJ__pxoGetXform().
lNonLinearExtLeading
Nonlinear external leading in 28.4 device units.
lNonLinearIntLeading
Nonlinear internal leading in 28.4 device units.
lNonLinearMaxCharWidth
Nonlinear maximum character increment in 28.4 device units.
lNonLinearAvgCharWidth
Nonlinear average character width in 28.4 device units.
alReserved[4]
Not used.

Comments

On input to the font provider, GDI sets all DWORD fields to 0xffffffff. The font provider is expected to overwrite a field if the corresponding quantity does not scale linearly under realization.

See Also

DrvQueryFontData


FD_GLYPHSET

typedef struct _FD_GLYPHSET {
    ULONG cjThis;
    FLONG flAccel;
    ULONG cGlyphsSupported;
    ULONG cRuns;
    WCRUN awcrun[1];
} FD_GLYPHSET, *PFD_GLYPHSET;

The FD_GLYPHSET structure is used to define the mappings from Unicode characters to glyph handles.

Members

cjThis
Specifies the size, in bytes, of the structure, including the array of WCRUN structures.
flAccel
Specifies accelerator flags. This member must be the following value:

Flags

Meaning

GS_UNICODE_HANDLES

For all runs in this GLYPHSET, the handle is obtained by zero extending the Unicode code point.

cGlyphsSupported
Specifies the total number of glyphs in all runs.
cRuns
Specifies the number of RUN structures in the awcrun array.
awcrun
An array of WCRUN structures.

See Also

DrvQueryFontTree, WCRUN


FD_KERNINGPAIR

typedef struct _FD_KERNINGPAIR {
    WCHAR wcFirst;
    WCHAR wcSecond;
    FWORD fwdKern;
} FD_KERNINGPAIR;

The FD_KERNINGPAIR structure is used to store information about kerning pairs.

Members

wcFirst
Specifies the code point of the first character in the kerning pair.
wcSecond
Specifies the code point of the second character in the kerning pair.
fwdKern
Specifies the kerning value, in font (notional) units, for the kerning pair. If this value is greater than zero, the characters will be moved apart; otherwise, the characters will be moved together.

Comments

An array of FD_KERNINGPAIR structures must be zero-terminated, which means the last FD_KERNINGPAIR structure in the array has all structure members set to zero. An array of FD_KERNINGPAIR structures must be sorted in increasing order according to an unsigned 32-bit key, calculated as follows:

    wcFirst + 65536 * wcSecond.

See Also

DrvQueryFontTree


FLOATOBJ

A FLOATOBJ structure is used to emulate a floating point number. This structure, in conjunction with the FLOATOBJ_Xxx service routines, allows graphics drivers to emulate floating point arithmetic in the NT kernel. Floating point arithmetic is not otherwise supported in the Windows NT kernel code.

There are no public members in the FLOATOBJ structure.

See Also

FLOATOBJ_Add, FLOATOBJ_AddFloat, FLOATOBJ_AddLong, FLOATOBJ_Div, FLOATOBJ_DivFloat, FLOATOBJ_DivLong, FLOATOBJ_Equal, FLOATOBJ_EqualLong, FLOATOBJ_GetFloat, FLOATOBJ_GetLong, FLOATOBJ_GreaterThan, FLOATOBJ_GreaterThanLong, FLOATOBJ_LessThan, FLOATOBJ_LessThanLong, FLOATOBJ_Mul, FLOATOBJ_MulFloat, FLOATOBJ_MulLong, FLOATOBJ_Neg, FLOATOBJ_SetFloat, FLOATOBJ_SetLong, FLOATOBJ_Sub, FLOATOBJ_SubFloat, FLOATOBJ_SubLong, FLOATOBJ_XFORM, XFORMOBJ_iGetFloatObjXform


FLOATOBJ_XFORM

typedef struct {
    FLOATOBJ eM11;
    FLOATOBJ eM12;
    FLOATOBJ eM21;
    FLOATOBJ eM22;
    FLOATOBJ eDx;
    FLOATOBJ eDy;
} FLOATOBJ_XFORM, *PFLOATOBJ_XFORM;

The FLOATOBJ_XFORM structure describes an arbitrary linear two-dimensional transform, such as for geometric wide lines.

Members

eM11, eM12, eM21, eM22
The four elements that comprise a 2x2 row-major matrix.
eDx, eDy
The x- and y-translation components of the transform.

Comments

All elements are specified as FLOATOBJ values. The transform can be downloaded to the driver. Structure members can be operated on by the FLOATOBJ_Xxx routines.

See Also

FLOATOBJ, XFORMOBJ_iGetXform


FONTINFO

typedef struct {
    ULONG cjThis;
    FLONG flCaps;
    ULONG cGlyphsSupported;
    ULONG cjMaxGlyph1;
    ULONG cjMaxGlyph4;
    ULONG cjMaxGlyph8;
    ULONG cjMaxGlyph32;
} FONTINFO, *PFONTINFO;

The FONTINFO structure contains information regarding a specific font. GDI writes and returns this structure via FONTOBJ_vGetInfo.

Members

cjThis
Size of this FONTINFO structure in BYTEs.
flCaps
Capabilities flags. The allowed flags are FO_DEVICE_FONT and FO_OUTLINE_CAPABLE.
cGlyphsSupported
Number of glyphs in the font.
cjMaxGlyph1
Size of the largest glyph in 1 bit/pixel. A nonzero value implies that 1-bit-per-pixel bitmaps are supported.
cjMaxGlyph4
Size of the largest glyph in 4 bit/pixel. A nonzero value implies that 4-bit-per-pixel bitmaps are supported.
cjMaxGlyph8
Size of the largest glyph in 8 bit/pixel. A nonzero value implies that 8-bit-per-pixel bitmaps are supported.
cjMaxGlyph32
Size of the largest glyph in 32 bit/pixel. A nonzero value implies that 32-bit-per-pixel bitmaps are supported.

See Also

FONTOBJ_vGetInfo


FONTOBJ

typedef struct _FONTOBJ {
    ULONG iUniq;
    ULONG iFace;
    ULONG cxMax;
    FLONG flFontType;
    ULONG iTTUniq;
    ULONG iFile;
    SIZE sizLogResPpi;
    ULONG ulStyleSize;
    PVOID pvConsumer;
    PVOID pvProducer;
} FONTOBJ;

The FONTOBJ structure is used to give a driver access to information about a particular instance of a font. As an accelerator, the driver is allowed to access the public members of the FONTOBJ structure.

Members

iUniq
Specifies a distinct realization of the font. This value can be used by the driver to identify a GDI font that it might have cached or to identify a driver’s realization of its own font. If this member is zero for a GDI font, the font should not be cached.
iFace
Specifies the device index for a device font, which was registered by a call to DrvQueryFont. If the font is a GDI font, this member has meaning only to GDI, and the driver should ignore it.
cxMax
Specifies the width, in pixels, of the largest glyph in the specified font.
flFontType
A value specifying the type of the font. This member can be a combination of the following flags:

Flag

Meaning

RASTER_FONTTYPE

Bitmap font.

DEVICE_FONTTYPE

Device-specific font.

TRUETYPE_FONTTYPE

TrueType font.

FO_SIM_ITALIC

Driver-simulated italic font.

FO_SIM_BOLD

Driver-simulated bold font.

FO_EM_HEIGHT

TrueType driver internal flag.

FO_4_LEVEL

Four-level antialiased font.

FO_16_LEVEL

Sixteen-level antialiased font.

FO_64_LEVEL

Sixty-four-level antialiased font.

If the RASTER_FONTYPE flag is set, the glyphs written to the specified STROBJ structure are bitmaps, otherwise they are pointers to PATHOBJ structures. If the glyph images are returned in the form of PATHOBJ structures, the driver must inspect the FM_INFO_TECH_STROKE flag of the flInfo member of the associated IFIMETRICS structure. If that flag is set, the paths should be stroked, otherwise the paths must be filled using the alternating mode convention.

If the FO_16_LEVEL bit is set, the associated glyph’s bitmaps are 4-bits-per-pixel. If this bit is not set, the glyph’s bitmaps are monochrome.

iTTUniq
Specifies the associated TrueType file. Two separate point size realizations of a TrueType font face will have FONTOBJ structures that share the same iTTUniq value, but will have different iUniq values. Only TrueType font types can have a nonzero iTTUniq member. For more information see flFontType.
iFile
The identifier returned for device fonts already loaded. If the font is a GDI font, then this member is used internally to identify the font and should be ignored.
sizLogResPpi
Specifies the resolution of the device for which this font is realized.
ulStyleSize
Specifies the style size of the font instance, in points.
pvConsumer
A consumer is a driver that accepts glyph information as input for generating text output. Only a font consumer can modify this member. The consumer of this font can store any information in the location pointed to by this member. This member should be used to store a pointer to consumer-allocated data associated with this font instance. The engine will not modify this member. The pvConsumer member is guaranteed to be null the first time a FONTOBJ structure is passed to the consumer.
pvProducer
A producer is a driver that can produce glyph information as output; this includes glyph metrics, bitmaps, and outlines. Only a font producer can modify this member. The producer of this font can store any information in the location pointed to by this member. This member should be used to store a pointer to producer-allocated data associated with this font instance. The engine will not modify this member. The pvProducer member is guaranteed to be null the first time a FONTOBJ structure is passed to the producer.

Comments

A driver can be both a producer and a consumer. For example, a printer driver can act as a producer while processing a call to the driver-supplied DrvQueryFontData function to provide glyph metrics, and later act a consumer while processing a call to the driver-supplied DrvTextOut function.

See Also

DrvDestroyFont, DrvGetGlyphMode, DrvQueryFont, DrvQueryTrueTypeOutline, FONTOBJ_cGetAllGlyphHandles, FONTOBJ_cGetGlyphs, FONTOBJ_pifi, FONTOBJ_pxoGetXform, FONTOBJ_vGetInfo, IFIMETRICS


GDIINFO

typedef struct _GDIINFO {
    ULONG ulVersion;
    ULONG ulTechnology;
    ULONG ulHorzSize;
    ULONG ulVertSize;
    ULONG ulHorzRes;
    ULONG ulVertRes;
    ULONG cBitsPixel;
    ULONG cPlanes;
    ULONG ulNumColors;
    ULONG flRaster;
    ULONG ulLogPixelsX;
    ULONG ulLogPixelsY;
    ULONG flTextCaps;
    ULONG ulDACRed;
    ULONG ulDACGreen;
    ULONG ulDACBlue;
    ULONG ulAspectX;
    ULONG ulAspectY;
    ULONG ulAspectXY;
    LONG xStyleStep;
    LONG yStyleStep;
    LONG denStyleStep;
    POINTL ptlPhysOffset;
    SIZEL szlPhysSize;
    ULONG ulNumPalReg;
    COLORINFO ciDevice;
    ULONG ulDevicePelsDPI;
    ULONG ulPrimaryOrder;
    ULONG ulHTPatternSize;
    ULONG ulHTOutputFormat;
    ULONG flHTFlags;
    ULONG ulVRefresh;
    ULONG ulBltAlignment;
    ULONG ulPanningHorzRes;
    ULONG ulPanningVertRes;
} GDIINFO, *PGDIINFO;

The GDIINFO structure describes the graphic capabilities of a given device. GDI zero-initializes this structure before calling the driver-supplied DrvEnablePDEV function.

Members

ulVersion
Specifies the driver version number. The byte ordering of ulVersion has the following form.
The high-order 16 bits must be set to zero. Bits 8 through 15 specify the version number of the Microsoft operating system for which the driver is designed. The high-order 4 bits of this range specify the major number of the version, the low-order 4 bits contain the minor number of the version. The low-order 8 bits of ulVersion specify the version number of the display driver; this value should be incremented for each release of the display driver binary file.

The version number contained in ulVersion is displayed in the Driver Information group of the Display Type dialog box displayed by the Display applet in Control Panel.

ulTechnology
Specifies the device technology. This member can be one of the following values:

Value

Meaning

DT_PLOTTER

Vector plotter

DT_RASDISPLAY

Raster display

DT_RASPRINTER

Raster printer

DT_RASCAMERA

Raster camera

DT_CHARSTREAM

Device fonts only

ulHorzSize
Specifies the width of the physical surface, in millimeters.
ulVertSize
Specifies the height of the physical surface, in millimeters.
ulHorzRes
Specifies the width of the physical surface, in pixels. See also ulDesktopHorzRes.
ulVertRes
Specifies the height of the physical surface, in pixels. See also ulDesktopHorzRes.
cBitsPixel
Specifies the number of adjacent bits in each color plane.
cPlanes
Specifies the number of color planes.
ulNumColors
Specifies the number of distinct colors supported at the same time (entries in device’s color table).
flRaster
Reserved ; must be zero.
ulLogPixelsX
Specifies the width in logical pixels per inch. This member must be 96 for this release.
ulLogPixelsY
Specifies the height in logical pixels per inch. This member must be 96 for this release.
flTextCaps
Specifies a flag describing Windows 3.1 text capabilities. If the driver TC_SCROLLBLT flag is in this member, it indicates that the console should perform text scrolling by redrawing the entire screen, using the driver-supplied DrvTextOut function rather than the DrvBitBlt or DrvCopyBits functions. The driver should set this flag if screen-to-screen bit-block transfers are slow. If this flag is not set, the driver is implicitly requesting that the console perform text scrolls through DrvBitBlt/DevCopyBits.
ulDAC color
Specifies the display number of DAC bits for the specified color.
ulAspectX
Specifies the relative width of a device pixel, in the range of one to 1000.
ulAspectY
Specifies the relative height of a device pixel, in the range of one to 1000.
ulAspectXY
Specifies the square root of the sum of the squares of ulAspectX and ulAspectY.
xStyleStep
Specifies the numerator of style advance for x-major lines, dx. For additional information, refer to the following Comments section and Styled Cosmetic Lines.
yStyleStep
Specifies the numerator of style advance for y-major lines, dy. For additional information, refer to the following Comments section and Styled Cosmetic Lines.
denStyleStep
Specifies the denominator of style advance, D. For additional information, refer to the following Comments section and Styled Cosmetic Lines.
ptlPhysOffset
Specifies the size, in pixels, of the unwriteable margin of a surface.
szlPhysSize
Specifies the size, in pixels, of the entire surface, including unwritable margins.
ulNumPalReg
Specifies the number of palette registers for an indexed device.
ciDevice
A COLORINFO structure that defines the device’s colors in CIE coordinate space.
ulDevicePelsDPI
For printers, specifies the number of pixels (or dots, or nozzles) per inch if the pixels are laid out side by side without overlapping or space between. For example, if the size of a pixel is 0.001 inch, this value is equal to one-divided-by 0.001. If the member is zero, GDI halftoning calculates this number based on the assumption that all pixels are connected with no overlapping.

Because the physical dot size for most printers is larger than the measured dot size, GDI uses this value to approximate how many physical dots can be placed, based on the cell size (pattern size). A log regression is then performed to determine what is most linear; that is, where the dots should be placed for the best coverage to optimize the overlapped device pixels coverage (dot gain).

For displays, this member should be set to zero.

ulPrimaryOrder
Specifies the bit order of the device’s primary colors or plane numbers for the halftone output in accordance with the color-to-bit mapping listed in the following table. For example, if your device output order is BGR (Bit 0[C], Bit 1[B], Bit 2[A]), set this member to PRIMARY_ORDER_CBA.

Bit 2(A)

Bit 1(B)

Bit 0(C)

RGB Primary Colors

R

G

B

CMY Primary Colors

C

M

Y

Plane Numbers

Plane3

Plane2

Plane1

This member can be one of the following values:

PRIMARY_ORDER_ABC
PRIMARY_ORDER_ACB
PRIMARY_ORDER_BAC
PRIMARY_ORDER_BCA
PRIMARY_ORDER_CBA
PRIMARY_ORDER_CAB

ulHTPatternSize
Specifies the size of the halftone pattern. The values ending with AxB_M are variations of the AxB patterns. In other words, SIZE_AxB and SIZE_AxB_M differ by which pixels are lit in an A x B pattern. This member can be one of the following values:

HT_PATSIZE_2x2
HT_PATSIZE_2x2_M
HT_PATSIZE_4x4
HT_PATSIZE_4x4_M
HT_PATSIZE_6x6
HT_PATSIZE_6x6_M
HT_PATSIZE_8x8
HT_PATSIZE_8x8_M
HT_PATSIZE_10x10
HT_PATSIZE_10x10_M
HT_PATSIZE_12x12
HT_PATSIZE_12x12_M
HT_PATSIZE_14x14
HT_PATSIZE_14x14_M
HT_PATSIZE_16x16
HT_PATSIZE_16x16_M
HT_PATSIZE_MAX_INDEX
HT_PATSIZE_DEFAULT

ulHTOutputFormat
Specifies the preferred output format for halftone. HT_FORMAT_4BPP uses only 8 full intensity colors while HT_FORMATP_IRGB uses all the 16 colors including the half-intensity colors. It is assumed that a 5 x 5 x 5 format (5 bits per color) is used for HT_FORMAT_16BPP. This member can be one of the following values:

HT_FORMAT_1BPP
HT_FORMAT_4BPP
HT_FORMAT_4BPP_IRGB
HT_FORMAT_8BPP
HT_FORMAT_16BPP
HT_FORMAT_24BPP
HT_FORMAT_32BPP

flHTFlags
Specifies a combination of flags describing the device. These flags are needed for halftoning. This member can be a combination of the following values:

Flag

Meaning

HT_FLAG_SQUARE_DEVICE_PEL

Device pixel is square rather than round (displays only ¾ printers require rounded pixels).

HT_FLAG_HAS_BLACK_DYE

Device has separate black dye.

HT_FLAG_ADDITIVE_PRIMS

Device primaries are additive.

HT_FLAG_OUTPUT_CMY

Device uses CMY primaries rather than RGB.

ulVRefresh
The video refresh rate for the currrent display mode. This is the value returned by the miniport driver for the refresh rate for the current mode.

The refresh rate contained in ulVRefresh is displayed in the Driver Information group of the Display Type dialog box displayed by the Display applet contained in Control Panel.

ulBltAlignment
This member indicates the preferred x-alignment for bit block transfers to the device. A value of zero indicates that bit block transfers are accelerated; any other nonnegative number indicates that bit block transfers are not accelerated, and gives the preferred horizontal alignment as a pixel multiple.

This value is used by the system to determine the default alignment for window positions and is also used to set the initial full-drag default during setup. A value of zero indicates that full-drag should be on by default; any value other than zero indicates that full-drag should be off by default.

ulPanningHorzRes, ulPanningVertRes
ulPanningHorzRes and ulPanningVertRes are provided for drivers which support seamless multiple displays and should be set to the pixel dimensions of the entire virtual desktop. ulHorzRes and ulVertRes should be set to the physical pixel dimensions of the smallest screen. Settings these values for multiple-display drivers will ensure that dialog boxes and maximized windows are not split across physical displays.

If a driver does not support seamless multiple displays, ulPanningHorzRes and ulPanningVertRes should be set to the same values as ulHorzRez and ulVertRes.

Comments

The xStyleStep, yStyleStep, and denStyleStep members define how a cosmetic line style should advance as it draws each pixel of a cosmetic line. The amount advanced along the style for each pixel is defined as a fraction that depends on whether the line is x-styled or y-styled. If the line is x-styled, the style advances by the fractional amount dx/D for each pixel moved in the x direction. Otherwise the style advances by dy/D for each pixel moved in the y direction.

The dots in the predefined line style PS_DOT are each one unit long. If the driver defines xStyleStep as one and denStyleStep as 5, then a dotted horizontal line consists of 5-pixels-on followed by 5-pixels-off, repeated.

Each of these three numbers must be less than 65536, even though the caps members are LONG values. These style steps are defined by the driver to ensure that the dots and dashes in a line are a pleasing size on the output device. The horizontal and vertical steps can be different to correct for non trivial aspect ratios. For example, on an EGA display, whose pixels are 33 percent higher than they are wide, you can set:

pdevcaps->xStyleStep   =  3;    // For an EGA
pdevcaps->yStyleStep   =  4;
pdevcaps->denStyleStep = 12;

In this case, horizontal dotted lines are 4-pixels-on, 4-pixels-off, because the style advances by 3/12 or 1/4 for each pixel. Vertical dotted lines are 3-pixels-on/3-pixels-off.

Styled lines look better if both the x and y style steps divide evenly into the style denominator, as they do in the preceding example. This gives dashes and dots that are always the same length.

GDI needs this information so that its bitmap functions can emulate exactly what the device does on its own surface. Applications can access this information to determine exactly which pixels will be turned on for styled lines. Refer also to Styled Cosmetic Lines.

See Also

CIECHROMA, COLORINFO, DrvBitBlt, DrvCopyBits, DrvEnablePDEV, DrvTextOut


GLYPHBITS

typedef struct _GLYPHBITS {
    POINTL ptlOrigin;
    SIZEL  sizlBitmap;
    BYTE   aj[1];
} GLYPHBITS;

The GLYPHBITS structure is used to define a glyph bitmap.

Members

ptlOrgin
A POINTL structure that defines the origin of the character in the bitmap.
sizlBitmap
Specifies the size, in bytes, of the bitmap.
aj
A variable-sized byte array containing a BYTE-aligned bitmap of the glyph. The array must include padding at the end to DWORD-align the entire structure.

The form of the bitmap requested is defined by pfo>flFontType and FO_16_LEVEL. If this is zero, then GDI is requesting a monchrome bitmap; otherwise GDI requests 16-level antialiased glyphs.

GDI will make this request of drivers that have antialiased fonts (see the description of DrvQueryFontCaps). It is possible that a driver may not be able to render a font in a multilevel format. In this case, the driver fails the call and GDI will call the driver again requesting a monochrome realization.

See Also

DrvGetGlyphMode, DrvQueryFontData, FONTOBJ_cGetGlyphs, GLYPHDEF


GLYPHDEF

typedef union _GLYPHDEF {
    GLYPHBITS *pgb;
    PATHOBJ   *ppo;
} GLYPHDEF;

The GLYPHDEF union identifies individual glyphs. It provides either a pointer to a GLYPHBITS structure or a pointer to a PATHOBJ structure.

Members

pgb
If pgb is defined, this member points to a GLYPHBITS structure. The driver can use the bitmap bits stored in this structure to form the associated glyph on its surface.
ppo
If ppo is defined, this member points to a PATHOBJ structure the driver can examine to extract the path describing the associated glyph.

See Also

GLYPHBITS, PATHOBJ


GLYPHPOS

typedef struct _GLYPHPOS {
    HGLYPH   hg;
    GLYPHDEF *pgdf;
    POINTL   *ptl;
} GLYPHPOS, *PGLYPHPOS;

The GLYPHPOS structure is passed to the driver when it calls STROBJ_bEnum. GDI passes a set of positions and glyph identities to the driver.

Members

hg
Identifies the glyph.
pgdf
Points to the glyph definition.
ptl
Specifies the point in device space where the character origin of the glyph should be placed. Note that if the driver had set GCAPS_HIGHRESTEXT in the flGraphicsCaps member of DEVINFO at enable time, the x and y coordinates should be interpreted as FIX point quantities; otherwise they should be interpreted as LONGs.

See Also

STROBJ, STROBJ_bEnum, STROBJ_vEnumStart


IFIMETRICS

typedef struct _IFIMETRICS {
    ULONG cjThis;
    ULONG ulVersion;
    PTRDIFF dpwszFamilyName;
    PTRDIFF dpwszStyleName;
    PTRDIFF dpwszFaceName;
    PTRDIFF dpwszUniqueName;
    PTRDIFF dpFontSim;
    LONG lEmbedId;
    LONG lItalicAngle;
    LONG lCharBias;
    PTRDIFF dpCharSets;
    BYTE jWinCharSet;
    BYTE jWinPitchAndFamily;
    USHORT usWinWeight;
    ULONG flInfo;
    USHORT fsSelection;
    USHORT fsType;
    FWORD fwdUnitsPerEm;
    FWORD fwdLowestPPEm;
    FWORD fwdWinAscender;
    FWORD fwdWinDescender;
    FWORD fwdMacAscender;
    FWORD fwdMacDescender;
    FWORD fwdMacLineGap;
    FWORD fwdTypoAscender;
    FWORD fwdTypoDescender;
    FWORD fwdTypoLineGap;
    FWORD fwdAveCharWidth;
    FWORD fwdMaxCharInc;
    FWORD fwdCapHeight;
    FWORD fwdXHeight;
    FWORD fwdSubScriptXSize;
    FWORD fwdSubScriptYSize;
    FWORD fwdSubScriptXOffset;
    FWORD fwdSubScriptYOffset;
    FWORD fwdSuperScriptXSize;
    FWORD fwdSuperScriptYSize;
    FWORD fwdSuperScriptXOffset;
    FWORD fwdSuperScriptYOffset;
    FWORD fwdUnderscoreSize;
    FWORD fwdUnderscorePosition;
    FWORD fwdStrikeoutSize;
    FWORD fwdStrikeoutPosition;
    BYTE chFirstChar;
    BYTE chLastChar;
    BYTE chDefaultChar;
    BYTE chBreakChar;
    WCHAR wcFirstChar;
    WCHAR wcLastChar;
    WCHAR wcDefaultChar;
    WCHAR wcBreakChar;
    POINTL ptlBaseline;
    POINTL ptlAspect;
    POINTL ptlCaret;
    RECTL rclFontBox;
    BYTE achVendId[4];
    ULONG cKerningPairs;
    ULONG ulPanoseCulture;
    PANOSE panose;
} IFIMETRICS, *PIFIMETRICS;

The IFIMETRICS structure defines all the information for a given typeface that GDI can understand. Most of the members are FWORD values, which are signed 16-bit quantities in design space. If the font is a raster font, design space and device space are the same and a font unit is equivalent to the distance between pixels.

Members

cjThis
Specifies the size, in bytes, of this structure, including any Unicode strings appended to the end.
ulVersion
Must be set to 0x000000001 for this version.
dpwszFamilyName
Specifies the offset in bytes to a zero-terminated Unicode string containing the family name of the font (for example, “Times Roman”). Generally, this string immediately follows the IFIMETRICS structure. This string should be the same as the name recorded in the lfFaceName member of the LOGFONT structure.
dpwszStyleName
Specifies the offset, in bytes, to a zero-terminated Unicode string describing the style of the font (for example, “Bold”).
dpwszFaceName
Specifies the offset, in bytes, to a zero-terminated Unicode string representing the unique and complete name of the font. The name contains the family and subfamily names of the font (for example, “Times New Roman Bold”).
dpwszUniqueName
Specifies the offset, in bytes, to a zero-terminated Unicode string representing the unique identifier of the font (for example, “Monotype:Times New Roman:1990”).
dpFontSim
This member must be set to zero for this version of Windows NT.
lEmbedId
The Embedding ID of the font. This is TrueType specific and should be set to zero by all other font providers.
lItalicAngle
Specifies the italic angle of the font. It is TrueType-specific and should be set to zero by all other font providers.
lCharBias
Specifies the character bias. This value is TrueType-specific and should be set to zero by all other font providers.
dpCharSets
Pointer to a reserved 32-bit values. For this version of Windows NT, this must be set to zero.
jWinCharSet
Specifies the character set of the font. This member can be one of the following values:

Value

Meaning

ANSI_CHARSET

This font supports the Windows ANSI character set.

SYMBOL_CHARSET

This font supports the Windows symbol character set.

SHIFTJIS_CHARSET

This font supports the Shift-JIS character set.

HANGEUL_CHARSET

This font supports the Hangeul character set.

CHINESEBIG5_CHARSET

This font supports the Chinese Big 5 character set.

OEM_CHARSET

This font supports an OEM-specific character set. The OEM character set is system dependent. This is the value to be returned to the application in the lfCharSet member of the LOGFONT structure.

jWinPitchAndFamily
Specifies the pitch of the font. This is the value to be recorded in the lfWeight member of the LOGFONT structure. The two low-order bits specify the pitch of the font and can be one of the following values:

Value

Meaning

FIXED_PITCH

For fixed pitch fonts

VARIABLE_PITCH

For variable pitch fonts

Bits 4 through 7 of this member specify the font family and can be one of the following values:

Value

Meaning

FF_DECORATIVE

Novelty fonts, such as Old English.

FF_DONTCARE

Don’t care or unknown.

FF_MODERN

Fonts with constant stroke width (fixed-pitch), with or without serifs. Fixed-pitch fonts are usually modern, such as Pica, Elite, and Courier.

FF_ROMAN

Fonts with variable stroke width (proportionally spaced) and with serifs, such as Times Roman, Palatino, and Century Schoolbook.

FF_SCRIPT

Fonts designed to look like handwriting, such as Script and Cursive.

FF_SWISS

Fonts with variable stroke width (proportionally spaced) and without serifs, such as Helvetica and Swiss.

usWinWeight
Specifies the weight of the font in the range 0 to 1000 (for example, 400 is normal and 700 is bold). This value is provided to the application in the lfWeight member of the LOGFONT structure.
flInfo
Specifies a combination of the following flags:

Value

Meaning

FM_INFO_TECH_TRUETYPE

Indicates that the font is a TrueType font.

FM_INFO_TECH_BITMAP

Indicates that the font is a bitmap font.

FM_INFO_TECH_STROKE

Indicates that the font is based on a stroked font technology. This flag does not specify whether the paths returned for this font should be filled or stroked; the consumer should examine the FM_INFO_RETURNS_STROKES and FM_INFO_RETURNS_OUTLINES flags for this information.

FM_INFO_TECH_OUTLINE_NOT_TRUETYPE

Indicates that the font is based on a scalable font technology that uses outline paths, but is not based on TrueType. This flag does not specify whether the paths returned for this font should be filled or stroked; the consumer should examine the FM_INFO_RETURNS_STROKES and FM_INFO_RETURNS_OUTLINES flags for this information.

FM_INFO_ARB_XFORMS

Indicates that a font can be realized under a continuous range of two dimensional linear transformations.

FM_INFO_1BBP

Indicates that a glyph bitmap has a color depth of 1 bit per pixel. This must be set for the first version of Windows NT.

FM_INFO_INTEGER_WIDTH

Indicates that all glyphs have nonfractional advance widths. Bitmap fonts usually set this flag.

FM_INFO_CONSTANT_WIDTH

Indicates that all glyphs of the font under all realizations have the same value of character increment. If this flag is set, the FM_INFO_OPTICALLY_FIXED_PITCH flag must also be set.

FM_INFO_NOT_CONTIGUOUS

Indicates that the supported character set is not contiguous.

FM_INFO_PID_EMBEDDED

Indicates that the font is an embedded font; it is TrueType-specific and should not be set by other font providers.

FM_INFO_RETURNS_OUTLINES

Indicates that for any glyph supported by the driver, GDI can request a PATHOBJ structure that describes the outline of that glyph. If possible, when the outline is filled using GDI’s path filling conventions, the resulting bitmap should be identical to the bitmap returned by the driver. The FM_INFO_RETURNS_OUTLINES and FM_INFO_RETURNS_STOKES flags cannot be set concurrently.

FM_INFO_RETURNS_STROKES

Indicates that for any glyph supported by the drivers, GDI can request a PATHOBJ structure that describes the spline of the glyph. This path cannot be filled but can be stroked to give a representation of the glyph. The FM_INFO_RETURNS_OUTLINES and FM_INFO_RETURNS_STOKES flags cannot be set concurrently.

FM_INFO_RETURNS_BITMAPS

Indicates that the driver returns bitmaps for any glyph supported by the font.

FM_INFO_UNICODE_COMPLIANT

Indicates that a font is compliant with the Unicode standard.

FM_INFO_RIGHT_HANDED

Indicates that the ascent direction of the font is 90 degrees counterclockwise from the baseline direction. The ascent direction is the direction along which height is measured and is always perpendicular to the baseline direction.

FM_INFO_INTEGRAL_SCALING

Indicates that the font can be scaled by an integral amount in both the x and y directions. If this flag is set, then the driver must be able to render glyphs in the case where the notional to device transformation is scaled by integral amounts in the x and y directions. GDI requests the integral scaling of a font by including the axial scalings in the notional to device transformation passed to the Driver when creating the font. This flag is meaningful only when the FM_INFO_ARB_XFORMS flag has not been set.

FM_INFO_90_DEGREE_ROTATIONS

Indicates that the font can be realized in 90 degree rotations of the original notional shape. GDI requests the rotation of a font by including the rotation in the notional to device transformation passed to the driver when creating the font. This member has meaning only when the FM_INFO_ARB_XFORMS flag has not been set.

FM_INFO_OPTICALLY_FIXED_PITCH

Indicates that this font is considered typographically as fixed pitch. This is an optical quality of the font and does not necessarily indicate that all the glyphs of the font have the same character increment.

FM_INFO_DO_NOT_ENUMERATE

Indicates that this font will not be enumerated by either EnumFontFamilies or EnumFonts. Moreover, under GetTextFace, the string returned to the application will be retrieved from the string dpwszUniqueName. This flag allows the font provider to associate more than one IFIMETRICS structure with one of its fonts.

FM_INFO_ISOTROPIC_SCALING_ONLY

Indicates that the font supports arbitrary isotropic scaling only. That is, transforms are equivalent to the identity matrix multiplied by a positive real number. If this flag is set, then neither the FM_INFO_ARB_XFORMS nor the FM_INFO_ANISOTROPIC_SCALING_ONLY flags can be set. If the FM_INFO_90DEGREE_ROTATIONS flag is set, the font supports transformations equivalent to an isotropic scaling followed by a rotation by a multiple of 90 degrees.

FM_INFO_ANISOTROPIC_SCALING_ONLY

Indicates that the font supports only arbitrary anisotropic scaling. That is, transforms are equivalent to a diagonal matrix multiplied by a positive real number. If this flag is set, then the FM_INFO_ARB_XFORMS and the FM_INFO_ISOTROPIC_SCALING_ONLY flags cannot be set. If the FM_INFO_90DEGREE_ROTATIONS flag is also set, the font supports transformations that are a combination of a simple anisotropic scaling followed by a rotation by a multiple of 90 degrees.

FM_INFO_TID_EMBEDDED

This flag is used for font embedding; it is TrueType specific and should be set to zero for all other font providers.

FM_INFO_FAMILY_EQUIV

Indicates that the dpwszFamilyName offset in IFIMETRICS is actually the offset to a list of equivalent family names or aliases. The first name is the base or real name; the subsequent names are equivalents or aliases. Each name in the list is zero-terminated; the list is terminated by two zeros.

<base name> <0> <alias 1> <0>...<alias n>
    <0> <0>

The based names are used only for mapping; they are not enumerated.

FM_INFO_DBCS_FIXED_PITCH

Indicates that double-byte characters for this font are fixed pitch. Nothing is implied about single byte characters. This flag is meaningful only for fonts that support a double-byte character set (DBCS), such as shift JIS. Fonts that do not support a DBCS should not set this flag.

FM_INFO_NONNEGATIVE_AC

Indicates that all glyphs of this font have nonnegative a and c spacing. That is, the glyph black box never extends outside the region bordered by the character origin and the character concatenation point.

FM_INFO_IGNORE_TC_RA_ABLE

Indicates that, for this font, the TC_RA_ABLE flag is ignored.

fsSelection
Specifies a combination of the following flags:

Value

Meaning

FM_SEL_ITALIC

Set if the characters of the font are italic.

FM_SEL_UNDERSCORE

Set if all the characters of the font are underscored by default; otherwise underscoring must be simulated.

FM_SEL_NEGATIVE

Set if the characters of the font have the foreground and background reversed.

FM_SEL_OUTLINED

Set if the characters of the font are hollow.

FM_SEL_STRIKEOUT

Set if the characters of the font are struck out by default; otherwise strikeouts must be simulated.

FM_SEL_BOLD

Set if the characters of the font are bold.

FM_SEL_REGULAR

Set if the characters of the font are normal weight.

fsType
This is a TrueType-specific bit field indicating certain properties for the font, such as font embedding and licensing rights for the font. Embeddable fonts can be stored in a document. When a document with embedded fonts is opened on a system that does not have the font installed (the remote system), the embedded font can be loaded for temporary (and in some cases permanent) use on that system by an embedding-aware application. Embedding licensing rights are granted by the font vendor. The following flags can be set:

Value

Meaning

FM_TYPE_LICENSED

Set if the font is a Restricted License font. When only this bit is set, this font must not be modified, embedded, or exchanged in any manner without first obtaining permission of the legal owner. For Restricted License embedding to take effect, it must be the only level of embedding selected.

FM_READONLY_EMBED

Set if read-write embedding is not permitted; only “preview and print” encapsulation is allowed. When this bit is set, the font can be embedded and temporarily loaded on the remote system. Documents containing “preview and print” fonts must be opened “read-only;” no edits can be applied to the document.

FM_EDITABLE_EMBED

Set if the font can be embedded and temporarily loaded on other systems. Documents containing Editable fonts can be opened for reading and writing.

Fonts with the FM_READONLY_EMBED bit set indicate that they can be embedded within documents but must only be installed temporarily on the remote system. Any document that includes an FM_READONLY_EMBED font must be opened “read-only.” That is, the application can let the user view and/or print the document, but not edit it.

Fonts with the FM_EDITABLE_EMBED bit set indicate that they can be embedded in documents, but must only be installed temporarily on the remote system. In contrast to FM_READONLY_EMBED fonts, documents containing Editable fonts can be opened “read-write;” editing is permitted, and changes can be saved.

Fonts with no fsType bits set indicate that they can be embedded and permanently installed on the remote system by an application. The user of the remote system acquires the identical rights, obligations, and licenses for that font as the original purchaser of the font, and is subject to the same end-user license agreement, copyright, design patent, and/or trademark as was the original purchaser.

Applications that implement support for font embedding, either through use of the Font Embedding DLL or through other means, must not embed fonts that are not licensed to permit embedding. Further, applications loading embedded fonts for temporary use must delete the fonts when the document containing the embedded font is closed.

If multiple embedding bits are set, the least restrictive license granted takes precedence. For example, if bits 1 and 3 are set, bit 3 takes precedence over bit 1and the font can be embedded with Editable rights. For compatibility purposes, most vendors granting Editable embedding rights also set the Preview & Print bit (0x000C). This permits an application that only supports Preview & Print embedding to detect that font embedding is allowed.

fwdUnitsPerEm
Specifies the em-height of the font.
fwdLowestPPEm
Specifies the smallest readable size of the font, in pixels. This value is ignored for bitmap fonts.
fwdWinAscender
Specifies the Windows ascender value for the font.
fwdWinDescender
Specifies the Windows descender value for the font.
fwdMacAscender
Specifies the Macintosh ascender value for the font.
fwdMacDescender
Specifies the Macintosh descender value for the font. This number is typically less than zero. It measures the signed displacement from the base line of the lowest descender in the Macintosh character set.
fwdMacLineGap
Specifies the Macintosh line gap for the font. The suggested Macintosh interline spacing is equal to fwdMacLineGap + fwdMacAscender - fwdMacDescender.
fwdTypoAscender
Specifies the typographic ascender value for the font.
fwdTypoDescender
Specifies the typographic descender value for the font. This value specifies the signed displacement of the lowest descender from the baseline.
fwdTypoLineGap
Specifies the typographic line gap for the font.
fwdAveCharWidth
Specifies the arithmetic average of the width of all of the 26 lower case letters ‘a’ through ‘z’ of the Latin alphabet and the space character. If any of the 26 lowercase letters are not present, then this member should be set equal to the weighted average of all glyphs in the font.
fwdMaxCharInc
Specifies the maximum character increment of all glyphs in the font.
fwdCapHeight
Specifies the height of the optical line describing the top of the uppercase ‘H’ in font units (FUnits). This might not be the same as the measured height of the uppercase ‘H.’ If this information does not exist, fwdCapHeight should be set to zero, which indicates that it is undefined.
fwdXHeight
Specifies the height of the optical line describing the height of the lowercase ‘x’ in font units. This might not be the same as the measured height of the lowercase ‘x.’ A value of zero indicates that this member is undefined.
fwdSubScriptXSize
Specifies the suggested character width (the size along the baseline direction) of the subscript font.
fwdSubScriptYSize
Specifies the suggested character height (the size along the ascender direction) of the subscript font.
fwdSubScriptXOffset
Specifies the suggested offset in the baseline direction of the subscript character. The offset is with respect to the character origin of the base character.
fwdSubScriptYOffset
Specifies the suggested offset in the baseline direction of the subscript character. The offset is taken from the character origin of the base character.
fwdSuperScriptXSize
Specifies the suggested character width (the size along the baseline direction) of the superscript font.
fwdSuperScriptYSize
Specifies the suggested character height (the size along the ascender direction) of the superscript font.
fwdSuperScriptXOffset
Specifies the suggested offset in the baseline direction of the superscript character. The offset is taken from the character origin of the base character.
fwdSuperScriptYOffset
Specifies the suggested offset in the baseline direction of the superscript character. The offset is taken from the character origin of the base character.
fwdUnderscoreSize
Specifies the suggested width of the underscore bar, in font units.
fwdUnderscorePosition
Specifies the suggested displacement, in font units, from the base line to the middle of the underscore bar.
fwdStrikeoutSize
Specifies the suggested width of the strike-out bar, in font coordinates.
fwdStrikeoutPosition
Specifies the suggested displacement of the middle of the strikeout bar from the baseline.
chFirstChar
Specifies the lowest supported character in the code page specified in jWinCharSet. This field is provided for Windows 3.1 compatibility.
chLastChar
Specifies the highest supported character in the code page specified in jWinCharSet. This field is provided for Windows 3.1 compatibility.
chDefaultChar
Specifies the default character in the code page specified in jWinCharSet. This field is provided for Windows 3.1 compatibility.
chBreakChar
Specifies the break character in the code page specified in jWinCharSet. This field is provided for Windows 3.1 compatibility.
wcFirstChar
Specifies the supported character with the smallest Unicode character code.
wcLastChar
Specifies the supported character with the largest Unicode character code.
wcDefaultChar
Specifies the character to be substituted when an application requests a character that is not supported by the font.
wcBreakChar
Specifies the code point of the space character or its equivalent.
ptlBaseline
Specifies the intended writing direction of this font. For example, a typical Latin font specifies a value of (1,0).
ptlAspect
Specifies the aspect ratio of the pixel centers for which the bitmap font was designed. This value is used only by bitmap fonts.
ptlCaret
Specifies the direction of the ascender direction of the font. For example, the value for a non-italicized Latin font is (0,1) while an italicized Latin font might specify a value of (2,5).
rclFontBox
Specifies the bounding box of all glyphs in the font in design space.
achVendId
Specifies a four character identifier for the font vendor. Identifiers are documented in the Microsoft TrueType specification.
cKerningPairs
Specifies the number of kerning pairs associated with this font.
ulPanoseCulture
Specifies the manner in which to interpret the panose number. This number should be set to FM_PANOSE_CULTURE_LATIN for Latin-based fonts. See the Win32 SDK for information about the PANOSE structure.
panose
An array of 10 bytes used to describe the visual characteristics of a given typeface. These characteristics are then used to associate the font with other fonts of similar appearance having different names. See the Win32 SDK for information about the PANOSE structure.

Comments

The IFIMETRICS structure defines all the information per typeface that GDI understands. Most of the members are FWORD values, which are signed 16-bit quantities in design space. If the font is a raster font, design space and device space are the same and a font unit is equivalent to the distance between pixels.

The coordinate system in the font/notional space is such that the y coordinate increases in an upward direction and the x coordinate increases to the right.

See Also

PATHOBJ


LINEATTRS

typedef struct _LINEATTRS {
    FLONG       fl;
    ULONG       iJoin;
    ULONG       iEndCap;
    FLOAT_LONG  elWidth;
    FLOAT       eMiterLimit;
    ULONG       cstyle;
    PFLOAT_LONG pstyle;
    FLOAT_LONG  elStyleState;
} LINEATTRS, *PLINEATTRS;

The LINEATTRS structure is used by a driver’s line-drawing functions to determine line attributes.

Members

fl
Option flags. This member can be one of the following values:

Flag

Meaning

LA_GEOMETRIC

A geometric wide line.

LA_ALTERNATE

A special cosmetic line style; every other pixel is on.

LA_STARTGAP

The first entry in the style array specifies the length of the first gap.

iJoin
Specifies join styles for geometric wide lines. This member can be one of the following values:

Join Style

Meaning

JOIN_ROUND

The joining edges of wide lines are rounded.

JOIN_BEVEL

The joining edges of wide lines are beveled.

JOIN_MITER

The joining edges of wide lines are mitered.

iEndCap
Specifies the end cap style for a geometric wide line. This member can be one of the following values:

End Cap Style

Meaning

ENDCAP_ROUND

The end cap is rounded.

ENDCAP_SQUARE

The end cap is square.

ENDCAP_BUTT

The end cap is

elWidth
Specifies the width of the line. This width is measured in FLOAT world coordinates for a geometric wide line, but in LONG device coordinates for a cosmetic wide line.
eMiterLimit
Sets the limit as a multiple of the line width that a miter join is allowed to extend from its inside corner to its outer vertex. This prevents very long spikes from occurring when lines of a path meet at very small angles. If the miter limit is exceeded, a bevel join should be used instead.

This member is used only by geometric wide lines.

cstyle
Specifies the number of entries in the style array.
pstyle
Points to the style array. If this member is null, the line style is solid.
elStyleState
A pair of 16-bit values supplied by GDI whenever the driver calls PATHOBJ_bEnumClipLines. These two values, packed into a LONG, specify where in the styling array (at which pixel) to start the first subpath. This value must be updated as part of the output routine if the line is not solid. This member applies to cosmetic lines only. Refer also to Styled Cosmetic Lines.

See Also

DrvStrokeAndFillPath, DrvStrokePath, PATHOBJ_vEnumStartClipLines


PATHDATA

typedef struct _PATHDATA {
    FLONG    flags;
    ULONG    count;
    POINTFX  pptfx;
} PATHDATA, *PPATHDATA;

The PATHDATA structure describes all or part of a subpath.

For example, a MoveTo call by an application within a path begins a new subpath.

Members

flags
Flags describing the data returned are defined as follows:

Value

Meaning

PD_BEGINSUBPATH

The first point begins a new subpath. It is not connected to the previous subpath. If this flag is not set, the starting point for the first curve to be drawn from this data is the last point returned in the previous call.

PD_ENDSUBPATH

The last point in the array ends the subpath. This subpath can be open or closed depending on the PD_CLOSEFIGURE flag. If there is more data to be returned in the path, the next record begins a new subpath. Note that a single record might begin and end a subpath.

PD_RESETSTYLE

This bit is defined only if this record begins a new subpath. If set, it indicates the style state should be reset to zero at the beginning of the subpath. If not set, the style state is defined by the LINEATTRS structure, or continues from the previous subpath.

PD_CLOSEFIGURE

This bit is only defined if the record ends a subpath. If set, there is an implicit line segment connecting the last point of the subpath with the first point. If such a closed subpath is being stroked, joins are used all around the path, and there are no end caps. If this flag is not set, the subpath is considered open, even if the first and last points happen to coincide. In that case, end caps should be drawn. This flag is not relevant to filling because all subpaths are assumed closed when a path is filled.

PD_BEZIERS

If set, each set of three control points returned for this call describes a Bezier curve. If clear, each control point describes a line segment. A starting point for either type is either explicit at the beginning of the subpath, or implicit as the endpoint of the previous curve.

count
Specifies the count of POINTFX structures pointed to by pptfx.
pptfx
Points to an array of POINTFX structures that define the control points for the curves. These structures must not be modified.

See Also

LINEATTRS, PATHOBJ_bEnum


PATHOBJ

typedef struct _PATHOBJ{
    FLONG fl;
    ULONG cCurves;
} PATHOBJ;

The PATHOBJ structure is used to describe a set of lines and Bezier curves that are to be stroked or filled. Associated functions allow the lines and curves to be enumerated for the driver.

Members

fl
A set of hint flags that describe the path. This member can be one of the following values:

Value

Meaning

PO_BEZIERS

The path contains Bezier curves.

PO_ELLIPSE

The path consists of a single ellipse inscribed in the path’s bounding rectangle.

cCurves
The number of lines and Bezier curves that make up the path.

The following GDI service routines are provided for PATHOBJ objects:

PATHOBJ_bCloseFigure
PATHOBJ_bEnum
PATHOBJ_bEnumClipLines
PATHOBJ_bMoveTo
PATHOBJ_bPolyBezierTo
PATHOBJ_bPolyLineTo
PATHOBJ_vEnumstart
PATHOBJ_vEnumStartClipLines
PATHOBJ_vGetBounds

See Also

DrvFillPath, DrvStrokeAndFillPath, DrvStrokePath, EngCreatePath, EngDeletePath


RUN

typedef struct _RUN {
    LONG iStart;
    LONG iStop;
} RUN, *PRUN;

The RUN structure is used to describe a linear set of pixels that is not clipped by the CLIPLINE structure.

Members

iStart
Specifies the starting point for a field of pixels to be drawn. The first pixel of the unclipped line is pixel 0.
iStop
Specifies the stopping point for a field of pixels to be drawn.

Comments

If the clip region is complex, a single line segment can be broken into many RUNs. The same segment is returned as many times as necessary to list all of its RUNs.

See Also

PATHOBJ_bEnumClipLines


STROBJ

class _STROBJ {
    ULONG cGlyphs;
    FLONG flAccel;
    ULONG ulCharInc;
    RECTL rclBkGround;
    GLYPHPOS *pgp;
    PWSTR pwszOrg;
} STROBJ;

The STROBJ class, or text string object, contains an enumeration of glyph handles and positions for the device driver.

Members

cGlyphs
Specifies the number of glyphs in the STROBJ.
flAccel
Accelerator flags. This member can be any of the following values:

Flag

Meaning

SO_FLAG_DEFAULT_PLACEMENT

Default increment vectors are used to position the characters.

SO_HORIZONTAL

Text is horizontal; left-to-right or right-to-left, depending on SO_REVERSED. The y coordinate is only supplied for the first glyph in the string.

SO_VERTICAL

Text is vertical; top-to-bottom or bottom-to-top, depending on SO_REVERSED. The x coordinate is only supplied for the first glyph in the string.

SO_REVERSED

Set if horizontal is right-to-left or vertical is bottom-to-top.

SO_ZERO_BEARINGS

All glyphs have zero A and zero C spacing. This bit guarantees that glyph’s black boxes do not overlap.

SO_CHAR_INC_EQUAL_BM_BASE

Spacing is determined by bitmap sizes. In this context, bitmap base is defined as cx for horizontal case, cy for vertical case. This bit is set means that every glyph in a font the bitmap (supplied in GLYPHBITS structure by GDI) will have a base equal to the advance width for that glyph. This does not mean that this is a fixed pitch font. (Not used for PATHOBJs.)

SO_MAXEXT_EQUAL_BM_SIDE

Bitmaps have the same widths. In this context, bitmap side is defined as cy for horizontal case and cx for vertical case. This flag guarantees that all glyphs in the font will have the same side equal to the sum of ascender and descender. However, this bit indicates more than just a constant side. For horizontal case, this bit also indicates that the vertical distance between the top of the glyph's bitmap and its character origin is equal to the ascender for all glyphs. For vertical case, this bit indicates that the horizontal distance between the glyph's origin and "top" of the glyph's bitmap (which is really right or left, depending on whether you are writing in the direction of positive or negative y axis) is equal to the ascender. In either case, the tops and bottoms of all glyph bitmaps lay on the same line in the direction of writing. (Not used for PATHOBJs.)

ulCharInc
Specifies whether the font is a fixed-pitch (monospace) font. If it is, this member is equal to the advance width of glyphs in pels; otherwise, it is zero. When this member is nonzero, GDI supplies a valid coordinate for only the first character in the string. All other character positions must be generated by the driver by successively adding the value of this member along the writing direction.
rclBkGround
Specifies the bounding box for the string.
pgp
Points to the GLYPHPOS array for the whole string. If this member is not null, enumeration is not required.
pwszOrg
Points to the original Unicode string or cGlyphs characters. Contrary to its name, this string is not usually zero-terminated. Also, this string is not always valid, such as in journalling with printer fonts, in which case this parameter will be null.

Comments

STROBJ_vEnumStart and STROBJ_bEnum provide access to the Unicode string for the driver.

See Also

DrvGetGlyphMode , DrvTextOut


SURFOBJ

typedef struct _SURFOBJ {
    DHSURF dhsurf;
    HSURF  hsurf;
    DHPDEV dhpdev;
    HDEV   hdev;
    SIZEL  sizlBitmap;
    ULONG  cjBits;
    PVOID  pvBits;
    PVOID  pvScan0;
    LONG   lDelta;
    ULONG  iUniq;
    ULONG  iBitmapFormat;
    USHORT iType;
    USHORT fjBitmap;
} SURFOBJ;

The SURFOBJ structure is the user object for a surface. A device driver usually calls methods on a surface object only when the surface object represents a GDI bitmap or a device-managed surface.

Members

dhsurf
If the surface is a device managed surface, this member is a device handle identifying the surface. Otherwise, this member is zero.
hsurf
Identifies the surface.
dhpdev
Identifies the device’s PDEV that is associated with the specified surface.
hdev
GDI’s logical handle for the PDEV associated with this device.
sizlBitmap
Specifies the size of the surface, in pixels.
cjBits
Specifies the size of the buffer pointed to, if pvBits is not null.
pvBits
Points to the surface’s pixels, if the surface is a standard format bitmap. Otherwise, this member is null.
pvScan0
Points to the first scanline of the bitmap.
lDelta
Specifies the count of bytes required to move down one scan line in the bitmap.
iUniq
Specifies the current state of the specified surface. Every time the surface changes this value is incremented; this enables drivers to cache source surfaces.
iBitmapFormat
Specifies the standard format most closely matching this surface. If the iType member specifies a bitmap (STYPE_BITMAP), this member specifies its format. Windows NT supports a set of predefined formats, although applications can also send device-specific formats by using SetDIBitsToDevice. Supported predefined formats include the following:

Value

Meaning

BMF_1BPP

1 bit per pixel.

BMF_4BPP

4 bits per pixel.

BMF_8BPP

8 bits per pixel.

BMF_16BPP

16 bits per pixel.

BMF_24BPP

24 bits per pixel.

BMF_32BPP

32 bits per pixel.

BMF_4RLE

4 bits per pixel, run length encoded.

BMF_8RLE

8 bits per pixel, run length encoded.

iType
Surface type, which is one of the following:

Type

Definition

STYPE_BITMAP

The surface is a bitmap.

STYPE_DEVICE

The surface is managed by the device.

STYPE_DEVBITMAP

The surface is a device format bitmap.

fjBitmap
If the surface is of type STYPE_BITMAP and is a standard noncompressed format bitmap, the following flag can be set. Otherwise, this member should be ignored.

Value

Meaning

BMF_TOPDOWN

The first scan line represents the top of the bitmap.

BMF_NOZEROINIT

The bitmap was not zero-initialized.

BMF_DONTCACHE

The bitmap should not be cached by the driver because it is a transient bitmap, created by GDI, that the driver will never see again.

Comments

When information about a particular surface is required by a driver, the driver must access the SURFOBJ. This structure allows quick access to the properties of the surface.

When a SURFOBJ structure represents a GDI bitmap, the driver must be able to determine the format of the bitmap and locate the bitmap bits.

When a SURFOBJ structure represents a device surface, the driver must be able to locate the device handle for the surface.


TYPE1_FONT

typedef struct _TYPE1_FONT {
    HANDLE  hPFM;
    HANDLE  hPFB;
    ULONG   ulIdentifier;
}  TYPE1_FONT;

The TYPE1_FONT structure contains the information necessary for a PostScript driver to access a Type1 font through GDI.

Members

hPFM
Handle to the PostScript Type1 .pfm file.
hPFB
Handle to the PostScript Type1 .pfb file.
ulIdentifier
An identifier that is generated and used by GDI. The driver stores ulIdentifier in the dpCharSets field of the IFIMETRICS structure.

Comments

A PostScript driver can obtain a list of installed Type1 fonts by calling EngGetType1FontList.

Each PostScript Type1 font comes with two separate files: a .pfm file and a .pfb file. The .pfm file contains font metrics information; the .pfb file contains the PostScript Type1 binary font data.

See Also

IFIMETRICS


WCRUN

typedef struct _WCRUN {
    WCHAR   wcLow;
    USHORT  cGlyphs;
    HGLYPH *phg;
} WCRUN, *PWCRUN;

The WCRUN structure describes a run of HGLYPH structures.

Members

wcLow
The first character in the run.
cGlyphs
A count of characters in the run.
phg
A pointer to an array of HGLYPH structures that correspond to this run. If this member is null, then all characters in this run can be converted to HGLYPH structures by a simple zero extension.

Comments

GDI relies on the runs being arranged in increasing order by code points. A binary search is made through the list of runs.

See Also

DrvQueryFontTree


WNDOBJ

typedef struct _WNDOBJ{
    CLIPOBJ  coClient;
    PVOID    pvConsumer;
    RECTL    rclClient;
} WNDOBJ, *PWNDOBJ;

The WNDOBJ structure allows the driver to keep track of the position, size, and visible client region changes of a window. The visible client region can be enumerated by calling the WNDOBJ_cEnumStart and WNDOBJ_bEnum functions.

Members

coClient
A CLIPOBJ that describes the region of the window. If iDComplexity is DC_RECT and the left edge in rclBounds is greater than or equal to the right edge, or the top edge is greater than or equal to the bottom edge, the client region is invisible.
pvConsumer
A driver-defined value for identifying this particular WNDOBJ. This value can be set by calling the WNDOBJ_vSetConsumer function.
rclClient
A lower-right exclusive rectangle of the client area of the window in screen coordinates.

Comments

A driver can associate its own data with a WNDOBJ by calling the WNDOBJ_vSetConsumer function.

As an accelerator, the driver can access public members of the WNDOBJ. These public members are guaranteed to remain unchanged only in the context of the driver callback routine supplied to GDI in the EngCreateWnd function, or the functions where a WNDOBJ is given.

See Also

CLIPOBJ, WNDOBJ_bEnum, WNDOBJ_cEnumStart, WNDOBJ_vSetConsumer


XFORMOBJ

The XFORMOBJ structure describes an arbitrary linear two-dimensional transform, such as for geometric wide lines. The transform can be downloaded to the driver. Functions are also provided to apply the transform to driver-supplied data.

There are no public members in the XFORMOBJ structure.

See Also

FONTOBJ_pxoGetXform, XFORMOBJ_bApplyXform, XFORMOBJ_iGetXform


XLATEOBJ

typedef struct _XLATEOBJ {
    ULONG  iUniq;
    FLONG  flXlate;
    USHORT iSrcType;
    USHORT iDstType;
    ULONG  cEntries;
    ULONG  *pulXlate;
} XLATEOBJ;

The XLATEOBJ structure is used to translate color indices from one palette to another.

Members

iUniq
A cache identifier that enables the driver to recognize an XLATEOBJ structure that it has previously cached. If this member is zero, the driver should not cache the XLATEOBJ structure.
flXlate
Flags specifying hints about the translation. This member can be any combination of the following values:

Value

Meaning

XO_TRIVIAL

Source indices are usable as target indices.

XO_TABLE

A table is provided to translate source indices to target indices. This flag will always be set for 1-, 4-, and 8-bbp source bitmaps.

XO_TO_MONO

Source indices are translated to a monochrome format with the special property that all indices map to zero, except for one. A driver can use this to accelerate a block transfer.

iSrcType
Specifies the type of the source palette.This member can be one of the following values:

Value

Meaning

PAL_INDEXED

Indexed palette.

PAL_BITFIELDS

Palette is defined by 3 masks.

PAL_RGB

8 bits for each of R, G, B; least significant byte is R.

PAL_BGR

8 bits for each of B, G, R; least significant byte is B.

If the source palette is not one of these types, the driver should not attempt to download the palette description.

iDstType
Describes the type of the destination palette. See iSrcType for allowable values. If the destination palette is not one of these types, the driver should not attempt to download the palette description.
cEntries
Count of entries in pulXlate. Indexing into pulXlate with a value greater than cEntries results in a memory access violation.
pulXlate
Points to an array of translation entries. If this member is null and the iSrcType member is PAL_INDEXED, the driver should call the XLATEOBJ_piVector function to retrieve the translation vector.

Comments

The destination palette always belongs to the destination surface of some drawing operation. The source palette is an application-selected palette or a palette from another surface.

The XLATEOBJ structure is used to translate color indices that refer to the source palette to indices for the destination palette. The resulting index identifies a color that matches the source color as closely as possible.

See Also

XLATEOBJ_piVector


Chapter 3 DirectDraw Hardware Interface Functions

This chapter provides reference pages for the DirectDraw hardware interface and DirectDraw-specific DDI functions currently supported in Windows NT. Except for the DrvXxx functions, all other function names are pseudonames to be decided upon by the display driver writer.

Driver writers must include both ddrawint.h and ddraw.h in their driver. The former include file is provided with the Windows NT DDK; the latter is shipped with the Win32 SDK.

For DirectDraw hardware interface data structure definitions, see DirectDraw Hardware Interface Structures.


Blt

DWORD (APIENTRY *PDD_SURFCB_BLT)(
PDD_BLTDATA lpBlt
);

Blt performs a bit-block transfer.

Parameters

lpBlt
Pointer to the DD_BLTDATA structure that contains the information required for the driver to perform the blt.

Return Value

Blt returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.


CanCreateSurface

DWORD (APIENTRY *PDD_CANCREATESURFACE)(
PDD_CANCREATESURFACEDATA lpCanCreateSurface
);

CanCreateSurface is a DirectDraw object callback that queries whether the driver can support a surface that is different from the primary surface.

Parameters

lpCanCreateSurface
Pointer to a DD_CANCREATESURFACEDATA structure.

Return Value

CanCreateSurface returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

DirectDraw calls this routine to determine if the driver supports a pixel format that is different from that of the primary surface.


CreateSurface

DWORD (APIENTRY *PDD_CREATESURFACE)(
PDD_CREATESURFACEDATA lpCreateSurface
);

CreateSurface is a DirectDraw object callback that creates a DirectDraw suface object.

Parameters

lpCreateSurface
Pointer to a DD_CREATESURFACEDATA structure that contains the information required to create a surface.

Return Value

CreateSurface returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.


DestroySurface

DWORD (APIENTRY *PDD_SURFCB_DESTROYSURFACE)(
PDD_DESTROYSURFACEDATA lpDestroySurface
);

DestroySurface is a DirectDraw object callback that destroys a DirectDraw surface.

Parameters

lpDestroySurface
Pointer to a DD_DESTROYSURFACEDATA structure that contains the information required to delete a surface.

Return Value

DestroySurface returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.


DrvDisableDirectDraw

VOID DrvDisableDirectDraw(
DHPDEV dhpdev
);

DrvDisableDirectDraw disables hardware for DirectDraw use.

Parameters

dhpdev
Handle to the PDEV that was returned by the driver’s DrvEnablePDEV routine.

Comments

GDI calls DrvDisableDirectDraw when the last DirectDraw application has finished running. A driver’s DrvDisableDirectDraw implementation should clean up any software resources and reclaim any hardware resources that the driver dedicated to DirectDraw in DrvEnableDirectDraw.


DrvEnableDirectDraw

BOOL DrvEnableDirectDraw(
DHPDEV dhpdev,
 DD_CALLBACKS *pCallBacks,
DD_SURFACECALLBACKS *pSurfaceCallBacks,
DD_PALETTECALLBACKS *pPaletteCallBacks
);

DrvEnableDirectDraw enables hardware for DirectDraw use.

Parameters

dhpdev
Handle to the PDEV that was returned by the driver’s DrvEnablePDEV routine.
pCallBacks
Pointer to a DD_CALLBACKS structure that the driver initializes and returns to the caller.
pSurfaceCallBacks
Pointer to a DD_SURFACECALLBACKS structure that the driver initializes and returns to the caller.
pPaletteCallBacks
Pointer to a DD_PALETTECALLBACKS structure that the driver initializes and returns to the caller.

Return Value

DrvEnableDirectDraw returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

GDI calls DrvEnableDirectDraw to obtain pointers to the DirectDraw callbacks that the driver supports. The driver should set the function pointer members of DD_CALLBACKS, DD_SURFACECALLBACKS, and DD_PALETTECALLSBACKS to point to those functions that it implements. A driver should also set the corresponding bit fields in the dwFlags members of these structures for all supported callbacks.

A driver’s DrvEnableDirectDraw implementation can also dedicate hardware resources, such as video memory, for use by DirectDraw only.

See Also

DrvGetDirectDrawInfo


DrvGetDirectDrawInfo

BOOL DrvGetDirectDrawInfo(
DHPDEV dhpdev,
 DD_HALINFO *pHalInfo,
DWORD *pdwNumHeaps,
VIDEOMEMORY *pvmList,
DWORD pdwNumFourCC,
DWORD *pdwFourCC
);

DrvGetDirectDrawInfo gets the capabilities of the graphics hardware.

Parameters

dhpdev
Handle to the PDEV that was returned by the driver’s DrvEnablePDEV routine.
pHalInfo
Pointer to a DD_HALINFO structure in which the driver should return its supported hardware capabilities.
pdwNumHeaps
Pointer to a DWORD in which the number of VIDEOMEMORY structures pointed to by pvmList is returned.
pvmList
Pointer to an array of VIDEOMEMORY structures in which the driver should return information about each video memory chunk that it controls.
pdwNumFourCC
Pointer to a DWORD in which the number of DWORDs pointed to by pdwFourCC is returned.
pdwFourCC
Pointer to an array of DWORDs in which the driver should return information about each FOURCC that it supports.

Return Value

DrvGetDirectDrawInfo returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

GDI calls DrvGetDirectDrawInfo and DrvEnableDirectDraw in response to an application’s call to DirectDrawObjectCreate.

When enabling a DirectDraw driver, GDI will call DrvGetDirectDrawInfo twice. The first call determines the size of the video memory heap and the number of FOURCCs that the driver supports. In this call, GDI passes NULL for both pvmList and pdwFourCC, so the driver should initialize and return pdwNumHeaps and pdwNumFourCC only. The second call is made after GDI allocates the required amount of memory for pvmList and pdwFourCC based on the values returned by the first call. In the second call, the driver should initialize and return pdwNumHeaps, pvmList, pdwNumFourCC, and pdwFourCC.

GDI allocates and zero-initializes the DD_HALINFO structure to which pHalInfo points. The driver need only fill in the fields of the DD_HALINFO structure that pertain to it.

See Also

DrvEnableDirectDraw


Flip

DWORD (*PDD_SURFCB_FLIP)(
PDD_FLIPDATA lpFlip
);

Flip causes the surface memory associated with the target surface to become the primary surface, and the current surface to become the nonprimary surface.

Parameters

lpFlip
Pointer to a DD_FLIPDATA structure that contains the information required to perform the flip.

Return Value

Flip returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

Flip allows a display driver to perform multi-buffering.


GetBltStatus

DWORD GetBltStatus(
PDD_GETBLTSTATUSDATA lpGetBltStatus
);

GetBltStatus queries the driver for its blt status.

Parameters

lpGetBltStatus
Pointer to a DD_GETBLTSTATUSDATA structure that contains the information required to perform the blt status query.

Return Value

GetBltStatus returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

Accelerators do not typically multi-thread on drawing, in which case the driver should simply determine if its hardware is ready for a surface blt. The driver reports its blt status based on the flag set in the dwFlags member of lpGetBltStatus. If the driver is busy and cannot otherwise perform a blt at this time, it should set the ddRVal member to DDERR_WASSTILLDRAWING; otherwise, the driver should set ddRVal to DD_OK.


GetFlipStatus

DWORD GetFlipStatus(
PDD_GETFLIPSTATUSDATA lpGetFlipStatus
);

GetFlipStatus queries the driver for its flip status.

Parameters

lpGetFlipStatus
Pointer to a DD_GETFLIPSTATUSDATA structure that contains the information required to perform the flip status query.

Return Value

GetFlipStatus returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

The driver should report its flip status based on the flag set in the dwFlags member of lpGetFlipStatus. If the driver is busy and cannot otherwise perform a flip at this time, it should set the ddRVal member to DDERR_WASSTILLDRAWING; otherwise, the driver should set ddRVal to DD_OK.


Lock

DWORD (APIENTRY *PDD_SURFCB_LOCK)(
PDD_LOCKDATA lpLock
);

Lock provides a valid pointer to a block of memory associated with a surface.

Parameters

lpLock
Pointer to a DD_LOCKDATA structure that contains the information required to perform the lockdown.

Return Value

Lock returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

A driver should ensure that a blt or flip is not in progress before obtaining the memory pointer.

A lock does not provide exclusive access to the requested memory block; that is, multiple threads can lock the same surface at the same time. It is the application’s responsibility to synchronize access to the memory block whose pointer is being obtained.


MapMemory

DWORD (APIENTRY *PDD_MAPMEMORY)(
PDD_MAPMEMORYDATA lpMapMemory
);

MapMemory is the DirectDraw object callback that maps or unmaps application-modifiable portions of the frame buffer into or from the user-mode address space of the specified process.

Parameters

lpMapMemory
Pointer to a DD_MAPMEMORYDATA structure that contains details of memory mapping or unmapping operation.

Return Value

MapMemory returns a callback status code indicating how it handled the memory operation.

Comments

MapMemory is called to perform memory mapping before the first call to Lock. It is called to unmap memory after the last UnLock call is made.


SetColorKey

DWORD (APIENTRY *PDD_SETCOLORKEY)(
PDD_DRVSETCOLORKEYDATA lpSetColorKey
);

SetColorKey sets the color key value.

Parameters

lpSetColorKey
Pointer to a DD_SETCOLORKEYDATA structure that contains the information required to set the color key.

Return Value

SetColorKey returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

SetColorKey usually needs to be implemented only for drivers that support overlays with color key capabilities; it is used to specify a source or destination color key.


SetOverlayPosition

DWORD (APIENTRY *PDD_SURFCB_SETOVERLAYPOSITION)(
PDD_SETOVERLAYPOSITIONDATA lpSetOverlayPosition
);

SetOverlayPosition sets the position for an overlay.

Parameters

lpSetOverlayPosition
Pointer to a DD_SETOVERLAYPOSITIONDATA structure that contains the information required to set the overlay position.

Return Value

SetOverlayPosition returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.


Unlock

DWORD (APIENTRY *PDD_SURFCB_UNLOCK)(
PDD_UNLOCKDATA lpUnlock
);

UnLock releases the lock held on a block of memory.

Parameters

lpUnlock
Pointer to a DD_UNLOCKDATA structure that contains the information required to perform the lock release.

Return Value

Unlock returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

The driver does not need to verify that the memory was previously locked down by Lock, because DirectDraw does parameter validation before calling this routine.


UpdateOverlay

DWORD (APIENTRY *PDD_SURFCB_UPDATEOVERLAY)(
PDD_UPDATEOVERLAYDATA lpUpdateOverlay
);

UpdateOverlay repositions or modifies the visual attributes of an overlay surface.

Parameters

lpUpdateOverlay
Pointer to a DD_UPDATEOVERLAYDATA structure that contains the information required to update the overlay.

Return Value

UpdateOverlay returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.

Comments

UpdateOverlay shows, hides, or repositions an overlay surface on the screen. It also sets attributes of the overlay surface, such as the stretch factor or type of color key to be used. Note that Flip will be used for flipping between overlay surfaces, so performance for UpdateOverlay is not critical.


WaitForVerticalBlank

DWORD (APIENTRY *PDD_WAITFORVERTICALBLANK)(
PDD_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank
);

WaitForVerticalBlank returns the vertical blank status of the device.

Parameters

lpWaitForVerticalBlank
Pointer to a DD_WAITFORVERTICALBLANKDATA structure that contains the information required to perform the vertical blank status.

Return Value

WaitForVerticalBlank returns a DDHAL_DRIVER_Xxx callback code that indicates how the driver handled the call. These return codes are defined in ddrawint.h.


Chapter 4 DirectDraw Hardware Interface Structures

This chapter describes the DirectDraw hardware interface structures currently supported in Windows NT. These structures are used by display drivers that offer DirectDraw support.

See DirectDraw Hardware Interface Functions for DirectDraw hardware interface function references.


DD_BLTDATA

typedef struct _DD_BLTDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpDDDestSurface;
    RECTL                        rDest;
    PDD_SURFACE_LOCAL            lpDDSrcSurface;
    RECTL                        rSrc;
    DWORD                        dwFlags;
    DWORD                        dwROPFlags;
    DDBLTFX                      bltFX;
    HRESULT                      ddRVal;
    VOID *                       Blt;
    BOOL                         IsClipped;
    RECTL                        rOrigDest;
    RECTL                        rOrigSrc;
    DWORD                        dwRectCnt;
    LPRECT                       prDestRects;
} DD_BLTDATA;

The DD_BLTDATA structure contains all information required by a driver to perform a bit-blt. It is passed as a parameter to a driver’s Blt function.

Members

lpDD
Points to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDDestSurface
Points to the DD_SURFACE_LOCAL structure that describes the blt’s destination surface.
rDest
Describes the rectangle on the destination surface in which the blt should occur.
lpDDSrcSurface
Points to the DD_SURFACE_LOCAL structure that describes the blt’s source surface.
rSrc
Describes the rectangle on the source surface from which the blt data should be copied.
dwFlags
Bitmask that tells the driver how to perform the blt. The DDBLT_Xxx bit fields are enumerated in ddraw.h.
dwROPFlags
Specifies the ROP flags. A driver should look at this member only when handling ROPs. This field is unused by Windows NT.
bltFX
DDBLTFX structure containing override information for the Blt surface callback. The dwFillColor field is used for solid color fills, and the ddckSrcColorKey and ddckDestColorKey fields are used for color key blts.
ddRVal
Value in which the driver returns the status of a blt operation.
Blt
Unused by Windows NT.
IsClipped
Unused by Windows NT.
rOrigDest
Unused by Windows NT.
dwRectCnt
Unused by Windows NT.
prDestRect
Unused by Windows NT.


DD_CALLBACKS

typedef struct DD_CALLBACKS{
    DWORD                            dwSize;
    DWORD                            dwFlags;
    PDD_DESTROYDRIVER                DestroyDriver;
    PDD_CREATESURFACE                CreateSurface;
    PDD_SETCOLORKEY                  SetColorKey;
    PDD_SETMODE                      SetMode;
    PDD_WAITFORVERTICALBLANK         WaitForVerticalBlank;
    PDD_CANCREATESURFACE             CanCreateSurface;
    PDD_CREATEPALETTE                CreatePalette;
    PDD_GETSCANLINE                  GetScanLine;
    PDD_MAPMEMORY                    MapMemory;
} DD_CALLBACKS;

The DD_CALLBACKS structure contains pointers to the object callbacks that a device driver supports. The driver initializes this structure in DrvEnableDirectDraw.

Members

dwSize
Size, in bytes, of the DD_CALLBACKS structure. The driver must set this field to the size in bytes of the DD_HALINFO structure.
dwFlags
Bitmask that the driver initializes to indicate which object callbacks it supports. For every bit set in dwFlags, the driver must initialize the corresponding function pointer member of this structure. Bitfields are enumerated in ddrawint.h as DDHAL_CB32_Xxx.
DestroyDriver
Pointer to the driver-supplied DestroyDriver object callback.
CreateSurface
Pointer to the driver-supplied CreateSurface object callback.
SetColorKey
Pointer to the driver-supplied SetColorKey object callback.
SetMode
Pointer to the driver-supplied SetMode object callback.
WaitForVerticalBlank
Pointer to the driver-supplied WaitForVerticalBlank object callback.
CanCreateSurface
Pointer to the driver-supplied CanCreateSurface object callback.
CreatePalette
Pointer to the driver-supplied CreatePalette object callback.
GetScanLine
Pointer to the driver-supplied GetScanLine object callback.
MapMemory
Pointer to the driver-supplied MapMemory object callback.

See Also

DD_PALETTECALLBACKS, DD_SURFACECALLBACKS, DrvEnableDirectDraw


DD_CANCREATESURFACEDATA

typedef struct _DD_CANCREATESURFACEDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACEDESC              lpDDSurfaceDesc;
    DWORD                        bIsDifferentPixelFormat;
    HRESULT                      ddRVal;
    VOID *                       CanCreateSurface;
} DD_CANCREATESURFACEDATA;

The DD_CANCREATESURFACEDATA structure is passed to the DirectDraw driver’s CanCreateSurface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurfaceDesc
Pointer to the DD_SURFACEDESC structure that describes the surface to be created. The DD_SURFACEDESC structure is defined in ddraw.h.
bIsDifferentPixelFormat
Boolean that specifies whether the pixel format of this surface differs from that of the primary surface.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
CanCreateSurface
Unused by Windows NT.


DD_CREATESURFACEDATA

typedef struct _DD_CREATESURFACEDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACEDESC              lpDDSurfaceDesc;
    PDD_SURFACE_LOCAL            *lplpSList;
    DWORD                        dwSCnt;
    HRESULT                      ddRVal;
    VOID *                       CreateSurface;
} DD_CREATESURFACEDATA;

The DD_CREATESURFACEDATA structure is passed to the DirectDraw hardware interface’s CreateSurface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurfaceDesc
Pointer to the DD_SURFACEDESC structure that describes the surface to be created. The DD_SURFACEDESC structure is filled in by the application, and is defined in ddraw.h.
lplpSList
Pointer to a pointer in which the driver returns the list of created DD_SURFACE_LOCAL surface(s). The driver should create dwSCnt surfaces, which is the number of surfaces required to fulfill the requested surface creation description in lpDDSurfaceDesc.
dwSCnt
Member in which the driver returns the number of surfaces pointed to by lplpSList.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
CreateSurface
Unused by Windows NT.


DD_DESTROYSURFACEDATA

typedef struct _DD_DESTROYSURFACEDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpDDSurface;
    HRESULT                      ddRVal;
    VOID *                       DestroySurface;
} DD_DESTROYSURFACEDATA;

The DD_DESTROYSURFACEDATA structure is passed to the DirectDraw hardware interface’s DestroySurface surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurface
Pointer to a DD_SURFACE_LOCAL structure that describes the surface object to be destroyed.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
DestroySurface
Unused by Windows NT.


DD_DIRECTDRAW_GLOBAL

typedef struct _DD_DIRECTDRAW_GLOBAL{
    VOID *                      dhpdev;
    DWORD                       dwReserved1;
    DWORD                       dwReserved2;
} DD_DIRECTDRAW_GLOBAL;

DD_DIRECTDRAW_GLOBAL contains driver information that is relevant to its device.

Members

dhpdev
Handle to the driver’s private PDEV.
dwReserved1
Reserved for use by the display driver.
dwReserved2
Reserved for use by the display driver.

Comments

Only one DD_DIRECTDRAW_GLOBAL definition will ever exist for a device.

The dwReserved1 and dwReserved2 fields can be used as required by the driver. For example, a driver might store pointers to internal data structures in these fields.


DD_DIRECTDRAW_LOCAL

typedef struct _DD_DIRECTDRAW_LOCAL{
    PDD_DIRECTDRAW_GLOBAL        lpGbl;
    FLATPTR                      fpProcess;
} DD_DIRECTDRAW_LOCAL;

DD_DIRECTDRAW_LOCAL contains driver information that is relevant to the current DirectDraw process only. Applications make DirectDraw objects to do their drawing.

Members

lpGbl
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
fpProcess
Pointer to the address of the frame buffer in the calling process’s address space.


DD_FLIPDATA

typedef struct _DD_FLIPDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpSurfCurr;
    PDD_SURFACE_LOCAL            lpSurfTarg;
    DWORD                        dwFlags;
    HRESULT                      ddRVal;
    VOID *                       Flip;
} DD_FLIPDATA;

The DD_FLIPDATA structure is passed to the DirectDraw hardware interface’s Flip surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpSurfCurr
Pointer to the DD_SURFACE_LOCAL structure that describes the current surface.
lpSurfTarg
Pointer to the DD_SURFACE_LOCAL structure that describes the surface to be flipped to.
dwFlags
Bitmask that tells the driver how to perform the blt. The DDFLIP_Xxx bit fields are enumerated in ddraw.h.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
Flip
Unused by Windows NT.


DD_GETBLTSTATUSDATA

typedef struct _DD_GETBLTSTATUSDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpDDSurface;
    DWORD                        dwFlags;
    HRESULT                      ddRVal;
    VOID *                       GetBltStatus;
} DD_GETBLTSTATUSDATA;

The DD_GETBLTSTATUSDATA structure is passed to the DirectDraw hardware interface’s GetBltStatus surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface on which the blt status query should be made.
dwFlags
Flag that tells the driver what information is being requested about the blt. The DDGBS_Xxx bit fields are enumerated in ddraw.h.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
GetBltStatus
Unused by Windows NT.


DD_GETFLIPSTATUSDATA

typedef struct _DD_GETFLIPSTATUSDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpDDSurface;
    DWORD                        dwFlags;
    HRESULT                      ddRVal;
    VOID *                       GetFlipStatus;
} DD_GETFLIPSTATUSDATA;

The DD_GETFLIPSTATUSDATA structure is passed to the DirectDraw hardware interface’s GetFlipStatus surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface on which the flip status query should be made.
dwFlags
Flag that tells the driver what information is being requested about the flip. The DDGFS_Xxx bit fields, enumerated in ddraw.h, are:

Value

Meaning

DDGFS_CANFLIP

Driver should determine whether it can perform a flip at this time.

DDGFS_ISFLIPDONE

Driver should determine whether an initiated flip has completed.

ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
GetFlipStatus
Unused by Windows NT.


DD_HALINFO

typedef struct _DD_HALINFO{
    DWORD                    dwSize;
    VIDEOMEMORYINFO          vmiData;
    DDCAPS                   ddCaps;
    DWORD                    dwMonitorFrequency;
    DWORD                    dwFlags;
} DD_HALINFO;

The DD_HALINFO structure describes the driver’s supported hardware capabilities. The driver sets and returns all fields of this structure in its DrvGetDirectDrawInfo implementation.

Members

dwSize
Size, in bytes, of the DD_HALINFO structure.
vmiData
VIDEOMEMORYINFO structure that contains video memory information.
ddCaps
DDCAPS structure that contains driver-specific capabilities. The DD_CAPS structure is defined in ddraw.h.
dwMonitorFrequency
Monitor frequency in the current mode.
dwFlags
Create flags. This field is unused by Windows NT.


DD_LOCKDATA

typedef struct _DD_LOCKDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpDDSurface;
    DWORD                        bHasRect;
    RECTL                        rArea;
    LPVOID                       lpSurfData;
    HRESULT                      ddRVal;
    VOID *                       Lock;
    DWORD                        dwFlags;
} DD_LOCKDATA;

The DD_LOCKDATA structure is passed to the DirectDraw hardware interface’s Lock surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface associated with the memory region to be locked.
bHasRect
BOOLEAN that specifies whether rArea is valid.
rArea
RECTL that specifies the area on the surface to be locked down.
lpSurfData
Location in which the driver returns a pointer to the memory region that it locked down.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
Lock
Unused by Windows NT.
dwFlags
Bitfield that tells the driver how to perform the memory lockdown. The DDLOCK_Xxx bit fields are enumerated in ddraw.h.


DD_MAPMEMORYDATA

typedef struct _DD_MAPMEMORYDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    BOOL                         bMap;
    HANDLE                       hProcess;
    FLATPTR                      fpProcess;
    HRESULT                      ddRVal;
} DD_MAPMEMORYDATA;

The DD_MAPMEMORYDATA structure is passed to the DirectDraw hardware interface’s MapMemory object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
bMap
Specifies the memory operation that the driver should perform. A value of TRUE indicates that the driver should map memory; FALSE means that the driver should unmap memory.
hProcess
Handle to the process whose address space is affected.
fpProcess
Pointer to the address in the process’s address space in which the memory mapping/unmapping occurs.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.


DD_SETCOLORKEYDATA

typedef struct _DD_SETCOLORKEYDATA {
    PDD_DIRECTDRAW_GLOBAL       lpDD;
    PDD_SURFACE_LOCAL           lpDDSurface;
    DWORD                       dwFlags;
    DDCOLORKEY                  ckNew;
    HRESULT                     ddRVal;
    VOID *                      SetColorKey;
} DD_SETCOLORKEYDATA;

The DD_SETCOLORKEYDATA structure is passed to the DirectDraw hardware interface’s SetColorKey surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface whose color key is to be set.
dwFlags
Specifies how the driver should handle the color key. This field can be a combination of the following values:

Value

Meaning

DDCKEY_SRCBLT

Specifies the default source color key for Blt operations.

DDCKEY_DESTBLT

Specifies the default destination color key for Blt operations.

DDCKEY_SRCOVERLAY

Specifies the source color key for the overlay.

DDCKEY_DESTOVERLAY

Specifies the destination color key for the overlay.

ckNew
Specifies the DD_COLORKEY structure that describes the color key.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
SetColorKey
Unused by Windows NT.


DD_SETOVERLAYPOSITIONDATA

typedef struct _DD_SETOVERLAYPOSITIONDATA {
    PDD_DIRECTDRAW_GLOBAL       lpDD;
    PDD_SURFACE_LOCAL           lpDDSrcSurface;
    PDD_SURFACE_LOCAL           lpDDDestSurface;
    LONG                        lXPos;
    LONG                        lYPos;
    HRESULT                     ddRVal;
    VOID *                      SetOverlayPosition;
} DD_SETOVERLAYPOSITIONDATA;

The DD_SETOVERLAYPOSITIONDATA structure is passed to the DirectDraw hardware interface’s SetOverlayPosition surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSrcSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the overlay surface.
lpDDDestSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface that is overlayed.
lXPos
Specifies the x coordinate of the upper left corner of the overlay.
lYPos
Specifies the y coordinate of the upper left corner of the overlay.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
SetOverlayPosition
Unused by Windows NT.


DD_SURFACECALLBACKS

typedef struct DD_SURFACECALLBACKS{
    DWORD                                    dwSize;
    DWORD                                    dwFlags;
    PDD_SURFCB_DESTROYSURFACE                DestroySurface;
    PDD_SURFCB_FLIP                          Flip;
    PDD_SURFCB_SETCLIPLIST                   SetClipList;
    PDD_SURFCB_LOCK                          Lock;
    PDD_SURFCB_UNLOCK                        Unlock;
    PDD_SURFCB_BLT                           Blt;
    PDD_SURFCB_SETCOLORKEY                   SetColorKey;
    PDD_SURFCB_ADDATTACHEDSURFACE            AddAttachedSurface;
    PDD_SURFCB_GETBLTSTATUS                  GetBltStatus;
    PDD_SURFCB_GETFLIPSTATUS                 GetFlipStatus;
    PDD_SURFCB_UPDATEOVERLAY                 UpdateOverlay;
    PDD_SURFCB_SETOVERLAYPOSITION            SetOverlayPosition;
    LPVOID                                   reserved4;
    PDD_SURFCB_SETPALETTE                    SetPalette;
} DD_SURFACECALLBACKS;

The DD_SURFACECALLBACKS structure contains pointers to the DirectDraw surface callbacks that a device driver supports. The driver initializes this structure in DrvEnableDirectDraw.

Members

dwSize
Size, in bytes, of the DD_SURFACECALLBACKS structure. This field is unused by Windows NT.
dwFlags
Bitmask that the driver initializes to indicate which DirectDraw surface callbacks it supports. For every bit set in dwFlags, the driver must initialize the corresponding function pointer member of this structure. Bitfields are enumerated in ddrawint.h as DDHAL_SURFCB32_Xxx.
DestroySurface
Pointer to the driver-supplied DestroySurface surface callback.
Flip
Pointer to the driver-supplied Flip surface callback.
SetClipList
Pointer to the driver-supplied SetClipList surface callback.
Lock
Pointer to the driver-supplied Lock surface callback.
Unlock
Pointer to the driver-supplied Unlock surface callback.
Blt
Pointer to the driver-supplied Blt surface callback.
SetColorKey
Pointer to the driver-supplied SetColorKey surface callback.
AddAttachedSurface
Pointer to the driver-supplied AddAttachedSurface surface callback.
GetBltStatus
Pointer to the driver-supplied GetBltStatus surface callback.
GetFlipStatus
Pointer to the driver-supplied GetFlipStatus surface callback.
UpdateOverlay
Pointer to the driver-supplied UpdateOverlay surface callback.
SetOverlayPosition
Pointer to the driver-supplied SetOverlayPosition surface callback.
reserved4
Reserved for system use.
SetPalette
Pointer to the driver-supplied SetPalette surface callback.

See Also

DD_CALLBACKS, DD_PALETTECALLBACKS, DrvEnableDirectDraw


DD_SURFACE_GLOBAL

typedef struct _DD_SURFACE_GLOBAL{
    DWORD                dwBlockSizeY;
    union {
        DWORD            dwBlockSizeX;
        DWORD            dwUserMemSize;
    };
    FLATPTR              fpVidMem;
    LONG                 lPitch;
    LONG                 yHint;
    LONG                 xHint;
    DWORD                wHeight;
    DWORD                wWidth;
    DWORD                dwReserved1;
    DDPIXELFORMAT        ddpfSurface;
} DD_SURFACE_GLOBAL;

The DD_SURFACE_GLOBAL structure contains surface-related data that applies globally to all duplicate surface objects.

Members

dwBlockSizeY
Location in which the driver returns the height, in scanlines, of the off-screen memory block that it wants DirectDraw to allocate. The driver should set this value when it returns DDHAL_PLEASEALLOC_BLOCKSIZE in fpVidMem.
dwBlockSizeX
Location in which the driver returns the size in bytes of the width of the off-screen memory block that it wants DirectDraw to allocate. The driver should set this value when it returns DDHAL_PLEASEALLOC_BLOCKSIZE in fpVidMem.
dwUserMemSize
Location in which the driver returns the size in bytes of the memory block that it wants DirectDraw to allocate in user-mode system memory. The driver should set this value when it returns DDHAL_PLEASEALLOC_USERMEM in fpVidMem.
fpVidMem
If the driver allocates the memory block, it should return the offset into video memory in this field. If the driver wants DirectDraw to do the memory allocation, it can instead return one of the following values in this field from its CreateSurface routine:

Value

Meaning

DDHAL_PLEASEALLOC_BLOCKSIZE

Driver wants DirectDraw to allocate a memory block of size dwBlockSizeX in off-screen memory.

DDHAL_PLEASEALLOC_USERMEM

Driver wants DirectDraw to allocate a memory block of size dwBlockSizeX in user-mode memory.

lPitch
Specifies the pitch of the surface; that is, the distance to the start of the next line. This is also known as the stride of the surface.
yHint
Specifies the y-coordinate of the surface. This field is a 2D Cartesian coordinate specified in device space.
xHint
Specifies the x-coordinate of the surface. This field is a 2D Cartesian coordinate specified in device space.
wHeight
Specifies the height of the surface.
wWidth
Specifies the width of the surface.
dwReserved1
Reserved for use by the display driver.
ddpfSurface
Pointer to the DDPIXELFORMAT structure that describes the pixel format of the surface.


DD_SURFACE_LOCAL

typedef struct _DD_SURFACE_LOCAL{
    PDD_SURFACE_GLOBAL        lpGbl;
    DWORD                     dwFlags;
    DDSCAPS                   ddsCaps;
    DWORD                     dwReserved1;
    DDCOLORKEY                ddckCKSrcBlt;
    DDCOLORKEY                ddckCKDestBlt;
} DD_SURFACE_LOCAL;

The DD_SURFACE_LOCAL structure contains surface-related data that is unique to an individual surface object.

Members

lpGbl
Pointer to the DD_SURFACE_GLOBAL structure containing surface data that is shared globally with duplicate surfaces.
dwFlags
Surface flags.
ddsCaps
DDSCAPS structure that describes the capabilities of the surface.
dwReserved1
Specifies the surface field available for modification and use by the display driver.
ddckCKSrcBlt
DDCOLORKEY structure that describes the color key for source color key overlays.
ddckCKDestBlt
DDCOLORKEY structure that describes the color key for destination color key overlays.


DD_UNLOCKDATA

typedef struct _DD_UNLOCKDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    PDD_SURFACE_LOCAL            lpDDSurface;
    HRESULT                      ddRVal;
    VOID *                       Unlock;
} DD_UNLOCKDATA;

The DD_UNLOCKDATA structure is passed to the DirectDraw hardware interface’s Unlock surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface to be unlocked.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
Lock
Unused by Windows NT.


DD_UPDATEOVERLAYDATA

typedef struct _DD_UPDATEOVERLAYDATA {
    PDD_DIRECTDRAW_GLOBAL       lpDD;
    PDD_SURFACE_LOCAL           lpDDDestSurface;
    RECTL                       rDest;
    PDD_SURFACE_LOCAL           lpDDSrcSurface;
    RECTL                       rSrc;
    DWORD                       dwFlags;
    DDOVERLAYFX                 overlayFX;
    HRESULT                     ddRVal;
    VOID *                      UpdateOverlay;
} DD_UPDATEOVERLAYDATA;

The DD_UPDATEOVERLAYDATA structure is passed to the DirectDraw hardware interface’s UpdateOverlay surface object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
lpDDDestSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the surface to be overlayed. This value can be NULL if DDOVER_HIDE is specified for dwFlags.
rDest
RECTL that specifies the destination for the overlay.
lpDDSrcSurface
Pointer to the DD_SURFACE_LOCAL structure that describes the overlay surface.
rSrc
RECTL that specifies the portion of the source surface to be used for the overlay.
dwFlags
Specifies how the driver should handle the overlay. This field can be a combination of the following values:

Value

Meaning

DDOVER_HIDE

The driver should hide the overlay surface.

DDOVER_SHOW

The driver should show the overlay surface using the attributes specified by overlayFX.

DDOVER_KEYDEST

The driver should use the color key associated with the destination surface by SetColorKey.

DDOVER_KEYDESTOVERRIDE

The driver should use the destination color key specified by overlayFX.dckDestColorKey.

DDOVER_KEYSRC

The driver should use the color key associated with the destination surface by SetColorKey.

DDOVER_KEYSRCOVERRIDE

The driver should use the source color key specified by overlayFX.dckSrcColorKey.

overlayFX
DDOVERLAYFX structure that contains the requested attributes for the overlay.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
UpdateOverlay
Unused by Windows NT.


DD_WAITFORVERTICALBLANKDATA

typedef struct _DD_WAITFORVERTICALBLANKDATA{
    PDD_DIRECTDRAW_GLOBAL        lpDD;
    DWORD                        dwFlags;
    DWORD                        bIsInVB;
    DWORD                        hEvent;
    HRESULT                      ddRVal;
    VOID *                       WaitForVerticalBlank;
} DD_WAITFORVERTICALBLANKDATA;

The DD_WAITFORVERTICALBLANKDATA structure is passed to the DirectDraw hardware interface’s WaitForVerticalBlank object callback.

Members

lpDD
Pointer to the DD_DIRECTDRAW_GLOBAL structure that describes the driver.
dwFlags
Specifies how the driver should handle the vertical blank. This field can be one of the following values:

Value

Meaning

DDWAITVB_I_TESTVB

Driver should simply return the current status of the vertical blank.

DDWAITVB_BLOCKBEGIN

Driver should return from WaitForVerticalBlank only when the vertical blank interval begins.

DDWAITVB_BLOCKEND

Driver should return from WaitForVerticalBlank only when the vertical blank interval ends and display begins.

bIsInVB
Specifies the raster state of the device. A value of TRUE indicates that the device is in a vertical blank; FALSE means that it is not.
hEvent
Handle to the event that the driver should trigger when the specified vertical blank status occurs. This is not currently supported by Windows NT.
ddRVal
Location in which the driver returns a DirectDraw status return code. The status codes are defined in ddraw.h.
WaitForVerticalBlank
Unused by Windows NT.


VIDEOMEMORY

typedef struct _VIDEOMEMORY{
    DWORD              dwFlags;
    FLATPTR            fpStart;
    union{
        FLATPT         fpEnd;
        DWORD          dwWidth;
    };
    DDSCAPS            ddsCaps;
    DDSCAPS            ddsCapsAlt;
        DWORD          dwHeight;
} VIDEOMEMORY;

The VIDEOMEMORY structure describes the size and capabilities of a chunk of video memory that the driver controls.

Members

dwFlags
Bitmask that describes the video memory. This member can be a bitwise-OR of any of the following:

Bitfield

Meaning

VIDMEM_ISLINEAR

The video memory is a continguous block of memory.

VIDMEM_ISRECTANGULAR

The video memory is rectangular.

fpStart
Pointer to the beginning of a chunk of memory.
fpEnd
Pointer to the end of the memory chunk to which fpStart points.
dwWidth
Width of the chunk of memory pointed to by fpStart. This member should be used to describe rectangular memory regions.
ddsCaps
DDSCAPS structure in which the driver returns the capabilities of a surface.
ddsCapsAlt
DDSCAPS structure in which the driver returns
dwHeight
Height of the chunk of memory pointed to by fpStart.

Comments

The driver returns a list of VIDEOMEMORY structures from its DrvGetDirectDrawInfo implementation. This list provides a complete description of the offscreen memory that the driver controls.


VIDEOMEMORYINFO

typedef struct _VIDEOMEMORYINFO{
    FLATPTR              fpPrimary;
    DWORD                dwFlags;
    DWORD                dwDisplayWidth;
    DWORD                dwDisplayHeight;
    LONG                 lDisplayPitch;
    DDPIXELFORMAT        ddpfDisplay;
    DWORD                dwOffscreenAlign;
    DWORD                dwOverlayAlign;
    DWORD                dwTextureAlign;
    DWORD                dwZBufferAlign;
    DWORD                dwAlphaAlign;
    PVOID                pvPrimary;
} VIDEOMEMORYINFO;

The VIDEOMEMORYINFO structure describes the general format of the primary surface.

Members

fpPrimary
Specifies the offset to the primary surface.
dwFlags
Bitmask describing the fields of ddpfDisplay that are supported by the driver.
dwDisplayWidth
Specifies the current width of the display.
dwDisplayHeight
Specifies the current height of the display.
lDisplayPitch
Specifies the current pitch of the display.
ddpfDisplay
DDPIXELFORMAT structure in which the pixel format of the display is described.
dwOffscreenAlign
Specifies the byte alignment for offscreen surfaces.
dwOverlayAlign
Specifies the byte alignment for the overlay planes.
dwTextureAlign
Specifies the byte alignment for textures.
dwZBufferAlign
Specifies the byte alignment for the depth buffer.
dwAlphaAlign
Specifies the byte alignment for alpha.
pvPrimary
Kernel-mode pointer to the beginning of the primary surface.

Comments

The VIDEOMEMORYINFO structure is a member of the DD_HALINFO structure. It is initialized and returned by the driver’s DrvGetDirectDrawInfo routine.


Chapter 5 VideoPort Functions

This chapter describes the VideoPort Xxx functions. These functions comprise the video port driver, which is a system-supplied component provided to support kernel-mode video miniport drivers.

References for the VideoPort Xxx functions are provided here in alphabetic order.


VideoDebugPrint

VOID VideoDebugPrint(

ULONG DebugPrintLevel,

PCHAR DebugMessage,

...

);

VideoDebugPrint prints the specified debug message to the kernel debugger.

Parameters

DebugPrintLevel
Specifies the level of debug messages to be printed to. This parameter can be 0 through 3, where 3 is the most verbose. The video port compares this parameter to the global debugging variable set in the registry. If DebugPrintLevel is equal to or less than the global debugging variable, the message is printed.
DebugMessage
Points to a string containing the debug message to be printed.

Comments

The video port driver checks the registry for the following entry at startup:

\\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\
Services\DriverName\Device N\VideoDebugLevel:REG_DWORD:n

where:

DriverName is the video miniport’s installed name;

Device N is the installed device number (for example, Device0);

n is the video port's global debug level setting, from 0 to 3, for this miniport driver.

If the VideoDebugLevel entry doesn’t exist in the registry, the video port driver uses a default value of 0, meaning that no debug statements will be printed.

The compiler eliminates references to VideoDebugPrint for free builds of the video port and video miniport.

Miniport drivers must not call VideoPortDebugPrint.

See Also

VideoPortLogError


VideoPortCompareMemory

ULONG VideoPortCompareMemory(

PVOID Source1,

PVOID Source2,

ULONG Length

);

VideoPortCompareMemory compares two blocks of system memory, byte-by-byte, and returns the number of compared bytes that are equivalent.

Parameters

Source1
Points to the first block of memory to compare.
Source2
Points to the second block of memory to compare.
Length
Specifies the number of bytes to be compared.

Return Value

VideoPortCompareMemory returns the number of bytes that are equivalent. If both blocks are equivalent, it returns the Length parameter.

The given Source1 and Source2 must be in a mapped logical range returned by VideoPortGetDeviceBase and/or a system-space RAM address, such as an address on the stack. Attempting to compare the contents of a device-memory block, such as an adapter frame buffer or ROM block, can cause access violations in certain Windows NT RISC-based platforms.

See Also

VideoPortGetDeviceBase, VideoPortMoveMemory, VideoPortScanRom, VideoPortZeroDeviceMemory, VideoPortZeroMemory


VideoPortDisableInterrupt

VP_STATUS VideoPortDisableInterrupt(

PVOID HwDeviceExtension

);

VideoPortDisableInterrupt disables interrupts from a video adapter. As a result, interrupts coming from the device are ignored by the operating system and are not forwarded to the driver.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.

Return Value

VideoPortDisableInterrupt returns NO_ERROR if it disabled calls to the miniport’s HwVidInterrupt function. Otherwise, it can return ERROR_INVALID_FUNCTION.

Comments

A call to VideoPortDisableInterrupt is valid only if the interrupt is defined, thereby providing the appropriate data at initialization time to set up the interrupt and the entry point of the miniport’s HwVidInterrupt function. After a successful call to VideoPortDisableInterrupt, interrupts from the caller’s adapter remain disabled until the miniport calls VideoPortEnableInterrupt.

The reciprocal call to VideoPortEnableInterrupt should follow the call to VideoPortDisableInterrupt.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortDisableInterrupt.

See Also

HwVidInterrupt, VIDEO_HW_INITIALIZATION_DATA, VideoPortEnableInterrupt


VideoPortEnableInterrupt

VP_STATUS VideoPortEnableInterrupt(

PVOID HwDeviceExtension

);

VideoPortEnableInterrupt re-enables interrupts from a video adapter after a call to VideoPortDisableInterrupt.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.

Return Value

VideoPortEnableInterrupt returns NO_ERROR if it re-enabled calls to the miniport’s HwVidInterrupt function. Otherwise, it can return ERROR_INVALID_FUNCTION.

Comments

A call to VideoPortEnableInterrupt is valid only if the interrupt is defined, thereby providing the appropriate data at initialization time to set up the interrupt and the entry point of the miniport’s HwVidInterrupt function.

Even if the caller has set up a HwVidInterrupt function, a call to VideoPortEnableInterrupt is valid only following a preceding call to VideoPortDisableInterrupt.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortEnableInterrupt.

See Also

HwVidInterrupt, VIDEO_HW_INITIALIZATION_DATA, VideoPortDisableInterrupt


VideoPortFreeDeviceBase

VOID VideoPortFreeDeviceBase(

PVOID HwDeviceExtension,

PVOID MappedAddress

);

VideoPortFreeDeviceBase frees a range of bus-relative device I/O ports or memory addresses previously mapped into the system address space by calling VideoPortGetDeviceBase.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
MappedAddress
Specifies the base address of the mapped range to be freed. This value must be the same as the value returned by a preceding call to VideoPortGetDeviceBase.

Comments

VideoPortFreeDeviceBase must be called from the miniport’s HwVidFindAdapter function if the miniport has already mapped an address range for an adapter it cannot support or does not use any longer.

When this occurs, the miniport also must release its claim on the corresponding hardware resources in the registry. To release all claims on resources for a particular adapter, call VideoPortVerifyAccessRanges or VideoPortGetAccessRanges with the NumAccessRanges parameter set to zero. To release claims on selected access ranges, do the following:

  1. Modify the access ranges array of claimed bus-relative ranges for the adapter by setting the appropriate elements’ RangeLength(s) to zero. Leave the current specification as is in all elements on which the miniport will not release its claims.

  2. Call VideoPortVerifyAccessRanges with this modified AccessRanges array.

See Also

HwVidFindAdapter, VideoPortGetAccessRanges, VideoPortGetDeviceBase, VideoPortVerifyAccessRanges


VideoPortGetAccessRanges

VP_STATUS VideoPortGetAccessRanges(
PVOID HwDeviceExtension, |
ULONG NumRequestedResources, |
PIO_RESOURCE_DESCRIPTOR RequestedResources, | /* optional */
ULONG NumAccessRanges, |
PVIDEO_ACCESS_RANGE AccessRanges, |
PVOID VendorId, |
PVOID DeviceId, |
PULONG Slot |
); |

VideoPortGetAccessRanges retrieves bus-relative configuration information and, if possible, claims these hardware resources in the registry for the caller.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
NumRequestedResources
Specifies the number of elements in the RequestedResources array, described next.
RequestedResources
An array of IO_RESOURCE_DESCRIPTOR-type elements. Each descriptor specifies a single hardware resource that the miniport needs, prefers, or can use as an alternative to that specified in another array element. For detailed information about this structure, see the description of IoAssignResources.
NumAccessRanges
Specifies the number of elements in the AccessRanges array, described next.
AccessRanges
Points to an area on the stack or to a static structure in the miniport driver to which VideoPortGetAccessRanges returns an array of ACCESS_RANGE-type elements filled with the bus-relative device memory ranges for the adapter.
VendorId
Should be set to NULL if the adapter is not a PCI device. Otherwise, this points to a USHORT-type value, allocated by the PCI SIG, that identifies the manufacturer of the adapter.
DeviceId
Should be set to NULL if the adapter is not a PCI device. Otherwise, this points to a USHORT value that identifies a particular PCI video adapter model. This value is assigned by the manufacturer.
Slot
Points to a variable specifying the logical slot number or location of the adapter, depending on the type of bus. For a PCI adapter, Slot points to a PCI_SLOT_NUMBER structure that locates the adapter on the PCI bus on return from VideoPortGetAccessRanges.

Return Value

VideoPortGetAccessRanges returns NO_ERROR if it successfully filled in the AccessRanges information or returned configuration information at RequestedResources.

Comments

VideoPortGetAccessRanges can be called only from a miniport’s HwVidFindAdapter function.

For most miniport drivers, VideoPortGetAccessRanges can retrieve, verify, and claim the bus-relative access ranges and any interrupt and/or DMA channel/port used by a particular video adapter, while VideoPortVerifyAccessRanges can only verify and claim miniport-specified resources. That is, for all configuration information that it returns, VideoPortGetAccessRanges claims the corresponding hardware resources in the registry for the caller. A miniport driver need not call VideoPortVerifyAccessRanges with the returned bus-relative configuration information, unless the miniport driver attempts to modify any of the returned values.

Each successful call to VideoPortGetAccessRanges or VideoPortVerifyAccessRanges for a particular adapter overwrites the miniport’s preceding claim on hardware resources in the registry.

After a successful call to VideoPortGetAccessRanges, the miniport driver must map the returned bus-relative ranges to logical ranges with VideoPortGetDeviceBase before calling the appropriate VideoPortRead/Write Xxx function to communicate with the adapter.

Usually, the miniport driver of a PCI device should have its HwVidFindAdapter function call VideoPortGetAccessRanges, rather than attempting to manipulate the nondevice-specific PCI_COMMON_CONFIG information returned by a call to VideoPortGetBusData. Such a driver generally can call VideoPortGetAccessRanges with a NULL RequestedResources pointer. The video port driver then uses the PCI bus’s configuration space to determine the resources for the video adapter. Such a miniport can call VideoPortGetAccessRanges with a set of driver-supplied RequestedResources specifications if its original call fails to return valid configuration data for the adapter.

Note that miniport drivers of adapters on other types of I/O buses also can call VideoPortGetAccessRanges. These drivers should call VideoPortGetAccessRanges with a RequestedResources pointer to a driver-supplied array of I/O resource descriptors.

If the HwVidFindAdapter function claims bus-relative access ranges and possibly other hardware resources for an adapter but then determines that it does not support the adapter, the miniport driver must erase its claims on hardware resources in the registry by calling VideoPortGetAccessRanges or VideoPortVerifyAccessRanges with the NumAccessRanges parameter set to zero.

See Also

HwVidFindAdapter, IoAssignResources, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER, VIDEO_PORT_CONFIG_INFO, VideoPortGetBusData, VideoPortGetDeviceBase, VideoPortSetBusData, VideoPortVerifyAccessRanges


VideoPortGetBusData

ULONG VideoPortGetBusData(

PVOID HwDeviceExtension,

BUS_DATA_TYPE BusDataType,

ULONG SlotNumber,

PVOID Buffer,

ULONG Offset,

ULONG Length

);

VideoPortGetBusData returns bus-type-specific configuration information.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
BusDataType
Specifies the type of bus configuration data to return. Currently, the value of this parameter can be one of the following:

Cmos
EisaConfiguration
Pos
PCIConfiguration

However, additional types of bus configuration will be supported in the future. The upper bound on the types supported is always MaximumBusDataType.

SlotNumber
Specifies the logical slot number or, for a Cmos BusDataType, the location of the device on the bus.

If PCIConfiguration is specified as the BusDataType, this parameter must be specified as a PCI_SLOT_NUMBER-type value.

Buffer
Points to a buffer into which VideoPortGetBusData returns the configuration information. The contents of the buffer depend on the BusDataType, as follows:
  • If Cmos is specified, the buffer contains the contents of the CMOS (bus number equals zero) or ECMOS (bus number equals one) locations, starting with the location specified for SlotNumber. A miniport’s HwVidFindAdapter function can determine the number of the bus from the SystemIoBusNumber member of the VIDEO_PORT_CONFIG_INFO input structure.

  • If EisaConfiguration is specified, the buffer contains the CM_EISA_SLOT_INFORMATION structure, followed by zero or more CM_EISA_FUNCTION_INFORMATION structures for the specified slot.

  • If Pos is specified, the buffer contains a CM_MCA_POS_DATA structure for the specified slot.

  • If PCIConfiguration is specified, the buffer contains the PCI_COMMON_CONFIG information for the specified slot.
Offset
Zero for all BusInterfaceType values except PCIConfiguration. Otherwise, this specifies the starting offset within the PCI_COMMON_CONFIG structure from which the requested information should be returned. Callers can use PCI_COMMON_HDR_LENGTH to specify the device-specific area of PCI_COMMON_CONFIG.
Length
Specifies the maximum number of bytes to return in Buffer.

Return Value

VideoPortGetBusData returns the number of bytes of configuration information it has written in Buffer. If the given BusDataType is not valid for the current platform, this function usually returns zero.

When the BusDataType parameter is PCIConfiguration, VideoPortGetBusData can return either of the following values to indicate an error:
Value | Meaning
0 | The PCI bus does not exist.
2 | The PCI bus exists, but there is no device at the given PCI SlotNumber. The Buffer contains the value PCI_INVALID_VENDOR_ID at the PCI_COMMON_CONFIG VendorId member.

Comments

VideoPortGetBusData can be called only from a miniport’s HwVidFindAdapter function.

Drivers of PCI video adapters usually call VideoPortGetAccessRanges rather than VideoPortGetBusData.

See Also

CM_EISA_FUNCTION_INFORMATION, CM_EISA_SLOT_INFORMATION, CM_MCA_POS_DATA, HwVidFindAdapter, HwVidQueryDeviceCallback, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER, VIDEO_PORT_CONFIG_INFO, VideoPortGetAccessRanges, VideoPortGetDeviceBase, VideoPortGetDeviceData, VideoPortGetRegistryParameters


VideoPortGetCurrentIrql

UCHAR VideoPortGetCurrentIrql( );

Miniport drivers can ignore this function. VideoPortGetCurrentIrql is a debugging function that can be called only in checked builds of the system and only from pageable code. It ASSERTs if the caller is currently running at IRQL > APC_LEVEL. (Calls to pageable code from IRQL > APC_LEVEL cause fatal page faults.)


VideoPortGetDeviceBase

PVOID VideoPortGetDeviceBase(

PVOID HwDeviceExtension,

PHYSICAL_ADDRESS IoAddress,

ULONG NumberOfUchars,

UCHAR InIoSpace

);

VideoPortGetDeviceBase maps a range of bus-relative device memory or I/O addresses into the system (kernel) address space.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
IoAddress
Specifies the base physical address of the range to be mapped. This is a bus-relative value obtained by calling VideoPortGetBusData, VideoPortGetDeviceData, VideoPortGetRegistryParameters, or VideoPortGetAccessRanges, or else this value is a driver-supplied default base address for the device memory or I/O ports. However obtained, the range described by IoAddress and NumberOfUchars must have been successfully claimed in the registry with a preceding call to VideoPortVerifyAccessRanges or VideoPortGetAccessRanges.
NumberOfUchars
Specifies the number of bytes, starting at the given IoAddress, to be mapped.
InIoSpace
Specifies that the range is in system I/O space if the value is TRUE. FALSE indicates that the range is in memory space.

Return Value

VideoPortGetDeviceBase returns a mapped logical base address suitable for use with the VideoPortRead Xxx, VideoPortWrite Xxx, and all VideoPort..Memory functions except VideoPortMapMemory and VideoPortUnmapMemory. If the given bus-relative range cannot be mapped, it returns NULL.

Comments

After VideoPortGetDeviceBase has performed the mapping, the miniport driver can communicate with its adapter by calling the VideoPortRead/Write Xxx functions, using the mapped logical addresses. A miniport driver also must use mapped logical addresses to call VideoPortCompareMemory, VideoPortMoveMemory, VideoPortZeroDeviceMemory, and VideoPortZeroMemory. VideoPortGetDeviceBase must be called from the miniport’s HwVidFindAdapter, HwVidQueryDeviceCallback, or HwVidQueryNamedValueCallback function.

The mapped base address returned by VideoPortGetDeviceBase and length of the mapped access range should be stored in the HwDeviceExtension for later use before HwVidFindAdapter returns control.

Access to the mapped address space must follow these rules:

  • If the input value for InIoSpace is TRUE, indicating that the address is in I/O space, the returned mapped logical address should be used with the VideoPortRead/WritePort Xxx and VideoPortRead/WritePortBuffer Xxx functions where Xxx is one of Uchar, Ushort, or Ulong.

  • If the input value for InIoSpace is FALSE, indicating the address is not in I/O space but in memory space, the returned mapped logical address should be used with the VideoPortRead/WriteRegister Xxx and VideoPortRead/WriteRegisterBuffer Xxx functions, where Xxx is one of Uchar, Ushort, or Ulong.

  • The driver must not access addresses outside the range delimited by the given NumberOfUchars.

If a miniport driver finds that it does not support an adapter for which it has mapped a logical range, it must call VideoPortFreeDeviceBase to unmap the previously mapped range from the system address space, and release its claims on the range in the registry with VideoPortGetAccessRanges or VideoPortVerifyAccessRanges, before it returns control to the DriverEntry function.

See Also

HwVidFindAdapter, HwVidQueryDeviceCallback, HwVidQueryNamedValueCallback, VideoPortCompareMemory, VideoPortFreeDeviceBase, VideoPortGetAccessRanges, VideoPortGetBusData, VideoPortGetDeviceData, VideoPortGetRegistryParameters, VideoPortMoveMemory, VideoPortRead Xxx, VideoPortVerifyAccessRanges, VideoPortWrite Xxx, VideoPortZeroDeviceMemory, VideoPortZeroMemory


VideoPortGetDeviceData

VP_STATUS VideoPortGetDeviceData(

PVOID HwDeviceExtension,

VIDEO_DEVICE_DATA_TYPE DeviceDataType,

PMINIPORT_QUERY_DEVICE_ROUTINE CallbackRoutine,

PVOID Context

);

VideoPortGetDeviceData retrieves system-detected configuration information from the ..\Machine\Hardware\Description tree in the registry. This information is bus-specific or adapter-specific and stored in the registry by the system loader or the HAL.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
DeviceDataType
Specifies the type of data being requested as a VIDEO_DEVICE_DATA_TYPE value. Usually, this is one of the following values:

VpBusData
VpControllerData
VpMonitorData

The VpControllerData and VpMonitorData values have meaning only on ARC-compliant platforms. Miniport drivers of x86-type video adapters usually specify VpBusData, particularly for adapters on EISA or MCA buses. The VpMachineData value is defined for future use.

CallbackRoutine
Points to a driver-supplied HwVidQueryDeviceCallback function to be called with the requested information.
Context
Points to a caller-determined context parameter to be passed to the CallbackRoutine. Usually, it points to the VIDEO_PORT_CONFIG_INFO buffer.

Return Value

VideoPortGetDeviceData returns NO_ERROR if it successfully called the miniport’s HwVidQueryDeviceCallback function with configuration information.

Comments

VideoPortGetDeviceData can be called only from a miniport’s HwVidFindAdapter function.

The registry tree from which VideoPortGetDeviceData retrieves configuration information is volatile: that is, it is re-created by the system loader or HAL every time the system is loaded. Because this information is collected and stored early in the boot process, the bus-relative configuration information returned by VideoPortGetBusData can be more complete.

See Also

HwVidFindAdapter, HwVidQueryDeviceCallback, VIDEO_PORT_CONFIG_INFO, VideoPortGetRegistryParameters


VideoPortGetRegistryParameters

VP_STATUS VideoPortGetRegistryParameters(

PVOID HwDeviceExtension,

PWSTR ParameterName,

UCHAR IsParameterFileName,

PMINIPORT_GET_REGISTRY_ROUTINE CallbackRoutine,

PVOID Context

);

VideoPortGetRegistryParameters retrieves adapter-specific configuration information under the ..\CurrentControlSet\Services\DriverName\Device Number key in the registry. VideoPortGetRegistryParameters automatically searches for the specified value entry name in the key for the miniport’s current device.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
ParameterName
Points to a zero-terminated Unicode string that names the value entry to be retrieved from the registry.
IsParameterFileName
If the value is TRUE, the data value normally returned is treated as a file name, and the buffered contents of that file are returned.
CallbackRoutine
Points to the miniport’s HwVidQueryNamedValueCallback function.
Context
Points to a caller-determined context parameter to be passed to the CallbackRoutine. Usually, it points to the VIDEO_PORT_CONFIG_INFO buffer.

Return Value

VideoPortGetRegistryParameters returns NO_ERROR if it successfully collected the requested information and called the miniport’s HwVidQueryNamedValueCallback function.

Comments

VideoPortGetRegistryParameters can be called only from a miniport’s HwVidFindAdapter or HwVidInitialize function.

The registry key from which VideoPortGetRegistryParameters retrieves configuration information cannot contain any information about the corresponding display driver, because miniport HwVidFindAdapter functions execute before any user-mode display driver is loaded.

Miniports should not query configuration information stored in the DefaultSettings entries, which are not guaranteed to be defined in future versions of Windows NT.

See Also

HwVidFindAdapter, HwVidInitialize, HwVidQueryDeviceCallback, HwVidQueryNamedValueCallback, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceData


VideoPortInitialize

VP_STATUS VideoPortInitialize(
PVOID Argument1, |
PVOID Argument2, |
PVIDEO_HW_INITIALIZATION_DATA HwInitializationData, |
PVOID HwContext | /* optional */
); |

VideoPortInitialize performs the initialization of the driver, allocating system resources for the miniport.

Parameters

Argument1
A pointer with which the operating system called DriverEntry.
Argument2
A second pointer with which the operating system called DriverEntry.
HwInitializationData
Points to the VIDEO_HW_INITIALIZATION_DATA structure on the stack. This structure has been zero-initialized and then filled in by DriverEntry with driver-specific configuration information describing a video adapter that the miniport can support.
HwContext
Points to a driver-supplied context parameter to be passed to the miniport driver’s HwVidFindAdapter function. This parameter can be NULL.

Return Value

VideoPortInitialize returns the final status of the initialization operation.

Comments

Every video miniport driver must call VideoPortInitialize from its DriverEntry function. The DriverEntry function can call VideoPortInitialize only after it has first zeroed and then set up the VIDEO_HW_INITIALIZATION_DATA structure. VideoPortInitialize can be called only from a miniport’s DriverEntry function.

VideoPortInitialize allocates resources for the miniport driver, allocates a buffer for and initializes as much configuration information as it can in the VIDEO_PORT_CONFIG_INFO structure, and then calls the miniport’s HwVidFindAdapter function.

Assuming the miniport’s HwVidFindAdapter function finds a video adapter it supports, VideoPortInitialize allocates and sets up system resources for the miniport before it returns control to the DriverEntry function. For example, VideoPortInitialize sets up a symbolic link between the named device object it creates to represent the video adapter and an alias so the corresponding display driver can open the video adapter.

If a miniport can support video adapters on different types of I/O buses, such as Isa and MicroChannel type buses, it should call VideoPortInitialize for each supported interface type.

If a miniport’s DriverEntry function calls VideoPortInitialize more than once for different types of I/O buses, DriverEntry must return the smallest of the values returned by VideoPortInitialize.

See Also

DriverEntry, HwVidFindAdapter, HwVidInitialize, VIDEO_HW_INITIALIZATION_DATA, VIDEO_PORT_CONFIG_INFO, VideoPortZeroMemory


VideoPortInt10

VP_STATUS VideoPortInt10(

PVOID HwDeviceExtension,

PVIDEO_X86_BIOS_ARGUMENTS BiosArguments

);

VideoPortInt10 performs the equivalent of an MS-DOS INT10 operation, such as setting the video mode. VideoPortInt10 runs the BIOS ROM code on the device.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
BiosArguments
Points to a structure containing values for the x86 registers that should be set before making the BIOS call. The miniport driver should set any unused registers to zero. All values set up in the VIDEO_X86_BIOS_ARGUMENTS structure are interpreted as immediate values.

Return Value

VideoPortInt10 returns NO_ERROR if it successfully called the given BIOS INT10. Otherwise, it returns an error status.

Comments

Usually, VGA-compatible miniport drivers, which support full-screen MS-DOS applications on x86-based machines, call VideoPortInt10. Such a driver’s HwVidFindAdapter function must have set up the VdmPhysicalVideoMemoryAddress and VdmPhysicalVideoMemoryLength for the range in the VIDEO_PORT_CONFIG_INFO structure.

However, other video miniport drivers also can call this function.

Because VideoPortInt10 interprets the BiosArgument parameter values as immediates, the caller cannot pass in or retrieve data from a memory buffer with this function.

See Also

HwVidFindAdapter, VIDEO_PORT_CONFIG_INFO, VIDEO_X86_BIOS_ARGUMENTS, VideoPortGetDeviceBase


VideoPortLogError

VOID VideoPortLogError(
PVOID HwDeviceExtension, |
PVIDEO_REQUEST_PACKET Vrp, | /* optional */
VP_STATUS ErrorCode, |
ULONG UniqueId |
); |

VideoPortLogError logs errors to the system event log when a miniport driver detects a hardware error condition during I/O operations.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
Vrp
Points to the video request packet if one is associated with the error. Otherwise, this parameter is NULL.
ErrorCode
Specifies a miniport-defined error code indicating the type of hardware error.
UniqueId
Specifies a unique identifier for the error. This value differentiates the current error from other errors with the same ErrorCode. For some miniport drivers, this identifies the line of code where the error was detected; for others, it is a value returned by the hardware.

Comments

Miniport drivers should call VideoPortLogError to notify the user if the driver encounters unusual hardware errors during normal operations. Posting such errors to the system event log warns the user that the video adapter might be failing so the user can replace (or reconfigure) the adapter before a total hardware failure occurs.

However, miniport drivers should not log errors, such as “failed to detect hardware,” that occur frequently during normal operation.

VideoPortLogError can be called from a miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function.

See Also

VideoDebugPrint


VideoPortMapBankedMemory

VP_STATUS VideoPortMapBankedMemory(

PVOID HwDeviceExtension,

PHYSICAL_ADDRESS PhysicalAddress,

PULONG Length,

PULONG InIoSpace,

PVOID *VirtualAddress,

ULONG BankLength,

UCHAR ReadWriteBank,

PBANKED_SECTION_ROUTINE BankRoutine,

PVOID Context

);

VideoPortMapBankedMemory remaps a bus-relative physical range of video memory into the corresponding display driver’s virtual address space in response to a VRP with the IoControlCode member set to IOCTL_VIDEO_SHARE_VIDEO_MEMORY and possibly to IOCTL_VIDEO_MAP_VIDEO_MEMORY.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
PhysicalAddress
Specifies the bus-relative base address of the range to be mapped.
Length
Points to a variable specifying the total number of bytes of device memory to be mapped (regardless of bank size). VideoPortMapBankedMemory returns the actual size of the memory mapped, which can be rounded to a system-determined alignment boundary, in this parameter. However, the miniport and display drivers cannot access memory outside the range delimited by the input value at Length.
InIoSpace
Points to a variable indicating whether the range is in I/O space or memory space. TRUE indicates I/O space.
VirtualAddress
Points to a variable that specifies a process handle, which usually should be set to zero. Such a handle can also be a value passed in a VRP by the corresponding display driver. Zero specifies that the port driver should map the logical memory range at any location in the address space of the current process. On return from a successful call, VideoPortMapBankedMemory resets the variable to the base virtual address to which it has mapped the given PhysicalAddress.
BankLength
Specifies the size of a Bank, in bytes.
ReadWriteBank
If set to TRUE the Bank is Read\Write; if set to FALSE there are two independent Read and Write Banks.
BankRoutine
Points to a driver-supplied HwVidBankedMemoryCallback function to be called by the Memory Manager when a new bank is accessed by the display driver.
Context
Points to a miniport driver-supplied context that is passed back to the driver when the BankRoutine is called.

Return Value

VideoPortMapBankedMemory returns NO_ERROR if the given logical range was successfully mapped to a user-space virtual range. Otherwise, it can return ERROR_INVALID_PARAMETER.

Comments

VideoPortMapBankedMemory runs in kernel mode within the same context as the user-mode thread that initiated the call.

VideoPortMapBankedMemory is called by miniport drivers to efficiently manage x86 type devices that are limited to mapping a frame buffer in banks. When this routine returns to the caller, the frame buffer managed by the miniport driver is mapped as a linear frame buffer into the address space of the requesting process (see VirtualAddress parameter). When an access is made to an address in that mapped space, the Memory Manager calls the miniport driver to update the Bank Index Register to point to a new bank that contains the currently referenced address. The Bank Index Register is maintained by the miniport driver in BankRoutine. The correct Bank Index is calculated by the Memory Manager transparently to a display driver and passed to the miniport driver in the callback to BankRoutine.

Callers of VideoPortMapBankedMemory are running at IRQL = PASSIVE_LEVEL.

See Also

HwVidBankedMemoryCallback, IOCTL_VIDEO_MAP_VIDEO_MEMORY, IOCTL_VIDEO_SHARE_VIDEO_MEMORY, IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY, VIDEO_REQUEST_PACKET, VideoPortMapMemory, VideoPortUnmapMemory


VideoPortMapMemory

VP_STATUS VideoPortMapMemory(

PVOID HwDeviceExtension,

PHYSICAL_ADDRESS PhysicalAddress,

PULONG Length,

PULONG InIoSpace,

PVOID *VirtualAddress

);

VideoPortMapMemory remaps a bus-relative physical range of video memory into the corresponding display driver’s virtual address space in response to a VRP with the IoControlCode member set to IOCTL_VIDEO_MAP_VIDEO_MEMORY.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
PhysicalAddress
Specifies the bus-relative base address of the range to be mapped.
Length
Points to a variable specifying the number of bytes of device memory to be mapped. VideoPortMapMemory returns the actual size of the memory mapped, which can be rounded to a system-determined alignment boundary, in this parameter. However, the miniport and display drivers cannot access memory outside the range delimited by the input value at Length.
InIoSpace
Points to a variable indicating whether the range is in I/O space or memory space. TRUE indicates I/O space.
VirtualAddress
Points to a variable that specifies a process handle, which usually should be set to zero. Such a handle can also be a value passed in a VRP by the corresponding display driver. Zero specifies that the port driver should map the logical memory range at any location in the address space of the current process. On return from a successful call, VideoPortMapMemory resets the variable to the base virtual address to which it has mapped the given PhysicalAddress.

Return Value

VideoPortMapMemory returns NO_ERROR if the given logical range was successfully mapped to a user-space virtual range. Otherwise, it can return ERROR_INVALID_PARAMETER.

Comments

VideoPortMapMemory runs in kernel mode within the same context as the user-mode thread that initiated the call.

VideoPortMapMemory should be used by miniport drivers to manage video adapters that allow the video frame buffer to be completely mapped at all times. That is, miniport drivers for adapters that are not restricted to using Banks to map a slice at a time can use the more efficient VideoPortMapMemory.

Callers of VideoPortMapMemory are running at IRQL = PASSIVE_LEVEL.

See Also

IOCTL_VIDEO_MAP_VIDEO_MEMORY , VIDEO_REQUEST_PACKET, VideoPortUnmapMemory


VideoPortMoveMemory

VOID VideoPortMoveMemory(

PVOID Destination,

PVOID Source,

ULONG Length

);

VideoPortMoveMemory copies data from the source location to the destination location in system memory.

Parameters

Destination
Points to the destination location.
Source
Points to the location of the data to copy.
Length
Specifies the number of bytes to copy.

Comments

VideoPortMoveMemory moves memory either forward or backward, aligned or unaligned, in 4-byte blocks, followed by any remaining bytes.

The given Destination and Source must be in a mapped logical range returned by VideoPortGetDeviceBase and/or a system-space RAM address, such as an address on the stack. Attempting to transfer data to or from a device-memory block, such as an adapter frame buffer or ROM block, can cause access violations in certain Windows NT® RISC-based platforms.

See Also

VideoPortCompareMemory, VideoPortGetDeviceBase, VideoPortZeroDeviceMemory, VideoPortZeroMemory


VideoPortReadPortBufferUchar

VOID VideoPortReadPortBufferUchar(

PUCHAR Port,

PUCHAR Buffer,

ULONG Count

);

VideoPortReadPortBufferUchar reads a number of bytes from a mapped I/O port into a buffer.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer into which the UCHAR values are read.
Count
Specifies the number of bytes to be transferred into the buffer.

Comments

The buffer must be large enough to contain at least the specified number of bytes.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadPortBufferUchar.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadPortBufferUlong

VOID VideoPortReadPortBufferUlong(

PULONG Port,

PULONG Buffer,

ULONG Count

);

VideoPortReadPortBufferUlong reads a number of ULONG values from a mapped I/O port into a buffer.

Parameters

Port
Points to the I/O port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer into which the ULONG values are read.
Count
Specifies the number of ULONG values to be transferred into the buffer.

Comments

The buffer must be large enough to contain at least the specified number of ULONG values.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadPortBufferUlong.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadPortBufferUshort

VOID VideoPortReadPortBufferUshort(

PUSHORT Port,

PUSHORT Buffer,

ULONG Count

);

VideoPortReadPortBufferUshort reads a number of USHORT values from a mapped I/O port into a buffer.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer into which an array of USHORT values is read.
Count
Specifies the number of USHORT values to be transferred into the buffer.

Comments

The buffer must be large enough to contain at least the specified number of USHORT values.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadPortBufferUshort.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadPortUchar

UCHAR VideoPortReadPortUchar(

PUCHAR Port

);

VideoPortReadPortUchar reads a byte from a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.

Return Value

VideoPortReadPortUchar returns the byte read from the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadPortUchar.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadPortUlong

ULONG VideoPortReadPortUlong(

PULONG Port

);

VideoPortReadPortUlong reads a ULONG value from a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.

Return Value

VideoPortReadPortUlong returns the ULONG value read from the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadPortUlong.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadPortUshort

USHORT VideoPortReadPortUshort(

PUSHORT Port

);

VideoPortReadPortUshort reads a USHORT value from a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.

Return Value

VideoPortReadPortUshort returns the USHORT value read from the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadPortUshort.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadRegisterBufferUchar

VOID VideoPortReadRegisterBufferUchar(

PUCHAR Register,

PUCHAR Buffer,

ULONG Count

);

VideoPortReadRegisterBufferUchar reads a number of bytes from a mapped device memory range into a buffer.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer into which the UCHAR values are read.
Count
Specifies the number of bytes to be transferred into the buffer.

Comments

The buffer must be large enough to contain at least the specified number of bytes.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadRegisterBufferUchar.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadRegisterBufferUlong

VOID VideoPortReadRegisterBufferUlong(

PULONG Register,

PULONG Buffer,

ULONG Count

);

VideoPortReadRegisterBufferUlong reads a number of ULONG values from a mapped device memory range into a buffer.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer into which the ULONG values are read.
Count
Specifies the number of ULONG values to be transferred into the buffer.

Comments

The buffer must be large enough to contain at least the specified number of ULONG values.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadRegisterBufferUlong.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadRegisterBufferUshort

VOID VideoPortReadRegisterBufferUshort(

PUSHORT Register,

PUSHORT Buffer,

ULONG Count

);

VideoPortReadRegisterBufferUshort reads a number of USHORT values from a mapped device memory range into a buffer.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer into which the USHORT values are read.
Count
Specifies the number of USHORT values to be transferred into the buffer.

Comments

The buffer must be large enough to contain at least the specified number of USHORT values.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadRegisterBufferUshort.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadRegisterUchar

UCHAR VideoPortReadRegisterUchar(

PUCHAR Register

);

VideoPortReadRegisterUchar reads a byte from a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.

Return Value

VideoPortReadRegisterUchar returns the byte read from the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadRegisterUchar.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadRegisterUlong

ULONG VideoPortReadRegisterUlong(

PULONG Register

);

VideoPortReadRegisterUlong reads a ULONG value from a mapped register range.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.

Return Value

VideoPortReadRegisterUlong returns the ULONG value read from the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadRegisterUlong.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortReadRegisterUshort

USHORT VideoPortReadRegisterUshort(

PUSHORT Register

);

VideoPortReadRegisterUshort reads a USHORT value from a mapped register range.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.

Return Value

VideoPortReadRegisterUshort returns the USHORT value read from the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortReadRegisterUshort.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase


VideoPortScanRom

BOOLEAN VideoPortScanRom(

PVOID HwDeviceExtension,

PUCHAR RomBase,

ULONG RomLength,

PUCHAR String

);

VideoPortScanRom performs a case-sensitive search for a specified string in ROM.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
RomBase
Specifies the base ROM address at which the search should start. The given RomBase must be in a mapped range returned by VideoPortGetDeviceBase.
RomLength
Specifies the size, in bytes, of the mapped ROM to be searched.
String
Points to the string in the driver or on the stack to search for.

Return Value

If the string is found, VideoPortScanRom returns TRUE.

Comments

VideoPortScanRom can be called only by a miniport’s HwVidFindAdapter function.

See Also

HwVidFindAdapter, VideoPortGetDeviceBase


VideoPortSetBusData

ULONG VideoPortSetBusData(

PVOID HwDeviceExtension,

BUS_DATA_TYPE BusDataType,

ULONG SlotNumber,

PVOID Buffer,

ULONG Offset,

ULONG Length

);

VideoPortSetBusData sets bus-configuration data for an adapter on a dynamically configurable I/O bus with a published, standard interface.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
BusDataType
Specifies the type of bus data to be set. Currently, its value can be PCIConfiguration. However, additional types of standardized, dynamically configurable buses will be supported in the future. The upper bound on the bus types supported is always MaximumBusDataType.
SlotNumber
Specifies the logical slot number. When PCIConfiguration is specified, this is a PCI_SLOT_NUMBER-type value.
Buffer
Points to a caller-supplied storage area with configuration information specific to BusDataType.

When PCIConfiguration is specified, the buffer contains some or all of the PCI_COMMON_CONFIG information for the given SlotNumber. The specified Offset and Length determine how much information is supplied.

Offset
Specifies the byte offset within the PCI_COMMON_CONFIG structure at which the caller-supplied configuration values begin. A miniport driver can use PCI_COMMON_HDR_LENGTH to specify the offset of the device-specific area in PCI_COMMON_CONFIG.
Length
Specifies the number of bytes in Buffer.

Return Value

VideoPortSetBusData returns the number of bytes of data successfully set for the given SlotNumber. If the given BusDataType is not valid for the current platform or if the supplied information is invalid, VideoPortSetBusData returns zero.

Comments

Miniport drivers of adapters on a PCI bus seldom call VideoPortSetBusData, unless unusual circumstances or the nature of a particular driver’s video adapter requires such a call.

For example, a miniport driver might call VideoPortSetBusData to clear a bit in the PCI status register if its adapter signals a target abort during initialization. If a PCI video adapter must be configured with device-specific data, its driver also calls this function. In either case, such a driver is then likely to call VideoPortGetAccessRanges with a RequestedResources pointer to a driver-supplied array of resource descriptors.

VideoPortSetBusData can be called only from the miniport’s HwVidFindAdapter function.

See Also

HalSetBusDataByOffset, HwVidFindAdapter, PCI_COMMON_CONFIG, PCI_SLOT_NUMBER, VideoPortGetAccessRanges, VideoPortGetBusData


VideoPortSetRegistryParameters

VP_STATUS VideoPortSetRegistryParameters(

PVOID HwDeviceExtension,

PWSTR ValueName,

PVOID ValueData,

ULONG ValueLength

);

VideoPortSetRegistryParameters writes information to the registry, in the miniport-specific and, possibly, adapter-specific ..CurrentControlSet\Services\DriverName\Device Number key.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
ValueName
Points to a buffer containing a zero-terminated Unicode string that names the value entry for which data is being written in the registry.
ValueData
Points to a buffer containing the values to be written for the ValueName entry.
ValueLength
Specifies the size, in bytes, of the data to be written to the registry.

Return Value

VideoPortSetRegistryParameters returns NO_ERROR if the given data was successfully written to the registry. Otherwise, it can return ERROR_INVALID_PARAMETER.

Comments

VideoPortSetRegistryParameters automatically searches for or creates the given ValueName in the \Device Number subkey of the current driver for the current device.

VideoPortSetRegistryParameters can be called only from the miniport’s HwVidFindAdapter or HwVidInitialize function.

See Also

HwVidFindAdapter, HwVidInitialize, VideoPortGetRegistryParameters


VideoPortSetTrappedEmulatorPorts

VP_STATUS VideoPortSetTrappedEmulatorPorts(

PVOID HwDeviceExtension,

ULONG NumAccessRanges,

PVIDEO_ACCESS_RANGE AccessRange

);

VGA-compatible (SVGA) miniport drivers call VideoPortSetTrappedEmulatorPorts to dynamically change the list of I/O ports that are trapped when a VDM runs in full-screen mode on an x86-based machine.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
NumAccessRanges
Specifies the number of elements in the AccessRange array.
AccessRange
Points to an array of VIDEO_ACCESS_RANGE elements. Each element describes a proper subrange of the EmulatorAccessEntries that the miniport set up in VIDEO_PORT_CONFIG_INFO. Setting the RangeVisible member of an AccessRange element to TRUE enables direct access to the I/O port range by the full-screen MS-DOS application. Setting a RangeVisible member to FALSE causes application-issued INs, INSB/INSW/INSDs, OUTs and/or OUTSB/OUTSW/OUTSDs to that range to be trapped and forwarded to the corresponding miniport SvgaHwIoPort Xxx function for validation.

The AccessRange array passed to VideoPortSetTrappedEmulatorPorts must be a proper subset of the I/O port range(s) that the HwVidFindAdapter function set up in the EmulatorAccessEntries array of the VIDEO_PORT_CONFIG_INFO structure. Any I/O port ranges in the access ranges array that are not included in the EmulatorAccessEntries array are trapped and reflected to the user-mode VDD.

Return Value

VideoPortSetTrappedEmulatorPorts returns NO_ERROR if it successfully changed the trapped ports. Otherwise, it can return ERROR_INVALID_PARAMETER.

Comments

By default, the AccessRange of I/O ports that full-screen MS-DOS applications can access directly includes none of the access range array elements describing I/O port ranges that also have corresponding elements in the EmulatorAccessEntries array of the VIDEO_PORT_CONFIG_INFO structure. That is, I/O port ranges with corresponding emulator access entries are, by default, hooked to the miniport’s SvgaHwIoPort Xxx functions so application-issued instructions are forwarded to the SvgaHwIoPort Xxx functions for validation.

A VGA-compatible SVGA miniport driver can enable and disable ranges of I/O ports on an as-needed basis with calls to VideoPortSetTrappedEmulatorPorts. Enabled port ranges allow a full-screen MS-DOS application to access those I/O ports directly, without having application-issued assembly instructions trapped and forwarded to the miniport driver’s SvgaHwIoPort Xxx functions for validation first. Direct I/O port access for such an application gives the user faster video I/O response times.

VideoPortSetTrappedEmulatorPorts scans the AccessRange parameter array from the first element to the last, enabling and disabling I/O ports as directed in each element. Note that a range of enabled ports (the RangeVisible member set to TRUE) can be disabled again in the same call if the miniport includes a duplicate description of the same range with the RangeVisible member set to FALSE. Note also that a miniport can enable a wide range of I/O ports in the initial element of the AccessRange array and selectively disable subranges of ports in subsequent array elements when it calls VideoPortSetTrappedEmulatorPorts.

All full-screen MS-DOS applications use the same IOPM (I/O permission map) in x86-based machines and, therefore, the same set of enabled or disabled I/O ports. On each switch to such a full-screen application, a VGA-compatible miniport driver’s HwVidStartIO function is called with the IOCTL_VIDEO_ENABLE_VDM VRP. The miniport should then reinitialize a default set of directly accessible I/O ports, which can include any ports in the EmulatorAccessEntries array.

While giving full-screen MS-DOS applications on x86-based machines direct access to the video ports makes application-initiated video operations faster, every VGA-compatible SVGA miniport driver must continue to trap a subset of critical I/O ports to prevent such an application from hanging the machine. In particular, such miniports always should trap application I/O to the VGA-compatible adapter sequencer and miscellaneous output registers. Such a miniport driver also should trap and validate application-issued direct I/O that could cause the machine to hang for any additional adapter-dependent subset of I/O ports.

Calling VideoPortSetTrappedEmulatorPorts again and resetting the RangeVisible member of an AccessRange element to FALSE causes the application-issued instructions for that range to be forwarded to the corresponding miniport SvgaHwIoPort Xxx function.

See Also

EMULATOR_ACCESS_ENTRY, HwVidFindAdapter, HwVidStartIO, IOCTL_VIDEO_ENABLE_VDM, SvgaHwIoPort Xxx, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortSynchronizeExecution


VideoPortStallExecution

VOID VideoPortStallExecution(

ULONG Microseconds

);

VideoPortStallExecution retains control of the processor for the specified number of microseconds and returns to the caller.

Parameters

Microseconds
Specifies the delay interval, in microseconds.

Comments

Maximum acceptable values for Microseconds are thousands of microseconds during miniport initialization. Otherwise, the given delay interval must be no more than 50 microseconds. In general, VideoPortStallExecution can be called only if the miniport must wait for a very few microseconds for its adapter to update state.

While a miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortStallExecution, the miniport should be designed to avoid such a call if at all possible. Delays while running at high hardware priorities adversely affect the overall I/O throughput of the system and can freeze the machine.

If a miniport driver has work to be done at regular intervals of more than 50 microseconds, it should have a HwVidTimer function. Calls to a miniport-supplied HwVidTimer function at approximately one-second intervals can be enabled with VideoPortStartTimer and disabled with VideoPortStopTimer.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, HwVidTimer, VideoPortStartTimer, VideoPortStopTimer


VideoPortStartTimer

VOID VideoPortStartTimer(

PVOID HwDeviceExtension

);

VideoPortStartTimer enables calls to a miniport’s HwVidTimer function.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension. This pointer is the input parameter to the HwVidTimer function.

Comments

After a miniport driver calls VideoPortStartTimer, its HwVidTimer function is called periodically at approximate one-second intervals until the driver calls VideoPortStopTimer.

A miniport driver cannot call VideoPortStartTimer or VideoPortStopTimer if its DriverEntry function set the HwTimer member of the VIDEO_HW_INITIALIZATION_DATA structure to NULL.

A miniport’s HwVidTimer function can call VideoPortStopTimer. Only another miniport function can re-enable calls to HwVidTimer by calling VideoPortStartTimer again.

See Also

DriverEntry, HwVidTimer, VIDEO_HW_INITIALIZATION_DATA, VideoPortStopTimer


VideoPortStopTimer

VOID VideoPortStopTimer(

PVOID HwDeviceExtension

);

VideoPortStopTimer disables calls to a miniport’s HwVidTimer function.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension. This pointer is the input parameter to the HwVidTimer function.

Comments

VideoPortStopTimer is called to disable the miniport’s HwVidTimer function. After a miniport driver calls VideoPortStartTimer, its HwVidTimer function is called periodically at approximate one-second intervals until the driver calls VideoPortStopTimer.

A miniport driver cannot call VideoPortStartTimer or VideoPortStopTimer if its DriverEntry function set the HwTimer member of the VIDEO_HW_INITIALIZATION_DATA structure to NULL.

A miniport’s HwVidTimer function can call VideoPortStopTimer.

See Also

DriverEntry, HwVidTimer, VIDEO_HW_INITIALIZATION_DATA, VideoPortStartTimer


VideoPortSynchronizeExecution

BOOLEAN VideoPortSynchronizeExecution(
PVOID HwDeviceExtension, |
VIDEO_SYNCHRONIZE_PRIORITY Priority, |
PMINIPORT_SYNCHRONIZE_ROUTINE SynchronizeRoutine, |
PVOID Context | /* optional */
); |

VideoPortSynchronizeExecution synchronizes the execution of a miniport-supplied HwVidSynchronizeExecutionCallback function with the miniport’s HwVidInterrupt function, if any. Otherwise, it runs HwVidSynchronizeExecutionCallback at a raised priority.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
Priority
Specifies the type of priority at which the given SynchronizeRoutine must be run, as one of the following:
  • If Priority is set to VpLowPriority, the current thread is raised to the highest noninterrupt-masking priority. Accordingly, the current thread can be preempted only by an ISR if a device interrupts.

  • If Priority is set to VpMediumPriority and the miniport has an ISR associated with its video adapter, the call to the given SynchronizeRoutine is synchronized with the miniport’s HwVidInterrupt function. Otherwise, synchronization is made at VpHighPriority level.

  • If Priority is set to VpHighPriority, the hardware priority for the SynchronizeRoutine is raised to effectively mask out all interrupts in the system. VpHighPriority (and VpMediumPriority from a miniport without a HwVidInterrupt function) should not be specified unless absolutely necessary. If either is, the given SynchronizeRoutine must return control as quickly as possible. Delays can hang the entire system.
SynchronizeRoutine
Points to the miniport’s HwVidSynchronizeExecutionCallback function.
Context
Points to a caller-supplied context to be passed to the miniport’s HwVidSynchronizeExecutionCallback function. This pointer can be NULL.

Return Value

If the operation succeeds, VideoPortSynchronizeExecution returns TRUE.

Comments

Miniport drivers seldom call this routine unless either of the following conditions hold:

  • The driver’s HwVidInterrupt function shares memory with other miniport functions. In order to access the shared memory in a multiprocessor-safe way, such miniport functions must call VideoPortSynchronizeExecution with HwVidSynchronizeExecutionCallback. This miniport function can safely access the shared memory because the video port driver prevents the HwVidInterrupt function from accessing the same memory concurrently.

  • The adapter must be programmed with a sequence of commands without being subject to a context switch. For example, a miniport’s SvgaHwIoPort Xxx function that has buffered a sequence of I/O instructions and validated the sequence might call VideoPortSynchronizeExecution with HwVidSynchronizeExecutionCallback. This miniport function can transfer the buffered and validated I/O stream to the adapter very quickly.

A caller should specify the lowest practical Priority value for the work HwVidSynchronizeExecutionCallback must do. Any CallbackRoutine that is run at a high hardware priority (VpMediumPriority or VpHighPriority) should return control as quickly as possible. A driver with such a high-priority HwVidSynchronizeExecutionCallback function should be designed to do as much work as possible in every other driver function except its HwVidSynchronizeExecutionCallback and/or HwVidInterrupt function(s).

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, SvgaHwIoPort Xxx


VideoPortUnmapMemory

VP_STATUS VideoPortUnmapMemory(

PVOID HwDeviceExtension,

PVOID VirtualAddress,

HANDLE ProcessHandle

);

VideoPortUnmapMemory releases a mapping between a logical address range for the adapter and a virtual address range in the user-mode address space of a particular thread. This function is the reciprocal of VideoPortMapMemory and VideoPortMapBankedMemory.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
VirtualAddress
Points to a virtual address within the mapped range to be released.
ProcessHandle
Should be set to zero, or to the process handle specified when the miniport called VideoPortMapMemory.

Return Value

VideoPortUnmapMemory returns NO_ERROR if the mapping was released. Otherwise, it returns ERROR_INVALID_PARAMETER.

Comments

A miniport driver cannot release a subrange of the mapping between a logical device range and the user-space virtual address range of its corresponding display driver. Whether the VirtualAddress parameter is the base virtual address for the mapped range that was returned by VideoPortMapMemory or VideoPortMapBankedMemory, or is an offset into that mapped virtual range, VideoPortUnmapMemory releases the mapping for the full range.

See Also

VideoPortMapBankedMemory, VideoPortMapMemory


VideoPortVerifyAccessRanges

VP_STATUS VideoPortVerifyAccessRanges(

PVOID HwDeviceExtension,

ULONG NumAccessRanges,

PVIDEO_ACCESS_RANGE AccessRanges

);

VideoPortVerifyAccessRanges checks the registry for whether another driver has already claimed ownership of the specified bus-relative access ranges and any other hardware resources specified in the VIDEO_PORT_CONFIG_INFO structure. If not, this function claims the given resources for the caller.

Parameters

HwDeviceExtension
Points to the miniport driver’s device extension.
NumAccessRanges
Specifies the number of elements in the AccessRanges array.
AccessRanges
Points to the minport driver’s access ranges array. Each VIDEO_ACCESS_RANGE-type element in this array specifies a bus-relative range of device memory, I/O ports, or register addresses for the adapter.

Return Value

VideoPortVerifyAccessRanges returns one of the following values:
Value | Meaning
NO_ERROR | The given AccessRanges are valid and have been claimed for use by the caller.
ERROR_INVALID_PARAMETER | An error occurred or a conflict was found; that is, another driver has already claimed one or more of the given hardware resources for its device.

Comments

Every video miniport driver either must call VideoPortVerifyAccessRanges or use access ranges returned by VideoPortGetAccessRanges before attempting to access a video adapter during the driver (and system) initialization process.

VideoPortVerifyAccessRanges can be called only by a miniport’s HwVidFindAdapter function.

Every video miniport driver must define the bus-relative access ranges for its device, either as statically allocated memory in the driver’s header file or code or on the stack. Most miniport drivers set up their video access ranges on the stack, except for those that use PC standard address ranges for video memory, such as VGA-compatible SVGA miniports.

The HwVidFindAdapter function should try to obtain bus-relative access range information by calling VideoPortGetBusData or VideoPortGetAccessRanges, or by checking the registry through calls to VideoPortGetDeviceData or VideoPortGetRegistryParameters. If HwVidFindAdapter cannot get this information, the miniport should have a set of bus-relative default values for access ranges.

If a miniport’s access ranges are externally configurable, the installation program sets up access ranges for the adapter in the registry. Such a miniport’s HwVidFindAdapter function can call VideoPortGetRegistryParameters with a miniport-supplied HwVidQueryNamedValueCallback function that processes information retrieved from the registry.

HwVidFindAdapter must not pass any access range addresses to VideoPortGetDeviceBase unless it calls VideoPortVerifyAccessRanges or VideoPortGetAccessRanges first and the respective function returns NO_ERROR.

VideoPortVerifyAccessRanges can be called again if the initial AccessRanges specification or value in the VIDEO_PORT_CONFIG_INFO, such as an interrupt vector, causes it to return an ERROR_XXX indicating that another driver has already claimed the resource(s).

If VideoPortVerifyAccessRanges returns NO_ERROR, a subsequent call for the same adapter overwrites the miniport’s claim on resources for that adapter in the registry.

Note that a miniport driver cannot communicate with its video adapter, except by using mapped addresses returned by VideoPortGetDeviceBase with the VideoPortRead/Write Xxx functions.

If the HwVidFindAdapter function claims bus-relative access ranges and possibly other hardware resources for an adapter but then determines that it does not support the adapter, the miniport driver must erase its claims on hardware resources in the registry by calling VideoPortVerifyAccessRanges or VideoPortGetAccessRanges with the NumAccessRanges parameter set to zero.

To erase claims on a subset of claimed access ranges that the miniport is no longer using, do the following:

  1. Modify the AccessRanges specification for the adapter so that each element describing a range to be released still has RangeStart set to the bus-relative base of a claimed range but RangeLength reset to zero.

  2. Call VideoPortVerifyAccessRanges with this modified AccessRanges array.

See Also

HwVidFindAdapter, HwVidQueryDeviceCallback, HwVidQueryNamedValueCallback, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetAccessRanges, VideoPortGetBusData, VideoPortGetDeviceBase, VideoPortGetDeviceData, VideoPortGetRegistryParameters


VideoPortWritePortBufferUchar

VOID VideoPortWritePortBufferUchar(

PUCHAR Port,

PUCHAR Buffer,

ULONG Count

);

VideoPortWritePortBufferUchar writes a number of bytes to a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer containing the bytes to be written.
Count
Specifies the number of bytes to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWritePortBufferUchar.

See Also

VideoPortGetDeviceBase


VideoPortWritePortBufferUlong

VOID VideoPortWritePortBufferUlong(

PULONG Port,

PULONG Buffer,

ULONG Count

);

VideoPortWritePortBufferUlong writes a number of ULONG values to a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer containing the ULONG values to be written.
Count
Specifies the number of ULONG values to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWritePortBufferUlong.

See Also

VideoPortGetDeviceBase


VideoPortWritePortBufferUshort

VOID VideoPortWritePortBufferUshort(

PUSHORT Port,

PUSHORT Buffer,

ULONG Count

);

VideoPortWritePortBufferUshort writes a number of USHORT values to a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer containing the USHORT values to be written.
Count
Specifies the number of USHORT values to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWritePortBufferUshort.

See Also

VideoPortGetDeviceBase


VideoPortWritePortUchar

VOID VideoPortWritePortUchar(

PUCHAR Port,

UCHAR Value

);

VideoPortWritePortUchar writes a byte to a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Value
Specifies a byte to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWritePortUchar.

See Also

VideoPortGetDeviceBase


VideoPortWritePortUlong

VOID VideoPortWritePortUlong(

PULONG Port,

ULONG Value

);

VideoPortWritePortUlong writes a ULONG value to a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Value
Specifies a ULONG value to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWritePortUlong.

See Also

VideoPortGetDeviceBase


VideoPortWritePortUshort

VOID VideoPortWritePortUshort(

PUSHORT Port,

USHORT Value

);

VideoPortWritePortUshort writes a USHORT value to a mapped I/O port.

Parameters

Port
Points to the port. The given Port must be in a mapped I/O-space range returned by VideoPortGetDeviceBase.
Value
Specifies a USHORT value to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWritePortUshort.

See Also

VideoPortGetDeviceBase


VideoPortWriteRegisterBufferUchar

VOID VideoPortWriteRegisterBufferUchar(

PUCHAR Register,

PUCHAR Buffer,

ULONG Count

);

VideoPortWriteRegisterBufferUchar writes a number of unsigned bytes to a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer containing the bytes to be written.
Count
Specifies the number of bytes to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWriteRegisterBufferUchar.

See Also

VideoPortGetDeviceBase


VideoPortWriteRegisterBufferUlong

VOID VideoPortWriteRegisterBufferUlong(

PULONG Register,

PULONG Buffer,

ULONG Count

);

VideoPortWriteRegisterBufferUlong writes a number of ULONG values to a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer containing the ULONG values to be written.
Count
Specifies the number of ULONG values to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWriteRegisterBufferUlong.

See Also

VideoPortGetDeviceBase


VideoPortWriteRegisterBufferUshort

VOID VideoPortWriteRegisterBufferUshort(

PUSHORT Register,

PUSHORT Buffer,

ULONG Count

);

VideoPortWriteRegisterBufferUshort writes a number of USHORT values to a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Buffer
Points to a buffer containing the USHORT values to be written.
Count
Specifies the number of USHORT values to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWriteRegisterBufferUshort.

See Also

VideoPortGetDeviceBase


VideoPortWriteRegisterUchar

VOID VideoPortWriteRegisterUchar(

PUCHAR Register,

UCHAR Value

);

VideoPortWriteRegisterUchar writes a byte to a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Value
Specifies a byte to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWriteRegisterUchar.

See Also

VideoPortGetDeviceBase


VideoPortWriteRegisterUlong

VOID VideoPortWriteRegisterUlong(

PULONG Register,

ULONG Value

);

VideoPortWriteRegisterUlong writes a ULONG value to a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Value
Specifies a ULONG value to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWriteRegisterUlong.

See Also

VideoPortGetDeviceBase


VideoPortWriteRegisterUshort

VOID VideoPortWriteRegisterUshort(

PUSHORT Register,

USHORT Value

);

VideoPortWriteRegisterUshort writes a USHORT value to a mapped register.

Parameters

Register
Points to the register. The given Register must be in a mapped memory-space range returned by VideoPortGetDeviceBase.
Value
Specifies a USHORT value to be transferred to the adapter.

Comments

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortWriteRegisterUshort.

See Also

VideoPortGetDeviceBase


VideoPortZeroDeviceMemory

VOID VideoPortZeroDeviceMemory(

PVOID Destination,

ULONG Length

);

VideoPortZeroDeviceMemory fills an adapter frame buffer or other device memory with zeros.

Parameters

Destination
Specifies the base address of the adapter memory area. This value must be a mapped logical address returned by VideoPortGetDeviceBase.
Length
Specifies the size, in bytes, to be filled.

Comments

Miniport drivers should always call this function, rather than VideoPortZeroMemory, to fill on-adapter memory with zeros.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortZeroDeviceMemory.

See Also

HwVidInterrupt, HwVidSynchronizeExecutionCallback, VideoPortGetDeviceBase, VideoPortZeroMemory


VideoPortZeroMemory

VOID VideoPortZeroMemory(

PVOID Destination,

ULONG Length

);

VideoPortZeroMemory fills a block of system memory with zeros.

Parameters

Destination
Specifies the starting address of the block of memory. This value must be in a mapped logical range returned by VideoPortGetDeviceBase.
Length
Specifies the size, in bytes, of the block.

Comments

Miniports’ DriverEntry functions commonly call VideoPortZeroMemory to initialize the VIDEO_HW_INITIALIZATION_DATA structure with zeros.

The given Destination must be in a mapped logical range returned by VideoPortGetDeviceBase and/or a system-space RAM address, such as an address on the stack. Use VideoPortZeroDeviceMemory to fill any device-memory block, such as a frame buffer, with zeros.

A miniport’s HwVidInterrupt or HwVidSynchronizeExecutionCallback function can call VideoPortZeroMemory.

See Also

DriverEntry, HwVidInterrupt, HwVidSynchronizeExecutionCallback, VIDEO_HW_INITIALIZATION_DATA, VideoPortCompareMemory, VideoPortMoveMemory, VideoPortZeroDeviceMemory


Chapter 6 Video Miniport Driver Functions

Video miniport drivers must have some and can have all of the functions that are described in this chapter in alphabetic order.

Function names in the form of HwVid Xxx and SvgaHwIoPort Xxx are sample names only; that is, these functions can have any name chosen by the driver writer. DriverEntry is a required name. The function, when assigned to the appropriate structure member for initialization purposes, will be assigned to HwXxx as opposed to HwVidXxx.

All miniport driver functions should be made pageable, with a few exceptions. Miniport functions that cannot be made pageable are:

  • HwVidInterrupt

  • A HwVidSynchronizeExecutionCallback that is passed in calls to VideoPortSynchronizeExecution

  • HwVidResetHw

  • HwVidBankedMemoryCallback

  • An internal function called by any of the preceding function(s)

For more information about video-specific I/O requests that must be supported by the port-miniport driver pair, see also Chapter 6 of this manual. For more information about the sequence in which these miniport functions are called, see also the Graphics Driver Design Guide.


DriverEntry of Video Miniport Driver

ULONG DriverEntry(

IN PVOID Context1,

IN PVOID Context2

);

Each miniport driver must have a function explicitly named DriverEntry in order to be loaded.

Parameters

Context1
Points to a context with which the miniport driver must call VideoPortInitialize.
Context2
Points to a second context with which the miniport driver must call VideoPortInitialize.

Return Value

DriverEntry returns the value returned by VideoPortInitialize.

Comments

DriverEntry is called directly by the I/O system.

First, DriverEntry allocates memory on the stack and calls VideoPortZeroMemory to initialize a VIDEO_HW_INITIALIZATION_DATA structure. Then, DriverEntry fills in driver-supplied data in the VIDEO_HW_INITIALIZATION_DATA structure and calls VideoPortInitialize.

If a miniport driver supports adapter(s) that can be connected on different types of I/O bus, such as both MicroChannel and Isa type buses, it should call VideoPortInitialize once for each type of I/O bus. Such a miniport driver must return the lowest value returned by its calls to VideoPortInitialize from the DriverEntry function. The writer of such a driver should make no assumptions about the values returned by VideoPortInitialize.

See Also

HwVidFindAdapter, VIDEO_HW_INITIALIZATION_DATA, VideoPortInitialize, VideoPortZeroMemory


HwVidBankedMemoryCallback

VOID HwVidBankedMemoryCallback(

ULONG ReadBank,

ULONG WriteBank,

PVOID Context

);

Every video miniport driver must have an HwVidBankedMemoryCallback function if it calls VideoPortMapBankedMemory to map a frame buffer that must be mapped in multiple Banks. HwVidBankedMemoryCallback is called by the Memory Manager whenever a location in the frame buffer referenced by a display driver requires the miniport driver to update the Bank Index Register in order to access that location.

Parameters

ReadBank
The index of the bank that has been referenced by the display driver in a read request.
WriteBank
The index of the bank that has been referenced by the display driver in a write request.
Context
Points to the context passed by the miniport driver in its VideoPortMapBankedMemory request.

Return Value

HwVidBankedMemoryCallback updates the Bank Index Register but does not return any status.

Comments

The Memory Manager calls this function at IRQL APC_LEVEL. No HwVidBankedMemoryCallback function can be pageable. If calling this function causes a page fault, the system will hang.

This function must execute quickly; therefore, the function must only update the Bank Index Register and return.

See Also

VideoPortMapBankedMemory, IOCTL_VIDEO_MAP_VIDEO_MEMORY, IOCTL_VIDEO_UNMAP_MEMORY, IOCTL_VIDEO_SHARE_VIDEO_MEMORY, IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY.


HwVidFindAdapter

VP_STATUS HwVidFindAdapter(

PVOID HwDeviceExtension,

PVOID HwContext,

PWSTR ArgumentString,

PVIDEO_PORT_CONFIG_INFO ConfigInfo,

PUCHAR Again

);

Every video miniport driver must have a HwVidFindAdapter function. The system video port driver calls HwVidFindAdapter when the miniport’s DriverEntry function has called VideoPortInitialize.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-adapter storage area. The video port driver allocates memory for and initializes this extension with zeros before it calls the miniport’s HwVidFindAdapter function.
HwContext
Points to the optional context value passed to VideoPortInitialize by DriverEntry. This pointer can be NULL.
ArgumentString
Points to a zero-terminated ASCII string. This string originates with the user, and this pointer can be NULL.
ConfigInfo
Points to a VIDEO_PORT_CONFIG_INFO structure. The port driver allocates memory for and initializes this structure with any known configuration information, such as values the miniport set in the VIDEO_HW_INITIALIZATION_DATA and the SystemIoBusNumber.
Again
Points to a variable that HwVidFindAdapter sets to TRUE if it should be called again with a new DeviceExtension and ConfigInfo. Set this to FALSE if the miniport can support only a single video adapter on a given I/O bus.

Return Value

HwVidFindAdapter must return one of the following status codes:
Value | Meaning
NO_ERROR | Indicates a supported adapter was found and that the adapter-relevant configuration information was successfully determined, set up, and supplied in the VIDEO_PORT_CONFIG_INFO structure.
ERROR_DEV_NOT_EXIST | Indicates the miniport driver found no adapter it supports on the given I/O bus.
ERROR_INVALID_PARAMETER | Indicates the miniport driver does not support the adapter described by the input VIDEO_PORT_CONFIG_INFO or that it could not configure or initialize the adapter successfully.

Comments

VideoPortInitialize calls the HwVidFindAdapter function after allocating storage for the HwDeviceExtension according to the HwDeviceExtensionSize that the miniport’s DriverEntry function specified in the VIDEO_HW_INITIALIZATION_DATA structure.

VideoPortInitialize also allocates the storage for the configuration information buffer. It fills in all available VIDEO_PORT_CONFIG_INFO information, based on miniport-supplied information in the VIDEO_HW_INITIALIZATION_DATA, before calling HwVidFindAdapter.

HwVidFindAdapter must use any supplied configuration information to determine if the video adapter that the input VIDEO_PORT_CONFIG_INFO describes is one that the miniport supports. HwVidFindAdapter cannot access an adapter described by VIDEO_PORT_CONFIG_INFO without setting up and claiming access ranges for device memory, possibly getting and claiming interrupt configuration information for its adapter, and mapping bus-relative access range values to system logical addresses.

Depending on the adapter and the AdapterInterfaceType value in VIDEO_PORT_CONFIG_INFO, HwFindAdapter can call some of the following VideoPort Xxx to get the necessary bus-relative configuration data and mapped access ranges to communicate with the adapter:

  • VideoPortGetBusData

  • VideoPortGetDeviceData

  • VideoPortGetRegistryParameters

  • VideoPortGetAccessRanges

  • VideoPortVerifyAccessRanges

  • VideoPortGetDeviceBase

The hardware configuration information returned by VideoPortGetBusData can be more complete than that stored in the registry by the system loader and retrieved by VideoPortGetDeviceData. If possible, a miniport driver should have a set of default bus-relative configuration information, if it cannot get sufficient configuration information from the port driver and/or the registry.

Every HwVidFindAdapter function must successfully call either VideoPortVerifyAccessRanges or VideoPortGetAccessRanges before it can call VideoPortGetDeviceBase to map bus-relative device access ranges to system-assigned logical addresses.

A miniport driver can communicate with an adapter only by passing a mapped logical address to the VideoPortRead/Write Xxx functions. If HwVidFindAdapter determines that an adapter is not one that the miniport supports, it must call VideoPortFreeDeviceBase to release the mapped access ranges before it returns control. It also must release the bus-relative ranges it has claimed in the registry for such an adapter by calling VideoPortVerifyAccessRanges.

Note that HwVidFindAdapter must not leave such an unsupported adapter with its state changed. For VGA/SVGA adapters, the miniport must leave the adapter in a VGA state and must restore any extended registers it might have modified to their original condition.

HwVidFindAdapter must fill in any relevant but missing configuration information for an adapter it supports in the ConfigInfo buffer and set the value of Again before it returns control. Setting Again to FALSE does not prevent VideoPortInitialize from calling HwVidFindAdapter again for another I/O bus (with a different SystemIoBusNumber) of the same AdapterInterfaceType.

If a miniport driver’s HwVidFindAdapter function finds that the video adapter does not generate interrupts or that it cannot determine a valid interrupt vector/level for the adapter, HwVidFindAdapter should set both InterruptLevel and InterruptVector in the VIDEO_PORT_CONFIG_INFO structure to zero.

When HwVidFindAdapter returns control, the video port driver checks the interrupt configuration members of VIDEO_PORT_CONFIG_INFO and if both are zero, does not connect the interrupt for the miniport driver. Explicitly setting both InterruptLevel and InterruptVector to zero in HwVidFindAdapter disables the HwVidInterrupt entry point, if any, that was set up by the miniport driver’s DriverEntry routine.

See Also

DriverEntry, HwVidQueryDeviceCallback, HwVidQueryNamedValueCallback, VIDEO_HW_INITIALIZATION_DATA, VIDEO_PORT_CONFIG_INFO, VideoPortFreeDeviceBase, VideoPortGetAccessRanges, VideoPortGetBusData, VideoPortGetDeviceBase, VideoPortGetDeviceData, VideoPortGetRegistryParameters, VideoPortVerifyAccessRanges


HwVidInitialize

BOOLEAN HwVidInitialize(

PVOID HwDeviceExtension

);

Every video miniport driver must have a HwVidInitialize function.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-adapter storage area.

Return Value

If the initialization succeeds, HwVidInitialize returns TRUE.

Comments

HwVidInitialize performs the first initialization of the adapter, after the HAL has given up control of the video hardware to the video port driver. The port driver calls HwVidInitialize in response to an open request by the corresponding display driver.

As soon as HwVidInitialize is called, the miniport driver can change the state of the adapter, unlike the miniport’s HwFindAdapter function, which must leave the adapter in VGA mode. On return from HwVidInitialize, the adapter must be initialized to a state equivalent to that set up by the miniport’s HwVidResetHw function. This feature is used by autodetection to get mode information from the miniport.

See Also

HwVidFindAdapter, HwVidResetHw


HwVidInterrupt

BOOLEAN HwVidInterrupt(

PVOID HwDeviceExtension

);

If its video adapter generates interrupts, a miniport driver must have a HwVidInterrupt function.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-adapter storage area.

Return Value

If HwVidInterrupt determines that the hardware is not interrupting, it returns FALSE. Otherwise, it must dismiss the interrupt on its adapter before it returns TRUE.

Comments

First, HwVidInterrupt should determine whether its adapter actually caused the interrupt. If not, this function should return FALSE immediately so the ISR of the device that caused the interrupt will be called promptly.

Otherwise, HwVidInterrupt must dismiss interrupts on the adapter before it returns control.

If a miniport driver has a HwVidInterrupt function, no register or memory location that can be accessed by HwVidInterrupt can be visible to the corresponding display driver. An interrupt can occur while the display driver is modifying one of the registers or memory locations involved, and there is no way to ensure synchronization. Therefore, all functions requiring access to the critical registers or memory locations for interrupt-driven operations must be in the miniport driver.

If any other miniport function shares memory, such as part of the HwDeviceExtension, with HwVidInterrupt, it must call VideoPortSynchronizeExecution to synchronize its access to the shared area.

HwVidInterrupt can call only a subset of the system-supplied VideoPort Xxx routines safely. In particular, it can call the following functions:

  • VideoPortEnableInterrupt and VideoPortDisableInterrupt

  • VideoPortZeroDeviceMemory and VideoPortZeroMemory

  • VideoPortLogError

  • VideoPortStallExecution, but it should avoid calling this function if possible

  • All VideoPortRead Xxx and VideoPortWrite Xxx

HwVidInterrupt must not call any VideoPort Xxx that is not in the preceding list.

A HwVidInterrupt function cannot be pageable, nor can any function that it calls.

See Also

HwVidSynchronizeExecutionCallback, VideoPortDisableInterrupt, VideoPortEnableInterrupt, VideoPortLogError, VideoPortRead Xxx, VideoPortStallExecution, VideoPortSynchronizeExecution, VideoPortWrite Xxx, VideoPortZeroDeviceMemory, VideoPortZeroMemory


HwVidQueryDeviceCallback

VP_STATUS HwVidQueryDeviceCallback(

PVOID HwDeviceExtension,

PVOID Context,

VIDEO_DEVICE_DATA_TYPE DeviceDataType,

PVOID Identifier,

ULONG IdentifierLength,

PVOID ConfigurationData,

ULONG ConfigurationDataLength,

PVOID ComponentInformation,

ULONG ComponentInformationLength

);

HwVidQueryDeviceCallback is passed in a call to VideoPortGetDeviceData from the miniport’s HwVidFindAdapter function. VideoPortGetDeviceData calls HwVidQueryDeviceCallback after collecting available configuration information under the \Registry\Machine\Hardware\Description node of the registry.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-adapter storage area.
Context
Points to the context value set up by HwVidFindAdapter. Usually, it points to the VIDEO_PORT_CONFIG_INFO buffer or to an offset in that buffer.
DeviceDataType
Specifies the type of configuration information that was requested, which is usually one of the following:

VpBusData
VpControllerData
VpMonitorData

The VpControllerData and VpMonitorData values have meaning only on ARC-compliant (RISC) platforms. Miniport drivers of x86-type video adapters usually specify VpBusData, particularly for adapters on EISA or MCA buses. The VpMachineData value is defined for future versions of Windows NT®.

Identifier
Points to the name of the device as determined by the ARC firmware. This parameter should be used only on ARC-compliant platforms. Otherwise, this pointer should be NULL.
IdentifierLength
Specifies the size in bytes of the buffered Identifier string. The value should be zero if the machine is not ARC-compliant.
ConfigurationData
Points to hardware configuration data. The format of this data is determined by the specified DeviceDataType and by the AdapterInterfaceType value in the VIDEO_PORT_CONFIG_INFO.
ConfigurationDataLength
Specifies the size in bytes of the ConfigurationData buffer. In effect, this indicates how much information was collected from the registry and stored in the ConfigurationData buffer allocated by VideoPortGetDeviceBase.
ComponentInformation
Is reserved for system use.
ComponentInformationLength
Is reserved for system use.

Return Value

HwVidQueryDeviceCallback returns the status of the operation.

Comments

HwVidQueryDeviceCallback examines the ConfigurationData, collected from the registry by VideoPortGetDeviceData. It uses this information to locate an adapter it can support, to configure its adapter, and, possibly, to fill in missing configuration information in the VIDEO_PORT_CONFIG_INFO structure.

HwVidQueryDeviceCallback cannot pass access range values found in the ConfigurationData to any VideoPortRead Xxx or VideoPortWrite Xxx directly; such addresses first must be mapped by calling VideoPortGetDeviceBase.

If the ConfigurationData buffer has no access range information and the miniport’s HwVidFindAdapter function has not already called VideoPortGetBusData (or VideoPortGetAccessRanges), its HwVidQueryDeviceCallback function can call VideoPortGetBusData. Access range information returned by VideoPortGetBusData also must be passed to VideoPortVerifyAccessRanges.

If VideoPortVerifyAccessRanges returns NO_ERROR, the miniport driver can call VideoPortGetDeviceBase to obtain mapped logical addresses that it can use to communicate with the adapter by calling the VideoPortRead Xxx and/or VideoPortWrite Xxx functions.

If it cannot get bus-relative access ranges values by calling VideoPortGetDeviceData, VideoPortGetBusData, or VideoPortGetAccessRanges, a miniport driver can use a set of driver-supplied default access-range values to find its adapter. In these circumstances, the miniport must call VideoPortVerifyAccessRanges with the miniport-supplied access ranges, and, then call VideoPortGetDeviceBase only if VideoPortVerifyAccessRanges returned NO_ERROR. If a call to VideoPortVerifyAccessRanges is unsuccessful, a given bus-relative range is already in use by the driver of another device.

See Also

VideoPortGetAccessRanges, VideoPortGetBusData, VideoPortGetDeviceBase, VideoPortGetDeviceData, VideoPortVerifyAccessRanges


HwVidQueryNamedValueCallback

VP_STATUS HwVidQueryNamedValueCallback(

PVOID HwDeviceExtension,

PVOID Context,

PWSTR ValueName,

PVOID ValueData,

ULONG ValueLength

);

HwVidQueryNamedValueCallback is an optional miniport driver function passed in a call to VideoPortGetRegistryParameters. VideoPortGetRegistryParameters calls HwVidQueryNamedValueCallback after collecting available configuration information about the given ValueName in the ..\CurrentControlSet\Services\DriverName\Device Number key of the registry.

Parameters

HwDeviceExtension
Points to the miniport driver’s per-adapter storage area.
Context
Points to a driver-determined context to be passed to the callback routine. Usually, it points to a location on the stack at which the value data should be stored.
ValueName
Points to a zero-terminated Unicode string naming the entry.
ValueData
Points to the buffered data associated with ValueName, supplied by VideoPortGetRegistryParameters.
ValueLength
Specifies the size in bytes of the buffer at ValueData.

Return Value

HwVidQueryNamedValueCallback returns the status of the operation.

Comments

HwVidQueryNamedValueCallback processes the ValueData retrieved from the registry.

The returned ValueData is on the stack, so it can be referenced locally. HwVidQueryNamedValueCallback can store some or all of the returned information for use by other miniport functions in the input HwDeviceExtension or use the input Context as a pointer to a location where the data can be stored.

See Also

HwVidFindAdapter, VideoPortGetRegistryParameters, VideoPortSetRegistryParameters


HwVidResetHw

BOOLEAN HwVidResetHw(

PVOID HwDeviceExtension,

ULONG Columns,

ULONG Rows

);

Any video miniport driver must have a HwVidResetHw function if its adapter cannot be reset to a fully initialized state without a hard boot of the machine. For example, if the adapter’s ROM initialization code cannot reset the adapter state to a boot-up mode, the miniport must have a HwVidResetHw function.

Parameters

HwDeviceExtension
Points to the miniport’s per-adapter storage area.
Columns
Specifies the number of columns of the mode to be set up.
Rows
Specifies the number of rows of the mode to be set up.

Return Value

HwVidResetHw should return TRUE if it completely reset the adapter to the given character mode. Otherwise, it should return FALSE to indicate that the HAL should perform the equivalent of an INT10, MODE3-type BIOS call after HwVidResetHw returns control.

Comments

The HAL calls HwVidResetHw if the system is about to crash, so that the HAL can display information on the screen while the system is being shut down. The HAL also calls HwVidResetHw just before the machine is rebooted when a soft boot occurs.

Most miniport drivers must provide this function, except for drivers of adapters that are reset to a VGA standard character mode on receipt of an INT10, MODE3-type command.

HwVidResetHw resets the adapter to character mode, according to the given Columns and Rows values. However, because the system is shutting down, HwVidResetHw cannot call the BIOS or any pageable code. HwVidResetHw should call only the VideoPortRead/WritePort Xxx and/or VideoPortRead/WriteRegister Xxx functions.

If HwVidResetHw cannot change the mode of the adapter by simply programming the adapter registers, it can set up the appropriate values in adapter registers and return FALSE. This causes the HAL to perform an extended INT10-type operation to reset the video adapter to chararacter mode.

HwVidResetHw cannot and must not attempt to call VideoPortInt10. A miniport’s HwVidStartIO function is called with the IOCTL_VIDEO_RESET_DEVICE VRP to reset the adapter whenever the Win32 video installation applet is used to test or change the graphics display mode, not the miniport’s HwVidResetHw function.

See Also

HwVidInitialize, HwVidStartIO, IOCTL_VIDEO_RESET_DEVICE, VideoPortInt10, VideoPortRead Xxx, VideoPortWrite Xxx


HwVidStartIO

BOOLEAN HwVidStartIO(

PVOID HwDeviceExtension,

PVIDEO_REQUEST_PACKET RequestPacket

);

Every video miniport driver must have a HwVidStartIO function. The video port driver calls HwVidStartIO in response to each GDI EngDeviceIoControl request, which originates in the corresponding display driver. When HwVidStartIO is called, the miniport driver owns the input video request packet until it completes the requested operation.

Parameters

HwDeviceExtension
Points to the miniport’s per-adapter storage area.
RequestPacket
Points to a VRP, which contains all the parameters originally passed to EngDeviceIoControl.

Return Value

HwVidStartIO must return TRUE, indicating that it has completed the request.

Comments

HwVidStartIO must examine the VRP, carry out whatever operation is requested, set the appropriate status in the status block of the VRP, and then return TRUE.

The system video port driver serializes all requests. A miniport driver need not perform any serialization of its own unless it has a HwVidInterrupt function.

However, every miniport’s HwVidStartIO function must complete each requested operation or set an appropriate error in the VRP’s StatusBlock before it returns control.

See Also

IOCTL_VIDEO_XXX, VIDEO_REQUEST_PACKET, VideoPortSynchronizeExecution


HwVidSynchronizeExecutionCallback

BOOLEAN HwVidSynchronizeExecutionCallback(

IN PVOID Context

);

HwVidSynchronizeExecutionCallback is an optional miniport driver function, passed in calls to VideoPortSynchronizeExecution.

Parameters

Context
Points to context data passed to the callback routine through VideoPortSynchronizeExecution. Usually, this is a pointer to the device extension or an offset within the device extension.

Return Value

If the operation succeeds, HwVidSynchronizeExecutionCallback returns TRUE.

Comments

A miniport driver with one or more functions that share memory with its HwVidInterrupt function must have a HwVidSynchronizeExecutionCallback function. Any function that shares memory with HwVidInterrupt must call VideoPortSynchronizeExecution with the HwVidSynchronizeExecutionCallback to maintain the integrity of data in the shared memory area. That is, only one of the HwVidSynchronizeExecutionCallback and HwInterrupt functions can update state in the shared area at any given moment.

The miniport driver of an adapter that does not generate interrupts also can have a HwVidSynchronizeExecutionCallback function to be passed to VideoPortSynchronizeExecution when such a driver needs to get some critical work done at a relatively high runtime priority. For example, VGA-compatible miniport drivers with SvgaHwIoPort Xxx functions might have a HwVidSynchronizeExecutionCallback that is responsible for transferring driver-buffered and validated application-issued I/O to the adapter.

See Also

HwVidInterrupt, SvgaHwIoPort Xxx, VideoPortSynchronizeExecution


HwVidTimer

VOID HwVidTimer(

PVOID HwDeviceExtension

);

HwVidTimer is an optional miniport driver function to which calls are enabled and disabled with VideoPortStartTimer and VideoPortStopTimer, respectively.

Parameters

HwDeviceExtension
Points to the miniport’s per-adapter storage area.

Comments

Any miniport driver can have a HwVidTimer function at the discretion of the driver designer. For example, a HwVidTimer function could be used to read the state of the “VGA” registers on a high-end video adapter so that the miniport can emulate VGA-compatible behavior on an as-needed basis.

After a call to VideoPortStartTimer, the video port driver calls a miniport’s HwVidTimer function approximately once each second until the miniport calls VideoPortStopTimer.

The HwVidTimer function can disable the timer with a call to VideoPortStopTimer. Another miniport function can re-enable the timer later with a call to VideoPortStartTimer.

See Also

VideoPortStartTimer, VideoPortStopTimer


SvgaHwIoPortUchar

VP_STATUS SvgaHwIoPortUchar(

ULONG Context,

ULONG Port,

UCHAR AccessMode,

PUCHAR Data

);

SvgaHwIoPortUchar traps an I/O port to which a full-screen MS-DOS application in an x86-based machine is sending UCHAR-sized data. Only miniport drivers of VGA-compatible SVGA adapters have SvgaHwIoPort Xxx functions.

Parameters

Context
Specifies the miniport-defined context value that was set in the EmulatorAccessEntriesContext member of VIDEO_PORT_CONFIG_INFO.
Port
Specifies the mapped I/O port to be trapped.
AccessMode
Specifies the type of access to be trapped, which can be one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

Data
Points to the byte to be transferred.

Return Value

SvgaHwIoPortUchar returns the final status of the operation.

Comments

SvgaHwIoPortUchar will intercept any byte access attempted by a full-screen MS-DOS application issuing either or both of the instructions OUT DX, AL and IN AL, DX.

If the miniport driver enables the Port for direct access by calling VideoSetTrappedEmulatorPorts, its SvgaHwIoPortUchar function will not be called. Such an application then will have direct access to the Port, unless the miniport driver disables the Port with another call to VideoSetTrappedEmulatorPorts.

If any application-issued x86 IN or OUT instruction(s) might affect the state of the VGA-compatible adapter sequencer register, miscellaneous output register, or any adapter-specific register and, thereby, cause the machine to hang, the miniport driver must not enable the port for direct access by calling VideoPortSetTrappedEmulatorPorts.

SvgaHwIoPortUchar must buffer subsequent instructions from the application to the Port and check that none can hang the machine. If the application issues any sequence of instructions that might hang the machine, SvgaHwIoPortUchar must discard the buffered instructions. Otherwise, it should output them, a UCHAR at a time, to the specified, mapped I/O port.

See Also

EMULATOR_ACCESS_ENTRY, SvgaHwIoPortUcharString, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceBase, VideoPortSetTrappedEmulatorPorts


SvgaHwIoPortUcharString

VP_STATUS SvgaHwIoPortUcharString(

ULONG Context,

ULONG Port,

UCHAR AccessMode,

PUCHAR Data,

ULONG DataLength

);

SvgaHwIoPortUcharString traps an I/O port to which a full-screen MS-DOS application in an x86-based machine is sending a sequence of UCHAR-sized data. Only miniport drivers of VGA-compatible SVGA adapters have SvgaHwIoPort Xxx functions.

Parameters

Context
Specifies the miniport-determined context value that was set in the EmulatorAccessEntriesContext member of VIDEO_PORT_CONFIG_INFO.
Port
Specifies the mapped I/O port.
AccessMode
Specifies the type of access allowed, which can be one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

Data
Points to the UCHAR string to be transferred. One character at a time is hooked out until the whole string is transferred.
DataLength
Specifies the number of UCHAR values in the string.

Return Value

SvgaHwIoPortUcharString returns the final status of the operation.

Comments

SvgaHwIoPortUcharString will intercept any byte access attempted by a full-screen MS-DOS application issuing either or both of the instructions REP OUTSB DX, ESI and REP INSB EDI, DX.

If the miniport driver enables the Port for direct access by calling VideoSetTrappedEmulatorPorts, its SvgaHwIoPortUcharString function will not be called. Such an application then will have direct access to the I/O port, unless the miniport driver disables the port with another call to VideoSetTrappedEmulatorPorts.

If an application-issued x86 INSB or OUTSB instruction(s) might affect the state of the VGA-compatible adapter sequencer register, miscellaneous output register, or any adapter-specific register and, thereby, cause the machine to hang, the miniport driver must not enable the port for direct access by calling VideoPortSetTrappedEmulatorPorts.

SvgaHwIoPortUcharString must buffer subsequent instructions from the application and check that none can hang the machine. If the application issues any sequence of instructions that might hang the machine, SvgaHwIoPortUcharString must discard the buffered instructions. Otherwise, it should output them, a UCHAR at a time, to the specified, mapped I/O port.

See Also

EMULATOR_ACCESS_ENTRY, SvgaHwIoPortUchar, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceBase, VideoPortSetTrappedEmulatorPorts


SvgaHwIoPortUlong

VP_STATUS SvgaHwIoPortUlong(

ULONG Context,

ULONG Port,

UCHAR AccessMode,

PULONG Data

);

SvgaHwIoPortUlong traps an I/O port range to which a full-screen MS-DOS application in an x86-based machine is sending a sequence of ULONG-sized data. Only miniport drivers of VGA-compatible SVGA adapters have SvgaHwIoPort Xxx functions.

Parameters

Context
Specifies the miniport-determined context value that was set in the EmulatorAccessEntriesContext member of VIDEO_PORT_CONFIG_INFO.
Port
Specifies the mapped I/O port to be trapped.
AccessMode
Specifies the type of access allowed, which can be one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

Data
Points to the ULONG data to be transferred.

Return Value

SvgaHwIoPortUlong returns the final status of the operation.

Comments

SvgaHwIoPortUlong will intercept any range access attempted by a full-screen MS-DOS application issuing either or both of the instructions OUT DX, EAX and IN EAX, DX.

If the miniport driver enables the Port range for direct access by calling VideoSetTrappedEmulatorPorts, its SvgaHwIoPortUlong function will not be called. Such an application then will have direct access to the I/O port range, unless the miniport driver disables the range with another call to VideoSetTrappedEmulatorPorts.

If an application-issued x86 IN or OUT instruction(s) might affect the state of the VGA-compatible adapter sequencer register, miscellaneous output register, or any adapter-specific register and, thereby, cause the machine to hang, the miniport driver must not enable the port for direct access by calling VideoPortSetTrappedEmulatorPorts.

SvgaHwIoPortUlong must buffer subsequent instructions from the application and check that none can hang the machine. If the application issues any sequence of instructions that might hang the machine, SvgaHwIoPortUlong must discard the buffered instructions. Otherwise, it should output them, a ULONG at a time, to the specified, mapped I/O port.

See Also

EMULATOR_ACCESS_ENTRY, SvgaHwIoPortUlongString, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceBase, VideoPortSetTrappedEmulatorPorts


SvgaHwIoPortUlongString

VP_STATUS SvgaHwIoPortUlongString(

ULONG Context,

ULONG Port,

UCHAR AccessMode,

PULONG Data,

ULONG DataLength

);

SvgaHwIoPortUlongString traps an I/O port range to which a full-screen MS-DOS application in an x86-based machine is sending a sequence of ULONG-sized data. Only miniport drivers of VGA-compatible SVGA adapters have SvgaHwIoPort Xxx functions.

Parameters

Context
Specifies the miniport-determined context value that was set in the EmulatorAccessEntriesContext member of VIDEO_PORT_CONFIG_INFO.
Port
Specifies the mapped I/O port to be trapped.
AccessMode
Specifies the type of access allowed, which can be one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

Data
Points to the ULONG data string to be transferred. The string is hooked out one value at a time until it is all used.
DataLength
Specifies the number of ULONG values in the string.

Return Value

SvgaHwIoPortUlongString returns the final status of the operation.

Comments

SvgaHwIoPortUlongString will intercept any range access attempted by a full-screen MS-DOS application issuing either or both of the instructions REP OUTSD DX, ESI and REP INSD EDI, DX.

If the miniport driver enables the Port range for direct access by calling VideoSetTrappedEmulatorPorts, its SvgaHwIoPortUlongString function will not be called. Such an application then will have direct access to the I/O port range, unless the miniport driver disables the Port range with another call to VideoSetTrappedEmulatorPorts.

If an application-issued x86 INSD or OUTSD instruction(s) might affect the state of the VGA-compatible adapter sequencer register, miscellaneous output register, or any adapter-specific register and, thereby, cause the machine to hang, the miniport driver must not enable the port for direct access by calling VideoPortSetTrappedEmulatorPorts.

SvgaHwIoPortUlongString must buffer subsequent instructions from the application and check that none can hang the machine. If the application issues any sequence of instructions that might hang the machine, SvgaHwIoPortUlongString must discard the buffered instructions. Otherwise, it should output them, a ULONG at a time, to the specified, mapped I/O port.

See Also

EMULATOR_ACCESS_ENTRY, SvgaHwIoPortUlong, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceBase, VideoPortSetTrappedEmulatorPorts


SvgaHwIoPortUshort

VP_STATUS SvgaHwIoPortUshort(

ULONG Context,

ULONG Port,

UCHAR AccessMode,

PUSHORT Data

);

SvgaHwIoPortUshort traps an I/O port range to which a full-screen MS-DOS application in an x86-based machine is sending a sequence of USHORT-sized data. Only miniport drivers of VGA-compatible SVGA adapters have SvgaHwIoPort Xxx functions.

Parameters

Context
Specifies the miniport-determined context value that was set in the EmulatorAccessEntriesContext member of VIDEO_PORT_CONFIG_INFO.
Port
Specifies the mapped I/O port to be trapped.
AccessMode
Specifies the type of access allowed, which can be one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

Data
Points to the USHORT data to be transferred.

Return Value

SvgaHwIoPortUshort returns the final status of the operation.

Comments

SvgaHwIoPortUshort will intercept any range access attempted by a full-screen MS-DOS application issuing either or both of the instructions OUT DX, AX and IN AX, DX.

If the miniport driver enables the Port range for direct access by calling VideoSetTrappedEmulatorPorts, its SvgaHwIoPortUshort function will not be called. Such an application then will have direct access to the I/O port range, unless the miniport driver disables the Port range with another call to VideoSetTrappedEmulatorPorts.

If an application-issued x86 IN or OUT instruction(s) might affect the state of the VGA-compatible adapter sequencer register, miscellaneous output register, or any adapter-specific register and, thereby, cause the machine to hang, the miniport driver must not enable the port for direct access by calling VideoPortSetTrappedEmulatorPorts.

SvgaHwIoPortUshort must buffer subsequent instructions from the application and check that none can hang the machine. If the application issues any sequence of instructions that might hang the machine, SvgaHwIoPortUshort must discard the buffered instructions. Otherwise, it should output them, a USHORT at a time, to the specified, mapped I/O port.

See Also

EMULATOR_ACCESS_ENTRY, SvgaHwIoPortUshortString, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceBase, VideoPortSetTrappedEmulatorPorts


SvgaHwIoPortUshortString

VP_STATUS SvgaHwIoPortUshortString(

ULONG Context,

ULONG Port,

UCHAR AccessMode,

PUSHORT Data,

ULONG DataLength

);

SvgaHwIoPortUshortString traps an I/O port range to which a full-screen MS-DOS application in an x86-based machine is sending a sequence of USHORT-sized data. Only miniport drivers of VGA-compatible SVGA adapters have SvgaHwIoPort Xxx functions.

Parameters

Context
Specifies the miniport-determined context value that was set in EmulatorAccessEntriesContext of VIDEO_PORT_CONFIG_INFO.
Port
Specifies the mapped I/O port to be trapped.
AccessMode
Specifies the type of access allowed, which can be one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

Data
Points to the string of USHORT data values to be transferred. This string is accessed one value at a time until all values have been hooked.
DataLength
Specifies the number of USHORT values in the string.

Return Value

SvgaHwIoPortUshortString returns the final status of the operation.

Comments

SvgaHwIoPortUshortString will intercept any range access attempted by a full-screen MS-DOS application issuing either or both of the instructions REP OUTSW DX, ESI and REP INSW EDI, DX.

If the miniport driver enables the Port range for direct access by calling VideoSetTrappedEmulatorPorts, its SvgaHwIoPortUshortString function will not be called. Such an application then will have direct access to the I/O port range, unless the miniport driver disables the Port range with another call to VideoSetTrappedEmulatorPorts.

If an application-issued x86 INSW or OUTSW instruction(s) might affect the state of the VGA-compatible adapter sequencer register, miscellaneous output register, or any adapter-specific register and, thereby, cause the machine to hang, the miniport driver must not enable the port for direct access by calling VideoPortSetTrappedEmulatorPorts.

SvgaHwIoPortUshortString must buffer subsequent instructions from the application and check that none can hang the machine. If the application issues any sequence of instructions that might hang the machine, SvgaHwIoPortUshortString must discard the buffered instructions. Otherwise, it should output them, a USHORT at a time, to the specified, mapped I/O port.

See Also

EMULATOR_ACCESS_ENTRY, SvgaHwIoPortUshort, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetDeviceBase, VideoPortSetTrappedEmulatorPorts


Chapter 7 Video Structures

This chapter describes structures used by kernel-mode video miniport drivers that are linked with the NT video port driver. Structures are described in alphabetic order.

For more information about video requests and IOCTLs handled by the port-miniport driver pair, see also Chapter 6 of this manual.


EMULATOR_ACCESS_ENTRY

typedef struct _EMULATOR_ACCESS_ENTRY {
    ULONG BasePort;
    ULONG NumConsecutivePorts;
    EMULATOR_PORT_ACCESS_TYPE AccessType;
    UCHAR AccessMode;
    UCHAR StringSupport;
    PVOID Routine;
} EMULATOR_ACCESS_ENTRY, *PEMULATOR_ACCESS_ENTRY;

EMULATOR_ACCESS_ENTRY specifies an element in the EmulatorAccessEntries array set up in the VIDEO_PORT_CONFIG_INFO structure by drivers of VGA-compatible (SVGA) adapters on x86-based Windows NT® platforms.

Members

BasePort
Specifies the bus-relative physical address of the I/O port(s) to be hooked out by the V86 emulator. This value and the range of ports must fall within an access range of I/O ports already claimed in the registry by VideoPortGetAccessRanges or VideoPortVerifyAccessRanges.
NumConsecutivePorts
Specifies the number of consecutive ports to be hooked by the V86 emulator, starting at BasePort. This value is determined by how wide each hooked I/O port is considered to be. For example, if port addresses 0x3C0 and 0x3C1 are hooked as a single USHORT-addressable port, this value should be one.
AccessType
Specifies the size of the data transferred through the given I/O port as one of the following values:

Uchar
Ushort
Ulong

AccessMode
Specifies how the given I/O port can be accessed as one or a combination (ORed) of the following values:

EMULATOR_READ_ACCESS
EMULATOR_WRITE_ACCESS

The I/O port range must be accessible in at least one of the preceding modes.

StringSupport
Indicates whether the driver-supplied SvgaHwIoPort Xxx function supports string accesses in cases for which many values of the given AccessType are “pumped” through an I/O port consecutively. If this member is set to TRUE, the Routine member must specify the entry point of a miniport-supplied SvgaHwIoPort Xxx String function.
Routine
Points to the miniport’s SvgaHwIoPort Xxx that handles accesses to the port(s) described in this structure.

Comments

VGA-compatible miniport drivers of SVGA video hardware in x86-based machines must define emulator access ranges, which a VGA-compatible miniport driver can set up with VideoPortSetTrappedEmulatorPorts to be accessed directly from full-screen MS-DOS™ applications for faster I/O. Such a driver must supply a set of SvgaHwIoPort Xxx functions to validate any sequence of application-issued INs, INSB/INSW/INSDs, OUTs, and/or OUTSB/OUTSW/OUTSDs to each such I/O port range.

Data in each EMULATOR_ACCESS_ENTRY-type element is used to determine which I/O ports (memory locations at which adapter registers are accessible) in the miniport’s access ranges have been taken over (hooked out) by the V86 emulator on x86-based platforms. Values written to these locations by full-screen MS-DOS applications are trapped and forwarded to the miniport driver’s corresponding SvgaHwIoPort Xxx function for validation before they are written to or read from the video adapter. However, the miniport can enable direct access by the application to particular I/O port ranges by calling VideoPortSetTrappedEmulatorPorts for performance reasons.

See Also

SvgaHwIoPort Xxx, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortGetAccessRanges, VideoPortSetTrappedEmulatorPorts, VideoPortVerifyAccessRanges


STATUS_BLOCK

typedef struct _STATUS_BLOCK {
    VP_STATUS Status;
    ULONG Information;
} STATUS_BLOCK, *PSTATUS_BLOCK;

STATUS_BLOCK is a substructure within the VIDEO_REQUEST_PACKET structure. A miniport driver’s HwVidStartIO function must set the status block of each VRP that it gets.

Members

Status
Is set to a status code indicating the result of the requested operation, such as:

Code

Description

NO_ERROR

The requested operation has been carried out and completed successfully.

ERROR_INVALID_FUNCTION

The miniport driver does not handle this request.

ERROR_NOT_ENOUGH_MEMORY

There is insufficient memory to process the request.

ERROR_INVALID_PARAMETER

A parameter in the VRP is invalid.

ERROR_INSUFFICIENT_BUFFER

Either the VRP InputBuffer is too small to provide the data required to process the given request or the OutputBuffer is too small to return the requested data.

ERROR_IO_PENDING

An operation has not yet completed.

ERROR_MORE_DATA

The driver has additional data to be returned but has already filled the given VRP OutputBuffer.

Information
Supplies additional information about the completed operation. The meaning of the value varies according to VRP. Generally, this member is used to return the minimum size required for the input buffer if the VRP passes data in the InputBuffer. Alternatively, it contains the number of bytes of data transferred if the requested operation returns data in the VRP OutputBuffer.

See Also

HwVidStartIO, VIDEO_REQUEST_PACKET


VIDEO_ACCESS_RANGE

typedef struct _VIDEO_ACCESS_RANGE {
    PHYSICAL_ADDRESS RangeStart;
    ULONG RangeLength;
    UCHAR RangeInIoSpace;
    UCHAR RangeVisible;
    UCHAR RangeShareable;
} VIDEO_ACCESS_RANGE, *PVIDEO_ACCESS_RANGE;

VIDEO_ACCESS_RANGE defines a device I/O port or memory range for the video adapter. Every miniport’s HwVidFindAdapter function must set up an array of VIDEO_ACCESS_RANGE-type elements, called the access ranges array, for each video adapter the miniport supports.

For VGA-compatible miniport drivers, VIDEO_ACCESS_RANGE also defines an element in an array passed to VideoPortSetTrappedEmulatorPorts to enable or disable direct access to I/O ports by full-screen MS-DOS applications.

Members

RangeStart
Specifies the bus-relative base address of a memory or I/O port range for an element in the access ranges array passed to VideoPortVerifyAccessRanges or returned by VideoPortGetAccessRanges.

Specifies the bus-relative base address of an I/O port range for an array to be passed to VideoPortSetTrappedEmulatorPorts.

RangeLength
Specifies the number of I/O ports or size in bytes for the range.
RangeInIoSpace
Is set to TRUE (one) if the given range is in I/O space or to FALSE (zero) if the access range is in memory space.
RangeVisible
Is ignored if the miniport’s HwVidFindAdapter function is setting up the access ranges array.

Is set to TRUE by VGA-compatible miniport drivers and passed to VideoPortSetTrappedEmulatorPorts to enable direct access to the I/O port range by a full-screen MS-DOS application. If set to FALSE, application-issued instructions continue to be trapped and forwarded to the miniport’s SvgaHwIo Xxx function for validation.

RangeShareable
Is set to TRUE if the access range described by this element can be shared with another driver and/or device or to FALSE if the range cannot be shared.

Is ignored by VideoPortSetTrappedEmulatorPorts.

Comments

A miniport’s HwVidFindAdapter function sets up the access ranges array for an adapter. It can use information returned by VideoPortGetBusData or VideoPortGetAccessRanges. As an alternative, it can use information retrieved from the registry by calling VideoPortGetDeviceData with a miniport-supplied HwVidQueryDeviceCallback function or VideoPortGetRegistryParameters with a miniport-supplied HwVidQueryNamedValueCallback function. If calling these VideoPort Xxx does not supply the bus-relative access range values, HwVidFindAdapter can set up access ranges elements using driver-supplied bus-relative default values.

Claiming Access Ranges in the Registry

The miniport should call VideoPortVerifyAccessRanges with any access ranges obtained from VideoPortGetDeviceData, returned by VideoPortGetBusData, or supplied as defaults by the miniport. If VideoPortVerifyAccessRanges returns NO_ERROR for such an array of access ranges, the HwVidFindAdapter or HwVidQueryDeviceCallback function can then map the ranges with VideoPortGetDeviceBase and use the returned mapped logical addresses to access the adapter.

A successful call to VideoPortGetAccessRanges also claims the returned bus-relative access ranges in the registry for the caller. If the miniport driver modifies any of the returned values, it must call VideoPortVerifyAccessRanges with the full access range, including any unmodified elements. Each call to VideoPortGetAccessRanges or VideoPortVerifyAccessRanges for a particular video adapter overwrites the caller’s claimed hardware resources in the registry.

A miniport driver must not attempt to use a range for which VideoPortVerifyAccessRanges or VideoPortGetAccessRanges does not return NO_ERROR.

Mapping Access Ranges to Communicate with the Adapter

After a miniport driver has claimed resources in the registry for an adapter, it cannot use bus-relative addresses to access or configure the adapter, because the HAL can remap all bus-relative device addresses to system space, particularly in multibus machines.

The miniport’s HwVidFindAdapter function must call VideoPortGetDeviceBase to get mapped logical addresses for its access ranges. Only then can the miniport driver communicate with the video adapter by passing the returned mapped logical range addressess to the VideoPortRead/WritePort Xxx to access device memory in I/O space and/or VideoPortRead/WriteRegister Xxx to access device memory in memory space.

Determining whether an Access Range is Shareable

Follow these guidelines to determine whether an access range can be shared:

  • If the range of memory or I/O ports should be “owned” by this driver, and/or access to this range by any other driver can cause a problem, set RangeShareable to FALSE.

  • If the range can be shared with a cooperating device driver, set RangeShareable to TRUE.

SVGA miniport drivers that implement all VGA functionality (declared in the registry as VgaCompatible set to one) should claim their access ranges as unshareable so the system VGA driver will not be loaded. On the other hand, miniports for adapters such as the S3 or the XGA, which set VgaCompatible to zero in the registry, should claim all the resources they share with the system VGA driver as shareable.

However, miniports for cards that work with a pass-through IOCTL and that can be connected to any VGA or SVGA card should not be using any system VGA ports or memory ranges. If they do, such a driver should not attempt to claim any of the VGA access ranges in the registry. Attempts to claim VGA resources by such a miniport driver are likely to cause a resource conflict because the driver of any SVGA card in the machine will have claimed these access ranges as unshareable.

Passing I/O Port Range Elements to VideoPortSetTrappedEmulatorPorts

All VIDEO_ACCESS_RANGE-type array elements describing I/O port ranges are assumed to be invisible, unless a VGA-compatible miniport driver in an x86-based machine explicitly resets the RangeVisible member(s) to TRUE and calls VideoPortSetTrappedEmulatorPorts to enable one or more I/O port ranges. VideoPortSetTrappedEmulatorPorts ignores the RangeShareable members of the input array.

In an array of VIDEO_ACCESS_RANGE-type elements passed to VideoPortSetTrappedEmulatorPorts, the value of each element’s RangeVisible member determines whether the given I/O port(s) can be accessed directly by the VDM (MS-DOS application running in full-screen on an x86-based machine) or whether such an application-issued I/O stream is trapped and forwarded to a miniport-supplied SvgaHwIoPort Xxx function for validation first.

See Also

EMULATOR_ACCESS_ENTRY, HwVidFindAdapter, HwVidQueryDeviceCallback, HwVidQueryNamedValueCallback, VIDEO_HW_INITIALIZATION_DATA, VideoPortGetAccessRanges, VideoPortGetBusData, VideoPortGetDeviceBase, VideoPortGetDeviceData, VideoPortGetRegistryParameters, VideoPortInitialize, VideoPortRead Xxx, VideoPortSetTrappedEmulatorPorts, VideoPortVerifyAccessRanges, VideoPortWrite Xxx


VIDEO_HW_INITIALIZATION_DATA

typedef struct _VIDEO_HW_INITIALIZATION_DATA {
    ULONG HwInitDataSize;
    INTERFACE_TYPE AdapterInterfaceType;
    PVIDEO_HW_FIND_ADAPTER HwFindAdapter;
    PVIDEO_HW_INITIALIZE HwInitialize;
    PVIDEO_HW_INTERRUPT HwInterrupt;
    PVIDEO_HW_START_IO HwStartIO;
    ULONG HwDeviceExtensionSize;
    ULONG StartingDeviceNumber;
    PVIDEO_HW_RESET_HW HwResetHw;
    PVIDEO_HW_TIMER HwTimer;
} VIDEO_HW_INITIALIZATION_DATA, *PVIDEO_HW_INITIALIZATION_DATA;

VIDEO_HW_INITIALIZATION_DATA is a structure created (on the stack) and initialized by the miniport driver in its DriverEntry function. It specifies the entry points and storage requirements for the miniport driver.

Members

HwInitDataSize
Is the size in bytes of this structure. In effect, this indicates the version of VIDEO_HW_INITIALIZATION_DATA being used.
AdapterInterfaceType
Specifies the type of I/O bus used by the video hardware. Currently, the value can be one of the following:

Internal
Isa
Eisa
MicroChannel
TurboChannel
PCIBus

However, additional types of I/O buses will be supported in future versions of Windows NT. The upper bound on the system-defined AdapterInterfaceType values is always MaximumInterfaceType.

HwFindAdapter
Points to the miniport’s HwVidFindAdapter function, which is required for all miniport drivers.
HwInitialize
Points to the miniport’s HwVidInitialize function, which is required for all miniport drivers.
HwInterrupt
Points to the miniport’s HwVidInterrupt function, which is required only if the miniport’s adapter generates interrupts. If not, this pointer must be NULL.
HwStartIO
Points to the miniport driver’s HwVidStartIO function, which is required for all miniport drivers.
HwDeviceExtensionSize
Specifies the size in bytes of the storage the miniport driver requires for its private, adapter-specific device extension. A miniport driver uses this storage to hold driver-determined per-adapter information, such as the mapped logical address ranges for the adapter registers and whatever context information the driver maintains about its I/O operations.

A pointer to the device extension is passed in every call made to the miniport driver’s standard functions except DriverEntry, HwVidSynchronizeExecutionCallback, and any SvgaHwIoPort Xxx. The video port driver allocates the memory for the device extension and initializes it with zeros before it is passed to the miniport’s HwVidFindAdapter function.

StartingDeviceNumber
Set this to zero for the initial call to VideoPortInitialize for each type of I/O bus. Device numbering is used to determine which ..\Device Number subkey of the ..CurrentControlSet\Services\DriverName key in the registry contains the adapter-specific parameters to the miniport driver. The starting number is automatically incremented if the miniport driver’s HwVidFindAdapter function is called more than once for adapters on a particular I/O bus because HwVidFindAdapter returned control with the Again parameter set to TRUE.
HwResetHw
Points to the miniport’s HwVidResetHw function, which is required for any miniport driver of an adapter that does not reset fully on a soft reboot of the machine. Drivers of SVGA adapters that are fully reset to a VGA standard character mode on receipt of an INT10, MODE3-type command usually set this to NULL.
HwTimer
Points to a miniport’s HwVidTimer function, which is optional. This pointer can be NULL.

Comments

A miniport’s DriverEntry function must initialize this structure with zeros before it sets relevant values in any member.

See Also

EMULATOR_ACCESS_ENTRY, HwVidFindAdapter, HwVidInitialize, HwVidInterrupt, HwVidResetHw, HwVidStartIO, HwVidSynchronizeExecutionCallback, HwVidTimer, SvgaHwIoPort Xxx, VIDEO_ACCESS_RANGE, VIDEO_PORT_CONFIG_INFO, VideoPortZeroMemory


VIDEO_MODE

typedef struct _VIDEO_MODE {
    ULONG RequestedMode;
} VIDEO_MODE, *PVIDEO_MODE;

VIDEO_MODE contains the requested VGA mode that an adapter should set. This structure is used in conjunction with IOCTL_VIDEO_SET_CURRENT_MODE.

Members

RequestedMode
Is the mode that the miniport driver should set if possible. In addition, the two high order bits are used to request special behavior from the miniport driver. These flags are defined below:
VIDEO_MODE_ZERO_MEMORY
Indicates that the miniport should zero the video memory in conjunction with the mode set.
VIDEO_MODE_MAP_MEM_LINEAR
Indicates that the miniport should map the video memory in a linear fashion if the adapter supports such an operation.


VIDEO_PORT_CONFIG_INFO

typedef struct _VIDEO_PORT_CONFIG_INFO {
    ULONG Length;
    ULONG SystemIoBusNumber;
    INTERFACE_TYPE AdapterInterfaceType;
    ULONG BusInterruptLevel;
    ULONG BusInterruptVector;
    KINTERRUPT_MODE InterruptMode;
    ULONG NumEmulatorAccessEntries;
    PEMULATOR_ACCESS_ENTRY EmulatorAccessEntries;
    ULONG EmulatorAccessEntriesContext;
    PHYSICAL_ADDRESS VdmPhysicalVideoMemoryAddress;
    ULONG VdmPhysicalVideoMemoryLength;
    ULONG HardwareStateSize;
    ULONG DmaChannel;
    ULONG DmaPort;
    UCHAR DmaShareable;
    UCHAR InterruptShareable;
} VIDEO_PORT_CONFIG_INFO, *PVIDEO_PORT_CONFIG_INFO;

VIDEO_PORT_CONFIG_INFO contains bus-specific adapter configuration information. VideoPortInitialize allocates memory for and fills in as much information as it can in this structure before it calls the miniport driver’s HwVidFindAdapter function. If the input structure describes an adapter that the miniport supports, HwVidFindAdapter fills all remaining relevant members of this structure.

This structure is used to set up the video hardware and to supply the port driver with configuration information. The port driver can then allocate necessary system resources for the miniport driver and its adapter.

Members

Length
Is the size in bytes of this structure. The port driver always initializes the Length member. In effect, its value indicates the version of VIDEO_PORT_CONFIG_INFO being used by the system. Because this structure might grow from one version of the system to the next, a miniport driver should check this member to determine whether it is at least the sizeof(VIDEO_PORT_CONFIG_INFO) that the miniport uses to configure its adapter.
SystemIoBusNumber
Specifies the system-assigned number of the I/O bus on which the miniport’s adapter might be connected. The port driver always initializes this member.
AdapterInterfaceType
Specifies the type of bus interface. The video port driver always sets this member to the value passed in the VIDEO_HW_INITIALIZATION_DATA structure when the miniport’s DriverEntry function called VideoPortInitialize.
BusInterruptLevel
This member is irrelevant if the video hardware does not generate interrupts, indicated by setting the HwInterrupt member in the VIDEO_HW_INITIALIZATION_DATA structure to NULL. Otherwise, it specifies the bus-relative IRQL that corresponds to the interrupt request on Isa or MicroChannel type buses. The preset default value for this member is zero. A miniport driver must supply the correct value if it handles interrupts for a video adapter on an Isa or MicroChannel type bus, or for a video adapter configured for level-sensitive interrupts on an Eisa type bus.

If a miniport driver’s HwVidFindAdapter function finds that the video adapter does not generate interrupts or that it cannot determine a valid interrupt vector/level for the adapter, HwVidFindAdapter should set both BusInterruptLevel and BusInterruptVector to zero.

BusInterruptVector
This member is irrelevant if the video hardware does not generate interrupts, indicated by setting the HwInterrupt member in the VIDEO_HW_INITIALIZATION_DATA structure to NULL. Otherwise, it specifies the bus-relative vector used by the video hardware on I/O buses that use interrupt vectors, such as PCI buses. The preset default value for this member is zero.

If a miniport driver’s HwVidFindAdapter function finds that the video adapter does not generate interrupts or that it cannot determine a valid interrupt vector/level for the adapter, HwVidFindAdapter should set both BusInterruptVector and BusInterruptLevel to zero.

InterruptMode
Indicates whether the video hardware uses Latched or LevelSensitive interrupts. The port driver initializes this member if it is relevant, but a miniport driver with an ISR should check that it contains the correct value and reset it if necessary.
NumEmulatorAccessEntries
This member and all subsequent members through HardwareStateSize are irrelevant to miniport drivers that do not declare themselves to be VGA-compatible miniport drivers on x86-based Windows NT platforms. For miniport drivers that do not support VGA-compatible SVGA adapters on x86-based machines, this member should be zero. Otherwise, it specifies the number of EMULATOR_ACCESS_RANGE-type elements in the following array.
EmulatorAccessEntries
Points to an array of emulator access ranges set up by the VGA-compatible miniport driver. Each emulator access range must be a proper subset of the miniport’s VIDEO_ACCESS_RANGE-type array. Each element specifies a range of I/O ports to be hooked out by the V86 emulator and, possibly, monitored by a driver-supplied SvgaHwIoPort Xxx function whenever an MS-DOS application, running full screen, attempts to write directly to the video adapter registers. Usually, this array describes all I/O port ranges in the corresponding access ranges array. If the miniport driver defines its array of emulator access entries statically in the driver itself, the port driver initializes this pointer to that array. If NumEmulatorAccessEntries is zero, this member is NULL.
EmulatorAccessEntriesContext
Specifies a value passed with each call to an SvgaHwIoPort Xxx function described in the EmulatorAccessEntries array. Usually, a VGA-compatible miniport driver sets the value of this member to the HwDeviceExtension pointer, or to an offset within the device extension, so the miniport driver can maintain state, such as batched application-issued instructions, in its SvgaHwIoPort Xxx functions.
VdmPhysicalVideoMemoryAddress
Specifies the base (mapped) logical address of a range of video memory to be mapped into a VDM’s address space for x86 BIOS INT10 support. For miniport drivers that do not support VGA-compatible adapters on x86-based machines, this member should be NULL.
VdmPhysicalVideoMemoryLength
Specifies the size in bytes of the range be mapped into a VDM’s address space for x86 BIOS support. For miniport drivers that do not support VGA-compatible adapters on x86-based machines, this member should be zero.
HardwareStateSize
Specifies the minimum size in bytes required to store hardware state information in response to an IOCTL_VIDEO_SAVE_HARDWARE_STATE request, which must be supported only by VGA-compatible miniport drivers on x86-based machines. The initialized value for this member is zero. A VGA-compatible miniport driver must set this member to the number of bytes it requires to hold saved adapter state.
DmaChannel
Is initialized to zero and should be left as is.
DmaPort
Is initialized to zero and should be left as is.
DmaShareable
Is initialized to zero and should be left as is.
InterruptShareable
If the miniport’s device interrupts, this parameter should be set to zero if the interrupt cannot be shared with another device, or set to one if the interrupt can be shared. Otherwise, a miniport driver can ignore this member.

See Also

HwVidFindAdapter, IOCTL_VIDEO_SAVE_HARDWARE_STATE, VIDEO_ACCESS_RANGE, VIDEO_HW_INITIALIZATION_DATA, VideoPortGetAccessRanges, VideoPortGetDeviceBase, VideoPortGetDeviceData, VideoPortVerifyAccessRanges


VIDEO_REQUEST_PACKET

typedef struct _VIDEO_REQUEST_PACKET {
    ULONG IoControlCode;
    PSTATUS_BLOCK StatusBlock;
    PVOID InputBuffer;
    ULONG InputBufferLength;
    PVOID OutputBuffer;
    ULONG OutputBufferLength;
} VIDEO_REQUEST_PACKET, *PVIDEO_REQUEST_PACKET;

A pointer to a VIDEO_REQUEST_PACKET is passed to the miniport driver’s HwVidStartIO function by the video port driver. The port driver sets up the VRP from the IRP sent in response to a GDI call to EngDeviceIoControl. Usually, the corresponding display driver makes the call to EngDeviceIoControl when it needs support from the port-miniport driver pair. The port driver calls the miniport driver with the VRP to process the given IOCTL_VIDEO_XXX request.

Members

IoControlCode
Specifies an IOCTL_VIDEO_XXX value passed to the EngDeviceIoControl function by the caller and sent to the port driver in an IRP. For more information about the set of system-defined IOCTL_VIDEO_XXX that miniport drivers must support, see also Part II of this manual.
StatusBlock
Points to a STATUS_BLOCK structure in the VRP. StatusBlock is filled in by the miniport driver with return information upon completion of each VRP.
InputBuffer
Points to an input buffer that contains information passed in by the caller. The structure for the data depends on the value of IoControlCode. This member actually points to the same buffer as that indicated by OutputBuffer.
InputBufferLength
Specifies the size in bytes of the input buffer.
OutputBuffer
Points to an output buffer into which the miniport driver transfers data to be returned to the caller. The structure for the data depends on the value of IoControlCode. Because this member points to the same buffer as InputBuffer, a miniport driver must not write output in the OutputBuffer before it has consumed all input data from the InputBuffer.
OutputBufferLength
Specifies the size in bytes of the output buffer. A miniport driver cannot enlarge this buffer. A miniport driver should set the Status member of the StatusBlock with ERROR_INSUFFICIENT_BUFFER or ERROR_MORE_DATA if the given OutputBuffer is too small to contain all the returned information.

See Also

HwVidStartIO, IOCTL_VIDEO_Xxx , STATUS_BLOCK


VIDEO_x86_BIOS_ARGUMENTS

typedef struct VIDEO_x86_BIOS_ARGUMENTS {
    ULONG Eax;
    ULONG Ebx;
    ULONG Ecx;
    ULONG Edx;
    ULONG Esi;
    ULONG Edi;
    ULONG Ebp;
} VIDEO_x86_BIOS_ARGUMENTS, *PVIDEO_x86_BIOS_ARGUMENTS;

VIDEO_x86_BIOS_ARGUMENTS members include seven of the high-end x86 microprocessor registers. It is used to support full-screen MS-DOS application INT10 calls.

Comments

MS-DOS INT10s are usually “set mode” requests for the video adapter. Each value is put in the appropriate register and an INT10 call is performed by VideoPortInt10.

A miniport driver should not set a segment:offset-type pointer in any member of this structure. Such an address is interpreted as an unsigned DWORD value.

See Also

VideoPortInt10


Chapter 8 Video I/O Control Codes

This chapter summarizes the public IOCTL_VIDEO_XXX requests that video miniport drivers handle. Every video miniport driver must provide support for at least the following requests:

  • IOCTL_VIDEO_MAP_VIDEO_MEMORY

  • IOCTL_VIDEO_UNMAP_VIDEO_MEMORY

  • IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES

  • IOCTL_VIDEO_QUERY_AVAIL_MODES

  • IOCTL_VIDEO_QUERY_CURRENT_MODE

  • IOCTL_VIDEO_SET_CURRENT_MODE

  • IOCTL_VIDEO_RESET_DEVICE

Depending on the adapter features, a miniport driver can be required to support additional IOCTL_VIDEO_XXX requests, while support for others is optional.

For information on the design of display drivers and video miniport drivers, see the Graphics Driver Design Guide.

IOCTL_VIDEO_DISABLE_CURSOR

Operation

Makes the cursor invisible: that is, disables the cursor’s visibility attribute. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. Support is optional for other miniport drivers.

Input

None

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_DISABLE_POINTER

Operation

Makes the pointer invisible: that is, disables pointer attributes. Supports for this request, which is modal, is optional.

Input

None

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_ENABLE_CURSOR

Operation

Makes the cursor visible: that is, enables the cursor’s visibility attribute. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. Support is optional for other miniport drivers.

Input

None

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_ENABLE_POINTER

Operation

Makes the pointer visible: that is, enables pointer attributes. Support for this request, which is modal, is optional. However, if a miniport driver supports this request, it must process an IOCTL_VIDEO_SET_POINTER_ATTR request before processing this request.

Input

None

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_FREE_PUBLIC_ACCESS_RANGES

Operation

Releases the address range that was allocated by a preceding IOCTL_VIDEO_QUERY_PUBLIC_ACCESS_RANGES request. Support for this request, which is nonmodal, is optional.

Input

The VRP InputBuffer contains an array of VIDEO_PUBLIC_ACCESS_RANGES to be released.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_GET_BANK_SELECT_CODE

Operation

Returns a block of x86-specific executable code to be used by a high-resolution SVGA display driver for bank-switching. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. It is optional for other miniport drivers. On RISC-based machines, this request is a no-op.

Input

None

Output

The miniport driver returns the VIDEO_BANK_SELECT information in the VRP OutputBuffer.

Status Block

If the miniport driver returns its code block, it sets the Information field to sizeof(VIDEO_BANK_SELECT). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_GET_POWER_MANAGEMENT

Operation

Returns the current power-consumption level of the adapter. Support for this IOCTL, which is nonmodal, is required for adapters that conform to the VESA DPMS standard.

Input

None

Output

The miniport driver returns the VIDEO_POWER_MANAGEMENT information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the power-consumption information, it sets the Information field to sizeof(VIDEO_POWER_MANAGEMENT). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_LOAD_AND_SET_FONT

Operation

Loads a user-defined font on the adapter. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. Support is optional for other miniport drivers.

Input

The VRP InputBuffer contains the VIDEO_LOAD_FONT_INFORMATION describing the font’s width, height, and size, as well as a pointer to the font buffer.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_MAP_VIDEO_MEMORY

Operation

Maps the video hardware frame buffer and video RAM into the virtual address space of the requestor. Miniport drivers are required to handle this IOCTL and to map all video memory in the caller’s address space with VideoPortMapMemory. This request is both modal and nonmodal: the miniport driver must return the location size of the frame buffer within video memory, and the frame buffer size and location can vary from mode to mode (modal characteristic), but a subsequent set-mode operation (to the same mode) must not cause the video memory to change location (nonmodal characteristic).

Input

The VRP InputBuffer contains a VIDEO_MEMORY structure specifying either a requested virtual base address or zero, which requests the system to assign a virtual base address mapped to the adapter’s RAM and frame buffer range(s).

Output

The miniport driver returns the VIDEO_MEMORY_INFORMATION in the VRP OutputBuffer.

Status Block

If the miniport driver successfully maps the memory, it sets the Information field to sizeof(VIDEO_MEMORY_INFORMATION). Otherwise, it sets the Information field to zero.

IOCTL_VIDEO_QUERY_AVAIL_FONTS

Operation

None. This request is obsolete.

IOCTL_VIDEO_QUERY_AVAIL_MODES

Operation

Returns information about each video mode supported by the adapter. Miniport drivers are required to support this request, which is nonmodal.

Input

None

Output

The miniport driver returns an array of VIDEO_MODE_INFORMATION-type elements in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the available modes, it sets the Information field to (sizeof(VIDEO_MODE_INFORMATION) * NumberOfSupportedModes). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_COLOR_CAPABILITIES

Operation

Returns the color-capabilities information found in the VDDP description file for the adapter. Support for this request, which is nonmodal, is optional. However, if a miniport driver supports this request, it cannot return a subset of the color-capabilities information.

Input

None

Output

The miniport driver returns all VIDEO_COLOR_CAPABILITIES information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the color-capabilities data, it sets the Information field to sizeof(VIDEO_COLOR_CAPABILITIES). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_CURRENT_FONT

Operation

None. This request is obsolete.

IOCTL_VIDEO_QUERY_CURRENT_MODE

Operation

Returns information about the current adapter mode. Miniport drivers are required to support this request, which is modal.

Input

None

Output

The miniport driver returns the VIDEO_MODE_INFORMATION in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the mode data, it sets the Information field to sizeof(VIDEO_MODE_INFORMATION). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_CURSOR_ATTR

Operation

Returns the size, position, and visibility of the cursor. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. Support is optional for other miniport drivers. The cursor position is specified by top and bottom scan lines, instead of row and column information, for VGA-compatible adapters.

Input

None

Output

The miniport driver returns the VIDEO_CURSOR_ATTRIBUTES information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the cursor-attribute data, it sets the Information field to sizeof(VIDEO_CURSOR_ATTRIBUTES). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_CURSOR_POSITION

Operation

Returns the current cursor position (row and column). Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. It is optional for other miniport drivers.

Input

None

Output

The miniport driver returns the VIDEO_CURSOR_POSITION information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the cursor position, it sets the Information field to sizeof(VIDEO_CURSOR_POSITION). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_NUM_AVAIL_FONTS

Operation

None. This request is obsolete.

IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES

Operation

Returns the number of video modes supported by the adapter and the size in bytes of the video mode information, which can be used to allocate a buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request. Miniport drivers are required to support this request, which is nonmodal.

Input

None

Output

The miniport driver returns the VIDEO_NUM_MODES in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the mode count, it sets the Information field to sizeof(VIDEO_NUM_MODES). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_POINTER_ATTR

Operation

Returns the attributes for the pointer, defined as a set of pixels used to paint the shape associated with the mouse. Support for this request, which is modal, is optional.

Input

None

Output

The miniport driver returns the VIDEO_POINTER_ATTRIBUTES information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the pointer attributes, it sets the Information field to sizeof(VIDEO_POINTER_ATTRIBUTES). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES

Operation

Returns information about the adapter’s “hardware cursor” features. Support for this request, which is modal, is optional.

Input

None

Output

The miniport driver returns the VIDEO_POINTER_CAPABILITIES information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the pointer capabilities, it sets the Information field to sizeof(VIDEO_POINTER_CAPABILITIES). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_POINTER_POSITION

Operation

Returns the current position of the pointer. Support for this request, which is modal, is optional.

Input

None

Output

The miniport driver returns the VIDEO_POINTER_POSITION information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully returns the pointer position, it sets the Information field to sizeof(VIDEO_POINTER_POSITION). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_QUERY_PUBLIC_ACCESS_RANGES

Operation

Returns an array, possibly with one element, of address ranges used to program the adapter registers or ports directly. Support for this request, which is nonmodal, is optional.

Input

None

Output

The miniport driver returns an array of VIDEO_PUBLIC_ACCESS_RANGES in the VRP OutputBuffer.

Status Block

If the miniport driver returns address range(s), it sets the Information field to sizeof(VIDEO_PUBLIC_ACCESS_RANGES). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_RESET_DEVICE

Operation

Resets the video hardware to the default mode, to which it was initialized at system boot. Miniport drivers are required to support this request, which is nonmodal.

Input

None

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_RESTORE_HARDWARE_STATE

Operation

Restores all values used to set the hardware registers. Miniport drivers for VGA-compatible adapters are required to support this request, which is nonmodal. It is optional for other miniport drivers.

Input

The VRP InputBuffer contains the VIDEO_HARDWARE_STATE previously saved.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SAVE_HARDWARE_STATE

Operation

Saves the state of the video adapter in the VRP OutputBuffer. Miniport drivers for VGA-compatible adapters are required to support this request, which is nonmodal. Support is optional for other miniport drivers.

Input

None

Output

The miniport driver returns the VIDEO_HARDWARE_STATE information in the VRP OutputBuffer.

Status Block

If the miniport driver successfully saves the adapter state, it sets the Information field to sizeof(VIDEO_HARDWARE_STATE). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_SET_COLOR_REGISTERS

Operation

Sets the adapter’s color registers to the specified RGB values. If the adapter has a CLUT, sometimes called a “palette,” the miniport driver is required to support this request, which is modal.

Input

The VRP InputBuffer contains the VIDEO_CLUT, specifying an array of RGB values to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_CURRENT_FONT

Operation

None. This request is obsolete.

IOCTL_VIDEO_SET_CURRENT_MODE

Operation

Sets the adapter to the specified operating mode. Miniport drivers are required to support this request, which is nonmodal since it resets the current mode. The miniport driver must also consider the two high order flags which are used to additionally control the mode set operation. See VIDEO_MODE for further information.

Input

The VRP InputBuffer contains a VIDEO_MODE structure specifying the mode to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_CURSOR_ATTR

Operation

Sets the cursor size, position, and visibility. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. Support is optional for other miniport drivers.

Input

The VRP InputBuffer contains the VIDEO_CURSOR_ATTRIBUTES to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_CURSOR_POSITION

Operation

Sets the cursor position (row and column). Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. It is optional for other miniport drivers.

Input

The VRP InputBuffer contains the VIDEO_CURSOR_POSITION to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_PALETTE_REGISTERS

Operation

Loads a specified portion of the adapter palette registers with an array of colors. Miniport drivers for VGA-compatible adapters are required to support this request, which is modal. Support is optional for other miniport drivers.

Input

The VRP InputBuffer contains the VIDEO_PALETTE_DATA, specifying an array of colors to be set in the palette registers.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_POINTER_ATTR

Operation

Sets the pointer attributes. Support for this request, which is modal, is optional. A supporting miniport driver can fail this request (for example, if the attributes specify a larger pointer than the miniport driver can supply). When a miniport driver fails this request, the corresponding display driver must manage the pointer.

Input

The VRP InputBuffer contains the VIDEO_POINTER_ATTRIBUTES to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_POINTER_POSITION

Operation

Sets the pointer position. Support for this request, which is modal, is optional. A supporting miniport driver should have already processed an enable-pointer request before processing this request.

Input

The VRP InputBuffer contains the VIDEO_POINTER_POSITION to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SET_POWER_MANAGEMENT

Operation

Resets the power-consumption level of the adapter to the specified state. As long as the state is VideoPowerOff, the port driver intercepts and fails all IOCTL_VIDEO_XXX requests to the miniport driver until the next IOCTL_VIDEO_SET_POWER_MANAGEMENT request that resets the state to a power-on condition.

Input

The VRP InputBuffer contains the VIDEO_POWER_MANAGEMENT structure, specifying the state to be set.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_SHARE_VIDEO_MEMORY

Operation

This IOCTL is called by a process that will share video memory as a linear frame buffer. Miniport drivers handle this IOCTL by mapping the frame buffer into the caller’s address space with VideoPortMapBankedMemory. Otherwise this IOCTL is the same as IOCTL_VIDEO_MAP_VIDEO_MEMORY.

This request is both modal and nonmodal: the miniport driver must return the location size of the frame buffer within video memory, and the frame buffer size and location can vary from mode to mode (modal characteristic), but a subsequent set-mode operation (to the same mode) must not cause the video memory to change location (nonmodal characteristic)

This IOCTL is DCI only.

Input

The VRP InputBuffer contains a VIDEO_SHARE_MEMORY structure specifying a handle to the process mapping the frame buffer, and the requested view size and offset into the shared memory. The view size plus the offset must not exceed the memory size of the adapter.

Output

The miniport driver returns the VIDEO_MEMORY_INFORMATION structure in the VRP OutputBuffer. This output contains the virtual address where the video memory is mapped, the view size in bytes of the mapped memory and the offset into the view.

Status Block

If the miniport driver successfully maps the memory, it sets the Information field to sizeof(VIDEO_SHARE_MEMORY_INFORMATION). Otherwise, the miniport driver sets the Information field to zero.

IOCTL_VIDEO_UNMAP_VIDEO_MEMORY

Operation

Releases a mapping between the virtual address space and the adapter’s frame buffer and video RAM. Miniport drivers are required to support this request, which is nonmodal.

Input

The VRP InputBuffer contains the VIDEO_MEMORY, specifying the previously mapped virtual base address to be released with VideoPortUnmapMemory.

Output

None

Status Block

The miniport driver does not set the Information field.

IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY

Operation

Unmaps a video hardware frame buffer and video RAM from the virtual address space of the requestor that was mapped by an IOCTL_VIDEO_SHARE_VIDEO_MEMORY request.

Input

The VRP InputBuffer contains a VIDEO_MEMORY structure specifying the process and the virtual address where the video memory is mapped. The memory is unmapped by calling VideoPortUnmapMemory.

Output

None.

Status Block

The miniport driver does not set the Information field.


Chapter 9 Printer User Interface Functions and Structures

This chapter describes the functions and structures that a printer driver’s user interface component must support. Printer driver function and structure declarations are found in winddiui.h, which is provided with this DDK.

If you implement a printer user interface module for your printing device, you must ensure that this module can communicate with the Common Property Sheet User Interface. Reference material for the CPSUI can be found in Common Property Sheet User Interface Functions and Structures.


Printer User Interface Functions

This section describes the functions that must be supported by a printer driver’s user interface component.


DevQueryPrint

BOOL DevQueryPrint(

HANDLE hPrinter,

PDEVMODE pDevMode,

DWORD *pResID

);

DevQueryPrint is obsolete and is supported for backward compatibility only. New printer driver user interfaces must implement DevQueryPrintEx instead.


DevQueryPrintEx

BOOL DevQueryPrintEx(

PDEVQUERYPRINT_INFO pDQPInfo

);

DevQueryPrintEx determines whether or not the driver can print the specified job on a printer.

Parameters

pDQPInfo
Pointer to the DEVQUERYPRINT_INFO structure that contains the data required to do the query.

Return Value

DevQueryPrintEx returns TRUE if the job is printable and not to be held; otherwise, it returns FALSE.

Comments

The spooler calls DevQueryPrintEx when a print job is scheduled to print or when the Hold Mismatched Documents option is checked on the printer’s Scheduling property sheet page in the Printers folder.

The driver should always validate the pDevMode field of the DEVQUERYPRINT_INFO structure before performing the query.

If the job is determined to be printable, the driver need not return an error string.

If the job is not printable, the driver should return the applicable error string and its length in pszErrorStr and cchNeeded, respectively. If the buffer to which pszErrorStr points is too small to hold the entire error string, the driver should truncate the error string to be cchErrorStr characters in length, including the null terminator. Regardless of whether the string is truncated, the driver should always return the length of the original error string in cchNeeded. The driver should define resource error strings for possible error conditions, such as memory allocation failure or the inability to open a printer’s data file.

DevQueryPrintEx must be implemented.


DrvAdvancedDocumentProperties

LONG DrvAdvancedDocumentProperties(

HWND hwnd,

HANDLE hPrinter,

PWSTR lpszDevName,

PDEVMODE pdmOutput,

PDEVMODE pdmInput

);

DrvAdvancedDocumentProperties is obsolete and is supported for backward compatibility only. New printer driver user interfaces should implement DrvDocumentPropertySheets instead.


DrvConvertDevMode

BOOL DrvConvertDevMode(

LPTSTR pPrinterName,

PDEVMODE pdmIn,

PDEVMODE pdmOut,

PLONG pcbNeeded,

DWORD fMode

);

DrvConvertDevMode converts the specified DEVMODE.

Parameters

pPrinterName
Pointer to the name of the printer whose DEVMODE is to be converted. The driver passes this string to the Win32 OpenPrinter function to get related printer data.
pdmIn
Pointer to the input DEVMODE structure. If pdmIn is null, the driver need not perform a DEVMODE conversion. Otherwise, pdmIn must point to a DEVMODE that is valid to the driver.
pdmOut
Pointer to an output DEVMODE buffer. If fMode is CDM_CONVERT, the buffer must be a valid DEVMODE from this driver.
pcbNeeded
As input, pcbNeeded points to a LONG value that specifies the size, in bytes, of pdmOut. As output, pcbNeeded specifies the number of bytes converted and copied into pdmOut if the function succeeds. If the function fails because pdmOut is too small, pcbNeeded specifies the number of bytes required to perform the conversion.
fMode
Specifies the conversion to be performed. One of the following constants is specified to indicate how the converted or default DEVMODE should be returned:

Value

Meaning

CDM_CONVERT

The driver should determine that both pdmIn and pdmOut point to valid DEVMODEs that were previously returned by it. If either DEVMODE is null or invalid, the driver should set the last-error code to ERROR_INVALID_PARAMETER and return FALSE. Otherwise, the driver should convert the input DEVMODE to the output DEVMODE.

CDM_CONVERT351

If the input value of pcbNeeded specifies an output buffer of sufficient size, the driver should copy its Windows NT Version 3.51 default DEVMODE to pdmOut. Then, if pdmIn is not null, the driver should convert the input DEVMODE to its Windows NT 3.51 DEVMODE, merging the results in the buffer to which pdmOut points. If pcbNeeded does not point to a buffer of sufficient size, the driver should update pcbNeeded with the required buffer size, set the last-error code to ERROR_INSUFFICIENT_BUFFER, and return FALSE.
The driver should simply convert the DEVMODE to the current version if it does not have a Windows NT 3.51 DEVMODE version.

CDM_DRIVER_DEFAULT

If the input value of pcbNeeded specifies an output buffer of sufficient size, the driver should copy the current version of its default DEVMODE to pdmOut. If pcbNeeded does not point to a buffer of sufficient size, the driver should update pcbNeeded with the required buffer size, set the last-error code to ERROR_INSUFFICIENT_BUFFER, and return FALSE. The pdmIn parameter is always null.

Return Value

DrvConvertDevMode returns TRUE when it succeeds; otherwise, it sets a last-error code and returns FALSE.

Comments

The spooler calls DrvConvertDevMode when a call is made to the Win32 SetPrinter or GetPrinter function. DrvConvertDevMode translates the DEVMODE from another operating system version to the DEVMODE of the spooler’s operating system version. The interoperation of a network of machines with different operating system levels requires that a DEVMODE be converted so that it matches the version of the driver/operating system on a particular machine.


DrvDeviceCapabilities

DWORD DrvDeviceCapabilities(

HANDLE hPrinter,

PWSTR pDeviceName,

WORD iDevCap,

VOID *pvOutput,

PDEVMODE pDevMode

);

DrvDeviceCapabilities provides information about the specified device and its capabilities.

Parameters

hPrinter
Handle to the printer whose capabilities are being queried.
pDeviceName
Points to a null-terminated string that specifies the name of the printing device.
iDevCap
Specifies the device capability about which information is requested. This parameter can be one of the following values, as defined in windef.h:

Flag

Description

DC_FIELDS

Returns the dmFields member of the printer driver’s DEVMODE structure, indicating which members in the device-independent portion of the structure are supported by the printer driver.

DC_PAPERS

Writes a list of indexes in the WORD array to which pvOutput points, where each index represents a supported form. The return value is set to the number of supported forms.

DC_PAPERSIZE

Writes a list of supported paper sizes, in 0.1mm increments, in the array of POINT structures to which pvOutput points. The return value is set to the number of supported paper sizes.

DC_PAPERNAMES

Writes a list of supported form names in the Unicode string array to which pvOutput points. The pvOutput parameter points to an array of string buffers that are each 64 characters in length. If a form name is less than 64 characters in length, the driver pads that buffer with null characters; if a form name is equal to 64 characters in length, the driver does not append a null character. Form names that are greater than 64 characters in length are truncated and no null character is appended. Return value is set to the number of supported form names.

DC_MINEXTENT

Writes the minimum allowable values for the dmPaperLength and dmPaperWidth members of the driver’s DEVMODE structure. The function returns these values in a POINTS structure. The low and high words of the returned value specify x and y, respectively.

DC_MAXEXTENT

Writes the maximum allowable values for the dmPaperLength and dmPaperWidth members of the printer driver’s DEVMODE structure. The function returns these values in a POINTS structure. The low and high words of the returned value specify x and y, respectively.

DC_BINS

Writes a list of indexes in a WORD array to which pvOutput points, where each index represents an available paper bin. Returns the number of input bins.

DC_BINNAMES

Writes a list of the names of the input bins in pvOutput. The pvOutput parameter points to an array of string buffers that are each 24 characters in length. The driver should add a terminating null character to each string. Bin names that are greater than 24 characters in length are truncated and a null character is appended. Returns the number of input bins.

DC_DUPLEX

Returns 1 if the printer supports duplex printing; otherwise, zero is returned.

DC_SIZE

Returns the dmSize member of the printer driver’s DEVMODE structure.

DC_EXTRA

Returns the dmDriverExtra member of the driver’s DEVMODE structure.

DC_VERSION

Returns the dmSpecVersion member of the driver’s DEVMODE structure.

DC_DRIVER

Returns the dmDriverVersion member of driver’s DEVMODE structure.

DC_ENUMRESOLUTIONS

Writes the list of printer-supported resolutions in pvOutput. Each resolution is specified as a pair of LONG integers that represent the horizontal and vertical resolutions. The return value is the number of printer-supported resolutions.

DC_FILEDEPENDENCIES

Writes the names of all files that need to be loaded when the driver is installed in pvOutput. The pvOutput parameter points to an array of string buffers that are each 64 characters in length. Each filename is a null-terminated string. The return value is the number of files.

DC_TRUETYPE

Returns a bitwise OR of one or more of the following values, indicating the ability of the driver to use TrueType fonts (a value of zero indicates no support):
DCTT_BITMAP: Device can print TrueType fonts as graphics.
DCTT_DOWNLOAD: Device can download TrueType fonts.
DCTT_SUBDEV: Device can substitute device fonts for TrueType fonts.

DC_ORIENTATION

Returns the relationship between portrait and landscape orientations for a device. The return value is in terms of the number of degrees that portrait orientation is rotated counterclockwise to produce landscape orientation. Zero means that landscape orientation is not available.

DC_COPIES

Returns the maximum number of copies of a document that the printer can print.

pvOutput
Pointer to the output buffer in which device capability data can be returned. The size and type of returned device capability data, if any, depends on the value specified in iDevCap.
pDevMode
Pointer to the source DEVMODE structure.

Return Value

DrvDeviceCapabilities returns the specified device’s capabilities upon success. The actual value depends on iDevCap. The driver should turn off the iDevCap bit for any feature it does not support and return a single value for that value. For example, if the device does not support multiple resolutions, the drivers returns only one resolution when queried with an iDevCap value of DC_ENUMRESOLUTIONS.

This function returns GDI_ERROR if it fails.

Comments

If pvOutput is null for an iDevCap value that requires a buffer, the driver should set the return value accordingly so that the caller can allocate the correct size of buffer and call DrvDeviceCapabilities again.


DrvDevicePropertySheets

LONG DrvDevicePropertySheets(

PPROPSHEETUI_INFO pPSUIInfo,

LPARAM lParam

);

DrvDevicePropertySheets adds the driver’s printer properties page(s) to the specified property sheet.

Parameters

pPSUIInfo
Pointer to a PROPSHEETUI_INFO structure containing the information necessary to call back to the CPSUI for the actual property sheet construction.
lParam
Pointer to a DEVICEPROPERTYHEADER structure.

Return Value

DrvDevicePropertySheets returns a value greater than zero if it succeeds; it returns a value equal to or less than zero if it fails.

Comments

DrvDevicePropertySheets is called when the Print Folder requests that a printer’s device properties be displayed and/or updated on that printer’s property sheet. This function allows vendors to provide device-specific information to the CPSUI for inclusion as a page on a common property sheet.

DrvDevicePropertySheets should follow the implementation guidelines described in the CPSUI reference page for PFNPROPSHEETUI.


DrvDocumentEvent

INT DrvDocumentEvent(

HANDLE hPrinter,

HDC hdc,

int iEsc,

ULONG cbIn,

PULONG pbIn,

ULONG cbOut,

PULONG pbOut

);

DrvDocumentEvent allows the printer driver UI to hook certain printer management calls.

Parameters

hPrinter
Handle that identifies the printer.
hdc
Handle to the device context
iEsc
Specifies the escape code that identifies the event to be handled. The escape code is packed in the LOWORD of iEsc, and the HIWORD contains zero. This parameter can be one of the following values:

Escape

Definition

DOCUMENTEVENT_CREATEDCPRE

User-mode GDI is calling DrvDocumentEvent before sending an application’s CreateDC call to kernel-mode GDI for processing.
pbIn points to an array of four ULONGs that are 0, pointer to the printer name, pointer to the input DEVMODE, and a Boolean value that specifies whether the call was CreateIC (TRUE) or CreateDC (FALSE).
The driver should return a pointer to the output DEVMODE in pbOut.

DOCUMENTEVENT_CREATEDCPOST

User-mode GDI is calling DrvDocumentEvent after kernel-mode GDI has processed an application’s CreateDC call.
pbIn points to the DEVMODE structure returned by a previous call to DrvDocumentEvent with an iEsc of DOCUMENTEVENT_CREATEDCPRE.
pbOut is not used.

DOCUMENTEVENT_RESETDCPRE

User-mode GDI is calling DrvDocumentEvent before sending an application’s ResetDC call to kernel-mode GDI for processing.
pbIn points to an input DEVMODE.
The driver should return a pointer to the output DEVMODE in pbOut.

DOCUMENTEVENT_RESETDCPOST

User-mode GDI is calling DrvDocumentEvent after kernel-mode GDI has processed an application’s ResetDC call.
pbIn points to the DEVMODE structure returned by a previous call to DrvDocumentEvent with an iEsc of DOCUMENTEVENT_RESETDCPRE.
pbOut is not used.

DOCUMENTEVENT_STARTDOCPRE
DOCUMENTEVENT_STARTDOC

User-mode GDI is calling DrvDocumentEvent before sending an application’s StartDoc call to kernel-mode GDI for processing.
pbIn points to an input DOCINFO structure.
pbOut is not used.

DOCUMENTEVENT_STARTDOCPOST

User-mode GDI is calling DrvDocumentEvent after kernel-mode GDI has processed an application’s StartDoc call.
pbIn points to a ULONG that specifies the ID of the current print job.
pbOut is not used.

DOCUMENTEVENT_STARTPAGE

User-mode GDI is calling DrvDocumentEvent before sending an application’s StartPage call to kernel-mode GDI for processing.
Neither pbIn or pbOut are used.

DOCUMENTEVENT_ENDPAGE

User-mode GDI is calling DrvDocumentEvent before sending an application’s EndPage call to kernel-mode GDI for processing.
Neither pbIn or pbOut are used.

DOCUMENTEVENT_ENDDOCPRE
DOCUMENTEVENT_ENDDOC

User-mode GDI is calling DrvDocumentEvent before sending an application’s EndDoc call to kernel-mode GDI for processing.
Neither pbIn or pbOut are used.

DOCUMENTEVENT_ENDDOCPOST

User-mode GDI is calling DrvDocumentEvent after kernel-mode GDI has processed an application’s EndDoc call.
Neither pbIn or pbOut are used.

DOCUMENTEVENT_ABORTDOC

User-mode GDI is calling DrvDocumentEvent before sending an application’s AbortDoc call to kernel-mode GDI for processing.
Neither pbIn or pbOut are used.

DOCUMENTEVENT_DELETEDC

User-mode GDI is calling DrvDocumentEvent before sending an application’s DeleteDC call to kernel-mode GDI for processing.
Neither pbIn or pbOut are used.

DOCUMENTEVENT_ESCAPE

User-mode GDI is calling DrvDocumentEvent before sending an application’s ExtEscape call to kernel-mode GDI for processing.
pbIn points to an array of three ULONGs that specify the nEscape, cbInput, and lpszInData parameters of the Win32 ExtEscape.
The driver should return cbOut and pbOut so that they correspond with the cbOutput and lpszOutData parameters to ExtEscape.

cbIn
Size in bytes of the buffer to which pbIn points.
pbIn
Pointer to the input data associated with iEsc, if any. See the description of iEsc for details.
cbOut
Size in bytes of the buffer to which pbOut points.
pbOut
Pointer to the output data associated with iEsc, if any. See the description of iEsc for details.

Return Value

DrvDocumentEvent returns one of the following values:
Return Value | Meaning
DOCUMENTEVENT_SUCCESS | The driver successfully handled the hooked call identified by iEsc.
DOCUMENTEVENT_UNSUPPORTED | The driver does not support the hooked call identified by iEsc.
DOCUMENTEVENT_FAILURE | The driver supports the hooked call identified by iEsc, but failed to handle the request.

Comments

A printer driver UI can optionally implement DrvDocumentEvent to hook certain printer management calls made by an application to GDI. This enables the printer driver’s UI module to process data in user mode before GDI passes the call to kernel-mode, and to post-process the data returned from kernel-mode before GDI returns it to the application. DrvDocumentEvent is executed in the context of the application’s process.

GDI calls DrvDocumentEvent before sending the application call on to kernel-mode GDI when the escape code definition has no suffix or is suffixed with PRE. For those escape code definitions suffixed with POST, GDI calls DrvDocumentEvent after the call is processed by kernel-mode GDI but before the results are returned to the application.

A FAX driver can implement DrvDocumentEvent so that it displays a user interface dialog box upon a call to the Win32 StartDoc routine.


DrvDocumentProperties

LONG DrvDocumentProperties(

HWND hwnd,

HANDLE hPrinter,

PWSTR lpszDeviceName,

PDEVMODE pdmOutput,

PDEVMODE pdmInput,

DWORD fMode

);

DrvDocumentProperties is obsolete and is supported for backward compatibility only. Printer driver user interface writers should implement DrvDocumentPropertySheets instead.


DrvDocumentPropertySheets

LONG DrvDocumentPropertySheets(

PPROPSHEETUI_INFO pPSUIInfo,

LPARAM lParam

);

DrvDocumentPropertySheets adds the driver’s document property pages to the specified property sheet.

Parameters

pPSUIInfo
Pointer to a PROPSHEETUI_INFO structure containing the information necessary to call back to the CPSUI for the actual property sheet construction.
lParam
Pointer to a DOCUMENTPROPERTYHEADER structure.

Return Value

DrvDocumentPropertySheets returns a value greater than zero if it succeeds; it returns a value equal to or less than zero if it fails.

Comments

DrvDocumentPropertySheets is called when a user requests that a printer’s document properties be displayed and/or updated.

If pPSUIInfo is null, the caller is the spooler, so the driver need not call the CPSUI because no user interface is required. Instead, the driver should perform the operation requested in the fMode member of the DOCUMENTPROPERTYHEADER structure. If fMode is zero or the pdmOut member is null, the driver should simply return the size in bytes of its DEVMODE structure.

If pPSUIInfo is not null, the caller is the CPSUI. In this situation, DrvDocumentPropertySheets should follow the implementation guidelines laid out in PFNPROPSHEETUI. This function allows vendors to provide device-specific document property information to the CPSUI for inclusion as a page on a common property sheet.


DrvPrinterEvent

BOOL DrvPrinterEvent

LPWSTR pPrinterName,

INT DriverEvent,

DWORD Flags,

LPARAM lParam

);

DrvPrinterEvent is called by the print subsystem when an event occurs that might require action by the printer driver.

Parameters

pPrinterName
Pointer to a null-terminated string that identifies the printer. The string name can identify a local or remote printer; that is, it can be in the form of “PrinterName” or “\\Machine\PrinterName”.
DriverEvent
Specifies the event that occurred. Possible values for DriverEvent are as follows:
  • PRINTER_EVENT_ADD_CONNECTION

  • PRINTER_EVENT_DELETE_CONNECTION

  • PRINTER_EVENT_INITIALIZE

  • PRINTER_EVENT_DELETE

  • PRINTER_EVENT_CACHE_REFRESH

  • PRINTER_EVENT_CACHE_DELETE
Flags
Bitmask in which the following field can be set:

Value

Meaning

PRINTER_EVENT_FLAG_NO_UI

The driver should not display any kind of user interface.

lParam
Specifies the parameters associated with the event that occurred. This field is currently always null.

Return Value

DrvPrinterEvent returns TRUE upon success; otherwise it returns FALSE.

Comments

The print subsystem calls DrvPrinterEvent when certain events occur that might require action by the printer driver. Printer interface drivers must support the PRINTER_EVENT_INITIALIZE event; all other events can be supported optionally. Details associated with each DriverEvent are described in the sections that follow.

PRINTER_EVENT_ADD_CONNECTION

The print subsystem calls DrvPrinterEvent with this event after a successful call to the Win32 AddPrinterConnection function. The driver should store connection settings in the HKEY_CURRENT_USER key of the registry, because printer connections are stored on a per-user basis. Thus, when a user with a floating profile logs into other machines, the connection floats too. Because printer connections can float, DrvPrinterEvent is not called from any machines that a user subsequently logs into after establishing a printer connection on their current machine.

The string to which pPrinterName points always identifies a remote printer for this DriverEvent. The user typically does not have administrative privileges on the remote server.

DrvPrinterEvent is called in the context of the calling application for this event; the calling application is usually the Printer Folder. As a result, the driver can display a user interface. The print subsystem ignores the value returned by the driver.

PRINTER_EVENT_DELETE_CONNECTION

The print subsystem calls DrvPrinterEvent with this event before calling the Win32 DeletePrinterConnection function. The driver should delete connection settings stored earlier in the HKEY_CURRENT_USER key of the registry during a previous call to this function with a DriverEvent of PRINTER_EVENT_ADD_CONNECTION, and perform any other clean-up.

The string to which pPrinterName points always identifies a remote printer for this DriverEvent. The user typically does not have administrative privileges on the remote server.

DrvPrinterEvent is called in the context of the calling application for this event; the calling application is usually the Printer Folder. As a result, the driver can display a user interface. The print subsystem ignores the value returned by the driver.

PRINTER_EVENT_INITIALIZE

The print subsystem calls DrvPrinterEvent with this event when a printer is created so that the driver can initialize its registry settings. It is also called when the Win32 SetPrinter function is called and the driver changes.

DrvPrinterEvent is called in the context of the spooler process for this event; consequently, the driver must not display a user interface. The spooler verifies that the user has administrative privileges before making the call, and calls the driver while impersonating the user.

A driver must implement this event.

PRINTER_EVENT_DELETE

The print subsystem calls DrvPrinterEvent with this event when a printer is being deleted so that the driver can delete its registry settings.

DrvPrinterEvent is called in the context of the spooler process for this event; consequently, the driver must not display a user interface. The spooler verifies that the user has administrative privileges before making the call, and calls the driver while impersonating the user.

PRINTER_EVENT_CACHE_REFRESH

The print subsystem calls DrvPrinterEvent with this event when the spooler is establishing a workstation cache or detects that a change to the cache has occurred. The driver can update any private cache data, such as font files, at this time.

DrvPrinterEvent is called in the context of the spooler process for this event; consequently, the driver must not display a user interface. The print subsystem ignores the value returned by the driver.

PRINTER_EVENT_CACHE_DELETE

The print subsystem calls DrvPrinterEvent with this event while the spooler is deleting a cached printer. The driver should delete any privately cached data, such as font files, at this time.

DrvPrinterEvent is called in the context of the spooler process for this event; consequently, the driver must not display a user interface. The print subsystem ignores the value returned by the driver.


DrvUpgradePrinter

BOOL DrvUpgradePrinter(

DWORD Level,

LPBYTE pDriverUpgradeInfo

);

DrvUpgradePrinter upgrades a printer’s registry device settings.

Parameters

Level
Specifies the version number of the structure to which pDriverUpgradeInfo points. Currently this value is always 1.
pDriverUpgradeInfo
Pointer to a DRIVER_UPGRADE_INFO_1 structure containing the information required by the driver to perform the upgrade.

Return Value

If DrvUpgradePrinter is successful, the return value is TRUE. Otherwise, DrvUpgradePrinter sets a last-error code and returns FALSE.

Comments

The spooler calls DrvUpgradePrinter for every printer when a new printer driver is copied onto the system. This occurs when a system is upgraded from one operating system release to the next, or when a user updates a printer driver by calling the Win32 AddPrinterDriver function. DrvUpgradePrinter should update the registry with the new device settings for the specified printer.


Printer User Interface Structures

This section describes the structures associated with a printer user interface module.


DEVICEPROPERTYHEADER

typedef struct _DEVICEPROPERTYHEADER {
    WORD            cbSize;
    WORD            Flags;
    HANDLE          hPrinter;
    LPTSTR          pszPrinterName;
} DEVICEPROPERTYHEADER, *PDEVICEPROPERTYHEADER;

The DEVICEPROPERTYHEADER structure is passed to DrvDevicePropertySheets as the lParam parameter.

Members

cbSize
Size of this structure, in bytes.
Flags
Reserved. Must be set to zero.
hPrinter
Handle to the printer whose printer properties are being queried or set.
pszPrinterName
Pointer to a null-terminated string that specifies the name of the printer.

Comments

The Print Folder initializes and provides a pointer to this structure to DrvDevicePropertySheets when a user requests that a device’s property sheet page(s) be displayed.

See Also

PFNPROPSHEETUI


DEVQUERYPRINT_INFO

typedef struct _DEVQUERYPRINT_INFO {
    WORD          cbSize;
    WORD          Level;
    HANDLE        hPrinter;
    PDEVMODE      pDevMode;
    LPTSTR        pszErrorStr;
    DWORD         cchErrorStr;
    DWORD         cchNeeded;
} DEVQUERYPRINT_INFO, *PDEVQUERYPRINT_INFO;

The DEVQUERYPRINT_INFO structure contains information required by the driver’s DevQueryPrintEx function to determine whether a print job is printable.

Members

cbSize
Specifies the size in bytes of this structure.
Level
Specifies the level of this structure. Currently, this member is always set to 1.
hPrinter
Handle to the printer on which the query is to be made.
pDevMode
Pointer to the DEVMODE structure that describes the job to be printed.
pszErrorStr
Pointer to a buffer in which a null-terminated error string can be returned if the driver determines that the job is unprintable.
cchErrorStr
Specifies the size in bytes of the string buffer pointed to by pszErrorStr.
cchNeeded
Specifies the size, in bytes, of the error string returned in the buffer to which pszErrorStr points if the buffer is large enough to hold the entire string. If the supplied buffer is too small, cchNeeded specifies the buffer size required for the entire error string. In this situation, the driver should truncate the error string to fit the supplied buffer.


DOCUMENTPROPERTYHEADER

typedef struct _DOCUMENTPROPERTYHEADER {
    WORD            cbSize;
    WORD            Reserved;
    HANDLE          hPrinter;
    LPTSTR          pszPrinterName;
    PDEVMODE        pdmIn;
    PDEVMODE        pdmOut;
    DWORD           cbOut;
    DWORD           fMode;
} DOCUMENTPROPERTYHEADER, *PDOCUMENTPROPERTYHEADER;

The DOCUMENTPROPERTYHEADER structure is passed to DrvDocumentPropertySheets as the lParam parameter.

Members

cbSize
Size of this structure, in bytes.
Reserved
Reserved. Must be set to zero.
hPrinter
Handle to the printer whose configuration data is being queried or set.
pszPrinterName
Pointer to a null-terminated string that specifies the name of the printer.
pdmIn
Pointer to the input DEVMODE structure that contains the configuration data to be used by the driver to initialize the controls of the dialog box. This parameter can be null.
pdmOut
Pointer to an output DEVMODE structure in which the final printer configuration data is returned. This parameter can be null.
cbOut
Specifies the size, in bytes, of the buffer to which pdmOut points.
fMode
Bitmask specifying the operation(s) to be performed by DrvDocumentPropertySheets. If this member is zero, the driver should simply return the size, in bytes, of its DEVMODE structure. Otherwise, this parameter can be one or more of the following values:

Flag

Meaning

DM_IN_BUFFER

The buffer to which pdmIn points is valid. Before doing anything else, the driver should merge its current settings with the settings in the input DEVMODE, updating only those members specified in the dmField member of the input DEVMODE. In cases of conflict during the merge, the driver should use the setting provided by the input DEVMODE.

This value is also defined as DM_MODIFY.

DM_IN_PROMPT

The buffer to which pdmIn points is valid. The driver should present its Document Properties property sheet using the default values specified in the input DEVMODE. The driver should update its DEVMODE structure to those values specified by the user through the UI. The driver should ignore this field when pPSUIInfo is null, because no dialog box interaction should occur.

This value is also defined as DM_PROMPT.

DM_OUT_BUFFER

The driver should write its current print settings, including private data, in the buffer to which pdmOut points. If the DM_OUT_BUFFER bit is not set, pdmOut can be null.

This value is also defined as DM_COPY.

DM_ADVANCED

DrvDocumentPropertySheets has been called as a result of an application call to the Win32 AdvancedDocumentProperties function. This bit is set by the spooler.

DM_NOPERMISSION

The driver should not allow the user to change any items on the property sheet. This bit is set by the spooler.

DM_USER_DEFAULT

DrvDocumentPropertySheets was called by the Printer Folder as a result of the user selecting “Document Properties”.


DRIVER_UPGRADE_INFO_1

typedef struct _DRIVER_UPGRADE_INFO_1 {
    LPTSTR        pPrinterName;
    LPTSTR        pOldDriverDirectory;
} DRIVER_UPGRADE_INFO_1, *PDRIVER_UPGRADE_INFO_1;

The spooler passes DRIVER_UPGRADE_INFO_1 as the pDriverUpgradeInfo parameter to a printer driver’s DrvUpgradePrinter function.

Members

pPrinterName
Pointer to a null-terminated string that specifies the name of the printer.
pOldDriverDirectory
Pointer to a null-terminated string that specifies the local directory in which the old printer driver files can be found.


Chapter 10 Common Property Sheet User Interface Functions and Structures

This chapter describes the functions and structures that comprise the Common Property Sheet User Interface (CPSUI). The CPSUI executable is a system-supplied module called compstui.dll. If you implement a printer user interface module for your printing device, you must ensure that this module can communicate with the CPSUI. CPSUI function and structure declarations are found in compstui.h, which is provided by this DDK.

Reference material for the printer user interface are found in Printer User Interface Functions and Structures.


Common Property Sheet User Interface Functions

This section describes CPSUI-implemented functions and caller-implemented functions that the CPSUI calls.


CommonPropertySheetUI

LONG CommonPropertySheetUI(

HWND hWndOwner,

PFNPROPSHEETUI pfnPropSheetUI,

LPARAM lParam,

LPDWORD pResult

);

CommonPropertySheetUI manages and displays a property sheet.

Parameters

hWndOwner
Window handle for the owner of the property sheet. This parameter can be null.
pfnPropSheetUI
Pointer to a callback function of type PFNPROPSHEETUI.
lParam
Specifies the parameter that is passed to the function to which pfnPropSheetUI points when it is called by the CPSUI.
pResult
Pointer to a DWORD that receives the final result of the function to which pfnPropSheetUI points when it is called. Specifically, the Result field of the PROPSHEETUI_INFO structure is copied into pResult if an error is not returned; otherwise, pResult is set to null.

Return Value

CommonPropertySheetUI returns one of the following values:
Return Value | Description
CPSUI_CANCEL | The property sheet was not displayed because there were no pages.
CPSUI_OK | The function succeeded.
CPSUI_RESTARTWINDOWS | The function succeeded. The property sheet was displayed, and the end-user changed a selection that requires the login session to be restarted in order for the change to take effect.
CPSUI_REBOOTSYSTEM | The function succeeded. The property sheet was displayed, and the end-user changed a selection that requires the system to be rebooted in order for the change to take effect.
ERR_CPSUI_Xxx | The property sheet was not displayed due to the error specified by the return error code. These error code values are always less than zero.

Comments

CommonPropertySheetUI is the main entry point for the CPSUI. An application calls this function to have the CPSUI manage and display a property sheet.

The function to which pfnPropSheetUI points is a caller-defined function in which the caller requests the CPSUI to add pages to the property sheet. The CPSUI calls this function as follows:

PROPSHEETUI_INFO   PSUIInfo;
pfnPropSheetUI(&PSUIInfo, lParam);

When an end-user closes the property sheet by selecting OK or Cancel, CommonPropertySheetUI returns the appropriate CPSUI_Xxx value to the original caller.

If the function to which pfnPropSheetUI points returns a LONG value greater than zero, the CPSUI displays the property sheet dialog boxes. When an end-user closes the property sheet by selecting OK or Cancel, CommonPropertySheetUI returns one of CPSUI_OK, CPSUI_RESTARTWINDOWS, or CPSUI_REBOOTSYSTEM. If the callback returns a LONG value that is equal to or less than zero, there were no pages on the property sheet or an error was encountered. In this situation, the CPSUI did not display the property sheet and CommonPropertySheetUI returns CPSUI_CANCEL or ERR_CPSUI_Xxx.

When the last call to pfnPropSheetUI() returns, the value in the Result field of PROPSHEETUI_INFO is copied into the DWORD to which pResult points. The pResult parameter is set only when CommonPropertySheetUI does not return ERR_CPSUI_Xxx. CommonPropertySheetUI does not return the last DWORD result of pfnPropSheet() if pResult is null.

See Also

PFNPROPSHEETUI


_CPSUICALLBACK

LONG (APIENTRY *_CPSUICALLBACK)(
PCPSUICBPARAM pComPropSheetUICBParam
);

_CPSUICALLBACK is a caller-defined function that is called by the CPSUI to allow the caller to make changes to an option item.

Parameters

pComPropSheetUICBParam
Pointer to a CPSUICBPARAM structure containing the reason for calling _CPSUICALLBACK. This structure also contains the parameters needed by the caller to make changes to an option item and return an action to the CPSUI to redisplay the affected dialog box.

Return Value

_CPSUICALLBACK returns one of the following values to the CPSUI:
Value | Definition
CPSUICB_ACTION_NONE | No action is required by the CPSUI for all option items.
CPSUICB_ACTION_ OPTIF_CHANGED | The CPSUI should examine the OPTIF_CHANGED flag in the pOptItem field of the structure to which pComPropSheetUICBParam points. If OPTIF_CHANGED is set, the CPSUI needs to redisplay the item because the item’s Flags field or selection has changed.
CPSUICB_ACTION_ REINIT_ITEMS | The CPSUI should examine the OPTIF_CHANGED flag in the pOptItem field of the structure to which pComPropSheetUICBParam points. If OPTIF_CHANGED is set, the CPSUI needs to reinitialize the item in the dialog box control because its associated OPTTYPE structure or OPTPARAM’s Flag and/or pData fields have changed.
CPSUICB_ACTION_ NO_APPLY_EXIT | Notifies the CPSUI that, due to constraints on the OPTITEM to which pOptItem points, _CPSUICALLBACK displayed a dialog box message and/or had the user correct or confirm something in the OPTITEM. This return value is valid only when _CPSUICALLBACK is called with the Reason field of the pComPropSheetUICBParam parameter set to CPSUICB_REASON_APPLYNOW.
CPSUICB_ACTION_ ITEMS_APPLIED | Notifies the CPSUI that the callback has recorded the option(s) that were changed by the user. The CPSUI clears the OPTIF_CHANGEONCE flag of the OPTITEM to which pOptItem points, and saves the new default(s) for future Undo operation(s) that the user might perform. This action is returned only in response to a Reason of CPSUICB_REASON_APPLYNOW.

Comments

_CPSUICALLBACK is a caller-defined function. The caller sets the pfnCallBack member of the COMPROPSHEETUI structure to point to its _CPSUICALLBACK function, and passes the structure to PFNCOMPROPSHEET with the CPSFUNC_ADD_PCOMPROPSHEETUI function index.

The CPSUI calls _CPSUICALLBACK so that the CPSUI-caller can respond to option item change(s) made by the user. This call occurs when the user selects OK or Apply on the property sheet page after changing one or more options. _CPSUICALLBACK should record the results of the change and return the appropriate action to the CPSUI. The CPSUI does not call _CPSUICALLBACK if no changes are made or if the user selects Cancel on the property sheet page.

See Also

COMPROPSHEETUI, CPSUICBPARAM, PFNCOMPROPSHEET


GetCPSUIUserData

DWORD APIENTRY GetCPSUIUserData(

HWND hDlg

);

GetCPSUIUserData retrieves information associated with the specified property sheet dialog box.

Parameters

hDlg
Handle to the property sheet dialog box to be queried.

Return Value

GetCPSUIUserData returns the 32-bit value associated with hDlg upon success. If the function fails, the return value is zero.

Comments

The caller’s dialog box subclass procedure, defined as the DlgProc member of the DLGPAGE structure, can call GetCPSUIUserData to retrieve user data stored by the caller in a previous call to SetCPSUIUserData.


PFNCOMPROPSHEET

DWORD ( CALLBACK *PFNCOMPROPSHEET)(

HANDLE hComPropSheet,

UINT Function,

LPARAM lParam1,

LPARAM lParam2

);

PFNCOMPROPSHEET is a CPSUI-defined callback that is called by the caller’s PFNPROPSHEETUI callback to perform an action on a property sheet.

Parameters

hComPropSheet
Handle to the CPSUI object on which action is to be performed. This handle is created by the CPSUI, and must be the handle passed from the CPSUI when it called the caller-supplied PFNPROPSHEETUI callback.
Function
Specifies the action to be performed by the CPSUI’s PFNCOMPROPSHEET callback. This value can be one of the CPSFUNC_Xxx definitions. See the Comments section for details.
lParam1
Data required by the CPSUI to process the specified action. The CPSUI’s interpretation of lParam1 depends on Function. See the Comments section for details.
lParam2
Data required by the CPSUI to process the specified action. The CPSUI’s interpretation of lParam2 depends on Function. See the Comments section for details.

Return Value

The value returned by PFNCOMPROPSHEET depends on Function. See the Comments section for details.

Comments

The caller’s PFNPROPSHEETUI calls PFNCOMPROPSHEET to perform an action on the property sheet page(s) identified by hComPropSheet. The action is specified by Function.

The lParam1 and lParam2 parameters and PFNCOMPROPSHEET’s return value all depend on the value of Function, and are summarized in the following topics.

CPSFUNC_ADD_HPROPSHEETPAGE

Adds a page to the CPSUI object identified by hComPropSheet using a handle to a PROPSHEETPAGE structure. The caller obtains a PROPSHEETPAGE handle by calling the Win32 CreatePropertySheetPage function.

lParam1 is a handle to the PROPSHEETPAGE structure to be used by CPSUI to add the page.

lParam2 is not used, and must be set to zero.

PFNCOMPROPSHEET returns a handle to the newly added page if it succeeds; otherwise, it returns null.

CPSFUNC_ADD_PROPSHEETPAGE

Adds a page to the CPSUI object identified by hComPropSheet using a PROPSHEETPAGE structure.

lParam1 is a pointer to a PROPSHEETPAGE structure.

lParam2 is not used and must be set to zero.

PFNCOMPROPSHEET returns a handle to the newly added page if it succeeds; it returns null if it fails.

CPSFUNC_ADD_PCOMPROPSHEETUI

Adds one or more pages to the CPSUI object identified by hComPropSheet using the COMPROPSHEETUI data structure.

lParam1 is a pointer to the COMPROPSHEETUI structure to be used by CPSUI to add the page(s).

lParam2 is a pointer to a 32-bit location in which the CPSUI returns the number of pages added when it succeeds, or ERR_CPSUI_Xxx if it fails.

PFNCOMPROPSHEET returns a handle to the newly added page(s) if it succeeds; otherwise, it returns null.

CPSFUNC_ADD_PFNPROPSHEETUI

Adds the function to which lParam1 points to the CPSUI object identified by hComPropSheet. The CPSUI calls this function to add page(s) to the property sheet as follows:
PROPSHEETUI_INFO            PSUIInfo;
PSUIInfo.cbSize             = sizeof(PROPSHEETUI_INFO);
PSUIInfo.Version            = PROPSHEETUI_INFO_VERSION;
PSUIInfo.Flags              = (Ansi) ? 0 : PSUIINFO_UNICODE;
PSUIInfo.Reason             = PROPSHEETUI_REASON_INIT;
PSUIInfo.hComPropSheet	      = hComPropSheet;
PSUIInfo.pfnComPropSheet    = ComPropSheetUICallBack;
PSUIInfo.lParamInit         = lParam2;
PSUIInfo.UserData           = 0;
PSUIInfo.Result             = 0;
((PFNPROPSHEETUI)lParam1)(&PSUIInfo, lParam2);

lParam1 is a pointer to a PFNPROPSHEETUI callback that is called to add the property sheet page(s). This function’s implementation must use the hComPropSheet parameter as its handle when it calls back to the CPSUI’s PFNCOMPROPSHEET callback to add/delete pages.

lParam2 is a 32-bit data field that is passed to PFNPROPSHEETUI as the lParam parameter.

PFNCOMPROPSHEET returns a handle to the newly added property sheet page(s) if it succeeds; it returns null if it fails or no pages were added.

CPSFUNC_DELETE_HCOMPROPSHEET

Deletes the child page(s) from the parent CPSUI object using the passed child handle. The parent is identified by hComPropSheet.

lParam1 is a handle to the child page(s) to be deleted. This handle must previously have been returned from an executed CPSFUNC_ADD_Xxx or CPSFUNC_INSERT_Xxx function.

lParam2 is not used and must be set to zero.

PFNCOMPROPSHEET returns a value greater than zero if it succeeds; it returns a value less than or equal to zero if it fails.

CPSFUNC_SET_HSTARTPAGE

Marks the page(s) identified by hComPropSheet as the initial page(s) of the property sheet. The caller can call PFNCOMPROPSHEET with this function index both before and after the property sheet is displayed. If it is called after the initial display, the CPSUI switches the top (displayed) property sheet page with the first page of the marked page(s) on top.

lParam1 is a handle to the page(s) to be marked as those page(s) that the CPSUI displays first on the property sheet when it displays the property sheet dialog boxes. This handle must have been returned from an executed CPSFUNC_ADD_Xxx or CPSFUNC_INSERT_Xxx function.

lParam2 is an index to the exact page to be displayed first. If lParam1 is a parent handle, lParam2 is a zero-based index to the child page that should be marked as first. This parameter can also be one of the following special indexes if the handle identified by lParam1 was added by the CPSFUNC_ADD_PCOMPROPSHEETUI function.
Value | Description
SSP_TVPAGE | Mark the specified page as the treeview page.
SSP_STDPAGE | Mark the specified page as the Page Setup document property sheet page.

The SSP_Xxx values apply only to property sheet requests that originated in a call to the Win32 DocumentPropertySheet function.

PFNCOMPROPSHEET returns a value greater than zero if it succeeds; it returns a value less than or equal to zero if it fails.

CPSFUNC_GET_PAGECOUNT

Returns the number of pages that belong to the CPSUI object identified by hComPropSheet.

lParam1 is not used and must be set to zero.

lParam2 is not used and must be set to zero.

PFNCOMPROPSHEET returns the page count if it succeeds; it returns zero if it fails. There are never zero pages.

CPSFUNC_GET_HPSUIPAGES

Returns an array of handles to PROPSHEETPAGE structures. These handles identify the children that belong to the parent CPSUI object identified by hComPropSheet. To obtain all of the children’s handles, the caller should call PFNCOMPROPSHEET as follows:
if (((cPage = PFNCOMPROPSHEET(hComPropSheet,
CPSFUNC_GET_PAGECOUNT, 0L, 0L)) > 0) &&
((phPage = ALLOCMEM(cPage * sizeof(HANDLE))) != NULL) {
PFNCOMPROPSHEET(hComPropSheet, CPSFUNC_GET_HPSUIPAGES,
        (LPARAM)phPage, (LPARAM)cPage);
}

lParam1 is a pointer to an array in which the CPSUI writes the children’s handles.

lParam2 specifies the number of handles that can be stored in the array to which lParam1 points.

PFNCOMPROPSHEET returns the number of handles stored in lParam1 if it succeeds; it returns a value less than or equal to zero if it fails.

CPSFUNC_SET_RESULT

Propagates up the result of the property sheet page to the parent that added it through a CPSFUNC_ADD_Xxx function.

lParam1 is a handle to the page that is setting the result. If lParam1 is null, the CPSUI uses hComPropSheet.

lParam2 specifies the 32-bit DWORD result to be set.

Upon success, PFNCOMPROPSHEET returns the number of parents for which the result was set, or returns zero if the page identified by lParam1 has no parent. It returns a value less than zero if it fails because lParam1 is an invalid handle. The CPSUI propagates the result to the root handle.

CPSFUNC_LOAD_CPSUI_STRING

Loads the CPSUI resource string identified by the resource ID in the high word of lParam2.

lParam1 is a pointer to a null-terminated string of type LPTSTR in which the CPSUI loads the resource string from the CPSUI’s DLL.

lParam2 specifies the following two values.
WORD | Description
LOWORD(lParam2) | Number of characters, including the null terminator, in the string to which lParam1 points.
HIWORD(lParam2) | Specifies the resource ID of the string to be loaded. This value is one of the predefined IDS_CPSUI_Xxx string resource ID definitions.

PFNCOMPROPSHEET returns the number of characters stored in the string to which lParam1 points upon success; this does not include the null terminator. It returns zero when an invalid IDS_CPSUI_Xxx string resource ID is passed in the high word of lParam2. It returns a value less than zero when lParam1 is null or when the low word of lParam2 is zero.

CPSFUNC_LOAD_CPSUI_ICON

Loads the CPSUI resource icon identified by lParam1.

lParam1 specifies the resource ID of the icon to be loaded. This value is one of the predefined IDI_CPSUI_Xxx icon resource ID definitions.

lParam2 specifies the following two values.
WORD | Description
LOWORD(lParam2) | Specifies the icon width in pixels. If this value is zero, the CPSUI uses SM_CXICON.
HIWORD(lParam2) | Specifies the icon height in pixels. If this value is zero, the CPSUI uses SM_CYICON.

PFNCOMPROPSHEET returns a handle to the icon upon success, and null when it fails. The caller must call the Win32 DestroyIcon function when it no longer needs the icon.

CPSFUNC_GET_PFNPROPSHEETUI_ICON

Returns the icon handle that represents the children page(s) specified by lParam1.

lParam1 is a handle to the page(s) whose icon handle is to be queried. This handle must have been returned from a call to PFNCOMPROPSHEET with a CPSFUNC_ADD_PFNPROPSHEETUI function.

lParam2 specifies the following two values.
WORD | Description
LOWORD(lParam2) | Specifies the icon width in pixels. If this value is zero, the CPSUI uses SM_CXICON.
HIWORD(lParam2) | Specifies the icon height in pixels. If this value is zero, the CPSUI uses SM_CYICON.

PFNCOMPROPSHEET returns a handle to the icon when it succeeds; it returns a null handle when it fails. The caller is responsible for calling the Win32 DestroyIcon function when it no longer needs the icon identified by the returned icon handle.

CPSFUNC_INSERT_PSUIPAGE

Inserts property sheet page(s) at the position specified in lParam2. The CPSUI object identified by hComPropSheet must be the parent handle.

lParam1 specifies a handle to the CPSUI object by which the page(s) are inserted. This handle must have been returned from a call to PFNCOMPROPSHEET with a CPSFUNC_ADD_Xxx function, or with a CPSFUNC_INSERT_PSUIPAGE.

lParam2 is a pointer to an INSERTPSUIPAGE_INFO structure.

PFNCOMPROPSHEET returns a handle to the newly added property sheet page(s) upon success; it returns null when it fails.

CPSFUNC_SET_PSUIPAGE_TITLE

Sets the tab title for a property sheet page.

lParam1 is a handle to the property sheet page whose title is to be set. This handle must have been returned from a call to PFNCOMPROPSHEET with a function of CPSFUNC_ADD_PROPSHEETPAGE, CPSFUNC_ADD_HPROPSHEETPAGE, or CPSFUNC_INSERT_PSUIPAGE with the Type field of the INSERTPSUIPAGE_INFO structure set to PSUIPAGEINSERT_PROPSHEETPAGE or PSUIPAGEINSERT_HPROPSHEETPAGE.

lParam2 is a pointer to a null-terminated string that specifies the new title.

PFNCOMPROPSHEET returns a value greater than zero upon success; it returns a value less than or equal to zero if it fails. This function returns zero if the property sheet page is not currently displayed.

CPSFUNC_SET_PSUIPAGE_ICON

Sets a new icon for display on a property sheet page’s tab.

lParam1 specifies a handle to the page with which the icon is to be associated. This handle must previously have been returned from a call to PFNCOMPROPSHEET with a function of CPSFUNC_ADD_PROPSHEETPAGE, CPSFUNC_ADD_HPROPSHEETPAGE, or CPSFUNC_INSERT_PSUIPAGE with the Type field of the INSERTPSUIPAGE_INFO structure set to PSUIPAGEINSERT_PROPSHEETPAGE or PSUIPAGEINSERT_HPROPSHEETPAGE.

lParam2 is a handle to the icon. The CPSUI stretches or shinks all icons to be of size 16x16 pixels.

PFNCOMPROPSHEET returns a value greater than zero upon success; it returns a value less than or equal to zero if it fails. This function returns zero if the property sheet page is not currently displayed.

If the icon was originally created through a call to the Win32 CreateIcon function, the caller is responsible for destroying the icon by calling the Win32 DestroyIcon function. The caller need not destroy the icon if it was created through a call to the Win32 LoadIcon function.


PFNPROPSHEETUI

LONG (FAR *PFNPROPSHEETUI)(

PPROPSHEETUI_INFO pPSUIInfo,

LPARAM lParam

);

PFNPROPSHEETUI is a caller-defined function that the CPSUI calls to enable its caller to add or delete pages from a property sheet.

Parameters

pPSUIInfo
Pointer to a PROPSHEETUI_INFO structure. The CPSUI allocates and initializes this structure with the information needed by PFNPROPSHEETUI to add property sheet pages. This parameter can be null.
lParam
If pPSUIInfo is null, the caller of PFNPROPSHEETUI is not the CPSUI, but is the spooler or Print Folder instead. In this situation, lParam is a parameter whose type is known the caller. If pPSUIInfo is non-null, the CPSUI has called PFNPROPSHEETUI. In this situation, the value of lParam depends on the Reason member of the PROPSHEETUI_INFO structure shown in the following table.

Reason

Description

PROPSHEETUI_REASON_INIT

The lParam parameter is either the unmodified lParam parameter passed to CommonPropertySheetUI, or the lParam2 parameter passed to PFNCOMPROPSHEET with a Function of CPSFUNC_ADD_PFNPROPSHEETUI.

The CPSUI has copied this instance of lParam to the lParamInit field of the PROPSHEETUI_INFO structure. The lParamInit field is passed to all subsequent calls to PFNPROPSHEETUI without modification.

The lParam parameter must not reside on the calling function’s stack.

PROPSHEETUI_REASON_
GET_INFO_HEADER

The lParam parameter is a pointer to a PROPSHEETUI_INFO_HEADER structure. PFNPROPSHEETUI must fill in the fields of this structure for the CPSUI.

PROPSHEETUI_REASON_
DESTROY

The lParam parameter is zero when the destroy request occurs because the caller has called the CPSUI’s PFNCOMPROPSHEET callback with Function set to CPSFUNC_DELETE_HCOMPROPSHEET, or when the CPSUI’s callback failed a caller’s CPSFUNC_ADD_Xxx request.

The lParam parameter is nonzero when the user is exiting the property sheet.

PROPSHEETUI_REASON_
SET_RESULT

The lParam parameter is a pointer to a SETRESULT_INFO structure in which the CPSUI has set the following fields:

hSetResult contains the handle to those page(s) added to the sheet through a call to PFNCOMPROPSHEET with a CPSFUNC_ADD_Xxx function index;

Result contains the result obtained when hSetResult was generated. PFNPROPSHEETUI should save this result in the Result field of the structure to which pPSUIInfo points. When PFNPROPSHEETUI’s return value is greater than zero, the CPSUI propagates the Result field of the structure to which pPSUIInfo points to its parent.

PROPSHEETUI_REASON_
GET_ICON

The lParam parameter is a pointer to a PROPSHEETUI_GETICON_INFO structure in which the CPSUI has set the following fields:

cxIcon is the icon’s width, in pixels;

cyIcon is the icon’s height, in pixels;

hIcon is null. PFNPROPSHEETUI should create an icon and return its handle, or, if it does not create an icon, should return null.

Return Value

The return value of PFNPROPSHEETUI depends on the Reason field of the PROPSHEETUI_INFO structure shown in the following table.
Reason | Return Value
PROPSHEETUI_REASON_INIT, PROPSHEETUI_REASON_GET_INFO_HEADER, PROPSHEETUI_REASON_DESTROY | Returns greater than zero to indicate success, and less than or equal to zero to indicate failure.
PROPSHEETUI_REASON_SET_RESULT | Returns greater than zero to indicate that the Result field in the structure to which pPSUIInfo points should be set to its parent; returns less than or equal to zero to indicate that Result need not be set to its parent.
PROPSHEETUI_REASON_GET_ICON | Returns greater than zero to indicate success, zero to indicate that no icon was available, and less than zero to indicate failure.

Comments

PFNPROPSHEETUI is a pseudoname; the actual name of this function can be decided upon by the developer. PFNPROPSHEETUI must be implemented.

PFNPROPSHEETUI is a caller-defined function in which the caller can request the CPSUI to add pages to the property sheet. The CPSUI calls this function as follows:

PROPSHEETUI_INFO   PSUIInfo;
PFNPROPSHEETUI(&PSUIInfo, lParam);

The CPSUI stores the reason for calling PFNPROPSHEETUI in the Reason field of the PROPSHEETUI_INFO structure to which pPSUIInfo points. PFNPROPSHEETUI should be implemented as a re-entrant, message-based function, as the CPSUI calls it several times over the lifetime of the property sheet.

The CPSUI calls PFNPROPSHEETUI for the first time with Reason always set to PROPSHEETUI_REASON_INIT. PFNPROPSHEETUI should initialize itself and then call the CPSUI to add new pages to the property sheet root identified by the hComPropSheet member of the PROPSHEETUI_INFO structure. PFNPROPSHEETUI must call back to the CPSUI to actually add (or delete) pages from the property sheet. The CPSUI provides a pointer to its callback in the pfnComPropSheet field. The CPSUI callback returns a value greater than zero to indicate success, and a value less than or equal to zero to indicate failure.

If the first call to PFNPROPSHEETUI does not return successfully, the CPSUI calls PFNPROPSHEETUI with Reason set to PROPSHEETUI_REASON_DESTROY so that PFNPROPSHEETUI can uninitialize itself.

The CPSUI calls PFNPROPSHEETUI with Reason set to PROPSHEETUI_REASON_GET_INFO_HEADER to obtain high-level property sheet information to use when it displays the property sheet dialog boxes.

See Also

CommonPropertySheetUI, PFNCOMPROPSHEET, PROPSHEETUI_INFO, PROPSHEETUI_INFO_HEADER


SetCPSUIUserData

BOOL APIENTRY SetCPSUIUserData(

HWND hDlg,

DWORD CPSUIUserData

);

SetCPSUIUserData associates user-defined data with the specified property sheet dialog box.

Parameters

hDlg
Handle to the property sheet dialog box with which CPSUIUserData is to be stored.
CPSUIUserData
Specifies the 32-bit value to be stored with hDlg.

Comments

The caller’s dialog box subclass procedure, defined as the DlgProc member of the DLGPAGE structure, can call SetCPSUIUserData to store a 32-bit value with the dialog box.

See Also

GetCPSUIUserData


Common Property Sheet User Interface Structures

This section describes the structures associated with the CPSUI.


COMPROPSHEETUI

typedef struct _COMPROPSHEETUI {
    WORD                  cbSize;
    WORD                  Flags;
    HINSTANCE             hInstCaller;
    LPTSTR                pCallerName;
    DWORD                 UserData;
    LPTSTR                pHelpFile;
    _CPSUICALLBACK        pfnCallBack;
    POPTITEM              pOptItem;
    PDLGPAGE              pDlgPage;
    WORD                  cOptItem;
    WORD                  cDlgPage;
    DWORD                 IconID;
    LPTSTR                pOptItemName;
    WORD                  CallerVersion;
    WORD                  OptItemVersion;
    DWORD                 dwReserved[4];
} COMPROPSHEETUI, *PCOMPROPSHEETUI;

The COMPROPSHEETUI data structure contains the caller-specific data required by the CPSUI to construct and display the property sheet pages.

Members

cbSize
Size in bytes of this structure.
Flags
Bitmask in which the caller can set any combination of the following flags.

Value

Description

CPSUIF_UPDATE_PERMISSION

All valid items to which pOptItem points are changeable by the user.

CPSUIF_ICONID_AS_HICON

The CPSUI should interpret IconID as a handle to an icon rather than a resource ID.

CPSUIF_ABOUT_CALLBACK

If the end-user selects the About… button in the treeview tab, the CPSUI should call the callback to which pfnCallBack points with the Reason field of the pComPropSheetUICBParam parameter set to CPSUICB_REASON_ABOUT.

hInstCaller
Handle to the caller’s instance of itself. The CPSUI uses this handle to load the caller’s icon and other resources.
pCallerName
Pointer to a null-terminated string that specifies the name of the caller. This string is typically a driver’s name, such as “PostScript Driver”.
UserData
A 32-bit value that is defined and used by the caller. When the CPSUI calls pfnCallBack, it passes this member as the UserData field of the pComPropSheetUICBParam parameter. The CPSUI does not not modify this value.
pHelpFile
Pointer to a null-terminated string that specifies a standard Microsoft help file. This string must contain the complete path to the file. The HelpIndex field of the OPTITEM structure specifies the index location into the help file for the context string associated with the item.
pfnCallBack
Pointer to a _CPSUICALLBACK callback that is defined by the caller. The CPSUI calls this callback for various CPSUICB_REASON_Xxx reasons only when an OPTITEM’s OPTIF_CALLBACK bit is set.
pOptItem
Pointer to an array of OPTITEM structures that describe the option items to be displayed by the CPSUI.
pDlgPage
Pointer to an array of DLGPAGE structures that describe the characteristics of each property sheet page. The CPSUI has defined the following standard dialog boxes for use by the caller.

DLGPAGE

Description

CPSUI_PDLGPAGE_DOCPROP

The property sheet has two standard pages, whose tabs are Page Setup and Advanced. The Advanced page is a treeview.

CPSUI_PDLGPAGE_ADVDOCPROP

The property sheet has one treeview page whose tab reads Advanced.

CPSUI_PDLGPAGE_PRINTERPROP

The property sheet has one treeview page whose tab reads Device Settings. This page is a CPSUI-defined standard printer property sheet.

CPSUI_PDLGPAGE_TREEVIEWONLY

The property sheet has one treeview page.

cOptItem
Specifies the number of OPTITEM structures to which pOptItem points.
cDlgPage
Specifies the number of DLGPAGE structures to which pDlgPage points. The CPSUI ignores this field if pDlgPage specifies a standard CPSUI_PDLGPAGE_Xxx dialog box.
IconID
Identifies the icon to be displayed on the title bar of the property sheet page. This field can be a standard IDI_CPSUI_Xxx icon ID, a caller’s icon resource ID, or, if the CPSUIF_ICONID_AS_HICON bit is set in Flags, a handle to a caller-defined icon. If IconID is zero, the property sheet’s title bar does not have an icon associated with it.
pOptItemName
Pointer to a null-terminated string that identifies the root-level option item. This field is typically the device name, such as “HP 4si”.
CallerVersion
Specifies the caller’s version number. The high byte identifies the major version; the low byte is the minor version. For example, a CallerVersion value of 0x310 specifies a caller version number of 3.16.
OptItemVersion
Specifies the version number for the root-level option item. The high byte identifies the major version; the low byte is the minor version. For example, an OptItemVersion value of 0x3ff specifies a caller version number of 3.255.
dwReserved[4]
Reserved. These fields must be set to zero.

Comments

A COMPROPSHEETUI structure is initialized by the caller and passed to the CPSUI’s PFNCOMPROPSHEET callback as lParam1 when Function is CPSFUNC_ADD_PCOMPROPSHEETUI.

If the CPSUIF_ABOUT_CALLBACK bit is set in the Flags field, the callback to which pfnCallBack points must handle a user request to see the About dialog box. If this bit is not set, the CPSUI calls the Win32 ShellAbout function with the pCallerName and CallerVersion fields formatted as the szApp parameter, and pOptItemName and OptItemVersion formatted as the szOtherStuff parameter.

The CPSUI automatically modifies the DlgPageIdx fields of each OPTITEM when the user specifies CPSUI_PDLGPAGE_Xxx in pDlgPage.


CPSUICBPARAM

typedef struct _CPSUICBPARAM {
    WORD              cbSize;
    WORD              Reason;
    HWND              hDlg;
    POPTITEM          pOptItem;
    WORD              cOptItem;
    WORD              Flags;
    POPTITEM          pCurItem;
    union {
        LONG          OldSel;
        LPTSTR        pOldSel;
    };
    DWORD             UserData;
    DWORD             Result;
} CPSUICBPARAM, *PCPSUICBPARAM;

The CPSUICBPARAM structure contains the reason for the CPSUI’s call to _CPSUICALLBACK, and the parameters needed by _CPSUICALLBACK to make changes to an option item and return an action to the CPSUI. The CPSUI can then redisplay the treeview or Page Setup data.

Members

cbSize
Size in bytes of this structure.
Reason
Specifies the reason that the CPSUI is calling back to the caller. This member can be one of the following values.

Reason

Description

CPSUICB_REASON_
SEL_CHANGED

The user has made a change to the item to which pCurItem points. If the DMPubID field of the affected OPTITEM is DMPUB_COPIES_COLLATE, the CPSUI enables/disables the extended check box without calling back to _CPSUICALLBACK.

CPSUICB_REASON_
PUSH BUTTON

The user has pushed the push button, and the push button item’s OPTPARAM Style field is set to PUSH BUTTON_TYPE_CALLBACK.

CPSUICB_REASON_
ECB_CHANGED

The user has changed state in the extended check box described by the structure to which pCurItem->pExtChkBox points.

CPSUICB_REASON_
DLGPROC

The user has pushed the push button, and the push button item’s OPTPARAM Style field is set to PUSH BUTTON_TYPE_DLGPROC.

CPSUICB_REASON_
EXTPUSH

The user has pushed the extended push button described by the structure to which pCurItem->pExtPush points.

CPSUICB_REASON_
APPLYNOW

The user has pushed the Apply Now push button. The CPSUI sets pCurItem to point to the item to which pOptItem points. It also sets OldSel to -1 to indicate that all valid changed items should be applied now.

CPSUICB_REASON_
OPTITEM_SETFOCUS

An option item has keyboard focus. The CPSUI calls the callback so that it can examine the item.

CPSUICB_REASON_
ITEMS_REVERTED

After changing option(s), the user has pushed the Undo push button to undo the changes.

CPSUICB_REASON_
ABOUT

The user has pushed the About Xxx push button on the treeview page, and the CPSUIF_ABOUT_CALLBACK flag is set, indicating that the caller has hooked the About page to display their proprietary About information. _CPSUICALLBACK sets pCurItem to point to the item to which pOptItem points, and sets pOldSel to point to the original copy of the COMPROPSHEETUI structure passed to the CPSUI.

hDlg
Handle to the dialog box that is currently active for the _CPSUICALLBACK function.
pOptItem
Points to an array of OPTITEM structures that describe the treeview items to be displayed by the CPSUI. This is the same pointer sent by the caller to the CPSUI’s pfnComPropSheet callback in the pOptItem field of the COMPROPSHEETUI structure.
cOptItem
Number of OPTITEM structures to which pOptItem points. This is the same value sent by the caller to the CPSUI’s pfnComPropSheet callback in the cOptItem field of the COMPROPSHEETUI structure.
Flags
Specifies the flags passed by the caller to the CPSUI’s pfnComPropSheet callback in the Flags field of the COMPROPSHEETUI structure.
pCurItem
Pointer to the OPTITEM structure in the array to which pOptItem points that describes the item for which the callback was generated.
OldSel
The Sel field of the OPTITEM structure that was current before the user made a change. This field is valid only when the Reason callback is CPSUICB_REASON_SEL_CHANGED. The callback can use this field to check the change against the previous item selection.
pOldSel
Pointer to a null-terminated string that describes the option item’s last selection before a change was made by the user; that is, pOldSel is equal to the previously current OPTITEM’s pSel field. The pOldSel member is valid only when the Reason callback is CPSUICB_REASON_SEL_CHANGED. The callback can use this field to check the change against the previous item selection.
UserData
Specifies the 32-bit value provided by the original caller to the CPSUI’s pfnComPropSheet callback in the UserData field of the COMPROPSHEETUI structure.
Result
Specifies the result of the _CPSUICALLBACK callback. The default Result is CPSUI_OK, and is set by the CPSUI. The _CPSUICALLBACK function can alter this result before returning to the CPSUI. The Result field need only be set when Reason is CPSUICB_REASON_APPLYNOW; the CPSUI ignores Result for all other reasons.

Comments

When Reason is CPSUICB_REASON_APPLYNOW, _CPSUICALLBACK must set the requested result in the Result field when it returns a value other than CPSUICB_ACTION_NO_APPLY_EXIT. The CPSUI sends Result to the parent of the current property sheet page.


DLGPAGE

typedef struct _DLGPAGE {
    WORD              cbSize;
    WORD              Flags;
    DLGPROC           DlgProc;
    LPTSTR            pTabName;
    DWORD             IconID;
    union {
        WORD          DlgTemplateID;
        HANDLE        hDlgTemplate;
    };
} DLGPAGE, *PDLGPAGE;

The DLGPAGE structure describes the non-treeview property sheet page characteristics.

Members

cbSize
Size in bytes of this structure.
Flags
Bitmask in which any combination of the following flags can be set.

Flag

Description

DPF_ICONID_AS_HICON

IconID is a handle to an icon rather than an icon resource ID.

DPF_USE_HDLGTEMPLATE

The CPSUI should use the hDlgTemplate union member.

DlgProc
Pointer to a caller-defined DLGPROC procedure that subclasses the page’s dialog box processing.
pTabName
Pointer to the null-terminated string to be displayed on the dialog box tab.
IconID
Specifies the icon to display on the dialog box tab. This member can be a standard IDI_CPSUI_Xxx icon ID, a caller-defined icon resource ID, or a handle to a caller-defined icon if DPF_ICONID_AS_HICON is set in the Flags field. A value of zero indicates that no icon should be displayed.
DlgTemplateID
Specifies the ID of the template to use for the property sheet page dialog box. The DP_STD_Xxx templates are 252x216 dialog box units in size. This field can be user-defined, or can have one of the following values.

Value

Description

DP_STD_TREEVIEWPAGE

This page is a standard treeview page provided by the CPSUI. The CPSUI treeview page displays all valid OPTITEMs as a tree, and allows the user to modify valid selectable OPTITEMs on the page.

DP_STD_DOCPROPPAGE

This page is the standard Document Properties page provided by the CPSUI.

User-defined values must not conflict with system-supplied ones.

hDlgTemplate
Handle to the DLGTEMPLATE structure that describes the property sheet page dialog box. The CPSUI uses this handle to display the dialog box.

Comments

If DlgProc is not null and DlgTemplateID is not DP_STD_Xxx, the CPSUI processes messages as shown in the following table.
Message | Description
WM_INITDIALOG | The CPSUI initializes the dialog box and then calls the function to which DlgProc points with the WM_INITDIALOG message. The DlgProc function should return the behavior expected for this message; see the Win32 SDK for details. The lParam member of WM_INITDIALOG is a pointer to a Win32 PROPSHEETPAGE structure. The lParam field of the PROPSHEETPAGE structure is the UserData field of the COMPROPSHEETUI structure. To access the PSPINFO structure associated with this property sheet page, the caller’s DlgProc function should use the CPSUI-defined PPSPINFO_FROM_WM_INITDIALOG_LPARAM(lParam) macro, where lParam is the parameter passed to the WM_INITDIALOG message. The caller’s subclass function should save the pointer to the PSPINFO structure for later use. It must not modify the contents of the PSPINFO structure.
All Other Dialog Messages | If the value returned by the DlgProc function is non-zero, the CPSUI assumes that the DlgProc function has processed the message entirely. If the value returned is zero, the CPSUI processes the message.

Each OPTITEM corresponds to one input control with one extended check box or extended push button. Because the CPSUI automatically disables and removes from the property sheet those items with the OPTIF_HIDE flag set, and dynamically moves the remaining controls, the following tips for designing the dialog box controls should be followed:

  • Each item should have one input control with an optional extended check box or extended push button, one icon control, and other static controls.

  • Each item should occupy the entire horizontal space of the property sheet page. Items should not overlay each other in the vertical direction.

  • For items whose option type is TVOT_2STATES or TVOT_3STATES, if the option buttons are arranged from left to right in state order, the option buttons and icons should be aligned on the X axis. If the option buttons are arranged from top to bottom in state order, the option buttons and icons should be aligned on the Y axis.

  • If option buttons and icons are arranged top to bottom in order and there are other controls obscured in Y direction, the CPSUI rearranges the option buttons but does not remove any white space in the Y direction.

  • If multiple items share one group box, the group box must belong to the first OPTITEM, which is the topmost item in the group box. The caller must design the group box to be large enough to contain all items associated with it.


EXTCHKBOX

typedef struct _EXTCHKBOX {
    WORD             cbSize;
    WORD             Flags;
    LPTSTR           pTitle;
    LPTSTR           pSeparator;
    LPTSTR           pCheckedName;
    DWORD            IconID;
    WORD             wReserved[4];
    DWORD            dwReserved[2];
} EXTCHKBOX, *PEXTCHKBOX;

The EXTCHKBOX structure describes the extended check box available to an option item. Each option item can optionally have one extended push button or one extended check box callback. A selection item with an extended check box can be checked or unchecked, based on user input.

Members

cbSize
Size of this structure in bytes.
Flags
Bitmask that describes the extended check box. This field can be a bitwise OR of the following values.

Flag

Description

ECBF_CHECKNAME_
AT_FRONT

The CPSUI displays the strings associated with this extended check box as follows in the treeview: “pCheckedName pSeparator SelectName”, where SelectName is the string associated with the selected option item. By default, the CPSUI displays the check box treeview strings as “SelectName pSeparator pCheckedName”.

ECBF_CHECKNAME_
ONLY_ENABLED

If this check box is visible and enabled, the CPSUI displays only the string to which pCheckedName points in the treeview.

ECBF_ICONID_AS_HICON

The CPSUI interprets IconID as a handle to an icon rather than as a resource ID.

ECBF_OVERLAY_
WARNING_ICON

The CPSUI overlays its IDI_CPSUI_WARNING icon on the icon associated with this check box.

ECBF_OVERLAY_
ECBICON_IF_CHECKED

The CPSUI overlays the icon associated with this check box on the icon associated with the option item if the check box is checked.

ECBF_OVERLAY_
STOP_ICON

The CPSUI overlays the IDI_CPSUI_STOP icon on the icon associated with this check box.

ECBF_OVERLAY_
NO_ICON

The CPSUI overlays its IDI_CPSUI_NO icon on the icon associated with this check box.

pTitle
Pointer to a null-terminated string to be used as the extended check box title, or a resource ID.
pSeparator
Pointer to the separator that the CPSUI uses in an option item’s entry on a treeview page or the static title control on the non-treeview page.
pCheckedName
Pointer to a null-terminated string that the CPSUI displays in the treeview if the check box is checked, or a resource ID.
IconID
Identifies the icon to be associated with this check box. This field can be a standard IDI_CPSUI_Xxx icon ID, a caller’s icon resource ID, or, if the ECBF_ICONID_AS_HICON bit is set in Flags, a handle to a caller-defined icon. If IconID is zero, the selectable entry does not have an icon associated with it.
wReserved[4]
Reserved; these fields must be set to zero.
dwReserved[2]
Reserved; these fields must be set to zero.

Comments

The EXTCHKBOX structure describes the extended check box available to an option item.

The pTitle parameter is either a pointer to a null-terminated string or an integer value that specifies a resource identifier. If pTitle specifies a resource identifier, its high-order word must be zero and its low-order word must contain the identifier. This is also true for the pCheckedName parameter.

See Also

OPTITEM


EXTPUSH

typedef struct _EXTPUSH {
    WORD                  cbSize;
    WORD                  Flags;
    LPTSTR                pTitle;
    union {
        DLGPROC           DlgProc;
        FARPROC           pfnCallBack;
    }
    DWORD                 IconID;
    union {
        WORD              DlgTemplateID;
        HANDLE            hDlgTemplate;
    }
    DWORD                 dwReserved[3];
} EXTPUSH, *PEXTPUSH;

The EXTPUSH structure describes the extended push button available on an OPTITEM. Each option item can optionally have one extended push button callback or one extended check box callback.

Members

cbSize
Size of this structure in bytes.
Flags
Bitmask that describes the extended push button. This field can be a bitwise OR of the following values.

Flag

Description

EPF_PUSH_TYPE_DLGPROC

The extended push button has a DLGPROC function associated with it. The CPSUI uses the DlgProc and DlgTemplateID fields.

EPF_INCLUDE_SETUP_TITLE

The CPSUI appends “Setup” to the string to which pTitle points.

EPF_NO_DOT_DOT_DOT

The CPSUI does not append “…” to the string to which pTitle points.

EPF_ICONID_AS_HICON

The CPSUI interprets IconID as a handle to an icon rather than as a resource ID.

EPF_OVERLAY_WARNING_ICON

The CPSUI overlays its IDI_CPSUI_WARNING icon on the icon associated with this push button.

EPF_OVERLAY_STOP_ICON

The CPSUI overlays its IDI_CPSUI_STOP icon on the icon associated with this push button.

EPF_OVERLAY_NO_ICON

The CPSUI overlays its IDI_CPSUI_NO icon on the icon associated with this push button.

EPF_USE_HDLGTEMPLATE

The CPSUI uses the hDlgTemplate field to obtain the dialog box template.

pTitle
Pointer to a null-terminated string to be used as the extended push button title, or a resource ID.
DlgProc
Pointer to a caller-supplied DLGPROC function. The CPSUI calls the Win32 DialogBoxParam function with this function pointer, and passes a pointer to the CPSUICBPARAM structure ¾ with the Reason field set to CPSUICB_REASON_EXTPUSH ¾ to the WM_INITDIALOG message. If DlgProc is null, the CPSUI assumes that the extended push button is disabled. If DlgProc is not null, the CPSUI calls it only when the EPF_PUSH_TYPE_DLGPROC flag is set.
pfnCallBack
Pointer to a _CPSUICALLBACK callback. The CPSUI calls the function to which pfnCallBack points only when the EPF_PUSH_TYPE_DLGPROC flag is not set. The CPSUI passes a pointer to the CPSUICBPARAM structure as the parameter to this callback.
IconID
Identifies the icon to be associated with this push button. This field can be a standard IDI_CPSUI_Xxx icon ID, a caller’s icon resource ID, or, if the EPF_ICONID_AS_HICON bit is set in Flags, a handle to a caller-defined icon. If IconID is zero, the selectable entry does not have an icon associated with it.
DlgTemplateID
Specifies the resource ID for the dialog box.
hDlgTemplate
Handle to the DLGTEMPLATE that the CPSUI uses to display the dialog box.
dwReserved[3]
Reserved; these fields must be set to zero.

Comments

The EXTPUSH structure describes the extended push button available on an OPTITEM.

The pTitle parameter is either a pointer to a null-terminated string or an integer value that specifies a resource identifier. If pTitle specifies a resource identifier, its high-order word must be zero and its low-order word must contain the identifier.

See Also

OPTITEM


INSERTPSUIPAGE_INFO

typedef struct _INSERTPSUIPAGE_INFO {
    WORD            cbSize;
    BYTE            Type;
    BYTE            Mode;
    DWORD           dwData1;
    DWORD           dwData2;
    DWORD           dwData3;
} INSERTPSUIPAGE_INFO, *PINSERTPSUIPAGE_INFO;

The INSERTPSUIPAGE_INFO structure contains the information required by the CPSUI to insert a property sheet page.

Members

cbSize
Size in bytes of this structure.
Type
Specifies the type of property sheet page(s) to be inserted by the CPSUI. This field can be one of the following values.

Value

Description

PSUIPAGEINSERT_
GROUP_PARENT

Inserts a new group parent under which child pages can be inserted. This type allows a set of pages to be grouped such that the CPSUI can delete the entire set through the parent, rather than having to individually delete each child page.

The handle returned by the PFNCOMPROPSHEET function with a CPSFUNC_INSERT_PSUIPAGE function and this type can be nested. That is, the returned handle can be passed in the hComPropSheet parameter to the CPSUI’s callback as the parent handle such that the new page is inserted at the level below the returned group parent handle.

The dwData1, dwData2, and dwData3 fields must all be set to zero.

PSUIPAGEINSERT_
PCOMPROPSHEETUI

Insert pages using the data supplied in a COMPROPSHEETUI structure.

The dwData1 field is a pointer to the COMPROPSHEETUI structure; dwData2 and dwData3 must be set to zero. The CPSUI returns the total number of added pages in dwData2 when it succeeds, or ERR_CPSUI_Xxx if it fails.

PSUIPAGEINSERT_
PFNPROPSHEETUI

Insert pages using the PFNPROPSHEETUI function. The CPSUI calls this function with the Reason field of the pPSUIInfo parameter set to PROPSHEETUI_REASON_INIT to have it add the pages.

The PFNPROPSHEETUI function pointer is specified by dwData1, and dwData2 is passed to this function as the lParam parameter. The dwData3 field must be set to zero.

PSUIPAGEINSERT_
PROPSHEETPAGE

Insert pages using a PROPSHEETPAGE structure.

The dwData1 field is a pointer to the PROPSHEETPAGE structure; dwData2 and dwData3 must be set to zero.

PSUIPAGEINSERT_
HPROPSHEETPAGE

Insert pages using an HPROPSHEETPAGE handle.

The dwData1 field contains the handle returned from a call to the Win32 CreatePropertySheetPage function; dwData2 and dwData3 must be set to zero.

PSUIPAGEINSERT_DLL

Insert pages from a dynamic link library. The CPSUI loads the DLL, gets the procedure address of the PFNPROPSHEETUI function, and calls this function with the Reason field of the pPSUIInfo parameter set to PROPSHEETUI_REASON_INIT.

The dwData1 field is a pointer to the null-terminated string that specifies the name of the DLL. The dwData2 field is a pointer to the null-terminated ANSI string that specifies the name of the PFNPROPSHEETUI function to call to do the insertion. The dwData3 field is passed to the DLL-loaded function as the lParam parameter.

This method of inserting pages guarantees that the DLL is unloaded correctly.

Mode
Specifies how the CPSUI should insert the pages. This field can be one of the following values.

Mode

Description

INSPSUIPAGE_
MODE_BEFORE

Inserts the page(s) before the CPSUI object handle identified by the lParam1 parameter to the PFNCOMPROPSHEET callback.

INSPSUIPAGE_
MODE_AFTER

Inserts the page(s) after the CPSUI object handle identified by the lParam1 parameter to the PFNCOMPROPSHEET callback.

INSPSUIPAGE_
MODE_FIRST_CHILD

Inserts the page(s) as the first child of the parent handle identified by hComPropSheet. The CPSUI ignores the lParam1 parameter sent to its PFNCOMPROPSHEET callback.

INSPSUIPAGE_
MODE_LAST_CHILD

Inserts the page(s) as the last child of the parent handle identified by hComPropSheet. The CPSUI ignores the lParam1 parameter sent to its PFNCOMPROPSHEET callback.

INSPUIPAGE_
MODE_INDEX

Inserts the page(s) as a zero-based child index of the parent handle identified by hComPropSheet. The lParam1 parameter to the PFNCOMPROPSHEET callback specifies the handle to the zero-based index. This handle must be generated by using the CPSUI-defined HINSPSUIPAGE_INDEX(Index) macro. If the index is greater than or equal to the number of children, the CPSUI uses INSPSUIPAGE_MODE_LAST_CHILD as the mode.

dwData1

dwData2

dwData3
32-bit data fields whose interpretations depend on Type. See the Type member’s description for details.

Comments

The original caller initializes an INSERTPSUIPAGE_INFO structure and passes it to the CPSUI’s PFNCOMPROPSHEET callback as lParam2 when the function index is CPSFUNC_INSERT_PSUIPAGE. The CPSUI adds the specified pages at the position specified by Mode.


OIEXT

typedef struct _OIEXT {
    WORD             cbSize;
    WORD             Flags;
    HINSTANCE        hInstCaller;
    LPTSTR           pHelpFile;
    DWORD            dwReserved[4];
} OIEXT, *POIEXT;

The OIEXT structure allows the caller to optionally extend an option item defined by an OPTITEM structure.

Members

cbSize
Size of this structure in bytes.
Flags
Flag field in which the following flag can be set.

Flag

Description

OIEXTF_ANSI_STRING

The string to which pHelpFile points is an ANSI string, as opposed to a UNICODE string. The CPSUI checks this bit only when pHelpFile does not specify a resource ID.

hInstCaller
Specifies a DLL instance handle. If hInstCaller is not null, the CPSUI loads all resource strings and icons for the associated OPTITEM, OPTTYPE, and OPTPARAM structures from the DLL identified by hInstCaller. If hInstCaller is null, the CPSUI uses the DLL handle identified by the hInstCaller member of the COMPROPSHEETUI.
pHelpFile
Specifies the per-OPTITEM help file used by the CPSUI for help indexing. This field can be a resource ID or a pointer to the help file. If pHelpFile is null, the CPSUI uses the help file identified by the pHelpFile member of the COMPROPSHEETUI structure.
dwReserved[4]
Reserved; these fields must be set to zero.

Comments

The OIEXT structure allows the caller to optionally extend an option item defined by an OPTITEM structure.

The pHelpFile parameter is either a pointer to a null-terminated string or an integer value that specifies a resource identifier. If pHelpFile specifies a resource identifier, its high-order word must be zero and its low-order word must contain the identifier.


OPTITEM

typedef struct _OPTITEM {
    WORD                  cbSize;
    BYTE                  Level;
    BYTE                  DlgPageIdx;
    DWORD                 Flags;
    DWORD                 UserData;
    LPTSTR                pName;
    union {
        LONG              Sel;
        LPTSTR            pSel;
    };
    union {
        PEXTCHKBOX        pExtChkBox;
        PEXTPUSH          pExtPush;
    };
    POPTTYPE              pOptType;
    DWORD                 HelpIndex;
    BYTE                  DMPubID;
    BYTE                  UserItemID;
    WORD                  wReserved;
    POIEXT                pOIExt;
    DWORD                 dwReserved[3];
} OPTITEM, *POPTITEM;

The OPTITEM structure is used to describe each item’s name, selection type, and selectability in the dialog box view(s).

Members

cbSize
Size in bytes of this structure.
Level
Specifies the level of this item in the treeview. The root item should have the lowest level, and numbering should start at zero. The maximum number of levels is 256.
DlgPageIdx
Specifies a zero-based index into the array of DLGPAGE structures pointed to by the pDlgPage member of the COMPROPSHEETUI structure. The maximum index possible is MAX_DLGPAGE_COUNT. The CPSUI sets DlgPageIdx if pDlgPage is a standard CPSUI_PDLGPAGE_Xxx DLGPAGE.
Flags
Specifies the flags to apply to the option item identified by this OPTITEM structure. The OPTIF_CHANGEONCE flag is set by the CPSUI; all other flags are set by the caller. Any combination of the following flags can be set in Flags.

Flag

Description

OPTIF_COLLAPSE

Collapse this treeview item and its children so that it is not expanded initially.

OPTIF_HIDE

Hide this item from the treeview. The OPTIF_HIDE flag must be set before the item is initially displayed; the CPSUI ignores this bit if it is set after the item is displayed.

OPTIF_CALLBACK

The CPSUI should call back to the caller-supplied _CPSUICALLBACK function when a user changes this item.

OPTIF_CHANGED

This item has changed and needs to be redisplayed. The caller should set this flag only when returning from its _CPSUICALLBACK callback.

OPTIF_CHANGEONCE

This item has been changed. The CPSUI sets this bit.

OPTIF_DISABLED

This item is not selectable.

OPTIF_ECB_CHECKED

The associated extended check box is in the checked state.

OPTIF_EXT_HIDE

Hide the extended check box or extended push button.

OPTIF_EXT_DISABLED

The extended check box or extended push button is not selectable.

OPTIF_SEL_AS_HICON

The Sel field contains a handle to an icon rather than an icon resource ID. The CPSUI uses this flag only when pOptType is null.

OPTIF_EXT_
IS_EXTPUSH

The CPSUI should use the pExtPush field if it is not null. By default, if the union field contains a valid pointer and the OPTIF_EXT_IS_EXTPUSH bit is not set, the CPSUI assumes that the pointer points to an EXTCHKBOX structure.

OPTIF_NO_
GROUPBOX_NAME

The CPSUI should not overwrite the group box’s title text if a group box ID is defined.

OPTIF_OVERLAY_
WARNING_ICON

The CPSUI overlays its IDI_CPSUI_WARNING icon on the icon associated with this option item.

OPTIF_OVERLAY_
STOP_ICON

The CPSUI overlays its IDI_CPSUI_STOP icon on the icon associated with this option item.

OPTIF_OVERLAY_
NO_ICON

The CPSUI overlays its IDI_CPSUI_NO icon on the icon associated with this option item.

OPTIF_INITIAL_TVITEM

This item is the initial item to be selected and displayed on the treeview page. The CPSUI expands tree tiers and scrolls the selection into view as necessary. If this bit is not set or if the item is hidden, the CPSUI chooses the initial item to display.

OPTIF_HAS_POIEXT

The pOIExt field contains a valid pointer to an OIEXT structure. The CPSUI uses the OIEXT structure only when this bit is set.

UserData
Specifies a 32-bit value that is set and used by the caller. The CPSUI never uses or modifies this member.
pName
Pointer to a null-terminated string that specifies the name of this option item. Alternatively, it is used as data as described in the DMPubID member.
Sel
Specifies the index number for this item’s current selection. If pOptType is null, then this index number is the icon ID to be used for the non-selectable header of child option items.
pSel
Pointer to a null-terminated string that specifies the current selection for this item.
pExtChkBox
Pointer to the EXTCHKBOX structure that describes the extended check box associated with this item. If pExtChkBox is null, then this item does not have a check box associated with it.

The caller must clear the OPTIF_EXT_IS_EXTPUSH flag to notify the CPSUI that the option item has an extended check box associated with it. The OPTIF_ECB_CHECKED flag specifies whether the extended check box is checked or not checked.

pExtPush
Pointer to the EXTPUSH structure that describes the extended push button associated with this item. If pExtPush is null, then this item does not have a push button associated with it. The caller must set the OPTIF_EXT_IS_EXTPUSH flag to notify the CPSUI that the option item has an extended push button associated with it.
pOptType
Pointer to an OPTTYPE structure that describes the display and selections of this item. This item has no selection(s) when pOptType is null, so the CPSUI uses this item as the header for child items. When pOptType is null, the icon ID is specified by Sel.
HelpIndex
Specifies the index into the help file being used for context-sensitive help. A value of zero indicates that help does not exist for this item.
DMPubID
Specifies whether this item is one of the public fields in the DEVMODE structure supported by the CPSUI. For consistency, the caller should set pName to the standard CPSUI-defined resource ID for those DMPUB_Xxx values that identify DEVMODE fields. This field can be one of the following values.

Value

DEVMODE Field and Recommended pName

DMPUB_NONE

Not a public field of DEVMODE.

DMPUB_ORIENTATION

The dmOrientation field. Set pName to IDS_CPSUI_ORIENTATION.

DMPUB_SCALE

The dmScale field. Set pName to IDS_CPSUI_SCALE.

DMPUB_COPIES_COLLATE

The dmCopies / dmCollate field. Set pName to IDS_CPSUI_COPIES.

DMPUB_DEFSOURCE

The dmDefSource field. Set pName to IDS_CPSUI_SOURCE.

DMPUB_PRINTQUALITY

The dmPrintQuality field. Set pName to IDS_CPSUI_PRINTQUALITY or IDS_CPSUI_RESOLUTION. If pName is not set to either of these values, the CPSUI defaults it to IDS_CPSUI_RESOLUTION.

DMPUB_COLOR

The dmColor field. Set pName to IDS_CPSUI_COLOR_APPERANCE.

DMPUB_DUPLEX

The dmDuplex field. Set pName to IDS_CPSUI_DUPLEX.

DMPUB_TTOPTION

The dmTTOption field. Set pName to IDS_CPSUI_TTOPTION.

DMPUB_FORMNAME

The dmFormName field. Set pName to IDS_CPSUI_FORMNAME.

DMPUB_ICMMETHOD

The dmICMMethod field. Set pName to IDS_CPSUI_ICMMETHOD.

DMPUB_ICMINTENT

The dmICMIntent field. Set pName to IDS_CPSUI_ICMINTENT.

DMPUB_MEDIATYPE

The dmMediaType field. Set pName to IDS_CPSUI_MEDIA.

DMPUB_DITHERTYPE

The dmDitherType field. Set pName to IDS_CPSUI_DITHERING.

Greater than or equal to DMPUB_USER

Specifies a caller-defined value.

UserItemID
Specifies a caller-defined byte ID. The caller can use UserItemID to identify this item.
wReserved
Reserved; this field must be set to zero.
pOIExt
Pointer to an OIEXT structure containing extended data to describe this option item.
dwReserved[3]
Reserved; these fields must be set to zero.

Comments

The OPTITEM structure is used to describe each item’s name, selection type, and selectability in the dialog box view(s).

When the CPSUI is called to construct and display the Document Properties page, it automatically groups many of the DEVMODE’s public items together. The grouping is CPSUI version-dependent, and cannot be altered by the caller.

When DMPubID is DMPUB_COPIES_COLLATE, the CPSUI does the following before calling the _CPSUICALLBACK callback if the OPTIF_EXT_HIDE flag is not set and pExtChkBox is not null:

  • Enable the Collate Copies extended check box when a user requests more than one copy, and disable it when only one copy is requested.

  • Set the item’s postfix to copy for one copy and copies for more than one copy.

When DMPubID is DMPUB_COLOR, the CPSUI does the following before calling _CPSUICALLBACK if Color is selected over Gray Scale:

  • Calls the halftone color adjustment with the current color selection.

  • Disables color matching when color is not selected.

The CPSUI requires that Gray Scale be state 0 and Color be state 1.


OPTPARAM

typedef struct _OPTPARAM {
    WORD          cbSize;
    BYTE          Flags;
    BYTE          Style;
    LPTSTR        pData;
    DWORD         IconID;
    LONG          lParam;
    DWORD         dwReserved[2];
} OPTPARAM, *POPTPARAM;

The OPTPARAM structure is used to describe each selectable entry in an option item’s list of selections.

Members

cbSize
Size in bytes of this structure.
Flags
Bitmask in which the caller can return a bitwise OR of any of the following flags.

Flag

Description

OPTPF_HIDE

This selection is hidden from the user.

OPTPF_DISABLED

This selection is not available for a user to select.

OPTPF_ICONID_AS_HICON

The CPSUI interprets the IconID member as a handle to an icon rather than as a resource ID.

OPTPF_OVERLAY_WARNING_ICON

The CPSUI overlays its IDI_CPSUI_WARNING icon on the icon associated with this selection.

OPTPF_OVERLAY_STOP_ICON

The CPSUI overlays its IDI_CPSUI_STOP icon on the icon associated with this selection.

OPTPF_OVERLAY_NO_ICON

The CPSUI overlays its IDI_CPSUI_NO icon on the icon associated with this selection.

OPTPF_USE_HDLGTEMPLATE

The lParam member is a handle to a DLGTEMPLATE structure supplied by the original caller.

Style
Specifies the action to be performed on this item if it is selected. The pData and lParam fields depend on the value of Style. This member is valid only when the treeview option type is TVOT_PUSH BUTTON, and can be one of the following values.

Value

Description

PUSH BUTTON_TYPE_DLGPROC

This push button causes the CPSUI to display a caller-specified dialog box.

PUSH BUTTON_TYPE_CALLBACK

This push button requires that the caller process the option item.

PUSH BUTTON_TYPE_HTCLRADJ

This push button causes the CPSUI to display a halftone color adjustment dialog box.

PUSH BUTTON_TYPE_HTSETUP

This push button causes the CPSUI to display a device halftone setup dialog box.

pData
Pointer to data whose interpretation depends on the Type field of the associated OPTTYPE structure. For each OPTPARAM in the array to which the pOptParam member of OPTYPE points, pData has the following value:

Type

Description

TVOT_2STATES

pOptParam[0]->pData points to the text string that describes State 1.
pOptParam[1]->pData points to the string that describes State 2.

TVOT_3STATES

pOptParam[0]->pData points to the text string that describes State 1.
pOptParam[1]->pData points to the text string that describes State 2.
pOptParam[2]->pData points to the text string that describes State 3.

TVOT_UDARROW

pOptParam[0]->pData points to the text string for the selection postfix and icons.

pOptParam[1]->pData points to the help line text above the control.

TVOT_TRACKBAR
TVOT_SCROLLBAR

pOptParam[0]->pData points to the text string for the selection postfix and icons.
pOptParam[1]->pData points to the low range of the trackbar/scrollbar control. This value must be in the range of a 16-bit signed integer.

TVOT_LISTBOX
TVOT_COMBOBOX

pOptParam[0]->pData points to the text string for the first OPTPARAM.
pOptParam[1]->pData points to the text string for the second OPTPARAM.
…
pOptParam[n-1]->pData points to the text string for the nth OPTPARAM.

TVOT_EDITBOX

pOptParam[0]->pData points to the text string for the selection postfix and icons.
pOptParam[1]->pData points to the help line text above the control.

TVOT_PUSH BUTTON

Depends on the value of Style. See the Comments section for details.

TVOT_CHKBOX

pOptParam[0]->pData points to the text string for the OPTPARAM.

IconID
Identifies the icon to be associated with this selectable option item entry. This field can be a standard IDI_CPSUI_Xxx icon ID, a caller’s icon resource ID, or, if the OPTPF_ICONID_AS_HICON bit is set in Flags, a handle to a caller-defined icon. If IconID is zero, the selectable entry does not have an icon associated with it.
lParam
The value of this field depends on the TVOT_Xxx type specified in the Type field of the OPTTYPE structure of which this OPTPARAM structure is a member, and is described in the following table.

Type

Description

TVOT_2STATES

The CPSUI ignores lParam.

TVOT_3STATES

The CPSUI ignores lParam.

TVOT_UDARROW

Specifies the high range of the up-down control. This value must be in the range of a 16-bit signed integer.

TVOT_TRACKBAR
TVOT_SCROLLBAR

Specifies the high range of the trackbar/scrollbar control. This value must be in the range of a 16-bit signed integer.

TVOT_LISTBOX
TVOT_COMBOBOX

The CPSUI ignored lParam.

TVOT_EDITBOX

The CPSUI ignored lParam.

TVOT_PUSH BUTTON

Depends on the value of Style. See the Comments section for details.

TVOT_CHKBOX

The CPSUI ignored lParam.

dwReserved[2]
Reserved; these fields must be set to zero.

Comments

The OPTPARAM structure is used to describe each selectable entry in an option item’s list of selections.

When the caller has set the OPTPF_HIDE flag, the CPSUI does not display this selection in the selection list on the property sheet. If all of the selectable items associated with an option item are marked as hidden, then the CPSUI automatically hides the entire option item.

The OPTPF_HIDE flag is valid only when the treeview option type is one of TVOT_3STATES, TVOT_LISTBOX, or TVOT_COMBOBOX. If the Type field of an option item’s OPTTYPE structure is TVOT_3STATES and only two of the three states are specified as hidden, the CPSUI does not add the page and returns an error condition.

When the caller has set the OPTPF_DISABLED flag, the CPSUI displays this selection, but does not allow a user to select it. This flag is valid only when the treeview option type is one of TVOT_2STATES, TVOT_3STATES, TVOT_LISTBOX, or TVOT_COMBOBOX.

When the Type field of the associated OPTTYPE structure is TVOT_PUSH BUTTON, the pData and lParam fields depend on the value of Style, and are interpreted as shown in the following table.
Style | pData and lParam
PUSH BUTTON_ TYPE_DLGPROC | The pData field is a pointer to the caller’s dialog box function; the lParam field specifies the caller’s DIALOG resource template ID or DLGTEMPLATE handle, depending on the OPTPF_USE_HDLGTEMPLATE flag.
PUSH BUTTON_ TYPE_CALLBACK | The pData field is a pointer to a callback of type _CPSUICALLBACK; the lParam field is not used.
PUSH BUTTON_ TYPE_HTCLRADJ | The pData field is a pointer to a COLORADJUSTMENT structure; the lParam field is not used.
PUSH BUTTON_ TYPE_HTSETUP | The pData field is a pointer to a DEVHTADJDATA structure; the lParam field is not used.

The pData field must be supplied when Type is TVOT_CHKBOX and Style is one of CHKBOXS_FALSE_PDATA, CHKBOXS_NO_PDATA, CHKBOXS_OFF_PDATA, or CHKBOXS_NONE_PDATA.

If the treeview option type is not TVOT_PUSH BUTTON, the pData field is either a pointer to a null-terminated string that describes the selectable option item entry, or an integer value that specifies a string resource identifier. If pData specifies a resource identifier, its high-order word must be zero and its low-order word must contain the identifier.

When using resource IDs, the caller should use the standard IDS_CPSUI_Xxx values as often as possible for user interface consistency.


OPTTYPE

typedef struct _OPTTYPE {
    WORD             cbSize;
    BYTE             Type;
    BYTE             Flags;
    WORD             Count;
    WORD             BegCtrlID;
    POPTPARAM        pOptParam;
    WORD             Style;
    WORD             wReserved[3];
    DWORD            dwReserved[3];
} OPTTYPE, *POPTTYPE;

The OPTTYPE structure describes the set of possible selections for an option item, and the selection method for the set.

Members

cbSize
Size in bytes of this structure.
Type
Specifies the type of the option item. The CPSUI uses Type to determine how to lay out the OPTPARAM selections associated with an OPTITEM option item. This field must be one of the following predefined TVOT_Xxx values.

Value

Description

TVOT_2STATES

There are two option buttons for the item’s two possible states.

TVOT_3STATES

There are three option buttons for the item’s three possible states.

TVOT_UDARROW

The item has an up-down control.

TVOT_TRACKBAR

The item has a trackbar.

TVOT_SCROLLBAR

The item has a scroll bar.

TVOT_LISTBOX

The item has a list box.

TVOT_COMBOBOX

The item has a combo box.

TVOT_EDITBOX

The item has an edit box.

TVOT_PUSH BUTTON

The item has a push button.

TVOT_CHKBOX

The item has a check box.

Flags
Bitmask containing a bitwise OR of any of the following values.

Flag

Description

OPTTF_TYPE_DISABLED

Specifies that the array of OPTPARAM structures to which pOptParam points are disabled, meaning that none of the selectable items can be selected.

OPTTF_NOSPACE_
BEFORE_POSTFIX

Specifies that the CPSUI should not add a space character before the postfix string when combining it with the string specified in the pName field of the associated OPTITEM. For example, this bit could be set when the postfix string is “%”, so that the percent value and percent sign are displayed without a space between them.

This bit is valid only when an associated OPTPARAM item specifies a postfix string.

Count
Specifies the number of OPTPARAM structures to which pOptParam points. This value depends on the value of Type, and must be set as shown in the following table.

TVOT_Xxx

Count

TVOT_2STATES

2

TVOT_3STATES

3

TVOT_UDARROW

2

TVOT_TRACKBAR

3

TVOT_SCROLLBAR

3

TVOT_LISTBOX

n

TVOT_COMBOBOX

n

TVOT_EDITBOX

2

TVOT_PUSH BUTTON

1

TVOT_CHKBOX

1

BegCtrlID
Specifies the start control window ID. Each OPTITEM has a predefined number of control window IDs associated with it. This field must be set to zero if the caller is using the CPSUI’s standard pages.

The CPSUI uses BegCtrlID to manage the selections and initializations of an item associated with the caller’s dialog box. It uses BegCtrlID only when the OPTITEM is associated with a user-defined DLGTEMPLATE; that is, only when the DlgTemplateID field of the associated DLGPAGE structure is not a standard DP_STD_Xxx template ID.

The caller must organize control IDs based on Type as shown in the following table.

TVOT_Xxx

Control ID Values

TVOT_2STATES

BegCtrlID = 2 States group box ID
BegCtrlID+1 = 2 States static title text ID
BegCtrlID+2 = State 1 radio button ID
BegCtrlID+3 = State 1 icon control ID
BegCtrlID+4 = State 2 radio button ID
BegCtrlID+5 = State 2 icon control ID
BegCtrlID+6 = Extended check box/push button control ID
BegCtrlID+7 = Extended check box/push button icon control ID

TVOT_3STATES

BegCtrlID = 3 States group box ID
BegCtrlID+1 = 3 States static title text ID
BegCtrlID+2 = State 1 radio button ID
BegCtrlID+3 = State 1 icon control ID
BegCtrlID+4 = State 2 radio button ID
BegCtrlID+5 = State 2 icon control ID
BegCtrlID+6 = State 3 radio button ID
BegCtrlID+7 = State 3 icon control ID
BegCtrlID+8 = Extended check box/push button control ID
BegCtrlID+9 = Extended check box/push button icon control ID

TVOT_UDARROW

BegCtrlID = Up-down arrow group box ID
BegCtrlID+1 = Up-down arrow static title text ID
BegCtrlID+2 = Up-down arrow edit box ID
BegCtrlID+3 = Up-down arrow icon control ID
BegCtrlID+4 = Up-down arrow postfix static text ID
BegCtrlID+5 = Up-down arrow help static text ID
BegCtrlID+6 = Up-down arrow’s arrow ID
BegCtrlID+7 = Extended check box/push button control ID
BegCtrlID+8 = Extended check box/push button icon control ID

TVOT_TRACKBAR
TVOT_SCROLLBAR

BegCtrlID = Track/Scroll bar group box ID
BegCtrlID+1 = Track/Scroll bar static title text ID
BegCtrlID+2 = Horizontal Track/Scroll bar ID
BegCtrlID+3 = Track/Scroll bar icon control ID
BegCtrlID+4 = Track/Scroll bar low range text control ID
BegCtrlID+5 = Track/Scroll bar high range text control ID
BegCtrlID+6 = Track/Scroll bar postfix ID
BegCtrlID+7 = Extended check box/push button control ID
BegCtrlID+8 = Extended check box/push button icon control ID

TVOT_LISTBOX
TVOT_COMBOBOX

BegCtrlID = List/Combo box group box ID
BegCtrlID+1 = List/Combo box static title text ID
BegCtrlID+2 = List/Combo box ID
BegCtrlID+3 = List/Combo box icon control ID
BegCtrlID+4 = Extended check box/push button control ID
BegCtrlID+5 = Extended check box/push button icon control ID

TVOT_EDITBOX

BegCtrlID = Edit box group box ID
BegCtrlID+1 = Edit box static title text ID
BegCtrlID+2 = Edit box ID
BegCtrlID+3 = Edit box icon control ID
BegCtrlID+4 = Edit box postfix static text ID
BegCtrlID+5 = Edit box help static text ID
BegCtrlID+6 = Extended check box/push button control ID
BegCtrlID+7 = Extended check box/push button icon control ID

TVOT_PUSH BUTTON
TVOT_CHKBOX

BegCtrlID = Push button/Check box group box ID
BegCtrlID+1 = Push button/Check box static title text ID (not used by CPSUI)
BegCtrlID+2 = Push button/Check box ID
BegCtrlID+3 = Push button/Check box icon control ID
BegCtrlID+4 = Extended check box/push button control ID
BegCtrlID+5 = Extended check box/push button icon control ID

pOptParam
Pointer to an array of OPTPARAM structures describing the selections that belong to an OPTITEM item’s set.
Style
Specifies the style of the group box for an option item. OTS_LBCB_Xxx styles are applied only to the TVOT_LISTBOX and TVOT_COMBOBOX item types; OTS_PUSH_Xxx styles are applied only to TVOT_PUSH BUTTON types. This field can be a bitwise OR of the following values.

Value

Description

OTS_LBCB_SORT

The CPSUI sorts the listbox or combobox selectable items in ascending order, based on the pData fields of the OPTPARAM structures to which pOptParam points.

OTS_LBCB_
PROPPAGE_LBUSECB

If Type is TVOT_LISTBOX and the selection set identified by this OPTTYPE structure is on a non-treeview user-defined property sheet page dialog box, the CPSUI interprets control for a combobox rather than a listbox.

OTS_LBCB_
PROPPAGE_CBUSELB

If Type is TVOT_COMBOBOX and the selection set identified by this OPTTYPE structure is on a non-treeview user-defined property sheet page dialog box, the CPSUI interprets control for a listbox rather than a combobox.

OTS_LBCB_
INCLUDE_ITEM_NONE

The CPSUI adds Not Available to the list of selections in a listbox or combobox. The CPSUI sets the Sel field of the OPTITEM structure to -1 when a user selects None from the list. It also validates Sel during page initialization, setting any out-of-range values to -1.

OTS_LBCB_
NO_ICON16_IN_ITEM

The CPSUI does not include icons in the listbox or combobox. By default, each item string in a listbox or combobox has a 16x16 icon placed in front of it.

OTS_PUSH_
INCLUDE_SETUP_TITLE

The CPSUI appends “Setup” to the push button’s text string.

OTS_PUSH_
NO_DOT_DOT_DOT

The CPSUI does not append “…” to the text string on a push button.

OTS_PUSH_ENABLE_ALWAYS

Regardless of permissions, the CPSUI allows the user to select the push button. The associated callback or dialog box procedure should display the current setting but not allow the user to modify it.

wReserved[3]
Reserved; these fields must be set to zero.
dwReserved[3]
Reserved; these fields must be set to zero.


PROPSHEETUI_GETICON_INFO

typedef struct _PROPSHEETUI_GETICON_INFO {
    WORD            cbSize;
    WORD            Flags;
    WORD            cxIcon;
    WORD            cyIcon;
    HICON           hIcon;
} PROPSHEETUI_GETICON_INFO, *PPROPSHEETUI_GETICON_INFO;

The CPSUI provides a PROPSHEETUI_GETICON_INFO structure as the lParam parameter when it calls PFNPROPSHEETUI for the PROPSHEETUI_REASON_GET_ICON reason.

Members

cbSize
Size of this structure in bytes.
Flags
Reserved. Must be set to zero.
cxIcon
Width of the icon in pixels.
cyIcon
Height of the icon in pixels.
hIcon
Specifies the handle for the created icon. PFNPROPSHEETUI should set this member to null if it does not create an icon.


PROPSHEETUI_INFO

typedef struct _PROPSHEETUI_INFO {
    WORD                    cbSize;
    WORD                    Version;
    WORD                    Flags;
    WORD                    Reason;
    HANDLE                  hComPropSheet;
    PFNCOMPROPSHEET         pfnComPropSheet;
    LPARAM                  lParamInit;
    DWORD                   UserData;
    DWORD                   Result;
} PROPSHEETUI_INFO, *PPROPSHEETUI_INFO;

The PROPSHEETUI_INFO structure is passed from the CPSUI to the caller-defined PFNPROPSHEETUI function.

Members

cbSize
Size of this structure in bytes.
Version
Version number of this structure. The current version is defined as PROPSHEETUI_INFO_VERSION in compstui.h.
Flags
Flag field in which the following bit can be set.

Flag

Description

PSUIINFO_UNICODE

The caller’s executable was compiled for Unicode.

Reason
Specifies the action to be performed on the property sheet by the PFNPROPSHEETUI callback to which the PROPSHEETUI_INFO structure was passed. It can be one of the following values.

Reason

Description

PROPSHEETUI_REASON_INIT

This is the first call that the CPSUI makes to PFNPROPSHEETUI. The callback should initialize itself and call the CPSUI function to which pfnComPropSheet points to add new pages to the CPSUI object identified by hComPropSheet.

PROPSHEETUI_REASON_
GET_INFO_HEADER

The CPSUI calls PFNPROPSHEETUI with this reason when the first call with PROPSHEETUI_REASON_INIT set returns successfully. The callback should supply the property sheet title bar information needed by the CPSUI to display the property sheet dialog boxes. The CPSUI passes a PROPSHEETUI_INFO_HEADER structure as the lParam parameter to PFNPROPSHEETUI.

PROPSHEETUI_REASON_
GET_ICON

The CPSUI calls PFNPROPSHEETUI with this reason to retrieve the icon that represents this PFNPROPSHEETUI function.

PROPSHEETUI_REASON_
SET_RESULT

A new handle, added through a call to PFNCOMPROPSHEET with Function set to CPSFUNC_ADD_Xxx, needs to return its result to its parent. The CPSUI provides a pointer to a SETRESULT_INFO structure with the following fields set:

hSetResult specifies the handle that was added;

Result specifies the result from adding the property sheet page(s) identified by hSetResult. This result is to be set in the PROPSHEETUI_INFO Result field in this call to PFNPROPSHEETUI.

PROPSHEETUI_REASON_
DESTROY

Pages previously added by PFNPROPSHEETUI need to be deleted now, or the property sheet is being destroyed entirely. PFNPROPSHEETUI should uninitialize itself and free all memory allocated in this function instance.

When PFNPROPSHEETUI is called with this reason, all children of hComPropSheet are destroyed, and the children’s handles are no longer valid.

hComPropSheet
Handle to the CPSUI object on which action is to be performed. This handle is created and managed by the CPSUI, and is typically a parent handle. When PFNPROPSHEETUI calls the function to which pfnComPropSheet points to add or delete property sheet pages, it should pass this handle as the hComPropSheet parameter.
pfnComPropSheet
Pointer to the CPSUI’s callback function. PFNPROPSHEETUI calls the function to which pfnComPropSheet points so that the CPSUI can complete the action to be performed on the property sheet.
lParamInit
Specifies the LPARAM value passed in the first call to the caller’s PFNPROPSHEETUI callback by the CPSUI; the first call is always made with Reason set to PROPSHEETUI_REASON_INIT. This member is passed to each subsequent call to PFNPROPSHEETUI, regardless of reason.
UserData
Field in which the current PFNPROPSHEETUI call can store data. This field is an IN/OUT parameter whose value depends on Reason as shown in the following table.

Reason

Description

PROPSHEETUI_REASON_INIT

IN: Set to zero.

OUT: Contains data that was stored by this call to PFNPROPSHEETUI. This data is passed in the next call to PFNPROPSHEETUI for any reason.

All other reasons

IN: Specifies the data stored in the last call to PFNPROPSHEETUI for any reason.

OUT: Contains data that was stored by this call to PFNPROPSHEETUI, if any.

Result
Member in which the result of the call to PFNPROPSHEETUI is stored. This field is an IN/OUT parameter whose value depends on Reason as shown in the following table.

Reason

Description

PROPSHEETUI_REASON_INIT

IN: Set to zero.

OUT: Set to the appropriate result of the current call to PFNPROPSHEETUI.

All other reasons

IN: Specifies the result returned by the previous call to PFNPROPSHEETUI for any reason.

OUT: Set to the appropriate result of the current call to PFNPROPSHEETUI.

Comments

The CPSUI allocates, initializes, and maintains the PROPSHEETUI_INFO structure. The CPSUI passes this structure as a parameter to PFNPROPSHEETUI.

The caller’s PFNPROPSHEETUI callback can determine and set the UserData and Result fields of the PROPSHEETUI_INFO structure for any PROPSHEETUI_REASON_Xxx reason that it is called.


PROPSHEETUI_INFO_HEADER

typedef struct _PROPSHEETUI_INFO_HEADER {
    WORD             cbSize;
    WORD             Flags;
    LPTSTR           pTitle;
    HWND             hWndParent;
    HINSTANCE        hInst;
    union {
        HICON        hIcon;
        DWORD        IconID;
    };
} PROPSHEETUI_INFO_HEADER, *PPROPSHEETUI_INFO_HEADER;

The PROPSHEETUI_INFO_HEADER structure contains caller-specific details required by the CPSUI to display the property sheet’s dialog boxes.

Members

cbSize
Size in bytes of this structure.
Flags
Bitmask in which the caller can return a bitwise OR of any of the following flags.

Flag

Description

PSUIHDRF_NOAPPLYNOW

The CPSUI should remove the Apply Now button.

PSUIHDRF_PROPTITLE

The CPSUI should append the word “Properties” to the title bar string.

PSUIHDRF_USEHICON

The hIcon union field is a valid handle to an icon that CPSUI should use.

PSUIHDRF_DEFTITLE

The CPSUI should include “Default” in the title bar string. CPSUI adds “Default” after pTitle, and, if PSUIHDRF_PROPTITLE is set, before “Properties”.

pTitle
Pointer to a null-terminated string to be used on the property sheet’s title bar, or a resource ID.
hWndParent
Handle to the window that is the parent of the property sheet. If this parameter is null, the CPSUI uses the currently active window of the calling thread.
hInst
Handle to the driver’s instance of itself. The CPSUI uses this handle to load the driver’s icon and other other resources.
hIcon
Handle to the icon that the CPSUI should put on the property sheet’s title bar. The CPSUI uses this field when the PSUIHDRF_USEICON bit is set in Flags.
IconID
Specifies the resource ID of the icon that the CPSUI should put on the property sheet’s title bar. The CPSUI uses this field when the PSUIHDRF_USEICON bit is not set in Flags.

Comments

The CPSUI allocates and initializes a PROPSHEETUI_INFO_HEADER structure when a property sheet has been requested. It passes this structure to the caller’s PFNPROPSHEETUI callback to fill with information needed for the property sheet’s title bar. The CPSUI calls the callback with the Reason member of pPSUIInfo set to PROPSHEETUI_REASON_GET_INFO_HEADER; the PROPSHEETUI_INFO_HEADER structure is passed as lParam.

The pTitle parameter is either a pointer to a null-terminated string or an integer value that specifies a resource identifier. If pTitle specifies a resource identifier, its high-order word must be zero and its low-order word must contain the identifier.


PSPINFO

typedef struct _PSPINFO {
    WORD               cbSize;
    WORD               wReserved;
    HANDLE             hComComPropSheet;
    HANDLE             hCPSUIPage;
    PFNCOMPROPSHEET    pfnComPropSheet;
} PSPINFO, *PPSPINFO;

The PSPINFO structure is used by a property sheet page’s dialog box procedure with the WM_INITDIALOG message.

Members

cbSize
Size of this structure in bytes.
wReserved
Reserved; must be set to zero.
hComPropSheet
Handle to the parent page. This handle has been obtained in a call to PFNCOMPROPSHEET with a Function of CPSFUNC_ADD_PROPSHEETPAGE.
hCPSUIPage
Handle to the added property sheet page.
pfnComPropSheet
Pointer to the CPSUI’s callback.

Comments

When the WM_INITDIALOG message is sent to the dialog box procedure, the lParam parameter is a pointer to a PROPSHEETPAGE, and the lParam member of the PROPSHEETPAGE structure is a pointer to a PSPINFO structure. When processing the WM_INITDIALOG message, the dialog box procedure should save the PSPINFO pointer so that the CPSUI can be called back to process future CPSFUNC_Xxx requests. The dialog box procedure should use the PPSPINFO_FROM_WM_INITDIALOG_LPARAM macro defined in compstui.h to retrieve this pointer.

See the Win32 SDK for information on the PROPSHEETPAGE structure.

See Also

DLGPAGE


SETRESULT_INFO

typedef struct _SETRESULT_INFO {
    WORD            cbSize;
    WORD            wReserved;
    HANDLE          hSetResult;
    DWORD           Result;
} SETRESULT_INFO, *PSETRESULT_INFO;

The CPSUI provides a SETRESULT_INFO structure as the lParam parameter when it calls PFNPROPSHEETUI for the PROPSHEETUI_REASON_SET_RESULT reason.

Members

cbSize
Size in bytes of this structure.
wReserved
Reserved.
hSetResult
Handle to the added CPSUI page(s) requested by the caller when its PFNPROPSHEETUI callback called the CPSUI’s callback with the Function parameter set to CPSUI_ADD_Xxx.
Result
Specifies the result to be set to this call to PFNPROPSHEETUI from the property sheet page(s) identified by hSetResult. PFNPROPSHEETUI should set the Result field of the structure to which its pPSUIInfo parameter points based on PFNPROPSHEETUI’s child’s Result in the SETRESULT_INFO structure. When PFNPROPSHEETUI’s return value is greater than zero, the CPSUI sets Result to the parent of hSetResult.


Chapter 11 User-Mode Printer Driver Functions

This chapter describes the DrvSpl Xxx functions that comprise the user-mode printer driver. User-mode printer driver function declarations can be found in winddiui.h, which is provided with this DDK.

A printer driver writer can optionally implement a user-mode printer driver to provide rendering functionality in user mode. If you do implement this module, see the Comments section of each reference page to determine whether a DrvSpl Xxx function is required or optional.


DrvSplAbort

VOID DrvSplAbort(

HANDLE hDriver

);

DrvSplAbort aborts the printing job.

Parameters

hDriver
Identifies the per-job instance handle with which the aborted job is associated. The driver generated this handle in response to a call to DrvSplStartDoc.

Comments

The spooler calls DrvSplAbort when it receives notification that the print job associated with hDriver has been aborted. The driver should discard any pending work, or mark it as discardable. The driver can clean up work marked as discardable when its DrvSplClose routine is subsequently called.

The user-mode printer driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplAbort can be optionally implemented.


DrvSplClose

VOID DrvSplClose(

HANDLE hDriver

);

DrvSplClose releases the specified user-mode printer driver handle and its associated resources.

Parameters

hDriver
Identifies the per-job instance handle to be closed. The driver generated this handle in response to a call to DrvSplStartDoc.

Comments

The spooler calls DrvSplClose when it will not be making any more calls to the user-mode printer driver using the driver data identified by hDriver. This happens at the normal end of a print job or when an application terminates in an error condition.

The user-mode printer driver should free hDriver and clean up any other resources associated with the print job for which hDriver was generated. The driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplClose must be implemented if you do write a user-mode printer driver.

See Also

DrvSplAbort, DrvSplStartDoc


DrvSplEndDoc

VOID DrvSplEndDoc(

HANDLE hDriver

);

DrvSplEndDoc ends a print job.

Parameters

hDriver
Identifies the per-job instance handle with which the printer and print job are associated. The driver generated this handle in response to a call to DrvSplStartDoc.

Comments

The spooler calls DrvSplEndDoc at the normal end of a printed document. The user-mode printer driver should flush any pending I/O by calling the Win32 WritePrinter function.

The user-mode printer driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplEndDoc must be implemented if you do write a user-mode printer driver.

See Also

DrvSplStartDoc


DrvSplEndPage

BOOL DrvSplEndPage(

HANDLE hDriver

);

DrvSplEndPage provides end-of-page notification.

Parameters

hDriver
Identifies the per-job instance handle with which the printer and print job are associated. The driver generated this handle in response to a call to DrvSplStartDoc.

Return Value

DrvSplEndPage returns TRUE when it succeeds; otherwise it returns FALSE.

Comments

The spooler calls DrvSplEndPage to provide the driver with end-of-page notification. Drivers should perform any work required to finish rendering the current page.

The user-mode printer driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplEndPage can be optionally implemented.

See Also

DrvSplStartPage


DrvSplStartDoc

HANDLE DrvSplStartDoc(

HANDLE hPrinter,

DWORD JobId

);

DrvSplStartDoc starts the identified print job.

Parameters

hPrinter
Handle that identifies the printer on which the job is to be printed.
JobId
Specifies the ID number of the job to be printed.

Return Value

DrvSplStartDoc returns a per-job instance handle upon success; it returns zero if it fails.

Comments

The handle returned by DrvSplStartDoc is typically a pointer to a block of memory allocated by the user-mode printer driver for the instance data associated with the job identified by JobId. The user-mode printer driver should save hPrinter and JobId because they are not provided in any subsequent DrvSpl Xxx calls.

The user-mode printer driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplStartDoc must be implemented if you do write a user-mode printer driver.

See Also

DrvSplEndDoc


DrvSplStartPage

BOOL DrvSplStartPage(

HANDLE hDriver

);

DrvSplStartPage prepares the printer to accept data.

Parameters

hDriver
Identifies the per-job instance handle with which the printer and print job are associated. The driver generated this handle in response to a call to DrvSplStartDoc.

Return Value

DrvSplStartPage returns TRUE upon success; otherwise it returns FALSE.

Comments

The spooler calls DrvSplStartPage to prepare the printer to accept data. The driver should perform any per-page activities required before the page can be rendered. This includes per-page initializations and the resetting of defaults.

The user-mode printer driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplStartPage can be optionally implemented.

See Also

DrvSplEndPage, DrvSplWritePrinter


DrvSplWritePrinter

BOOL DrvSplWritePrinter(

HANDLE hDriver,

LPVOID pBuf,

DWORD cbBuf,

LPDWORD pcWritten

);

DrvSplWritePrinter writes data to a printer.

Parameters

hDriver
Identifies the per-job instance handle with which the printer and print job are associated. The driver generated this handle in response to a call to DrvSplStartDoc.
pBuf
Pointer to an array of printer data.
cbBuf
Specifies the size in bytes of the array to which pBuf points.
pcWritten
Pointer to a DWORD in which the number of bytes processed by the user-mode printer driver is returned.

Return Value

DrvSplWritePrinter returns TRUE if it succeeds; otherwise, an error is logged and the return value is FALSE. The spooler propagates the last-error code back to the kernel-mode printer driver.

Comments

The spooler calls DrvSplWritePrinter when GDI notifies the spooler of a kernel-mode printer driver’s call to EngWritePrinter. The kernel-mode driver can send any format of data understood by its corresponding user-mode printer driver for processing and sending to the printer. This data is not interpreted or altered in any way by GDI or the spooler; they simply pass the data on.

A user-mode printer driver can either render the data as it is received, or can buffer it and render the page in its entirety in DrvSplEndPage.

The buffer to which pBuf points is in user-mode address space.

The user-mode printer driver should take any steps necessary to protect itself in the event that multiple jobs are printed simultaneously.

DrvSplWritePrinter must be implemented if you do write a user-mode printer driver.


Chapter 12 Printer Monitor Functions and Structures

This chapter provides function and structure reference pages for both port and language monitors. Function prototypes and structure definitions can be found in the Windows NT DDK winsplp.h include file.

Except for InitializePrintMonitor, InitializeMonitorEx, and InitializeMonitor, this chapter’s functions have pseudonames that can be renamed by monitor developers.

All functions are noted as required or optional for each monitor type.


AddPort

BOOL (WINAPI *pfnAddPort)(

LPWSTR pName,

HWND hWnd,

LPWSTR pMonitorName

);

AddPort creates a port and adds it to the list of ports currently supported by the specified monitor in the spooler environment.

Parameters

pName
Points to a zero-terminated string that specifies the name of the server to which the port is connected. If pName is null, the port is local.
hWnd
Handle to the parent window of the dialog box in which the port name will be entered.
pMonitorName
Points to a zero-terminated string that specifies the monitor associated with the port.

Return Value

The return value is TRUE if the function is successful, and FALSE otherwise.

Comments

The spooler calls AddPort when it receives an application request to add a port to its environment. The spooler forwards the call to the named monitor on the named server.

AddPort allows a port to be added interactively. A monitor should prompt a user to enter the port name in a dialog box on the window associated with hWnd. AddPort should validate the entered port name by calling the Win32 EnumPorts function to ensure that no duplicate port names are added to the spooler environment. A monitor should also verify that the port is one that it supports.

The spooler does not support remote AddPort calls. Consequently, AddPort implementations can ignore the pName and pMonitorName parameters.

AddPort is a port management function that must be implemented by port monitors and LMPMs. Pure language monitors should not implement this function.

See Also

AddPortEx, DeletePort


AddPortEx

BOOL (WINAPI *pfnAddPortEx)(

LPWSTR pName,

DWORD Level,

LPBYTE lpBuffer,

LPWSTR lpMonitorName

);

AddPortEx creates a port and adds it to the list of ports currently supported by the specified monitor in the spooler environment.

Parameters

pName
Points to a zero-terminated string that specifies the name of the server to which the port is connected. If pName is null, the port is local.
Level
Specifies the type of data in the buffer to which lpBuffer points.
lpBuffer
Points to a buffer containing the structure that identifies the name of the port to be added.
lpMonitorName
Points to a zero-terminated string that specifies the monitor associated with the port.

Return Value

The return value is TRUE if the function is successful.

Comments

The spooler calls AddPortEx when it receives an application’s Win32 AddPortEx request to add a port to its environment. The spooler forwards the call to the named monitor on the named server.

AddPortEx allows the spooler to request that a port be added noninteractively; that is, without the dialog box required by AddPort.

The spooler does not support remote AddPortEx calls. Consequently, AddPortEx implementations can ignore the pName and lpMonitorName parameters.

Possible values for Level and the corresponding structure to which lpBuffer points are shown in the following table:
Level | Structure to which lpBuffer points
1 | PORT_INFO_1
2 | PORT_INFO_2

AddPortEx should log an ERROR_INVALID_LEVEL error code and then fail if it does not support the specified level. The PORT_INFO_1 and PORT_INFO_2 structures are described in the Win32 SDK.

AddPortEx should validate the supplied port name by calling the Win32 EnumPorts function to ensure that no duplicate port names are added to the spooler environment. A monitor should also verify that the port is one that it supports.

AddPortEx is a port management function that must be implemented by port monitors and LMPMs. Pure language monitors should not implement this function.

See Also

AddPort, DeletePort


ClosePort

BOOL (WINAPI *pfnClosePort)(

HANDLE hPort

);

ClosePort closes the port specified by hPort when there are no printers connected to it.

Parameters

hPort
Handle to the port that is to be closed. This handle was acquired by the spooler in a previous call to OpenPort or OpenPortEx.

Return Value

ClosePort returns TRUE if the operation is successful.

Comments

The spooler calls ClosePort when a monitor other than the current one should hold the port, or when no printers are connected to the port specified by hPort.

If ClosePort receives an invalid handle, it should log an ERROR_INVALID_HANDLE error and fail. Otherwise, the monitor should clean up any resources, such as data structures that it allocated for the port, at this time.

ClosePort must be implemented by both port and language monitors.

See Also

OpenPort, OpenPortEx


ConfigurePort

BOOL (WINAPI *pfnConfigurePort)(

LPWSTR pName,

HWND hWnd,

LPWSTR pPortName

);

ConfigurePort is a port management function that configures the specified port.

Parameters

pName
Points to a zero-terminated string that specifies the name of the server on which the given port exists. If this string is null, the port is local.
hWnd
Handle to the parent window of the dialog box in which the configuration information will be entered.
pPortName
Points to a zero-terminated string that specifies the name of the port to be configured.

Return Value

The return value is TRUE if the function is successful.

Comments

The spooler calls ConfigurePort to perform port configuration. ConfigurePort can offer a dialog box to obtain some or all of the necessary port configuration information from the user.

The spooler does not support remote ConfigurePort calls; consequently, monitors can ignore the pName parameter.

ConfigurePort is a port management function that must be implemented by port monitors and LMPMs. Pure language monitors should not implement this function.

See Also

AddPort, AddPortEx, DeletePort


DeletePort

BOOL (WINAPI *pfnDeletePort)(

LPWSTR pName,

HWND hWnd,

LPWSTR pPortName

);

DeletePort deletes a port from the monitor’s environment.

Parameters

pName
Points to a zero-terminated string that specifies the name of the server on which the port to be deleted exists. If this parameter is null, the port is local.
hWnd
Handle to the parent window of the port-deletion dialog box.
pPortName
Points to a zero-terminated string that names the port to be deleted.

Return Value

The return value is TRUE if the function is successful.

Comments

The spooler calls DeletePort to delete a port from the monitor’s environment. The monitor should delete the specified port from its state. The spooler will not call DeletePort on a monitor as long as a port is open.

Applications can delete local and remote ports. The printer UI displays a confirmation message box before the spooler calls DeletePort, so a monitor should ignore the hWnd parameter and not display another dialog box.

DeletePort is a port management function that must be implemented by port monitors and LMPMs. Pure language monitors should not implement this function.

See Also

AddPort, AddPortEx


EndDocPort

BOOL (WINAPI *pfnEndDocPort)(

HANDLE hPort

);

EndDocPort performs end-of-print-job tasks on the specified port.

Parameters

hPort
Handle to the port through which the document was printed.

Return Value

The return value is TRUE if the function is successful.

Comments

The spooler calls EndDocPort after it has finished sending the print job to the printer by way of WritePort.

Monitors should call the Win32 SetJob function to inform the spooler that a print job is completed. A port monitor’s EndDocPort routine should call SetJob with the dwCommand parameter set to JOB_CONTROL_SENT_TO_PRINTER. When a print job goes through a language monitor, the spooler ignores any JOB_CONTROL_SENT_TO_PRINTER notifications that it receives from the port monitor. Consequently, a monitor that can determine true end-of-job status should delay calling SetJob until the printer notifies it that the print job has been completely printed. A language monitor’s EndDocPort routine can setup to listen for end-of-job notification from the printer. This monitor should pass JOB_CONTROL_LAST_PAGE_EJECTED when it does receive notification from a bidirectional printer that printing has been completed.

Monitors might wish to modify their behavior when a user deletes or restarts a print job. To determine whether this has occurred, monitors can call the Win32 GetJob function and check the job status for the JOB_STATUS_DELETING or JOB_STATUS_RESTART flags.

EndDocPort should also free any per-print-job resources that were allocated by StartDocPort.

Both port and language monitors must implement EndDocPort.

See Also

StartDocPort, WritePort


EnumPorts

BOOL (WINAPI *pfnEnumPorts)(

LPWSTR pName,

DWORD Level,

LPBYTE pPorts,

DWORD cbBuf,

LPDWORD pcbNeeded,

LPDWORD pcReturned

);

EnumPorts enumerates the ports available for printing on a specified server.

Parameters

pName
Points to a zero-terminated string that specifies the name of the server whose ports are to be enumerated. If this parameter is null, local ports are enumerated.
Level
Specifies the version of the structure to which pPorts points. This value must be one or two.
pPorts
Depending on Level, points to an array of PORT_INFO_1 or PORT_INFO_2 structures where data describing all of the available ports is to be written.
cbBuf
Specifies the size, in bytes, of the buffer to which pPorts points.
pcbNeeded
Points to a DWORD in which the buffer size required to hold all enumeration data is returned.
pcReturned
Points to a DWORD in which the number of PORT_INFO_1 or PORT_INFO_2 structures written to pPorts is returned.

Return Value

The return value is TRUE if the function is successful; otherwise, it is FALSE.

Comments

The spooler calls EnumPorts to get the list of ports maintained by a monitor. At spooler initialization, the spooler calls EnumPorts for all installed print monitors to build up a list of all available ports. The spooler expects valid results from EnumPorts regardless of whether a printer has yet been defined.

If a monitor doesn’t support the structure specified by Level, EnumPorts should log an ERROR_INVALID_LEVEL error and fail.

If cbBuf is too small, EnumPorts should set ERROR_INSUFFICIENT_BUFFER, and return in pcbNeeded the number of bytes required to copy all of the data. If cbBuf is large enough, EnumPorts should return the number of bytes copied into pPorts.

EnumPorts must write the PORT_INFO_Xx structure(s) at the beginning of the pPorts buffer, and the string(s) associated with the PORT_INFO_Xx structure(s) at the end of the buffer. Monitors that do not pack data in this way will cause Win32 EnumPorts calls to the spooler to fail. See the localmon code for an example of how to pack the pPorts buffer.

Monitors should set the pMonitorName field of the PORT_INFO_2 structure to be a string that describes their monitor. It is the monitor’s responsibility to localize this string. Monitors should set PORT_INFO_2’s fPortType field to zero.

EnumPorts is a port management function that must be implemented by port monitors and LMPMs. Pure language monitors should not implement this function.

See Also

AddPort, AddPortEx, DeletePort


GetPrinterDataFromPort

BOOL (WINAPI *pfnGetPrinterDataFromPort)(

HANDLE hPort,

DWORD ControlID,

LPWSTR pValueName,

LPWSTR lpInBuffer,

DWORD cbInBuffer,

LPWSTR lpOutBuffer,

DWORD cbOutBuffer,

LPDWORD lpcbReturned

);

GetPrinterDataFromPort gets data from a bidirectional printer.

Parameters

hPort
Handle to the port to which the bidirectional printer is connected.
ControlID
Specifies the I/O control code for the operation.
pValueName
Points to a null-terminated string value understood by both monitors and print drivers. Vendors can define a set of values for this parameter that their language monitor can interpret as requests for bidirectional printer status from their printer driver(s).

Port monitors should ignore this parameter.

lpInBuffer
Points to a buffer containing the data required to perform the operation. This parameter can be null if the call does not require input data.
cbInBuffer
Specifies the size, in bytes, of lpInBuffer.
lpOutBuffer
Points to a buffer in which the specified operation’s output data is returned. This parameter can be null if the call does not produce output data.
cbOutBuffer
Specifies the size, in bytes, of lpOutBuffer.
lpcbReturned
Points to a DWORD in which the number of bytes written in lpOutBuffer is returned.

Return Value

GetPrinterDataFromPort returns TRUE if the operation is successful; otherwise, it returns FALSE.

Comments

A port monitor can implement GetPrinterDataFromPort to provide IEEE 1284 support for a language monitor’s usage. A language monitor can support GetPrinterDataFromPort to query information from a bidirectional printer that supports the IEEE 1284 standard. Vendors define the status request strings passed from a language monitor to the associated bidirectional printer.

Monitors should log the ERROR_INVALID_PARAMETER error for all calls that they do not understand. This ensures that the spooler will then try to read the key from the registry directly.

GetPrinterDataFromPort can be optionally implemented by all monitors.

See Also

ReadPort


InitializeMonitor

BOOL InitializeMonitor(

LPWSTR pRegistryRoot

);

InitializeMonitor is supported for existing print monitors only. New print monitors should export InitializePrintMonitor instead.


InitializeMonitorEx

BOOL InitializeMonitorEx(
LPWSTR pRegistryRoot,
LPMONITOR pMonitor
);

InitializeMonitorEx is supported for existing print monitors only. New print monitors should export InitializePrintMonitor instead.


InitializePrintMonitor

LPMONITOREX InitializePrintMonitor(

LPWSTR pRegistryRoot

);

InitializePrintMonitor initializes the print monitor.

Parameters

pRegistryRoot
Points to the null-terminated string that identifies the registry root at which the print monitor can store internal information. The spooler will clean up this registry path when the print monitor is deleted.

Return Value

Upon success, InitializePrintMonitor returns an initialized MONITOREX structure. If it fails, this function logs an error code and returns null.

Comments

The spooler calls InitializePrintMonitor to obtain a MONITOREX structure with initialized pointers to the functions that the monitor implements. The spooler uses these function pointers to make subsequent calls into the print monitor.

New port and language monitors must implement InitializePrintMonitor and export it for the spooler. InitializeMonitor and InitializeMonitorEx are supported for backward compatibility only.


MONITOR

typedef struct _MONITOR{
    BOOL (WINAPI *pfnEnumPorts)(LPWSTR pName, DWORD Level, LPBYTE pPorts,
            DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
    BOOL (WINAPI *pfnOpenPort)(LPWSTR pName, PHANDLE pHandle);
    BOOL (WINAPI *pfnOpenPortEx)(LPWSTR pPortName,LPWSTR pPrinterName,
            PHANDLE pHandle, struct _MONITOR FAR *pMonitor);
    BOOL (WINAPI *pfnStartDocPort)(HANDLE hPort,LPWSTR pPrinterName,DWORD JobId,
            DWORD Level,LPBYTE pDocInfo);
    BOOL (WINAPI *pfnWritePort)(HANDLE hPort,LPBYTE pBuffer,DWORD cbBuf,
            LPDWORD pcbWritten);
    BOOL (WINAPI *pfnReadPort)(HANDLE hPort,LPBYTE pBuffer,DWORD cbBuffer,
            LPDWORD pcbRead);
    BOOL (WINAPI *pfnEndDocPort)(HANDLE hPort);
    BOOL (WINAPI *pfnClosePort)(HANDLE hPort);
    BOOL (WINAPI *pfnAddPort)(LPWSTR pName, HWND hWnd, LPWSTR pMonitorName);
    BOOL (WINAPI *pfnAddPortEx)(LPWSTR pName, DWORD Level, LPBYTE lpBuffer,
            LPWSTR lpMonitorName);
    BOOL (WINAPI *pfnConfigurePort)(LPWSTR pName, HWND hWnd, LPWSTR pPortName);
    BOOL (WINAPI *pfnDeletePort)(LPWSTR pName, HWND hWnd, LPWSTR pPortName);
    BOOL (WINAPI *pfnGetPrinterDataFromPort)(HANDLE hPort, DWORD ControlID,
            LPWSTR pValueName, LPWSTR lpInBuffer, DWORD cbInBuffer,
            LPWSTR lpOutBuffer, DWORD cbOutBuffer, LPDWORD lpcbReturned);
    BOOL (WINAPI *pfnSetPortTimeOuts)(HANDLE hPort,LPCOMMTIMEOUTS lpCTO,
            DWORD reserved // must be set to 0);
} MONITOR, FAR *LPMONITOR;

MONITOR contains pointers to the functions that implement a print monitor. It is a member of the MONITOREX structure.

A monitor should set pointers for unimplemented routines to null.


MONITOREX

typedef struct _MONITOREX{
    DWORD  dwMonitorSize;
    MONITOR  Monitor;
} MONITOREX, FAR *LPMONITOREX;

A print monitor returns an initialized MONITOREX structure to the spooler when the spooler calls InitializePrintMonitor.

Members

dwMonitorSize
Size of a MONITOR structure, in bytes.
Monitor
MONITOR structure containing function pointers to the routines that a monitor implements.

Comments

A print monitor should initialize the function pointers in the Monitor field to point to the functions that it supports. Monitors are also responsible for setting dwMonitorSize to be the size of the MONITOR structure, in bytes.

A language monitor’s OpenPortEx routine should check that the specified port monitor’s function table implements the functionality required by the language monitor.


OpenPort

BOOL (WINAPI *pfnOpenPort)(

LPWSTR pName,

PHANDLE pHandle

);

OpenPort opens a port for a printer.

Parameters

pName
Points to a string that specifies the name of the port to be opened.
pHandle
Points to the location in which the port handle will be returned.

Return Value

The return value is TRUE if the function is successful, FALSE if it fails.

Comments

All port monitors must implement OpenPort, whereas language monitors implement OpenPortEx. Monitors that combine both port and language monitor capabilities in one DLL need to implement both OpenPort and OpenPortEx.

OpenPort is called by the spooler whenever a port is assigned to a printer. It returns a handle to the port identified by pName. The spooler uses the returned port handle in subsequent calls to the port monitor’s StartDocPort, WritePort, ReadPort, and EndDocPort routines. The spooler waits for OpenPort to return, so a monitor should return success or fail within a reasonable amount of time.

If necessary, the port monitor should lock other processes from using the specified port after it has been connected to a printer.

OpenPort can also perform any one-time port initializations needed to set up the port for printing. To avoid hung or unreasonably delayed port responses, port monitors should always establish reasonable time-outs before actually calling a port. Port timeouts can be established as a part of OpenPort’s one-time port initializations, or by SetPortTimeouts.

Note that the parameter lists for OpenPort and the language monitor’s OpenPortEx function differ in that OpenPortEx also receives the printer name and a port monitor’s function table.

See Also

ClosePort, OpenPortEx, SetPortTimeouts


OpenPortEx

BOOL (WINAPI *pfnOpenPortEx)(

LPWSTR pPortName,

LPWSTR pPrinterName,

PHANDLE pHandle,

struct _MONITOR FAR *pMonitor

);

OpenPortEx provides a port on which a language monitor and its associated bidirectional printer can communicate.

Parameters

pPortName
Points to a string that specifies the name of the port to be opened.
pPrinterName
Points to a string that specifies the name of the printer that is connected to the port.
pHandle
Points to the location in which the port handle will be returned.
pMonitor
Points to the MONITOR structure defining the routines that a port monitor implements.

Return Value

The return value is TRUE if the function is successful, FALSE if it fails.

Comments

All language monitors must implement OpenPortEx, whereas port monitors implement OpenPort. Monitors that combine both port and language monitor capabilities in one DLL need to implement both OpenPort and OpenPortEx.

OpenPortEx is called by the spooler whenever a port is assigned to the printer identified by pPrinterName. OpenPortEx returns a handle to the port identified by pPortName; this handle is used in subsequent calls to the language monitor’s StartDocPort, WritePort, ReadPort, and EndDocPort routines.

OpenPortEx should validate the port monitor identified by pMonitor before calling its OpenPort function to establish a port connection.

Note that the parameter lists for OpenPort and the language monitor’s OpenPortEx function differ in that OpenPortEx also receives the printer name and a port monitor’s function table.

All language monitors must implement OpenPortEx.

See Also

ClosePort, OpenPort


ReadPort

BOOL (WINAPI *pfnReadPort)(

HANDLE hPort,

LPBYTE pBuffer,

DWORD cbBuffer,

LPDWORD pcbRead

);

ReadPort reads data from the specified printer port.

Parameters

hPort
Handle to the port from which to read.
pBuffer
Points to a buffer in which data read from the printer can be written.
cbBuffer
Specifies the size, in bytes, of the buffer pointed to by pBuffer.
pcbRead
Points to the location in which to return the number of bytes successfully read from the printer.

Return Value

The return value is TRUE if the function is successful; otherwise it is FALSE.

Comments

ReadPort supports bidirectional printers. If the printer provides any data, this data is written to the buffer pointed to by pBuffer.

If there is no response from the printer, ReadPort waits long enough to ensure there is no data coming, and then returns FALSE. ReadPort should always initialize pcbRead to zero before attempting the port read. If the port read succeeds, pcbRead should be overwritten with the number of bytes read. Note that a port can return zero bytes on a successful read; ReadPort should return TRUE in this situation.

A timeout could be specified by calling a port monitor’s SetPortTimeOuts routine.

Both port and language monitors must implement ReadPort. Typically, a language monitor’s ReadPort function calls the ReadPort function of the port monitor that it is using. The port monitor must return the read data stream to the language monitor without altering the data in any way. The language monitor is then responsible for interpreting the data returned to it.

A language monitor can create a thread to asychronously call the port monitor’s ReadPort function in order to query unsolicited data from the printer at timed intervals. Language monitors might call a port monitor’s ReadPort routine outside of a StartDocPort/EndDocPort pair. Port monitors that do not allow this should fail the call, and language monitors should be written to handle such a failure.

See Also

GetPrinterDataFromPort


SetPortTimeOuts

BOOL (WINAPI *pfnSetPortTimeOuts)(

HANDLE hPort,

LPCOMMTIMEOUTS lpCTO,

DWORD reserved

);

SetPortTimeOuts sets timeout values on an open port.

Parameters

hPort
Handle to the open port on which to set the timeout values.
lpCTO
Points to a Win32 COMMTIMEOUTS structure that contains the timeout values to set.
reserved
Reserved for future use. Must be set to zero.

Return Value

SetPortTimeOuts returns TRUE if it succeeds; otherwise, it returns FALSE.

Comments

To avoid hung or unreasonably delayed port responses, port monitors should always establish reasonable time-outs before actually calling a port. Port timeouts can also be established with the one-time port initializations performed by OpenPort.

The communications timeout structure, COMMTIMEOUTS, is defined in the Win32 SDK.

Port monitors and LMPMs can optionally implement SetPortTimeOuts. Language monitors should not implement this function because it performs port management. A language monitor can call a port monitor’s implementation.

See Also

OpenPort


StartDocPort

BOOL (WINAPI *pfnStartDocPort)(

HANDLE hPort,

LPWSTR pPrinterName,

DWORD JobId,

DWORD Level,

LPBYTE pDocInfo

);

StartDocPort performs the tasks required to start a print job on the specified port.

Parameters

hPort
Handle that identifies the port to which the print job is being sent.
pPrinterName
Points to a zero-terminated string specifying the name of the printer to which the job is being sent.
JobId
Identifies the job being printed.
Level
Specifies the level of the structure pointed to by pDocInfo. This value can be one or two.
pDocInfo
Depending on Level, points to a DOC_INFO_1 or DOC_INFO_2 structure that describes the document to be printed.

Return Value

The return value is TRUE if the function is successful; FALSE if the function fails.

Comments

The spooler calls StartDocPort when it is ready to send a job to the printer. StartDocPort should perform whatever port setup is required in order to send data from subsequent WritePort calls to the printer.


WritePort

BOOL (WINAPI *pfnWritePort)(

HANDLE hPort,

LPBYTE pBuffer,

DWORD cbBuf,

LPDWORD pcbWritten

);

WritePort writes data to a port.

Parameters

hPort
Identifies the port to which the data should be written.
pBuffer
Points to a buffer that contains data to be written to the port.
cbBuf
Specifies the size, in bytes, of pBuffer.
pcbWritten
Points to a location in which the number of bytes successfully written to the port will be written.

Return Value

The return value is TRUE if the function is successful.

Comments

WritePort sends the data pointed to by pBuffer to the printer. The spooler calls WritePort as often as is necessary to send an entire print job to the printer. The spooler sets the block size specified by cbBuf, so a monitor should not make any assumptions about it.

If there is no response from the printer, WritePort should wait a reasonable amount of time and then returns FALSE. WritePort should always initialize pcbWritten to zero before attempting the port write. If the port write succeeds, pcbWritten should be overwritten with the number of bytes sent. Note that a port can return zero bytes on a successful write; WritePort should return TRUE in this situation.

A language monitor can add information to the data stream before sending it to the port monitor’s WritePort routine for printing.

Language monitors might call a port monitor’s WritePort routine outside of a StartDocPort/EndDocPort pair. Port monitors that do not allow this can fail the call, and language monitors should be written to handle such a failure.

WritePort must be implemented by both port and language monitors.


Chapter 13 Print Processor Functions and Structures

This chapter describes the functions that a print processor must export to the local spooler, and the structures that it must support.

Function declarations and structure definitions can be found in winsplp.h. For more information on print processors, see Print Processors of the Kernel-mode Graphics Design Guide.


ClosePrintProcessor

BOOL ClosePrintProcessor(

HANDLE hPrintProcessor

);

ClosePrintProcessor closes the instance of the print processor indicated by hPrintProcessor.

Parameters

hPrintProcessor
Handle to an open instance of the print processor to close.

Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE, and the logged error can be retrieved by a call to GetLastError.

Comments

ClosePrintProcessor frees all resources allocated at the time the instance was opened.

See Also

OpenPrintProcessor


ControlPrintProcessor

BOOL ControlPrintProcessor(

HANDLE hPrintProcessor,

DWORD Command

);

ControlPrintProcessor provides control over printing the document.

Parameters

hPrintProcessor
Handle to an open instance of the print processor to which to send the command.
Command
This parameter can be one of the following values:

Value

Meaning

JOB_CONTROL_PAUSE

Pauses the print job.

JOB_CONTROL_RESUME

Resumes the print job at the point in the spool file where it was paused.

JOB_CONTROL_CANCEL

Cancels the print job.

Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE, and the logged error can be retrieved by a call to GetLastError.

Comments

Calls to this function can occur asynchronously; the function should not wait for the result. For example, the function call could set a flag and exit immediately.

See Also

OpenPrintProcessor


EnumPrintProcessorDatatypes

BOOL EnumPrintProcessorDatatypes(

LPWSTR pName,

LPWSTR pPrintProcessorName,

DWORD Level,

LPBYTE pDatatypes,

DWORD cbBuf,

LPDWORD pcbNeeded,

LPDWORD pcReturned

);

EnumPrintProcessorDatatypes enumerates the data types supported by a print processor.

Parameters

pName
Points to a zero-terminated string that specifies the name of the server on which the print processor is installed. If the string is null, the server is local.
pPrintProcessorName
Points to the print processor name.
Level
Specifies the version of the structure to which pDatatypes points. This value must be 1, indicating that the structure version is DATATYPES_INFO_1.
pDatatypes
Points to an array of DATATYPES_INFO_1 structures that are be written if the function succeeds.
cbBuf
Specifies the size, in bytes, of the buffer to which pDatatypes points.
pcbNeeded
Points to the location in which to return the required buffer size for the requested data. If cbBuf is too small, an error message is returned and pcbNeeded will contain the size required. If cbBuf is large enough, pcbNeeded contains the number of bytes that were copied.
pcReturned
Points to the location in which to return the number of DATATYPES_INFO_1 structures pointed to by pDatatypes.

Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE, and the logged error can be retrieved by a call to GetLastError.

Comments

EnumPrintProcessorDatatypes is called by the spooler at initialization.


OpenPrintProcessor

HANDLE OpenPrintProcessor(

LPWSTR pPrinterName,

PPRINTPROCESSOROPENDATA pPrintProcessorOpenData

);

OpenPrintProcessor opens an instance of the print processor for printing.

Parameters

pPrinterName
Points to the name of the printer for which the print processor is being opened. The printer should be callable by WritePrinter.
pPrintProcessorOpenData
Points to the data used to open the print processor.

Return Value

OpenPrintProcessor returns a handle that is used in subsequent calls to PrintDocumentOnPrintProcessor, ControlPrintProcessor, and ClosePrintProcessor.

See Also

PRINTPROCESSOROPENDATA


PrintDocumentOnPrintProcessor

BOOL PrintDocumentOnPrintProcessor(

HANDLE hPrintProcessor,

LPWSTR pDocumentName

);

PrintDocumentOnPrintProcessor prints the document on the print processor.

Parameters

hPrintProcessor
Specifies the handle to the print processor.
pDocumentName
Points to the document name.

Return Value

The function returns TRUE if it is successful. Otherwise it returns FALSE, and the logged error can be retrieved by a call to GetLastError.

Comments

PrintDocumentOnPrintProcessor is the heart of any print processor implementation. It calls OpenPrinter to get a handle to the document, sends the document handle with ReadPrinter, and calls WritePrinter to write the data. The function returns when the document is completely interpreted or canceled.

See Also

OpenPrintProcessor


PRINTPROCESSOROPENDATA

typedef struct _PRINTPROCESSOROPENDATA {
    DEVMODE	W	*pDevmode;
    LPWSTR		pDatatype;
    LPWSTR		pParameters;
    LPWSTR		pDocumentName;
    DWORD		JobId;
    LPWSTR		pOutputFile;
    LPWSTR		pPrinterName;
} PRINTPROCESSOROPENDATA, *PPRINTPROCESSOROPENDATA *LPPRINTPROCESSOROPENDATA;

The PRINTPROCESSOROPENDATA structure provides information that the print processor needs in order to open itself. The spooler initializes this structure when it starts a new thread to play back a spooled file. It then passes the initialized structure to the print processor with a call to OpenPrintProcessor.

Members

pDevmode
Points to the data structure that contains information about the device initialization and environment of a printer.
pDatatype
Points to the data type of the document.
pParameters
Points to a null-terminated string of parameters to the printer processor.
pDocumentName
Points to the name of the document.
JobId
Identifies the print job associated with the document.
pOutputFile
Points to the document spool file.
pPrinterName
Points to the name of the printer to be used for printing.


Introduction to Multimedia Drivers

The following topics provide an introduction to writing installable multimedia drivers for Windows NTâ:

  • Types of Multimedia Devices

  • User-Mode Multimedia Drivers

  • Kernel-Mode Multimedia Drivers

  • Multimedia Dynamic-Link Libraries

  • Designing Multimedia Drivers

  • Building Multimedia Drivers

  • Installing Multimedia Drivers

  • Configuring Multimedia Drivers

  • Multimedia Driver Reference

These topics provide information that pertains to all types of multimedia devices and drivers. For specific information about writing drivers for a certain type of multimedia device, refer to the topics listed in Types of Multimedia Devices.


Types of Multimedia Devices

Each piece of multimedia hardware can be classified as being either an audio device, a video capture device, or a positioning device. Each of these classifications can be broken down further, as follows:

  • Audio devices include waveform devices, MIDI devices, mixers, and auxiliary audio devices.

    For information about writing drivers for audio devices, see Audio Device Drivers.

    For information about writing drivers for compressing audio data, see Audio Compression Manager Drivers.

  • Video capture devices capture video images that can be stored in a disk file and played back later.

    For information about writing drivers for video capture devices, see Video Capture Device Drivers.

    For information about writing drivers for compressing video data, see Video Compression Manager Drivers.

  • Positioning devices, such as joysticks, light pens, and touch screens, are devices that can establish a screen position.

    For information about writing drivers for positioning devices, see Joystick Drivers.

For information about writing drivers for the Media Control Interface (MCI), which is a high-level application interface to all types of multimedia devices, see MCI Drivers.


User-Mode Multimedia Drivers

User-mode multimedia drivers executing under Windows NT have the following characteristics:

  • They execute in user mode.

  • They export user-mode driver entry points that are called by applications and other clients to request I/O operations.

  • They communicate with kernel-mode multimedia drivers by calling Win32 functions which, in turn, call functions in the Windows NT Executive. The Windows NT Executive functions provide the context switch from user mode to kernel mode.

    Typically, user-mode drivers call CreateFile to open a device instance. Then they make numerous calls to DeviceIoControl to send I/O control codes to the kernel-mode driver. Some drivers also call the ReadFileEx and WriteFileEx functions to transfer data blocks. Calling CloseHandle closes the device instance. (All of these functions are described in the Win32 SDK.)

    User-mode drivers do not generally call these Win32 functions directly. Instead, they typically call functions in support libraries, which in turn call the Win32 functions. The support libraries are described in the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.

    User-mode drivers do not necessarily communicate with a kernel-mode driver ¾ some communicate with other user-mode drivers. For example some MCI drivers call Win32 Audio API functions to communicate with user-mode audio device drivers.

Because user-mode Windows NT drivers run under the Windows NT Win32 Subsystem and call Win32 API functions described in the Win32 SDK, these drivers are sometimes called Win32-based drivers.

For information about designing a user-mode multimedia driver, see Designing a User-Mode Multimedia Driver.


Kernel-Mode Multimedia Drivers

Kernel-mode multimedia drivers executing under Windows NT have the following characteristics:

  • They execute in kernel mode.

  • They export kernel-mode driver entry points that are called by the Windows NT Executive for performing I/O operations requested by user-mode multimedia drivers.

  • They are implemented as services under the control of the Windows NT Service Control Manager.

  • They communicate with device hardware by calling functions in the Windows NT Executive. The I/O Manager and the Hardware Abstraction Layer (HAL), both of which are parts of the Windows NT Executive, provide driver compatibility across the various hardware platforms supported by Windows NT.

    Kernel-mode drivers often do not call Windows NT Executive functions directly. Instead, they might call functions in support libraries, which in turn call the Executive functions. The support libraries are described in the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.

For information about designing a kernel-mode multimedia driver, see Designing a Kernel-Mode Multimedia Driver.


Multimedia Dynamic-Link Libraries

The following dynamic-link libraries export the APIs that clients use to access multimedia drivers.

winmm.dll

The winmm.dll dynamic-link library exports several Win32 multimedia APIs, described in the Win32 SDK, including the following:

  • Functions for accessing audio device drivers (wave-prefixed, midi-prefixed, mixer-prefixed, and aux-prefixed functions).

  • Functions for accessing MCI drivers (mci-prefixed functions).

  • Functions for accessing joystick drivers (joy-prefixed functions).

  • Functions for sending specific messages to a user-mode driver’s DriverProc function (OpenDriver, SendDriverMessage, and CloseDriver).

Additionally, winmm.dll exports DefDriverProc, which user-mode drivers can call for default processing of the standard driver messages.

msacm32.dll

The msacm32.dll dynamic-link library exports the audio compression functions that are described in the Win32 SDK and are used to send messages to Audio Compression Manager drivers.

avicap32.dll

The avicap32.dll dynamic-link library exports the AVI capture window class, which is described in the Win32 SDK and which is used to send messages to video capture device drivers.

msvfw32.dll

The msvfw32.dll dynamic-link library exports the following multimedia software components:

  • Functions described in the Video for Windows Development Kit, which are used to send messages to video capture device drivers.

  • The Video Compression Manager, which sends messages to Video Compression Manager drivers.

  • The MCIWnd window class, which is described in the Win32 SDK and which controls multimedia devices by sending messages to MCI drivers.

  • The DRAWDIB functions, which are described in the Win32 SDK and which provide capabilities for high-performance drawing of device-independent bitmaps (DIBs).


Designing Multimedia Drivers

This section provides the following topics about designing user-mode multimedia drivers and kernel-mode multimedia drivers:

  • Do you Need a New Driver?

  • Designing a User-Mode Multimedia Driver

  • Designing a Kernel-Mode Multimedia Driver


Do you Need a New Driver?

For a new piece of multimedia hardware, you must decide if you need both a new user-mode multimedia driver and a new kernel-mode multimedia driver. If a currently available user-mode driver allows applications to access all of the functionality provided by the new hardware, then you do not need to write a new user-mode driver. For example, the standard audio driver, mmdrv.dll, might provide all the necessary interfaces to access all the features of a new sound card. In this case, only a new kernel-mode driver is necessary. More typically, new hardware requires development of a new user-mode driver in order to handle the hardware’s unique configuration requirements. New user-mode drivers can be easily written by modifying sample drivers provided with this DDK.

New hardware almost always requires a new kernel-mode driver, because the kernel-mode driver contains information about a device’s registers and hardware buffers. Occasionally, a new user-mode driver is written that does not require support from a new kernel-mode driver. For example, the MCICDA CD audio driver uses the standard CD-ROM file system, so it does not require a unique underlying kernel-mode driver.

If your device can be connected to several different buses, you do not need a different kernel-mode driver for each bus. The Windows NT Hardware Abstraction Layer (HAL) insulates the kernel-mode driver from the bus. The Kernel-Mode Drivers Design Guide provides extensive general information about the design of kernel-mode drivers. Additionally, specific information about designing kernel-mode multimedia drivers is provided in the chapters discussing the multimedia driver types. The chapters are listed in Types of Multimedia Devices.


Designing a User-Mode Multimedia Driver

This section provides the following topics about designing a user-mode multimedia driver:

  • User-Mode Driver Entry Points

  • Standard Driver Messages

  • Customized Driver Messages

  • Driver Instances

  • Character Strings

For additional information about designing user-mode drivers, see the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.


User-Mode Driver Entry Points

Applications access user-mode driver capabilities by passing messages to standard driver entry points.

All Win32-based user-mode drivers must export an entry point function called DriverProc. This function receives a set of messages known as the standard driver messages. Generally, a user-mode driver’s DriverProc function also recognizes additional customized driver messages.

A default message handler, called DefDriverProc, is provided within winmm.dll. Every user-mode driver should call DefDriverProc from within its DriverProc function, if it receives an unrecognized or unsupported message.

User-mode audio device drivers provide additional entry point functions.

All driver entry points must be exported in a module definition (.def) file.


Standard Driver Messages

Every user-mode driver must export a DriverProc function. Each DriverProc function must recognize a set of standard driver messages, which are defined in mmsystem.h. A driver receives the following standard messages, in the order listed, when an application uses the driver to perform input or output operations.
Message | Operation Performed by Driver
DRV_LOAD | Performs post-load operations.
DRV_ENABLE | No operations performed under Windows NT.
DRV_OPEN | Opens a driver instance.
DRV_CLOSE | Closes a driver instance.
DRV_DISABLE | No operations performed under Windows NT.
DRV_FREE | Performs pre-unload operations.

Additionally, a driver can receive the following standard messages, which are typically sent from a Control Panel application during installation and configuration operations.
Message | Operation Performed by Driver
DRV_INSTALL | Installs the kernel-mode driver.
DRV_PNPINSTALL | Installs a kernel-mode driver, using Plug and Play configuration information.
DRV_CONFIGURE | Obtains configuration parameters.
DRV_QUERYCONFIGURE | Indicates whether configuration parameters can be modified.

Although applications can send standard driver messages directly by calling SendDriverMessage, described in the Win32 SDK, typically they do not. Instead, they call functions provided by higher level multimedia APIs. These APIs in turn act as clients to the user-mode drivers and send messages by:

  • Calling SendDriverMessage, to directly send any of the standard messages.

  • Calling OpenDriver, described in the Win32 SDK. This function calls SendDriverMessage to send DRV_LOAD and DRV_ENABLE messages, if the driver has not been previously loaded, and then to send DRV_OPEN.

  • Calling CloseDriver, described in the Win32 SDK. This function calls SendDriverMessage to send DRV_CLOSE and, if there are no other open instances of the driver, to also send DRV_DISABLE and DRV_FREE.

Besides supporting the standard driver messages, a user-mode multimedia driver’s DriverProc function generally also supports a set of customized driver messages.


Customized Driver Messages

Besides supporting the standard driver messages, a user-mode driver’s DriverProc function often supports additional customized messages that are specific to each multimedia device type. (User-mode audio device drivers support customized messages by providing additional entry point functions.)

Although applications can send customized driver messages directly by calling SendDriverMessage, described in the Win32 SDK, typically they do not. Instead, they call functions provided by higher level multimedia APIs. These APIs in turn act as clients to the user-mode drivers and send customized messages by calling SendDriverMessage.

Customized driver messages are described in the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.


Driver Instances

The Win32 multimedia APIs and the multimedia dynamic-link libraries allow multiple clients to simultaneously open a user-mode driver. You can choose whether to allow multiple open driver instances, as follows:

  • If you want to allow multiple open instances, your user-mode driver should define a linked list of dynamically allocated structures. All instance data should be stored in this list, and you should not use any static or global variables. When the driver’s DriverProc function receives a DRV_OPEN message, it should:

    1. Allocate memory space for a structure instance.

    2. Add the structure instance to the linked list.

    3. Store instance data in the new list entry.

    4. Specify the entry’s number or address as the return value for the DriverProc function.

    Subsequent calls to DriverProc will include the list entry’s identifier as its dwDriverID argument. The sample audio device drivers use this technique, although they use the customized audio driver entry points and messages instead of DriverProc and DRV_OPEN.

    If a user-mode driver allows multiple instances, the kernel-mode driver is usually responsible for rejecting conflicting requests for access to the hardware.

  • If you do not want to allow multiple open instances, your driver can set a flag the first time it receives a DRV_OPEN message. When subsequent DRV_OPEN messages are received, the driver can provide an error return value for DriverProc until a DRV_CLOSE message is received, at which point it can clear the flag. The sample video capture device drivers use this technique.


Character Strings

Under Windows NT, character strings consist of Unicode characters. All strings passed between clients and drivers are Unicode strings.

The Win32 SDK provides numerous articles on defining and using Unicode strings.


Designing a Kernel-Mode Multimedia Driver

This section provides the following topic for designing a kernel-mode multimedia driver:

  • Kernel-Mode Driver Entry Points

For additional information about designing kernel-mode drivers, see the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.


Kernel-Mode Driver Entry Points

All kernel-mode multimedia drivers must export a DriverEntry function. They must also fill in the Windows NT device object’s dispatch table.

DriverEntry in Kernel-Mode Multimedia Drivers

All kernel-mode multimedia drivers must export a DriverEntry function, which is the first function executed after the driver is loaded. (The driver is loaded as a result of being installed by a user-mode driver. See Installing a Kernel-Mode Multimedia Driver.)

DriverEntry for multimedia drivers should perform such installation-time operations as obtaining hardware configuration parameter values from the registry, reserving system resources, and verifying that device hardware is accessible.

For more information about the DriverEntry function, see the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.

The Driver Object’s Dispatch Table

Before Windows NT calls a kernel-mode driver’s DriverEntry function, it creates a driver object. It then passes the driver object’s address as an input argument to DriverEntry for multimedia drivers. The kernel-mode driver is responsible for filling in the driver object’s MajorFunction member, which is a dispatch table representing the various I/O control codes that a user-mode driver can send to a kernel-mode driver. For more information about driver objects, which are defined by the DRIVER_OBJECT structure, see the Kernel-Mode Drivers Reference.

Kernel-mode multimedia drivers do not always fill in the dispatch table directly. Sometimes, multimedia driver support libraries take care of this operation. The support libraries are described in the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.


Building Multimedia Drivers

To build a multimedia driver, you should use the BUILD utility, which is described in the Programmer’s Guide. For more information, see the following topics:

  • Building a User-Mode Multimedia Driver

  • Building a Kernel-Mode Multimedia Driver


Building a User-Mode Multimedia Driver

To build user-mode drivers, refer to the description of the BUILD utility in the Programmer’s Guide. You must provide a file named makefile and a file named sources, and place them in the directory path containing your source files. Refer to the makefile and sources files provided with the source code for the sample drivers in this DDK.

All user-mode multimedia drivers must be linked with winmm.dll. Additional libraries are provided for the various device and driver types and are referred to in the chapters describing each driver type. The chapters are listed in Types of Multimedia Devices.

All user-mode multimedia drivers must include the mmystem.h header file. Additional header files are provided for the various device and driver types and are referred to in the chapters describing each driver type.

Because user-mode drivers are dynamic-link libraries, their file extension should generally be .dll. (This is not a requirement and, for example, Audio Compression Manager drivers have an extension of .acm.) If you provide both a 16-bit and a 32-bit version of your driver, append 32 to the file name for the 32-bit version.


Building a Kernel-Mode Multimedia Driver

To build kernel-mode drivers, refer to the description of the BUILD utility in the Programmer’s Guide. You must provide a file named makefile and a file named sources, and place them in the directory path containing your source files. Refer to the makefile and sources files provided with the source code for the sample drivers in this DDK.

Libraries and header files are provided for the various device and driver types and are referred to in the chapters describing each driver type. The chapters are listed in Types of Multimedia Devices.

Kernel-mode multimedia drivers for Windows NT must have a file extension of .sys.


Installing Multimedia Drivers

Driver installation procedures are described in the Windows NT DDK Programmer’s Guide. For multimedia drivers not supplied by Microsoft, installation is accomplished by running the Multimedia applet in the Control Panel. This section provides the following topics:

  • Installing a User-Mode Multimedia Driver

  • Installing a Kernel-Mode Multimedia Driver


Installing a User-Mode Multimedia Driver

Windows NT users with Administrator privilege can install non-Microsoft multimedia drivers by running the Control Panel’s Multimedia applet. The Multimedia applet reads oemsetup.inf files to determine which files to install. To allow the Multimedia applet to install your user-mode driver, you must provide an oemsetup.inf file identifying the user-mode driver. For more information, see Using oemsetup.inf Files with Multimedia Drivers.


Using oemsetup.inf Files with Multimedia Drivers

The driver’s installation medium must include an oemsetup.inf file. A general discussion of oemsetup.inf files is provided in the Programmer’s Guide. When you use the Multimedia applet in the Control Panel to install a multimedia driver, the Multimedia applet reads the oemsetup.inf file in order to determine which driver files to install. To understand how oemsetup.inf files must be constructed for multimedia drivers, look at the following example:

[Source Media Descriptions]
    1  = "Sound Blaster Driver"  , TAGFILE = disk1

[Installable.Drivers]
soundblaster = 1:sndblst.dll, "wave,MIDI,aux,mixer", "Creative Labs Sound Blaster 1.X, Pro, 16" ,,,

[soundblaster]
1:sndblst.sys

The [Source Media Descriptions] section identifies the load medium, as described in the Programmer’s Guide.

Next is the [Installable.Drivers] section, which is required for multimedia drivers installed using the Multimedia Applet. Each line under this section is a driver profile. Each driver profile describes a user-mode driver. A driver profile consists of six fields, separated by commas. The first three fields are:

  1. Source medium and user-mode driver filename

  2. List of supported device types, also known as aliases

  3. Driver description

In the example, the “1:sndblst.dll” field indicates that the driver’s file name is sndblst.dll, located on source medium 1. This file will be copied to the system’s \system32 subdirectory.

The next field lists all of the device types (aliases) supported by the driver. The Multimedia applet creates a registry entry for each device type. It would create the following entries for the example .inf file:

wave : REG_SZ : sndblst.dll
MIDI : REG_SZ : sndblst.dll
aux : REG_SZ : sndblst.dll
mixer : REG_SZ : sndblst.dll

If the alias already exists in the registry path, a number is appended to the alias string. The entries are placed in the registry path HKEY_LOCAL_MACHINE \SOFTWARE \Microsoft \Windows NT \CurrentVersion \Drivers32.

The third field is the driver’s description, which is the description that is displayed by the Multimedia Applet. The remaining fields are not used.

The [soundblaster] section name matches the name given to the profile entry. This section lists additional files that need to be copied. Include your kernel-mode driver in this section. The Multimedia Applet copies files with an extension of .sys into the \system32\drivers subdirectory, and copies all other files into the \system32 subdirectory.

After the files have been copied, the Multimedia applet sends DRV_LOAD, DRV_ENABLE, DRV_OPEN, DRV_INSTALL, DRV_QUERYCONFIGURE, DRV_CONFIGURE, DRV_CLOSE, DRV_DISABLE, and DRV_FREE messages to the user-mode driver, in that order.

Sample oemsetup.inf files are included with the source code provided with this DDK.


Installing a Kernel-Mode Multimedia Driver

If you list your kernel-mode driver in your oemsetup.inf file, the Multimedia applet installs it for you by calling the Win32 CreateService function. For more information about oemsetup.inf files, see Using oemsetup.inf Files with Multimedia Drivers.

Additionally, the user-mode driver can install the kernel-mode driver by either calling CreateService, or by calling one of the functions in the driver support libraries. These libraries are described in the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.


Configuring Multimedia Drivers

Drivers typically store configuration information in the Windows NT Registry. Multimedia drivers can optionally require two types of configuration parameters ¾ hardware configuration parameters and user configuration parameters.

Hardware configuration parameters represent values that a kernel-mode driver needs in order to access device hardware. For information about storing these parameters, see Storing Hardware Configuration Parameters.

User configuration parameters represent values that a user-mode or kernel-mode driver needs in order to support user-controllable runtime options. For information about storing these parameters, see Storing User Configuration Parameters.


Storing Hardware Configuration Parameters

Kernel-mode drivers generally require hardware configuration parameters, such a device’s interrupt number and DMA channel, that must be supplied by a system administrator. The user-mode driver obtains values for these parameters by displaying a dialog box in response to a DRV_CONFIGURE message. The user-mode driver stores the parameter values in the Windows NT Registry, where they are accessible to the kernel-mode driver.

Because kernel-mode drivers are treated as services under Windows NT, their parameters are stored under the registry’s \Services key. The path to the \Services key is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services.

Under \Services, there is a subkey for each installed kernel-mode driver. For multimedia drivers, the registry structure is as follows:

    HKEY_LOCAL_MACHINE
        SYSTEM
            CurrentControlSet
                Services
                    DriverName
                            Type
                            Group
                            ErrorControl
                            Start
                            Tag
                         Parameters
                            Device0
                                    Interrupt
                                    Port
                                    DMA Channel
                                    (Etc.)
                            Device1
                                    Interrupt
                                    Port
                                    DMA Channel
                                    (Etc.)

The Service Control Manager creates and maintains DriverName, along with the Type, Group, ErrorControl, Start, and Tag subkeys. User-mode multimedia drivers provide the Parameters and underlying subkeys, as necessary. Use the Interrupt, Port, and DMA Channel value names to store interrupt levels, port addresses, and DMA channels.

Drivers can create and modify registry key values by calling Win32 functions, or by using functions provided in the driver support libraries. The support libraries are described in the chapters discussing the various driver types. The chapters are listed in Types of Multimedia Devices.


Storing User Configuration Parameters

Some multimedia drivers allow each user to specify configuration options. For example, a video capture driver might allow each user to specify video display color values. User-mode drivers should obtain user configuration parameter values and store them in the registry under a subkey of the registry path HKEY_CURRENT_USER\Software\Microsoft\Multimedia. For more information about storing user configuration parameters, see the chapters discussing the various driver types. Those chapters are listed in Types of Multimedia Devices.


Multimedia Driver Reference

This section provides the following topics:

  • Messages, Multimedia Drivers

  • Structures, Multimedia Drivers

  • Functions, Multimedia Drivers

The messages, structures, and functions described in this section are common to multimedia drivers for all device types. For information about additional messages, structures, and functions for specific device types, see the chapters discussing the various driver types. Those chapters are listed in Types of Multimedia Devices.


Messages, Multimedia Drivers

This section describes the standard driver messages that are received by user-mode multimedia drivers. The messages are listed in alphabetic order, and are defined in mmsystem.h or mmddk.h.


DRV_CLOSE

The DRV_CLOSE message requests a user-mode multimedia driver to close the specified driver instance.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_CLOSE
lParam1
Contains the lParam1 parameter from the CloseDriver function. Currently not used. Set to zero.
lParam2
Contains the lParam2 parameter from the CloseDriver function. Currently not used. Set to zero.
Return Value

The driver should return a nonzero value if the operation succeeds. Otherwise it should return zero.

Comments

The DRV_CLOSE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values.

When a driver receives a DRV_CLOSE message, it should close the specified driver instance. Other driver instances might still be open.


DRV_CONFIGURE

The DRV_CONFIGURE message requests a user-mode multimedia driver to display a dialog box that allows administrators to modify the driver’s hardware configuration parameters.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_CONFIGURE
lParam1
Handle to the parent window the driver should use when creating a configuration dialog box.
lParam2
If not null, specifies the address of a DRVCONFIGINFO structure.
Return Value

The driver should provide one of the following return values:
DRVCNF_CANCEL | The user canceled the configuration dialog box.
DRVCNF_OK | The configuration operation was successful.
DRVCNF_RESTART | The configuration operation was successful. The new configuration does not take effect until Windows NT is restarted.

Comments

The DRV_CONFIGURE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values. Typically, this message is sent by the Control Panel’s Multimedia applet.

Drivers display a dialog box to obtain configuration parameters from the system administrator. Your driver must confirm that the client has Administrator privilege.

Configuration parameters typically include information the kernel-mode driver needs in order to access the hardware, such as an interrupt number, DMA channel, and port address. After obtaining this information from the dialog box, the user-mode driver stores it in the registry, as described in Storing Hardware Configuration Parameters, where it is accessible to the kernel-mode driver.

Drivers receive a DRV_OPEN message before receiving DRV_CONFIGURE.

Some drivers combine installation and configuration operations into one step and perform them upon receipt of either a DRV_INSTALL or a DRV_CONFIGURE message.

If the driver returns DRVCNF_RESTART, you can assume that the caller will display a message telling the administrator to restart Windows NT.

You can assume that the Control Panel’s Multimedia applet will not install a driver that cannot be configured. When installing a driver, the Multimedia applet sends a DRV_CONFIGURE message immediately after sending DRV_INSTALL. If the driver returns DRVCNF_CANCEL in response to DRV_CONFIGURE, the driver is not installed.


DRV_DISABLE

The DRV_DISABLE message causes a Windows NT user-mode multimedia driver to just return a nonzero value (see the following Comments section).

Parameters
dwDriverID
Driver instance identifier. This value is zero for the DRV_DISABLE message.
hDriver
Driver handle.
uMsg
DRV_DISABLE
lParam1
Not used. Set to zero.
lParam2
Not used. Set to zero.
Return Value

The driver should return a nonzero value.

Comments

The DRV_DISABLE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values.

Windows 95 drivers respond to the DRV_DISABLE message by disabling hardware. Because hardware access under Windows NT is controlled by kernel-mode drivers, Windows NT user-mode drivers do not perform any operations when they receive a DRV_DISABLE message.


DRV_ENABLE

The DRV_ENABLE message causes a Windows NT user-mode multimedia driver to just return a nonzero value (see Comments section below).

Parameters
dwDriverID
Driver instance identifier. This value is zero for the DRV_ENABLE message.
hDriver
Driver handle.
uMsg
DRV_ENABLE
lParam1
Not used. Set to zero.
lParam2
Not used. Set to zero.
Return Value

The driver should return a nonzero value.

Comments

The DRV_ENABLE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values.

Windows 95 drivers respond to the DRV_ENABLE message by enabling hardware. Because hardware access under Windows NT is controlled by kernel-mode drivers, Windows NT user-mode drivers do not perform any operations when they receive a DRV_ENABLE message.


DRV_FREE

The DRV_FREE message requests a user-mode multimedia driver to perform pre-unload operations.

Parameters
dwDriverID
Driver instance identifier. This value is zero for the DRV_FREE message.
hDriver
Driver handle.
uMsg
DRV_FREE
lParam1
Not used. Set to zero.
lParam2
Not used. Set to zero.
Return Value

The driver should return a nonzero value if the operation succeeds. Otherwise it should return zero.

Comments

The DRV_FREE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values.

User-mode drivers receive the DRV_FREE message just prior to being freed (unloaded) from memory. It is the last message a driver receives before being unloaded. The driver should release acquired system resources.

Drivers receive DRV_CLOSE and DRV_DISABLE messages before receiving DRV_FREE.


DRV_INSTALL

The DRV_INSTALL message requests a user-mode multimedia driver to allow a system administrator to perform installation operations.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_INSTALL
lParam1
Specifies the handle to the parent window the driver should use when creating a configuration dialog box.
lParam2
If not null, specifies the address of a DRVCONFIGINFO structure.
Return Value

Drivers provide one of the following return values:
DRVCNF_CANCEL | The installation operation should be canceled.
DRVCNF_OK | The installation operation was successful.
DRVCNF_RESTART | The installation operation was successful. The installation does not take effect until Windows NT is restarted.

Comments

The DRV_INSTALL message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values. Typically, this message is sent by the Control Panel’s Multimedia applet.

Drivers receive DRV_LOAD, DRV_ENABLE, and DRV_OPEN messages before receiving DRV_INSTALL.

Installation operations include installing a kernel-mode driver, and creating Windows NT Registry keys along with their default values. Windows NT only allows users with Administrator privilege to install kernel-mode drivers, as discussed in Installing a Kernel-Mode Multimedia Driver.

Some drivers combine installation and configuration operations into one step and perform them upon receipt of either a DRV_INSTALL or a DRV_CONFIGURE message.

If the driver returns DRVCNF_RESTART, you can assume that the caller will display a message telling the administrator to restart Windows NT.

For more information about driver installation, see Installing Multimedia Drivers.


DRV_LOAD

The DRV_LOAD message requests a user-mode multimedia driver to perform post-load operations.

Parameters
dwDriverID
Specifies the driver instance identifier. This value is zero for the DRV_LOAD message.
hDriver
Driver handle.
uMsg
DRV_LOAD
lParam1
Not used. Set to zero.
lParam2
Not used. Set to zero.
Return Value

The driver should return a nonzero value if the operation succeeds. Otherwise it should return zero, which causes winmm.dll to unload the driver.

Comments

The DRV_LOAD message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values.

A user mode driver is not loaded until the first time a client attempts to open a driver instance. Immediately after being loaded, a driver receives a DRV_LOAD message so it can handle load-time activities. These activities might include initializing libraries or loading additional resources.

After a user-mode driver instance has been opened, the driver remains loaded until all instances have been closed.

See Also

DRV_FREE


DRV_OPEN

The DRV_OPEN message requests a user-mode multimedia driver to open a driver instance.

Parameters
dwDriverID
Specifies the driver instance identifier. This value is zero for the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_OPEN
lParam1
Address of a string containing any characters following the driver’s file name in the Windows NT Registry.
lParam2
Typically an interface-specific data structure. For example, MCI drivers received the address of an MCI data structure.
Return Value

The driver should return a nonzero value if the operation succeeds. Otherwise it should return zero. (See the following Comments section.)

Comments

The DRV_OPEN message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values.

The nonzero return value is saved by winmm.dll and used as the dwDriverID input value for subsequent calls to DriverProc. Because multiple driver instances can be opened simultaneously, drivers typically use this value to identify driver instance data. For example, the value could be an index into a driver-defined array, where each array element is a local, dynamically allocated structure containing driver instance data. When the driver receives subsequent calls to DriverProc for the open instance, it can use the dwDriverID value to determine which set of instance data to use.

Drivers receive DRV_LOAD and DRV_ENABLE messages before receiving DRV_OPEN.

Audio device drivers receive customized driver messages for opening driver instances. These drivers can ignore DRV_OPEN.


DRV_PNPINSTALL

The DRV_PNPINSTALL message requests a user-mode multimedia driver to allow a system administrator to perform installation operations, using Plug and Play configuration information.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_PNPINSTALL
lParam1
Handle to a device information set. The handle type is HDEVINFO, which is defined in setupapi.h.
lParam2
Pointer to an SP_DEVINFO_DATA structure.
Return Value

Drivers provide one of the following return values:
DRVCNF_CANCEL | The installation operation should be canceled.
DRVCNF_OK | The installation operation was successful.
DRVCNF_RESTART | The installation operation was successful. The installation does not take effect until Windows NT is restarted.

Comments

The DRV_PNPINSTALL message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values. Typically, this message is sent by the Media Class Installer, which is included in the Control Panel’s Multimedia applet.

If the system provides Plug and Play capabilities, the driver receives this message instead of DRV_INSTALL. The driver uses the received lParam1 and lParam2 values as inputs to the SetupDi-prefixed device installation functions provided by setupapi.dll. For descriptions of the device installation functions, see the Programmer’s Guide.

For more information about responding to the DRV_PNPINSTALL and DRV_CONFIGURE messages on a Windows NT system providing Plug and Play capabilities, see the user-mode driver source code for the Creative Labs Sound Blaster, which is one of the sample audio drivers.


DRV_QUERYCONFIGURE

The DRV_QUERYCONFIGURE message requests a user-mode multimedia driver to return a value indicating whether it provides modifiable operating parameters.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_QUERYCONFIGURE
lParam1
Not used. Set to zero.
lParam2
Not used. Set to zero.
Return Value

If the driver supports modifiable parameters, it should return a nonzero value. Otherwise it should return zero.

Comments

The DRV_QUERYCONFIGURE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values. Typically, this message is sent by the Control Panel’s Multimedia applet.

Drivers receive a DRV_OPEN message before receiving DRV_QUERYCONFIGURE.

If a driver provides modifiable parameters, it displays a dialog box when it receives a DRV_CONFIGURE command.


DRV_REMOVE

The DRV_REMOVE message requests a user-mode multimedia driver to allow a system administrator to perform removal operations.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
DRV_REMOVE
lParam1
Not used. Set to zero.
lParam2
Not used. Set to zero.
Return Value

Drivers provide one of the following return values:
DRVCNF_CANCEL | The removal operation failed.
DRVCNF_OK | The removal operation was successful.
DRVCNF_RESTART | The removal operation was successful. The removal does not take effect until Windows NT is restarted.

Comments

The DRV_REMOVE message is one of the standard driver messages. A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameter values. Typically, this message is sent by the Control Panel’s Multimedia applet.

Removal is the opposite of installation. Removal operations might include removing a kernel-mode driver and deleting Windows NT Registry keys that were created during installation.

Drivers receive a DRV_OPEN message before receiving DRV_REMOVE.

If the driver returns DRVCNF_RESTART, you can assume that the caller will display a message telling the administrator to restart Windows NT.

See Also

DRV_INSTALL


Structures, Multimedia Drivers

This section describes the structures used by user-mode multimedia drivers.


DRVCONFIGINFO

typedef struct tagDRVCONFIGINFO {
    DWORD dwDCISize;
    LPCWSTR lpszDCISectionName;
    LPCWSTR lpszDCIAliasName;
} DRVCONFIGINFO;

The DRVCONFIGINFO structure, defined in mmsystem.h, is used as an input argument with the DRV_INSTALL and DRV_CONFIGURE messages.

Members
dwDCISize
Specifies the size of the DRVCONFIGINFO structure.
lpszDCISectionName
Specifies a registry key name. This is always Drivers32.
lpszDCIAliasName
Specifies a driver alias.
Comments

For information about the \Drivers32 registry key name and driver aliases, see Using oemsetup.inf Files with Multimedia Drivers.

The alias is used in the registry as a value name. The value assigned to this value name is the user-mode driver’s file name.


Functions, Multimedia Drivers

This section describes functions that are either called by or exported by all multimedia drivers. The functions are listed in alphabetic order.


DefDriverProc

LRESULT WINAPI
DefDriverProc (
DWORD dwDriverID,
HDRVR hDriver,
UINT uMsg,
LPARAM lParam1,
LPARAM lParam2
);

The DefDriverProc function is called by user-mode multimedia drivers to handle messages not processed by the driver’s DriverProc function. The function is defined in winmm.dll.

Parameters
dwDriverID
Value passed to the driver as the dwDriverID argument to DriverProc.
hDriver
Value passed to the driver as the hDriver argument to DriverProc.
uMsg
Value passed to the driver as the uMsg argument to DriverProc.
lParam1
Value passed to the driver as the lParam1 argument to DriverProc.
lParam2
Value passed to the driver as the lParam2 argument to DriverProc.
Return Value

The DefDriverProc function returns a value that is based on the received message. Return values are shown in the following table.
Message | Return Value
DRV_LOAD | 1
DRV_FREE | 1
DRV_ENABLE | 1
DRV_DISABLE | 1
DRV_INSTALL | DRV_OK
DRV_REMOVE | DRV_OK
All other messages. | 0

Comments

A user-mode driver calls DefDriverProc by passing it the same arguments the driver received as input to its DriverProc function.

Typically, a user-mode driver’s DriverProc function assigns supported uMsg values to C-language case statement arguments, and associates DefDriverProc with a default statement. Refer to the sample drivers for examples. The value returned from DefDriverProc should be used as the return value for DriverProc.


DriverCallback

BOOL APIENTRY
DriverCallback(
DWORD dwCallBack,
DWORD dwFlags,
HDRVR hDriver,
DWORD dwMsg,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2
);

The DriverCallback function is used by user-mode drivers to send a callback message to a client application.

Parameters
dwCallBack
The target for the callback message. Can be one of:
  • An event handle

  • A callback function address

  • A thread identifier

  • A window handle

The application specifies the type of callback target when opening a driver instance. The dwFlags parameter indicates the type of value stored in dwCallback.

dwFlags
One of the following flags, defined in mmddk.h, indicating the type of callback target:

DCB_EVENT

Equivalent to HIWORD(CALLBACK_EVENT). Indicates dwCallback contains an event handle. Code in winmm.dll calls the Win32 SetEvent function.

DCB_FUNCTION

Equivalent to HIWORD(CALLBACK_FUNCTION). Indicates dwCallback contains a function address. Code in winmm.dll calls the function.

DCB_TASK

Equivalent to HIWORD(CALLBACK_THREAD). Indicates dwCallback contains a thread identifer. Code in winmm.dll calls the Win32 PostThreadMessage function to post a WM_USER message.

DCB_WINDOW

Equivalent to HIWORD(CALLBACK_WINDOW). Indicates dwCallback contains a window handle. Code in winmm.dll calls the Win32 PostMessage function.

The CALLBACK_EVENT, CALLBACK_FUNCTION, CALLBACK_THREAD, and CALLBACK_WINDOW flags, referred to in the preceding table, are longword values used by applications when calling Win32 API functions that open multimedia drivers, such as midiOutOpen, waveOutOpen, or videoStreamInit.

hDriver
The driver handle that the driver received with the DRV_OPEN message.
dwMsg
A message to send to the application. Ignored if dwFlags is DCB_EVENT or DCB_TASK. The messages that can be sent are unique for each type of multimedia device and are listed in the chapters for each device type.
dwInstance
An application-supplied value to be passed to a callback function. Ignored if dwFlags is DCB_EVENT, DCB_TASK, or DCB_WINDOW. Applications specify this value when calling Win32 API functions that open multimedia drivers, such as midiOutOpen, waveOutOpen, or videoStreamInit.
dwParam1
A message-dependent parameter. Ignored if dwFlags is DCB_EVENT or DCB_TASK.
dwParam2
A message-dependent parameter. Ignored if dwFlags is DCB_EVENT, DCB_TASK, or DCB_WINDOW.
Return Value

Returns FALSE if dwCallback is NULL, if dwFlags is invalid, or if the message cannot be queued. Otherwise returns TRUE.

Comments

User-mode drivers call the DriverCallback function to deliver callback messages to applications that have requested them. Applications request delivery of callback messages by specifying a callback target when they open a driver instance. Win32 API functions that allow applications to specify a callback target include midiOutOpen, waveOutOpen, videoStreamInit, and others.


DriverEntry for Multimedia Drivers

NTSTATUS
DriverEntry(
PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING RegistryPathName
);

The DriverEntry function is a kernel-mode driver’s entry point. For kernel-mode multimedia drivers, the DriverEntry function’s two parameters are defined as shown. (Other types of kernel-mode drivers might define the function’s parameters differently.)

Parameters
pDriverObject
Pointer to a DRIVER_OBJECT structure.
RegistryPathName
Pointer to the registry path that leads to the kernel-mode driver’s subkey. See the following Comments section.
Return Value

Returns STATUS_SUCCESS if initialization operations succeed. Otherwise the function returns one of the error codes defined in ntstatus.h that fail the NT_SUCCESS macro.

Comments

Windows NT calls a kernel-mode driver’s DriverEntry function immediately after it has loaded the driver.

The registry path pointed to by RegistryPathName is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\drivername, where drivername is the name that the user-mode driver specified when it installed the kernel-mode driver. Drivers use this path for storing hardware configuration parameters.

For more information about DriverEntry, see DriverEntry in Kernel-Mode Multimedia Drivers.


DriverProc

LRESULT
DriverProc (
DWORD dwDriverID,
HDRVR hDriver,
UINT uMsg,
LPARAM lParam1,
LPARAM lParam2
);

The DriverProc function is a user-mode driver’s entry point.

Parameters
dwDriverID
Instance identifier. This value is message-dependent.
hDriver
Driver handle.
uMsg
One of the standard driver messages or one of the customized driver messages.
lParam1
Message-dependent parameter.
lParam2
Message-dependent parameter.
Return Value

DriverProc returns a value that is dependent upon the message.

Comments

All user-mode multimedia drivers must define a DriverProc function. The DriverProc function name must be exported in a module definition (.def) file.

The meanings of the dwDriverID, lParam1, and lParam2 parameters are dependent upon the message that the driver receives as the uMsg parameter value. Refer to the description of each message to determine the meanings of these parameters.

For more information about DriverProc, see User-Mode Driver Entry Points.


MCI Drivers

The following topics explain how to write a Windows NTâ multimedia driver that supports the Media Control Interface (MCI):

  • Introduction to MCI

  • Introduction to MCI Drivers

  • Designing an MCI Driver

  • Creating Customized MCI Commands

  • MCI Reference

For a general discussion of multimedia drivers, see Introduction to Multimedia Drivers.


Introduction to MCI

The Media Control Interface (MCI) provides a convenient, common API that client applications can use for controlling all multimedia hardware. MCI is made up of the following components:

  • An API available to applications, consisting of callable functions plus a window class and associated messages.

  • MCI command parsing and dispatching routines.

  • A set of MCI drivers.

Under Windows NT, the command parsing and dispatching functions reside within winmm.dll. Client applications access these functions by calling mci-prefixed API functions provided by winmm.dll. Applications can also use the MCIWnd window class and its associated messages, which provide a simpler, higher-level interface than the mci-prefixed functions. (The MCI functions and window messages are described in the Win32 SDK.)

Usually when an application calls an MCI function, winmm.dll calls an MCI driver to perform the specified operation. Applications primarily call the mciSendCommand and mciSendString functions. Even when an application uses the MCIWnd window class, it is actually making indirect calls to these two functions. The mciSendCommand and mciSendString functions are described in the Win32 SDK.

When an application calls mciSendCommand, it specifies a device and an MCI command message, which is simply a predefined constant value, such as MCI_PLAY. The mciSendCommand function calls the MCI driver for the specified device and passes the command message. The driver processes the command and returns.

When an application calls mciSendString, it specifies an MCI command string, which is a text string describing the command and device, such as “play videodisc1”. In this case, an MCI command parser searches a set of MCI command tables to verify that the command is valid for the specified device. If the command is valid, the parser converts the command string to a command message, and the message is passed to the appropriate MCI driver. The driver processes the command and returns.

A unique data structure is defined for each MCI command. These MCI data structures have a standardized format and are used for passing information to and from MCI drivers. Applications calling mciSendCommand reference the structures directly. Applications calling mciSendString do not reference the structures. Instead, winmm.dll converts input strings into structure member values. Before the call returns, winmm.dll extracts output values from the structure and converts them to output strings.


Introduction to MCI Drivers

MCI drivers are installable, user-mode device drivers that process MCI commands. Like all other user-mode drivers under Windows NT, MCI drivers are DLLs. For more information about user-mode drivers under Windows NT, see Introduction to Multimedia Drivers.

MCI drivers can initiate hardware operations by either of two methods:

  1. They can call kernel-mode drivers using Win32 API functions such as CreateFile, ReadFile, and WriteFile, as described in Introduction to Multimedia Drivers.

  2. They can call other user-mode drivers, using Win32 multimedia functions described in the Win32 SDK. For example, a driver implementing an MCI interface for MIDI sequencers can call the Win32 MIDI functions, causing a user-mode audio driver to be accessed.

Like all other types of installable drivers, MCI drivers must define a DriverProc function. This function must handle the standard driver messages. Additionally, it must handle MCI messages. For more information, see DriverProc in MCI Drivers.

The following topics provide more introductory information about MCI drivers:

  • MCI Command Tables

  • MCI Command Types

  • MCI Device Types

  • Simple and Compound Devices

  • Sample MCI Driver


MCI Command Tables

Client applications can specify MCI commands as either strings or messages, but an MCI driver’s DriverProc function only accepts messages. Therefore, it is necessary to provide command tables, which the MCI command parser uses to translate command strings into messages. Furthermore, most commands can accept modifiers, so the MCI parser translates string modifiers into message arguments that MCI drivers can read.

There are three types of command tables:

  • Core command table

    There is one core command table. It contains the commands that all MCI drivers must support. These include the required and basic MCI command types. The core command table is included in winmm.dll.

  • Device-type command tables

    There are command tables for several MCI device types. Each of these tables contains commands that are common to a particular multimedia device type, such as “videodisc” or “waveaudio”. Several device-type tables are included in winmm.dll. Others can be included as separate files, with a filename extension of .mci.

    · Device-specific command tables

    There can be any number of device-specific command tables. If a particular device requires its own set of commands, a unique command table must be provided. These tables can exist as separate files, with a filename extension of .mci. They can also be defined within a particular driver’s resource file and linked to the driver.

Command tables are compiled by the Microsoft Windows Resource Compiler, which is described in the Win32 SDK.

Device-type command tables take precedence over the core command table. Likewise, device-specific command tables take precedence over device-type command tables. And finally, device-specific tables that reside in separate files take precedence over those that are linked to drivers. This scheme allows the core commands and device-type commands to be modified. In fact, it is common for a device-type or a device-specific table to redefine a core command in order to provide an expanded set of command modifiers.

For details about the contents of command tables and their associated data structures, see Creating Customized MCI Commands.


MCI Command Types

There are four types of MCI commands:

  1. System commands

  2. Required commands

  3. Basic commands

  4. Extended commands

System commands are not passed to an MCI driver. They are processed within winmm.dll.

Required commands are defined in the core command table. MCI drivers must recognize and process all required commands.

Basic commands are also defined in the core command table. MCI drivers must recognize all basic commands. However, if a basic command is not relevant to a particular device, the driver can simply return MCI_UNSUPPORTED_FUNCTION for that command.

Extended commands are those that are included in the device-type or device-specific command tables. Extended commands can either be modifications of the basic and required commands, or they can be new commands. An MCI driver only needs to recognize extended commands that are relevant for its device.

The following table lists the system, required, and basic commands. Extended commands are described within the Win32 SDK.
Command Type | Command String | Command Message
System Commands | break | MCI_BREAK
 | sound | MCI_SOUND
 | sysinfo | MCI_SYSINFO
Required Commands | capability | MCI_GETDEVCAPS
 | close | MCI_CLOSE (Driver receives MCI_CLOSE_DRIVER.)
 | info | MCI_INFO
 | open | MCI_OPEN (Driver receives MCI_OPEN_DRIVER.)
 | status | MCI_STATUS
Basic Commands | load | MCI_LOAD
 | pause | MCI_PAUSE
 | play | MCI_PLAY
 | record | MCI_RECORD
 | resume | MCI_RESUME
 | save | MCI_SAVE
 | seek | MCI_SEEK
 | set | MCI_SET
 | stop | MCI_STOP

All MCI commands are described in the Win32 SDK. To find the definition of a command, use the Win32 SDK keyword index to search for the command message (for example, MCI_SYSINFO).


MCI Device Types

Devices with similar properties are grouped together in categories known as device types. All of the devices belonging to a particular type respond to a common set of MCI commands. Each set of commands is contained in a separate MCI command table.

The following table lists device types defined by Microsoft. The table includes both string names and constants. Constants are defined in mmsystem.h.
String | Constant
animation | MCI_DEVTYPE_ANIMATION
cdaudio | MCI_DEVTYPE_CD_AUDIO
dat | MCI_DEVTYPE_DAT
digitalvideo | MCI_DEVTYPE_DIGITAL_VIDEO
overlay | MCI_DEVTYPE_OVERLAY
scanner | MCI_DEVTYPE_SCANNER
sequencer | MCI_DEVTYPE_SEQUENCER
vcr | MCI_DEVTYPE_VCR
videodisc | MCI_DEVTYPE_VIDEODISC
waveaudio | MCI_DEVTYPE_WAVEFORM_AUDIO

An MCI driver assigns a device to its appropriate type when it receives a DRV_OPEN message. (See Opening an MCI Driver.) This tells winmm.dll which command table to use. The driver uses the MCI_DEVTYPE_OTHER type for devices not belonging to any of the predefined types.

To find which MCI commands a driver must support for a particular device type, see the Win32 SDK. The Win32 SDK lists the command set associated with each device type. (Use the keyword index to search for a device type string, such as “videodisc”.)


Simple and Compound Devices

For the purposes of MCI, devices are classified as either:

Simple devices
Simple devices do not require a data file for playback. Videodisc players and compact disc (CD) audio players are examples of simple devices.
Compound devices
Compound Devices require a data file for playback. MIDI sequencers and waveform audio players are examples of compound devices. The data file associated with a compound device is known as a device element. Examples of device elements are MIDI files and waveform files.


Sample MCI Driver

Source code for a sample MCI driver is included with this DDK. The MCIPIONR driver is the MCI device driver for the Pioneer 4200 videodisc player. Its source files can be found in \ddk\src\mmedia\mcipionr.


Designing an MCI Driver

This topic provides the following subtopics, which cover information you need to design an MCI driver:

  • DriverProc in MCI Drivers

  • Handling Standard Driver Messages

  • Handling the MCI_NOTIFY Flag

  • Handling the MCI_WAIT Flag

  • Handling the MCI_TEST Flag

  • Opening an MCI Driver

  • Sharing A Device

  • Storing Instance-Specific Information

  • Providing Device Information

  • Closing an MCI Driver

  • Guidelines for Handling MCI Commands

If you are defining customized command tables, also see Creating Customized MCI Commands.


DriverProc in MCI Drivers

Like all other types of installable drivers, MCI drivers must define a DriverProc entry point. This function must handle the standard driver messages. Additionally, it must handle MCI messages. Within winmm.dll, application calls to mciSendCommand and mciSendString become calls to DrvSendMessage, which is described in the Win32 SDK and is the standard method for calling a driver’s DriverProc function.

Like the standard messages, MCI messages are defined as constant values that can be used in a C-language case statement. These constants are defined in mmsystem.h.

This section also provides information on DriverProc parameters and DriverProc return values for MCI drivers.


DriverProc Parameters

The DriverProc function is defined as follows:

LRESULT WINAPI DriverProc (
DWORD dwDriverID,
HDRVR hDriver,
UINT uMsg,
LPARAM lParam1,
LPARAM lParam2
);

When winmm.dll passes MCI messages to DriverProc, the function parameters are used as follows:

dwDriverID
Contains the driver identifier created by MCI.
hDriver
Contains a handle to the device driver.
uMsg
Contains an MCI message constant value.
lParam1
Contains message-specific MCI flags.
lParam2
Points to a message-specific MCI data structure.

The lParam1 and lParam2 parameters generally are used to represent MCI command arguments. Some arguments can simply be represented as flags in lParam1. Other arguments are passed as members of a data structure pointed to by lParam2. In this latter case, lParam1 flags are used to indicate the presence of valid members within the structure pointed to by lParam2. This structure is also used for returning information to the application.

There is a unique lParam2 data structure for each MCI message. Additionally, driver developers can define customized structures for extended commands. All of the data structures provided by Microsoft are defined in mmsystem.h and described in the Win32 SDK. Refer to Creating New MCI Command Structures to learn how to create custom structures.


DriverProc Return Values

When DriverProc receives an MCI message, its double-word return value must be assigned as follows:

  • If no errors occur, return zero.

  • If an error occurs, return one of the MCI error return values in the low word. (If the error is device-specific, winmm.dll places the device ID in the high word.)

The MCI error return values are defined in mmsystem.h, and are prefixed with “MCIERR_”. For a definition of each MCI error return value, see the Win32 SDK. Note that an additional set of error return values is defined for mciSendString. These additional values are returned by winmm.dll, not by MCI drivers.

When DriverProc receives one of the standard driver messages, its return value must be zero if an error occurs. Note that this is opposite to the situation for MCI messages.


Handling Standard Driver Messages

Handling standard driver messages is described in Introduction to Multimedia Drivers. Also see this chapter’s sections entitled Opening an MCI Driver and Closing an MCI Driver, which discuss some of the standard messages.


Handling the MCI_NOTIFY Flag

All MCI command tables must include the MCI_NOTIFY flag for all commands. Applications use this flag to request notification when an operation has completed.

Normally when an MCI driver receives a command, it should initiate the associated operation and then return control to the calling application. For example, if an application sends an MCI_SEEK command, the driver should start the seek operation and immediately return. The application is not notified when the operation completes.

As an option, an application sending any MCI command can request to be notified when the command operation has completed. To do this, the application specifies the following arguments along with the command:

  • The MCI_NOTIFY flag. The driver receives this flag in the lParam1 argument to DriverProc.

  • A window handle. The driver receives this handle as the dwCallback member of any structure whose address is passed as the lParam2 argument to DriverProc.

When a driver receives an MCI command that includes the MCI_NOTIFY flag, the driver must start the specified operation and then return. When the operation completes, the driver must call mciDriverNotify in winmm.dll. The mciDriverNotify function enqueues an MM_MCINOTIFY message to the window specified in dwCallback. The application’s window procedure must check for and process the MM_MCINOTIFY message.


Handling the MCI_WAIT Flag

All MCI command tables must include the MCI_WAIT flag for all commands. Applications use this flag to request the driver to complete the operation before returning control to the application.

When an MCI driver receives a command, by default it should start the operation and then return control to the calling application. The driver should not wait for the operation to complete before returning. For example, if an application sends an MCI_SEEK command, the driver should start the seek operation and immediately return.

Optionally, an application sending any MCI command can request the driver to wait until the associated operation is complete before returning. The application makes this request by including the MCI_WAIT flag as a command argument.

The driver receives the MCI_WAIT flag in the lParam1 argument to DriverProc. If the flag is present, then the driver must initiate the requested operation and then wait for it to complete before returning.

Yielding

Since the waiting time can be potentially long, a user must be allowed to interrupt the operation. By default, MCI provides a yield routine that checks for a break key. The default break key is CTRL+BREAK. An application can change the break key by sending the MCI_BREAK command. It can also replace the default yield routine with a customized one by calling the mciSetYieldProc function in winmm.dll. The yield routine returns a nonzero value if the driver should terminate the current operation.

While the driver is waiting for the requested operation to complete, it must periodically call the mciDriverYield function in winmm.dll. This function calls the currently selected yield routine and returns its return value. If this value is nonzero, the driver must stop the operation.


Handling the MCI_TEST Flag

The MCI command tables for some device types, including MCI_DEVTYPE_VCR and MCI_DEVTYPE_DIGITAL_VIDEO types, provide support for the MCI_TEST flag. The driver receives the MCI_TEST flag in the lParam1 argument to DriverProc.

If an application includes this flag with a command, the driver does not initiate the specified operation. Instead, it determines if the operation is currently available. If the operation is available, the driver returns zero. Otherwise it returns MCIERR_NONAPPLICABLE_FUNCTION. For example, a VCR driver might not allow an MCI_INDEX command while a seek operation is in progress. (Of course, in such a case the driver should return MCIERR_NONAPPLICABLE_FUNCTION even if the MCI_TEST flag is not specified.)


Opening an MCI Driver

A client application using MCI opens a driver by sending an MCI_OPEN message. This message is intercepted by winmm.dll, which first loads the appropriate MCI driver into the application’s address space, and then sends it the following messages, in the order listed:

  1. DRV_LOAD

  2. DRV_ENABLE

  3. DRV_OPEN

  4. MCI_OPEN_DRIVER

Note that the driver does not receive the MCI_OPEN message. Also be aware that DRV_LOAD and DRV_ENABLE are sent only if the driver was not previously loaded.

For more information, see the following topics:

  • Handling DRV_LOAD

  • Handling DRV_ENABLE

  • Handling DRV_OPEN

  • Handling MCI_OPEN_DRIVER


Handling DRV_LOAD

If your driver provides a customized command table, load it by calling mciLoadCommandResource when DriverProc receives the DRV_LOAD message. For more information on handling DRV_LOAD, see Introduction to Multimedia Drivers.


Handling DRV_ENABLE

For information on handling DRV_ENABLE, see Introduction to Multimedia Drivers.


Handling DRV_OPEN

When winmm.dll sends the DRV_OPEN message, it sets the DriverProc parameters as follows:

dwDriverID
Zero.
hDriver
The driver’s handle.
uMsg
DRV_OPEN.
lParam1
Contains a pointer to a zero-terminated string. The string contains any characters that follow the filename in the system registry.
lParam2
Pointer to an MCI_OPEN_DRIVER_PARMS structure.

The value of the lpstrParams member of MCI_OPEN_DRIVER_PARMS is the same as the value of lParam1.

Before DriverProc returns, you must:

  • Set the wCustomCommandTable member of MCI_OPEN_DRIVER_PARMS. If the driver is using a custom command table, this member must contain the handle returned by mciLoadCommandResource. Otherwise use MCI_NO_COMMAND_TABLE, defined in mmddk.h.

  • Set the wType member of MCI_OPEN_DRIVER_PARMS to one of the defined MCI device types. If the device does not belong to any of the defined types, use MCI_DEVTYPE_OTHER.

  • Assign the DriverProc function’s return value to be the contents of the wDeviceID member of MCI_OPEN_DRIVER_PARMS. However, if you encounter errors during the process of opening the device, you should assign a return value of zero instead.

The driver can also perform instance-specific operations for the device being opened.


Handling MCI_OPEN_DRIVER

The first MCI message a driver receives is MCI_OPEN_DRIVER. For this message, DriverProc parameters are set as follows:

dwDriverID
The driver ID value specified as the DriverProc return value for the DRV_OPEN message.
hDriver
The driver’s handle.
uMsg
MCI_OPEN_DRIVER.
lParam1
Flags. (See MCI_OPEN in Win32 SDK.)
lParam2
Pointer to an MCI_OPEN_PARMS structure.

An application specifies the contents of lParam1 and lParam2 when it sends an MCI_OPEN command.

For a compound device, the driver should test the MCI_OPEN_ELEMENT flag. If set, it indicates that a pointer to an element pathname is available in the lpstrElementName member of MCI_OPEN_PARMS.

Drivers for compound devices sometimes receive MCI_OPEN_DRIVER messages with the MCI_OPEN_ELEMENT flag cleared. This situation can occur if the application opens the device only for the purpose of querying the device with an MCI_GETDEVCAPS or MCI_INFO command. Therefore, drivers for compound devices must allow the MCI_OPEN_DRIVER command to succeed if it is received without an element name, but they must provide a failure return for any command that requires an element, if the element name is missing.

The MCI_OPEN_ELEMENT_ID flag indicates that the lpstrElementName member of MCI_OPEN_PARMS contains a DWORD value instead of a string pointer. This allows you to define an element specifier as being something other than a file pathname. Use of this flag is not recommended.

Drivers can ignore the MCI_OPEN_TYPE, MCI_OPEN_TYPE_ID, and MCI_OPEN_ALIAS flags. These are handled within winmm.dll.

Drivers that support sharing a device must test the MCI_OPEN_SHAREABLE flag.

The driver can also perform instance-specific operations for the device or element being opened, such as storing instance-specific information.

When handling the MCI_NOTIFY flag with the MCI_OPEN_DRIVER command, a driver should return to the application only after it verifies that the open operation will probably succeed. For example, suppose that opening a device element requires reading a large file from a CD-ROM. Before returning to the application, the driver should confirm that the file is accessible and that enough memory can be allocated to load it. This avoids providing a successful (zero) return value to DriverProc, only to later have to deliver an MM_MCINOTIFY message with a status of MCI_NOTIFY_FAILURE.


Sharing A Device

To share a simple device, a driver creates a single context that can be shared by multiple applications. This context allows each application to reference and modify device characteristics set up by other applications. For example, a shared driver for a CD player should allow an application to issue a command (“resume”, for example) that is based on the context of the last command received from any other application (“stop”, for example).

Because each instance of a shared driver is created by a separate DRV_OPEN command, a separate driver ID is assigned to each instance.

For a compound device, the shareable object is the device element. Each element has a unique device ID. The driver allows multiple applications to share an element’s context. Each application can reference and modify characteristics of the element that have been set up by other applications.

If a driver can share a device (or object), the decision about whether it should share the device (or object) is based on the first MCI_OPEN_DRIVER command. If this command includes the MCI_OPEN_SHAREABLE flag, then the driver should mark the device (or object) as being shared. Each subsequent MCI_OPEN_DRIVER command received must also include the MCI_OPEN_SHAREABLE flag, or else the MCIERR_MUST_USE_SHAREABLE error value must be returned.

On the other hand, if the first MCI_OPEN_DRIVER command does not include the MCI_OPEN_SHAREABLE flag, then no subsequent MCI_OPEN_DRIVER command can include the MCI_OPEN_SHAREABLE flag either. If it does, the MCIERR_MUST_USE_SHAREABLE error value must be returned.

If an application specifies the MCI_OPEN_SHAREABLE flag but the driver does not allow device sharing, then the driver must return MCIERR_UNSUPPORTED_FUNCTION.

For compound devices, drivers can alternatively allow several applications to open a single element without specifying the MCI_OPEN_SHAREABLE flag. In this case, each application should possess its own context for the element. As a result, each application makes its own modifications to the element context without affecting any other application’s context. Each application can move independently within a single element. Also, a single application can open a single element multiple times, providing multiple contexts for one element.


Storing Instance-Specific Information

It is sometimes necessary for a driver to save instance-specific information. Two functions supplied by winmm.dll, mciSetDriverData and mciGetDriverData, are useful for associating instance-specific information with driver ID’s. You can use these functions to store and retrieve a longword for each assigned driver ID (that is, for each opened device or element). Drivers typically cast the longword to a pointer to a locally allocated structure.


Providing Device Information

This section discusses the MCI commands that request information. It also explains the process of returning information to applications. Finally, it provides guidelines for returning information.


MCI Commands that Request Information

Four MCI commands allow applications to obtain information about a device. Three of these commands are sent to MCI drivers. They are:

  1. MCI_GETDEVCAPS

  2. MCI_INFO

  3. MCI_STATUS

A fourth command, MCI_SYSINFO, is handled within winmm.dll. This command is not sent to MCI drivers.

The MCI_GETDEVCAPS, MCI_INFO, and MCI_STATUS commands are similar to each other in the following ways:

  • The driver receives a flag in the lParam1 parameter of DriverProc, indicating the type of information being requested.

  • The driver receives the address of a data structure in the lParam2 parameter of DriverProc. This structure contains either a location or a pointer that the driver uses for returning the requested information.

An application can only request, and the driver can only return, one type of information per call. The MCI_GETDEVCAPS and MCI_STATUS commands return information as integer values. The MCI_INFO command returns information as strings (such as pathnames).

The types of information provided vary with the device type. See the descriptions of MCI_GETDEVCAPS, MCI_INFO, and MCI_STATUS in the Win32 SDK for information on the flags that have been defined with each command, for each device type. Drivers that define extended command sets can define new flags appropriate to their devices. Drivers can also define additional commands for returning other types of information. For example, drivers for video device types support an MCI_WHERE command, which returns a RECT data type for describing a rectangle.


Returning Information to Applications

There are two standardized methods for returning information to applications. One method is used for returning integer values and the other is used for returning strings.

When a driver receives an MCI command that requests information, it also receives the address of a data structure in the lParam2 parameter to DriverProc. A different structure is defined for each MCI command. Customized structures are defined for some device types. The structure’s definition dictates which method is used for returning the requested information.

Following is the structure definition for MCI_STATUS_PARMS, which is used with the MCI_STATUS command to return an integer value:

typedef struct tagMCI_STATUS_PARMS {
    DWORD   dwCallback;
    DWORD   dwReturn;
    DWORD   dwItem;
    DWORD   dwTrack;
} MCI_STATUS_PARMS, *PMCI_STATUS_PARMS, FAR * LPMCI_STATUS_PARMS;

The MCI_STATUS_PARMS structure defines a DWORD-sized member called dwReturn. To return an integer value for MCI_STATUS, the driver places a longword value into dwReturn.

Following is the structure definition for MCI_INFO_PARMS, used with the MCI_INFO command to return a string:

typedef struct tagMCI_INFO_PARMS {
    DWORD   dwCallback;
    LPSTR   lpstrReturn;
    DWORD   dwRetSize;
} MCI_INFO_PARMS, FAR * LPMCI_INFO_PARMS;

The MCI_INFO_PARMS structure defines two members, lpstrReturn and dwRetSize. These members are used for returning a string value. In this case, the application places a string buffer pointer in lpstrReturn and a buffer size in dwRetSize. The driver copies the return string into the buffer.

Returning String Resource Indentifiers

It is important to remember that applications can communicate with MCI by using either strings or command constants. When an application calls mciSendString, it specifies commands in string form and expects information to be returned in string form. When an application calls mciSendCommand it specifies command constants and flag constants, and expects information to be returned in a command-specific data structure, which it references. MCI drivers sometimes need to return information in both formats, in order to support both interfaces.

Suppose an application uses the MCI_STATUS command to request the device’s current mode, and it happens that the device is currently stopped. An application using mciSendCommand should be able to test the MCI_MODE_STOP flag value stored in the dwReturn member of an MCI_STATUS_PARMS structure. An application using mciSendString should receive the string “stopped” in its string buffer. The driver is responsible for returning both the flag and a string resource identifier. (String resources are discussed in the Win32 SDK.)

The proper way to return both a flag and a string resource identifier is to combine them in the dwReturn member, using the MAKEMCIRESOURCE macro. This macro concatenates two integers to make a single long integer. The resource identifier must be placed in the high word of the long integer.

If a driver returns a resource identifier, it must set the DriverProc return value to MCI_RESOURCE_RETURNED, as shown in the following example. (Notice that, in this case, the flag constant value and the string resource ID value are the same.)

wResource = MCI_MODE_STOP;
lpStatus->dwReturn = (DWORD)MAKEMCIRESOURCE(wResource, wResource);
dReturn = MCI_RESOURCE_RETURNED;

If the string resource is defined in a driver-specific resource file, the driver must also set MCI_RESOURCE_DRIVER in the return value, as shown in the following example of returning the “audio” status from an AVI driver. (Notice that, in this case, the flag constant value and the string resource ID value are not the same.)

lpStatus->dwReturn = (npMCI->dwFlags & MCIAVI_PLAYAUDIO) ?
                        (MAKEMCIRESOURCE(MCI_ON, MCI_ON_S)) :
                        (MAKEMCIRESOURCE(MCI_OFF, MCI_OFF_S));
return MCI_RESOURCE_RETURNED | MCI_RESOURCE_DRIVER;

Both MCI_RESOURCE_RETURNED and MCI_RESOURCE_DRIVER set bits in the return value’s high word.

If the application used mciSendString to send the command, then winmm.dll checks the high word of the DriverProc return value. If MCI_RESOURCE_RETURNED is set, winmm.dll loads the string associated with the resource identifier and places it in the application’s return buffer.

If the application used mciSendCommand to send the command and MCI_RESOURCE_RETURNED is set in the high word of the DriverProc return value, then winmm.dll just clears the high word before passing the return value to the application.

The following table contains the resource strings provided by winmm.dll for use by drivers. The resource identifiers and constants are defined within mmsystem.h and mmddk.h. (Sometimes the constant and resource ID are the same.) The strings are defined in a resource file that is part of winmm.dll. Driver developers can define additional strings within a driver-specific resource file. The driver calls mciLoadCommandResource to register the resource file with winmm.dll.
Constant | Resource ID | String
MCI_FALSE | MCI_FALSE | false
MCI_TRUE | MCI_TRUE | true
MCI_DEVTYPE_ANIMATION | MCI_DEVTYPE_ANIMATION | animation
MCI_DEVTYPE_CD_AUDIO | MCI_DEVTYPE_CD_AUDIO | cdaudio
MCI_DEVTYPE_DAT | MCI_DEVTYPE_DAT | dat
MCI_DEVTYPE_DIGITAL_VIDEO | MCI_DEVTYPE_DIGITAL_VIDEO | digitalvideo
MCI_DEVTYPE_OTHER | MCI_DEVTYPE_OTHER | other
MCI_DEVTYPE_OVERLAY | MCI_DEVTYPE_OVERLAY | overlay
MCI_DEVTYPE_SCANNER | MCI_DEVTYPE_SCANNER | scanner
MCI_DEVTYPE_SEQUENCER | MCI_DEVTYPE_SEQUENCER | sequencer
MCI_DEVTYPE_VCR | MCI_DEVTYPE_VCR | vcr
MCI_DEVTYPE_VIDEODISC | MCI_DEVTYPE_VIDEODISC | videodisc
MCI_DEVTYPE_WAVEFORM_AUDIO | MCI_DEVTYPE_WAVEFORM_AUDIO | waveaudio
MCI_FORMAT_BYTES | MCI_FORMAT_BYTES_S | bytes
MCI_FORMAT_FRAMES | MCI_FORMAT_FRAMES_S | frames
MCI_FORMAT_HMS | MCI_FORMAT_HMS_S | hms
MCI_FORMAT_MILLISECONDS | MCI_FORMAT_MILLISECONDS_S | milliseconds
MCI_FORMAT_MSF | MCI_FORMAT_MSF_S | msf
MCI_FORMAT_SAMPLES | MCI_FORMAT_SAMPLES_S | samples
MCI_FORMAT_SMPTE_24 | MCI_FORMAT_SMPTE_24_S | smpte 24
MCI_FORMAT_SMPTE_25 | MCI_FORMAT_SMPTE_25_S | smpte 25
MCI_FORMAT_SMPTE_30 | MCI_FORMAT_SMPTE_30_S | smpte 30
MCI_FORMAT_SMPTE_30DROP | MCI_FORMAT_SMPTE_30DROP_S | smpte 30 drop
MCI_FORMAT_TMSF | MCI_FORMAT_TMSF_S | tmsf
MCI_MODE_NOT_READY | MCI_MODE_NOT_READY | not ready
MCI_MODE_OPEN | MCI_MODE_OPEN | open
MCI_MODE_PAUSE | MCI_MODE_PAUSE | paused
MCI_MODE_PLAY | MCI_MODE_PLAY | playing
MCI_MODE_RECORD | MCI_MODE_RECORD | recording
MCI_MODE_SEEK | MCI_MODE_SEEK | seeking
MCI_MODE_STOP | MCI_MODE_STOP | stopped
MCI_SEQ_DIV_PPQN | MCI_SEQ_DIV_PPQN | PPQN
MCI_SEQ_DIV_SMPTE_24 | MCI_SEQ_DIV_SMPTE_24 | SMPTE 24 Frame
MCI_SEQ_DIV_SMPTE_25 | MCI_SEQ_DIV_SMPTE_25 | SMPTE 25 Frame
MCI_SEQ_DIV_SMPTE_30 | MCI_SEQ_DIV_SMPTE_30 | SMPTE 30 Frame
MCI_SEQ_DIV_SMPTE_30DROP | MCI_SEQ_DIV_SMPTE_30DROP | SMPTE 30 Drop Frame
MCI_SEQ_FILE | MCI_SEQ_FILE_S | file
MCI_SEQ_FORMAT_SONGPTR | MCI_SEQ_FORMAT_SONGPTR_S | song pointer
MCI_SEQ_MIDI | MCI_SEQ_MIDI_S | midi
MCI_SEQ_NONE | MCI_SEQ_NONE_S | none
MCI_SEQ_SMPTE | MCI_SEQ_SMPTE_S | smpte
MCI_VD_FORMAT_TRACK | MCI_VD_FORMAT_TRACK_S | track
MCI_VD_MEDIA_CAV | MCI_VD_MEDIA_CAV | CAV
MCI_VD_MEDIA_CLV | MCI_VD_MEDIA_CLV | CLV
MCI_VD_MEDIA_OTHER | MCI_VD_MEDIA_OTHER | other
MCI_VD_MODE_PARK | MCI_VD_MODE_PARK | parked
MIDIMAPPER | MIDIMAPPER_S | mapper
WAVE_FORMAT_PCM | WAVE_FORMAT_PCM_S | pcm
WAVE_MAPPER | WAVE_MAPPER_S | mapper

Returning Integers

If an application requests information that is represented as an integer value, the driver just places the integer in the dwReturn member of the command’s data structure. If the application used mciSendString to request the information, then winmm.dll converts the integer to a string and places the string in the application’s return buffer.

The driver can request winmm.dll to insert colons into the integer string. A typical reason for inserting colons is returning time values. To request colon insertion, a driver assigns MCI_COLONIZED3_RETURN or MCI_COLONIZED4_RETURN to the DriverProc return value, as shown in the next example. These constants set bits in the return value’s high word.

DWORD dwSeconds = dwFrames / CAV_FRAMES_PER_SECOND;
lpStatus->dwReturn = MCI_MAKE_HMS(dwSeconds / 3600,
                                  (dwSeconds % 3600) / 60,
                                  dwSeconds % 60);
return MCI_COLONIZED3_RETURN;

If the application used mciSendString to request the information, then winmm.dll treats each byte of the return value as a separate integer and inserts a colon between each integer in the string that is returned to the application. For an integer value of 0x01020304, specifying MCI_COLONIZED4_RETURN returns the string “4:3:2:1”, and specifying MCI_COLONIZED3_RETURN returns “4:3:2”.

One other special DriverProc return value, MCI_INTEGER_RETURNED, forces a command’s returned information to be an integer even though the command’s data structure defines the return type as a string. Microsoft uses this flag within winmm.dll, for support of the MCI_SYSINFO command. This command’s MCI_SYSINFO_PARMS structure defines a string return, but if the requested information type is MCI_SYSINFO_QUANTITY, then an integer value is placed in the structure. (For more information, see the Win32 SDK.) If the application requested the information by calling mciSendString, winmm.dll converts the integer to a string.


Guidelines for Returning Information

Use the following guidelines for returning information when developing a new driver:

  • Implement the MCI_GETDEVCAPS, MCI_INFO, and MCI_STATUS commands as they are described in the Win32 SDK. Be consistent with the implementations already provided with existing drivers.

  • Remember that the MCI_GETDEVCAPS and MCI_STATUS commands return integer values, while MCI_INFO returns string values.

  • Place strings requiring language translation in resource files. Return their resource identifiers as integer values, using MCI_GETDEVCAPS, MCI_STATUS, or a customized extended command. The strings returned by MCI_INFO contain information that does not require foreign language translation, such as file and path names, or media identifier strings.


Closing an MCI Driver

A client application using MCI closes a driver by sending an MCI_CLOSE message. This message is intercepted by winmm.dll, which sends the following messages to the appropriate MCI driver, in the order listed:

  1. MCI_CLOSE_DRIVER

  2. DRV_CLOSE

  3. DRV_DISABLE

  4. DRV_FREE

Note that the driver does not receive the MCI_CLOSE message. Also be aware that DRV_DISABLE and DRV_FREE are sent only if the calling application is the only one using the driver. After DRV_FREE is sent, winmm.dll unloads the driver from the application’s address space.

For more information, see the following topics:

  • Handling MCI_CLOSE_DRIVER

  • Handling DRV_CLOSE

  • Handling DRV_DISABLE

  • Handling DRV_FREE


Handling MCI_CLOSE_DRIVER

When a driver receives an MCI_CLOSE_DRIVER command, it should close the object that is associated with the driver identifier received in dwDriverID. For a nonshared simple device, the object is the device. For a compound device, the object is an element.

If a device or element is being shared, then reception of an MCI_CLOSE_DRIVER message only means that the driver should no longer allow the caller access to the device or element. The device or element should be closed only after there are no longer any applications using it.


Handling DRV_CLOSE

A driver should close a device only after all applications have finished using it. You might want to ignore the DRV_CLOSE command (just return with a nonzero return value) if the application that caused it to be sent is not the only one still using the driver.

Often, all of the steps required to close a device can be handled when a driver processes the MCI_CLOSE_DRIVER command. In such cases the driver can simply return a nonzero value when DRV_CLOSE is received.


Handling DRV_DISABLE

For information on handling DRV_DISABLE, see Introduction to Multimedia Drivers.


Handling DRV_FREE

If your driver provides a custom string table, unload it by calling mciFreeCommandResource when DriverProc receives the DRV_FREE message. For more information on handling DRV_FREE, see Introduction to Multimedia Drivers.


Guidelines for Handling MCI Commands

A developer’s most important guideline is to make sure the driver design adheres to the command descriptions and flag descriptions provided by the Win32 SDK. For example, the description of MCI_PLAY says that the starting position is the current position and the ending position is the end of the medium, unless the application includes MCI_FROM and MCI_TO flags.

If you need to provide extensions to the basic commands, do so in a manner that is consistent with extended command sets that already exist.

If an application sends an MCI_PLAY command with MCI_NOTIFY set, subsequent MCI_PAUSE and MCI_RESUME commands should not cancel the notification request. The application should still be notified when the original play operation has completed. The MCI_PAUSE and MCI_RESUME commands just delay the completion of the MCI_PLAY operation; they do not cancel or modify it.

Drivers for compound devices can allow MCI_RECORD commands to be sent with the the MCI_OPEN_ELEMENT flag set, but with the element file name specified as a null-terminated, zero-length string. In this case the recorded data should be saved in temporary storage. On receipt of a subsequent MCI_SAVE command, the driver should move the temporary data into a permanent file. Alternatively, drivers can disallow MCI_RECORD commands without a valid filename, and return MCIERR_FILENAME_REQUIRED if the filename is not included.


Creating Customized MCI Commands

If MCI’s required and basic command sets do not provide all of the functionality needed by applications to fully interact with a device, then a driver developer can create customized MCI commands by doing one or both of the following:

  • Extending the required and basic commands as necessary, by adding additional input flags, redefining the command data structures, or both.

  • Extending the command set by defining new commands and data structures.

You should match your MCI command extensions as closely as possible to existing command extensions. Refer to the Win32 SDK for descriptions of existing command extensions.

You define new or modified MCI commands by creating new MCI command tables. Use these tables to define new commands and flags, and to redefine or extend the required or basic commands.

Creating new MCI command structures is also sometimes necessary. You must define a new structure if you are creating a new command, or if you are modifying an existing command in a way that requires additional structure members.


Creating New MCI Command Tables

You create a new MCI command table by:

  • Defining MCI message constants for your commands, if they do not already exist.

  • Defining flag constants for your command flags, if they do not already exist.

  • Constructing a command table, using message strings, message constants, and flag constants.


Defining MCI Message Constants

If an MCI message constant does not already exist for your command in mmsystem.h, you must define one. Place this definition in an include file that is available both to your driver and to application developers. MCI message constants must be assigned values between the constants MCI_USER_MESSAGES and MCI_LAST, which are defined in mmsystem.h.

Note that message constants cannot be defined using an expression, such as “MCI_USER_MESSAGES + 1”, because you are using the constant in a command table. Command tables are defined within resource files, using RCDATA type, and this type does not accept expressions. Define each messageconstant as a numeric value, such as:

#define MCI_MYCOMMAND	0xA01


Defining Flag Constants

You need to define new flag constants whenever you create new command modifiers, either for existing commands or for new commands.

Flags are passed to MCI drivers in the lParam1 parameter to DriverProc. This is a DWORD parameter. The first 16 bits (0 through 15) of the DWORD are reserved by MCI. Driver developers can use bits 16 through 31 for customized flags. This means up to 16 different customized flags can be defined for each command. You should be sure that new flag definitions do not conflict with existing definitions.

Place new flag definitions in an include file that is available both to your driver and to application developers. When naming flags, include the command and a representation of your device type in the name. For example, flags for animation extensions to the “play” command are named as follows:

#define MCI_ANIM_PLAY_SPEED			0x00010000L
#define MCI_ANIM_PLAY_REVERSE		0x00020000L
#define MCI_ANIM_PLAY_FAST			0x00040000L
#define MCI_ANIM_PLAY_SLOW			0x00080000L
#define MCI_ANIM_PLAY_SCAN			0x00100000L


Constructing a Command Table

Command tables are defined as raw data resources which must be compiled using the Microsoft Windows Resource Compiler, discussed in the Win32 SDK. The command table can be created as a separate DLL file, with a file extension of .mci, or it can be linked to the driver.

If you place the command table in a separate DLL, you can also include all of the driver’s string resources in the same file. This provides a convenient means for translating the driver’s text into additional languages by simply replacing this one file.

Following is a small segment, including the first and last entries, of the core command table provided in winmm.dll. This segment provides an illustration of how command tables are constructed.

core RCDATA
BEGIN
  L"open\0",            MCI_OPEN, 0,                  MCI_COMMAND_HEAD,
  L"\0",                MCI_INTEGER, 0,               MCI_RETURN,
  L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
  L"wait\0",            MCI_WAIT,                     MCI_FLAG,
  L"type\0",            MCI_OPEN_TYPE,                MCI_STRING,
  L"element\0",         MCI_OPEN_ELEMENT,             MCI_STRING,
  L"alias\0",           MCI_OPEN_ALIAS,               MCI_STRING,
  L"shareable\0",       MCI_OPEN_SHAREABLE,           MCI_FLAG,
  L"\0",                0L,                           MCI_END_COMMAND,
  L"close\0",           MCI_CLOSE, 0,                 MCI_COMMAND_HEAD,
  L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
  L"wait\0",            MCI_WAIT,                     MCI_FLAG,
  L"\0",                0L,                           MCI_END_COMMAND,
  L"play\0",            MCI_PLAY, 0,                  MCI_COMMAND_HEAD,
  L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
  L"wait\0",            MCI_WAIT,                     MCI_FLAG,
  L"from\0",            MCI_FROM,                     MCI_INTEGER,
  L"to\0",              MCI_TO,                       MCI_INTEGER,
  L"\0",                0L,                           MCI_END_COMMAND,
                       .
                       .
                       .
  L"status\0",          MCI_STATUS, 0,                MCI_COMMAND_HEAD,
  L"\0",                MCI_INTEGER, 0,               MCI_RETURN,
  L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
  L"wait\0",            MCI_WAIT,                     MCI_FLAG ,
  L"\0",                MCI_STATUS_ITEM,              MCI_CONSTANT,
  L"position\0",        MCI_STATUS_POSITION,          MCI_INTEGER,
  L"length\0",          MCI_STATUS_LENGTH,            MCI_INTEGER,
  L"number of tracks\0",MCI_STATUS_NUMBER_OF_TRACKS,  MCI_INTEGER,
  L"ready\0",           MCI_STATUS_READY,             MCI_INTEGER,
  L"mode\0",            MCI_STATUS_MODE,              MCI_INTEGER,
  L"time format\0",     MCI_STATUS_TIME_FORMAT,       MCI_INTEGER,
  L"current track\0",   MCI_STATUS_CURRENT_TRACK,     MCI_INTEGER,
  L"\0",                0L,                           MCI_END_CONSTANT,
  L"track\0",           MCI_TRACK,                    MCI_INTEGER,
  L"start\0",           MCI_STATUS_START,             MCI_FLAG,
  L"\0",                0L,                           MCI_END_COMMAND,
                       .
                       .
                       .
  L"set\0",             MCI_SET, 0,                   MCI_COMMAND_HEAD,
  L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
  L"wait\0",            MCI_WAIT,                     MCI_FLAG ,
  L"time format\0",     MCI_SET_TIME_FORMAT,          MCI_CONSTANT,
  L"milliseconds\0",    MCI_FORMAT_MILLISECONDS, 0,   MCI_INTEGER,
  L"ms\0",              MCI_FORMAT_MILLISECONDS, 0,   MCI_INTEGER,
  L"\0",                0L,                           MCI_END_CONSTANT,
  L"door open\0",       MCI_SET_DOOR_OPEN,            MCI_FLAG,
  L"door closed\0",     MCI_SET_DOOR_CLOSED,          MCI_FLAG,
  L"audio\0",           MCI_SET_AUDIO,                MCI_CONSTANT,
  L"all\0",             MCI_SET_AUDIO_ALL,            MCI_INTEGER,
  L"left\0",            MCI_SET_AUDIO_LEFT,           MCI_INTEGER,
  L"right\0",           MCI_SET_AUDIO_RIGHT,          MCI_INTEGER,
  L"\0",                0L,                           MCI_END_CONSTANT,
  L"video\0",           MCI_SET_VIDEO,                MCI_FLAG,
  L"on\0",              MCI_SET_ON,                   MCI_FLAG,
  L"off\0",             MCI_SET_OFF,                  MCI_FLAG,
  L"\0",                0L,                           MCI_END_COMMAND,
                       .
                       .
                       .
  L"resume\0",          MCI_RESUME, 0,                MCI_COMMAND_HEAD,
  L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
  L"wait\0",            MCI_WAIT,                     MCI_FLAG,
  L"\0",                0L,                           MCI_END_COMMAND,
  L"\0",                0L,                         MCI_END_COMMAND_LIST
END

As shown in the previous example, a command table is defined by using an RCDATA statement, which is described in the Win32 SDK.. The RCDATA declaration includes the table’s name. For the core table, the table’s name is “core”. For a device-type table, the table’s name is the device type, such as “videodisc”.

Command tables contain command table entries. Each entry contains:

  • A null-terminated string identifying a command or command modifier. This is the text that applications specify with calls to mciSendString. Each string is prefaced with “L” to create wide-character storage for UNICODE characters.

  • A longword value containing a command constant or modifier flag constant value. Mostly, these are the constants an application uses with the mciSendCommand function. Drivers receive the constant values as umsg and lParam1 arguments to DriverProc. Note that some predefined constants, such as MCI_INTEGER, only define single word values. These single-word constants must be padded with an extra word to create a longword.

  • A single word value representing the entry type.

The table must end with an entry of type MCI_END_COMMAND_LIST.

Example

As an example of how to define a command, look at the “play” command contained in the core table. While the “play” command in the core command table is fairly simple, it illustrates several of the entry types that can be used for creating a command description.

To define a command in a command table
  1. Begin with an MCI_COMMAND_HEAD entry. This entry has the following format:
L"play\0",            MCI_PLAY, 0,                  MCI_COMMAND_HEAD,

First, the entry contains a null-terminated, wide-character string containing the command’s string name. Next, a longword value is specified, with the MCI_PLAY bit set. (Because MCI_PLAY is defined as a 16-bit constant, an additional word of zero is added as the highword value for the longword.) Finally, the entry type is specified as MCI_COMMAND_HEAD, which labels this entry as a new command.

2. Include two entries indicating that the command accepts the MCI_NOTIFY and MCI_WAIT modifiers. (All command definitions must contain these two entries.)

L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
L"wait\0",            MCI_WAIT,                     MCI_FLAG,

The MCI_FLAG entry type is used for defining command modifiers that do not require either input or output members within the command’s data structure (in this case, MCI_PLAY_PARMS). (See Creating New MCI Command Structures.)

3. Include command-specific modifier definitions. The “play” command accepts two modifiers, “from” and “to”.

L"from\0",            MCI_FROM,                     MCI_INTEGER,
L"to\0",              MCI_TO,                       MCI_INTEGER,

The “from” and “to” modifiers both accept numeric arguments, such as “play videodisc1 from 10 to 100”. The MCI_INTEGER type indicates that for each of these arguments, an integer-typed member exists in the “play” command’s MCI_PLAY_PARMS structure. Following is a list of recognized data types:
MCI_INTEGER | Integer
MCI_HWND | Window handle
MCI_HPAL | Palette handle
MCI_HDC | Device Context handle
MCI_RECT | Rectangle
MCI_STRING | String

For a complete list of entry types, see Command Table Entry Types.

  1. End the command definition with an MCI_END_COMMAND entry.
L"\0",                0L,                           MCI_END_COMMAND,

The entry must contain a null string, followed by a null longword.

Example

As another example, look at the “status” command. This command provides a return value, so its command description includes an MCI_RETURN entry. This entry must appear immediately after the MCI_COMMAND_HEAD entry.

L"status\0",          MCI_STATUS, 0,                MCI_COMMAND_HEAD,
L"\0",                MCI_INTEGER, 0,               MCI_RETURN,

An MCI_RETURN entry must contain a null string, followed by a longword specifiying the data type of the command’s return value. Any of the MCI data types can be used. In this case, the command returns an integer value. The command’s data structure must contain an integer-typed member for storing the return value. (See Creating New MCI Command Structures.)

Defining Constant Values

Sometimes it is necessary to define a set of constant values that can be assigned to a data structure member. Constant values are delimited by MCI_CONSTANT and MCI_END_CONSTANT entries. Here is a section of the “set” command description:

L"audio\0",           MCI_SET_AUDIO,                MCI_CONSTANT,
L"all\0",             MCI_SET_AUDIO_ALL,            MCI_INTEGER,
L"left\0",            MCI_SET_AUDIO_LEFT,           MCI_INTEGER,
L"right\0",           MCI_SET_AUDIO_RIGHT,          MCI_INTEGER,
L"\0",                0L,                           MCI_END_CONSTANT,

An MCI_CONSTANT entry must contain a string, followed by a longword containing a flag value. A command’s data structure must reserve a DWORD-sized member for each MCI_CONSTANT entry in the command table. Entries following the MCI_CONSTANT entry define a set of constant values that can be set in the data structure member. The set of constant entries ends with an MCI_END_CONSTANT entry, which must contain a null string and a null longword.

If an application uses mciSendString to specify the command “set vcr1 audio left”, then winmm.dll sets the MCI_SET_AUDIO flag in laram1 to indicate the dwAudio member for the MCI_SET_PARMS structure is valid, and sets MCI_SET_AUDIO_LEFT in dwAudio.

Here is another section of the “status” description:

L"\0",                MCI_STATUS_ITEM,              MCI_CONSTANT,
L"position\0",        MCI_STATUS_POSITION,          MCI_INTEGER,
L"length\0",          MCI_STATUS_LENGTH,            MCI_INTEGER,
L"number of tracks\0",MCI_STATUS_NUMBER_OF_TRACKS,  MCI_INTEGER,
L"ready\0",           MCI_STATUS_READY,             MCI_INTEGER,
L"mode\0",            MCI_STATUS_MODE,              MCI_INTEGER,
L"time format\0",     MCI_STATUS_TIME_FORMAT,       MCI_INTEGER,
L"current track\0",   MCI_STATUS_CURRENT_TRACK,     MCI_INTEGER,
L"\0",                0L,                           MCI_END_CONSTANT,

For this constant list, the MCI_CONSTANT entry contains a null string. A null string is allowed if the command description defines only one modifier type and, hence, one structure input member. If the description defines more than one modifier, a string must be included so the command parser can identify which command structure member to use.

For the “status” command, an application can specify “status vcr1 position”, for example, to obtain the current position, and “status vcr1 mode” to obtain the current mode. Both “position” and “mode” use the dwItem member in MCI_STATUS_PARMS. In constrast, for the “set” command an application must specify “set vcr1 time format milliseconds” to set the time format, and “set vcr1 audio all” to set all audio channels. Here, “time format” and “audio” differentiate between the dwTimeFormat and dwAudio members of MCI_SET_PARMS.


Command Table Entry Types

Following is a list of command table entry types that can be used for constructing a command table. All of these types are defined as single word values.
Entry Type | Purpose
MCI_COMMAND_HEAD | First entry for each command. String identifies command. Longword contains MCI message constant.
MCI_CONSTANT | Introduces a set of constant command modifiers. String and longword identify structure member in which constant value is stored.
MCI_END_COMMAND | Last entry for each command. String and longword values are null.
MCI_END_COMMAND_LIST | Last entry in a command table. String and longword values are null.
MCI_END_CONSTANT | Ends a constant set. String and longword values are null.
MCI_FLAG | Defines a command modifier that does not require associated data structure storage. String and longword values identify the modifier.
MCI_HDC | Defines a command modifier that accepts an HDC argument. String defines modifier name. Longword identifies data structure member in which HDC value is stored. Also used to specify return type with MCI_RETURN.
MCI_HPAL | Defines a command modifier that accepts an HPAL argument. String defines modifier name. Longword identifies data structure member in which HPAL value is stored. Also used to specify return type with MCI_RETURN.
MCI_HWND | Defines a command modifier that accepts an HWND argument. String defines modifier name. Longword identifies data structure member in which HWND value is stored. Also used to specify return type with MCI_RETURN.
MCI_INTEGER | Defines a command modifier that accepts an integer argument. String defines modifier name. Longword identifies data structure member in which integer is stored. Also used to specify return type with MCI_RETURN.
MCI_RECT | Defines a command modifier that accepts a RECT argument. String defines modifier name. Longword identifies data structure member in which RECT value is stored. Also used to specify return type with MCI_RETURN.
MCI_RETURN | Declares that command returns a value. String value is null. Longword identifies the return value’s type, which can be MCI_INTEGER, MCI_HDC, MCI_HPAL, MCI_HWND, MCI_RECT, or MCI_STRING.
MCI_STRING | Defines a command modifier that accepts a string pointer argument. String in command table defines modifier name. Longword identifies data structure member in which string pointer argument is stored. Also used to specify return type with MCI_RETURN.


Loading and Unloading a Command Table

A driver loads a customized command table by calling mciLoadCommandResource, as follows:

uCommandTable = mciLoadCommandResource(hModuleInstance, "mcivcr", 0);

The call should be made when DriverProc receives a DRV_LOAD message. In the example, the command table’s name is “mcivcr”, which means “mcivcr” is the nameID argument to the table’s RCDATA statement. Additionally, if you place the command table in a separate file, the file must be named mcivcr.mci. The file must reside in a directory that is accessible by Win32’s CreateFile function.

A driver must unload its customized command table by calling mciFreeCommandResource. This call should be made when DriverProc receives a DRV_FREE message.

Every driver that defines a customized command table must use mciLoadCommandResource and mciFreeCommandResource to load and unload the table. There is one exception to this rule ¾ if the command table defines a new device type and if the table resides in a separate file, then the driver does not need to explicitly load and unload the table. In this case, winmm.dll uses the device type name that the application specifies with the MCI_OPEN message to locate and load the command table.

In other words, a driver must explicitly load and unload its customized command table if the table is device-specific (as opposed to being for a device type) or if the table is linked to the driver.

If you create a customized command table for a one of the device types provided by Microsoft, and if you put the table in a separate file, do not use the device type name as the file name. Otherwise, other drivers will not be able to access the device type table provided by Microsoft because your table will override it.


How the MCI Parser Uses Command Tables

The MCI parser, within winmm.dll, is invoked when an application calls mciSendString. The parser reads the command from the command string and attempts to find the command in one of the command tables.

Code in winmm.dll searches for a customized table. A customized table is one that the driver has loaded by calling mciLoadCommandResource. First, winmm.dll uses the table name specified with mciLoadCommandResource and attempts to locate a file with that name and an extension of .mci. If a separate file is not found, the driver DLL is searched for a resource with the specified name.

This scheme of searching for a separate file before searching inside the driver facilitates creation of location-specific command tables. Tables for various languages can be created without affecting the driver. If you place the command table in a separate file, you can also include all of the driver’s string resources in the same file. When winmm.dll attempts to locate a string resource it also searchs in the .mci file first.

If a customized table is found, the parser searches the table for the command. If a customized table does not exist, or if the parser cannot find the command in a customized table, then the parser uses the tables defined in winmm.dll.

The parser compares the command string with the string specified in each MCI_COMMAND_HEAD entry of the table until it finds a match. When a match is found, the parser extracts the value stored in the longword following the string and uses it as the umsg argument for DriverProc. The parser then tries to match command modifiers included in the command string with entries in the command description. For each match, the parser extracts the value stored in the longword following the matched string and OR’s it into the lParam1 argument for DriverProc. For all modifier types except MCI_FLAG, the parser also takes the modifier value from the command string and stores it in the command’s data structure. The parser allows command modifiers to be included in random order in the command string, so “play vcr1 to 100 from 10” is equivalent to “play vcr1 from 10 to 100”.

Every time the parser receives a new command, it checks for the command tables in the order described. This means that a driver’s customized command table does not need to include all of the commands the driver supports, if it only extends some of them. If, for example, a driver adds new flags to the “play” command but supports only the default (core) behavior for all other commands, then the custom table only needs to contain the extended “play” command. The parser uses the core table to parse the rest of the commands.


Creating New MCI Command Structures

You must create new MCI command structures whenever you extend existing commands or define new ones. For every command description you place in a customized command table, you must define a customized data structure. Place this definition in an include file that is available both to your driver and to application developers. Structure definitions for the core commands reside in mmsystem.h.

Command structure definitions follow a standardized format. A typical structure is the following MCI_STATUS_PARMS structure, used with the “status” command.

typedef struct tagMCI_STATUS_PARMS {
    DWORD   dwCallback;
    DWORD   dwReturn;
    DWORD   dwItem;
    DWORD   dwTrack;
} MCI_STATUS_PARMS;

There are rules you should follow for naming command structures, laying out command structures, and referencing command structures.


Naming Command Structures

When naming a command structure, include the command in the structure name. Also include the device type, in order to differentiate customized structures from the structures defined for the core commands. An example is the following structure, used with the extension to the play command for animation devices:

typedef struct tagMCI_ANIM_PLAY_PARMS {
    DWORD   dwCallback;
    DWORD   dwFrom;
    DWORD   dwTo;
    DWORD   dwSpeed;
} MCI_ANIM_PLAY_PARMS;


Laying Out Command Structures

The first member of a command structure must be a DWORD for storing a window handle. The window handle is provided by the application and is used by winmm.dll for delivering command notification messages associated with the MCI_NOTIFY flag. Since all commands must support MCI_NOTIFY, all structures must contain this member. By convention, this member is called dwCallback.

Following the dwCallback member, each additional structure member must be matched to an entry in the command’s description within the command table. The order and data type of each member must match those in the command description.

If a return value is associated with the command, then the second structure member is used for storing the return value. For all return types except MCI_STRING and MCI_RECT, the return member must be DWORD-sized. If the command returns a string, then the structure must provide storage for a string buffer address and a string buffer length, both of which are supplied by the application. For commands whose return type is MCI_RECT, the structure must provide a RECT-sized return member.

The “info” command’s return value is a string, and its structure follows:

typedef struct tagMCI_INFO_PARMS {
    DWORD   dwCallback;
    LPSTR   lpstrReturn;
    DWORD   dwRetSize;
} MCI_INFO_PARMS;

If a command does not provide a return value, then the command’s structure does not include return members. For example, the “play” command does not provide a return value. Following is its structure:

typedef struct tagMCI_PLAY_PARMS {
    DWORD   dwCallback;
    DWORD   dwFrom;
    DWORD   dwTo;
} MCI_PLAY_PARMS;

After the return value member, the rest of the structure members are used for storing input or output arguments. Following is the “play” command description, so you can see how it matches the MCI_PLAY_PARMS structure. Entries with the MCI_FLAG type do not require space in the data structure.

L"play\0",            MCI_PLAY, 0,                  MCI_COMMAND_HEAD,
L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
L"wait\0",            MCI_WAIT,                     MCI_FLAG,
L"from\0",            MCI_FROM,                     MCI_INTEGER,
L"to\0",              MCI_TO,                       MCI_INTEGER,
L"\0",                0L,                           MCI_END_COMMAND,

Here’s the “open” command’s description:

L"open\0",            MCI_OPEN, 0,                  MCI_COMMAND_HEAD,
L"\0",                MCI_INTEGER, 0,               MCI_RETURN,
L"notify\0",          MCI_NOTIFY,                   MCI_FLAG,
L"wait\0",            MCI_WAIT,                     MCI_FLAG,
L"type\0",            MCI_OPEN_TYPE,                MCI_STRING,
L"element\0",         MCI_OPEN_ELEMENT,             MCI_STRING,
L"alias\0",           MCI_OPEN_ALIAS,               MCI_STRING,
L"shareable\0",       MCI_OPEN_SHAREABLE,           MCI_FLAG,
L"\0",                0L,                           MCI_END_COMMAND,

And here is the MCI_OPEN_PARMS structure:

typedef struct tagMCI_OPEN_PARMS {
    DWORD       dwCallback;
    MCIDEVICEID wDeviceID;
    WORD        wReserved0;
    LPCSTR      lpstrDeviceType;
    LPCSTR      lpstrElementName;
    LPCSTR      lpstrAlias;
} MCI_OPEN_PARMS;

Note the difference between string return values, shown previously in MCI_INFO_PARMS, and string arguments, illustrated in MCI_OPEN_PARMS. For string return values, the structure must contain members to hold both a string buffer address and a string buffer size. For string arguments, only a string pointer is stored.


Referencing Command Structures

Drivers should obey the following rules when referencing command data structures:

  • Test for null structure pointers. For some commands, it is acceptable for an application to send the command without the structure. For example, if an application sends the “play” command without modifiers, it does not need the structure.

  • Before referencing a structure’s input members, check the lParam1 argument to DriverProc to see if the member’s flag is set. Data in the structure member is only valid if the approriate flag has been set. Following is an example:
     if (lparam1 & MCI_FROM)
            dwFrom = lparam2->dwFrom;


MCI Reference

This section describes the functions, messages, structures, and macros used by MCI drivers. The following topics are provided:

  • MCI Functions

  • MCI Messages

  • MCI Structures

  • MCI Macros


MCI Functions

This section describes the functions defined by winmm.dll that are used by MCI drivers.


mciDriverNotify

BOOL APIENTRY
mciDriverNotify (
HANDLE hwndCallback,
MCIDEVICEID wDeviceID,
UINT uStatus
);

MCI drivers call mciDriverNotify to post an MM_MCINOTIFY message to an application.

Parameters
hwndCallback
Specifies the handle of the window to notify. The handle is obtained from the dwCallback member of the structure pointed to by the DriverProc lParam2 parameter.
wDeviceID
Specifies the device ID. This is the device ID received from DriverProc.
uStatus
Specifies the status of the operation requested by the application. Can be one of the following values:

MCI_NOTIFY_SUCCESSFUL

Driver successfully completed the requested operation.

MCI_NOTIFY_SUPERSEDED

Application sent an MCI message with the MCI_NOTIFY flag set, then sent a second message with MCI_NOTIFY set before the first operation completed. Driver calls mciNotifyDriver with MCI_NOTIFY_SUPERSEDED status, then calls mciNotifyDriver again when the second operation completes.

MCI_NOTIFY_ABORTED

Application sent a command that prevents the notification condition from being satisfied. For example, the command “stop vcr1” cancels a pending notification for “play vcr1 to 500 notify”.

MCI_NOTIFY_FAILURE

A device error prevented the notification condition from being satisfied.

Return Value

If a notification is successfully sent, mciDriverNotify returns TRUE; otherwise, it returns FALSE.

Comments

Drivers call mciDriverNotify after an operation has completed, if the application that requested the operation included the MCI_NOTIFY flag with the command. See Handling the MCI_NOTIFY Flag.


mciDriverYield

UINT APIENTRY
mciDriverYield (
MCIDEVICEID wDeviceID
);

The mciDriverYield function calls the application’s yield function. The yield function checks to see if the user has pressed the break key.

Parameters
wDeviceID
Specifies the device ID. This is the device ID received from DriverProc.
Return Value

Returns the value returned from the yield function. If the break key was pressed, the return value should be a nonzero value. Otherwise returns zero.

Comments

Drivers call mciDriverYield while waiting for a requested operation to complete, if the application included the MCI_WAIT flag with the command. See Handling the MCI_WAIT Flag.


mciFreeCommandResource

BOOL APIENTRY
mciFreeCommandResource(
UINT wTable
);

The mciFreeCommandResource function frees from memory a command table that was loaded with mciLoadCommandResource.

Parameters
wTable
The table index number returned from a previous call to mciLoadCommandResource.
Return Value

Returns FALSE if the table index number is invalid. Oherwise, returns TRUE.

Comments

For more information see Loading and Unloading a Command Table.


mciGetDriverData

DWORD APIENTRY
mciGetDriverData(
MCIDEVICEID wDeviceID
);

The mciGetDriverData function returns instance-specific information that was set with mciSetDriverData.

Parameters
wDeviceID
Specifies the MCI device ID.
Return Value

Returns the driver instance information. Returns zero if an error occurs.

Comments

A driver can test for an error return only when the valid value is known to be nonzero, if, for example, the driver had previously called mciSetDriverData and specified a pointer value. If the driver has set the instance information to zero with mciSetDriverData, then it cannot test for an error return from mciGetDriverData.


mciLoadCommandResource

UINT APIENTRY
mciLoadCommandResource(
HANDLE hInstance,
LPCWSTR lpResName,
UINT wType
);

The mciLoadCommandResource function loads the specified resource and registers it as an MCI command table.

Parameters
hInstance
Specifies the driver module’s instance handle. This argument is ignored if the resource resides in an external file.
lpResName
Points to a string representing the name of the resource.
wType
Specifies the device type. Customized device-specific tables must specify a type of zero.
Return Value

If an error occurs, returns MCI_NO_COMMAND_TABLE. Otherwise, returns a command table index number.

Comments

The mciLoadCommandResource function first attempts to locate a file, using the lpResName argument as the filename, with an extension of .mci. If a file is found, the command table is loaded from the file. This file can also contain string resources.

If a file is not found, then lpResName is assumed to be the name of a resource that is linked to the driver.

For more information see Loading and Unloading a Command Table.


mciSetDriverData

BOOL APIENTRY
mciSetDriverData(
MCIDEVICEID wDeviceID,
DWORD dwData
 );

The mciSetDriverData function is used for storing instance-specific information for an MCI driver.

Parameters
wDeviceID
Specifies a device ID.
dwData
Specifies the information to assign.
Return Value

Returns FALSE if the device ID is invalid. Otherwise returns TRUE.

Comments

Drivers can call mciSetDriverData to associate a driver-defined DWORD value with the current instance of the driver. Typically, an MCI driver calls this function while processing the MCI_OPEN_DRIVER message, specifying, for example, a pointer to an instance-specific data structure. The driver might call the function again when processing an MCI_CLOSE_DRIVER message, specifying a dwData value of zero. The driver can retrieve the stored value by calling mciGetDriverData.


MCI Messages

This section describes the command messages that MCI drivers are required to support. For more information on the types of MCI command messages, see MCI Command Types.

For descriptions of all other MCI commands, refer to the Win32 SDK.

The command messages are defined in mmsystem.h.


MCI_CLOSE

MCI drivers do not receive the MCI_CLOSE command. Instead, they receive MCI_CLOSE_DRIVER. See Closing an MCI Driver.


MCI_CLOSE_DRIVER

The MCI_CLOSE_DRIVER message requests an MCI driver to close a driver instance that was previously opened with an MCI_OPEN_DRIVER message.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
MCI_CLOSE_DRIVER
lParam1
Contains the flags specified by the application, with the MCI_CLOSE command.
lParam2
Specifies a pointer to an MCI_GENERIC_PARMS structure, or to a customized structure.
Return Value

If the close operation succeeds, the driver returns zero. Otherwise, the driver returns one of tbe MCIERR error codes defined in mmsystem.h.

Comments

A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameters. When an application sends an MCI_CLOSE message, winmm.dll intercepts it and sends MCI_CLOSE_DRIVER to the driver.

See Also

Closing an MCI Driver, MCI_OPEN_DRIVER


MCI_GETDEVCAPS

The MCI_GETDEVCAPS message requests an MCI driver to return device capabilities information.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
MCI_GETDEVCAPS
lParam1
Contains the flags specified by the application.
lParam2
Specifies a pointer to an MCI_GETDEVCAPS_PARMS structure, or to a customized structure.
Return Value

If the operation succeeds, the driver returns zero. Otherwise, the driver returns one of tbe MCIERR error codes defined in mmsystem.h.

Comments

A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameters.

A driver tests lParam1 to determine the type of information to return. The driver returns the requested information in the structure pointed to by lParam2. Refer to the Win32 SDK description of MCI_GETDEVCAPS to determine the types of information the driver should return.

See Also

Providing Device Information


MCI_INFO

The MCI_INFO message requests an MCI driver to return device or driver information.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
MCI_INFO
lParam1
Contains the flags specified by the application.
lParam2
Specifies a pointer to an MCI_INFO_PARMS structure, or to a customized structure.
Return Value

If the operation succeeds, the driver returns zero. Otherwise, the driver returns one of tbe MCIERR error codes defined in mmsystem.h.

Comments

A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameters.

A driver tests lParam1 to determine the type of information to return. The driver returns the requested information as a string in a buffer. The buffer’s address and size are contained in the structure pointed to by lParam2. Refer to the Win32 SDK description of MCI_INFO to determine the types of information the driver should return.

If the application includes the MCI_INFO_PRODUCT flag, the returned string should include the manufacturer of the hardware and, if possible, model identification information. If a driver applies to a device type instead of a specific device, it should return the string name of the device type.

If a driver extends the MCI_INFO command to request additional information, it should not return information that is returned for the MCI_STATUS command.

See Also

Providing Device Information


MCI_OPEN

MCI drivers do not receive the MCI_OPEN command. Instead, they receive MCI_OPEN_DRIVER. See “Opening an MCI Driver”.


MCI_OPEN_DRIVER

The MCI_OPEN_DRIVER message requests an MCI driver to open a driver instance.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
MCI_OPEN_DRIVER
lParam1
Contains the flags specified by the application, with the MCI_OPEN command.
lParam2
Specifies a pointer to an MCI_OPEN_PARMS structure, or to a customized structure.
Return Value

If the operation succeeds, the driver returns zero. Otherwise, the driver returns one of the MCIERR error codes defined in mmsystem.h.

Comments

A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameters. When an application sends an MCI_OPEN message, winmm.dll intercepts it and sends MCI_OPEN_DRIVER to the driver.

See Also

Opening an MCI Driver, MCI_CLOSE_DRIVER


MCI_STATUS

The MCI_STATUS message requests an MCI driver to return device status information.

Parameters
dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
MCI_STATUS
lParam1
Contains the flags specified by the application.
lParam2
Specifies a pointer to an MCI_STATUS_PARMS structure, or to a customized structure.
Return Value

If the operation succeeds, the driver returns zero. Otherwise, the driver returns one of tbe MCIERR error codes defined in mmsystem.h.

Comments

A client sends the message by calling the driver’s DriverProc entry point, passing the specified parameters.

A driver tests lParam1 to determine the type of information to return. The driver returns the requested information in the structure pointed to by lParam2. Refer to the Win32 SDK description of MCI_STATUS to determine the types of information the driver should return.

See Also

Providing Device Information


MCI Structures

This section describes the structures associated with the command messages that MCI drivers are required to support.

For descriptions of all other MCI command structures, refer to the Win32 SDK.


MCI_GENERIC_PARMS

typedef struct tagMCI_GENERIC_PARMS {
    DWORD   dwCallback;
} MCI_GENERIC_PARMS;

The MCI_GENERIC_PARMS structure is the data structure associated with the MCI_CLOSE_DRIVER command. This structure can be used with any command requiring only a minimal structure definition.

Members
dwCallback
Contains handle of window to receive MM_MCINOTIFY message.


MCI_GETDEVCAPS_PARMS

typedef struct tagMCI_GETDEVCAPS_PARMS {
    DWORD   dwCallback;
    DWORD   dwReturn;
    DWORD   dwItem;
} MCI_GETDEVCAPS_PARMS;

The MCI_GETDEVCAPS_PARMS structure is the standard data structure associated with the MCI_GETDEVCAPS command. To find out if customized versions of this structure exist, see the description of MCI_GETDEVCAPS in the Win32 SDK.

Members
dwCallback
Contains handle of window to receive MM_MCINOTIFY message.
dwReturn
Receives information returned by the driver.
dwItem
Contains a flag indicating the type of information requested. For a list of valid flags, see the description of MCI_GETDEVCAPS in the Win32 SDK. The contents of dwItem are valid only if MCI_GETDEVCAPS_ITEM is set in the lParam1 argument to DriverProc.


MCI_INFO_PARMS

typedef struct tagMCI_INFO_PARMS {
    DWORD   dwCallback;
    LPSTR   lpstrReturn;
    DWORD   dwRetSize;
} MCI_INFO_PARMS;

The MCI_INFO_PARMS structure is the standard data structure associated with the MCI_INFO command. To see if customized versions of this structure exist, refer to the description of MCI_INFO in the Win32 SDK.

Members
dwCallback
Contains handle of window to receive MM_MCINOTIFY message.
lpstrReturn
Contains pointer to buffer that the driver fills with return string.
dwRetSize
Contains size of return buffer.


MCI_OPEN_DRIVER_PARMS

typedef struct {
    MCIDEVICEID   wDeviceID;
    LPCWSTR  lpstrParams;
    UINT  wCustomCommandTable;
    UINT  wType;
} MCI_OPEN_DRIVER_PARMS;

The MCI_OPEN_DRIVER_PARMS structure is the data structure associated with the DRV_OPEN command for MCI drivers. See Opening an MCI Driver.

Members
wDeviceID
Contains the MCI device ID.
lpstrParams
Contains a pointer to a zero-terminated string. The string contains any characters that follow the filename in the system registry.
wCustomCommandTable
Receives a handle returned from mciLoadCommandResource, or MCI_NO_COMMAND_TABLE.
wType
Receives one of the following defined MCI device types:
  • MCI_DEVTYPE_ANIMATION

  • MCI_DEVTYPE_CD_AUDIO

  • MCI_DEVTYPE_DAT

  • MCI_DEVTYPE_DIGITAL_VIDEO

  • MCI_DEVTYPE_OVERLAY

  • MCI_DEVTYPE_SEQUENCER

  • MCI_DEVTYPE_SCANNER

  • MCI_DEVTYPE_VCR

  • MCI_DEVTYPE_VIDEODISC

  • MCI_DEVTYPE_WAVEFORM_AUDIO

If the driver does not support any of the defined types, it should set wType to MCI_DEVTYPE_OTHER.


MCI_OPEN_PARMS

typedef struct tagMCI_OPEN_PARMS {
    DWORD       dwCallback;
    MCIDEVICEID wDeviceID;
    WORD        wReserved0;
    LPCSTR      lpstrDeviceType;
    LPCSTR      lpstrElementName;
    LPCSTR      lpstrAlias;
} MCI_OPEN_PARMS;

The MCI_OPEN_PARMS structure is the standard data structure associated with the MCI_OPEN_DRIVER command. To find out if customized versions of this structure exist, see the description of MCI_OPEN in the Win32 SDK.

Members
dwCallback
Contains handle of window to receive MM_MCINOTIFY message.
wDeviceID
Contains the device ID.
wReserved0
Reserved.
lpstrDeviceType
Contains device type string, if MCI_OPEN_TYPE is set in the lParam1 argument to DriverProc.
lpstrElementName
Contains element name string, if MCI_OPEN_ELEMENT is set in the lParam1 argument to DriverProc.
lpstrAlias
Contains alias string, if MCI_OPEN_ALIAS is set in the lParam1 argument to DriverProc.


MCI_STATUS_PARMS

typedef struct tagMCI_STATUS_PARMS {
    DWORD   dwCallback;
    DWORD   dwReturn;
    DWORD   dwItem;
    DWORD   dwTrack;
} MCI_STATUS_PARMS;

The MCI_STATUS_PARMS structure is the standard data structure associated with the MCI_STATUS command. To find out if customized versions of this structure exist, see the description of MCI_STATUS in the Win32 SDK.

Members
dwCallback
Contains handle of window to receive MM_MCINOTIFY message.
dwReturn
Receives driver-supplied integer return value.
dwItem
Contains a flag indicating the type of information requested. For a list of valid flags, see the description of MCI_STATUS in the Win32 SDK. The contents of dwItem is valid only if MCI_STATUS_ITEM is set in the lParam1 argument to DriverProc.


MCI Macros

This topic describes macros available to MCI drivers.


MAKEMCIRESOURCE

LRESULT
MAKEMCIRESOURCE(
WORD wReturn,
WORD wResource
);

The MAKEMCIRESOURCE macro concatenates two word values to create a longword value. Its purpose is to create a longword value that contains both a constant value and a resource ID.

Parameters
wReturn
Specifies the constant value.
wResource
Specifies the resource ID.
Return Value

Returns the longword result of the concatenation.

Comments

The MAKEMCIRESOURCE macro is used for constructing a longword value that can be returned in a command structure’s dwReturn member. Structures for the MCI_GETDEVCAPS and MCI_STATUS commands contain this member. Extended commands can also include this structure member. The MAKEMICRESOURCE macro is used in combination with the MCI_RESOURCE_RETURNED and MCI_RESOURCE_DRIVER return values for DriverProc. See Returning Information to Applications.


Time Formatting Macros

The following time formatting macros are available to both MCI drivers and applications. The macros are defined in mmsystem.h and are described in the Win32 SDK.

MCI_HMS_HOUR
Retrieves the hours component from a parameter containing packed hours/minutes/seconds (HMS) information.
MCI_HMS_MINUTE
Retrieves the minutes component from a parameter containing packed hours/minutes/seconds (HMS) information.
MCI_HMS_SECOND
Retrieves the seconds component from a parameter containing packed hours/minutes/seconds (HMS) information.
MCI_MAKE_HMS
Creates a time value in packed hours/minutes/seconds (HMS) format from the given hours, minutes, and seconds values.
MCI_MAKE_MSF
Creates a time value in packed minutes/seconds/frames (MSF) format from the given minutes, seconds, and frame values.
MCI_MAKE_TMSF
Creates a time value in packed tracks/minutes/seconds/frames (TMSF) format from the given tracks, minutes, seconds, and frames values.
MCI_MSF_FRAME
Creates the frames component from a parameter containing packed minutes/seconds/frames (MSF) information.
MCI_MSF_MINUTE
Creates the minutes component from a parameter containing packed minutes/seconds/frames (MSF) information.
MCI_MSF_SECOND
Creates the seconds component from a parameter containing packed minutes/seconds/frames (MSF) information.
MCI_TMSF_FRAME
Retrieves the frames component from a parameter containing packed tracks/minutes/seconds/frames (TMSF) information.
MCI_TMSF_MINUTE
Retrieves the minutes component from a parameter containing packed tracks/minutes/seconds/frames (TMSF) information.
MCI_TMSF_SECOND
Retrieves the seconds component from a parameter containing packed tracks/minutes/seconds/frames (TMSF) information.
MCI_TMSF_TRACK
Retrieves the tracks component from a parameter containing packed tracks/minutes/seconds/frames (TMSF) information.


Audio Device Drivers

The following topics explain how to write audio device drivers for Windows NTâ:

  • Introduction to Audio Device Drivers

  • Designing a User-Mode Audio Driver

  • Designing a Kernel-Mode Audio Driver

  • Audio Driver Reference

For a general discussion of multimedia device drivers, refer to Introduction to Multimedia Drivers.


Introduction to Audio Device Drivers

The following topics provide an introductory information about audio device drivers:

  • Types of Audio Devices

  • Audio Software Components

  • The Standard Audio Driver, mmdrv.dll

  • Sample Audio Drivers


Types of Audio Devices

The following types of audio devices can be accessed by applications using API functions provided by the Windows NT Win32 subsystem:

  • Waveform input devices that convert analog audio signals into digital information.

  • Waveform output devices that convert digital audio information into analog audio signals.

  • MIDI input ports that receive MIDI messages from external MIDI devices, such as keyboards.

  • MIDI output ports that send MIDI messages to external MIDI devices, such as keyboards and drum machines.

  • Internal MIDI synthesizers, which are internal MIDI output devices that synthesize music from MIDI messages sent by applications.

  • Auxiliary audio devices such as CD players, which do not require data transfers but whose output can be mixed with MIDI and waveform audio, and whose volume can be controlled by a driver.

  • Mixer devices that control multiple audio lines.

Combination drivers are audio device drivers that can support several of these types of audio devices. The standard audio driver, mmdrv.dll, is a combination driver.


Audio Software Components

The following diagram illustrates the relationship of the major Windows NT audio software components.

The components in the diagram include:
  • Application

    Any user-mode, Win32-based application that calls the audio API functions described in the Win32 SDK.

  • winmm.dll

    A dynamic-link library that exports the waveform, MIDI, mixer, and auxiliary audio functions described in the Win32 SDK.

  • msacm32.dll

    A dynamic-link library that contains the Audio Compression Manager (ACM). For information about the ACM and ACM drivers, see Audio Compression Manager Drivers.

  • midimap.dll

    The Windows NT MIDI Mapper.

  • msacm32.drv

    The Windows NT Wave Mapper.

  • User-mode drivers

    Dynamic-link libraries that communicate with kernel-mode drivers (or sometimes other user-mode drivers).

  • ACM drivers

    Dynamic-link libraries under the control of the ACM. See msacm32.dll, above.

  • drvlib.lib

    A library used as a basis for user-mode audio drivers. For more information, see Using drvlib.lib.

  • Kernel-mode drivers

    Kernel-mode code that communicates with the Windows NT Executive in order to access device hardware.

  • soundlib.lib

    A library used as a basis for kernel-mode audio drivers. For more information, see Using soundlib.lib.


The Standard Audio Driver, mmdrv.dll

Microsoft provides a standard user-mode audio driver for Windows NT, called mmdrv.dll. It is a combination driver that supports waveform input and output devices, MIDI input and output devices, and auxiliary audio devices. It provides the user-mode audio driver entry points and support functions necessary for handling all of these device types.

If you are providing driver support for new audio hardware, you might be able to use mmdrv.dll as your user-mode driver. You can use mmdrv.dll if it provides access to all of the functionality provided by your kernel-mode driver. If you are able to use mmdrv.dll as your user-mode driver, then you only need to write a kernel-mode driver for your hardware.

If you want to use mmdrv.dll functionality but need to provide customized installation and configuration operations, consider using drvlib.lib, which is a library you can use to construct a customized user-mode driver, instead of using mmdrv.dll.

You can even use mmdrv.dll and drvlib.lib in combination. The user-mode driver for the Creative Labs Sound Blasterä does this. See Sample Audio Drivers and Examining sndblst.sys.

The MS-DOS device names supported by mmdrv.dll are defined in the ntddwave.h, ntddmidi.h, and ntddaux.h files. MS-DOS names are prepended with “\\.\”, as in “\\.\WaveOut0”.

Source code for mmdrv.dll is included with this DDK.


Sample Audio Drivers

This DDK includes the source code for the following audio drivers and libraries:
Driver or Library | Location of Source Files
User mode audio driver library (see Using drvlib.lib.) | \ddk\src\mmedia\drvlib
Standard user-mode audio driver | \ddk\src\mmedia\mmdrv
Media Vision ProAudio Spectrum 16 driver | \ddk\src\mmedia\mvaudio
Creative Labs Sound Blaster driver | \ddk\src\mmedia\sndblst
Windows sound system driver | \ddk\src\mmedia\sndsys
Kernel-mode driver library (see Using soundlib.lib.) | \ddk\src\mmedia\soundlib
Ad Lib and OPL3 MIDI synthesizer driver | \ddk\src\mmedia\synth
User-mode synthesizer driver library (see Using synthlib.lib.) | \ddk\src\mmedia\synthlib

For the driver samples, code for both the user-mode and the kernel-mode driver is provided. Under the listed driver directory, a \dll subdirectory contains the user-mode driver sources, and a \driver subdirectory contains the kernel-mode driver sources.

Within this chapter frequent reference is made to the user-mode and kernel-mode drivers for the Creative Labs Sound Blaster card. The kernel-mode driver is sndblst.sys. Two user-mode drivers call sndblst.sys: the standard audio driver, mmdrv.dll, handles MIDI input and output operations; a customized driver, sndblst.dll, handles waveform, mixer, synthesizer, and auxiliary I/O operations.


Designing a User-Mode Audio Driver

User-mode audio drivers are implemented as dynamic-link libraries. This section contains the following topics to assist you in designing a user-mode audio driver:

  • User-Mode Audio Driver Entry Points

  • User-Mode Audio Driver Messages

  • Controlling Waveform and MIDI Devices

  • Controlling Auxiliary Audio Devices

  • Controlling Mixers

  • Notifying Clients from Audio Drivers

  • Using drvlib.lib

  • Using synthlib.lib

  • General Design Guidelines


User-Mode Audio Driver Entry Points

Like all installable user-mode drivers, user-mode audio drivers must export a DriverProc entry point and support the standard driver messages. Audio drivers typically ignore the DRV_OPEN standard message.

Audio drivers must also export one or more of the following entry points:
auxMessage | Entry point for auxiliary audio drivers.
midMessage | Entry point for MIDI input drivers.
modMessage | Entry point for MIDI output drivers.
mxdMessage | Entry point for mixer drivers.
widMessage | Entry point for waveform input drivers.
wodMessage | Entry point for waveform output drivers.

Like the DriverProc entry point, these additional entry points are implemented as functions that receive and process messages. Applications control multimedia device operations by calling multimedia API functions, which are described in the Win32 SDK and defined in winmm.dll. Code within winmm.dll translates each API function call into a call to a driver entry point, and includes one of the user-mode audio driver messages. For example, when an application calls the Win32 WaveOutOpen function, winmm.dll calls the appropriate user-mode driver’s wodMessage entry point, passing a WODM_OPEN message.


User-Mode Audio Driver Messages

The following table lists the messages that each driver entry point can receive. Messages are divided into those that the driver is required to support, and those that the driver can optionally support. Message definitions are contained in mmddk.h.
Entry Points | Required Messages | Optional Messages
auxMessage | AUXDM_GETDEVCAPS AUXDM_GETNUMDEVS | AUXDM_GETVOLUME AUXDM_SETVOLUME
midMessage | MIDM_ADDBUFFER MIDM_CLOSE MIDM_GETDEVCAPS MIDM_GETNUMDEVS MIDM_OPEN MIDM_RESET MIDM_START MIDM_STOP | MIDM_PREPARE MIDM_UNPREPARE
modMessage | MODM_CACHEDRUMPATCHES MODM_CACHEPATCHES MODM_CLOSE MODM_DATA MODM_GETDEVCAPS MODM_GETNUMDEVS MODM_LONGDATA MODM_OPEN MODM_RESET | MODM_GETVOLUME MODM_PREPARE MODM_SETVOLUME MODM_UNPREPARE
mxdMessage | MXDM_CLOSE MXDM_GETCONTROLDETAILS MXDM_GETDEVCAPS MXDM_GETLINECONTROLS MXDM_GETLINEINFO MXDM_GETNUMDEVS MXDM_OPEN MXDM_SETCONTROLDETAILS | MXDM_INIT
widMessage | WIDM_ADDBUFFER WIDM_CLOSE WIDM_GETDEVCAPS WIDM_GETNUMDEVS WIDM_GETPOS WIDM_OPEN WIDM_RESET WIDM_START WIDM_STOP | WIDM_LOWPRIORITY WIDM_PREPARE WIDM_UNPREPARE
wodMessage | WODM_BREAKLOOP WODM_CLOSE WODM_GETDEVCAPS WODM_GETNUMDEVS WODM_GETPOS WODM_OPEN WODM_PAUSE WODM_RESET WODM_RESTART WODM_WRITE | WODM_GETPITCH WODM_GETPLAYBACKRATE WODM_GETVOLUME WODM_PREPARE WODM_SETPITCH WODM_SETPLAYBACKRATE WODM_SETVOLUME WODM_UNPREPARE

Note: If you are writing a driver for a waveform device that supports compressed data formats, your driver must also support some of the Audio Compression Manager (ACM) messages. For further information, see Providing ACM Support in Device Drivers, which is contained in the Audio Compression Manager Drivers chapter.


Controlling Waveform and MIDI Devices

The following topics describe the methods used for controlling input and output data transfers for both waveform and MIDI devices:

  • Introduction to Transferring Audio Data

  • Transferring Waveform Input Data

  • Transferring Waveform Output Data

  • Transferring MIDI Input Data

  • Transferring MIDI Output Data

Within these topics, the term client is used to refer to any higher-level software that is making calls into the user-mode driver. This higher-level software could be an application or, more likely, it could be an intermediate interface called by an application and implemented in winmm.dll.


Introduction to Transferring Audio Data

A primary responsibility of user-mode waveform and MIDI drivers is to pass streams of data between clients and kernel-mode drivers. For both input and output operations, clients first allocate data buffers and request the user-mode driver to prepare the buffers for use.

For input operations, the client passes empty buffers to the user-mode driver, which then requests data from the kernel-mode driver. The kernel-mode driver reads data from the device and passes it back the user-mode driver, which then fills the buffers. The user-mode driver notifies the client each time a buffer is filled, so that the client can copy the data from the buffer. The client can then re-use the buffer or, if all data has been received, it can request the user-mode driver to remove the buffer’s preparation, and then deallocate the buffer.

For output operations, the client fills the buffers with data and begins passing them to the user-mode driver. The user-mode driver reads the data and passes it to the kernel-mode driver, which in turn sends the data to the device. The user-mode driver notifies the client when each buffer has been read. The client can then re-use the buffer or, if all data has been sent, it can request the user-mode driver to remove the buffer’s preparation, and then deallocate the buffer. (Some MIDI output operations do not use buffers.)

For more details about transferring audio data, see:

  • Transferring Waveform Input Data

  • Transferring Waveform Output Data

  • Transferring MIDI Input Data

  • Transferring MIDI Output Data

The algorithms described in these topics are implemented in drvlib.lib and mmdrv.dll.


Transferring Waveform Input Data

For waveform input operations, clients call the user-mode driver’s widMessage function. The user-mode driver should expect the client to first send a WIDM_OPEN message to open a driver instance. Next, the client allocates memory for one or more data buffers and sends WIDM_PREPARE messages to prepare the buffers for use. The client then sends a WIDM_ADDBUFFER message for each buffer, which passes the address of the empty buffer to the user-mode driver. The user-mode driver keeps a list of available empty buffers.

To start the read operation, the client sends WIDM_START. The user-mode driver then uses a separate thread to begin requesting data from the kernel-mode driver, typically by calling ReadFile or ReadFileEx, and filling the empty buffers. Each time a buffer has been filled, the user-mode driver notifies the client by sending it a WIM_DATA callback message. The client copies the data from the buffer and re-adds the buffer to the user-mode driver’s buffer list by sending another WIDM_ADDBUFFER message.

When the client has finished the input operation, it sends WIDM_STOP. It can also send WIDM_RESET, which indicates to the user-mode driver that it should not fill any remaining data buffers. The client can then send a WIDM_UNPREPARE message for each buffer and deallocate the buffers. Finally, the driver should expect the client to close the instance by sending WIDM_CLOSE.


Transferring Waveform Output Data

For waveform output operations, clients call the user-mode driver’s wodMessage function. User-mode drivers should expect the client to first send a WODM_OPEN message to open a driver instance. Next, the client allocates memory for one or more data buffers and sends WODM_PREPARE messages to prepare the buffers for use.

The client then begins filling the buffers and sending WODM_WRITE messages, which include a buffer address. The user-mode driver keeps a queue of buffer addresses. When the user-mode driver starts receiving WODM_WRITE messages, it uses a separate thread to begin sending the received data to the kernel-mode driver, typically by calling WriteFile or WriteFileEx. Each time the user-mode driver reads a buffer and sends the data to the kernel-mode driver, it dequeues the buffer and sends the client a WOM_DONE callback message to notify it that the buffer has been read. The client can then re-use the buffer by specifying its address with another WODM_WRITE message.

When the client has finished the output operation, it stops sending WODM_WRITE messages. It can also send WODM_RESET, which indicates to the user-mode driver that it should not dequeue any remaining data buffers. The client can then send a WODM_UNPREPARE message for each buffer and deallocate the buffers. Finally, the driver should expect the client to close the instance by sending WODM_CLOSE.


Waveform Looping

Waveform looping is the repeated output of a set of data buffers. Clients mark the beginning and ending buffers of a loop by setting flags in the WAVEHDR buffer header, which is part of each buffer passed with a WODM_WRITE message. Clients also specify the number of loop iterations. User-mode drivers repeatedly output the contents of the buffers defining the loop, until the client sends either a WODM_BREAKLOOP or a WODM_RESET message, or until the iteration count has been reached.


Transferring MIDI Input Data

For MIDI input operations, clients call the user-mode driver’s midMessage function. The user-mode driver should expect the client to first send a MIDM_OPEN message to open a driver instance. Next, the client allocates memory for one or more data buffers and sends MIDM_PREPARE messages to prepare the buffers for use. The client then sends a MIDM_ADDBUFFER message for each buffer, which passes the address of the empty buffer to the user-mode driver. The user-mode driver keeps a queue of available empty buffers.

To start the read operation, the client sends MIDM_START. The user-mode driver then uses a separate thread to begin requesting data from the kernel-mode driver, typically by calling ReadFileEx. The user-mode driver receives a buffer of MIDI data that can consist of a combination of short MIDI messages, or single events, and long MIDI messages, or system-exclusive events. (For descriptions of MIDI events, see the Standard MIDI Files 1.0 specification.) The user-mode driver must parse the bytes received and do the following:

  • Create a time stamp (see Adding Time Stamps, below).

  • If the received event is system-exclusive, place the event’s bytes in the next available buffer from the queue of client buffers. If the buffer becomes full, notify the client with a MIM_LONGDATA callback message. The client can read the buffer and re-use it by sending another MIDM_ADDBUFFER message.

  • If the received event is not system-exclusive, check the message to see if running status (see Running Status, below) is in effect and if so, add the previous status byte to the event. (All events passed to clients must include a status byte.) Then pass the event’s bytes to the client with a MIM_DATA callback message.

When the client has finished the input operation, it sends MIDM_STOP. It can also send MIDM_RESET, which indicates to the user-mode driver that it should not fill any remaining data buffers. The client can then send a MIDM_UNPREPARE message for each buffer and deallocate the buffers. Finally, the driver should expect the client to close the instance by sending MIDM_CLOSE.

Adding Time Stamps

All MIDI events that are returned to a client must include a time stamp. The time stamp represents the number of milliseconds that have passed since input began. When a client sends the MIDM_START message, the kernel-mode driver saves the current system time to use as a reference time. Then each time the kernel-mode driver reads an event, it saves a time stamp value equal to the difference between the current time and the reference time.

Running Status

MIDI events might or might not include a status byte. If the status byte is not included, then the client is employing running status. This means that the last status byte sent is still in effect and need not be re-sent. When a user-mode driver receives a MODM_DATA message it checks the status byte and if no value is present, it does not pass the byte to the kernel-mode driver.

Handling MIDI Thru

Code within winmm.dll supports MIDI thru-ing to the extent that it will connect one MIDI input driver to one MIDI output driver. You can write a thru-ing driver by responding to DRVM_ADD_THRU and DRVM_REMOVE_THRU messages within midMessage and modMessage. For more information, see the discussion of managing MIDI thru-ing and the description of midiConnect in the Win32 SDK.


Transferring MIDI Output Data

Clients can send output data to user-mode MIDI drivers as either short MIDI messages or long MIDI messages. A short MIDI message contains a single MIDI event. The event is passed as an argument to the user-mode driver’s modMessage function. Long MIDI messages consist of a buffer of MIDI events, including MIDI system exclusive events. The buffer address is passed as an argument to the user-mode driver’s modMessage function. (For descriptions of MIDI events, see the Standard MIDI Files 1.0 specification.)

For MIDI output operations, clients call the user-mode driver’s modMessage function. User-mode drivers should expect the client to first send a MODM_OPEN message to open a driver instance. If the client will be sending long MIDI messages, it allocates memory for one or more data buffers and sends MODM_PREPARE messages to prepare the buffers for use. The client then begins sending either short messages or long messages.

To send a short message, the client sends a MODM_DATA message and includes the message data. Since MIDI events can be one, two, or three bytes in length, the user-mode driver must examine the event’s status field to determine how many bytes are valid. It then passes the proper number of bytes to the kernel-mode driver by calling DeviceIoControl.

To send a long message, the client places the message in a buffer and sends a MODM_LONGDATA message, which includes the buffer’s address as an argument. The user-mode driver does not examine the contents of a long message; it just passes the buffer contents to the kernel-mode driver by calling DeviceIoControl.

When the user-mode driver begins receiving MODM_DATA and MODM_LONGDATA messages, it uses a separate thread to begin sending the received data to the kernel-mode driver, typically by calling DeviceIoControl. After the data has been sent to the kernel-mode driver, the user-mode driver notifies the client by sending it a MOM_DONE callback message. Clients can re-use a MODM_LONGDATA buffer by refilling it and including it with a subsequent MODM_LONGDATA message.

When the client has finished the output operation, it stops sending MODM_DATA and MODM_LONGDATA messages. It can also send MODM_RESET, which indicates to the user-mode driver that it should not dequeue any remaining data buffers. The client can then send a MODM_UNPREPARE message for each buffer and deallocate the buffers. Finally, the driver should expect the client to close the instance by sending MODM_CLOSE.

MIDI Output Streams

Clients create MIDI streams by using the MIDI stream functions. These functions are defined within winmm.dll and described in the Win32 SDK. Code in winmm.dll translates the MIDI stream functions into MODM_DATA and MODM_LONGDATA messages. Therefore, MIDI drivers are not aware of stream operations. Each MODM_LONGDATA message buffer contains a single MIDI event that can be passed directly to the driver by calling DeviceIoControl.


Controlling Auxiliary Audio Devices

To control auxiliary audio device operations, clients call the user-mode driver’s auxMessage function. Clients do not need to open or close auxiliary audio devices. The user-mode driver should expect to receive AUXDM_GETDEVCAPS, AUXDM_GETNUMDEVS, AUXDM_GETVOLUME, and AUXDM_SETVOLUME messages in any order.


Controlling Mixers

To control mixer operations, clients call the user-mode driver’s mxdMessage function. The user-mode driver should first expect the client to send a MXDM_OPEN message to open a driver instance. Then the driver can expect to receive MXDM_GETLINEINFO, MXDM_GETLINECONTROLS, MXDM_GETCONTROLDETAILS, and MXDM_SETCONTROLDETAILS messages from the opened the instance, until the client sends MXDM_CLOSE.


Notifying Clients from Audio Drivers

User-mode drivers are responsible for notifying clients upon the completion of various audio operations. Clients indicate the type of notification, if any, they expect when they open a driver instance for waveform, MIDI, or mixer operations. (Refer to MIDM_OPEN, MODM_OPEN, MXDM_OPEN, WIDM_OPEN, and WODM_OPEN messages.) Clients that request notification can specify any of the following notification targets:

  • A callback function

  • A window handle

  • An event handle

  • A thread identifier

Mixer drivers accept only window handles.

User-mode drivers notify clients by calling the DriverCallback function in winmm.dll. This function delivers a message to the client’s notification target. The function also delivers message parameters, if the target type accepts parameters.

Following are the messages user-mode drivers must send to a client if the client has requested notification:
Operations | Messages
Waveform Input | WIM_CLOSE WIM_DATA WIM_OPEN
Waveform Output | WOM_CLOSE WOM_DONE WOM_OPEN
MIDI Input | MIM_CLOSE MIM_DATA MIM_ERROR MIM_LONGDATA MIM_LONGERROR MIM_MOREDATA MIM_OPEN
MIDI Output | MOM_CLOSE MOM_DONE MOM_OPEN MOM_POSITIONCB
Mixer |


Using drvlib.lib

The user-mode library drvlib.lib provides the following:

  • A standard set of user-mode audio driver entry points for waveform, MIDI, mixer, and auxiliary user-mode drivers.

  • A communication path to the device’s kernel-mode driver. This path consists of:

    • Calling the Windows NT I/O Manager (by means of DeviceIoControl, described in the Win32 SDK) to send I/O control codes to the kernel-mode driver.

    • Calling ReadFileEx and WriteFileEx (described in the Win32 SDK) to pass data between the user-mode and kernel-mode drivers.

    For information on I/O control codes for multimedia drivers, see the Kernel-Mode Drivers Reference.

  • A set of functions for creating and managing a kernel-mode driver as a Windows NT service, and for easily accessing driver keys within the Windows NT Registry. These functions are useful for installation and configuration operations, and are listed in Installing and Configuring your Driver, Using drvlib.lib.

The entry points, support functions, and communications path to the kernel-mode driver are identical to those provided by the standard audio driver, mmdrv.dll. This library is particularly useful if your user-mode driver requires only the functionality of mmdrv.dll, with the addition of customized installation and configuration operations. In such a case, you would create a module containing a customized DriverProc function, and then link it with drvlib.lib. To initialize the library, call DrvLibInit when your driver is loaded.

If your driver requires customized audio entry point functions, you might still want to base your functions on the those contained in drvlib.lib. Sources for drvlib.lib are provided with this DDK, in the directory path listed in Sample Audio Drivers.


Installing and Configuring your Driver, Using drvlib.lib

User-mode drivers can call functions in drvlib.lib to start the kernel-mode driver and to modify registry keys. The most commonly used functions include:

DrvLibInit
Initializes drvlib.lib for use with your user-mode driver.
DrvCreateServicesNode
Creates a connection to the Windows NT service control manager and, optionally, creates a service object for the kernel-mode driver.
DrvConfigureDriver
Opens a connection to the service control manager, creates a kernel-mode driver service for the specified driver, and loads the kernel-mode driver. This function is typically called when DriverProc receives a DRV_INSTALL or DRV_CONFIGURE message.
DrvCloseServiceManager
Closes a connection to the service control manager.
DrvCreateDeviceKey
Creates a device subkey under the driver’s \Parameters registry key. You should store a device’s configuration parameters under this key.
GetInterruptsAndDma
Examines the registry to determine which interrupt numbers and DMA channels are assigned to devices.
DrvSetDeviceIdParameter
Assigns a value to a value name in the registry, under a device’s \Parameters key. Use this function to save configuration parameters.
DrvQueryDeviceIdParameter
Reads the value associated with a value name, under a device’s \Parameters registry key. Use this function to read configuration parameters you have stored in the registry.
DrvRemoveDriver
Unloads the kernel-mode driver and marks the kernel-mode driver service for deletion. Typically called when DriverProc receives a DRV_REMOVE message.

Other installation and configuration functions provided by drvlib.lib are DrvSaveParametersKey, DrvRestoreParametersKey, DrvDeleteServicesNode, DrvLoadKernelDriver, DrvUnloadKernelDriver, DrvIsDriverLoaded, DrvNumberOfDevices, and DrvSetMapperName.


Using synthlib.lib

The synthlib.lib library provides support for internal MIDI synthesizers by converting MIDI output messages into calls to MIDI FM synthesis functions. The library supports ADLIB and OPL3 synthesizers.

The library contains an alternate modMessage entry point function called modsynthMessage. If you choose to use this library, link it with your user-mode driver. The driver’s module definition file must contain the following export line:

modMessage = modsynthMessage

Source code for synthlib.lib is provided with this DDK, at the directory path listed in Sample Audio Drivers.


General Design Guidelines

You most important guideline is to make sure the driver design adheres to the function descriptions provided by the Win32 SDK. Oftentimes the messages, entry point function parameters, and return codes described within this chapter correlate to Win32 API functions, parameters, and return codes described in the Win32 SDK. This chapter provides cross references to function descriptions in the Win32 SDK.

For MIDI file format descriptions, see the MIDI 1.0 Detailed Specification and Standard MIDI Files 1.0.


Designing a Kernel-Mode Audio Driver

Kernel-mode audio drivers are responsible for accessing audio hardware. They are implemented as services under the control of the Windows NT service control manager.

The following topics are provided to assist you in designing a kernel-mode audio driver:

  • Using soundlib.lib

  • Examining sndblst.sys

  • Initializing and Configuring a Driver

  • Synchronizing Driver Activities

  • Supporting Waveform Devices

  • Supporting MIDI Devices

  • Supporting Mixer Devices

  • Supporting Auxiliary Audio Devices

For general information on the structure of kernel-mode drivers, refer to the Kernel-Mode Drivers Design Guide and Kernel-Mode Drivers Reference in this DDK.


Using soundlib.lib

Kernel-mode audio drivers can use the soundlib.lib library. For waveform, MIDI, auxiliary audio, and mixer drivers, soundlib.lib provides the following:

  • Functions that the kernel-mode driver can call for initialization, configuration, and synchronization activities.

  • A set of dispatch functions that the driver can use for handling IRPs and I/O control codes, which are received from the Windows NT I/O Manager as a result of calls from user-mode drivers.

For information on IRPs and I/O control codes for multimedia drivers, see the Kernel-Mode Drivers Reference.

You can use soundlib.lib to make creating a new kernel-mode driver easier. To use soundlib.lib, link it with your kernel-mode driver. You’ll need to include some or all of the following header files:
devices.h | mtddmix.h
midi.h | ntddwave.h
mixer.h | soundcfg.h
mmsystem.h | soundlib.h
ntddk.h | synthdrv.h
ntddaux.h | wave.h
ntddmidi.h |

If you include soundlib.h, it includes many, but not all, of the other header files listed.

Source code for soundlib.lib is included with this DDK, at the directory path listed in Sample Audio Drivers.

Descriptions of the functions and structures provided by soundlib.lib can be found in the Audio Driver Reference.


Examining sndblst.sys

To illustrate both the construction of a kernel-mode driver and the use of soundlib.lib, the following sections examine the sample kernel-mode driver sndblst.sys. This driver supports the Creative Labs Sound Blaster card. It is called from the user-mode driver sndblst.dll, for waveform, mixer, and auxiliary I/O operations. For MIDI operations, sndblst.sys is called from the standard audio driver, mmdrv.dll. Source code for sndblst.sys, sndblst.dll, and mmdrv.dll are included with this DDK, at the directory paths listed in Sample Audio Drivers. When you read the topics within this section, it is helpful to refer to the source code for sndblst.sys.


Initializing and Configuring a Driver

The following topics examine how the kernel-mode audio driver, sndblst.sys, uses soundlib.lib functions to perform initialization and configuration operations:

  • Examining DriverEntry in sndblst.sys

  • Hardware and Driver Initialization

  • Handling System Shutdown

  • Using I/O Ports

  • Using Interrupts

  • Using DMA Channels


Examining DriverEntry in sndblst.sys

Like all kernel-mode drivers, sndblst.sys supplies an initialization function called DriverEntry to handle initialization and configuration operations. Because this code is only executed once, it is located in the driver’s INIT data segment, which is marked as discardable.

For more information about DriverEntry, see DriverEntry for Multimedia Drivers.

The DriverEntry function for sndblst.sys is located in \src\mmedia\sndblst\driver\init.c. As a first step, the function initializes the received device object’s dispatch table. Drivers using soundlib.lib must specify SoundDispatch as the dispatcher for the IRP_MJ_CLEANUP, IRP_MJ_CLOSE, IRP_MJ_CREATE, IRP_MJ_DEVICE_CONTROL, IRP_MJ_READ ,and IRP_MJ_ WRITE control codes.

The DriverEntry function calls SoundEnumSubkeys in soundlib.lib to locate the registry entry for each card. For each card found, SoundEnumSubkeys calls back into SoundCardInstanceInit, also located in init.c. If any callback to SoundCardInstanceInit returns a failure, SoundEnumSubkeys returns immediately. After SoundEnumSubkeys returns, DriverEntry calls SoundWriteRegistryDWORD to write each card’s initialization status into the registry.

Note: The DriverEntry function for sndblst.sys searches for multiple cards. Both soundlib.lib and the registry’s multimedia device keys can handle multiple sound cards. However, the current implementation of sndblst.sys references only one card for I/O operations.


Hardware and Driver Initialization

The DriverEntry function in sndblst.sys calls SoundEnumSubkeys, which in turn calls the SoundCardInstanceData function in sndblst.sys (defined in \src\mmedia\sndblst\driver\init.c) for each card described in the registry. For each card, SoundCardInstanceInit is responsible for:

  • Saving the registry path

  • Allocating device-specific memory

  • Finding the bus number and type

  • Obtaining configuration parameters

  • Creating device objects

  • Acquiring hardware resources

  • Updating configuration information in the registry.
Saving the Registry Path

When SoundCardInstanceInit is called, it receives a pointer to a registry path. This path is the full path to a key representing a hardware device, such as HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number. The driver saves this path in device-specific storage for later use with soundlib.lib functions requiring a registry path as input.

Note: Drivers that do not support multiple hardware devices do not call SoundEnumSubkeys. Instead, they call SoundSaveRegistryPath to store the registry path for later use with soundlib.lib functions requiring a registry path as input.

Allocating Device-Specific Memory

Waveform output and MIDI input drivers use interrupts and therefore provide interrupt service routines (ISRs) and deferred procedure calls (DPCs), as discussed in the Kernel-Mode Drivers Design Guide. All data that these routines access must be nonpaged. The driver allocates card-specific memory from nonpaged pool in a GLOBAL_DEVICE_INFO structure and adds the structure to a linked list.

Finding the Bus Number and Type

Calls to SoundGetBusNumber are used to determine which buses exist. Code in SoundCardInstanceData checks for ISA, EISA, and Microchannel buses, in that order. The code assumes that the Sound Blaster card is connected to the first of these buses that it finds.

This code can be modified to provide a more sophisticated bus search.

Different buses do not require different kernel-mode drivers. The Windows NT Hardware Abstraction Layer (HAL) insulates the kernel-mode driver from the bus.

Obtaining Configuration Parameters

Another task of SoundCardInstanceInit is copying device configuration information from the registry into the device’s GLOBAL_DEVICE_INFO structure. Calling RtlQueryRegistryValues is an easy way to read registry values. The registry path to the card’s subkeys are passed to SoundCardInstanceInit by SoundEnumSubKeys.

For the Sound Blaster card, configuration information stored in the registry includes the card I/O address, interrupt number, and DMA channel number. Usually, configuration values stored in the registry are values that can be set on the card. They can also be read-only values. Values that can be referenced only indirectly, such as those on the Sound Blaster Pro Card that can only be accessed through a device interrupt, can also be stored in the registry.

Creating Device Objects

SoundCreateDevice is used to create a new sound device object. SoundSaveDeviceName stores the device name under the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices. Code in drvlib.lib finds the name there when the user-mode driver opens the device. The \Devices subkey is volatile and thus isn’t written to disk.

Acquiring Hardware Resources

Before a kernel-mode driver can use hardware resources, such as DMA channels, I/O ports, and interrupts, the resources must be acquired for use. The SoundReportResourceUsage function determines if specified resources are already assigned to another piece of hardware and, if they are not, assigns them to the caller.

Updating Configuration Information in the Registry

The SoundWriteRegistryDWORD function is called if it is necessary to update hardware configuration information stored in the registry.


Handling System Shutdown

For cards with mixer devices, call IoRegisterShutdownNotification so that the IRP_MJ_SHUTDOWN entry point in the driver object is called when the system is shut down. Within the shutdown routine, save the mixer settings in the registry.


Using I/O Ports

After sndblst.sys acquires an I/O port address range by calling SoundReportResourceUsage, it calls SoundMapPortAddress to map the physical address range to a virtual range. The virtual addresses can be used as input to HAL functions, such as READ_PORT_UCHAR, and WRITE_PORT_UCHAR, as described in the Kernel-Mode Drivers Reference.

Drivers call also access an address by first calling HalTranslateBusAddress (or a related function) to map the address, and then calling READ_REGISTER_UCHAR, WRITE_REGISTER_UCHAR and related functions.

Drivers must not access port addresses directly, or they will not be portable.


Using Interrupts

After sndblst.sys calls SoundReportResourceUsage to acquire an interrupt number, it calls SoundConnectInterrupt to connect an interrupt service routine to the interrupt. After this call is made, interrupts can be received.


Using DMA Channels

After sndblst.sys calls SoundReportResourceUsage to acquire a DMA channel, it calls HalGetAdapter to acquire an adapter object.

Since sndblst.sys uses auto-initialize DMA for transferring data to and from waveform devices, it must acquire an adapter object and allocate a buffer for DMA transfers. The buffer must be noncached and sharable between the driver and the HAL. So, sndblst.sys calls the SoundGetCommonBuffer function in soundlib.lib, which in turn calls HalGetAdapter to acquire the adapter object and HalAllocateCommonBuffer to allocate the buffer. (HalAllocateCommonBuffer is more commonly used for bus-mastering devices.) It also allocates a memory descriptor list.

Since sndblst.sys requires a second DMA channel without an additional DMA buffer, it calls HalGetAdapter directly for the second DMA Channel.


Synchronizing Driver Activities

The kernel-mode audio drivers, including the Sound Blaster driver, do not provide a StartIo function, so they must manage their own queues of I/O Request Packets (IRPs). You must consider synchronization issues under the following circumstances:

  • Opening and closing devices.

  • Handling I/O requests, which are sent when a client calls ReadFile, WriteFile, or DeviceIoControl, and which are received in IRPs.

  • Sharing hardware. For instance, MIDI input and output hardware sometimes uses the same I/O ports as waveform input and output hardware.

  • Synchronizing with Deferred Procedure Call routines (DPCs).

  • Synchronizing with Interrupt Service Routines (ISRs).

Most of the time, kernel-mode audio drivers execute at PASSIVE_LEVEL priority. Synchronization can be achieved by:

  • Using exclusion routines

  • Using spin locks

  • Using KeSynchronizeExecution

  • Using system worker threads


Using Exclusion Routines

Drivers using soundlib.lib must define an exclusion routine that accepts input messages. The exclusion routine is called under the following circumstances:

  • When a device is opened for writing.

    The exclusion routine receives a SoundExcludeOpen message.

  • When a device, previously opened for writing, is closed.

    The exclusion routine receives a SoundExcludeClose message.

  • When a request is directed to a device opened for writing.

    The exclusion routine receives a SoundExcludeEnter message.

  • When a request to a device opened for writing is complete.

    The exclusion routine receives a SoundExcludeLeave message.

  • When the caller wants to confirm that the drive is open.

    The exclusion routine receives a SoundExcludeQueryOpen message.

The exclusion routine is specified as the ExclusionRoutine member of a SOUND_DEVICE_INIT structure. Exclusion messages are defined in devices.h. In sndblst.sys, the exclusion routine is called SoundExcludeRoutine and is contained in the file \src\mmedia\sndblst\driver\init.c. The function’s purpose is to implement mutual exclusion during write operations. For sndblst.sys, SoundExcludeRoutine does this by calling KeWaitForSingleObject when it receives the SoundExcludeEnter message and KeReleaseMutex when it receives SoundExcludeLeave.

For information on mutexes, see the Kernel-Mode Drivers Design Guide.


Using Spin Locks

Code that references the same objects that the driver’s deferred procedure call (DPC) function references must be synchronized to avoid simultaneous attempts at referencing the same object. Drivers can acquire a spin lock at a specified IRQL in order to prevent other processors, or other code running at a lower IRQL, from simultaneously referencing an object. To synchronize access to objects referenced by a DPC function, the spin lock is obtained at an IRQL of DISPATCH_LEVEL.

Spin locks are used within soundlib.lib.

The Kernel-Mode Drivers Design Guide provides an extensive discussion of spin locks.


Using KeSynchronizeExecution

Code that references the same objects that the driver’s interrupt service routine (ISR) references must be synchronized to avoid simultaneous attempts at referencing the same object. Drivers must use KeSynchronizeExecution to achieve this synchronization. For example, sndblst.sys provides synchronization routines that set up the hardware for either DSP or mixer operations. Then a single ISR is provided to handle interrupts. The synchronization routines provided by sndblst.sys are contained in \src\mmedia\sndblst\driver\hardware.c.


Using System Worker Threads

It is sometimes useful to delegate the completion of some non-time-critical tasks to a system worker thread. An example is the quiescing of some older, slower sound cards after wave output has completed.

For wave devices, soundlib.lib queues a worker routine that you specify as the HwStopDMA member in a WAVE_INFO structure. This routine is called after your DPC completes. (For more information, see SOUND_DEVICE_INIT.) Code in soundlib.lib calls ExInitializeWorkItem and ExQueueWorkItem to cause a system worker thread to execute the function pointed to by HwStopDMA in WAVE_INFO (which, in sndblst.sys, happens to be a function called HwStopDMA).


Supporting Waveform Devices

Of the types of operations supported by sndblst.sys, waveform I/O operations are the most complex. Waveform operations are complex because they require the use of both interrupts and auto-initialize DMA. Functions within soundlib.lib support both interrupt and DMA operations for waveform devices. To use soundlib.lib for handling waveform devices, you must:

  • Within the driver object received by DriverEntry, assign SoundDispatch to be the driver’s main dispatcher for IRP control codes. See “Examining DriverEntry in sndblst.sys.”

  • Define a SOUND_DEVICE_INIT structure for waveform input, and another for waveform output. The structures’ DispatchRoutine members must be set to the address of SoundWaveDispatch, which is the waveform dispatch routine within soundlib.lib for DeviceIoControl messages.

  • Define a WAVE_INFO structure for each waveform device that can be in operation simultaneously. If your device can only support waveform input or output at one time, then only one WAVE_INFO structure is needed. If input and output can occur simultaneously, two structures must be defined. In sndblst.sys, only one WAVE_INFO structure is defined because input and output cannot occur simultaneously.

  • Call SoundCreateDevice once for each SOUND_DEVICE_INIT structure you have defined.


Supporting MIDI Devices

Functions in soundlib.lib provide support for MIDI synthesizers and external MIDI devices. Both Ad Lib and OPL3 synthesizer types are supported. External MIDI devices are supported in UART mode. All of these capabilities are utilized by sndblst.sys.

Supporting MIDI Synthesizers

To make use of the synthesizer functions provided in soundlib.lib, do the following:

  • Within the driver object received by DriverEntry, assign SoundDispatch to be the driver’s main dispatcher for IRP control codes. See “Examining DriverEntry in sndblst.sys.”

  • Call the SynthInit function for each card, during hardware and driver initialization.

  • Call the SynthCleanup function for each card, just before the driver is unloaded.

Additionally, if your hardware generates an interrupt for timer expiration, your driver must provide code to connect to the interrupt and to dismiss the interrupt. This interrupt is not handled by soundlib.lib.

Supporting External MIDI Devices

To use soundlib.lib for handling external MIDI devices, you must:

  • Within the driver object received by DriverEntry, assign SoundDispatch to be the driver’s main dispatcher for IRP control codes. See “Examining DriverEntry in sndblst.sys.”

  • Define a SOUND_DEVICE_INIT structure for MIDI input, and another for MIDI output. The structures’ DispatchRoutine members must be set to the address of SoundMidiDispatch, which is the MIDI dispatch routine within soundlib.lib for DeviceIoControl messages.

  • Define a MIDI_INFO structure. A single MIDI_INFO structure can support both MIDI output and MIDI input.

  • Call SoundCreateDevice once for each SOUND_DEVICE_INIT structure you have defined.


Supporting Mixer Devices

User-mode mixer drivers send IOCTL_MIX_REQUEST_NOTIFY messages to kernel-mode drivers to request notification of changes to line and control information. If a user-mode driver uses drvlib.lib, code in drvlib.lib begins continually calling DeviceIoControl, sending IOCTL_MIX_REQUEST_NOTIFY messages, after MXDM_OPEN is received. (This assumes the client has stipulated change notification when sending MXDM_OPEN.) Functions are provided in soundlib.lib to assist kernel-mode drivers in responding to IOCTL_MIX_REQUEST_NOTIFY messages. Code in soundlib.lib’s SoundMixerDispatch queues the IRPs associated with these notification requests.

Kernel-mode drivers call the SoundMixerChangedItem function to queue information about line and control changes. This function, in turn, dequeues the queued IRPs, writes changed information into each IRP structure, and calls IoCompleteRequest to complete the I/O request and pass the changed information back to drvlib.lib, in user mode.

Kernel-mode drivers call SoundSetLineNotify to register a routine that soundlib.lib calls whenever the status of a line changes. For wave devices, soundlib.lib calls this routine whenever the device state should change from inactive to active, or vice versa. The routine typically sets hardware appropriately and calls SoundMixerChangedItem.

To use soundlib.lib for handling mixer devices, you must:

  • Within the driver object received by DriverEntry, assign SoundDispatch to be the driver’s main dispatcher for IRP control codes. See “Examining DriverEntry in sndblst.sys.”

  • Define a SOUND_DEVICE_INIT structure for each mixer device. Mixer drivers generally support one device instance per card. The structures’ DispatchRoutine members must be set to the address of SoundMixerDispatch, which is the mixer dispatch routine within soundlib.lib for DeviceIoControl messages.

  • Define a MIXER_INFO structure.

  • Call SoundCreateDevice once for each SOUND_DEVICE_INIT structure you have defined.

  • Assign the address of the mixer device’s LOCAL_DEVICE_INFO structure to the MixerDevice member of every other device’s LOCAL_DEVICE_INFO structure.

Mixer drivers should save their settings in the registry before system shutdown. To register for shutdown notification, call IoRegisterShutdownNotification. Drivers can save mixer settings in the registry in any format, but the most efficient registry data type for saving the settings is REG_BINARY. When the driver initializes, it can use either the stored settings or default settings.


Supporting Auxiliary Audio Devices

Kernel-mode drivers that support auxiliary audio devices can use the auxiliary audio message dispatcher, SoundAuxDispatch, provided by soundlib.lib. If you use soundlib.lib, the amount of customized code a driver needs to provide is minimal. If the hardware provides a mixer, then soundlib.lib calls the mixer’s HwSetControlData and HwGetControlData functions to control volume (see MIXER_INFO). If the hardware does not provide mixer hardware, the driver needs to include a HwSetVolume function for the auxiliary audio device, to control volume (see SOUND_DEVICE_INIT). A DevCapsRoutine is the only other function you need to provide (see SOUND_DEVICE_INIT).

To use soundlib.lib for handling auxiliary audio devices, you must:

  • Within the driver object received by DriverEntry, assign SoundDispatch to be the driver’s main dispatcher for IRP control codes. See “Examining_DriverEntry in sndblst.sys.”

  • Define a SOUND_DEVICE_INIT structure. The structures’ DispatchRoutine members must be set to the address of SoundAuxDispatch, which is the auxiliary audio dispatch routine within soundlib.lib for DeviceIoControl messages.

  • Call SoundCreateDevice once for each SOUND_DEVICE_INIT structure you have defined.


Audio Driver Reference

This section describes the functions, messages, structures, and types used by audio drivers.

Topics for user-mode drivers include:

  • Entry Points, User-Mode Audio Drivers

  • Messages, User-Mode Audio Drivers

  • Structures, User-Mode Audio Drivers

  • Functions and Macros, drvlib.lib

  • Structures and Types, drvlib.lib

Topics for kernel-mode drivers include:

  • Functions, soundlib.lib

  • Structures and Types, soundlib.lib


Entry Points, User-Mode Audio Drivers

This section describes the entry point functions that user-mode audio drivers must provide.


auxMessage

DWORD APIENTRY
auxMessage (
UINT uDeviceId,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2
);

The auxMessage function is one of the user-mode audio driver entry points. It is the entry point for user-mode auxiliary audio drivers.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
Specifies one of the user-mode audio driver messages.
dwUser
Not used.
dwParam1
Specifies the first message parameter. Dependent on message type.
dwParam2
Specifies the second message parameter. Dependent on message type.
Return Value

The auxMessage function returns a value that is dependent upon the message. If the received message is not recognized, the function returns MMSYSERR_NOTSUPPORTED.


midMessage

DWORD APIENTRY
midMessage (
UINT uDeviceId,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2
);

The midMessage function is one of the user-mode audio driver entry points. It is the entry point for user-mode MIDI input drivers.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
Specifies one of the user-mode audio driver messages.
dwUser
Specifies a device instance identifier. For the MIDM_OPEN message, this is an output parameter. The driver creates the instance identifier and returns it in the address specified as the argument. For all other messages, this is an input parameter. The argument is the instance identifier.
dwParam1
Specifies the first message parameter. Dependent on message type.
dwParam2
Specifies the second message parameter. Dependent on message type.
Return Value

The midMessage function returns a value that is dependent upon the message. If the received message is not recognized, the function returns MMSYSERR_NOTSUPPORTED.

Comments

You can use dwUser in any manner you wish. Drivers that can support multiple clients return a different value for each MIDM_OPEN message, in order to keep track of which subsequent messages are being sent by which client.


modMessage

DWORD APIENTRY
modMessage (
UINT uDeviceId,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2
);

The modMessage function is one of the user-mode audio driver entry points. It is the entry point for user-mode MIDI output drivers.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
Specifies one of the user-mode audio driver messages.
dwUser
Specifies a device instance identifier. For the MODM_OPEN message, this is an output parameter. The driver creates the instance identifier and returns it in the address specified as the argument. For all other messages, this is an input parameter. The argument is the instance identifier.
dwParam1
Specifies the first message parameter. Dependent on message type.
dwParam2
Specifies the second message parameter. Dependent on message type.
Return Value

The modMessage function returns a value that is dependent upon the message. If the received message is not recognized, the function returns MMSYSERR_NOTSUPPORTED.

Comments

You can use dwUser in any manner you wish. Drivers that can support multiple clients return a different value for each MODM_OPEN message, in order to keep track of which subsequent messages are being sent by which client.


mxdMessage

DWORD APIENTRY
mxdMessage (
UINT uDeviceId,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2
);

The mxdMessage function is one of the user-mode audio driver entry points. It is the entry point for user-mode mixer drivers.

Parameters
uDeviceId
Device identifier (0, 1, 2, etc.) for the target device.
uMsg
Specifies one of the user-mode audio driver messages.
dwUser
Specifies a device instance identifier. For the MXDM_OPEN message, this is an output parameter. The driver creates the instance identifier and returns it in the address specified as the argument. For all other messages, this is an input parameter. The argument is the instance identifier.
dwParam1
Specifies the first message parameter. Dependent on message type.
dwParam2
Specifies the second message parameter. Dependent on message type.
Return Value

The mxdMessage function returns a value that is dependent upon the message. If the received message is not recognized, the function returns MMSYSERR_NOTSUPPORTED.

Comments

You can use dwUser in any manner you wish. Drivers that can support multiple clients return a different value for each MXDM_OPEN message, in order to keep track of which subsequent messages are being sent by which client.


widMessage

DWORD APIENTRY
widMessage (
UINT uDeviceId,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2
);

The widMessage function is one of the user-mode audio driver entry points. It the entry point for user-mode waveform input drivers.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
Specifies one of the user-mode audio driver messages.
dwUser
Specifies a device instance identifier. For the WIDM_OPEN message, this is an output parameter. The driver creates the instance identifier and returns it in the address specified as the argument. For all other messages, this is an input parameter. The argument is the instance identifier.
dwParam1
Specifies the first message parameter. Dependent on message type.
dwParam2
Specifies the second message parameter. Dependent on message type.
Return Value

The widMessage function returns a value that is dependent upon the message. If the received message is not recognized, the function returns MMSYSERR_NOTSUPPORTED.

Comments

You can use dwUser in any manner you wish. Drivers that can support multiple clients return a different value for each WIDM_OPEN message, in order to keep track of which subsequent messages are being sent by which client.


wodMessage

DWORD APIENTRY
wodMessage (
UINT uDeviceId,
UINT uMsg,
DWORD dwUser,
DWORD dwParam1,
DWORD dwParam2
);

The wodMessage function is one of the user-mode audio driver entry points. It the entry point for user-mode waveform output drivers.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
Specifies one of the user-mode audio driver messages.
dwUser
Specifies a device instance identifier. For the WODM_OPEN message, this is an output parameter. The driver creates the instance identifier and returns it in the address specified as the argument. For all other messages, this is an input parameter. The argument is the instance identifier.
dwParam1
Specifies the first message parameter. Dependent on message type.
dwParam2
Specifies the second message parameter. Dependent on message type.
Return Value

The wodMessage function returns a value that is dependent upon the message. If the received message is not recognized, the function returns MMSYSERR_NOTSUPPORTED.

Comments

You can use dwUser in any manner you wish. Drivers that can support multiple clients return a different value for each WODM_OPEN message, in order to keep track of which subsequent messages are being sent by which client.


Messages, User-Mode Audio Drivers

This section describes the messages, listed in alphabetical order, that are received by user-mode audio drivers.


AUXDM_GETDEVCAPS

The AUXDM_GETDEVCAPS message requests an auxiliary audio driver to return the capabilities of the specified auxiliary audio device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
AUXDM_GETDEVCAPS
dwUser
Not used.
dwParam1
Pointer to an empty AUXCAPS structure. This structure is used to return the capabilities of the device. (The AUXCAPS structure is described in the Win32 SDK.)
dwParam2
Size of the AUXCAPS structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h.

Comments

A client sends the AUXDM_GETDEVCAPS message by calling the user-mode driver’s auxMessage entry point, passing the specified parameters.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_AUX_GET_CAPABILITIES control code.

The user-mode driver fills the AUXCAPS structure.


AUXDM_GETNUMDEVS

The AUXDM_GETNUMDEVS message requests an auxiliary audio driver to return the number of auxiliary audio device instances that it supports.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
AUXDM_GETNUMDEVS
dwUser
Not used.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver returns the number of auxiliary audio device instances it supports.

Comments

A client sends the AUXDM_GETNUMDEVS message by calling the user-mode driver’s auxMessage entry point, passing the specified parameters.

The driver should return the number of logical auxiliary audio devices that can be supported. Typically, for each physical device, a kernel-mode driver can support one or more logical devices of various types. For example, for each Creative Labs Sound Blaster card, there are MIDI, waveform input, mixer, and auxiliary audio devices. Kernel-mode drivers store logical device names and types in the registry under the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices. To correctly return the number of logical devices, the user-mode driver should examine the \Devices subkey for each of the driver’s \Device Number keys, searching for logical devices of the desired type. (Code in drvlib.lib provides this capability.)


AUXDM_GETVOLUME

The AUXDM_GETVOLUME message requests a user-mode driver to return the current volume level setting for the specified auxiliary audio device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
AUXDM_GETVOLUME
dwUser
Not used.
dwParam1
Pointer to a DWORD location to receive the volume setting.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h.

Comments

A client sends the AUXDM_GETVOLUME message by calling the user-mode driver’s auxMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports AUXDM_SETVOLUME, it must support AUXDM_GETVOLUME.

The volume value is returned in the DWORD pointed to by dwParam1 as follows:
Channel | Portion of dwParam1 Used
Left channel | Low word
Right channel | High word
Single channel | Low word

A value of zero is silence, and a value of 0xFFFF is full volume.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_AUX_GET_VOLUME control code.


AUXDM_SETVOLUME

The AUXDM_SETVOLUME message requests a user-mode driver to set the volume level for the specified auxiliary audio device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
AUXDM_SETVOLUME
dwUser
Not used.
dwParam1
A DWORD containing the volume setting.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h.

Comments

A client sends the AUXDM_SETVOLUME message by calling the user-mode driver’s auxMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports AUXDM_SETVOLUME, it must support AUXDM_GETVOLUME.

The volume value is specified by dwParam1 as follows.
Channel | Portion of dwParam1 Used
Left channel | Low word
Right channel | High word
Single channel | Low word

A value of zero is silence, and a value of 0xFFFF is full volume.

The kernel-mode driver might not support the full 16 bits of volume control and can truncate the lower bits. However, the original value requested with AUXDM_SETVOLUME should be returned with AUXDM_GETVOLUME.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_AUX_SET_VOLUME control code.


MIDM_ADDBUFFER

The MIDM_ADDBUFFER message requests a user-mode MIDI input driver to add an empty input buffer to its input buffer queue.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
AUXDM_SETVOLUME
dwUser
Device instance identifier.
dwParam1
Pointer to a MIDIHDR structure identifying the buffer. (The MIDIHDR structure is described in the Win32 SDK.)
dwParam2
Size of the MIDIHDR structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInAddBuffer return values in the Win32 SDK.

Comments

A client sends the AUXDM_SETVOLUME message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

If the MHDR_PREPARED flag is not set in the dwFlags member of the MIDIHDR structure, the driver should return MIDIERR_UNPREPARED. If the flag is set, the driver should:

  • Clear the MHDR_DONE flag.

  • Set the MHDR_INQUEUE flag.

  • Place the empty buffer in its input queue.

  • Return control to the client with a return value of MMSYSERR_NOERROR.

Only system-exclusive events (long messages) should be placed in the buffer. Other MIDI events (short messages) should be passed to the client with a MIM_DATA callback message.

The user-mode driver starts recording when it receives a MIDM_START message.

For additional information, see Transferring MIDI Input Data.


MIDM_CLOSE

The MIDM_CLOSE message requests a MIDI input driver to close a specified device instance that was previously opened with a MIDM_OPEN message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_CLOSE
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInClose return values in the Win32 SDK.

Comments

A client sends the MIDM_CLOSE message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

If the driver has not filled and returned all of the buffers received with MIDM_ADDBUFFER messages, it should not close the instance and should instead return MIDIERR_STILLPLAYING.

After the driver closes the device instance it should send a MIM_CLOSE callback message to the client.

For more information about closing a device instance, see Transferring MIDI Input Data.


MIDM_GETDEVCAPS

The MIDM_GETDEVCAPS message requests a MIDI input driver to return the capabilities of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_GETDEVCAPS
dwUser
Device instance identifier.
dwParam1
Pointer to a MIDIINCAPS structure. (The MIDIINCAPS structure is described in the Win32 SDK.)
dwParam2
Size of the MIDIINCAPS structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInGetDevCaps return values in the Win32 SDK.

Comments

A client sends the MIDM_GETDEVCAPS message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_MIDI_GET_CAPABILITIES control code.

The user-mode driver fills the MIDIINCAPS structure.


MIDM_GETNUMDEVS

The MIDM_GETNUMDEVS message requests a MIDI input driver to return the number of device instances that it supports.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_GETNUMDEVS
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver returns the number of MIDI input device instances it supports.

Comments

A client sends the MIDM_GETNUMDEVS message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

The driver should return the number of logical MIDI input devices that can be supported. Typically, for each physical device, a kernel-mode driver can support one or more logical devices of various types. For example, for each Creative Labs Sound Blasterä card, there are MIDI, waveform, mixer, and auxiliary audio devices. Kernel-mode drivers store logical device names and types in the registry under the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices. To correctly return the number of logical devices, the user-mode driver should examine the \Devices subkey for each of the driver’s \Device Number keys, searching for logical devices of the desired type. (Code in drvlib.lib provides this capability.)


MIDM_OPEN

The MIDM_OPEN message is sent to a MIDI input driver to request it to open an instance of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_OPEN
dwUser
Pointer to location to receive device instance identifier.
dwParam1
Pointer to a MIDIOPENDESC structure, containing the client’s device handle, notification target, and instance ID.
dwParam2
Contains flags. The following flags are defined.

Flag

Definition

CALLBACK_WINDOW

Indicates the dwCallback member of MIDIOPENDESC is a window handle.

CALLBACK_FUNCTION

Indicates the dwCallback member of MIDIOPENDESC is the address of a callback function.

CALLBACK_TASK

Indicates the dwCallback member of MIDIOPENDESC is a task handle.

MIDI_IO_STATUS

Indicates the client wants to receive MIM_MOREDATA callback messages.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInOpen return values in the Win32 SDK.

Comments

A client sends the MIDM_OPEN message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

Typically, user-mode drivers connect to kernel-mode drivers by calling CreateFile, specifying the MS-DOS device name of one of the kernel-mode driver’s devices.

The driver assigns a device instance identifier and returns it in the location pointed to by dwUser. The driver can expect to receive this value as the dwUser input argument to all other midMessage messages.

The driver determines the number of clients it allows to use a particular device. If a device is opened by the maximum number of clients, it returns MMSYSERR_ALLOCATED for subsequent open requests.

If the open operation succeeds, the driver should send the client a MIM_OPEN message by calling the DriverCallback function.

For additional information, see Transferring MIDI Input Data.


MIDM_PREPARE

The MIDM_PREPARE message requests a MIDI input driver to prepare a system-exclusive data buffer for input.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_PREPARE
dwUser
Device instance identifier.
dwParam1
Pointer to a MIDIHDR structure identifying the buffer. (The MIDIHDR structure is described in the Win32 SDK.)
dwParam2
Size of the MIDIHDR structure.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInPrepareHeader return values in the Win32 SDK.

Comments

A client sends the MIDM_PREPARE message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports MIDM_PREPARE, it must also support MIDM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll prepares the buffer for use. For most drivers, this behavior is sufficient. If the driver does perform buffer preparation, it must set MHDR_PREPARED in the dwFlags member of MIDIHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring MIDI Input Data.


MIDM_RESET

The MIDM_RESET message requests a MIDI input driver to stop recording and return all buffers in the input queue to the client.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_RESET
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInReset return values in the Win32 SDK.

Comments

A client sends the MIDM_RESET message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

Typically, the user-mode driver stops recording by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_MIDI_SET_STATE control code.

For each buffer remaining in the driver’s input queue (see MIDM_ADDBUFFER), the driver should set MHDR_DONE and clear MHDR_INQUEUE in the dwFlags member of the buffer’s MIDIHDR structure, and also set the structure’s dwBytesRecorded member. Finally, a MOM_DONE callback message should be sent for each buffer.

For additional information, see Transferring MIDI Input Data.


MIDM_START

The MIDM_START message requests a MIDI input driver to begin recording.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_START
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInStart return values in the Win32 SDK.

Comments

A client sends the MIDM_START message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

This message resets the time stamp to zero. If the message is received after input has been started, the driver should return MMSYSERR_NOERROR.

Typically, the user-mode driver starts recording by calling ReadFileEx, to fill internal buffers with data returned by the kernel-mode driver, and by calling DeviceIoControl, to send the kernel-mode driver an IOCTL_MIDI_SET_STATE control code. When the kernel-mode driver returns a filled buffer, the user-mode driver should read the buffer data to differentiate short MIDI messages from long MIDI messages. The user-mode driver returns each short message to the client by means of a MIM_DATA callback message. It should copy long messages into the user-specified input buffers (see MIDM_ADDBUFFER) and, when a buffer is full, do the following:

  • Set the dwBytesRecorded member in the buffer’s MIDIHDR structure.

  • Set the buffer’s MHDR_DONE flag.

  • Clear the buffer’s MHDR_INQUEUE flag.

  • Send a MIM_LONGDATA callback message to the client.

If the driver receives long messages with no buffers in its input queue, it should ignore the messages without notifying the client.

Recording should continue until the client sends MIDM_STOP or MIDM_RESET.

For additional information, see Transferring MIDI Input Data.


MIDM_STOP

The MIDM_STOP message requests a MIDI input driver to stop recording.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_STOP
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInStop return values in the Win32 SDK.

Comments

A client sends the MIDM_STOP message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

If a buffer in the input queue (see MIDM_ADDBUFFER) has been partially filled, the driver should treat it as a full buffer and return it to the client (see MIDM_START). Empty buffers should remain in the queue.

While recording is stopped, the driver should maintain the current MIDI status byte for events using running status and the parsing state for multibyte events. If the driver receives a subsequent MIDM_START message, it should be able to resume recording from the point at which it was stopped.

If this message is received and recording is already stopped, the driver should return MMSYSERR_NOERROR.

Typically, the user-mode driver stops recording by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_MIDI_SET_STATE control code.

For additional information, see Transferring MIDI Input Data.


MIDM_UNPREPARE

The MIDM_UNPREPARE message requests a MIDI input driver to remove the buffer preparation that was performed in response to a MIDM_PREPARE message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MIDM_UNPREPARE
dwUser
Device instance identifier.
dwParam1
Specifies a pointer to MIDIHDR identifying the data buffer. (The MIDIHDR structure is described in the Win32 SDK.)
dwParam2
Specifies the size of MIDIHDR in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiInUnprepareHeader return values in the Win32 SDK.

Comments

A client sends the MIDM_UNPREPARE message by calling the user-mode driver’s midMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports MIDM_PREPARE, it must also support MIDM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll removes the buffer preparation. For most drivers, this behavior is sufficient. If the driver does support MIDM_UNPREPARE, it must clear MHDR_PREPARED in the dwFlags member of MIDIHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring MIDI Input Data.


MIM_CLOSE

The MIM_CLOSE callback message notifies a client that a user-mode driver has finished processing a MIDM_CLOSE message.

Parameters
dwMsg
MIM_CLOSE
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode MIDI input driver sends a MIM_CLOSE message to its client, by means of a callback, when the driver finishes processing a MIDM_CLOSE message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_CLOSE message only if the client has previously specified a notification target with a MIDM_OPEN message.

Win32 SDK documentation states that clients receive an MM_MIM_CLOSE message if the notification target is a window handle. MIM_CLOSE and MM_MIM_CLOSE are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MIM_DATA

The MIM_DATA callback message notifies a client that a user-mode driver has received a MIDI short message.

Parameters
dwMsg
MIM_DATA
dwParam1
MIDI short message contents (one to three bytes).
dwParam2
Time stamp. Number of milliseconds since MIDM_START was received.
Comments

A user-mode MIDI input driver sends a MIM_DATA message to its client, by means of a callback, when the driver has received a MIDI short message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_DATA message only if the client has previously specified a notification target with a MIDM_OPEN message.

If the driver detects that the message is invalid, it should send MIM_ERROR instead of MIM_DATA.

Win32 SDK documentation states that clients receive an MM_MIM_DATA message if the notification target is a window handle. MIM_DATA and MM_MIM_DATA are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MIM_ERROR

The MIM_ERROR callback message notifies a client that a user-mode driver has received an invalid MIDI short message.

Parameters
dwMsg
MIM_ERROR
dwParam1
Invalid MIDI short message contents (one to three bytes).
dwParam2
Time stamp. Number of milliseconds since MIDM_START was received.
Comments

A user-mode MIDI input driver sends a MIM_ERROR message to its client, by means of a callback, when the driver has received an invalid MIDI short message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_ERROR message only if the client has previously specified a notification target with a MIDM_OPEN message.

Win32 SDK documentation states that clients receive a MM_MIM_ERROR message if the notification target is a window handle. MIM_ERROR and MM_MIM_ERROR are equivalent.

For more information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MIM_LONGDATA

The MIM_LONGDATA callback message notifies a client that a user-mode driver has received a MIDI system-exclusive (long) message.

Parameters
dwMsg
MIM_LONGDATA
dwParam1
Address of a MIDIHDR structure identifying a buffer containing the long message. (MIDIHDR is defined in the Win32 SDK.)
dwParam2
Time stamp. Number of milliseconds since MIDM_START was received.
Comments

A user-mode MIDI input driver sends a MIM_LONGDATA message to its client, by means of a callback, when the driver has received a MIDI system-exclusive (long) message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_LONGDATA message only if the client has previously specified a notification target with a MIDM_OPEN message.

If the driver detects that the message is invalid, it should send MIM_LONGERROR instead of MIM_LONGDATA.

Win32 SDK documentation states that clients receive a MM_MIM_LONGDATA message if the notification target is a window handle. MIM_LONGDATA and MM_MIM_LONGDATA are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MIM_LONGERROR

The MIM_LONGERROR callback message notifies a client that a user-mode driver has received an invalid MIDI system-exclusive (long) message.

Parameters
dwMsg
MIM_LONGERROR
dwParam1
Address of a MIDIHDR structure identifying a buffer containing the invalid long message. (MIDIHDR is defined in the Win32 SDK.)
dwParam2
Time stamp. Number of milliseconds since MIDM_START was received.
Comments

A user-mode MIDI input driver sends a MIM_LONGERROR message to its client, by means of a callback, when the driver has received an invalid MIDI system-exclusive (long) message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_LONGERROR message only if the client has previously specified a notification target with a MIDM_OPEN message.

When a driver detects an invalid message long message, it should send MIM_LONGERROR instead of MIM_LONGDATA.

Win32 SDK documentation states that clients receive a MM_MIM_LONGERROR message if the notification target is a window handle. MIM_LONGERROR and MM_MIM_LONGERROR are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MIM_MOREDATA

The MIM_MOREDATA callback message notifies a client that a user-mode driver has received a MIDI short message, and the client is not processing MIM_DATA messages fast enough to keep up with the driver.

Parameters
dwMsg
MIM_MOREDATA
dwParam1
MIDI short message contents (one to three bytes).
dwParam2
Time stamp. Number of milliseconds since MIDM_START was received.
Comments

A user-mode MIDI input driver sends a MIM_MOREDATA message to its client, by means of a callback, when the driver has received a MIDI short message and the client is not processing MIM_DATA messages fast enough to keep up with the driver. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_MOREDATA message only if the client has previously specified a notification target with a MIDM_OPEN message, and only if the MIDI_IO_STATUS flag was included with the MIDM_OPEN message.

Currently, mmdrv.dll and drvlib.lib do not send MIM_MOREDATA messages.

If the driver detects that the message is invalid, it should send MIM_ERROR instead of MIM_MOREDATA.

Win32 SDK documentation states that clients receive a MM_MIM_MOREDATA message if the notification target is a window handle. MIM_MOREDATA and MM_MIM_MOREDATA are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MIM_OPEN

The MIM_OPEN callback message notifies a client that a user-mode driver has finished processing a MIDM_OPEN message.

Parameters
dwMsg
MIM_OPEN
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode MIDI input driver sends a MIM_OPEN message to its client, by means of a callback, when the driver finishes processing a MIDM_OPEN message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MIM_OPEN message only if the client has specified a notification target with the MIDM_OPEN message.

Win32 SDK documentation states that clients receive an MM_MIM_OPEN message if the notification target is a window handle. MIM_OPEN and MM_MIM_OPEN are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring MIDI Input Data.


MM_MIXM_CONTROL_CHANGE

The MM_MIXM_CONTROL_CHANGE callback message notifies a client that the value of a mixer control item has changed.

Parameters
dwMsg
MM_MIXM_CONTROL_CHANGE
dwParam1
Control ID. Must match control ID value returned to the client in response to an MXDM_GETLINECONTROLS message.
dwParam2
NULL
Comments

A user-mode mixer driver sends an MM_MIXM_CONTROL_CHANGE message to its client, by means of a callback, when the value of a mixer control item has changed. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MM_MIXM_CONTROL_CHANGE message only if the client has specified a notification target with the MXDM_OPEN message.

For kernel-mode drivers using soundlib.lib, this value is stored in a MIXER_DATA_ITEM structure and passed to the user-mode driver upon receipt of an IOCTL_MIX_REQUEST_NOTIFY control code.

For additional information, see Notifying Clients from Audio Drivers.


MM_MIXM_LINE_CHANGE

The MM_MIXM_LINE_CHANGE callback message notifies a client that the value of a mixer line item has changed.

Parameters
dwMsg
MM_MIXM_LINE_CHANGE
dwParam1
Line ID. Must match line ID value returned to the client in response to an MXDM_GETLINEINFO message.
dwParam2
NULL
Comments

A user-mode mixer driver sends an MM_MIXM_LINE_CHANGE message to its client, by means of a callback, when the value of a mixer line item has changed. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MM_MIXM_LINE_CHANGE message only if the client has specified a notification target with the MXDM_OPEN message.

For kernel-mode drivers using soundlib.lib, this value is stored in a MIXER_DATA_ITEM structure and passed to the user-mode driver upon receipt of an IOCTL_MIX_REQUEST_NOTIFY control code.

For additional information, see Notifying Clients from Audio Drivers.


MODM_CACHEDRUMPATCHES

The MODM_CACHEDRUMPATCHES message requests a MIDI output driver to load and cache a specified set of key-based percussion patches.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_CACHEDRUMPATCHES
dwUser
Device instance identifier.
dwParam1
Pointer to an array of type KEYARRAY, which is described in the Win32 SDK.
dwParam2
Contains a DWORD value, defined as follows:
Low word | Flag values. (For flag descriptions, see midiOutCacheDrumPatches in the Win32 SDK.)
High word | Drum patch number.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutCacheDrumPatches return values in the Win32 SDK.

Comments

A client sends the MODM_CACHEDRUMPATCHES message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Typically, the user-mode driver requests the kernel-mode driver to cache the patches by calling DeviceIoControl with an IOCTL_MIDI_CACHE_DRUM_PATCHES control code.


MODM_CACHEPATCHES

The MODM_CACHEPATCHES message requests a MIDI output driver to load and cache a specified bank of patches.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_CACHEPATCHES
dwUser
Device instance identifier.
dwParam1
Pointer to an array of type PATCHARRAY, which is described in the Win32 SDK.
dwParam2
Contains a DWORD value, defined as follows:
Low word | Flag values. (For flag descriptions, see midiOutCachePatches in the Win32 SDK.)
High word | Patch bank number. Zero implies the default bank.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutCachePatches return values in the Win32 SDK.

Comments

A client sends the MODM_CACHEPATCHES message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Typically, the user-mode driver requests the kernel-mode driver to cache the patch bank by calling DeviceIoControl with an IOCTL_MIDI_CACHE_PATCHES control code.


MODM_CLOSE

The MODM_CLOSE message requests a MIDI output driver to close a specified device instance that was previously opened with a MODM_OPEN message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_CLOSE
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutClose return values in the Win32 SDK.

Comments

A client sends the MODM_CLOSE message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

If the client has passed data buffers to the user-mode driver by means of MODM_LONGDATA messages, and if the user-mode driver hasn’t finished sending the data to the kernel-mode driver, the user-mode driver should return MIDIERR_STILLPLAYING in response to MODM_CLOSE.

After the driver closes the device instance it should send a MOM_CLOSE callback message to the client.

For additional information, see Transferring MIDI Output Data.


MODM_DATA

The MODM_DATA message requests a MIDI output driver to send a single MIDI short message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_DATA
dwUser
Device instance identifier.
dwParam1
A MIDI short message. (See Comments section below.)
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutShortMsg return values in the Win32 SDK.

Comments

A client sends the MODM_DATA message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

This message is used for the output of all MIDI events, except system-exclusive events. System-exclusive events are output with the MODM_LONGDATA message.

Because the client can employ running status, and because MIDI short messages have varying lengths, the user-mode driver must parse the dwParam1 parameter to determine the number of bytes to send to the kernel-mode driver. Unused bytes in dwParam1 are not guaranteed to be zero.

The driver can be designed to run synchronously, not returning until it sends the message, or asynchronously, returning immediately and sending the MIDI data in the background, using a separate thread.

Typically, the user-mode driver sends the message to the kernel-mode driver by calling DeviceIoControl with an IOCTL_MIDI_PLAY control code.


MODM_GETDEVCAPS

The MODM_GETDEVCAPS message requests a MIDI output driver to return the capabilities of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_GETDEVCAPS
dwUser
Device instance identifier.
dwParam1
Pointer to a MIDIOUTCAPS structure, which is described in the Win32 SDK.
dwParam2
Size of the MIDIOUTCAPS structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutGetDevCaps return values in the Win32 SDK.

Comments

A client sends the MODM_GETDEVCAPS message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_MIDI_GET_CAPABILITIES control code.

The user-mode driver fills the MIDIOUTCAPS structure.


MODM_GETNUMDEVS

The MODM_GETNUMDEVS message requests a MIDI output driver to return the number of device instances that it supports.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_GETNUMDEVS
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver returns the number of MIDI output device instances it supports.

Comments

A client sends the MODM_GETNUMDEVS message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

The driver should return the number of logical MIDI output devices that can be supported. Typically, for each physical device, a kernel-mode driver can support one or more logical devices of various types. For example, for each Creative Labs Sound Blaster card, there are MIDI, waveform, mixer, and auxiliary audio devices. Kernel-mode drivers store logical device names and types in the registry under the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices. To correctly return the number of logical devices, the user-mode driver should examine the \Devices subkey for each of the driver’s \Device Number keys, searching for logical devices of the desired type. (Code in drvlib.lib provides this capability.)


MODM_GETVOLUME

The MODM_GETVOLUME message requests a MIDI output driver to return the current volume level setting for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_GETVOLUME
dwUser
Device instance identifier.
dwParam1
Pointer to a DWORD location to receive the volume setting.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutGetVolume return values in the Win32 SDK.

Comments

A client sends the MODM_GETVOLUME message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports MODM_SETVOLUME, it must support MODM_GETVOLUME.

The volume value is returned in the DWORD pointed to by dwParam1 as follows:
Channel | Portion of dwParam1 Used
Left channel | Low word
Right channel | High word
Single channel | Low word

A value of zero is silence, and a value of 0xFFFF is full volume.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_MIDI_GET_VOLUME control code.

Only drivers for internal synthesizer devices can support volume level changes. Drivers for MIDI output ports should return a MMSYSERR_NOTSUPPORTED error for this message.


MODM_LONGDATA

The MODM_LONGDATA message requests a MIDI output driver to send the contents of a specified output buffer containing one or more MIDI events, including system-exclusive events.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_LONGDATA
dwUser
Device instance identifier.
dwParam1
Pointer to a MIDIHDR structure identifying the output buffer. (The MIDIHDR structure is described in the Win32 SDK.)
dwParam2
Size of the MIDIHDR structure.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutLongMsg return values in the Win32 SDK.

Comments

A client sends the MODM_LONGDATA message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

If the MHDR_PREPARED flag in the dwFlags member of MIDIHDR is not set, the driver should return MIDIERR_UNPREPARED.

The driver should clear the MHDR_DONE flag, set the MHDR_INQUEUE flag, and place the output buffer in its output queue. The driver returns control to the client by returning MMSYSERR_NOERROR.

When the buffer contents have been sent, the driver should set the MHDR_DONE flag, clear the MHDR_INQUEUE flag, and send the client a MOM_DONE callback message.

The driver can be designed to handle MODM_LONGDATA messages synchronously, not returning until the message has been sent to the kernel-mode driver, or asynchronously, returning immediately and using a separate thread to send the MIDI data in the background.

Typically, the user-mode driver sends the buffer to the kernel-mode driver by calling DeviceIoControl with an IOCTL_MIDI_PLAY control code.

If clients use high-level audio interfaces, winmm.dll guarantees that the input buffer contains only a single MIDI event, which can be either a short or long (system-exclusive) message. On the other hand, if clients call midiOutLongMsg, there is no such guarantee. If your user-mode driver is mmdrv.dll or is based on drvlib.lib functions, whatever is received in the input buffer is passed directly to the kernel-mode driver by means of the DeviceIoControl call.


MODM_OPEN

The MODM_OPEN message is sent to a MIDI output driver to request it to open an instance of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_OPEN
dwUser
Pointer to location to receive device instance identifier.
dwParam1
Pointer to a MIDIOPENDESC structure, containing the client’s device handle, notification target, and instance ID.
dwParam2
Contains flags. The following flags are defined.

Flag

Definition

CALLBACK_EVENT

Indicates dwCallback member of MIDIOPENDESC is an event handle.

CALLBACK_FUNCTION

Indicates dwCallback member of MIDIOPENDESC is the address of a callback function.

CALLBACK_TASK

Indicates dwCallback member of MIDIOPENDESC is a task handle.

CALLBACK_WINDOW

Indicates dwCallback member of MIDIOPENDESC is a window handle.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutOpen return values in the Win32 SDK.

Comments

A client sends the MODM_OPEN message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Typically, user-mode drivers connect to kernel-mode drivers by calling CreateFile, specifying the MS-DOS device name of one of the kernel-mode driver’s devices.

The driver assigns a device instance identifier and returns it in the location pointed to by dwUser. The driver can expect to receive this value as the dwUser input argument to all other modMessage messages.

The driver determines the number of clients it allows to use a particular device. If a device is opened by the maximum number of clients, it returns MMSYSERR_ALLOCATED for subsequent open requests.

If the open operation succeeds, the driver should send the client a MOM_OPEN message by calling the DriverCallback function.

For additional information, see Transferring MIDI Output Data.


MODM_PREPARE

The MODM_PREPARE message requests a MIDI output driver to prepare a system-exclusive data buffer for output.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_PREPARE
dwUser
Device instance identifier.
dwParam1
Pointer to a MIDIHDR structure identifying the buffer. (The MIDIHDR structure is described in the Win32 SDK.)
dwParam2
Size of the MIDIHDR structure.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutPrepareHeader return values in the Win32 SDK.

Comments

A client sends the MODM_PREPARE message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports MODM_PREPARE, it must also support MODM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll prepares the buffer for use. For most drivers, this behavior is sufficient. If the driver does perform buffer preparation, it must set MHDR_PREPARED in the dwFlags member of MIDIHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring MIDI Output Data.


MODM_RESET

The MODM_RESET message requests a MIDI output driver to stop sending output data and return all output buffers to the client.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_RESET
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutReset return values in the Win32 SDK.

Comments

A client sends the MODM_RESET message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

If the driver’s output queue contains any output buffers (see MODM_LONGDATA) whose contents have not been sent to the kernel-mode driver, the driver should set the MHDR_DONE flag and clear the MHDR_INQUEUE flag in each buffer’s MIDIHDR structure, and then send the client a MOM_DONE callback message for each buffer.

Typically, the user-mode driver stops device output by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_MIDI_SET_STATE control code.

If the device is an internal synthesizer, the driver should turn off all notes.

For additional information, see Transferring MIDI Output Data.


MODM_SETVOLUME

The MODM_SETVOLUME message requests a user-mode MIDI output driver to set the volume level for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_SETVOLUME
dwUser
Device instance identifier.
dwParam1
A DWORD containing the volume setting.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutSetVolume return values in the Win32 SDK.

Comments

A client sends the MODM_SETVOLUME message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports MODM_SETVOLUME, it must support MODM_GETVOLUME.

The volume value is specified by dwParam1 as follows:
Channel | Portion of dwParam1 Used
Left channel | Low word
Right channel | High word
Single channel | Low word

A value of zero is silence, and a value of 0xFFFF is full volume.

The kernel-mode driver might not support the full 16 bits of volume control and can truncate the lower bits. However, the original value requested with AUXDM_SETVOLUME should be returned with AUXDM_GETVOLUME.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_MIDI_SET_VOLUME control code.

Only drivers for internal synthesizer devices can support volume level changes. Drivers for MIDI output ports should return a MMSYSERR_NOTSUPPORTED error for this message.


MODM_UNPREPARE

The MODM_UNPREPARE message requests a MIDI output driver to remove the buffer preparation that was performed in response to a MODM_PREPARE message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MODM_UNPREPARE
dwUser
Device instance identifier.
dwParam1
Specifies a pointer to MIDIHDR identifying the data buffer. (The MIDIHDR structure is described in the Win32 SDK.)
dwParam2
Specifies the size of MIDIHDR in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIDIERR error codes defined in mmsystem.h. See midiOutUnprepareHeader return values in the Win32 SDK.

Comments

A client sends the MODM_UNPREPARE message by calling the user-mode driver’s modMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports MODM_PREPARE, it must also support MODM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll removes the buffer preparation. For most drivers, this behavior is sufficient. If the driver does support MODM_UNPREPARE, it must clear MHDR_PREPARED in the dwFlags member of MIDIHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring MIDI Output Data.


MOM_CLOSE

The MOM_CLOSE callback message notifies a client that a user-mode driver has finished processing a MODM_CLOSE message.

Parameters
dwMsg
MOM_CLOSE
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode MIDI output driver sends a MOM_CLOSE message to its client, by means of a callback, when the driver finishes processing a MODM_CLOSE message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MOM_CLOSE message only if the client has previously specified a notification target with a MODM_OPEN message.

Win32 SDK documentation states that clients receive a MM_MOM_CLOSE message if the notification target is a window handle. MOM_CLOSE and MM_MOM_CLOSE are equivalent.

For more information, see Notifying Clients from Audio Drivers and Transferring MIDI Output Data.


MOM_DONE

The MOM_DONE callback message notifies a client that a user-mode driver has finished processing a MODM_LONGDATA message.

Parameters
dwMsg
MOM_DONE
dwParam1
Address of the MDIHDR structure that was received with the MODM_LONGDATA message. (MIDIHDR is defined in the Win32 SDK.)
dwParam2
NULL
Comments

A user-mode MIDI output driver sends a MOM_DONE message to its client, by means of a callback, when the driver finishes processing a MODM_LONGDATA message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MOM_DONE message only if the client has previously specified a notification target with a MODM_OPEN message.

Win32 SDK documentation states that clients receive a MM_MOM_DONE message if the notification target is a window handle. MOM_DONE and MM_MOM_DONE are equivalent.

For more information, see Notifying Clients from Audio Drivers and Transferring MIDI Output Data.


MOM_OPEN

The MOM_OPEN callback message notifies a client that a user-mode driver has finished processing a MODM_OPEN message.

Parameters
dwMsg
MOM_OPEN
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode MIDI output driver sends a MOM_OPEN message to its client, by means of a callback, when the driver finishes processing a MODM_OPEN message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MOM_OPEN message only if the client has specified a notification target with the MODM_OPEN message.

Win32 SDK documentation states that clients receive an MM_MOM_OPEN message if the notification target is a window handle. MOM_OPEN and MM_MOM_OPEN are equivalent.

For more information, see Notifying Clients from Audio Drivers and Transferring MIDI Output Data.


MOM_POSITIONCB

The MOM_POSITIONCB callback message notifies a client that a user-mode driver has encountered a MIDI event containing a MEVT_F_CALLBACK flag.

Parameters
dwMsg
MOM_POSITIONCB
dwParam1
NULL
dwParam2
NULL
Comments

User-mode drivers do not send MOM_POSITIONCB callback messages.

Clients using the MIDI output stream functions send streams of data contained in a series of MIDIEVENT structures (see the Win32 SDK). Code in winmm.dll examines each MIDIEVENT structure and, if the MEVT_F_CALLBACK flag is set, sends a MOM_POSITIONCB message by calling DriverCallback, passing the specified parameters.

The winmm.dll code sends MOM_POSITIONCB messages only if the client has specified a notification target with the MODM_OPEN message.

Win32 SDK documentation states that clients receive a MM_MOM_POSITIONCB message if the notification target is a window handle. MOM_POSITIONCB and MM_MOM_POSITIONCB are equivalent.

For more information, see MIDI Output Streams, Notifying Clients from Audio Drivers and Transferring MIDI Output Data.


MXDM_CLOSE

The MXDM_CLOSE message requests a user-mode mixer driver to close the specified device instance that was opened with an MXDM_OPEN message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_CLOSE
dwUser
Instance identifier of instance to close.
lParam1
Not used.
lParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerClose return values in the Win32 SDK.

Comments

A client sends the MXDM_CLOSE message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

Often, closing a driver instance simply involves removing a client-specific data structure.


MXDM_GETCONTROLDETAILS

The MXDM_GETCONTROLDETAILS message requests a user-mode mixer driver to return detailed information about the specified control on the specified audio line.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_GETCONTROLDETAILS
dwUser
Instance identifier associated with the caller.
lParam1
Pointer to a MIXERCONTROLDETAILS structure, which is described in the Win32 SDK.
lParam2
Contains flag values. For a list of valid flags, see the description of mixerGetControlDetails in the Win32 SDK.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerGetControlDetails return values in the Win32 SDK.

Comments

A client sends the MXDM_GETCONTROLDETAILS message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

The driver receives an empty MIXERCONTROLDETAILS structure and fills it in.


MXDM_GETDEVCAPS

The MXDM_GETDEVCAPS message requests a user-mode mixer driver to return capabilities information about the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_GETDEVCAPS
dwUser
Instance identifier associated with the caller.
lParam1
Pointer to a MIXERCAPS structure, which is described in the Win32 SDK.
lParam2
Size of buffer pointed to by lparam1.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerGetDevCaps return values in the Win32 SDK.

Comments

A client sends the MXDM_GETDEVCAPS message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

The driver receives an empty MIXERCAPS structure and fills it in.


MXDM_GETLINECONTROLS

The MXDM_GETLINECONTROLS message requests a user-mode mixer driver to return information about specified controls connected to a specified audio line, for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_GETLINECONTROLS
dwUser
Instance identifier associated with the caller.
lParam1
Pointer to a MIXERLINECONTROLS structure, which is described in the Win32 SDK.
lParam2
Contains flag values. For a list of valid flags, see the description of mixerGetLineControls in the Win32 SDK.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerGetLineControls return values in the Win32 SDK.

Comments

A client sends the MXDM_GETLINECONTROLS message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

The driver receives an empty MIXERLINECONTROLS structure and fills it in.


MXDM_GETLINEINFO

The MXDM_GETLINEINFO message requests a user-mode mixer driver to return information about a specified audio line for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_GETLINEINFO
dwUser
Instance identifier associated with the caller.
lParam1
Pointer to a MIXERLINE structure, which is described in the Win32 SDK.
lParam2
Contains flag values. For a list of valid flags, see the description of mixerGetLineInfo in the Win32 SDK.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerGetLineInfo return values in the Win32 SDK.

Comments

A client sends the MXDM_GETLINEINFO message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

The driver receives an empty MIXERLINE structure and fills it in.


MXDM_GETNUMDEVS

The MXDM_GETNUMDEVS message requests a user-mode mixer driver to return the number of device instances it supports.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_GETNUMDEVS
dwUser
Not used.
lParam1
Not used.
lParam2
Not used.
Return Value

Returns the number of devices the driver supports.

Comments

A client sends the MXDM_GETNUMDEVS message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.


MXDM_INIT

A user-mode mixer driver’s mxdMessage function receives a MXDM_INIT message while the driver is being installed.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_INIT
dwUser
Not used.
lParam1
Not used.
lParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h.

Comments

A client sends the MXDM_INIT message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

Support for this message is optional. The message allows mixer drivers to perform additional installation activities after DRV_INSTALL has been received.


MXDM_OPEN

The MXDM_OPEN message requests a user-mode mixer driver to open an instance of the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_OPEN
dwUser
Address of location into which driver places instance identifier.
lParam1
Pointer to a MIXEROPENDESC structure.
lParam2
Contains flag values. This is always CALLBACK_FUNCTION. (See Comments section below.)
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerOpen return values in the Win32 SDK.

Comments

A client sends the MXDM_OPEN message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

Often, creating a driver instance simply entails creating an instance-specific data structure. The instance identifier can be a handle to this structure.

Even though the description of mixerOpen in the Win32 SDK lists numerous flag values, these flags are handled within winmm.dll.

According to the description of mixerOpen in the Win32 SDK, the only acceptable callback target is a window handle. Code within winmm.dll pre-empts this callback target by placing the address of a local callback function in the MIXEROPENDESC structure, and setting the CALLBACK_FUNCTION flag in lParam2. The driver calls DriverCallback at the appropriate times, specifying the callback function. The callback function, within winmm.dll, then sends a callback message to the client-specified window handle.


MXDM_SETCONTROLDETAILS

The MXDM_SETCONTROLDETAILS message requests a user-mode mixer driver to set information about specified controls connected to a specified audio line for a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
MXDM_SETCONTROLDETAILS
dwUser
Instance identifier associated with caller.
lParam1
Pointer to a MIXERCONTROLDETAILS structure, which is described in the Win32 SDK.
lParam2
Contains flag values. For a list of valid flags, see the description of mixerGetLineControls in the Win32 SDK.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or MIXERR error codes defined in mmsystem.h. See mixerSetControlDetails return values in the Win32 SDK.

Comments

A client sends the MXDM_SETCONTROLDETAILS message by calling the user-mode driver’s mxdMessage entry point, passing the specified parameters.

The driver receives a MIXERCONTROLDETAILS structure containing information to be set.


WIDM_ADDBUFFER

The WIDM_ADDBUFFER requests a user-mode waveform input driver to add an empty input buffer to its input buffer queue.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_ADDBUFFER
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEHDR structure identifying the buffer. (The WAVEHDR structure is described in the Win32 SDK.)
dwParam2
Size of the WAVEHDR structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInAddBuffer return values in the Win32 SDK.

Comments

A client sends the message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

If the WHDR_PREPARED flag is not set in the dwFlags member of the WAVEHDR structure, the driver should return WAVERR_UNPREPARED. If the flag is set, the driver should:

  • Clear the WHDR_DONE flag.

  • Set the WHDR_INQUEUE flag.

  • Place the empty buffer in its input queue.

  • Return control to the client with a return value of MMSYSERR_NOERROR.

The user-mode driver starts recording when it receives a WIDM_START message.

For additional information, see Transferring Waveform Input Data.


WIDM_CLOSE

The WIDM_CLOSE message requests a waveform input driver to close a specified device instance that was previously opened with a WIDM_OPEN message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_CLOSE
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInClose return values in the Win32 SDK.

Comments

A client sends the WIDM_CLOSE message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

If the driver has not filled and returned all of the buffers received with WIDM_ADDBUFFER messages, it should not close the instance and should instead return WAVERR_STILLPLAYING.

After the driver closes the device instance it should send a WIM_CLOSE callback message to the client.

For additional information, see Transferring Waveform Input Data.


WIDM_GETDEVCAPS

The WIDM_GETDEVCAPS message requests a waveform input driver to return the capabilities of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_GETDEVCAPS
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEINCAPS structure. (The WAVEINCAPS structure is described in the Win32 SDK.)
dwParam2
Size of the WAVEINCAPS structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInGetDevCaps return values in the Win32 SDK.

Comments

A client sends the WIDM_GETDEVCAPS message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_CAPABILITIES control code.

The user-mode driver fills the WAVEINCAPS structure.


WIDM_GETNUMDEVS

The WIDM_GETNUMDEVS message requests a waveform input driver to return the number of device instances that it supports.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_GETNUMDEVS
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver returns the number of waveform input device instances it supports.

Comments

A client sends the WIDM_GETNUMDEVS message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

The driver should return the number of logical waveform input devices that can be supported. Typically, for each physical device, a kernel-mode driver can support one or more logical devices of various types. For example, for each Creative Labs Sound Blasterä card, there are MIDI, waveform, mixer, and auxiliary audio devices. Kernel-mode drivers store logical device names and types in the registry under the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices. To correctly return the number of logical devices, the user-mode driver should examine the \Devices subkey for each of the driver’s \Device Number keys, looking for logical devices of the desired type. (Code in drvlib.lib provides this capability.)


WIDM_GETPOS

The WIDM_GETPOS message requests a waveform input driver to return the current input position within a waveform. The input position is relative to the first recorded sample of the waveform.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_GETPOS
dwUser
Device instance identifier.
dwParam1
Pointer to an MMTIME structure. (The MMTIME structure is defined in the Win32 SDK.)
dwParam2
Size of the MMTIME in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInGetPos return values in the Win32 SDK.

Comments

A client sends the WIDM_GETPOS message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

The wType member of the MMTIME structure indicates the time format requested by the client. If the driver cannot support the requested format, it should return the position in a format that it does support, and change the wType member accordingly.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_POSITION control code.


WIDM_LOWPRIORITY

The WIDM_LOWPRIORITY message requests a waveform input driver to run at low priority.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_LOWPRIORITY
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h.

Comments

A client sends the WIDM_LOWPRIORITY message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_LOW_PRIORITY control code. Kernel-mode drivers using soundlib.lib allow only one client to be running at low priority.

Support for this message by user-mode drivers is optional.


WIDM_OPEN

The WIDM_OPEN message is sent to a waveform input driver to request it to open an instance of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_OPEN
dwUser
Pointer to location to receive device instance identifier.
dwParam1
Pointer to a WAVEOPENDESC structure, containing the client’s device handle, notification target, and instance ID.
dwParam2
Contains flags. The following flags are defined:

Flag

Definition

CALLBACK_EVENT

Indicates the dwCallback member of WAVEOPENDESC is an event handle.

CALLBACK_FUNCTION

Indicates the dwCallback member of WAVEOPENDESC is the address of a callback function.

CALLBACK_TASK

Indicates the dwCallback member of WAVEOPENDESC is a task handle.

CALLBACK_WINDOW

Indicates the dwCallback member of WAVEOPENDESC is a window handle.

WAVE_FORMAT_DIRECT

Data compression/decompression operations should take place in hardware. See Comments section below.

WAVE_FORMAT_QUERY

The driver should indicate whether or not it supports the specified format. See Comments section below.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInOpen return values in the Win32 SDK.

Comments

A client sends the WIDM_OPEN message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

Typically, user-mode drivers connect to kernel-mode drivers by calling CreateFile, specifying the MS-DOS device name of one of the kernel-mode driver’s devices.

The driver assigns a device instance identifier and returns it in the location pointed to by dwUser. The driver can expect to receive this value as the dwUser input argument to all other widMessage messages.

The driver determines the number of clients it allows to use a particular device. If a device is opened by the maximum number of clients, it returns MMSYSERR_ALLOCATED for subsequent open requests.

The WAVE_FORMAT_DIRECT flag is meant for use with a wave mapper. If the flag is set in dwParam2, the driver should not call the Audio Compression Manager to handle compression/decompression operations; the caller wants the hardware to perform these operations directly. If the hardware is not capable of performing compression/decompression operations, the driver should return MMSYSERR_NOTSUPPORTED when WAVE_FORMAT_DIRECT is set.

If the WAVE_FORMAT_QUERY flag is set in dwParam2, the driver should not open the device, but should instead determine whether it supports the format specified by the WAVEOPENDESC structure’s lpFormat member. If the driver supports the requested format, it should return MMSYSERR_NOERROR. Otherwise it should return WAVERR_BADFORMAT.

If the open operation succeeds, the driver should send the client a WIM_OPEN message by calling the DriverCallback function.

For additional information, see Transferring Waveform Input Data.


WIDM_PREPARE

The WIDM_PREPARE message requests a waveform input driver to prepare a system-exclusive data buffer for input.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_PREPARE
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEHDR structure identifying the buffer. (The WAVEHDR structure is described in the Win32 SDK.)
dwParam2
Size of the WAVEHDR structure.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInPrepareHeader return values in the Win32 SDK.

Comments

A client sends the WIDM_PREPARE message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports WIDM_PREPARE, it must also support WIDM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll prepares the buffer for use. For most drivers, this behavior is sufficient. If the driver does perform buffer preparation, it must set WHDR_PREPARED in the dwFlags member of WAVEHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring Waveform Input Data.


WIDM_RESET

The WIDM_RESET message requests a waveform input driver to stop recording and return all buffers in the input queue to the client.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_RESET
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInReset return values in the Win32 SDK.

Comments

A client sends the WIDM_RESET message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

Typically, the user-mode driver stops recording by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_WAVE_SET_STATE control code.

For each buffer remaining in the driver’s input queue (see WIDM_ADDBUFFER), the driver should set WHDR_DONE and clear WHDR_INQUEUE in the dwFlags member of the buffer’s WAVEHDR structure, and also set the structure’s dwBytesRecorded member. Finally, a WIM_DATA callback message should be sent for each buffer.

For additional information, see Transferring Waveform Input Data.


WIDM_START

The WIDM_START message requests a waveform input driver to begin recording.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_START
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInStart return values in the Win32 SDK.

Comments

A client sends the WIDM_START message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

If the message is received after input has been started, the driver should return MMSYSERR_NOERROR.

User-mode waveform input drivers should handle input asynchronously, by creating a separate thread to handle communication with the kernel-mode driver. Typically, the new thread starts recording by calling DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_STATE control code, and by calling ReadFileEx to request the kernel-mode driver to fill client-supplied buffers (see WIDM_ADDBUFFER). When the kernel-mode driver returns a filled buffer, the user-mode driver should:

  • Set the dwBytesRecorded member in the buffer’s WAVEHDR structure.

  • Set the buffer’s WHDR_DONE flag.

  • Clear the buffer’s WHDR_INQUEUE flag.

  • Send a WIM_DATA callback message to the client.

To avoid unnecessarily locking too much memory, do not send the kernel-mode driver too many buffers at once, or buffers that are excessively large.

Recording should continue until the client sends WIDM_STOP or WIDM_RESET.

For additional information, see Transferring Waveform Input Data.


WIDM_STOP

The WIDM_STOP message requests a waveform input driver to stop recording.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_STOP
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInStop return values in the Win32 SDK.

Comments

A client sends the WIDM_STOP message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

If a buffer in the input queue (see WIDM_ADDBUFFER) has been partially filled, the driver should treat it as a full buffer and return it to the client (see WIDM_START). Empty buffers should remain in the queue.

If this message is received and recording is already stopped, the driver should return MMSYSERR_NOERROR.

Typically, the user-mode driver stops recording by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_WAVE_SET_STATE control code.

For additional information, see Transferring Waveform Input Data.


WIDM_UNPREPARE

The WIDM_UNPREPARE message requests a waveform input driver to remove the buffer preparation that was performed in response to a WIDM_PREPARE message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WIDM_UNPREPARE
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEHDR structure identifying the data buffer. (The WAVEHDR structure is described in the Win32 SDK.)
dwParam2
Size, in bytes, of the WAVEHDR structure.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveInUnprepareHeader return values in the Win32 SDK.

Comments

A client sends the WIDM_UNPREPARE message by calling the user-mode driver’s widMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports WIDM_PREPARE, it must also support WIDM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll removes the buffer preparation. For most drivers, this behavior is sufficient. If the driver does support WIDM_UNPREPARE, it must clear WHDR_PREPARED in the dwFlags member of WAVEHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring Waveform Input Data.


WIM_CLOSE

The WIM_CLOSE callback message notifies a client that a user-mode driver has finished processing a WIDM_CLOSE message.

Parameters
dwMsg
WIM_CLOSE
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode waveform input driver sends a WIM_CLOSE message to its client, by means of a callback, when the driver finishes processing a WIDM_CLOSE message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the WIM_CLOSE message only if the client has previously specified a notification target with a WIDM_OPEN message.

Win32 SDK documentation states that clients receive an MM_WIM_CLOSE message if the notification target is a window handle. WIM_CLOSE and MM_WIM_CLOSE are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring Waveform Input Data.


WIM_DATA

The WIM_DATA callback message notifies a client that a user-mode driver has filled a buffer with waveform data.

Parameters
dwMsg
WIM_DATA
dwParam1
Address of a WAVEHDR structure identifying a buffer containing the message. (WAVEHDR is defined in the Win32 SDK.)
dwParam2
NULL
Comments

A user-mode waveform input driver sends a WIM_DATA message to its client, by means of a callback, when the driver has filled a buffer with waveform data. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The WAVEHDR structure is one that was received along with a WIDM_ADDBUFFER message.

The driver sends the WIM_DATA message only if the client has previously specified a notification target with a WIDM_OPEN message.

Win32 SDK documentation states that clients receive an MM_WIM_DATA message if the notification target is a window handle. WIM_DATA and MM_WIM_DATA are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring Waveform Input Data.


WIM_OPEN

The WIM_OPEN callback message notifies a client that a user-mode driver has finished processing a WIDM_OPEN message.

Parameters
dwMsg
WIM_OPEN
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode waveform output driver sends a WIM_OPEN message to its client, by means of a callback, when the driver finishes processing a WIDM_OPEN message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the WIM_OPEN message only if the client has specified a notification target with the WIDM_OPEN message.

Win32 SDK documentation states that clients receive an MM_WIM_OPEN message if the notification target is a window handle. WIM_OPEN and MM_WIM_OPEN are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring Waveform Input Data.


WODM_BREAKLOOP

The WODM_BREAKLOOP message requests a waveform output driver to break an output loop that was created with a WODM_WRITE message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_BREAKLOOP
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutBreakLoop return values in the Win32 SDK.

Comments

A client sends the WODM_BREAKLOOP message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

The driver should stop output of the loop buffers at the end of the next loop iteration.

If the driver receives this message and a loop is not in progress, it should return MMSYSERR_NOERROR.


WODM_CLOSE

The WODM_CLOSE message requests a waveform output driver to close a specified device instance that was previously opened with a WODM_OPEN message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_CLOSE
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutClose return values in the Win32 SDK.

Comments

A client sends the WODM_CLOSE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

If the client has passed data buffers to the user-mode driver by means of WODM_WRITE messages, and if the user-mode driver hasn’t finished sending the data to the kernel-mode driver, the user-mode driver should return WAVERR_STILLPLAYING in response to WODM_CLOSE.

After the driver closes the device instance it should send a WOM_CLOSE callback message to the client.

For additional information, see Transferring Waveform Output Data.


WODM_GETDEVCAPS

The WODM_GETDEVCAPS message requests a waveform output driver to return the capabilities of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_GETDEVCAPS
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEOUTCAPS structure, which is described in the Win32 SDK.
dwParam2
Size of the WAVEOUTCAPS structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutGetDevCaps return values in the Win32 SDK.

Comments

A client sends the WODM_GETDEVCAPS message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_CAPABILITIES control code.

The user-mode driver fills the WAVEOUTCAPS structure.


WODM_GETNUMDEVS

The WODM_GETNUMDEVS message requests a waveform output driver to return the number of device instances that it supports.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_GETNUMDEVS
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver returns the number of waveform output device instances it supports.

Comments

A client sends the WODM_GETNUMDEVS message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

The driver should return the number of logical waveform output devices that can be supported. Typically, for each physical device, a kernel-mode driver can support one or more logical devices of various types. For example, for each Creative Labs Sound Blaster card, there are MIDI, waveform, mixer, and auxiliary audio devices. Kernel-mode drivers store logical device names and types in the registry under the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices. To correctly return the number of logical devices, the user-mode driver should examine the \Devices subkey for each of the driver’s \Device Number keys, searching for logical devices of the desired type. (Code in drvlib.lib provides this capability.)


WODM_GETPITCH

The WODM_GETPITCH message requests a waveform output driver to return the specified device’s current pitch multiplier value.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_GETPITCH
dwUser
Device instance identifier.
dwParam1
Pointer to a DWORD location used to return the current pitch multiplier value. This is specified as a fixed-point value, where the high-order word of the DWORD contains the signed integer part of the number, and the low-order word contains the fractional part. The fraction consists of a WORD value, for which 0x8000 represents one half, and 0x4000 represents one quarter. For example, the value 0x00010000 specifies a multiplier of 1.0 (no pitch change), and a value of 0x000F8000 specifies a multiplier of 15.5.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutGetPitch return values in the Win32 SDK.

Comments

A client sends the WODM_GETPITCH message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for the WODM_GETPITCH message by user-mode drivers is optional. If a driver supports the WODM_SETPITCH message, it must also support WODM_GETPITCH.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_PITCH control code.


WODM_GETPLAYBACKRATE

The WODM_GETPLAYBACKRATE message requests a waveform output driver to return the current playback rate multiplier value for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, etc.) for the target device.
uMsg
WODM_GETPLAYBACKRATE
dwUser
Device instance identifier.
dwParam1
Pointer to a DWORD location used to return the current playback rate multiplier value. This is specified as a fixed-point value, where the high-order word of the DWORD contains the signed integer part of the number, and the low-order word contains the fractional part. The fraction consists of a WORD value, for which 0x8000 represents one half, and 0x4000 represents one quarter. For example, the value 0x00010000 specifies a multiplier of 1.0 (no rate change), and a value of 0x000F8000 specifies a multiplier of 15.5.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutGetPlaybackRate return values in the Win32 SDK.

Comments

A client sends the WODM_GETPLAYBACKRATE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for the WODM_GETPLAYBACKRATE message by user-mode drivers is optional. If a driver supports the WODM_SETPLAYBACKRATE message, it must also support WODM_GETPLAYBACKRATE.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_PLAYBACK_RATE control code.


WODM_GETPOS

The WODM_GETPOS message requests a waveform output driver to return the current input position within a waveform. The input position is relative to the beginning of the waveform.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_GETPOS
dwUser
Device instance identifier.
dwParam1
Pointer to an MMTIME structure, which is described in the Win32 SDK.
dwParam2
Size of the MMTIME structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutGetPos return values in the Win32 SDK.

Comments

A client sends the WODM_GETPOS message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

The wType member of the MMTIME structure indicates the time format requested by the client. If the driver cannot support the requested format, it should return the position in a format that it does support, and change the wType member accordingly.

The position should be reset to zero when the driver receives a WODM_OPEN or WODM_RESET message.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_POSITION control code.


WODM_GETVOLUME

The WODM_GETVOLUME message requests a waveform output driver to return the current volume level setting for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_GETVOLUME
dwUser
Device instance identifier.
dwParam1
Pointer to a DWORD location to receive the volume setting.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutGetVolume return values in the Win32 SDK.

Comments

A client sends the WODM_GETVOLUME message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports WODM_SETVOLUME, it must support WODM_GETVOLUME.

The volume value is returned in the DWORD pointed to by dwParam1 as follows.
Channel | Portion of dwParam1 Used
Left channel | Low word
Right channel | High word
Single channel | Low word

A value of zero is silence, and a value of 0xFFFF is full volume.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_GET_VOLUME control code.


WODM_OPEN

The WODM_OPEN message requests a waveform output driver to open an instance of a specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_OPEN
dwUser
Pointer to location to receive device instance identifier.
dwParam1
Pointer to a WAVEOPENDESC structure, containing the client’s device handle, notification target, and instance ID.
dwParam2
Contains flags. The following flags are defined:

Flag

Definition

CALLBACK_EVENT

Indicates the dwCallback member of WAVEOPENDESC is an event handle.

CALLBACK_FUNCTION

Indicates the dwCallback member of WAVEOPENDESC is the address of a callback function.

CALLBACK_TASK

Indicates the dwCallback member of WAVEOPENDESC is a task handle.

CALLBACK_WINDOW

Indicates the dwCallback member of WAVEOPENDESC is a window handle.

WAVE_FORMAT_DIRECT

Data compression/decompression operations should take place in hardware. See Comments section below.

WAVE_FORMAT_QUERY

The driver should indicate whether or not it supports the specified format. See comments.

WAVE_ALLOWSYNC

Indicates the driver should allow opening of a synchronous device. Ignored by mmdrv.dll and drvlib.lib.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutOpen return values in the Win32 SDK.

Comments

A client sends the WODM_OPEN message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Typically, user-mode drivers connect to kernel-mode drivers by calling CreateFile, specifying the MS-DOS device name of one of the kernel-mode driver’s devices.

The driver assigns a device instance identifier and returns it in the location pointed to by dwUser. The driver can expect to receive this value as the dwUser input argument to all other wodMessage messages.

The driver determines the number of clients it allows to use a particular device. If a device is opened by the maximum number of clients, it returns MMSYSERR_ALLOCATED for subsequent open requests.

The WAVE_FORMAT_DIRECT flag is meant for use with a wave mapper. If the flag is set in dwParam2, the driver should not call the Audio Compression Manager to handle compression/decompression operations; the caller wants the hardware to perform these operations directly. If the hardware is not capable of performing compression/decompression operations, the driver should return MMSYSERR_NOTSUPPORTED when WAVE_FORMAT_DIRECT is set.

If the WAVE_FORMAT_QUERY flag is set in dwParam2, the driver should not open the device, but should instead determine whether it supports the format specified by the WAVEOPENDESC structure’s lpFormat member. If the driver supports the requested format, it should return MMSYSERR_NOERROR. Otherwise it should return WAVERR_BADFORMAT.

If the open operation succeeds, the driver should send the client a WOM_OPEN message by calling the DriverCallback function.

For additional information, see Transferring Waveform Output Data.


WODM_PAUSE

The WODM_PAUSE message requests a waveform output driver to pause playback of a waveform.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_PAUSE
dwUser
Pointer to location to receive device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutPause return values in the Win32 SDK.

Comments

A client sends the WODM_PAUSE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

The driver should stop playing the waveform and should save the current position. Playback should continue from this position when a WODM_RESTART message is received. Output buffers received with the WODM_WRITE message while playback is paused should be placed in the output queue.

If the driver receives this message while output is already paused, it should return MMSYSERR_NOERROR.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_STATE control code.


WODM_PREPARE

The WODM_PREPARE message requests a waveform output driver to prepare a system-exclusive data buffer for output.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_PREPARE
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEHDR structure identifying the buffer. (The WAVEHDR structure is described in the Win32 SDK.)
dwParam2
Size of the WAVEHDR structure.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutPrepareHeader return values in the Win32 SDK.

Comments

A client sends the WODM_PREPARE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports WODM_PREPARE, it must also support WODM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll prepares the buffer for use. For most drivers, this behavior is sufficient. If the driver does perform buffer preparation, it must set WHDR_PREPARED in the dwFlags member of WAVEHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring Waveform Output Data.


WODM_RESET

The WODM_RESET message requests a waveform output driver to stop sending output data and return all output buffers to the client.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_RESET
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutReset return values in the Win32 SDK.

Comments

A client sends the WODM_RESET message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

If the driver’s output queue contains any output buffers (see WODM_WRITE) whose contents have not been sent to the kernel-mode driver, the driver should set the WHDR_DONE flag and clear the WDR_INQUEUE flag in each buffer’s WAVEHDR structure The driver should then send the client a WOM_DONE callback message for each buffer.

The driver should reset its position count to zero. If playback is paused, the driver should also take itself out of the paused state.

Typically, the user-mode driver stops device output by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_WAVE_SET_STATE control code.

For additional information, see Transferring Waveform Output Data.


WODM_RESTART

The WODM_RESTART message requests a waveform output driver to continue playback of a waveform after playback has been paused with the WODM_PAUSE message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_RESET
dwUser
Device instance identifier.
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutRestart return values in the Win32 SDK.

Comments

A client sends the WODM_RESET message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Data output should resume from the position that was saved when the WODM_PAUSE message was received.

If the driver receives a WODM_RESTART message and output is not in a paused state, it should to nothing except return MMSYSERR_NOERROR.

Typically, the user-mode driver resumes device output by calling DeviceIoControl, sending the kernel-mode driver an IOCTL_WAVE_SET_STATE control code.


WODM_SETPITCH

The WODM_SETPITCH message requests a waveform output driver to set the specified device’s pitch multiplier value.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_SETPITCH
dwUser
Device instance identifier.
dwParam1
A DWORD containing the pitch multiplier value. This is specified as a fixed-point value, where the high-order word of the DWORD contains the signed integer part of the number, and the low-order word contains the fractional part. The fraction consists of a WORD value, for which 0x8000 represents one half, and 0x4000 represents one quarter. For example, the value 0x00010000 specifies a multiplier of 1.0 (no pitch change), and a value of 0x000F8000 specifies a multiplier of 15.5.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutSetPitch return values in the Win32 SDK.

Comments

A client sends the WODM_SETPITCH message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for the WODM_SETPITCH message by user-mode drivers is optional. If a driver supports the WODM_SETPITCH message, it must also support WODM_GETPITCH. Additionally, in response to a WODM_GETDEVCAPS message, it must set WAVECAPS_PITCH in the dwSupport member of the WAVEOUTCAPS structure.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_PITCH control code.

Note: The kernel-mode driver library, soundlib.lib, does not support pitch changes.


WODM_SETPLAYBACKRATE

The WODM_SETPLAYBACKRATE message requests a waveform output driver to set the playback rate multiplier value for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_SETPLAYBACKRATE
dwUser
Device instance identifier.
dwParam1
A DWORD containing the playback rate multiplier value. This is specified as a fixed-point value, where the high-order word of the DWORD contains the signed integer part of the number, and the low-order word contains the fractional part. The fraction consists of a WORD value, for which 0x8000 represents one half, and 0x4000 represents one quarter. For example, the value 0x00010000 specifies a multiplier of 1.0 (no rate change), and a value of 0x000F8000 specifies a multiplier of 15.5.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutSetPlaybackRate return values in the Win32 SDK.

Comments

A client sends the WODM_SETPLAYBACKRATE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for the WODM_SETPLAYBACKRATE message by user-mode drivers is optional. If a driver supports the WODM_SETPLAYBACKRATE message, it must also support WODM_GETPLAYBACKRATE. Additionally, in response to a WODM_GETDEVCAPS message, it must set WAVECAPS_PLAYBACKRATE in the dwSupport member of the WAVEOUTCAPS structure.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_PLAYBACK_RATE control code. Kernel-mode drivers can implement playback rate changes by skipping or repeating samples. For example, if the playback rate is 2.0, the driver would play every second sample at the original playback rate.

Note: The kernel-mode driver library, soundlib.lib, does not support playback rate changes.


WODM_SETVOLUME

The WODM_SETVOLUME message requests a waveform output driver to set the volume level for the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_SETVOLUME
dwUser
Device instance identifier.
dwParam1
Pointer to a DWORD location to receive the volume setting.
dwParam2
Not used.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutSetVolume return values in the Win32 SDK.

Comments

A client sends the WODM_SETVOLUME message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports WODM_SETVOLUME, it must support WODM_GETVOLUME.

The volume value is returned in the DWORD pointed to by dwParam1 as follows.
Channel | Portion of dwParam1 Used
Left channel | Low word
Right channel | High word
Single channel | Low word

A value of zero is silence, and a value of 0xFFFF is full volume.

Typically, the user-mode driver calls DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_VOLUME control code.


WODM_UNPREPARE

The WODM_UNPREPARE message requests a waveform output driver to remove the buffer preparation that was performed in response to a WODM_PREPARE message.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_UNPREPARE
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEHDR structure identifying the data buffer. (The WAVEHDR structure is described in the Win32 SDK.)
dwParam2
Size of the WAVEHDR structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutUnprepareHeader return values in the Win32 SDK.

Comments

A client sends the WODM_UNPREPARE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

Support for this message by user-mode drivers is optional. If the driver supports WODM_PREPARE, it must also support WODM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, winmm.dll removes the buffer preparation. For most drivers, this behavior is sufficient. If the driver does support WODM_UNPREPARE, it must clear WHDR_PREPARED in the dwFlags member of WAVEHDR and return MMSYSERR_NOERROR.

For additional information, see Transferring Waveform Output Data.


WODM_WRITE

The WODM_WRITE message requests a waveform output driver to write a waveform data block to the specified device.

Parameters
uDeviceId
Device identifier (0, 1, 2, and so on) for the target device.
uMsg
WODM_WRITE
dwUser
Device instance identifier.
dwParam1
Pointer to a WAVEHDR structure identifying the data buffer. (The WAVEHDR structure is described in the Win32 SDK.)
dwParam2
Size of the WAVEHDR structure in bytes.
Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR or WAVERR error codes defined in mmsystem.h. See waveOutWrite return values in the Win32 SDK.

Comments

A client sends the WODM_WRITE message by calling the user-mode driver’s wodMessage entry point, passing the specified parameters.

If the WHDR_PREPARED flag in the dwFlags member of WAVEHDR is not set, the driver should return WAVERR_UNPREPARED.

Unless the device has been paused with a WODM_PAUSE message, the driver should begin playback the first time it receives a WODM_WRITE message.

User-mode waveform output drivers should handle output asynchronously, by creating a separate thread to handle communication with the kernel-mode driver. Typically, the original thread queues the output buffer, sets its WHDR_INQUEUE flag and clears its WHDR_DONE flag in the WAVEHDR structure, and returns control to the client.

Meanwhile, the new thread starts the output operation by calling DeviceIoControl to send the kernel-mode driver an IOCTL_WAVE_SET_STATE control code, and by calling WriteFileEx to send the kernel-mode driver the client-supplied data. When the kernel-mode driver finishes using a buffer, this thread should set the buffer’s WHDR_DONE flag. clear the buffer’s WHDR_INQUEUE flag, and send a WOM_DONE callback message to the client.

To avoid unnecessarily locking too much memory, do not send the kernel-mode driver too many buffers at once, or buffers that are excessively large.

The driver should continue sending buffers to the kernel-mode driver until the client sends WODM_PAUSE or WIDM_RESET.

The user-mode driver is usually responsible for implementing waveform looping. The driver should check each buffer’s WAVEHDR structure for WHDR_BEGINLOOP and WHDR_ENDLOOP flags, along with an iteration count in the structure’s dwLoops member.

For additional information, see Transferring Waveform Output Data.


WOM_CLOSE

The WOM_CLOSE callback message notifies a client that a user-mode driver has finished processing a WODM_CLOSE message.

Parameters
dwMsg
WOM_CLOSE
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode waveform input driver sends a WOM_CLOSE message to its client, by means of a callback, when the driver finishes processing a WODM_CLOSE message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the WOM_CLOSE message only if the client has previously specified a notification target with a WODM_OPEN message.

Win32 SDK documentation states that clients receive an MM_WOM_CLOSE message if the notification target is a window handle. WOM_CLOSE and MM_WOM_CLOSE are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring Waveform Output Data.


WOM_DONE

The WOM_DONE callback message notifies a client that a user-mode driver has finished processing a WODM_WRITE message.

Parameters
dwMsg
WOM_DONE
dwParam1
Address of the WAVEHDR structure that was received with the WODM_WRITE message. (WAVEIHDR is defined in the Win32 SDK.)
dwParam2
NULL
Comments

A user-mode waveform output driver sends a WOM_DONE message to its client, by means of a callback, when the driver finishes processing a WODM_WRITE message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the WOM_DONE message only if the client has previously specified a notification target with a WODM_OPEN message.

Win32 SDK documentation states that clients receive an MM_WOM_DONE message if the notification target is a window handle. WOM_DONE and MM_WOM_DONE are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring Waveform Output Data.


WOM_OPEN

The WOM_OPEN callback message notifies a client that a user-mode driver has finished processing a WODM_OPEN message.

Parameters
dwMsg
WOM_OPEN
dwParam1
NULL
dwParam2
NULL
Comments

A user-mode waveform output driver sends a WOM_OPEN message to its client, by means of a callback, when the driver finishes processing a WODM_OPEN message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the WOM_OPEN message only if the client has specified a notification target with the WODM_OPEN message.

Win32 SDK documentation states that clients receive an MM_WOM_OPEN message if the notification target is a window handle. WOM_OPEN and MM_WOM_OPEN are equivalent.

For additional information, see Notifying Clients from Audio Drivers and Transferring Waveform Output Data.


Structures, User-Mode Audio Drivers

This section describes the structures used by user-mode audio drivers. These structures are defined in mmddk.h.


MIDIOPENDESC

typedef struct midiopendesc_tag {
    HMIDI hMidi;
    DWORD dwCallback;
    DWORD dwInstance;
    DWORD cIds;
    MIDIOPENSTRMID rgIds[1];
} MIDIOPENDESC;

The MIDIOPENDESC structure contains information needed by user-mode MIDI input and MIDI output drivers for sending callback messages to clients. The structure is created by winmm.dll and passed to the user-mode driver along with a MODM_OPEN or MIDM_OPEN message.

Members
hMidi
Specifies the client’s handle to the device, as assigned by winmm.dll. User-mode drivers specify this handle as the hDriver parameter to DriverCallback, when sending a callback message.
dwCallback
Specifies either the address of a callback function, a window handle, an event handle, or a task handle, depending on the flag specified in the dwParam2 parameter of the MODM_OPEN or MIDM_OPEN message.
dwInstance
Contains the dwCallbackInstance argument that the client specified when calling the midiInOpen or midiOutOpen function. This value is returned to the client as the dwInstance parameter to DriverCallback.
cIds
Number of rgIds array elements.
rgIds
Array of stream identifiers, containing one element for each open stream.


MIXEROPENDESC

typedef struct tMIXEROPENDESC
{
    HMIXER          hmx;
    LPVOID          pReserved0;
    DWORD           dwCallback;
    DWORD           dwInstance;
} MIXEROPENDESC;

The MIXEROPENDESC structure contains information needed by user-mode mixer drivers. The structure is created by winmm.dll and passed to the driver with an MXDM_OPEN message.

Members
hmx
Specifies the client’s handle to the device, as assigned by winmm.dll. User-mode drivers specify this handle as the hDriver parameter to DriverCallback, when sending a callback message.
dwCallback
Specifies the address of a callback function. (See the description of the MXDM_OPEN message.)
dwInstance
Contains the dwCallbackInstance argument that the client specified when calling the mixerOpen function. This value is returned to the client as the dwInstance parameter to DriverCallback.


WAVEOPENDESC

typedef struct waveopendesc_tag {
    HWAVE hWave;
    LPWAVEFORMAT lpFormat;
    DWORD dwCallback;
    DWORD dwInstance;
    UINT uMappedDeviceID;
} WAVEOPENDESC;

The WAVEOPENDESC structure contains information needed by user-mode waveform input and output drivers. The structure is created by winmm.dll and passed to the driver with a WODM_OPEN or WIDM_OPEN message.

Members
hWave
Specifies the client’s handle to the device, as assigned by winmm.dll. User-mode drivers specify this handle as the hDriver parameter to DriverCallback, when sending a callback message.
lpFormat
Points to a WAVEFORMATEX structure, indicating the waveform data format requested by the client. (The WAVEFORMATEX structure is described in the Win32 SDK.)
dwCallback
Specifies either the address of a callback function, a window handle, an event handle, or a task handle, depending on the flag contained in the dwParam2 parameter of the WODM_OPEN or WIDM_OPEN message.
dwInstance
Contains the dwCallbackInstance argument that the client specified when calling the waveInOpen or waveOutOpen function. This value is returned to the client as the dwInstance parameter to DriverCallback.
uMappedDeviceID
For wave mapper, contains device identifier of mapped device.


Functions and Macros, drvlib.lib

This section describes the functions and macros available to user-mode audio drivers using drvlib.lib. Function prototypes and macros are defined in the file registry.h.


DrvAccess

DrvAccess(
PREG_ACCESS RegAccess
);

The DrvAccess macro determines if access to the service control manager has been granted by a previous call to DrvCreateServicesNode.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

The macro equals 1 if access was granted. Otherwise the macro equals 0.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvCloseServiceManager

VOID
DrvCloseServiceManager(
PREG_ACCESS RegAccess
);

The DrvCloseServiceManager function closes the connection to the service control manager that was created by calling DrvCreateServicesNode.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

None.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

If DrvSaveParametersKey has been called to save registry contents in a temporary file, the file is deleted.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvConfigureDriver

BOOL
DrvConfigureDriver(
PREG_ACCESS RegAccess,
LPTSTR DriverName,
SOUND_KERNEL_MODE_DRIVER_TYPE DriverType,
BOOL (*SetParms)(PVOID),
PVOID Context
);

The DrvConfigureDriver function opens a connection to the service control manager, creates a kernel-mode driver service for the specified driver, and loads the kernel-mode driver.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
DriverName
Pointer to the driver name. Must match DriverName in registry path.
DriverType
Driver type, used for indicating the kernel-mode driver’s load group. The type is SOUND_KERNEL_MODE_DRIVER_TYPE. The value can be one of the following.
Value | Definition
SoundDriverTypeNormal | Adds kernel-mode driver to “base” load group.
SoundDriverTypeSynth | Adds kernel-mode driver to “Synthesizer Drivers” load group.

The “Synthesizer Drivers” group is unknown to Windows NT and therefore is guaranteed to be loaded last.

SetParms
Pointer to a driver-supplied function that is called before the kernel-mode driver is reloaded. Can be NULL.
Context
Pointer to a driver-defined structure that is passed as input to the function pointed to by SetParms. Can be NULL.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The function performs the following operations, in order:

  1. Calls DrvCreateServicesNode, specifying TRUE for the Create parameter.

  2. Calls DrvUnloadKernelDriver to unload the kernel-mode driver.

  3. Calls the driver-supplied function specified by the SetParms parameter. Generally, drivers use this function to modify configuration parameters in the registry.

  4. Calls DrvLoadKernelDriver to reload and restart the kernel-mode driver.

Typically, a user-mode driver calls DrvConfigureDriver from its DriverProc function when processing a DRV_CONFIGURE or DRV_INSTALL command, after obtaining user-specified configuration parameters from a dialog box.

After DrvConfigureDriver returns, call DrvCloseServiceManager.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvCreateDeviceKey

HKEY
DrvCreateDeviceKey(
LPCTSTR DriverName
);

The DrvCreateDeviceKey function creates a device subkey under the driver’s \Parameters key. The registry path to the created key is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number.

Parameters
DriverName
Pointer to the driver name. Must match DriverName in registry path.
Return Value

Returns a handle to the device subkey, if the operation succeeds. Otherwise returns NULL.

Comments

The function creates the \Parameters key if it does not exist.

Device subkey names are assigned as \Device0, \Device1, \Device2, and so on.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvCreateServicesNode

BOOL
DrvCreateServicesNode(
PTCHAR DriverName,
SOUND_KERNEL_MODE_DRIVER_TYPE DriverType,
PREG_ACCESS RegAccess,
BOOL Create
);

The DrvCreateServicesNode function creates a connection to the service control manager and, optionally, creates a service object for the kernel-mode driver. The caller must have Administrator’s privilege.

Parameters
DriverName
Pointer to the driver name. Must match DriverName in registry path.
DriverType
Driver type, used for indicating the kernel-mode driver’s load group. The type is SOUND_KERNEL_MODE_DRIVER_TYPE. The value can be one of the following.
Value | Definition
SoundDriverTypeNormal | Adds kernel-mode driver to “base” load group.
SoundDriverTypeSynth | Adds kernel-mode driver to “Synthesizer Drivers” load group.

The “Synthesizer Drivers” group is unknown to Windows NT and therefore is guaranteed to be loaded last.

RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Create
If TRUE, the function creates a service object for the kernel-mode driver, if it does not already exist. If FALSE, the function does not create the service object.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

Under Windows NT, kernel-mode drivers are considered to be services under the control of the service control manager. The DrvCreateServicesNode function calls OpenSCManager to create a connection to the local service control manager. OpenSCManager is called with a desired access type of SC_MANAGER_ALL_ACCESS, which requires Administrators privilege. (For information about OpenSCManager, see the Win32 SDK.) The service manager handle returned by OpenSCManager is stored in the REG_ACCESS structure.

If the Create parameter is TRUE, the DrvCreateServicesNode function calls CreateService to create the kernel-mode driver service and obtain a service handle. The DrvCreateServicesNode function sets the service’s start type to SERVICE_DEMAND_START, so it will not automatically reload when the system is restarted. (For information about CreateService, see the Win32 SDK.)

A result of calling CreateService is the creation of a driver subkey under the \Services registry key. The path to the subkey is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName.

If the Create parameter is FALSE, the function just creates a connection to the service control manager and saves the service manager handle returned by OpenSCManager in the REG_ACCESS structure. Anytime after calling DrvCreateServicesNode, you can call the DrvAccess macro to determine if access to the service control manager was granted.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvDeleteServicesNode

BOOL
DrvDeleteServicesNode(
PREG_ACCESS RegAccess
 );

The DrvDeleteServicesNode function marks for deletion a kernel-mode driver service that was created by calling DrvCreateServicesNode.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. To obtain an error code value, call GetLastError, which is described in the Win32 SDK.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The function calls DeleteService, described in the Win32 SDK, to mark the kernel-mode driver service for deletion.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvIsDriverLoaded

BOOL
DrvIsDriverLoaded(
PREG_ACCESS RegAccess
 );

The DrvIsDriverLoaded function determines if the kernel-mode driver is currently loaded and running.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns TRUE if the kernel-mode driver is loaded and running. Otherwise returns FALSE.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

Drivers must call DrvCreateServicesNode before calling DrvIsDriverLoaded.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvLibInit

BOOL
DrvLibInit(
HINSTANCE hModule,
ULONG Reason,
PCONTEXT pContext
);

The DrvLibInit function initializes drvlib.lib for use with the calling user-mode audio driver. User-mode audio drivers call this function before they begin calling other functions in drvlib.lib. They call the function again prior to being unloaded.

Parameters
hModule
Instance handle of the module that opened the user-mode driver. Obtained by calling GetDriverModuleHandle, which is described in the Win32 SDK.
Reason
Set to one of the following values.
  • DLL_PROCESS_ATTACH, if attaching to drvlib.lib.

  • DLL_PROCESS_DETACH, if detaching from drvlib.lib.
pContext
Not used. Should be set to NULL.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

When the driver’s DriverProc function receives a DRV_LOAD message, it should call DrvLibInit with a Reason value of DLL_PROCESS_ATTACH.

When the driver’s DriverProc function receives a DRV_FREE message, it should call DrvLibInit with a Reason value of DLL_PROCESS_DETACH.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvLoadKernelDriver

BOOL
DrvLoadKernelDriver(
PREG_ACCESS RegAccess
 );

The DrvLoadKernelDriver function loads and starts the kernel-mode driver.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. To obtain an error code value, call GetLastError. which is described in the Win32 SDK.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The function sets the kernel-mode driver service’s start type to SERVICE_SYSTEM_START, so it will automatically reload and restart when the system is restarted. (For more information, ChangeServiceConfig in the Win32 SDK.)

Drivers must call DrvCreateServicesNode before calling DrvLoadKernelDriver.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvNumberOfDevices

LONG
DrvNumberOfDevices(
PREG_ACCESS RegAccess,
LPDWORD NumberOfDevices
);

The DrvNumberOfDevices function determines the number of device subkeys existing under the driver’s \Parameters key in the registry.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
NumberOfDevices
Address of a location to receive the number of devices.
Return Value

Returns ERROR_SUCCESS if the operation succeeds. Otherwise returns one of the error codes defined in winerror.h.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

Drivers create device subkeys by calling DrvCreateDeviceKey.

Drivers must call DrvCreateServicesNode before calling DrvNumberOfDevices.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvQueryDeviceIdParameter

LONG
DrvQueryDeviceIdParameter(
PREG_ACCESS RegAccess,
UINT DeviceNumber,
PTCHAR ValueName,
PDWORD pValue);

The DrvQueryDeviceIdParameter function reads the value associated with the specified value name, under the specified driver’s \Parameters registry key.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
DeviceNumber
Device number. Used as an index to the device subkeys under the \Parameters key. See the Comments section below.
ValueName
Pointer to a string representing the value name.
pValue
Pointer to a DWORD to receive the requested value.
Return Value

Returns ERROR_SUCCESS if the operation succeeds. Otherwise returns one of the error codes defined in winerror.h. See the Comments section below.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The value name and value are read from the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number, where Number represents the number supplied by the DeviceNumber parameter.

The function can only return a value that is stored as a REG_DWORD type.

The function calls RegQueryValueEx and returns its return value. (See the Win32 SDK.)

To store values under a driver’s \Parameters registry key, call DrvSetDeviceIdParameter.

Drivers must call DrvCreateServicesNode before calling DrvQueryDeviceIdParameter.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvRemoveDriver

LRESULT
DrvRemoveDriver(
PREG_ACCESS RegAccess
 );

The DrvRemoveDriver function unloads the kernel-mode driver and marks the kernel-mode driver service for deletion.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns one of the following values.
Value | Definition
DRVCNF_OK | Kernel-mode driver service has been marked for deletion. Driver was not loaded prior to call.
DRVCNF_RESTART | Kernel-mode driver service has been marked for deletion. Driver was loaded prior to call.
DRVCNF_CANCEL | Attempt to mark kernel-mode driver service for deletion failed.

See the Comments section below.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The DrvRemoveDriver function first checks to see if the kernel-mode driver is loaded and, if it is, calls DrvUnloadKernelDriver. Then it calls DrvDeleteServicesNode.

Typically, a user-mode driver calls DrvRemoveDriver from its DriverProc function, when processing a DRV_REMOVE command. The return values provided by DrvRemoveDriver match those specified for DRV_REMOVE.

The driver must call DrvCreateServicesNode before calling DrvRemoveDriver. After DrvRemoveDriver returns, the driver should call DrvCloseServiceManager.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvRestoreParametersKey

BOOL
DrvRestoreParametersKey(
PREG_ACCESS RegAccess
 );

The DrvRestoreParametersKey function restores registry information that was saved when the driver called DrvSaveParametersKey.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The DrvRestoreParametersKey function deletes the temporary file in which the key information was written by DrvSaveParametersKey, and removes the file name from the REG_ACCESS structure.

Drivers must call DrvCreateServicesNode before calling DrvRestoreParametersKey.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvSaveParametersKey

BOOL
DrvSaveParametersKey(
PREG_ACCESS RegAccess
 );

The DrvSaveParametersKey function saves the current contents of the driver’s \Parameters registry key, along with its subkeys, in a temporary file.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

If the DrvSaveParametersKey function succeeds, the REG_ACCESS structure contains the name of the temporary file.

To restore the saved registry information, call DrvRestoreParametersKey.

Drivers must call DrvCreateServicesNode before calling DrvSaveParametersKey.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvSetDeviceIdParameter

LONG
DrvSetDeviceIdParameter(
PREG_ACCESS RegAccess,
UINT DeviceNumber,
PTCHAR ValueName,
DWORD Value
 );

The DrvSetDeviceIdParameter function assigns the specified value to the specified value name in the registry, under the specified device’s \Parameters key.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
DeviceNumber
Device number. Used as an index to the device subkeys under the \Parameters key. See the Comments section below.
ValueName
Pointer to a string representing the value name.
Value
Value to be written.
Return Value

Returns ERROR_SUCCESS if the operation succeeds. Otherwise returns one of the error codes defined in winerror.h. See the Comments section below.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

Before a driver can call the DrvSetDeviceIdParameter function, it must call DrvCreateDeviceKey.

The value name and value are written to the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number, where Number represents the number supplied by the DeviceNumber parameter.

The specified value is stored as a REG_DWORD type.

The function calls RegSetValueEx, which is described in the Win32 SDK, and returns its return value.

To retrieve a registry value written with DrvSetDeviceIdParameter, call DrvQueryDeviceIdParameter.

Drivers must call DrvCreateServicesNode before calling DrvSetDeviceIdParameter.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvSetMapperName

VOID
DrvSetMapperName(
LPTSTR SetupName
 );

The DrvSetMapperName function specifies the name of the map that the MIDI Mapper should use.

Parameters
SetupName
Pointer to a string representing the name of the mapping to use.
Return Value

None.

Comments

The functions assigns the specified string to be the value of “Mapping Name”, under the registry key HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Midimap.

Drivers must call DrvCreateServicesNode before calling DrvSetMapperName.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


DrvUnloadKernelDriver

BOOL
DrvUnloadKernelDriver(
PREG_ACCESS RegAccess
 );

The DrvUnloadKernelDriver function unloads the kernel mode driver.

Parameters
RegAccess
Pointer to a globally-defined structure of type REG_ACCESS.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. To obtain an error code value, call GetLastError, which is described in the Win32 SDK.

Comments

The structure pointed to by RegAccess must be a single, globally-defined REG_ACCESS structure that the driver uses with all calls to drvlib.lib functions requiring a RegAccess parameter.

The function sets the kernel-mode driver service’s start type to SERVICE_DEMAND_START, so it will not automatically reload when the system is restarted. (For more information, ChangeServiceConfig in the Win32 SDK.)

Drivers must call DrvCreateServicesNode before calling DrvUnLoadKernelDriver.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


GetInterruptsAndDMA

BOOL
GetInterruptsAndDMA(
LPDWORD InterruptsInUse,
LPDWORD DmaChannelsInUse,
LPCTSTR IgnoreDriver
);

The GetInterruptsAndDMA function examines the registry to determine which interrupt numbers and DMA channels are assigned to devices.

Parameters
InterruptsInUse
Pointer to a DWORD. Receives a bit array of interrupts in use. If interrupt i is in use, then bit i is set.
DMAChannelsInUse
Pointer to a DWORD. Receives a bit array of DMA channels in use. If channel i is in use, then bit i is set.
IgnoreDriver
Pointer to the name of a kernel-mode driver. The interrupt numbers and DMA channels of the devices controlled by this driver are not included in the returned InterruptsInUse and DMAChannelsInUse bit arrays. Can be NULL.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

Typically, a user-mode driver calling this function specifies the name of its kernel-mode driver for the IgnoreDriver parameter.

For additional information, see Installing and Configuring your Driver, Using drvlib.lib.


Structures and Types, drvlib.lib

This section describes the structures and types available to user-mode audio drivers using drvlib.lib. The structures and types are defined in the file registry.h.


REG_ACCESS

typedef struct {
    SC_HANDLE     ServiceManagerHandle;
    LPTSTR        DriverName;
    TCHAR         TempKeySaveFileName[MAX_PATH];
} REG_ACCESS, *PREG_ACCESS;

The REG_ACCESS structure contains registry access information.

Members
ServiceManagerHandle
Contains a handle to the local service control manager.
DriverName
Pointer to a driver name. This is the DriverName argument to DrvCreateServicesNode.
TempKeySaveFileName
Name of a file created by DrvSaveParametersKey.
Comments

User-mode drivers using drvlib.lib functions to access the registry must declare a single global variable of this type for use with all function calls.

Drivers do not reference the structure contents.

The structure is initialized by DrvCreateServicesNode.


SOUND_KERNEL_MODE_DRIVER_TYPE

typedef enum {
    SoundDriverTypeNormal = 1,
    SoundDriverTypeSynth           /* Go in the synth group */
} SOUND_KERNEL_MODE_DRIVER_TYPE;

SOUND_KERNEL_MODE_DRIVER_TYPE is an enumeration type used to differentiate drivers that belong to the “base” load group from those that do not.

Specifying SoundDriverTypeNormal assigns a driver to the “base” group.

Specifying SoundDriverTypeSynth assigns a driver to the “Synthesizer Drivers” group. This group is unknown to Windows NT and therefore is guaranteed to be loaded last.

The type is used with input parameters to DrvCreateServicesNode and DrvConfigureDriver. (See CreateService in the Win32 SDK for information about load groups.)


Functions, soundlib.lib

This section describes the functions available to kernel-mode audio drivers using soundlib.lib. Function prototypes are defined in soundlib.h, devices.h, wave.h, midi.h, mixer.h, and synthdrv.h.


SoundAddIrpToCancellableQ

VOID
SoundAddIrpToCancellableQ(
PLIST_ENTRY QueueHead,
PIRP Irp,
BOOLEAN Head
);

The SoundAddIrpToCancellableQ function adds an IRP to a queue and makes the IRP cancelable.

Parameters
QueueHead
Pointer to the head of a queue of IRPs.
Irp
Pointer to an IRP.
Head
TRUE or FALSE. If TRUE, add IRP to head of queue. If FALSE, add IRP to tail of queue.
Return Value

None.

Comments

Before an IRP can be added to the queue, the queue must be initialized by calling InitializeListHead.

Generally, drivers using soundlib.lib do not need to manipulate IRP queues because DPCs in soundlib.lib handle IRP completion. For drivers that do manipulate IRP queues, the SoundAddIrpToCancellableQ, SoundRemoveFromCancellableQ, SoundMoveCancellableQueue, SoundFreePendingIrps, and SoundFreeQ functions are provided.


SoundAuxDispatch

NTSTATUS
SoundAuxDispatch(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpStack
);

The SoundAuxDispatch function is the IRP control code dispatcher for auxiliary audio devices.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
pIrp
Pointer to an IRP.
IrpStack
Pointer to an I/O stack location.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

Kernel-mode auxiliary audio device drivers using soundlib.lib place the address of the SoundAuxDispatch function in the DispatchRoutine member of a SOUND_DEVICE_INIT structure. The function is called by soundlib.lib’s main dispatcher, SoundDispatch.

The function processes the following IRP control codes:

IRP_MJ_CLEANUP

IRP_MJ_CLOSE

IRP_MJ_CREATE

IRP_MJ_DEVICE_CONTROL

For IRP_MJ_DEVICE_CONTROL, the SoundAuxDispatch function processes the following I/O control codes:

IOCTL_AUX_GET_CAPABILITIES

IOCTL_AUX_GET_VOLUME

IOCTL_AUX_SET_VOLUME

IOCTL_SOUND_GET_CHANGED_VOLUME

Refer to the Kernel-Mode Drivers Reference for descriptions of PIRP and PIO_STACK_LOCATION types, along with IRP control codes and I/O control codes.


SoundConnectInterrupt

NTSTATUS
SoundConnectInterrupt(
IN ULONG InterruptNumber,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PKSERVICE_ROUTINE Isr,
IN PVOID ServiceContext,
IN KINTERRUPT_MODE InterruptMode,
IN BOOLEAN ShareVector,
OUT PKINTERRUPT *Interrupt
);

The SoundConnectInterrupt function creates an interrupt object and installs an interrupt handler.

Parameters
InterruptNumber
Interrupt number.
BusType
Bus type. INTERFACE_TYPE is defined in ntddk.h.
BusNumber
Bus number, as returned from SoundGetBusNumber.
Isr
Pointer to an interrupt service routine (ISR). PKSERVICE_ROUTINE is defined in ntddk.h as follows:
typedef BOOLEAN
(*PKSERVICE_ROUTINE) (
    IN struct _KINTERRUPT *Interrupt,
    IN PVOID ServiceContext
    );

ServiceContext
Pointer to driver-specified information that is passed to the ISR.
InterruptMode
Interrupt mode (Latched or LevelSensitive). KINTERRUPT_MODE is defined in ntddk.h.
ShareVector
TRUE if interrupt is shareable, FALSE otherwise.
Interrupt
Pointer to one or more system-defined interrupt objects in nonpaged memory.
Return Value

Returns one of the following values.
Value | Definition
STATUS_SUCCESS | Operation succeeded.
STATUS_DEVICE_CONFIGURATION_ERROR | Improper input parameter.
STATUS_INSUFFICIENT_RESOURCES | Insufficient system resources.

Comments

Interrupt objects are of type _KINTERRUPT. These are a system-defined type that your driver doesn’t reference directly. Save the interrupt object pointer received in Interrupt, because it must be passed as input to KeSynchronizeExecution.

The ISR is responsible for clearing device interrupts. The HAL handles controller interrupts. After interrupts are cleared, the ISR should call IoRequestDPC to queue a deferred procedure call to the DPC function pointed to by the DeferredRoutine member of the SOUND_DEVICE_INIT structure. The DPC function should finish processing the interrupt.

Prior to being unloaded, the driver must release the interrupt objects by calling IoDisconnectInterrupt.

For discussions of interrupt objects and deferred procedure calls, see the Kernel-Mode Drivers Design Guide.


SoundCreateDevice

NTSTATUS
SoundCreateDevice(
IN PCSOUND_DEVICE_INIT DeviceInit,
IN UCHAR CreationFlags,
IN PDRIVER_OBJECT pDriverObject,
IN PVOID pGDI,
IN PVOID DeviceSpecificData,
IN PVOID pHw,
IN int i,
OUT PDEVICE_OBJECT *ppDevObj
);

The SoundCreateDevice function creates a device object and an associated LOCAL_DEVICE_INFO structure.

Parameters
DeviceInit
Pointer to an initialized SOUND_DEVICE_INIT structure.
CreationFlags
The following flag values are supported:

Flag

Definition

SOUND_CREATION_NO_NAME_RANGE

Don’t append number to name prototype when creating device name.

SOUND_CREATION_NO_VOLUME

Volume setting not supported.

pDriverObject
Pointer to the driver object received as input to the DriverEntry function.
pGDI
Pointer to driver-specified information. Pointer is stored in the pGlobalInfo member of the device’s LOCAL_DEVICE_INFO structure.
DeviceSpecificData
Pointer to one of the following device-type structures.

Structure

Usage

WAVE_INFO

For waveform devices

MIDI_INFO

For MIDI devices

MIXER_INFO

For mixer devices

NULL

For other devices

pHw
Pointer to driver-specified hardware context information. The pointer is stored in the HwContext member of the device’s LOCAL_DEVICE_INFO structure.
i
Driver-specified index value. Stored in DeviceIndex member of the device’s LOCAL_DEVICE_INFO structure.
ppDevObj
Address of a location to receive a pointer to a DEVICE_OBJECT structure, if the call succeeds.
Return Value

Returns STATUS_SUCCESS if device creation succeeds. Returns STATUS_INSUFFICIENT_RESOURCES if device creation fails.

Comments

The SOUND_DEVICE_INIT structure must be nonpaged.

The SoundCreateDevice function creates a device object by calling IoCreateDevice. If the object creation succeeds, the function returns the address of a DEVICE_OBJECT structure. The function also allocates a LOCAL_DEVICE_INFO structure and assigns its address to the DeviceExtension member of the DEVICE_OBJECT structure.

The function calls IoCreateSymbolicLink to map the NT generic name for the device object to the corresponding Win32 logical name.

For more information about device objects, see the Kernel-Mode Drivers Design Guide.


SoundCreateDeviceName

NTSTATUS
SoundCreateDeviceName(
PCWSTR PrePrefix,
PCWSTR Prefix,
UCHAR Index,
PUNICODE_STRING DeviceName
);

The SoundCreateDeviceName function creates a device name from the specified component parts.

Parameters
PrePrefix
Pointer to a string to prepend to the string pointed to by Prefix. Typically, this string is “\Device\” or “\DosDevices\”.
Prefix
Pointer to a string containing the main part of the name. Typically, this is the PrototypeName member of a SOUND_DEVICE_INIT structure.
Index
Number to append to the device name. Specify 0xFF if an index number should not be appended.
DeviceName
Pointer to a UNICODE_STRING structure, which receives the constructed name string.
Return Value

Returns one of the following values.
Value | Definition
STATUS_SUCCESS | Operation succeeded.
STATUS_INSUFFICIENT_RESOURCES | Could not allocate string buffer.


SoundDelay

VOID
SoundDelay(
IN ULONG Milliseconds
);

The SoundDelay function delays execution of the calling thread for at least the specified number of milliseconds.

Parameters
Milliseconds
Number of milliseconds to delay.
Return Value

None.

Comments

If the IRQL is greater than or equal to DISPATCH_LEVEL, the function returns without waiting.


SoundDispatch

NTSTATUS
SoundDispatch(
IN PDEVICE_OBJECT pDO,
IN PIRP pIrp
);

The SoundDispatch function is the main dispatcher for IRP function codes within soundlib.lib

Parameters
pDO
Pointer to a device object.
pIrp
Pointer to an IRP.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

For more information about device objects and IRPs, see the Kernel-Mode Drivers Design Guide.

Within the DEVICE_OBJECT structure passed to a kernel-mode driver’s DriverEntry function, SoundDispatch must be specified as the dispatcher for the following IRP control codes:

  • IRP_MJ_CLEANUP

  • IRP_MJ_CLOSE

  • IRP_MJ_CREATE

  • IRP_MJ_DEVICE_CONTROL

  • IRP_MJ_READ

  • IRP_MJ_WRITE

SoundDispatch does not process the IRPs. Instead, it calls the secondary dispatcher specified as the DispatchRoutine member of the appropriate SOUND_DEVICE_INIT structure. (A pointer to the SOUND_DEVICE_INIT structure is contained in the device object.) Secondary dispatchers provided by soundlib.lib are:

  • SoundAuxDispatch

  • SoundMidiDispatch

  • SoundMixerDispatch

  • SoundWaveDispatch


SoundEnter

VOID
SoundEnter(
PLOCAL_DEVICE_INFO pLDI,
BOOLEAN Enter
);

The SoundEnter function calls a device’s exclusion routine, passing it either a SoundExcludeEnter or a SoundExcludeLeave message.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
Enter
TRUE or FALSE. If TRUE, the exclusion routine is called with a SoundExcludeEnter message. If FALSE, the exclusion routine is called with a SoundExcludeLeave message.
Return Value

None.

Comments

The SoundEnter function calls the exclusion routine that is specified as the ExclusionRoutine member of a SOUND_DEVICE_INIT structure. The SOUND_DEVICE-INIT structure’s address is contained in the pLDI structure. See “Using Exclusion Routines”.

For definitions of the SoundExcludeEnter and SoundExcludeLeave messages, see SOUND_EXCLUDE_CODE.


SoundEnumSubkeys

NTSTATUS
SoundEnumSubkeys(
IN PUNICODE_STRING RegistryPathName,
IN PWSTR Subkey,
IN PSOUND_REGISTRY_CALLBACK_ROUTINE Callback,
IN PVOID Context
);

The SoundEnumSubkeys function enumerates the subkeys under the specified subkey, which is in the specified registry path. For each subkey found under the specified subkey, SoundEnumSubkeys calls the specified callback function.

Parameters
RegistryPathName
Pointer to a string representing the registry path to the driver subkey. Use the path name received as the RegistryPathName argument to DriverEntry.
SubKey
Pointer to a string representing the subkey at end of the path specified by RegistryPathName. SoundEnumSubkeys enumerates subkeys under this subkey.
Callback
Pointer to a function to call for each subkey found under Subkey. The function type is SOUND_REGISTRY_CALLBACK_ROUTINE.
Context
Pointer to a driver-specified context parameter. This parameter is passed to the function pointed to by Callback.
Return Value

If the function detects a failure, it returns an NTSTATUS error code. Otherwise it returns the value returned by the callback function. If the callback function succeeds, it should return STATUS_SUCCESS. Otherwise it should return an NTSTATUS error code.

Comments

Use the SoundEnumSubkeys function when performing device initialization operations, if your driver supports multiple hardware devices. If you specify “Parameters” for the Subkey parameter, the function calls the callback function for each “device” subkey under \Parameters. For an example, see Examining DriverEntry in sndblst.sys. (The device entries under the \Parameters subkey are created by user-mode drivers, typically by calling DrvCreateDeviceKey.)

SoundEnumSubkeys passes a registry path name to the callback function (see SOUND_REGISTRY_CALLBACK_ROUTINE).This path name is the path name received as the RegistryPathName argument, with \Parameters\Device Number appended.

The callback function returns an NTSTATUS value. If, for any call to the callback function, the return value is not STATUS_SUCCESS, then SoundEnumSubkeys immediately returns to its caller, passing back the return value received from the callback function.

Drivers which do not support multiple hardware devices do not call SoundEnumSubkeys. Instead, they should call SoundSaveRegistryPath as part of their initialization operation, in order to create a registry path name that can be used as input to other soundlib.lib functions.


SoundFreeCommonBuffer

VOID
SoundFreeCommonBuffer(
IN OUT PSOUND_DMA_BUFFER SoundAutoData
 );

The SoundFreeCommonBuffer function frees a DMA buffer that was previously allocated by SoundGetCommonBuffer.

Parameters
SoundAutoData
Pointer to a SOUND_DMA_BUFFER structure.
Return Value

None.

Comments

The SoundFreeCommonBuffer function also frees the MDL associated with the buffer. (For information about MDLs, see the Kernel-Mode Drivers Design Guide.)

Drivers calling SoundFreeCommonBuffer must include wave.h.


SoundFreeDevice

VOID
SoundFreeDevice(
IN PDEVICE_OBJECT DeviceObject
);

The SoundFreeDevice function deletes a device object and frees its resources.

Parameters
DeviceObject
Pointer to a device object.
Return Value

None.

Comments

SoundFreeDevice frees resources that were reserved with SoundReportResourceUsage. It also frees resources associated with the driver object. The specified device object is deleted.


SoundFreePendingIrps

VOID
SoundFreePendingIrps(
PLIST_ENTRY QueueHead,
PFILE_OBJECT FileObject
);

The SoundFreePendingIrps function traverses a queue of IRPs. For each IRP associated with the specified file object, the function completes the interrupt request and removes the IRP from the queue.

Parameters
QueueHead
Pointer to the head of a queue of IRPs.
FileObject
Pointer to a file object.
Return Value

None.

Comments

The function completes the interrupt request by calling IoCompleteRequest, with a priority boost argument of IO_NO_INCREMENT.

SoundFreePendingIrps only works for IRP queues created by calling SoundAddIrpToCancellableQ. It makes each IRP noncancelable before calling IoCompleteRequest.

Generally, drivers using soundlib.lib do not need to manipulate IRP queues because DPCs in soundlib.lib handle IRP completion. For drivers that do manipulate IRP queues, the SoundAddIrpToCancellableQ, SoundRemoveFromCancellableQ, SoundMoveCancellableQueue, SoundFreePendingIrps, and SoundFreeQ functions are provided.


SoundFreeQ

VOID
SoundFreeQ(
PLIST_ENTRY ListHead,
NTSTATUS IoStatus
);

The SoundFreeQ function traverses a queue of IRPs. For each IRP, the function completes the interrupt request and removes the IRP from the queue.

Parameters
ListHead
Pointer to the head of a queue of IRPs.
IoStatus
An NTSTATUS code to use as an IRP completion status. This value, which is placed in each IRP, is generally either STATUS_SUCCESS or STATUS_CANCELLED.
Return Value

None.

Comments

The function completes the interrupt request by calling IoCompleteRequest, with a priority boost argument of IO_SOUND_INCREMENT.

SoundFreeQ only works for IRP queues created by calling SoundAddIrpToCancellableQ. It dequeues entries by calling SoundRemoveFromCancellableQ.

Generally, drivers using soundlib.lib do not need to manipulate IRP queues because DPCs in soundlib.lib handle IRP completion. For drivers that do manipulate IRP queues, the SoundAddIrpToCancellableQ, SoundRemoveFromCancellableQ, SoundMoveCancellableQueue, SoundFreePendingIrps, and SoundFreeQ functions are provided.


SoundGetBusNumber

NTSTATUS
SoundGetBusNumber(
IN OUT INTERFACE_TYPE InterfaceType,
OUT PULONG BusNumber
);

The SoundGetBusNumber function returns the number of the first bus of the specified bus type.

Parameters
InterfaceType
Type of bus for which to search. INTERFACE_TYPE is defined in ntddk.h.
BusNumber
Address of location to receive the bus number, if found.
Return Value

Returns STATUS_SUCCESS if successful. Otherwise returns STATUS_DEVICE_DOES_NOT_EXIST.


SoundGetCommonBuffer

NTSTATUS
SoundGetCommonBuffer(
IN PDEVICE_DESCRIPTION DeviceDescription,
IN OUT PSOUND_DMA_BUFFER SoundAutoData
);

The SoundGetCommonBuffer function allocates a buffer for use by waveform devices during auto-initialize DMA transfers, and maps the buffer so it is accessible to both the processor and the device.

Parameters
DeviceDescription
Pointer to an initialized DEVICE_DESCRIPTION structure. (For a description of this structure, see HalGetAdapter.)
SoundAutoData
Pointer to a SOUND_DMA_BUFFER structure. Generally this is the address of the DMABuf member of a WAVE_INFO structure.
Return Value

Returns one of the following values.
Value | Definition
STATUS_SUCCESS | Operation succeeded.
STATUS_DEVICE_CONFIGURATION_ERROR | Couldn’t find adapter.
STATUS_INSUFFICIENT_RESOURCES | Not enough memory to allocate buffer.

Comments

The returned buffer is described by the contents of the SOUND_DMA_BUFFER structure.

SoundGetCommonBuffer calls HalGetAdapter to obtain an adapter object and HalAllocateCommonBuffer to get a buffer. If a buffer of the requested size is not available, a smaller one is returned. The smallest buffer that can be requested is 4 kilobytes.

After the buffer has been allocated, the function calls IoAllocateMdl and MmBuildMdlForNonPagedPool to build a memory descriptor list (MDL). A MDL is needed by soundlib.lib for calls to IoMapTransfer.

If the requested buffer size is large, soundlib.lib might not use the entire buffer. To find out how much of the buffer is actually used, call SoundGetDMABufferSize.

Prior to being unloaded, the driver must free the allocated buffer space by calling SoundFreeCommonBuffer.

Drivers calling SoundGetCommonBuffer must include wave.h.


SoundGetDMABufferSize

ULONG
SoundGetDMABufferSize(
IN PWAVE_INFO WaveInfo
);

The SoundGetDMABufferSize function returns the actual DMA buffer size used for DMA transfers by the specified waveform device.

Parameters
WaveInfo
Pointer to a WAVE_INFO structure.
Return Value

Buffer size in bytes.

Comments

This function returns the number of bytes that are actually used within the DMA buffer specified by the WAVE_INFO structure’s DMABuf member. If the buffer is large enough, soundlib.lib tries to set a buffer size corresponding to 1/8 of a second of play or record time.

Generally, DMA buffers for waveform devices are allocated by calling SoundGetCommonBuffer.

Drivers calling SoundGetDMABufferSize must include wave.h.


SoundGetTime

LARGE_INTEGER
SoundGetTime(
VOID
);

The SoundGetTime function returns a time value in 100-nanosecond units.

Parameters

None.

Return Value

Returns a time value in 100-nanosecond units.

Comments

This function calls KeQueryPerformanceCounter. Refer to the Kernel-Mode Drivers Reference for a discussion of KeQueryPerformanceCounter and restrictions on its use.


SoundInitDataItem

VOID
SoundInitDataItem(
PMIXER_INFO MixerInfo,
PMIXER_DATA_ITEM MixerDataItem,
USHORT Message,
USHORT Id
);

The SoundInitDataItem function adds a mixer data item to an internal list. Mixer data items represent mixer lines and controls. When items on the list change state, clients are notified of the change.

Parameters
MixerInfo
Pointer to a MIXER_INFO structure.
MixerDataItem
Pointer to an empty, globally-allocated MIXER_DATA_ITEM structure.
Message
Message to be sent to the client when an item changes. One of the following messages will be sent:
  • MM_MIXM_LINE_CHANGE, for mixer line items.

  • MM_MIXM_CONTROL_CHANGE, for mixer control items.
Id
Driver-defined ID value.
Return Value

None.

Comments

The pointer specified by MixerDataItem is added to the ChangedItems list for the specified MIXER_INFO structure. The driver calls SoundMixerChangedItem when an item on the ChangedItems list changes state.

Drivers calling SoundInitDataItem must include mixer.h.


SoundInitializeWaveInfo

VOID
SoundInitializeWaveInfo(
PWAVE_INFO WaveInfo,
UCHAR DMAType,
PSOUND_QUERY_FORMAT_ROUTINE QueryFormat,
PVOID HwContext
);

The SoundInitializeWaveInfo initializes a WAVE_INFO structure.

Parameters
WaveInfo
Pointer to a WAVE_INFO structure.
DMAType
Type of DMA to use. One of the following enumerated values:
enum {
    SoundNoDMA,
    SoundAutoInitDMA,             // Use auto-initialize
    SoundReprogramOnInterruptDMA, // Reprogram on interrupt
    Sound2ChannelDMA              // Keep 2 channels going
};

Sound2ChannelDMA is not currently supported in soundlib.lib.

QueryFormat
Pointer to a function of type SOUND_QUERY_FORMAT_ROUTINE.
HwContext
Pointer to a driver-defined structure containing hardware context information. Pointer is stored in the HwContext member of WAVE_INFO.
Return Value

None.

Comments

Before calling SoundInitializeWaveInfo, the driver must initialize the HwSetupDMA, HwStopDMA, and HwSetWaveFormat structure members and zero the rest of the structure.

Drivers calling SoundInitializeWaveInfo must include wave.h.


SoundInitMidiIn

VOID
SoundInitMidiIn(
IN OUT PMIDI_INFO pMidi,
IN PVOID HwContext
);

The SoundInitMidiIn function initializes a MIDI_INFO structure.

Parameters
pMidi
Pointer to a MIDI_INFO structure.
HwContext
Pointer to a driver-defined structure containing hardware context information. The pointer is stored in the HwContext member of MIDI_INFO.
Return Value

None.

Comments

Before calling SoundInitMidiIn, the driver must initialize the HwStartMidiIn, HwStopMidiIn, HwMidiRead, and HwMidiOut structure members and zero the rest of the structure.

Drivers calling SoundInitMidiIn must include midi.h.


SoundInitMixerInfo

VOID
SoundInitMixerInfo(
IN OUT PMIXER_INFO MixerInfo,
PMIXER_DD_GET_SET_DATA HwGetLineData,
PMIXER_DD_GET_SET_DATA HwGetControlData,
PMIXER_DD_GET_SET_DATA HwGetCombinedControlData,
PMIXER_DD_GET_SET_DATA HwGetSetControlData
);

The SoundInitMixerInfo function initializes a MIXER_INFO structure.

Parameters
MixerInfo
Pointer to a MIXER_INFO structure.
HwGetLineData
Pointer to a function of type PMIXER_DD_GET_SET_DATA. The pointer is stored in the HwGetLineData member of MIXER_INFO.
HwGetControlData
Pointer to a function of type PMIXER_DD_GET_SET_DATA. The pointer is stored in the HwGetControlData member of MIXER_INFO.
HwGetCombinedControlData
Pointer to a function of type PMIXER_DD_GET_SET_DATA. The pointer is stored in the HwGetCombinedControlData member of MIXER_INFO.
HwGetSetControlData
Pointer to a function of type PMIXER_DD_GET_SET_DATA. The pointer is stored in the HwGetSetControlData member of MIXER_INFO.
Return Value

None.

Comments

The function zeros the structure before initializing it.

Drivers calling SoundInitMixerInfo must include mixer.h.


SoundMapPortAddress

PUCHAR
SoundMapPortAddress(
INTERFACE_TYPE BusType,
ULONG BusNumber,
ULONG PortBase,
ULONG Length,
PULONG MemType
);

The SoundMapPortAddress function translates a bus-specific address into the corresponding system-logical address and then, if the logical address is in memory address space, maps the address to a virtual address.

Parameters
BusType
Bus type. INTERFACE_TYPE is defined in ntddk.h.
BusNumber
Bus number, as returned from SoundGetBusNumber.
PortBase
Port number.
Length
Number of ports.
MemType
Address of a location to receive a value indicating the memory type. A value of zero indicates the return value is a memory address. A value of one indicates the return value is an I/O space address.
Return Value

Returns the base virtual address.

Comments

Use SoundMapPortAddress to obtain addresses that are suitable for use with HAL Port I/O routines, such as READ_PORT_UCHAR.

If a value of zero is received in the address pointed to by memtype, then the returned virtual address is a mapped address. In this case the driver must call MmUnmapIoSpace to unmap the address range, prior to being unloaded.

This function calls HalTranslateBusAddress and MmMapIoSpace.


SoundMidiDispatch

NTSTATUS
SoundMidiDispatch(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpStack
);

The SoundMidiDispatch function is the IRP control code dispatcher for MIDI devices.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
pIrp
Pointer to an IRP.
IrpStack
Pointer to an I/O stack location.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

Kernel-mode MIDI device drivers using soundlib.lib place the address of this function in the DispatchRoutine member of a SOUND_DEVICE_INIT structure. The function is called by soundlib.lib’s main dispatcher, SoundDispatch.

The function processes the following IRP control codes:

IRP_MJ_CLEANUP

IRP_MJ_CLOSE

IRP_MJ_CREATE

IRP_MJ_DEVICE_CONTROL

IRP_MJ_READ

For IRP_MJ_DEVICE_CONTROL, the function processes the following I/O control codes:

IOCTL_MIDI_GET_CAPABILITIES

IOCTL_MIDI_GET_STATE

IOCTL_MIDI_PLAY

IOCTL_MIDI_SET_STATE

See the Kernel-Mode Drivers Design Guide for descriptions of PIRP and PIO_STACK_LOCATION types, along with IRP control codes and I/O control codes.


SoundMidiInDeferred

VOID
SoundMidiInDeferred(
IN PKDPC pDpc,
IN PDEVICE_OBJECT pDeviceObject,
IN OUT PIRP pIrpDeferred,
IN OUT PVOID Context
);

The SoundMidiInDeferred function is the DPC function that is provided by soundlib.lib for MIDI input devices.

Parameters
pDpc
Pointer to a DPC object.
pDeviceObject
Pointer to a device object.
pIrpDeferred
Pointer to an IRP. (Not used by SoundMidiInDeferred. See Comments section below.)
Context
Pointer to context information. (Not used by SoundMidiInDeferred. See Comments section below.)
Return Value

None.

Comments

Drivers using soundlib.lib should specify this function’s address as the DeferredRoutine member of each MIDI input device’s SOUND_DEVICE_INIT structure. SoundCreateDevice passes the address to IoInitializeDpcRequest.

A driver causes SoundMidiInDeferred to be called by calling IoRequestDPC from its ISR. The driver should specify NULL for the Irp and Context parameters to IoRequestDPC, because SoundMidiInDeferred does not use them.


SoundMixerChangedItem

VOID
SoundMixerChangedItem(
IN OUT PMIXER_INFO MixerInfo,
IN OUT PMIXER_DATA_ITEM MixerItem
);

The SoundMixerChangedItem function is called when an item on a mixer’s ChangedItems list changes state.

Parameters
MixerInfo
Pointer to a mixer’s MIXER_INFO structure.
MixerItem
Pointer to a MIXER_DATA_ITEM structure.
Return Value

None.

Comments

The SoundMixerChangedItem function updates the LastSet member of the MIXER_DATA_ITEM structure and moves the structure pointer to the head of the ChangedItems list in MIXER_INFO. It then traverses the queue pointed to by NotifyQueue in MIXER_INFO, calling IoCompleteRequest for each IRP in the queue and thereby delivering notification messages to each requesting client.

Drivers calling SoundMixerChangedItem must include mixer.h.


SoundMixerDispatch

NTSTATUS
SoundMixerDispatch(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpStack
);

The SoundMixerDispatch function is the IRP control code dispatcher for mixer devices.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
pIrp
Pointer to an IRP structure.
IrpStack
Pointer to an IO_STACK_LOCATION structure.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

Kernel-mode mixer device drivers using soundlib.lib place the address of this function in the DispatchRoutine member of a SOUND_DEVICE_INIT structure. The function is called by soundlib.lib’s main dispatcher, SoundDispatch.

The SoundMixerDispatch function processes the following IRP control codes:

IRP_MJ_CLEANUP

IRP_MJ_CLOSE

IRP_MJ_CREATE

IRP_MJ_DEVICE_CONTROL

IRP_MJ_WRITE

For IRP_MJ_DEVICE_CONTROL, the function processes the following I/O control codes:

IOCTL_MIX_GET_CONFIGURATION

IOCTL_MIX_GET_CONTROL_DATA

IOCTL_MIX_GET_LINE_DATA

IOCTL_MIX_REQUEST_NOTIFY


SoundMoveCancellableQueue

VOID
SoundMoveCancellableQueue(
IN OUT PLIST_ENTRY From,
IN OUT PLIST_ENTRY To
);

The SoundMoveCancellableQueue function moves a queue of cancelable IRPs to another queue.

Parameters
From
Pointer to a queue of IRPs to be moved.
To
Pointer to an empty queue into which IRPs are to be moved.
Return Value

None.

Comments

The SoundMoveCancellableQueue function initializes the To queue by calling InitializeListHead.

Generally, drivers using soundlib.lib do not need to manipulate IRP queues because DPCs in soundlib.lib handle IRP completion. For drivers that do manipulate IRP queues, the SoundAddIrpToCancellableQ, SoundRemoveFromCancellableQ, SoundMoveCancellableQueue, SoundFreePendingIrps, and SoundFreeQ functions are provided.


SoundNoVolume

VOID
SoundNoVolume(
PLOCAL_DEVICE_INFO pLDI
 );

The SoundNoVolume function should be used as the HwSetVolume member of the SOUND_DEVICE_INIT structure associated with a device that either has no volume setting hardware or whose volume is controlled by a mixer device.

Parameters
pLDI
Pointer to the device’s LOCAL_DEVICE_INFO structure.
Return Value

None.

Comments

The SoundNoVolume function contains only a return statement.


SoundOpenDevicesKey

NTSTATUS
SoundOpenDevicesKey(
IN PWSTR RegistryPathName,
OUT PHANDLE DevicesKey
);

The SoundOpenDevicesKey function opens the Devices subkey under the specified registry path. If the Devices subkey does not exist, it is created.

Parameters
RegistryPathName
Pointer to a string representing the registry path to the device subkey. Use the path name created by either SoundEnumSubkeys or SoundSaveRegistryPath.
DevicesKey
Address of a location to receive a handle to the Devices subkey.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

A driver that calls SoundSaveDeviceName to save a device name in the registry must remove the created \Devices key, along with all device name subkeys, prior to being unloaded. To remove the \Devices key, a driver should:

  • Call SoundOpenDevicesKey to obtain a handle to the \Devices key.

  • Call ZwDeleteKey to delete the key.

  • Call ZwClose to close the key handle.


SoundPeakMeter

BOOLEAN
SoundPeakMeter(
IN PWAVE_INFO WaveInfo,
OUT PLONG Amplitudes
);

The SoundPeakMeter function returns the peak amplitude value currently contained in the DMA buffer for the specified waveform device.

Parameters
WaveInfo
Pointer to a WAVE_INFO structure.
Amplitudes
Pointer to a longword. Amplitude values are returned in the longword. The lower word contains the left channel value, and the upper word contains the right channel value. Returned values are between 0x0 and 0xFFFF.
Return Value

Always returns TRUE.

Comments

The SoundPeakMeter function is typically called from a mixer driver’s HwGetControlData function. See MIXER_INFO.

Drivers calling SoundPeakMeter must include wave.h.


SoundRemoveFromCancellableQ

PIRP
SoundRemoveFromCancellableQ(
PLIST_ENTRY QueueHead
);

The SoundRemoveFromCancellableQ function removes an IRP from the head of the specified IRP queue, makes the IRP noncancelable, and returns its address.

Parameters
QueueHead
Pointer to the head of a queue of IRPs.
Return Value

Address of an IRP structure containing the removed IRP.

Comments

Generally, drivers using soundlib.lib do not need to manipulate IRP queues because DPCs in soundlib.lib handle IRP completion. For drivers that do manipulate IRP queues, the SoundAddIrpToCancellableQ, SoundRemoveFromCancellableQ, SoundMoveCancellableQueue, SoundFreePendingIrps, and SoundFreeQ functions are provided.


SoundReportResourceUsage

NTSTATUS
SoundReportResourceUsage(
IN PDEVICE_OBJECT DeviceObject,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PULONG InterruptNumber OPTIONAL,
IN KINTERRUPT_MODE InterruptMode,
IN BOOLEAN InterruptShareDisposition,
IN PULONG DmaChannel OPTIONAL,
IN PULONG FirstIoPort OPTIONAL,
IN ULONG IoPortLength
);

The SoundReportResourceUsage function reserves hardware resources for use by the specified device or driver. This function does not allow reservation of resources already reserved for another device or driver.

Parameters
DeviceObject
Pointer to either a DEVICE_OBJECT or DRIVER_OBJECT structure. Represents the device or driver for which resources will be reserved.
BusType
Type of bus the device is on. INTERFACE_TYPE is defined in ntddk.h.
BusNumber
Number of the bus the device is on.
InterruptNumber
Pointer to the interrupt number, or NULL if no interrupt.
InterruptMode
Interrupt mode (Latched or LevelSensitive). Ignored if InterruptNumber is NULL. KINTERRUPT_MODE is defined in ntddk.h.
InterruptShareDisposition
TRUE if interrupt can be shared, FALSE otherwise. Ignored if InterruptNumber is NULL.
DmaChannel
Pointer to the device’s DMA channel, or NULL if there is no DMA channel.
FirstIoPort
Pointer to the device’s first I/O port address, or NULL if there are no I/O ports.
IoPortLength
Number of bytes of I/O space the device uses, starting at FirstIoPort. Ignored if FirstIoPort is NULL.
Return Value

Returns one of the following values.
Value | Definition
STATUS_SUCCESS | Success.
STATUS_DEVICE_CONFIGURATION_ERROR | Resources already assigned.
STATUS_INSUFFICIENT_RESOURCES | Resources unavailable.

Comments

Before attempting to access device hardware, call SoundReportResourceUsage to ensure the resources you intend to use are not already assigned to another device.

You can associate resources with either the driver object or with one of the device objects created by SoundCreateDevice. If the driver supports multiple cards, you must associate resources with device objects.

Each time you call SoundReportResourceUsage for a particular device object or driver object, you override the resource reservation made with any previous call for the same object.

Typically, a driver acquires resources for each device in turn, and then calls SoundReportResourceUsage a final time to declare (to the rest of the system) all the resources used by the card instance.

To free reserved resources, call SoundFreeDevice.


SoundSaveDeviceName

NTSTATUS
SoundSaveDeviceName(
IN PWSTR RegistryPathName,
IN PLOCAL_DEVICE_INFO pLDI
);

The SoundSaveDeviceName function writes a device’s name and type into the registry.

Parameters
RegistryPathName
Pointer to a string representing the registry path to the device subkey. Use the path name created by either SoundEnumSubkeys or SoundSaveRegistryPath.
pLDI
Pointer to the device’s LOCAL_DEVICE_INFO structure.
Return Value

Returns one of the following values.
Value | Definition
STATUS_SUCCESS | Success.
STATUS_INSUFFICIENT_RESOURCES | Couldn’t create a device name.

Comments

Under the specified registry path, the SoundSaveDeviceName function locates the \Devices subkey. Under this subkey, the function uses the device’s name as a value name and uses the device’s type as the value to associate with the name. The function creates a device name from the PrototypeName member of the device’s SOUND_DEVICE_INIT structure. It obtains the device type from the DeviceType member of the passed LOCAL_DEVICE_INFO structure.

After the function is called, the registry should contain the entry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number\Devices\DeviceName, where the value of DeviceName is the device type.

Drivers that call SoundSaveDeviceName must remove the saved device name from the registry prior to being unloaded. Refer to SoundOpenDevicesKey.


SoundSaveDeviceVolume

VOID
SoundSaveDeviceVolume(
PLOCAL_DEVICE_INFO pLDI,
PWSTR KeyName
);

The SoundSaveDeviceVolume function writes both left and right channel volume settings into the registry.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
KeyName
The name of the registry subkey under which the volume setting is to be stored. The values are retrieved from the LeftVolumeName and RightVolumeName members of the structure pointed to by the DeviceInit member of pLDI.
Return Value

None.

Comments

Left and right volume values are obtained from the volume member of the LOCAL_DEVICE_INFO structure. The names associated with these volumes are obtained from the SOUND_DEVICE_INIT structure pointed to by the LOCAL_DEVICE_INFO structure.

Call the SoundSaveDeviceVolume function when the system shuts down or when the driver is unloaded. Do not use this function if the volume is controlled by a mixer.


SoundSaveRegistryPath

NTSTATUS
SoundSaveRegistryPath(
IN PUNICODE_STRING RegistryPathName,
OUT PWSTR *SavedString
);

The SoundSaveRegistryPath function stores the specified registry path name, appends the \Parameters subkey to the name string, and returns a pointer to the stored string.

Parameters
RegistryPathName
Pointer to a registry path name. Typically, this is the path name received by the driver’s DriverEntry function.
SavedString
Address of a location to receive a pointer to the new string.
Comments

Drivers use the returned path name when calling soundlib.lib functions that require a registry path name as input.

Prior to being unloaded, the driver must deallocate the buffer pointed to by SavedString.

The SoundSaveRegistryPath function is typically used by drivers that support only a single hardware device. Drivers supporting multiple devices call SoundEnumSubkeys, which appends \Parameters\Device Num to the path name.


SoundSetErrorCode

NTSTATUS
SoundSetErrorCode(
IN PWSTR RegistryPath,
IN ULONG Value
);

The SoundSetErrorCode function assigns a value to the “Configuration Error” value name in the specified registry path.

Parameters
RegistryPath
Pointer to a full path to a registry key.
Value
Value to set. The value is stored in the registry as a REG_DWORD type.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.


SoundSetLineNotify

VOID
SoundSetLineNotify(
PLOCAL_DEVICE_INFO pLDI,
PSOUND_LINE_NOTIFY LineNotify
);

The SoundSetLineNotify function specifies a function to be called when the status of the specified device changes.

Parameters
pLDI
Pointer to the LOCAL_DEVICE_INFO structure of the device with which the notification function is to be associated.
LineNotify
Pointer to a notification function. Type is PSOUND_LINE_NOTIFY.
Return Value

None.

Comments

The SoundSetLineNotify function is called by mixer drivers, so they can receive notification of changes to devices associated with mixer lines.

A separate notification function can be registered for each device created with SoundCreateDevice, but code in soundlib.lib calls notification functions only for waveform devices and MIDI synthesizers.

If a driver has registered a notification function by calling SoundSetLineNotify, then soundlib.lib calls the function whenever the status of the device changes (that is, the device starts or stops).

Typically, the driver uses the notification function to send commands to mixer hardware, if necessary, and to call SoundMixerChangedItem, which queues the change and notifies clients.

Drivers calling SoundSetLineNotify must include mixer.h.


SoundSetShareAccess

NTSTATUS
SoundSetShareAccess(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN PIO_STACK_LOCATION IrpStack
);

The SoundSetShareAccess function sets a specified device access by calling IoSetShareAccess.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
IrpStack
Pointer to an IO_STACK_LOCATION structure that contains the desired access.
Return Value

Returns STATUS_SUCCESS if the requested access is granted. Otherwise returns STATUS_ACCESS_DENIED or STATUS_DEVICE_BUSY.

Comments

This function can only be called when processing an IRP_MJ_CREATE message, because it references message-specific contents of IrpStack. Drivers using the dispatch routines supplied by soundlib.lib do not need to call this function.


SoundSetVolumeControlId

VOID
SoundSetVolumeControlId(
PLOCAL_DEVICE_INFO pLDI,
UCHAR VolumeControlId
);

The SoundSetVolumeControlId function assigns the specified volume control ID to the specified device.

Parameters
pLDI
Pointer to the LOCAL_DEVICE_INFO structure of the device to which the volume control ID is to be assigned.
VolumeControlId
A driver-defined volume control ID value.
Return Value

None.

Comments

The SoundSetVolumeControlId function stores the specified value in the VolumeControlID member of the LOCAL_DEVICE_INFO structure. The value is driver-defined and enables the driver to associate a device’s volume settings with mixer settings. The volume ID is passed as input to functions pointed to by the HwGetControlData, HwGetCombinedControlData, and HwSetControlData members of MIXER_INFO.


SoundTestWaveDevice

int
SoundTestWaveDevice(
IN PDEVICE_OBJECT pDO
);

The SoundTestWaveDevice function initiates a short DMA transfer to determine if the specified waveform device’s interrupt number and DMA channel number are set correctly.

Parameters
pDO
Pointer to the waveform device’s device object. To obtain a device object, call SoundCreateDevice.
Return Value

Returns one of the following values.
Value | Definition
0 | Operation succeeded.
1 | Interrupt number is invalid.
2 | DMA channel number is invalid.

Comments

Use the SoundTestWaveDevice function only if no other means for establishing interrupt and DMA channel numbers are available.

This function only works for devices that perform auto-initialize DMA transfers.

Drivers calling SoundTestWaveDevice must include wave.h.


SoundVolumeNotify

VOID
SoundVolumeNotify(
IN OUT PLOCAL_DEVICE_INFO pLDI
);

The SoundVolumeNotify function is used by mixer drivers to notify clients when a device’s volume changes.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure representing the device whose volume has changed.
Return Value

None.

Comments

The function traverses the IRP queue pointed to by the VolumeQueue member of the LOCAL_DEVICE_INFO structure, calling IoCompleteRequest for each queue entry.

Mixer drivers only call this function if volume is controlled in software. For an example, see sndsys.sys, the kernel-mode driver for the Windows Sound System synthesizer. Sources for this driver are in \src\mmedia\sndsys\driver.


SoundWaveDeferred

VOID
SoundWaveDeferred(
PKDPC pDpc,
PDEVICE_OBJECT pDeviceObject,
PIRP pIrp,
PVOID Context
);

The SoundWaveDeferred function is the DPC function that is provided by soundlib.lib for waveform input and output devices.

Parameters
pDpc
Pointer to a DPC object.
pDeviceObject
Pointer to a device object.
pIrpDeferred
Pointer to an IRP. (Not used by SoundWaveDeferred. See Comments section below.)
Context
Pointer to context information. (Not used by SoundWaveDeferred. See Comments section below.)
Return Value

None.

Comments

Drivers using soundlib.lib should specify this routine’s address as the DeferredRoutine member of each waveform device’s SOUND_DEVICE_INIT structure. SoundCreateDevice passes to the address to IoInitializeDpcRequest.

A driver causes SoundWaveDeferred to be called by calling IoRequestDPC from its ISR. The driver should specify NULL for the Irp and Context parameters to IoRequestDPC, because SoundWaveDeferred does not use them.


SoundWaveDispatch

NTSTATUS
SoundWaveDispatch(
IN OUT PLOCAL_DEVICE_INFO pLDI,
IN PIRP pIrp,
IN PIO_STACK_LOCATION IrpStack
);

The SoundWaveDispatch function is the IRP control code dispatcher for waveform devices.

Parameters
pLDI
Pointer to a LOCAL_DEVICE_INFO structure.
pIrp
Pointer to an IRP structure.
IrpStack
Pointer to an IO_STACK_LOCATION structure.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

Kernel-mode waveform device drivers using soundlib.lib place the address of the SoundWaveDispatch function in the DispatchRoutine member of a SOUND_DEVICE_INIT structure. The function is called by soundlib.lib’s main dispatcher, SoundDispatch.

The function processes the following IRP control codes:

IRP_MJ_CLEANUP

IRP_MJ_CLOSE

IRP_MJ_CREATE

IRP_MJ_DEVICE_CONTROL

IRP_MJ_READ

IRP_MJ_WRITE

For IRP_MJ_DEVICE_CONTROL, the function processes the following I/O control codes:

IOCTL_SOUND_GET_CHANGED_VOLUME

IOCTL_WAVE_GET_CAPABILITIES

IOCTL_WAVE_GET_PITCH

IOCTL_WAVE_GET_PLAYBACK_RATE

IOCTL_WAVE_GET_POSITION

IOCTL_WAVE_GET_POSITION

IOCTL_WAVE_GET_STATE

IOCTL_WAVE_GET_VOLUME

IOCTL_WAVE_QUERY_FORMAT

IOCTL_WAVE_SET_FORMAT

IOCTL_WAVE_SET_LOW_PRIORITY

IOCTL_WAVE_SET_PITCH

IOCTL_WAVE_SET_PLAYBACK_RATE

IOCTL_WAVE_SET_STATE


SoundWriteRegistryDWORD

NTSTATUS
SoundWriteRegistryDWORD(
IN PCWSTR RegistryPath,
IN PCWSTR ValueName,
IN ULONG Value
);

The SoundWriteRegistryDWORD function assigns the specified value to the specified value name in the registry, at the specified registry key.

Parameters
RegistryPath
Pointer to full path to a registry key.
ValueName
Pointer to a value name string.
Value
Value to assign to ValueName. The value is stored as a REG_DWORD type.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.


SynthCleanup

VOID
SynthCleanup(
IN PGLOBAL_SYNTH_INFO pGDI
);

The SynthCleanup function deallocates synthesizer resources.

Parameters
pGDI
Pointer to a GLOBAL_SYNTH_INFO structure.
Return Value

None.

Comments

The SynthCleanup function calls MmUnmapIoSpace.

Drivers calling SynthCleanup must include synthdrv.h.


SynthInit

NTSTATUS
SynthInit(
IN PDRIVER_OBJECT pDriverObject,
IN PWSTR RegistryPathName,
IN PGLOBAL_SYNTH_INFO pGDI,
IN ULONG SynthPort,
IN BOOLEAN InterruptConnected,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN PMIXER_DATA_ITEM MidiOutItem,
IN UCHAR VolumeControlId,
IN BOOLEAN Multiple,
IN SOUND_DISPATCH_ROUTINE *DevCapsRoutine
);

The SynthInit function creates a device object for a MIDI synthesizer (AdLib or OPL3) and performs additional initialization tasks.

Parameters
pDriverObject
Address of a location to receive a pointer to a DEVICE_OBJECT structure, if the call succeeds.
RegistryPathName
Pointer to a string representing the registry path to the device subkey. Use the path name created by either SoundEnumSubkeys or SoundSaveRegistryPath.
pGDI
Pointer to a GLOBAL_SYNTH_INFO structure.
SynthPort
Port address.
InterruptConnected
TRUE if interrupt is connected, FALSE otherwise.
BusType
Bus type. INTERFACE_TYPE is defined in ntddk.h.
BusNumber
Bus number.
MidiOutItem
Not used. Should be set to NULL.
VolumeControlId
A driver-defined volume control ID value.
Multiple
TRUE if indexed device names are allowed, FALSE otherwise.
DevCapsRoutine
Pointer to a driver-defined function that returns device capabilities. The function type is SOUND_DISPATCH_ROUTINE. To read about functions that return device capabilities, refer to the description of the DevCapsRoutine member of the SOUND_DEVICE_INIT structure.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

You do not create a SOUND_DEVICE_INIT structure for a synthesizer device, because soundlib.lib creates one internally. It also provides an internal dispatch routine and exclusion routine.

The SynthInit function performs the following operations, in order:

  1. Calls SoundCreateDevice to create a device object and a LOCAL_DEVICE_INFO structure.

  2. Sets default volume values for left and right channels in the Volume member of the device’s LOCAL_DEVICE_INFO structure.

  3. Calls SoundReportResourceUsage to obtain system resources.

  4. Calls SoundMapPortAddress to map port addresses.

  5. Attempts to reference the synthesizer hardware to verify that it exists at the specified address.

  6. Calls SoundSaveDeviceName to write the device name in the registry.

  7. Stores the specified volume control ID in the device’s LOCAL_DEVICE_INFO structure.

  8. Determines if the synthesizer chip is OPL3-compatible.

  9. Initializes the synthesizer hardware to silence.

Setting Multiple to FALSE causes SoundCreateDevice to be called with the SOUND_CREATION_NO_NAME_RANGE flag set. Use FALSE if the port address is 0x388.

Synthesizer interrupts are not handled by soundlib.lib. If the device supports a hardware interrupt, the driver must provide ISR code to handle it.

Drivers calling SynthInit must include synthdrv.h.


Structures and Types, soundlib.lib

This section describes the structures and types available to kernel-mode audio drivers using soundlib.lib. The structures and types are defined in soundlib.h, devices.h, wave.h, midi.h, mixer.h, and synthdrv.h.


GLOBAL_SYNTH_INFO

typedef struct _GLOBAL_SYNTH_INFO {
    ULONG           Key;
#define SYNTH_KEY        (*(ULONG *)"Syn ")
    INTERFACE_TYPE  BusType;
    ULONG           BusNumber;
    KMUTEX          MidiMutex;
    ULONG           MemType;
    PDEVICE_OBJECT  DeviceObject;
    PDRIVER_OBJECT  DriverObject;
    SOUND_DISPATCH_ROUTINE  *DevCapsRoutine;
    UCHAR           DeviceInUse;
    volatile BOOLEAN  InterruptFired;     // Interrupt fired?
    BOOLEAN         IsOpl3;               // It's an OPL3
    SYNTH_HARDWARE  Hw;                   // Hardware specific stuff
} GLOBAL_SYNTH_INFO, *PGLOBAL_SYNTH_INFO;

The GLOBAL_SYNTH_INFO structure contains context information for a mixer device.

Members
Key
Internal only, for debugging. Should be “Syn “.
BusType
Bus type. INTERFACE_TYPE is defined in ntddk.h.
BusNumber
Bus number.
MidiMutex
Internal only. Mutex object used by soundlib.lib’s exclusion routine.
MemType
Memory type returned by SoundMapPortAddress.
DeviceObject
Pointer to device object returned by SoundCreateDevice.
DriverObject
Pointer to driver object received by DriverEntry.
DevCapsRoutine
Pointer to a driver-defined function that returns device capabilities. The function type is SOUND_DISPATCH_ROUTINE.
DeviceInUse
Internal only. Indicates device status.
InterruptFired
Set by driver if interrupt fired. Not set by soundlib.lib.
IsOpl3
Set if synthesizer is OPL3-compatible.
Hw
Hardware information. Member type is SYNTH_HARDWARE.
Comments

Allocate a GLOBAL_SYNTH_INFO structure from the nonpaged memory pool by calling ExAllocatePool, then zero it by calling RtlZeroMemory. The structure is initialized by code in SynthInit.


LOCAL_DEVICE_INFO

typedef struct _LOCAL_DEVICE_INFO {
    ULONG           Key;
#define LDI_WAVE_IN_KEY     (*(ULONG *)"LDWi")
#define LDI_WAVE_OUT_KEY    (*(ULONG *)"LDWo")
#define LDI_MIDI_IN_KEY     (*(ULONG *)"LDMi")
#define LDI_MIDI_OUT_KEY    (*(ULONG *)"LDMo")
#define LDI_AUX_KEY         (*(ULONG *)"LDAx")
#define LDI_MIX_KEY         (*(ULONG *)"LDMx")
    PVOID           pGlobalInfo;
    UCHAR           DeviceType;
    UCHAR           DeviceNumber;
    UCHAR           DeviceIndex;
    UCHAR           CreationFlags;
#define SOUND_CREATION_NO_NAME_RANGE ((UCHAR)0x01)
#define SOUND_CREATION_NO_VOLUME     ((UCHAR)0x02)
    BOOLEAN         PreventVolumeSetting;
    UCHAR           VolumeControlId;
    PSOUND_LINE_NOTIFY   LineNotify;
#ifndef SOUNDLIB_NO_OLD_VOLUME
    WAVE_DD_VOLUME  Volume;
#endif
#ifdef VOLUME_NOTIFY
    LIST_ENTRY      VolumeQueue;
    struct _LOCAL_DEVICE_INFO * MixerDevice;
#endif
#ifdef MASTERVOLUME
    BOOLEAN         MasterVolume;
#endif
    BOOLEAN         VolumeChanged;
    PVOID           DeviceSpecificData;
    PVOID           HwContext;
    ULONG           State;
    PCSOUND_DEVICE_INIT   DeviceInit;
} LOCAL_DEVICE_INFO, *PLOCAL_DEVICE_INFO;

Within soundlib.lib, the LOCAL_DEVICE_INFO structure is used as the device extension for each device object created by IoCreateDevice. Device objects and device extensions are described in the Kernel-Mode Drivers Design Guide. The LOCAL_DEVICE_INFO structure is defined in devices.h. One LOCAL_DEVICE_INFO structure exists for each device created by SoundCreateDevice.

Members
Key
Internal only. Used for debugging. The value is copied from the Key member of SOUND_DEVICE_INIT.
pGlobalInfo
Pointer to a driver-defined structure containing device object-specific data. Specified by the pGDI parameter of SoundCreateDevice.
DeviceType
Copied from the DeviceType member of the SOUND_DEVICE_INIT structure.
DeviceNumber
Contains number to append to the end of the device name. Set to 0xFF within SoundCreateDevice if the SOUND_CREATION_NO_NAME_RANGE flag is specified.
DeviceIndex
For use by the driver. Contains the i parameter specified with SoundCreateDevice.
CreationFlags
Contains flags passed as CreationFlags parameter of SoundCreateDevice.
PreventVolumeSetting
Internal only. Used to prevent volume being shared for devices opened without shared write access. Set by SoundSetShareAccess.
VolumeControlId
Set by SoundSetVolumeControlId.
LineNotify
Address of a driver-supplied function called when a line status changes. The function type is PSOUND_LINE_NOTIFY. Set by SoundSetLineNotify.
Volume
Contains the device’s volume setting.
VolumeQueue
IRP queue for SOUND_IOCTL_GET_CHANGED_VOLUME requests not completed.
MixerDevice
If the device object represents a mixer device, this member points to an additional LOCAL_DEVICE_INFO structure for the mixer device. This member must be set by the driver’s mixer initialization code. It is not set by soundlib.lib.
MasterVolume
Set if this device is the master volume control device.
VolumeChanged
Internal only. If there is no mixer device, soundlib.lib sets this after a volume change, to indicate that volume settings need to be updated in the registry before system shutdown.
DeviceSpecificData
Contains the value passed as the DeviceSpecificData parameter of SoundCreateDevice.
HwContext
Contains the value passed as the pHw parameter of SoundCreateDevice.
State
Internal only Used by soundlib.lib to store the current device state.
DeviceInit
Contains the value passed as the DeviceInit parameter to SoundCreateDevice.
Comments

Drivers do not allocate LOCAL_DEVICE_INFO structures locally. When a driver calls SoundCreateDevice, a pointer to a DEVICE_OBJECT structure is returned. The structure’s DeviceExtension member is used as the device’s LOCAL_DEVICE_INFO structure.


MIDI_INFO

typedef struct _MIDI_INFO
{
    ULONG           Key;
#define MIDI_INFO_KEY       (*(ULONG *)"Midi")
        KSPIN_LOCK      DeviceSpinLock;
    #if DBG
        BOOLEAN         LockHeld;
    #endif
        LARGE_INTEGER   RefTime;
        LIST_ENTRY      QueueHead;
        PVOID           HwContext;
        PMIDI_INTERFACE_ROUTINE HwStartMidiIn, HwStopMidiIn;
        BOOLEAN      (* HwMidiRead)(struct _MIDI_INFO *, PUCHAR);
        VOID         (* HwMidiOut)(struct _MIDI_INFO *, PUCHAR, int);
        BOOLEAN         fMidiInStarted;
        UCHAR           InputPosition;
        UCHAR           InputBytes;
        UCHAR           MidiInputByte[64];
} MIDI_INFO, *PMIDI_INFO;

The MIDI_INFO structure contains context information for an external MIDI device.

Members
Key
Internal only, for debugging. Should be “Midi”.
DeviceSpinLock
Internal only. Used for DPC synchronization.
LockHeld
Internal only. Used for debugging.
RefTime
Used by soundlib.lib to store the start time of an I/O operation, as reference for time stamps.
QueueHead
Internal only. Points to a buffer queue for MIDI input requests.
HwContext
Pointer to a driver-defined structure containing device-specific hardware information. Typically used by functions pointed to by the HwStartMidiIn, HwStopMidiIn, HwMidiRead, and HwMidiOut members.
HwStartMidiIn
Pointer to a driver-supplied function that programs the MIDI hardware to start recording. The function type is MIDI_INTERFACE_ROUTINE.

The function is called when SoundMIDIDispatch receives a IOCTL_MIDI_SET_STATE command. See \src\mmedia\soundlib\midi.c.

HwStopMidiIn
Pointer to a driver-supplied function that programs the MIDI hardware to stop recording. The function type is MIDI_INTERFACE_ROUTINE.

The function is called when SoundMIDIDispatch receives a IRP_MJ_CLEANUP command. For more information, see \src\mmedia\soundlib\midi.c.

HwMidiRead
Pointer to a driver-supplied function that reads one input byte. This operation might only consist of fetching the next byte from a buffer that was filled by an ISR. (An example is MPU401 support in sndblst.sys.) The function type is:
BOOLEAN (* HwMidiRead)(struct _MIDI_INFO *, PUCHAR)

The _MIDI_INFO* parameter points to a MIDI_INFO structure and the PUCHAR parameter receives the read byte. The function returns TRUE if a byte was read, and FALSE otherwise.

The function is called when SoundMIDIDispatch receives a IRP_MJ_READ command. For more information, see \src\mmedia\soundlib\midi.c.

This function executes at an IRQL of DISPATCH_LEVEL, so it cannot be pageable and it cannot reference pageable code or data. Also, the only synchronization method it can use is calling KeStallExecutionProcessor.

HwMidiOut
Pointer to a driver-supplied function that commands the MIDI hardware to write a string of bytes. Function type is:
VOID (* HwMidiOut)(struct _MIDI_INFO *, PUCHAR, int)

The first parameter points to a MIDI_INFO structure, the second parameter points to a mapped buffer of bytes, and the third parameter contains the buffer size.

The function is called when SoundMIDIDispatch receives a IOCTL_MIDI_PLAY command. For more information, see \src\mmedia\soundlib\midi.c.

fMidiInStarted
Internal only. Indicates a MIDI input operation is in progress.
InputPosition
Internal only. Pointer to an internal input buffer.
InputBytes
Internal only. Count of bytes in internal input buffer.
MidiInputByte
Internal only. Pointer to internal input buffer.
Comments

A single MIDI_INFO structure can be used to support simultaneous MIDI input and output. MIDI_INFO is defined in midi.h.

Allocate a MIDI_INFO structure from the nonpaged memory pool by calling ExAllocatePool, then zero it by calling RtlZeroMemory. To initialize a MIDI_INFO structure, call SoundInitMidiIn.

To create a MIDI device object, call SoundCreateDevice and specify a MIDI_INFO structure pointer for the DeviceSpecificData parameter.


MIDI_INTERFACE_ROUTINE

typedef BOOLEAN MIDI_INTERFACE_ROUTINE(struct _MIDI_INFO *);

MIDI_INTERFACE_ROUTINE is a type definition for functions that send commands to MIDI hardware.

Parameters
_MIDI_INFO*
Pointer to a MIDI_INFO structure.
Comments

Drivers using soundlib.lib define functions modeled on this type and place their addresses in the HwStartMidiIn and HwStopMidiIn members of a WAVE_INFO structure.


MIXER_DATA_ITEM

typedef struct _MIXER_DATA_ITEM {
    LIST_ENTRY    Entry;
    LARGE_INTEGER LastSet;
    USHORT        Message;
    USHORT        Id;
} MIXER_DATA_ITEM, *PMIXER_DATA_ITEM;

The MIXER_DATA_ITEM structure, defined in mixer.h, is used to represent a data item for either a mixer line or a mixer control. The structure is initialized by calling SoundInitDataItem and is modified by SoundMixerChangedItem.

Members
Entry
Internal only. Contains FLINK and BLINK list pointers. Set by SoundInitDataItem.
LastSet
Internal only. Contains relative time of last change. Set by SoundInitDataItem and SoundMixerChangedItem.
Message
Internal only. Set by SoundInitDataItem. Contains type of notification message to send to client. Can be either MM_MIXM_LINE_CHANGE or MM_MIXM_CONTROL_CHANGE.
Id
Internal only. Set by SoundInitDataItem. Contains either a line ID or a control ID.
Comments

Drivers allocate one of these structures for each line and control that can change. The structure must be globally allocated, because soundlib.lib does not copy it. Instead, soundlib.lib modifies the structure’s Entry element to determine the structure’s location within a linked list.


MIXER_INFO

typedef struct _MIXER_INFO {
    ULONG    Key;
#define MIX_INFO_KEY       (*(ULONG *)"Mix")
    UCHAR         NumberOfLines;
    UCHAR         NumberOfControls;
    LARGE_INTEGER CurrentLogicalTime;
    LIST_ENTRY    NotifyQueue;
    LIST_ENTRY    ChangedItems;
    PMIXER_DD_GET_SET_DATA HwGetLineData;
    PMIXER_DD_GET_SET_DATA HwGetControlData;
    PMIXER_DD_GET_SET_DATA HwGetCombinedControlData;
    PMIXER_DD_GET_SET_DATA HwSetControlData;
} MIXER_INFO, *PMIXER_INFO;

The MIXER_INFO structure contains context information for a mixer device.

Members
Key
Internal only, for debugging. Should be “Mix”.
NumberOfLines
Number of mixer lines.
NumberOfControls
Number of mixer controls.
CurrentLogicalTime
Internal only. Incremented each time an item is added to the ChangedItems queue. Used as a reference for determining the relative age of changed items.
NotifyQueue
Internal only. Pointer to an IRP queue of change notification targets. An IRP is added each time soundlib.lib receives an IOCTL_MIX_REQUEST_NOTIFY message.
ChangedItems
Internal only. Pointer to a list of mixer items of type MIXER_DATA_ITEM. Modified by SoundMixerChangedItem.
HwGetLineData
Pointer to a driver-supplied function that SoundMixerDispatch calls when it receives an IOCTL_MIX_GET_LINE_DATA message. The function type is PMIXER_DD_GET_SET_DATA, where the data parameter is a pointer to the fdwLine member of a MIXERLINE structure, and the length parameter is the member’s size. The function sets one or more of the following flags in the address pointed to by data.

Flag

Definition

MIXERLINE_LINEF_ACTIVE

Indicates that the line is active. Used mainly for waveform devices so the application can determine when to poll the peak meter.

MIXERLINE_LINEF_DISCONNECTED

Indicates that the line is permanently unavailable. Useful for disabling capabilities available only on some versions of the hardware.

MIXERLINE_LINEF_SOURCE

Indicates this is a source line, not a destination line.

These flags are defined in mmsystem.h. The MIXERLINE structure is described in the Win32 SDK.

HwGetControlData
Pointer to a driver-supplied function that SoundMixerDispatch calls when it receives an IOCTL_MIX_GET_CONTROL_DATA message. The function type is PMIXER_DD_GET_SET_DATA, where the length parameter is the size of the return buffer and the data parameter is the return buffer’s address. This address is the paDetails member of a MIXERCONTROLDETAILS structure. See the description of MIXERCONTROLDETAILS in the Win32 SDK for information on how the buffer should be filled.
HwGetCombinedControlData
Pointer to a driver-supplied function that returns the current values for the volume controls. If the volume is controlled by mixer hardware, then always return 0xFFFF for each control. Otherwise, if the master volume is supported in hardware, return the current value of the control. If the master volume is simulated, return the combined volume and master volume as a volume value.

The function type is PMIXER_DD_GET_SET_DATA, where the data parameter is a pointer to a WAVE_DD_VOLUME structure and the length parameter is the structure’s size.

HwSetControlData
Pointer to a driver-supplied function that SoundMixerDispatch calls when it receives an IRP_MJ_WRITE message to set control items. The soundlib.lib dispatchers for waveform, MIDI synthesizer, and auxiliary audio devices also call this function to set volume levels. Function type is PMIXER_DD_GET_SET_DATA, where the length parameter is the size of the data buffer and the data parameter is the data buffer’s address. This address is the paDetails member of a MIXERCONTROLDETAILS structure. Refer to the description of MIXERCONTROLDETAILS in the Win32 SDK to understand the buffer contents. The function should call SoundMixerChangedItem if the value of a control changes.
Comments

MIXER_INFO is defined in mixer.h.

Allocate a MIXER_INFO structure from the nonpaged memory pool by calling ExAllocatePool. To zero and initialize a MIXER_INFO structure, call SoundInitMixerInfo.

To create a mixer device object, call SoundCreateDevice and specify a MIXER_INFO structure pointer for the DeviceSpecificData parameter. Then assign the address of the mixer device’s LOCAL_DEVICE_INFO structure to the MixerDevice member of every other device’s LOCAL_DEVICE_INFO structure.


PMIXER_DD_GET_SET_DATA

typedef NTSTATUS (* PMIXER_DD_GET_SET_DATA)(struct _MIXER_INFO * MixerInfo, ULONG ID, ULONG Length, PVOID Data);

PMIXER_DD_GET_SET_DATA is a type definition for functions that set or retrieve information for mixer lines and controls.

Parameters
MixerInfo
Pointer to a MIXER_INFO structure.
ID
A line ID or a control ID. For HwGetLineData, the value represents a line ID. For HwGetControlData, HwGetCombinedControlData, and HwSetControlData, it is a control ID.
Length
Length of a data buffer.
Data
Pointer to a data buffer.
Comments

Drivers using soundlib.lib define functions modeled on this type and place their addresses in the HwGetLineData, HwGetControlData, HwGetCombinedControlData, and HwSetControlData members of a MIXER_INFO structure.


PSOUND_LINE_NOTIFY

typedef VOID (* PSOUND_LINE_NOTIFY)(struct _LOCAL_DEVICE_INFO *, UCHAR);

PSOUND_LINE_NOTIFY is the type definition for a function that is called when a line status changes.

Parameters
_LOCAL_DEVICE_INFO*
Type for a pointer to a LOCAL_DEVICE_INFO structure.
UCHAR
Type for a line notification code. The following codes are passed to the notification routine by soundlib.lib.
Device Type | Notification Code
For waveform devices: | SOUND_LINE_NOTIFY_WAVE SOUND_LINE_NOTIFY_VOICE
For MIDI synthesizers: | Always 0
Comments

Drivers using soundlib.lib define a function modeled on this type and call SoundSetLineNotify to place its address in the LineNotify member of a LOCAL_DEVICE_INFO structure.


SOUND_DEVICE_INIT

  typedef struct {
            PCWSTR LeftVolumeName, RightVolumeName;
            ULONG DefaultVolume;
            ULONG Type;
            ULONG DeviceType;
            char  Key[4];
            PCWSTR PrototypeName;
            PIO_DPC_ROUTINE DeferredRoutine;
            SOUND_EXCLUDE_ROUTINE *ExclusionRoutine;
            SOUND_DISPATCH_ROUTINE *DispatchRoutine;
            SOUND_DISPATCH_ROUTINE *DevCapsRoutine;
            SOUND_HW_SET_VOLUME_ROUTINE *HwSetVolume;
            ULONG IoMethod;
        } SOUND_DEVICE_INIT;

The SOUND_DEVICE_INIT structure associates driver dispatch routines with a driver object. A SOUND_DEVICE_INIT structure must be defined for each logical input or output device. The structure’s definition is in devices.h.

LeftVolumeName
Registry key value name used when storing the left channel volume in the registry. Used with SoundSaveDeviceVolume.
RightVolumeName
Registry key value name used when storing the right channel volume in the registry. Used with SoundSaveDeviceVolume.
DefaultVolume
Initial volume setting to use during installation. Required for devices with mixers.
Type
Type of device. SoundCreateDevice passes this value to IoCreateDevice. The following values, defined in ntddk.h, should be used.

Value

Definition

FILE_DEVICE_WAVE_IN

For waveform input

FILE_DEVICE_WAVE_OUT

For wave output

FILE_DEVICE_MIDI_IN

For MIDI input

FILE_DEVICE_MIDI_OUT

For MIDI output

FILE_DEVICE_SOUND

For all other audio devices

DeviceType
Type of device, used within soundlib.lib and drvlib.lib. The following values, defined in soundcfg.h, are accepted.

Value

Definition

WAVE_IN

Waveform input device

WAVE_OUT

Waveform output device

MIDI_IN

MIDI input device

MIDI_OUT

MIDI output device

AUX_DEVICE

Auxiliary audio device

MIXER_DEVICE

Mixer device

SYNTH_DEVICE

MIDI Synthesizer device (adlib or opl3)

Key
For debugging purposes. Code in soundlib.lib copies this four-character string value into the device’s LOCAL_DEVICE_INFO structure.
PrototypeName
Prototype to use for creating a device object name. Unless the SOUND_CREATION_NO_NAME_RANGE flag is specified as a SoundCreateDevice parameter, SoundCreateDevice appends a sequential number, starting with zero, to this name.

If you are using mmdrv.dll as your user-mode driver, then you must use the prototype name that mmdrv.dll recognizes for the device. The names recognized by mmdrv.dll are predefined and their string IDs can be referenced using the following names.

Name

Where Defined

DD_AUX_DEVICE_NAME_U

ntddaux.h

DD_MIDI_IN_DEVICE_NAME_U
DD_MIDI_OUT_DEVICE_NAME_U

ntddmidi.h

DD_MIX_DEVICE_NAME_U

ntddmix.h

DD_WAVE_IN_DEVICE_NAME_U
DD_WAVE_OUT_DEVICE_NAME_U

ntddwave.h

If you are using a customized user-mode driver, you cannot use the predefined names. For example, in the SOUND_DEVICE_INIT structures for the kernel-mode driver sndblst.sys, predefined names are used for MIDI devices but not for waveform, auxiliary, or mixer devices. The result is that mmdrv.dll handles user-mode MIDI operations, and sndblst.dll handles all others.

DeferredRoutine
Pointer to a deferred procedure call (DPC) routine, which SoundCreateDevice passes to IoInitializeDpcRequest.

If the device object does not support interrupts, this member must be NULL. For drivers using soundlib.lib, specify one of the following DPC routines.

Device Type | DPC Routine
Waveform input and output devices | SoundWaveDeferred
MIDI input devices | SoundMidiInDeferred
MIDI output, auxiliary audio, and mixers | NULL
ExclusionRoutine
Pointer to a mutual exclusion function, called from within soundlib.lib when it is necessary to acquire or release a mutex for the device. To understand under what circumstances this function is called, see the soundlib.lib source code, included with this DDK. The function type is SOUND_EXCLUDE_ROUTINE.
DispatchRoutine
Pointer to a function that serves as a dispatcher for IRP function codes received by the driver. Functions supplied by soundlib.lib are as follows:
Dispatcher | Purpose
SoundAuxDispatch | Dispatcher for auxiliary audio devices
SoundMidiDispatch | Dispatcher for MIDI input and output devices
SoundMixerDispatch | Dispatcher for mixer devices
SoundWaveDispatch | Dispatcher for waveform input and output devices

The function type is SOUND_DISPATCH_ROUTINE. The specified function is called by SoundDispatch.

DevCapsRoutine
Pointer to a driver-defined function that returns device capabilities.

The function type is SOUND_DISPATCH_ROUTINE. The specified function is called by the dispatcher pointed to by the DispatchRoutine member, when the dispatcher receives IRP_MJ_DEVICE_CONTROL with an accompanying request for device capabilities. Capabilities for waveform, MIDI, and auxiliary devices are written into the IRP at Irp->AssociatedIrp.SystemBuffer, in the form of either a WAVEINCAPS, WAVEOUTCAPS, MIDIINCAPS, MIDIOUTCAPS, or AUXCAPS capabilities structure. (These structures are defined in mmsystem.h and described in the Win32 SDK.)

Note: When filling in the szPname member of the capabilities structure, remember the following:

  • If your user-mode driver is mmdrv.dll, you must call InternalLoadString to translate string IDs into strings, and return the strings in the szPname member.

  • If your user-mode driver makes use of drvlib.lib, just return the string IDs in the szPname member. Code in drvlib.lib calls InternalLoadString.

For mixer devices only, the following rules apply:

  • Capabilities are written into the IRP at Irp->AssociatedIrp.SystemBuffer, in the form of a MIXER_DD_CONFIGURATION_DATA structure (defined in ntddmix.h). Code in drvlib.lib calls the capabilities function only when the device is being initialized. It stores the MIXER_DD_CONFIGURATION_DATA structure contents and returns them to a client, in a MIXERCAPS structure, when requested.

  • The capabilities function is called twice (and only twice). The first time, it must only return the size of the capabilities information. The second time it is called, the function returns the capabilities information as a MIXER_DD_CONFIGURATION_DATA structure and a set of associated structures.

As an aid to understanding these special rules, see the mixer capabilities function, SoundMixerDumpConfiguration, provided in sndblst.sys, in src\mmedia\sndblst\driver\mixer.c.

HwSetVolume
Pointer to a driver-supplied function that sets the volume for the device. The function type is SOUND_HW_SET_VOLUME_ROUTINE.

The specified function is called by the dispatcher pointed to by the DispatchRoutine member, when the dispatcher receives IRP_MJ_DEVICE_CONTROL with an accompanying request to set the volume.

For devices without volume setting capabilities, use the SoundNoVolume function. Also use SoundNoVolume for devices that include mixer hardware, because drivers for these devices include a MIXER_INFO structure, and volume is controlled by a routine pointed to by that structure’s HwSetControlData member.

IoMethod
Specifies whether the Windows NT I/O Manager should use direct I/O or buffered I/O for data transfers. Audio drivers should specify this value as shown in the following table.

Device Type

I/O Method

Auxiliary

DO_BUFFERED_IO

MIDI input

DO_DIRECT_IO

MIDI output

DO_DIRECT_IO

Mixer

DO_BUFFERED_IO

Wave input

DO_DIRECT_IO

Wave output

DO_DIRECT_IO

For a discussion of direct I/O and buffer I/O methods, refer to the Kernel-Mode Drivers Design Guide.

Comments

The SOUND_DEVICE_INIT structure’s address is passed to SoundCreateDevice. The structure must not be freed and must be nonpaged, because SoundCreateDevice does not copy it.

You must initialize all structure members before calling SoundCreateDevice. The LeftVolumeName, RightVolumeName, and DefaultVolume members can be initialized to NULL, NULL, and 0, respectively.


SOUND_DISPATCH_ROUTINE

typedef NTSTATUS SOUND_DISPATCH_ROUTINE(struct _LOCAL_DEVICE_INFO *, PIRP, PIO_STACK_LOCATION);

SOUND_DISPATCH_ROUTINE is the type definition of dispatch routines for IRP function codes.

Parameters
_LOCAL_DEVICE_INFO*
Type for a pointer to a LOCAL_DEVICE_INFO structure.
PIRP
Type for a pointer to an IRP structure.
PIO_STACK_LOCATION
Type for a pointer to an IO_STACK_LOCATION structure.
Return Value

Returns STATUS_SUCCESS if the operation succeeds. Otherwise returns an NTSTATUS error code.

Comments

Drivers using soundlib.lib place the address of a function modeled on this type in the DispatchRoutine and DevCapsRoutine members of a SOUND_DEVICE_INIT structure.


SOUND_DMA_BUFFER

typedef struct {
    PADAPTER_OBJECT      AdapterObject[2];
    ULONG                BufferSize;
    PVOID                VirtualAddress;
    PHYSICAL_ADDRESS     LogicalAddress;
    PMDL                 Mdl;
} SOUND_DMA_BUFFER, *PSOUND_DMA_BUFFER;

The SOUND_DMA_BUFFER structure describes a DMA buffer. It is used as input to the SoundGetCommonBuffer and SoundFreeCommonBuffer functions.

Members
AdapterObject[2]
Array of pointers to adapter objects. (Only the first array element is used.)
BufferSize
Size of buffer, in bytes.
VirtualAddress
Virtual address of buffer.
LogicalAddress
Logical address of buffer.
Mdl
Pointer to a memory descriptor list (MDL).


SOUND_DOUBLE_BUFFER

typedef struct {
    enum {LowerHalf = 0,
          UpperHalf}
                         NextHalf;
    ULONG                BufferSize;
    PUCHAR               Buf;
    ULONG                StartOfData;
    ULONG                nBytes;
    UCHAR                Pad;
} SOUND_DOUBLE_BUFFER, *PSOUND_DOUBLE_BUFFER;

The SOUND_DOUBLE_BUFFER structure is an internal structure that describes the usage of a DMA buffer of type SOUND_DMA_BUFFER.

Members
NextHalf
Indicates which half of the buffer is to be used next.
BufferSize
Actual amount of DMA buffer in use. Obtained by calling SoundGetDMABufferSize.
Buf
Pointer to a buffer specified by a SOUND_DMA_BUFFER structure.
StartOfData
Start of valid data.
nBytes
Number of bytes in buffer.
Pad
Value to use when padding the buffer.


SOUND_EXCLUDE_CODE

typedef enum {
    SoundExcludeOpen,
    SoundExcludeClose,
    SoundExcludeEnter,
    SoundExcludeLeave,
    SoundExcludeQueryOpen
} SOUND_EXCLUDE_CODE;

SOUND_EXCLUDE_CODE is an enumerated type that is used with exclusion routines. Exclusion routines are defined using the SOUND_EXCLUDE_ROUTINE type.

Elements
SoundExcludeOpen
The device is being opened.
SoundExcludeClose
The device is being closed.
SoundExcludeEnter
A request for this device is starting.
SoundExcludeLeave
The request is finished.
SoundExcludeQueryOpen
Queries to determine if the device is open.


SOUND_EXCLUDE_ROUTINE

typedef BOOLEAN SOUND_EXCLUDE_ROUTINE(struct _LOCAL_DEVICE_INFO *, SOUND_EXCLUDE_CODE);

SOUND_EXCLUDE_ROUTINE is a type definition for routines that handle mutual exclusion operations.

Parameters
_LOCAL_DEVICE_INFO*
Type for a pointer to a LOCAL_DEVICE_INFO structure.
SOUND_EXCLUDE_CODE
Type for a code indicating the type of exclusion operation the routine should handle. See SOUND_EXCLUDE_CODE for the type’s definition.
Comments

Drivers using soundlib.lib define a function modeled on this type and place its address in the ExclusionRoutine member of a SOUND_DEVICE_INIT structure.

The SoundExcludeOpen and SoundExcludeClose exclusion codes request and release access to the device, to serialize the device’s use. SoundExcludeEnter and SoundExcludeLeave control temporary synchronization to the device. Generally, a driver uses mutex objects to handle these operations.


SOUND_HW_SET_VOLUME_ROUTINE

typedef VOID SOUND_HW_SET_VOLUME_ROUTINE(struct _LOCAL_DEVICE_INFO *);

SOUND_HW_SET_VOLUME_ROUTINE is the type definition for a routine the sets the volume for a device.

Parameters
_LOCAL_DEVICE_INFO*
Type for a pointer to a LOCAL_DEVICE_INFO structure.
Comments

Drivers using soundlib.lib define a function modeled on this type and place its address in the HwSetVolume member of a SOUND_DEVICE_INIT structure.


SOUND_QUERY_FORMAT_ROUTINE

typedef NTSTATUS SOUND_QUERY_FORMAT_ROUTINE (PLOCAL_DEVICE_INFO, PPCMWAVEFORMAT);

SOUND_QUERY_FORMAT_ROUTINE is the type definition for a function used to determine if a specified wave format is supported.

Parameters
PLOCAL_DEVICE_INFO
Type for a pointer to a LOCAL_DEVICE_INFO structure.
PPCMWAVEFORMAT
Type for a wave format. Although PPCMWAVEFORMAT is specified, any PWAVEFORMATEX is valid. Wave formats are defined in mmsystem.h.
Comments

Drivers using soundlib.lib define a function modeled on this type and place its address in the QueryFormat member of a WAVE_INFO structure.


SOUND_REGISTRY_CALLBACK_ROUTINE

typedef NTSTATUS SOUND_REGISTRY_CALLBACK_ROUTINE(PWSTR RegistryPathName, PVOID Context);

SOUND_REGISTRY_CALLBACK_ROUTINE is the type definition for the callback function that is called by SoundEnumSubkeys.

Parameters
RegistryPathName
Pointer to the full registry path to a subkey representing a hardware device. The path format is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters\Device Number
Context
Pointer to driver-specified context information. This is the Context parameter to SoundEnumSubkeys, and is typically the address of a driver-defined structure for storing device-specific information.
Comments

The function should save the path name pointer in device-specific storage, and use it as input to soundlib.lib functions requiring a registry path to a hardware device.

Note: Prior to being unloaded, the driver must deallocate the space allocated to the path name.


SYNTH_HARDWARE

typedef struct {
    ULONG     Key;                // For debugging
#define SYNTH_HARDWARE_KEY   (*(ULONG *)"Hw  ")
    PUCHAR    SynthBase;          // base port address for synth
} SYNTH_HARDWARE, *PSYNTH_HARDWARE;

The SYNTH_HARDWARE structure contains hardware information for synthesizer devices.

Members
Key
Internal only, for debugging. Should be “hw “.
SynthBase
Base of mapped port address space. Returned from SoundMapPortAddress.


WAVE_INFO

typedef struct _WAVE_INFO {
    ULONG                   Key;
#define WAVE_INFO_KEY       (*(ULONG *)"Wave")
    PDEVICE_OBJECT          DeviceObject;
    SOUND_DMA_BUFFER        DMABuf;
    SOUND_DOUBLE_BUFFER     DoubleBuffer;
    SOUND_BUFFER_QUEUE      BufferQueue;
    ULONG                   SamplesPerSec;
    UCHAR                   BitsPerSample;
    UCHAR                   Channels;
    BOOLEAN                 FormatChanged;
    PWAVEFORMATEX           WaveFormat;
    BOOLEAN                 LowPrioritySaved;
    PFILE_OBJECT            LowPriorityHandle;
    PLOCAL_DEVICE_INFO      LowPriorityDevice;
    struct {
        SOUND_BUFFER_QUEUE      BufferQueue;
        ULONG                   SamplesPerSec;
        UCHAR                   BitsPerSample;
        UCHAR                   Channels;
        PWAVEFORMATEX           WaveFormat;
        ULONG                   State;
    } LowPriorityModeSave;
    PVOID                   MRB[2];
    KEVENT                  DmaSetupEvent;
    KEVENT                  DpcEvent;
    KEVENT                  TimerDpcEvent;
    KSPIN_LOCK              DeviceSpinLock;
#if DBG
    BOOLEAN                 LockHeld;
#endif
    PKINTERRUPT             Interrupt;
    BOOLEAN                 Direction;
    UCHAR                   DMAType;
    UCHAR                   InterruptHalf;
    volatile BOOLEAN        DMABusy;
    volatile BOOLEAN        DpcQueued;
    ULONG                   Overrun;
    PVOID                   HwContext;
    WORK_QUEUE_ITEM         WaveStopWorkItem;
    KEVENT                  WaveReallyComplete;
    PSOUND_QUERY_FORMAT_ROUTINE QueryFormat;
    PWAVE_INTERFACE_ROUTINE
                            HwSetupDMA,
                            HwStopDMA,
                            HwSetWaveFormat;
    KDPC                    TimerDpc;
    KTIMER                  DeviceCheckTimer;
    BOOLEAN                 GotWaveDpc;
    BOOLEAN                 DeviceBad;
    BOOLEAN                 TimerActive;
    UCHAR                   FailureCount;
} WAVE_INFO, *PWAVE_INFO;

The WAVE_INFO structure contains wave device context information.

Members
Key
Internal only, for debugging. Should be “Wave”.
DeviceObject
Pointer to a DEVICE_OBJECT structure.
DMABuf
Structure describing a DMA buffer. Structure type is SOUND_DMA_BUFFER. Call SoundGetCommonBuffer to fill in this structure.
DoubleBuffer
Internal only. This is an internal structure containing additional information about the DMA buffer. The structure contains a pointer to the buffer described by the DMABuf member. The structure type is SOUND_DOUBLE_BUFFER.
BufferQueue
Internal only. For queuing device requests.
SamplesPerSec
Current samples per second.
BitsPerSample
Current bits per sample, per channel.
Channels
Current number of channels.
FormatChanged
Format has changed. Tested by HwSetWaveFormat function.
WaveFormat
Format for non-PCM formats.

LowPrioritySaved

LowPriorityHandle

LowPriorityDevice

LowPriorityModeSave
These four members are used by soundlib.lib for management of low priority mode, which allows a wave input device to nominate itself as pre-emptible if its dispatch routine receives IOCTL_WAVE_SET_LOW_PRIORITY. Only one user can be in low priority mode at a time.
MRB
Internal only. Contains adapter information.
DmaSetupEvent
Internal only. Stores event to wait on during channel allocation.
DpcEvent
Internal only. Used for synchronization with DPC termination.
TimerDpcEvent
Internal only. Used to track rogue devices.
DeviceSpinLock
Internal only. Used for DPC synchronization.
LockHeld
Internal only. Used for debugging.
Interrupt
Pointer to an interrupt object. Should be obtained from a call to SoundConnectInterrupt.
Direction
Set by soundlib.lib. Set to TRUE for output, FALSE for input.
DMAType
Type of DMA. One of the following enumerated values:
enum {
    SoundNoDMA,
    SoundAutoInitDMA,             // Use auto-initialize
    SoundReprogramOnInterruptDMA, // Reprogram on interrupt
    Sound2ChannelDMA              // Keep 2 channels going
};

Sound2ChannelDMA is not currently supported in soundlib.lib. It is intended for a device whose hardware uses two DMA channels and alternates between them to achieve continuous sound.

InterruptHalf
Internal only. Reserved for use when the DMAType member’s value is SoundReprogramOnInterruptDMA.
DMABusy
Set by soundlib.lib if DMA is in progress.
DpcQueued
Used for detecting an overrun condition. The ISR should test DpcQueued. If clear, the ISR can call IoRequestDpc. If set, an overrun has occurred and the ISR should set Overrun (see below) instead of calling IoRequestDpc.
Overrun
Set by the driver’s ISR if overrun occurs.
HwContext
Pointer to a driver-defined structure containing device-specific hardware information. Typically used by functions pointed by the HwSetupDMA, HwStopDMA, and HwSetWaveFormat members.
WaveStopWorkItem
Internal only. Structure used for calls to ExInitializeWorkItem and ExQueueWorkItem, when soundlib.lib is using system worker threads.
WaveReallyComplete
Internal only. Set by system worker thread after HwStopDMA has returned.
QueryFormat
Pointer to a driver-supplied function called when the driver receives IOCTL_WAVE_SET_FORMAT or IOCTL_WAVE_QUERY_FORMAT message. The function type is SOUND_QUERY_FORMAT_ROUTINE.
HwSetupDMA
Pointer to a driver-supplied function that programs the hardware to start a DMA transfer. Function type is WAVE_INTERFACE_ROUTINE.

The function is called after soundlib.lib has set up map registers by calling IoMapTransfer. For more information, see \src\mmedia\soundlib\wave.c.

HwStopDMA
Points to a driver-supplied function that sends commands to the hardware to stop DMA transfers. The function type is WAVE_INTERFACE_ROUTINE.

The function is called just before soundlib.lib calls IoFlushAdapterBuffers. For more information, see \src\mmedia\soundlib\wave.c.

Note: HwStopDMA must not acquire the device exclusion mutex that soundlib.lib uses to synchronize device access. Code in soundlib.lib waits for a transfer to complete before starting a new one, and this wait occurs inside a request to the device, when the mutex is owned by the waiting thread. This means that HwStopDMA can require extra synchronization code, even though no further hardware calls to the current device can occur until the current transfer is complete.

HwSetWaveFormat
Points to a driver-supplied function that sends commands to the hardware to set the wave format. The function type is WAVE_INTERFACE_ROUTINE.

The function is called just before soundlib.lib starts each DMA transfer. For more information, see \src\mmedia\soundlib\wave.c.

TimerDpc
Internal only. Structure used by soundlib.lib for calls to KeInitializeDpc.
DeviceCheckTimer
Internal only. Structure used by soundlib.lib for calls to KeInitializeTimer.
GotWaveDpc
Internal only. Indicates the device is active.
DeviceBad
Internal only. Set if the device doesn’t send interrupts.
TimerActive
Internal only. Indicates the device is active.
FailureCount
Internal only. Contains count of failed I/O attempts. If the count reaches 30, BadDevice is set.
Comments

One WAVE_INFO structure must be defined for each waveform device (input or output) that can be in operation simultaneously. WAVE_INFO is defined in wave.h.

Allocate a WAVE_INFO structure from the nonpaged memory pool by calling ExAllocatePool, and then zero it by calling RtlZeroMemory. To initialize a WAVE_INFO structure, assign values to the HwSetupDMA, HwStopDMA, and HwSetWaveFormat members and then call SoundInitializeWaveInfo.

To create a waveform device object, call SoundCreateDevice and specify a WAVE_INFO structure pointer for the DeviceSpecificData parameter.


WAVE_INTERFACE_ROUTINE

typedef BOOLEAN WAVE_INTERFACE_ROUTINE(struct _WAVE_INFO *);

WAVE_INTERFACE_ROUTINE is a type definition for functions that send commands to waveform hardware.

Parameters
_WAVE_INFO*
Type for a pointer to a WAVE_INFO structure.
Comments

Drivers using soundlib.lib define functions modeled on this type and place their addresses in the HwSetupDMA, HwStopDMA, and HwSetWaveFormat members of a WAVE_INFO structure.


Audio Compression Manager Drivers

The Audio Compression Manager (ACM) and its associated drivers compress, decompress, convert, and filter waveform file data. The following topics are provided:

  • Introduction to the ACM

  • Introduction to ACM Drivers

  • Designing an ACM Driver

  • ACM Driver Reference


Introduction to the ACM

The Audio Compression Manager (ACM) provides a set of API functions that allows a client to perform compressions, decompressions, format conversions, and filtering operations on waveform file data.

The ACM is implemented as a dynamic-link library named msacm32.dll. Applications that link with the ACM can call its API functions. The ACM is also used by the Windows NT® wave mapper, msacm32.drv, so applications that specify the wave mapper as an input or output device make indirect use of the ACM. To view a diagram that illustrates the relationship of the ACM to other Windows NT audio software, see Audio Software Components.

Descriptions of the ACM API functions are provided in the Win32 SDK. ACM API functions begin with a prefix of acm.

The ACM calls installable, user-mode drivers to perform conversions. Most ACM drivers can be installed with the Control Panel’s Multimedia applet and are available to all Win32-based applications. (For more information, see Installing Multimedia Drivers.) Individual applications can also load ACM drivers for their own private use. (For more information, see acmDriverAdd in the Win32 SDK, and Designing Local ACM Drivers.)


Introduction to ACM Drivers

This section introduces some general information about ACM drivers. It describes the types of ACM drivers, discusses format tags and filter tags, and introduces the sample ACM drivers that are provided with this DDK.

An ACM driver is a type of user-mode multimedia driver. For a general discussion of multimedia drivers, see Introduction to Multimedia Drivers.


Types of ACM Drivers

There are three types of ACM drivers ¾ codecs, converters, and filters.

Codecs
A codec (the term is short for compressor/decompressor) converts from one format type to another. Typically, a codec converts between a compressed format, such as MS-ADPCM, and the uncompressed PCM format, which most hardware recognizes.
Converters
A converter converts between different formats belonging to the same format type, such as between 44 kHz PCM and 11 kHz PCM formats.
Filters
A filter modifies audio data without changing the format. For example, an echo filter might add an echo sound to a 44 kHz PCM waveform file.

For more information on the difference between codecs and converters, see Format Tags and Filter Tags.

ACM drivers provided by Microsoft include codecs that convert the MS-ADPCM, IMA ADPCM, GSM 6.10-compliant, and Truespeech compressed formats to PCM.

The ACM also provides a PCM converter that converts between 8-bit and 16-bit PCM, between mono and stereo, and among various PCM sampling frequencies. This converter is not implemented is a separate installable driver. Instead, it is contained within the ACM’s DLL file, msacm32.dll.


Format Tags and Filter Tags

A format tag represents the name for a format type. A filter tag represents the name for a filter type. Typically, an ACM driver supports one or more types of formats and/or filters. Within Windows NT DDK documentation, ACM data structures, and sample ACM drivers, the term tag is used more often than type.

Typically, a set of formats or filters is associated with each tag. For example, the sample IMA ADPCM audio codec supports two format tags, namely the WAVE_FORMAT_PCM tag for the PCM format type and the WAVE_FORMAT_IMA_ADPCM tag for the IMA ADPCM format type. For each of these format tags, the driver supports several formats consisting of various sample rates and sample sizes.

To differentiate between codecs and converters, we can say that a codec transforms data from a format belonging to one format tag into a format belonging to another format tag, while a converter transforms data from one format to another belonging to the same format tag.

Format tags and filter tags are defined in mmreg.h. If you are writing a driver for a new format type or filter type, you must register the type by using the Multimedia Developer Registration Kit.


Sample ACM Drivers

The Windows NT DDK includes the following three sample ACM drivers:

IMA ADPCM Audio Codec
The IMA ADPCM Audio Codec, imaadp32.dll, converts between PCM and the IMA ADPCM format. This codec is optimized for speed, and illustrates how to design conversion routines to reduce computation time. It also provides a configuration dialog box. The first time the codec is opened, it tries to automatically configure itself by calculating its maximum sample rate.

Source files for imaadp32.dll are located in \ddk\src\mmedia\imaadpcm.

Microsoft GSM 6.10 Audio Codec
The Microsoft GSM 6.10 Audio Codec, msgsm32.dll, implements the GSM 6.10 voice encoding standard, originally developed for digital cellular telephone encoding. The codec converts between GSM 6.10 and PCM formats. Like imaadp32.dll, this codec provides a configuration dialog box, and also attempts to automatically configure itself the first time it is opened.

Source files for msgsm32.dll are located in \ddk\src\mmedia\gsm610.

Microsoft Audio Filter
The Microsoft Audio Filter, msfltr32.dll, is a single driver that provides both a volume filter and an echo filter. This driver supports a custom About box and a custom icon, which are explained in Providing a Custom About Box and Providing a Custom Icon.

Source files for msfltr32.dll are located in \ddk\src\mmedia\msfilter.

You should be able to easily create a new ACM driver by using these samples as models. The source code contains extensive comments that explain how the code works and how it should be modified to implement a new driver. You’ll see that, in general, all ACM drivers are very similar to each other, both in their functionality and in their code layout.


Designing an ACM Driver

The best way to design and create a new ACM driver is to modify one of the sample ACM drivers, because all ACM drivers are similar to each other in layout, and because the sample code is well documented to indicate how changes should be made.

This section provides the following topics:

  • DriverProc in ACM Drivers

  • ACM Driver Messages

  • Converting Data Streams

  • Notifying Clients from ACM Drivers

  • Installing ACM Drivers

  • Configuring ACM Drivers

  • Designing Local ACM Drivers

  • Defining Format Structures and Filter Structures

  • Providing a Custom About Box

  • Providing a Custom Icon

  • Providing ACM Support in Device Drivers

  • Writing Portable ACM Drivers

  • Guidelines for Writing ACM Drivers


DriverProc in ACM Drivers

Like all other Win32-based user-mode drivers, ACM drivers must export a DriverProc entry point which recognizes all of the standard driver messages. The ACM sends messages to its drivers by calling SendDriverMessage, which is exported by winmm.dll and described in the Win32 SDK.

ACM drivers generally provide support for DRV_OPEN, DRV_CLOSE, DRV_CONFIGURE and DRV_QUERYCONFIGURE messages, as illustrated by the sample ACM drivers. ACM drivers generally do not need to provide much, if any, support for DRV_INSTALL, DRV_LOAD, DRV_ENABLE, DRV_DISABLE, DRV_FREE, or DRV_REMOVE messages.

When an ACM driver receives a DRV_OPEN message from the ACM, it also receives a pointer to an ACMDRVOPENDESC structure. The driver receives the pointer as the lParam2 argument to its DriverProc function.

In addition to supporting the standard messages, the DriverProc entry point for ACM drivers must support a set of ACM driver messages.


ACM Driver Messages

The following table lists the messages that the DriverProc function in an ACM driver can receive, along with the operation the driver performs when it receives each message. Message definitions are contained in msacmdrv.h.
Message | Operation Performed by Driver
ACMDM_DRIVER_ABOUT | Displays an About dialog box.
ACMDM_DRIVER_DETAILS | Returns information about the driver.
ACMDM_DRIVER_NOTIFY | Determines status changes in other drivers.
ACMDM_FILTER_DETAILS | Returns information about a filter.
ACMDM_FILTERTAG_DETAILS | Returns information about a filter tag (type).
ACMDM_FORMAT_DETAILS | Returns information about a format.
ACMDM_FORMAT_SUGGEST | Suggests an input or output format.
ACMDM_FORMATTAG_DETAILS | Returns information about a format tag (type).
ACMDM_HARDWARE_WAVE_CAPS_INPUT | Returns device input capabilities.
ACMDM_HARDWARE_WAVE_CAPS_OUTPUT | Returns device output capabilities.
ACMDM_STREAM_CLOSE | Closes a data stream.
ACMDM_STREAM_CONVERT | Performs conversion operations on supplied data, based on conversion parameters received by ACMDM_STREAM_OPEN.
ACMDM_STREAM_OPEN | Opens a data stream. Includes data conversion parameters.
ACMDM_STREAM_PREPARE | Prepares source and destination data buffers.
ACMDM_STREAM_RESET | Stops an asynchronous conversion operation.
ACMDM_STREAM_SIZE | Estimates the required size of a source or destination buffer.
ACMDM_STREAM_UNPREPARE | Removes preparation performed on source and destination data buffers.


Converting Data Streams

Each ACM driver, whether a codec, converter, or filter, treats data as a stream. A client passes the input stream to the driver in one or more source buffers. The driver performs a conversion operation on the data and returns the converted stream to the client in one or more destination buffers.

ACM drivers can be designed to handle data conversion operations either synchronously or asynchronously. Generally, asynchronous operation only makes sense when a driver can perform hardware-assisted conversions. Drivers providing software-implemented conversions, such as the sample ACM drivers, usually operate synchronously.

A driver opens a stream when a client sends it an ACMDM_STREAM_OPEN message. With this message, the client includes pointers to structures that describe the format and other characteristics of both the source (input) and destination (output) streams. The driver uses this information to determine the types of transformations to perform on the source data.

If the client sends the driver an ACMDM_STREAM_SIZE message, specifying a source (or destination) buffer size, the driver returns the required size of a destination (or source) buffer. The driver uses the source and destination format characteristics, received with the previous ACMDM_STREAM_OPEN message, to determine the necessary source or destination buffer size.

Before the client can pass data buffers to the driver, it must prepare the buffers for use by passing them to the driver with an ACMDM_STREAM_PREPARE message. Each ACMDM_STREAM_PREPARE message includes the address of a stream header structure, defined by ACMDRVSTREAMHEADER, containing pointers to a source buffer and a destination buffer.

When the driver receives an ACMDM_STREAM_CONVERT message from the client, it begins the data transformation. Each ACMDM_STREAM_CONVERT message includes the address of a prepared stream header structure. The algorithm for what happens next depends on whether the driver is designed to operate synchronously or asynchronously.

To convert data synchronously

  • The driver applies the appropriate transformation algorithms to the source buffer data and places the results in the destination buffer. It then returns control to the client.

  • The client can send additional ACMDM_STREAM_CONVERT messages. Each time the driver receives a message, it converts the data and places it in the destination buffer, then returns control to the client.

To convert data asynchronously

  • The driver places the address of the stream header structure in a queue, and returns control to the client. The client is then free to send additional ACMDM_STREAM_CONVERT messages, and the client adds the addresses of the additional header structures to its conversion queue.

  • The driver applies the appropriate transformation algorithms to each source buffer and places the results in the associated destination buffer. Each time a buffer is converted, the driver sends the client an MM_ACM_DONE callback message and dequeues the associated stream header structure.

  • The driver continues this conversion and notification sequence until all buffers have been converted, or until the client sends an ACMDM_STREAM_RESET message.

When the client has finished the conversion, it sends an ACMDM_STREAM_UNPREPARE message for each stream header structure, and then sends an ACMDM_STREAM_CLOSE message.

Note: If your driver supports asynchronous conversions and a client requests a synchronous conversion (by not specifying the ACM_STREAMOPENF_ASYNC flag with the acmStreamOpen function, which is described in the Win32 SDK), the ACM manager sets the ACM_STREAMOPENF_ASYNC flag and specifies a local event handle as a callback target. In other words, the driver always receives ACM_STREAMOPENF_ASYNC with ACMDM_STREAM_OPEN if it is an asynchronous driver. The ACM receives the callback notification messages sent by the driver, and the conversion appears to operate synchronously from the client’s point of view. For more information about callbacks, see Notifying Clients from ACM Drivers.

Real Time Conversions

Generally, ACM drivers should perform conversions in real time. This means the driver should be able to perform conversion operations fast enough that there are no delays in a recording or playback operation, if a client is requesting the conversion to take place simultaneously with the recording or playback operation. (An example of such a client is the wave mapper.) Consequently, if a driver’s conversion algorithm requires relatively large amounts of calculations, it might not be able to run in real time.

When a client sends an ACMDM_STREAM_OPEN message, it can set a flag to indicate that it does not require the conversion to take place in real time. Drivers that cannot provide real time conversions can only operate if the client sets this flag.

Hardware-Assisted Conversions

Some waveform devices support hardware-assisted conversions. These devices accept data in one format, convert it to another format, and return the converted data without playing it. Hardware-assisted conversions are typically faster than conversions that must be entirely implemented in software. Drivers that make use of hardware assistance should probably be written to operate asynchronously.

To access a hardware conversion operation, an ACM driver must call the device’s kernel-mode driver, typically by means of the DeviceIOControl function described in the Win32 SDK. (For more information about kernel-mode drivers, see Kernel-Mode Multimedia Drivers.)


Notifying Clients from ACM Drivers

Asynchronous ACM drivers are responsible for notifying clients upon the completion of certain driver events. When a client sends an ACMDM_STREAM_OPEN message, it indicates the type of notification, if any, it expects to receive. A client can specify any of the following notification targets:

  • A callback function

  • A window handle

  • An event handle

ACM drivers notify clients by calling the DriverCallback function in winmm.dll. This function delivers a message to the client’s notification target. The DriverCallback function also delivers message parameters, if the target type accepts parameters.

Asynchronous ACM drivers must send MM_ACM_OPEN, MM_ACM_CLOSE, and MM_ACM_DONE messages to clients.

Because the sample ACM drivers provided with this DDK operate synchronously, they do not send notification messages.


Installing ACM Drivers

If you are writing an ACM driver that is meant to be available to all applications, the driver must be installed so that the ACM can find it.

On the other hand, if you are writing an ACM driver that is meant to be locally available to a specific application, the driver is not installed. Instead, either the application or the driver calls acmDriverAdd, described in the Win32 SDK, to make the driver locally accessible. (For more information about local ACM drivers, see Designing Local ACM Drivers.)

ACM drivers that are meant to be available to all applications are installed by using the Multimedia applet in the Control Panel. You must provide an oemsetup.inf file for your driver. Following is a sample oemsetup.inf file:

[Source Media Descriptions]
    1  = "MSGSM610"  , TAGFILE = disk1

[Installable.Drivers]
msgsm610 = 1:msgsm32.acm, "msacm.msgsm610", "Microsoft GSM 6.10 Audio CODEC" ,,,

For a description of oemsetup.inf file contents, see Installing Multimedia Drivers. The sample file causes the Multimedia applet to create the following registry entry:

msacm.msgsm610 : REG_SZ : msgsm32.acm

This entry is placed in the registry path HKEY_LOCAL_MACHINE \SOFTWARE \Microsoft \Windows NT \CurrentVersion \Drivers32

The ACM searches this registry path to determine which ACM drivers are installed. For each installed ACM driver, there must be a registry value name of the form msacm.alias, where alias is a unique name. This name is generally known as the driver type. For ACM drivers, only one driver file can be associated with each driver type.

Notice that the ACM driver’s file extension is .acm. While not required, this extension is favored over .dll for ACM drivers.


Configuring ACM Drivers

It is often necessary for ACM drivers to obtain and store configuration parameters. A driver requiring configuration parameters must display a configuration dialog box when its DriverProc function receives a DRV_CONFIGURE message. The dialog box must allow a user to specify configuration parameter values. Optionally, the dialog box can allow the user to select automatic configuration. If your driver provides this automatic configuration option, it must attempt to automatically provide values for all configuration parameters. Two of the sample ACM drivers, the IMA ADPCM Audio Codec and the Microsoft GSM 6.10 Audio Codec, provide automatic configuration.

An ACM driver’s DriverProc function must respond to DRV_QUERYCONFIGURE messages by indicating whether or not the driver provides a configuration dialog box.

A separate copy of an ACM driver’s configuration parameters should be saved for each user. To accomplish this, the driver should store configuration parameters in the registry, under the path HKEY_CURRENT_USER\Software\Microsoft\Multimedia\msacm.alias.

For a description of alias, see Installing ACM Drivers. (Use the msacm.alias naming scheme even for local drivers, which are not installed.) Since configuration parameters should be stored for each user, the parameters should be obtained and stored when the driver receives a DRV_OPEN message.

It is important for your driver to provide default values for all configuration parameters. In some circumstances, such as the playing of system sounds, there is no user context, so attempts to reference a registry path under HKEY_CURRENT_USER will fail. In such a case your driver must use its default values.


Designing Local ACM Drivers

Local ACM drivers are available only to a specific application. The application must explicitly add the driver to the ACM’s list of available drivers with a call to acmDriverAdd, which is exported by the ACM and described in the Win32 SDK. If you are writing a local ACM driver, you can locate your driver code in your application or in a separate DLL file.

Including ACM Driver Code in an Application

If you include ACM driver code within an application, the application must define a DriverProc function that recognizes ACM driver messages. To access the ACM driver code, your application must:

  • Call acmDriverAdd, specifying it’s own module handle as an input parameter.

  • Communicate with the driver code by calling the ACM’s API functions.

  • Call acmDriverRemove when it has finished using the ACM driver code.

Providing a Local ACM Driver as a Separate DLL

If you provide a local ACM driver as a separate DLL, your application must call LoadLibrary (see the Win32 SDK) to load the driver. The driver must define a DriverProc function that recognizes ACM driver messages. After the driver has been loaded, it must:

  • Call acmDriverAdd, specifying it’s own module handle as an input parameter.

  • Respond to ACM driver messages received when the application calls the ACM’s API functions.

  • Call acmDriverRemove when the application signals that it has finished using the driver.


Defining Format Structures and Filter Structures

Associated with each format tag and filter tag is a data structure that is based on the WAVEFORMATEX structure or the WAVEFILTER structure. These structures are described in the Win32 SDK and defined in mmreg.h.

If your driver provides support for new format tags or filter tags, you must provide new structure definitions and register them by using the Multimedia Developer Registration Kit.


Providing a Custom About Box

An ACM driver can provide a custom About box. This About box is displayed by the Control Panel’s Multimedia applet. If the driver does not provide a custom About box, the Multimedia applet uses a default About box.

When the driver receives an ACMDM_DRIVER_ABOUT message, it should call DialogBox (described in the Win32 SDK) to create and display its custom dialog box. If the driver does not provide a custom dialog box, it should return MMMSYSERR_NOTSUPPORTED when it receives an ACMDM_DRIVER_ABOUT message.

To lessen the task involved in writing an ACM driver, and provide user interface consistency, it is better to use the default About box than to provide a custom About box.


Providing a Custom Icon

An ACM driver can provide a custom icon. This icon is displayed by the Control Panel’s Multimedia applet inside the driver’s Properties and About boxes. If the driver does not provide a custom icon, the Multimedia applet uses a default icon. To provide a custom icon, you should design the icon using a graphics application and define it to be a resource in the driver’s resource definition (.rc) file. For more information about creating icons, see the Win32 SDK.

When the driver receives an ACMDM_DRIVER_DETAILS message, it should call LoadIcon (described in the Win32 SDK) to load the icon, and return the icon’s handle in the ACMDRIVERDETAILS structure’s hIcon member. To use the default icon, the driver should return NULL in hIcon.


Providing ACM Support in Device Drivers

Some waveform devices can play and record data that is in a compressed format. For these devices, clients do not have to call ACM functions to convert data streams. However, ACM functions provide one of the means by which clients determine which formats are supported on a system. So even if a particular format is supported by device hardware, the client still might call ACM functions to determine if the format is supported. (The other means by which clients determine if a device supports a compressed format is by specifying the WAVE_FORMAT_DIRECT flag with the WODM_OPEN and WIDM_OPEN audio driver messages.)

If you are designing a user-mode audio driver for a waveform device that supports a format in hardware, your driver’s DriverProc function must support, at a minimum, the following ACM driver messages:

  • ACMDM_DRIVER_DETAILS

  • ACMDM_FORMAT_DETAILS

  • ACMDM_FORMATTAG_DETAILS

  • ACMDM_HARDWARE_WAVE_CAPS_INPUT

  • ACMDM_HARDWARE_WAVE_CAPS_OUTPUT

A user-mode audio driver that supports ACM messages is, in reality, both a device driver and an ACM driver. As such, it must be installed twice ¾ once as an audio device driver and once as an ACM driver. (See Installing Multimedia Drivers and Installing ACM Drivers.)

A driver of this type must be capable of determining when it is being opened as an audio driver and when it is being opened as an ACM driver. An easy way to make this determination is to examine the lParam2 argument to DriverProc when a DRV_OPEN message is received. When the driver is being opened by the ACM, this argument is a pointer to an ACMDRVOPENDESC structure. When the driver is being opened by any other client, such as winmm.dll or a Control Panel applet, the lParam2 argument is NULL.

Following is a possible scenario in which the driver is used as both an ACM driver and an audio driver:

  1. A client calls acmDriverDetails (in the ACM) to send an ACMDM_DRIVER_DETAILS message. The driver sets the specified ACMDRIVERDETAILS structure’s ACMDRIVERDETAILS_SUPPORTF_HARDWARE flag.

  2. The client detects this flag and calls acmMetrics (in the ACM), specifying the ACM_METRIC_HARDWARE_WAVE_OUTPUT flag to obtain a device identifier.

  3. The client uses the obtained device identifier as input to waveOutOpen (in winmm.dll), to open an output stream to a waveform device, and then sends data to the device using waveOutWrite.


Writing Portable ACM Drivers

Audio Compression Managers are provided for Windows NT® and for Windowsâ 95. The interfaces provided by both ACMs are identical. You can write binary-compatible ACM drivers that are portable between Windows NT and Windows 95, by obeying the following rules:

  • Do not call API functions that are only available under Windows NT. (Almost all functions described in the Win32 SDK are available under both Windows NT and Windows 95.)

  • Do not compile with the UNICODE constant defined.

The second rule requires further discussion. Windows NT provides Unicode versions of all Win32 API functions, but Windows 95 does not (with a few exceptions). However, ACM drivers must always pass Unicode strings to the ACM, whether the ACM is running under Windows NT or Windows 95. To get around this conflict, your ACM driver can include a copy of the LoadStringCodec function that is defined in the sample ACM drivers.

The LoadStringCodec function loads a string resource and converts the string to Unicode, even if the source code was not compiled with the UNICODE constant defined. The converted string can then be passed to the ACM (in an ACMDRIVERDETAILS structure, for example). You can also convert between Unicode and ANSI strings by calling the MultiByteToWideChar and WideCharToMultiByte functions that are described in the Win32 SDK.

The sample ACM drivers are written to be binary-compatible with Windows NT and Windows 95.


Guidelines for Writing ACM Drivers

Use the following guidelines when writing an ACM driver:

  • Allocate driver instance data when the driver receives a DRV_OPEN message, as explained in the description of DRV_OPEN.

  • Allocate stream instance data when the driver receives an ACMDM_STREAM_OPEN message, as explained in the description of ACMDM_STREAM_OPEN.

  • Do not use global variables. Defining a single DWORD of global data in a DLL allocates 4K of memory in every process that uses the ACM, because your driver is mapped into the address space of each process, and global data space is not shared. Instead of using global data, dynamically allocate local storage space for each driver instance and each stream instance, as needed.

  • Do not link to crtdll.dll, the dynamic-link version of the C runtime library. This DLL cannot be loaded into all contexts. As a result, your driver will not work correctly for system sounds that are played by means of the MessageBeep function. Use Win32 API functions instead of C library functions, or link to a static C runtime library (libc.lib or libcmt.lib).

  • Be careful when calling Win32 functions. If your driver is used in conjunction with playing system sounds, it might get loaded into a context in which these functions fail. This warning pertains to any Win32 function that requires an instance handle, and possibly other functions. Notice, for example, that if the samples call LoadString or LoadIcon, they do not test for error return values. If you strictly follow the model provided by the sample drivers, you will not have this problem.


ACM Driver Reference

This section describes the messages and structures used by ACM drivers.


Messages, ACM Drivers

This section describes the messages received by ACM drivers. The messages are listed in alphabetic order. They are defined in msacmdrv.h.


ACMDM_DRIVER_ABOUT

The ACMDM_DRIVER_ABOUT message requests an ACM driver to display its About dialog box.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_DRIVER_ABOUT
lParam1
Contains a validated window handle, which the driver should use to specify the parent window for the About dialog box. The value can also be -1L (See the Comments section below).
lParam2
Not used.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. If the driver does not provide an about box, it should return MMSYSERR_NOTSUPPORTED.

Comments

A client sends the ACMDM_DRIVER_ABOUT message by calling the driver’s DriverProc entry point, passing the specified parameters.

Typically, this message is sent by the Control Panel’s Multimedia applet.

An ACM driver does not have to provide an About dialog box. If it does not, it should always return MMSYSERR_NOTSUPPORTED in response to this message. The ACM provides a default About dialog box, which is displayed if the driver does not provide one.

If the driver does provide an About box, it should display it when it receives this message.

If lParam1 is -1L, the driver should not display its About dialog box. It should just return MMSYSERR_NOERROR if it provides an About box, and MMSYSERR_NOTSUPPORTED if it does not.

For more information about custom About boxes, see Providing a Custom About Box.


ACMDM_DRIVER_DETAILS

The ACMDM_DRIVER_DETAILS message requests an ACM driver to return detailed information about itself.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_DRIVER_DETAILS
lParam1
Pointer to an ACMDRIVERDETAILS structure. (ACMDRIVERDETAILS is defined in msacm.h and described in the Win32 SDK.)
lParam2
Not used.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

A client sends the ACMDM_DRIVER_DETAILS message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls acmDriverDetails, which is described in the Win32 SDK.

Before the driver’s DriverProc function is called, the ACM verifies that lParam1 contains a valid pointer and that the ACMDRIVERDETAILS structure’s cbStruct member contains a size value of at least four.

The driver should fill in the ACMDRIVERDETAILS structure members, up to the number of bytes specified by the cbStruct member.

ACM drivers must support this message.

For more information about custom icons, see Providing a Custom Icon.


ACMDM_DRIVER_NOTIFY

The ACMDM_DRIVER_NOTIFY message notifies an ACM driver of changes to other ACM drivers.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_DRIVER_NOTIFY
lParam1
Not used.
lParam2
Not used.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

The ACM sends the ACMDM_DRIVER_NOTIFY message by calling the driver’s DriverProc entry point, passing the specified parameters, each time a client calls the acmDriverAdd, acmDriverRemove, or acmDriverPriority function. (These functions are described in the Win32 SDK.)

ACM driver support for this message is optional. If the driver supports the message, it can call ACM API functions, such as acmEnumDrivers and acmMetrics, to determine which drivers have been added, removed, enabled, disabled, or had their priority changed.


ACMDM_FILTER_DETAILS

The ACMDM_FILTER_DETAILS message requests an ACM driver to return information about a filter associated with a specified filter tag.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_FILTER_DETAILS
lParam1
Pointer to an ACMFILTERDETAILS structure. (ACMFILTERDETAILS is defined in msacm.h and described in the Win32 SDK.)
lParam2
Contains one of the following flags, specified by the fdwDetails parameter of the acmFilterDetails function (described in the Win32 SDK):

Flag

Meaning

ACM_FILTERDETAILSF_INDEX

Indicates the dwFilterIndex member of the ACMFILTERDETAILS structure contains a filter index. The valid index range is from zero to one less than the cStandardFilters member returned in the ACMFILTERTAGDETAILS structure for the filter tag. (See ACMDM_FILTERTAG_DETAILS.)

ACM_FILTERDETAILSF_FILTER

Indicates the client has filled in the WAVEFILTER structure associated with the ACMFILTERDETAILS structure.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. Possible error codes include:
Error Code | Meaning
MMSYSERR_NOTSUPPORTED | The driver does not support filter operations or the specified query operation.
ACMERR_NOTPOSSIBLE | The input parameter values do not represent a valid filter or filter tag.

Comments

A client sends the ACMDM_FILTER_DETAILS message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls acmFilterDetails, which is described in the Win32 SDK.

An ACM driver that provides filters must support this message.

The client can do either of the following:

  • Specify a filter index, in order to obtain a description of the filter associated with the index.

  • Specify a filter description, in order to validate the filter and obtain the filter’s string description.

The client specifies the filter tag in the ACMFILTERDETAILS structure’s dwFilterTag member. The driver returns information for a particular filter belonging to the filter tag, as follows:

  • If the ACM_FILTERDETAILSF_INDEX flag is set, the client has specified an index value in the ACMFILTERDETAILS structure’s dwFilterIndex member. The driver fills in the WAVEFILTER structure for the filter associated with the specified index value. It also fills in the ACMFILTERDETAILS structure’s szFilter, fdwSupport, and cbStruct members.

  • If the ACM_FILTERDETAILSF_FILTER flag is set, the client has filled in the WAVEFILTER structure. The driver validates the structure contents and, if the contents are valid, fills in the ACMFILTERDETAILS structure’s szFilter, fdwSupport, and cbStruct members.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMFILTERDETAILS structure and its associated WAVEFILTER structure are readable and writeable.

  • The size of the ACMFILTERDETAILS structure, contained in its cbStruct member, is at least the structure’s defined size. (The structure’s size can be larger than its defined size, to allow for a longer szFilter member or to allow newer, larger structure definitions to be used within drivers under development.)

  • The size of the WAVEFILTER structure pointed to by the ACMFILTERDETAILS structure’s pwfltr member is at least as large as the generic WAVEFILTER structure’s defined size. (See The WAVEFILTER Structure below.)

  • The ACMFILTERDETAILS structure’s fdwSupport member contains zero.

  • The lParam2 parameter contains a valid flag value.

Before returning, the driver must set the ACMFILTERDETAILS structure’s cbStruct member to the actual number of bytes returned. The value returned in cbStruct must not be greater than the value received from the client.

For more information about filter tags and filter structures, see Format Tags and Filter Tags and Defining Format Structures and Filter Structures.

The WAVEFILTER Structure

The WAVEFILTER structure is a generic structure for describing a filter. Generally, you will extend this structure for your specific filter type, as has been done in the Microsoft Audio Filter. (For examples, see VOLUMEWAVEFILTER and ECHOWAVEFILTER in mmreg.h.) When a client sends an ACMDM_FILTER_DETAILS message, it specifies the address of a structure that you have defined for the specified filter type. This structure is typically larger than the generic WAVEFILTER structure.


ACMDM_FILTERTAG_DETAILS

The ACMDM_FILTERTAG_DETAILS message requests an ACM driver to return information about a filter tag.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_FILTERTAG_DETAILS
lParam1
Pointer to an ACMFILTERTAGDETAILS structure. (ACMFILTERTAGDETAILS is defined in msacm.h and described in the Win32 SDK.)
lParam2
Contains one of the following flags specified by the fdwDetails parameter of the acmFilterTagDetails function (described in the Win32 SDK):

Flag

Meaning

ACM_FILTERTAGDETAILSF_
INDEX

Indicates the dwFilterTagIndex member of the ACMFILTERTAGDETAILS structure contains a filter tag index. The valid index range is from zero to one less than the cFilterTags member returned in the driver’s ACMDRIVERDETAILS structure. (See ACMDM_DRIVER_DETAILS.)

The driver should return details for the filter tag associated with the index.

ACM_FILTERTAGDETAILSF_
FILTERTAG

Indicates the dwFilterTag member of the ACMFILTERTAGDETAILS structure contains a filter tag.

The driver should return details for the specified filter tag.

ACM_FILTERTAGDETAILSF_
LARGESTSIZE

Indicates the driver should return details for the filter tag having the largest filter. The dwFilterTag member of ACMFILTERTAGDETAILS can contain a filter tag or WAVE_FILTER_UNKNOWN.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. Possible error codes include:
Error Code | Meaning
MMSYSERR_NOTSUPPORTED | The driver does not support filter operations or the specified query operation.
ACMERR_NOTPOSSIBLE | The input parameter values don’t represent a valid filter or filter tag.

Comments

A client sends the ACMDM_FILTERTAG_DETAILS message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls acmFilterTagDetails, which is described in the Win32 SDK.

An ACM driver that provides filters must support this message.

The client specifies the filter tag in the ACMFILTERTAGDETAILS structure’s dwFilterTag member. The driver returns information for a particular tag, as follows:

  • If the ACM_FILTERDETAILSF_INDEX flag is set, the client has specified an index value in the ACMFILTERTAGDETAILS structure’s dwFilterTagIndex member. The driver fills in the ACMFILTERTAGDETAILS structure for the filter tag associated with the specified index value.

  • If the ACM_FILTERTAGDETAILSF_FILTERTAG flag is set, the client has specified a filter tag in the ACMFILTERTAGDETAILS structure’s dwFilterTag member. The driver fills in the ACMFILTERTAGDETAILS structure for the specified filter tag.

  • If the ACM_FILTERTAGDETAILSF_LARGESTSIZE flag is set, the driver does one of two things:

    1. If dwFilterTag contains WAVE_FILTER_UNKNOWN, the driver fills in the ACMFILTERTAGDETAILS structure for the filter tag having a filter with the largest filter structure size.

    2. If dwFilterTag contains a filter tag, the driver fills in the ACMFILTERTAGDETAILS structure for that filter tag, describing the filter with the largest structure size belonging to the specified tag.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMFILTERTAGDETAILS structure is readable and writeable.

  • The size of the ACMFILTERTAGDETAILS structure, contained in its cbStruct member, is at least the structure’s defined size. (The structure’s size can be larger than its defined size, to allow for a longer szFilterTag member or to allow newer, larger structure definitions to be used within drivers under development.)

  • The ACMFILTERTAGDETAILS structure’s fdwSupport member contains zero.

  • The lParam2 parameter contains a valid flag value.

Before returning, the driver must set the ACMFILTERTAGDETAILS structure’s cbStruct member to the actual number of bytes returned. The value returned in cbStruct must not be greater than the value received.

For more information about filter tags, see Format Tags and Filter Tags.


ACMDM_FORMAT_DETAILS

The ACMDM_FORMAT_DETAILS message requests an ACM driver to return information about a format associated with a specified format tag.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_FORMAT_DETAILS
lParam1
Pointer to an ACMFORMATDETAILS structure. (ACMFORMATDETAILS is defined in msacm.h and described in the Win32 SDK.)
lParam2
Contains one of the following flags, specified by the fdwDetails parameter of the acmFormatDetails function, which is described in the Win32 SDK:

Flag

Meaning

ACM_FORMATDETAILSF_INDEX

Indicates the dwFormatIndex member of the ACMFORMATDETAILS structure contains a format index. The valid index range is from zero to one less than the cStandardFormats member returned in the ACMFORMATTAGDETAILS structure for the format tag. (See ACMDM_FORMATTAG_DETAILS.)

ACM_FORMATDETAILSF_FORMAT

Indicates the client has filled in the WAVEFORMATEX structure associated with the ACMFORMATDETAILS structure.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. Possible error codes include:
Error Code | Meaning
MMSYSERR_NOTSUPPORTED | The driver does not support the specified query operation.
ACMERR_NOTPOSSIBLE | The input parameter values do not represent a valid format or format tag.

Comments

A client sends the ACMDM_FORMAT_DETAILS message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmFormatDetails function, which is described in the Win32 SDK.

All ACM drivers must support this message.

The client can do either of the following:

  • Specify a format index, in order to obtain a description of the format associated with the index.

  • Specify a format description, in order to validate the format and obtain the format’s string description.

The client specifies the format tag in the ACMFORMATDETAILS structure’s dwFormatTag member. The driver returns information for a particular format belonging to the format tag, as follows:

  • If the ACM_FORMATDETAILSF_INDEX flag is set, the client has specified an index value in the ACMFORMATDETAILS structure’s dwFormatIndex member. The driver fills in the WAVEFORMATEX structure for the format associated with the specified index value. It also fills in the ACMFORMATDETAILS structure’s szFormat, fdwSupport, and cbStruct members.

  • If the ACM_FORMATDETAILSF_FORMAT flag is set, the client has filled in the WAVEFORMATEX structure. The driver validates the structure contents and, if the contents are valid, fills in the ACMFORMATDETAILS structure’s szFormat, fdwSupport, and cbStruct members.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMFORMATDETAILS structure and its associated WAVEFORMATEX structure are readable and writeable.

  • The size of the ACMFORMATDETAILS structure (contained in its cbStruct member) is at least the structure’s defined size. (The structure’s size can be larger than its defined size, to allow for a longer szFormat member or to allow newer, larger structure definitions to be used within drivers under development.)

  • The size of the WAVEFORMATEX structure associated with the ACMFORMATDETAILS structure’s pwfx member is at least as large as the generic WAVEFORMATEX structure’s defined size. (See The WAVEFORMATEX Structure below.)

  • The ACMFORMATDETAILS structure’s fdwSupport member contains zero.

  • The lParam2 parameter contains a valid flag value.

Before returning, the driver must set the ACMFORMATDETAILS structure’s cbStruct member to the actual number of bytes returned. The value returned in cbStruct must not be greater than the value received.

The WAVEFORMATEX Structure

The WAVEFORMATEX structure is a generic structure for describing a waveform format. Generally, you will use this structure as a basis for defining structures for your specific format types, as has been done in the IMA ADPCM Audio Codec. (For an example, see IMAADPCMWAVEFORMAT in mmreg.h.) When a client sends an ACMDM_FORMAT_DETAILS message, it specifies the address of a structure that you have defined for the specified format type. This structure is typically larger than the generic WAVEFORMATEX structure.

For more information about format tags and format structures, see Format Tags and Filter Tags and Defining Format Structures and Filter Structures.

Returning a Description String

The WAVEFORMATEX structure’s szFormat member is used for returning a format description string. If an ACM driver returns a zero-length string in szFormat, the ACM creates an internationalized description string for the format. This string includes the format’s speed (in Hz), bit depth, and channel setting (mono or stereo), based on the contents of the nSamplesPerSec, wBitsPerSample, and nChannels members of the WAVEFORMATEX structure. If wBitsPerSample contains zero, the ACM does not include the bit depth in the description string. You can provide your own description string and return it in szFormat, but allowing ACM to generate an internationalized string is preferred.


ACMDM_FORMAT_SUGGEST

The ACMDM_FORMAT_SUGGEST message requests an ACM driver to suggest a destination format for a conversion, given a specified source format.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_FORMAT_SUGGEST
lParam1
Pointer to an ACMDRVFORMATSUGGEST structure. (ACMDRVFORMATSUGGEST is defined in msacmdrv.h.)
lParam2
Not used.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. Possible error codes include:
Error Code | Meaning
MMSYSERR_NOTSUPPORTED | The driver does not support format suggestion operations.
ACMERR_NOTPOSSIBLE | The driver cannot suggest a destination format, based on the specified source format and restriction flags.

Comments

A client sends the ACMDM_FORMAT_SUGGEST message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmFormatSuggest function, which is described in the Win32 SDK.

All ACM drivers that provide stream conversions must support this message.

The ACMDRVFORMATSUGGEST structure contains pointers to two WAVEFORMATEX structures. One of these structures describes the client-specified source format. The other structure is used by the driver to return a suggested destination format. The client might specify values for some of the destination structure members, in order to restrict the possible suggestions. For more information, see the description of ACMDRVFORMATSUGGEST.

Given the specified source format and destination restrictions (if any), the driver determines if it can provide a conversion from the specified source format to some destination format. If it can, it returns a description of that format in the destination WAVEFORMATEX structure.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The flag values contained in the ACMDRVFORMATSUGGEST structure are valid.

  • The WAVEFORMATEX structure containing the source format description is readable.

  • The WAVEFORMATEX structure specified for receiving the suggested destination format description is writeable.

  • The destination WAVEFORMATEX structure’s size, contained in the ACMDRVFORMATSUGGEST structure’s cbwfxDst member, is large enough to receive a format structure for an appropriate destination format.


ACMDM_FORMATTAG_DETAILS

The ACMDM_FORMATTAG_DETAILS message requests an ACM driver to return information about a format tag.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_FORMATTAG_DETAILS
lParam1
Pointer to an ACMFORMATTAGDETAILS structure. (ACMFORMATTAGDETAILS is defined in msacm.h and described in the Win32 SDK.)
lParam2
Contains one of the following flags, specified by the fdwDetails parameter of the acmFormatTagDetails function, which is described in the Win32 SDK:

Flag

Meaning

ACM_FORMATTAGDETAILSF_
INDEX

Indicates the dwFormatTagIndex member of the ACMFORMATTAGDETAILS structure contains a format tag index. The valid index range is from zero to one less than the cFormatTags member returned in the driver’s ACMDRIVERDETAILS structure. (See ACMDM_DRIVER_DETAILS.)

The driver should return details for the format tag associated with the index.

ACM_FORMATTAGDETAILSF_
FORMATTAG

Indicates the dwFormatTag member of the ACMFORMATTAGDETAILS structure contains a format tag.

The driver should return details for the specified format tag.

ACM_FORMATTAGDETAILSF_
LARGESTSIZE

Indicates the driver should return details for the format tag having the largest format. The dwFormatTag member of ACMFORMATTAGDETAILS can contain a format tag or WAVE_FORMAT_UNKNOWN.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. Possible error codes include:
Error Code | Meaning
MMSYSERR_NOTSUPPORTED | The driver does not support the specified query operation.
ACMERR_NOTPOSSIBLE | The input parameter values do not represent a valid format or format tag.

Comments

A client sends the ACMDM_FORMATTAG_DETAILS message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmFormatTagDetails function, which is described the Win32 SDK.

All ACM drivers must support this message.

The client specifies the format tag in the ACMFORMATTAGDETAILS structure’s dwFormatTag member. The driver returns information for a particular tag, as follows:

  • If the ACM_FORMATDETAILSF_INDEX flag is set, the client has specified an index value in the ACMFORMATTAGDETAILS structure’s dwFormatTagIndex member. The driver fills in the ACMFORMATTAGDETAILS structure for the format tag associated with the specified index value.

  • If the ACM_FORMATTAGDETAILSF_FORMATTAG flag is set, the client has specified a format tag in the ACMFORMATTAGDETAILS structure’s dwFormatTag member. The driver fills in the ACMFORMATTAGDETAILS structure for the specified format tag.

  • If the ACM_FORMATTAGDETAILSF_LARGESTSIZE flag is set, the driver does one of two things:

    1. If dwFormatTag contains WAVE_FORMAT_UNKNOWN, the driver fills in the ACMFORMATTAGDETAILS structure for the format tag that has a format with the largest format structure size.

    2. If dwFormatTag contains a format tag, the driver fills in the ACMFORMATTAGDETAILS structure for that format tag, describing the format with the largest structure size belonging to the specified tag.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMFORMATTAGDETAILS structure is readable and writeable.

  • The size of the ACMFORMATTAGDETAILS structure (contained in its cbStruct member) is at least the structure’s defined size. (The structure’s size can be larger than its defined size, to allow for a longer szFormatTag member or to allow newer, larger structure definitions to be used within drivers under development.)

  • The ACMFORMATTAGDETAILS structure’s fdwSupport member contains zero.

  • The lParam2 parameter contains a valid flag value.

If the format tag is WAVE_FORMAT_PCM, then the driver should return a zero-length string in szFormatTag. The ACM provides a description string for this format.

Before returning, the driver must set the ACMFORMATTAGDETAILS structure’s cbStruct member to the actual number of bytes returned. The value returned in cbStruct must not be greater than the value received.

For more information about format tags, see Format Tags and Filter Tags.


ACMDM_HARDWARE_WAVE_CAPS_INPUT

The ACMDM_HARDWARE_WAVE_CAPS_INPUT message requests an ACM driver to return hardware capabilities for a waveform input device.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_HARDWARE_WAVE_CAPS_INPUT
lParam1
Pointer to a WAVEINCAPS structure. (WAVEINCAPS is defined in mmsystem.h and described in the Win32 SDK.)
lParam2
Size of the WAVEINCAPS structure pointed to by lParam1.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. If the driver does not support waveform hardware, it should return MMSYSERR_NOTSUPPORTED.

Comments

The ACM sends the ACMDM_HARDWARE_WAVE_CAPS_INPUT message by calling the driver’s DriverProc entry point, passing the specified parameters, each time a client calls the acmMetrics function with the ACM_METRIC_HARDWARE_WAVE_INPUT index argument. (The acmMetrics function is described in the Win32 SDK.)

The driver receives the address of a WAVEINCAPS structure. The driver must fill in the structure.

Only ACM drivers that provide access to waveform input hardware need to support this message. If your driver supports the message, it must return ACMDRIVERDETAILS_SUPPORTF_HARDWARE in the ACMDRIVERDETAILS structure provided with the ACMDM_DRIVER_DETAILS message. For more information, see Providing ACM Support in Device Drivers.


ACMDM_HARDWARE_WAVE_CAPS_OUTPUT

The ACMDM_HARDWARE_WAVE_CAPS_OUTPUT message requests an ACM driver to return hardware capabilities for a waveform output device.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_HARDWARE_WAVE_CAPS_OUTPUT
lParam1
Pointer to a WAVEOUTCAPS structure. (WAVEOUTCAPS is defined in mmsystem.h and described in the Win32 SDK.)
lParam2
Size of the WAVEOUTCAPS structure pointed to by lParam1.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. If the driver does not support waveform hardware, it should return MMSYSERR_NOTSUPPORTED.

Comments

The ACM sends the ACMDM_HARDWARE_WAVE_CAPS_OUTPUT message by calling the driver’s DriverProc entry point, passing the specified parameters, each time a client calls the acmMetrics function with the ACM_METRIC_HARDWARE_WAVE_OUTPUT index argument. (The acmMetrics function is described in the Win32 SDK.)

The driver receives the address of a WAVEOUTCAPS structure. The driver must fill in the structure.

Only ACM drivers that provide access to waveform output hardware need to support this message. If your driver supports the message, it must return ACMDRIVERDETAILS_SUPPORTF_HARDWARE in the ACMDRIVERDETAILS structure provided with the ACMDM_DRIVER_DETAILS message. For more information, see Providing ACM Support in Device Drivers.


ACMDM_STREAM_CLOSE

The ACMDM_STREAM_CLOSE message requests an ACM driver to close a conversion stream that was opened with an ACMDM_STREAM_OPEN message.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_CLOSE
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Not used.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. An asynchronous driver might have to return ACMERR_BUSY if a conversion operation has not completed.

Comments

A client sends the ACMDM_STREAM_CLOSE message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamClose function, which is described in the Win32 SDK.

All ACM drivers that provide stream conversions must support this message. For more information about stream conversions, see Converting Data Streams.

If the driver supports asynchronous operations, and if the client has specified the ACM_STREAMOPENF_ASYNC flag (contained in the ACMDRVSTREAMINSTANCE structure’s fdwOpen member), then the driver should send the client an MM_ACM_CLOSE callback message, by calling the DriverCallback function, after the operation completes.


ACMDM_STREAM_CONVERT

The ACMDM_STREAM_CONVERT message requests a ACM driver to perform a conversion operation on a specified conversion stream.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_CONVERT
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Pointer to an ACMDRVSTREAMHEADER structure.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

A client sends the ACMDM_STREAM_CONVERT message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamConvert function, which is described in the Win32 SDK.

All ACM drivers that provide stream conversions must support this message. For more information about stream conversions, see Converting Data Streams.

The ACMDRVSTREAMINSTANCE structure received with this message is the same structure that was received with a previous ACMDM_STREAM_OPEN message. The driver does not need to validate the structure’s contents again.

The ACMDRVSTREAMHEADER structure identifies the source and destination data buffers. The source buffer contains the data to be converted. The driver places converted data into the destination buffer.

The driver must check the flags in the ACMDRVSTREAMHEADER structure’s fdwConvert member. These flags indicate how converted data should be returned.

Because stream conversions are time-critical operations, ACMDM_STREAM_CONVERT messages must be processed efficiently. The driver should perform as much processing as possible in response to the ACMDM_STREAM_OPEN message.

If the driver supports asynchronous operations, and if the client has specified the ACM_STREAMOPENF_ASYNC flag (contained in the ACMDRVSTREAMINSTANCE structure’s fdwOpen member), then the driver must do the following when it has finished converting the data in the source buffer:

  • Set the ACMDRVSTREAMHEADER structure’s ACMSTREAMHEADER_STATUSF_DONE flag.

  • Send the client an MM_ACM_DONE callback message, by calling the DriverCallback function.

Asynchronous drivers can make use of the ACMDRVSTREAMHEADER structure’s ACMSTREAMHEADER_STATUSF_INQUEUE flag, along with the structure’s padshNext member, to maintain a conversion queue of stream header structures.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMDRVSTREAMHEADER structure is readable and writeable, and of the proper size.

  • The ACMDRVSTREAMHEADER structure’s fdwConvert member contains valid flag values.

  • The ACMDRVSTREAMHEADER structure’s buffers have been prepared (see ACMDM_STREAM_PREPARE), and the specified buffer sizes are not larger than their prepared sizes.

  • The ACMDRVSTREAMHEADER structure is not currently in an asynchronous driver’s conversion queue. (That is, the structure’s ACMSTREAMHEADER_STATUSF_INQUEUE flag is not set.)


ACMDM_STREAM_OPEN

The ACMDM_STREAM_OPEN message requests an ACM driver to either open a conversion stream or indicate whether the specified conversion is supported.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_OPEN
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Not used.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h. If a specified conversion is not supported, the driver should return ACMERR_NOTPOSSIBLE.

Comments

A client sends the ACMDM_STREAM_OPEN message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamOpen function, which is described in the Win32 SDK.

All ACM drivers that provide stream conversions must support this message. For more information about stream conversions, see Converting Data Streams.

If the client has specified the ACM_STREAMOPENF_QUERY flag (contained in the ACMDRVSTREAMINSTANCE structure’s fdwOpen member), then the driver should not open a stream instance. It should return MMSYSERR_NOERROR if the conversion is possible, and MMSYSERR_NOTPOSSIBLE otherwise.

When a driver receives an ACMDM_STREAM_OPEN message, it should first determine if it can perform the specified conversion. If it can, then it should perform instance initialization operations for the stream, such as determining which conversion routines to use and allocating instance-specific resources.

Store stream instance data in a local, dynamically allocated structure. Store a pointer to the structure in the ACMDRVSTREAMINSTANCE structure’s dwDriver member.

If the driver supports asynchronous operations, and if the client has specified the ACM_STREAMOPENF_ASYNC flag (contained in the ACMDRVSTREAMINSTANCE structure’s fdwOpen member), then the driver should send the client an MM_ACM_OPEN callback message, by calling the DriverCallback function, after the operation completes.

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMDRVSTREAMINSTANCE structure’s fdwOpen member contains valid flag values.

  • The source and destination WAVEFORMATEX structures are readable.

  • If a WAVEFILTER structure is specified, it is readable and the source and destination format structures contain identical information.

  • If the client has specified different source and destination format tags, then the driver has declared itself to be a codec by setting ACMDRIVERDETAILS_SUPPORTF_CODEC in the ACMDRIVERDETAILS structure’s fdwSupport member. (See ACMDM_DRIVER_DETAILS.)

  • If the client has specified source and destination formats associated with a single format tag, then the driver has declared itself to be a converter by setting ACMDRIVERDETAILS_SUPPORTF_CONVERTER in the ACMDRIVERDETAILS structure’s fdwSupport member. (See ACMDM_DRIVER_DETAILS.)

  • If the client has specified a filter operation, then the driver has declared itself to be a filter by setting ACMDRIVERDETAILS_SUPPORTF_FILTER in the ACMDRIVERDETAILS structure’s fdwSupport member. (See ACMDM_DRIVER_DETAILS.)

If the client has specified the ACM_STREAMOPENF_NONREALTIME flag (contained in the ACMDRVSTREAMINSTANCE structure’s fdwOpen member), then the driver can perform the conversion without time constraints. However, if this flag is not specified, and the driver cannot perform the conversion in real time, then it should return ACMERR_NOTPOSSIBLE. For more information, see Real Time Conversions.


ACMDM_STREAM_PREPARE

The ACMDM_STREAM_PREPARE message requests an ACM driver to prepare the buffers associated with an ACMDRVSTREAMHEADER structure for use.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_PREPARE
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Pointer to an ACMDRVSTREAMHEADER structure.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

A client sends the ACMDM_STREAM_PREPARE message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamPrepareHeader function, which is described in the Win32 SDK.

Support for this message is optional. If a driver supports ACMDM_STREAM_PREPARE, it must support ACMDM_STREAM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, the ACM prepares the buffers for use. For most drivers, this behavior is sufficient. If the driver does perform buffer preparation, it should return MMSYSERR_NOERROR. In either case, the ACM sets ACMSTREAMHEADER_STATUSF_PREPARED in the ACMDRVSTREAMHEADER structure’s fdwStatus member. The driver never modifies this flag. (If you want both your driver and the ACM to perform buffer preparation operations, the driver should return MMSYSERR_NOTSUPPORTED after performing its preparation activity. The ACM can then also perform its preparation operation.)

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMDRVSTREAMHEADER structure is readable and writeable.

  • The ACMDRVSTREAMHEADER structure’s cbStruct member contains a size value that is at least as large as the structure’s defined size.

  • The specified buffers have not already been prepared.

For more information about the use of ACMDM_STREAM_PREPARE, see Converting Data Streams.


ACMDM_STREAM_RESET

The ACMDM_STREAM_RESET message requests an ACM driver to stop conversion operations for the specified stream.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_RESET
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Contains the fdwReset argument to the acmStreamReset function.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

A client sends the ACMDM_STREAM_RESET message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamReset function, which is described in the Win32 SDK.

Only asynchronous drivers receive this message. If a client calls acmStreamReset for a synchronous driver, the ACM returns MMSYSERR_NOERROR without calling the driver.

When an asynchronous driver receives this message, it should set the ACMSTREAMHEADER_STATUSF_DONE flag, and clear the ACMSTREAMHEADER_STATUSF_INQUEUE flag, in every ACMDRVSTREAMHEADER structure contained in its conversion queue.

For more information about stream conversions, see Converting Data Streams.


ACMDM_STREAM_SIZE

The ACMDM_STREAM_SIZE message requests an ACM driver to return the size required for a source (or destination) buffer, given a specified destination (or source) buffer size along with source and destination format descriptions.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_SIZE
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Pointer to an ACMDRVSTREAMSIZE structure.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

A client sends the ACMDM_STREAM_SIZE message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamSize function, which is described in the Win32 SDK.

All ACM drivers that provide stream conversions must support this message. For more information about stream conversions, see Converting Data Streams.

Based on the input arguments, the driver must answer one of the following questions:

  • Given a specified source buffer size, how large does a destination buffer need to be in order to hold all of the converted data?

  • Given a specified destination buffer size, what is the largest amount of source data that can specified without overflowing the destination buffer?

The ACMDRVSTREAMINSTANCE structure received with this message is the same structure that was received with a previous ACMDM_STREAM_OPEN message. The driver does not need to validate the structure’s contents again.

The driver examines the ACMDRVSTREAMSIZE structure to determine which buffer (source or destination) the client has supplied. The ACMDRVSTREAMINSTANCE structure contains structures that describe the source and destination formats, and, possibly, a filter specification. The driver uses this information to determine the size of the requested buffer.

If the driver returns a buffer length of zero, the ACM provides an error return code of ACMERR_NOTPOSSIBLE to acmStreamSize.


ACMDM_STREAM_UNPREPARE

The ACMDM_STREAM_UNPREPARE message requests an ACM driver to clear the preparation of the buffers associated with an ACMDRVSTREAMHEADER structure.

Parameters

dwDriverID
Driver instance identifier. This is the value returned by the driver in response to the DRV_OPEN message.
hDriver
Driver handle.
uMsg
ACMDM_STREAM_UNPREPARE
lParam1
Pointer to an ACMDRVSTREAMINSTANCE structure.
lParam2
Pointer to an ACMDRVSTREAMHEADER structure.

Return Value

The driver should return MMSYSERR_NOERROR if the operation succeeds. Otherwise it should return one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.

Comments

A client sends the ACMDM_STREAM_UNPREPARE message by calling the driver’s DriverProc entry point, passing the specified parameters. The ACM sends this message when an application calls the acmStreamUnprepareHeader function, which is described in the Win32 SDK.

Support for this message is optional. If a driver supports ACMDM_STREAM_PREPARE, it must support ACMDM_STREAM_UNPREPARE.

If the driver returns MMSYSERR_NOTSUPPORTED, the ACM clears the buffer preparation. For most drivers, this behavior is sufficient. If the driver does clear buffer preparation, it should return MMSYSERR_NOERROR. In either case, the ACM clears ACMSTREAMHEADER_STATUSF_PREPARED in the ACMDRVSTREAMHEADER structure’s fdwStatus member. The driver never modifies this flag. (If you want both your driver and the ACM to clear buffer preparations, the driver should return MMSYSERR_NOTSUPPORTED after clearing its preparation. The ACM can then also clear its preparation.)

Before calling the driver’s DriverProc function, the ACM verifies that:

  • The ACMDRVSTREAMHEADER structure is readable and writeable.

  • The ACMDRVSTREAMHEADER structure’s cbStruct member contains a size value that is at least as large as the structure’s defined size.

  • The buffers have been previously prepared.

  • The buffer addresses and sizes match those specified when these buffers were prepared.

  • The buffers are not currently in use by an asynchronous driver, based on the ACMDRVSTREAMHEADER structure’s ACMSTREAMHEADER_STATUSF_INQUEUE flag value.

For more information about the use of ACMDM_STREAM_UNPREPARE, see Converting Data Streams.


MM_ACM_CLOSE

The MM_ACM_CLOSE callback message notifies a client that an asynchronous ACM driver has finished processing an ACMDM_STREAM_CLOSE message.

Parameters

dwMsg
MM_ACM_CLOSE
dwParam1
NULL
dwParam2
NULL

Comments

An asynchronous ACM driver sends an MM_ACM_CLOSE message to its client, by means of a callback, when the driver finishes processing an ACMDM_STREAM_CLOSE message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

For more information about the use of MM_ACM_CLOSE, see Notifying Clients from ACM Drivers and Converting Data Streams.


MM_ACM_DONE

The MM_ACM_DONE callback message notifies a client that an asynchronous ACM driver has finished processing an ACMDM_STREAM_CONVERT message.

Parameters

dwMsg
MM_ACM_DONE
dwParam1
Address of the ACMDRVSTREAMHEADER structure that was received with the ACMDM_STREAM_CONVERT message.
dwParam2
NULL

Comments

An asynchronous ACM driver sends an MM_ACM_DONE message to its client, by means of a callback, when the driver finishes processing an ACMDM_STREAM_CONVERT message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

For more information about the use of MM_ACM_DONE, see Notifying Clients from ACM Drivers and Converting Data Streams.


MM_ACM_OPEN

The MM_ACM_OPEN callback message notifies a client that an asynchronous ACM driver has finished processing an ACMDM_STREAM_OPEN message.

Parameters

dwMsg
MM_ACM_OPEN
dwParam1
NULL
dwParam2
NULL

Comments

An asynchronous ACM driver sends an MM_ACM_OPEN message to its client, by means of a callback, when the driver finishes processing an ACMDM_STREAM_OPEN message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

For more information about the use of MM_ACM_OPEN, see Notifying Clients from ACM Drivers and Converting Data Streams.


Structures, ACM Drivers

This section describes the structures used by ACM drivers. The structures are listed in alphabetical order.


ACMDRVFORMATSUGGEST

typedef struct {
    DWORD cbStruct;
    DWORD fdwSuggest;
    LPWAVEFORMATEX pwfxSrc;
    DWORD cbwfxSrc;
    LPWAVEFORMATEX pwfxDst;
    DWORD cbwfxDst;
} ACMDRVFORMATSUGGEST;

The ACMDRVFORMATSUGGEST structure contains client-specified input arguments to the acmFormatSuggest function. The ACM fills in this structure with the client’s input arguments and passes it to an ACM driver with an ACMDM_FORMAT_SUGGEST message. ACMDRVFORMATSUGGEST is defined in msacmdrv.h.

Members

cbStruct
Size, in bytes, of the ACMDRVFORMATSUGGEST structure.
fdwSuggest
Contains restriction flags that limit the possible destination formats. Can contain any combination of the following flags.

Flag

Meaning

ACM_FORMATSUGGESTF_
WFORMATTAG

The wFormatTag member of the WAVEFORMATEX structure pointed to by pwfxDst contains a format tag. The driver can only suggest a destination format that is associated with the specified format tag.

ACM_FORMATSUGGESTF_
NCHANNELS

The nChannels member of the WAVEFORMATEX structure pointed to by pwfxDst contains a channel value. The driver can only suggest a destination format whose channel value matches the specified value.

ACM_FORMATSUGGESTF_
NSAMPLESPERSEC

The nSamplesPerSec member of the WAVEFORMATEX structure pointed to by pwfxDst contains a sample rate. The driver can only suggest a destination format whose sample rate matches the specified rate.

ACM_FORMATSUGGESTF_
WBITSPERSAMPLE

The nBitsPerSample member of the WAVEFORMATEX structure pointed to by pwfxDst contains a sample size. The driver can only suggest a destination format whose sample size matches the specified size.

pwfxSrc
Pointer to a WAVEFORMATEX structure describing the source format.
cbwfxSrc
Size, in bytes, of the WAVEFORMATEX structure pointed to by pwfxSrc.
pwfxDst
Pointer to a WAVEFORMATEX structure to receive the suggested destination format description. If flags in the fdwSuggest member are set, corresponding members of this structure contain client-specified values. The driver should fill in all empty structure members.
cbwfxDst
Size, in bytes, of the WAVEFORMATEX structure pointed to by pwfxDst. The driver cannot return an extended WAVEFORMATEX structure (see the Comments section) that is larger than this size.

Comments

The WAVEFORMATEX structures specified for source or destination formats might be extended structures defined for particular formats. (For example, see IMAADPCMWAVEFORMAT in mmreg.h.) Check the structure’s wFormatTag member to determine the format type and hence the specific structure being passed.

For more information about format structures, see Defining Format Structures and Filter Structures.


ACMDRVOPENDESC

typedef struct {
    DWORD cbStruct;
    FOURCC fccType;
    FOURCC fccComp;
    DWORD dwVersion;
    DWORD dwFlags;
    DWORD dwError;
    LPCSTR pszSectionName;
    LPCSTR pszAliasName;
    DWORD dnDevNode;
} ACMDRVOPENDESC;

The ACMDRVOPENDESC structure is used by the ACM for passing information to an ACM driver, when the ACM sends the driver a DRV_OPEN message. ACMDRVOPENDESC is defined in msacmdrv.h.

Members

cbStruct
Size, in bytes, of the ACMDRVOPENDESC structure.
fccType
Contains a four-character code identifying the driver type. The driver must compare this value with ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC, which is defined in msacm.h to equal the string “audc”. If the member contents does not match this string, the driver must fail the open request by specifying a DriverProc return value of zero.
fccComp
Not used. Defined to contain a four-character code identifying the driver sub-type.
dwVersion
Contains the ACM’s version number. The version number’s format is 0xAABBCCCC, where AA is the major version number, BB is the minor version number, and CCCC is the build number. This value is also returned by the ACM’s acmGetVersion function, described in the Win32 SDK.
dwFlags
Contains flags. This member is identical to the fdwOpen argument passed to acmDriverOpen. No flags are currently defined.
dwError
Used by drivers to supply an error code. User-mode drivers are restricted to specifying a DriverProc return value of zero for all error types. To provide better error resolution, ACM drivers can specify an error code in this member, if they set the DriverProc function’s return value to zero. The error code can be one of the MMSYSERR error codes defined in mmsystem.h, or one of the ACMERR error codes defined in msacm.h.
pszSectionName
Contains the registry key under which the driver’s alias is stored. For more information, see Installing ACM Drivers.
pszAliasName
Contains the driver’s alias. This is the driver’s “msacm.alias” entry in the registry. For more information, see Installing ACM Drivers.
dnDevNode
Device node ID.

Comments

When the ACM calls an ACM driver’s DriverProc entry point and specifies a DRV_OPEN message, it includes an ACMDRVOPENDESC structure as the lParam2 parameter to DriverProc. The ACM sends a DRV_OPEN message when an application calls the acmDriverOpen function, which is described in the Win32 SDK. For additional information, see DriverProc in ACM Drivers.

ACM drivers do not always receive this structure when they receive a DRV_OPEN message. They only receive the structure if they are called by the ACM. Circumstances in which a driver is not called by the ACM are as follows:

  • The driver might be called by a Control Panel applet for configuration purposes.

  • The driver might be designed to be both an ACM driver and an audio device driver. When such a driver is called by winmm.dll for device operations, it does not receive the structure. (For more information, see Providing ACM Support in Device Drivers.)


ACMDRVSTREAMHEADER

typedef struct {
    DWORD cbStruct;
    DWORD fdwStatus;
    DWORD dwUser;
    LPBYTE pbSrc;
    DWORD cbSrcLength;
    DWORD cbSrcLengthUsed;
    DWORD dwSrcUser;
    LPBYTE pbDst;
    DWORD cbDstLength;
    DWORD cbDstLengthUsed;
    DWORD dwDstUser;
    DWORD fdwConvert;
    LPACMDRVSTREAMHEADER padshNext;
    DWORD fdwDriver;
    DWORD dwDriver;
    DWORD fdwPrepared;
    DWORD dwPrepared;
    LPBYTE pbPreparedSrc;
    DWORD cbPreparedSrcLength;
    LPBYTE pbPreparedDst;
    DWORD cbPreparedDstLength;
} ACMDRVSTREAMHEADER;

The ACMDRVSTREAMHEADER structure describes a source buffer and a destination buffer associated with a conversion stream. The structure is used with the ACMDM_STREAM_PREPARE, ACMDM_STREAM_UNPREPARE, and ACMDM_STREAM_CONVERT messages. ACMDRVSTREAMHEADER is defined in msacmdrv.h.

Members

cbStruct
Contains the size, in bytes, of the ACMDRVSTREAMHEADER structure.
fdwStatus
Contains status flags. The defined flags are as follows:

Flag

Meaning

ACMSTREAMHEADER_
STATUSF_DONE

Indicates that a conversion is complete. For synchronous conversion, the ACM sets this flag when the driver returns from an ACMDM_STREAM_CONVERT message. For asynchronous drivers, the driver sets this flag after the data has been converted. The ACM clears the flag before sending each ACMDM_STREAM_CONVERT message.

ACMSTREAMHEADER_
STATUSF_PREPARED

Indicates that the data buffers have been prepared. This flag is set by the ACM, regardless of whether the driver or the ACM prepared the buffers. See ACMDM_STREAM_PREPARE and ACMDM_STREAM_UNPREPARE.

ACMSTREAMHEADER_
STATUSF_INQUEUE

Used by the driver, during asynchronous conversions, to indicate the structure has been queued for conversion. The driver is responsible for setting and clearing this flag. See ACMDM_STREAM_CONVERT.

dwUser
Contains information supplied by a client for its own use.
pbSrc
Pointer to a source buffer. For an ACMDM_STREAM_CONVERT message, this buffer contains the data to be converted.
cbSrcLength
Length, in bytes, of the source buffer pointed to by pbSrc. For the ACMDM_STREAM_PREPARE and ACMDM_STREAM_UNPREPARE messages, this value represents the maximum source buffer size. For ACMDM_STREAM_CONVERT, this value represents the length of the data in the buffer.
cbSrcLengthUsed
Length, in bytes, of source data that has been converted. This value is set by the driver to indicate the number of bytes in the source buffer that the driver actually converted. The value cannot be greater than the value in cbSrcLength.
dwSrcUser
Contains information supplied by a client for its own use.
pbDst
Pointer to a destination buffer. For an ACMDM_STREAM_CONVERT message, the driver fills this buffer with converted data.
cbDstLength
Length, in bytes, of the destination buffer pointed to by pbDst.
cbDstLengthUsed
Length, in bytes, of destination data that has been converted. This value is set by the driver to indicate the number of converted bytes that it has placed in the destination buffer. The value cannot be greater than the value in cbDstLength. If the conversion fails, the driver must set this value to zero.
dwDstUser
Contains information supplied by a client for its own use.
fdwConvert
Contains one of the following values:
  • For the ACMDM_STREAM_PREPARE message, the value specified as the acmStreamPrepareHeader function’s fdwPrepare argument. (Not used.)

  • For the ACMDM_STREAM_UNPREPARE message, the value specified as the acmStreamUnprepareHeader function’s fdwUnprepare argument. (Not used.)

  • For the ACMDM_STREAM_CONVERT message, the value specified as the acmStreamConvert function’s fdwConvert argument.

For ACMDM_STREAM_CONVERT, the following flags are defined.

Flags

Meaning

ACM_STREAMCONVERTF_
BLOCKALIGN

Indicates that only whole blocks of source data should be converted. The size of a block is obtained from the source format’s WAVEFORMATEX structure (see ACMDRVSTREAMINSTANCE). If the flag is set, the driver should not convert extra bytes that do not make up a whole block. Generally, clients set this flag for all buffers in a conversion stream except the last one.

ACM_STREAMCONVERTF_
START

Indicates that the driver should re-initialize stream instance data, such as predictor coefficients or scale factors, to default starting values. This flag can be specified with the ACM_STREAMCONVERTF_END flag.

ACM_STREAMCONVERTF_
END

Indicates that the driver should return end-of-stream data, such as tail end echo data for an echo filter, in the destination buffer, along with data converted from the source buffer. This flag can be specified with the ACM_STREAMCONVERTF_START flag.

padshNext
Pointer to another ACMDRVSTREAMHEADER structure. An asynchronous driver can use this member for creating a queue of pending conversion requests. The ACM clears the member prior to sending an ACMDM_STREAM_PREPARE or ACMDM_STREAM_CONVERT message.
fdwDriver
Contains stream instance information supplied by the driver for its own use. This member is intended for storing driver-defined flags, but you can use it for any purpose you wish. The ACM clears this member prior to sending an ACMDM_STREAM_PREPARE message. Otherwise its value is preserved from one message to the next.
dwDriver
Contains stream instance information supplied by the driver for its own use. You can use this member for any purpose you wish. The ACM clears this member prior to sending an ACMDM_STREAM_PREPARE message. Otherwise its value is preserved from one message to the next.
fdwPrepared
Used by ACM only. Contains the fdwPrepared argument to the acmStreamPrepareHeader function.
dwPrepared
Used by ACM only. Contains the has argument to the acmStreamPrepareHeader function.
pbPreparedSrc
Used by ACM only. Contains the address of source the buffer supplied with the acmStreamPrepareHeader function.
cbPreparedSrcLength
Used by ACM only. Contains the length of the source buffer supplied with the acmStreamPrepareHeader function.
pbPreparedDst
Used by ACM only. Contains the address of the destination buffer supplied with the acmStreamPrepareHeader function.
cbPreparedDstLength
Used by ACM only. Contains the length of the destination buffer supplied with the acmStreamPrepareHeader function.


ACMDRVSTREAMINSTANCE

typedef struct {
    DWORD cbStruct;
    LPWAVEFORMATEX pwfxSrc;
    LPWAVEFORMATEX pwfxDst;
    LPWAVEFILTER pwfltr;
    DWORD dwCallback;
    DWORD dwInstance;
    DWORD fdwOpen;
    DWORD fdwDriver;
    DWORD dwDriver;
    HACMSTREAM has;
} ACMDRVSTREAMINSTANCE;

The ACMDRVSTREAMINSTANCE structure describes an instance of a conversion stream. ACMDRVSTREAMINSTANCE is defined in msacmdrv.h.

Members

cbStruct
Contains the size, in bytes, of the ACMDRVSTREAMINSTANCE structure.
pwfxSrc
Pointer to a WAVEFORMATEX structure that defines the source format for a conversion stream. (WAVEFORMATEX is described in the Win32 SDK.)
pwfxDst
Pointer to a WAVEFORMATEX structure that defines the destination format for a conversion stream.
pwfltr
Pointer to an optional WAVEFILTER structure that defines a filter to be used on a conversion stream. This member is NULL if the application has not specified a filter. (WAVEFILTER is described in the Win32 SDK.)
dwCallback
Contains the application-specified dwCallback argument to the acmStreamOpen function.
dwInstance
Contains the application-specified dwInstance argument to the acmStreamOpen function.
fdwOpen
Contains the application-specified dwOpen argument to the acmStreamOpen function, which consists of a set of flags. (For flag descriptions, refer to acmStreamOpen in the Win32 SDK.)
fdwDriver
Contains driver-defined stream instance information. While intended for storing flag values, an ACM driver can use this member to store any instance-specific DWORD value. Because the same ACMDRVSTREAMINSTANCE structure is passed with all stream messages associated with a particular stream instance, the stored value can be read or modified each time a stream message is received, and the last saved value will be available the next time a stream message is received.
dwDriver
Contains driver-defined stream instance information. An ACM driver can use this member for storing any instance-specific DWORD value, such as a pointer to a local, dynamically allocated structure. Because the same ACMDRVSTREAMINSTANCE structure is passed with all stream messages associated with a particular stream instance, the stored value can be read or modified each time a stream message is received, and the last saved value will be available the next time a stream message is received.
has
Contains the ACM-defined client handle to the open conversion stream.

Comments

The ACM allocates an ACMDRVSTREAMINSTANCE structure each time an application calls acmStreamOpen. This ACMDRVSTREAMINSTANCE structure is then passed to the driver with all stream messages associated with a particular stream instance. Information in the structure does not change, so if a driver validates information within the structure when it receives an ACMDM_STREAM_OPEN message, it does not have to validate the information again when it receives subsequent messages for the same stream instance.


ACMDRVSTREAMSIZE

typedef struct {
    DWORD cbStruct;
    DWORD fdwSize;
    DWORD cbSrcLength;
    DWORD cbDstLength;
} ACMDRVSTREAMSIZE;

The ACMDRVSTREAMSIZE structure contains information needed by an ACM driver to respond to an ACMDM_STREAM_SIZE message. ACMDRVSTREAMSIZE is defined in msacmdrv.h.

Members

cbStruct
Size, in bytes, of the ACMDRVSTREAMSIZE structure.
fdwSize
Contains one of the following flags, indicating the query type.

Flag

Meaning

ACM_STREAMSIZEF_
SOURCE

Indicates the client has specified the size, in bytes, of a source buffer in the cbSrcLength member. The driver should return the required destination buffer length in cbDstLength.

ACM_STREAMSIZEF_
DESTINATION

Indicates the client has specified the size, in bytes, of a destination buffer in the cbDstLength member. The driver should return the required source buffer length in cbSrcLength.

cbSrcLength
Size, in bytes, of the source buffer. The flag value specified in fdwSize indicates whether this value is supplied by the client or by the driver.
cbDstLength
Size, in bytes, of the destination buffer. The flag value specified in fdwSize indicates whether this value is supplied by the client or by the driver.


Video Capture Device Drivers

The following topics explain how to write video capture drivers for Windows NTâ:

  • Introduction to Video Capture Drivers

  • Designing a User-Mode Video Capture Driver

  • Designing a Kernel-Mode Video Capture Driver

  • Video Capture Driver Reference

For a general discussion of multimedia device drivers, refer to Introduction to Multimedia Drivers.


Introduction to Video Capture Drivers

The following topics provide an introduction to video capture drivers:

  • Capabilities of Video Capture Drivers

  • Video Capture Software Components

  • Sample Video Capture Drivers


Capabilities of Video Capture Drivers

Video capture device drivers can capture video input images that device hardware has stored in a frame buffer. They can pass these images to client applications as device-independent bitmaps (DIBs). If the hardware allows, they can display captured images as an overlay on an output display device.

APIs provided by the Microsoft Video for Windows Development Kit and the AVI capture (AVIcap) window class allow applications to capture either single video images or streams of images, and to view images on an overlay display as they are captured. The AVIcap window class is described in the Win32 SDK.

The Video Compression Manager allows applications to play back previously recorded bitmap images. For more information about the Video Compression Manager and its drivers, see Video Compression Manager Drivers.


Video Capture Software Components

The following diagram illustrates the relationship of the major Windows NT video software components.

The components in the diagram include:

Application
Any user-mode, Win32-based application that creates an AVIcap window (described in the Win32 SDK), calls the video API functions (described in the Video for Windows Development Kit), or calls the Video Compression Manager functions (also described in the Win32 SDK).
msvfw32.dll
Microsoft Video for Windows dynamic-link library. Exports the video API functions described in the Video for Windows Development Kit. Also exports the MCIWnd window class and DRAWDIB functions, described in the Win32 SDK. Additionally, this library includes the Video Compression Manager. For more information about the Video Compression Manager, see Video Compression Manager Drivers.
avicap32.dll
Dynamic-link library supporting the AVI capture window class.
winmm.dll
Dynamic-link library that exports SendDriverMessage, which calls a user-mode driver’s DriverProc function. See winmm.dll.
User-mode video capture drivers
Dynamic-link libraries that communicate with kernel-mode drivers.
VCM drivers
Dynamic-link libraries that compress or decompress video data and either return it to the caller or send it to a kernel-mode driver.
VCUser.lib
Library used as a basis for user-mode video capture drivers. For details, see Using VCUser.lib.
VCKernel.lib
Library used as a basis for kernel-mode video capture drivers. For details, see Using VCKernel.lib.
Kernel-mode video capture drivers
Kernel-mode code that communicates with the Windows NT Executive in order to access device hardware.


Sample Video Capture Drivers

The Windows NT DDK includes the source code for the following video capture drivers and dynamic-link libraries:
Drivers or Libraries | Location of Source Files
Truevision Bravado video capture drivers | \ddk\src\mmedia\vidcap\bravado
Video Spigot video capture drivers | \ddk\src\mmedia\vidcap\spigot
Microsoft YUV compressor/decompressor | \ddk\src\mmedia\vidcap\msyuv
User-mode video capture driver library (For details, see Using VCUser.lib.) | \ddk\src\mmedia\vidcap\vcuser
Kernel-mode video capture driver library (For details, see Using VCKernel.lib.) | \ddk\src\mmedia\vidcap\vckernel

For the Bravado and Spigot driver samples, code for both the user-mode and the kernel-mode driver is provided. Under the listed driver directory, a \dll subdirectory contains the user-mode driver sources, and a \driver subdirectory contains the kernel-mode driver sources.

The Truevision Bravado video capture drivers (bravado.dll and bravado.sys) and hardware support image capture, scaling, and overlay. They do not support clipping. The hardware accepts compressed YUV-formatted data for output.

The Video Spigot video capture drivers (spigot.dll and spigot.sys) and hardware support image capture and scaling. They do not support overlay or clipping.

The Microsoft YUV compressor/decompressor (codec) is a user-mode driver (msyuv.dll) that can draw compressed YUV-formatted video data by sending it to bravado.sys, the kernel-mode driver for the Truevision Bravado hardware. For more information about video codecs, see Video Compression Manager Drivers.

All of the sample user-mode drivers, including the YUV codec (msyuv.dll), are built by using VCUser.lib.

All of the sample kernel-mode drivers are built by using VCKernel.lib.


Designing a User-Mode Video Capture Driver

User-mode video capture drivers are implemented as dynamic-link libraries. This section contains the following topics to assist you in designing a user-mode video capture driver:

  • DriverProc in User-Mode Video Capture Drivers

  • User-Mode Video Capture Driver Messages

  • Introduction to Video Channels

  • Opening Video Channels

  • Configuring Video Channels

  • Setting the Video Data Format

  • Setting Source and Destination Rectangles

  • Setting Palettes

  • Transferring Video Capture Data

  • Notifying Clients from Video Capture Drivers

  • Using VCUser.lib


DriverProc in User-Mode Video Capture Drivers

Like all other Win32-based user-mode drivers, user-mode video capture drivers must export a DriverProc entry point that recognizes the standard driver messages. Video capture driver clients, such as msvfw32.dll and avicap32.dll, send messages to video capture drivers by calling SendDriverMessage, which is exported by winmm.dll and described in the Win32 SDK.

When a user-mode video capture driver receives a DRV_OPEN message from avicap32.dll or msvfw32.dll, it also receives a pointer to a VIDEO_OPEN_PARMS structure. For more information about the use of this structure, see Opening Video Channels.

In addition to supporting the standard messages, the DriverProc entry point for user-mode video capture drivers must support a set of user-mode video capture driver messages.


User-Mode Video Capture Driver Messages

The following table lists the messages that the DriverProc function in a user-mode video capture driver can receive, along with the operation the driver performs when it receives each message. Message definitions are contained in msviddrv.h and msvideo.h.
Message | Operation Performed by Driver
DVM_CONFIGURESTORAGE | Saves or restores configuration information.
DVM_DIALOG | Displays a dialog box to obtain configuration information.
DVM_DST_RECT | Sets or retrieves destination rectangle parameters.
DVM_FORMAT | Sets or retrieves video-capture format parameters.
DVM_FRAME | Transfers data from the frame buffer.
DVM_GET_CHANNEL_CAPS | Returns channel capabilities.
DVM_GETERRORTEXT | Returns the text string associated with an error number.
DVM_GETVIDEOAPIVER | Returns the video API version used by the driver.
DVM_PALETTE | Sets or retrieves a logical palette.
DVM_PALETTERGB555 | Sets a logical palette and an RGB555 translation table.
DVM_SRC_RECT | Sets or retrieves source rectangle parameters.
DVM_STREAM_ADDBUFFER | Adds a data buffer to a capture stream.
DVM_STREAM_FINI | Terminates a capture stream.
DVM_STREAM_GETERROR | Returns a stream’s error status.
DVM_STREAM_GETPOSITION | Returns the current position within a capture stream.
DVM_STREAM_INIT | Initializes a capture stream.
DVM_STREAM_PREPAREHEADER | Prepares a data buffer.
DVM_STREAM_RESET | Stops and resets a capture stream.
DVM_STREAM_START | Starts a capture stream.
DVM_STREAM_STOP | Stops a capture stream.
DVM_STREAM_UNPREPAREHEADER | Removes preparation from a data buffer.
DVM_UPDATE | Updates a screen overlay area.


Introduction to Video Channels

Video capture device drivers define the four logical video channels shown in the following table. Each channel represents a portion of the data path between video hardware and system memory.
Channel | Constant | Definition
External In | VIDEO_EXTERNALIN | Data path from an external video source, such as a TV, VCR, or camera, into a frame buffer.
Video In | VIDEO_IN | Data path from the frame buffer to system memory.
Video Out | VIDEO_OUT | Data path from system memory to the frame buffer.
External Out | VIDEO_EXTERNALOUT | Data path from the frame buffer to an output display, such as an overlay window on a user’s screen.

User-mode video capture drivers implement the concept of logical video channels, and clients can open the channels they need to create a complete input and/or output data path. Logical channels do not exist within kernel-mode drivers.

The sample video capture drivers provided with the Windows NT DDK support the VIDEO_EXTERNALIN, VIDEO_IN, and VIDEO_EXTERNALOUT channels. Support for these channels means that users employing the AVIcap window class or the Video For Windows API can capture video images from an external source, store the images in memory or a file, and view the captured images as they are received. The sample user-mode video capture drivers do not support the VIDEO_OUT channel, so users cannot use them to play back recorded images. Instead, user applications call the Video Compression Manager’s API to send drawing requests to Video Compression Manager drivers.

For more information about video channels, see Opening Video Channels and Configuring Video Channels.


Opening Video Channels

A client must independently open each video channel that it intends to use. Therefore, a user-mode video capture driver receives a separate DRV_OPEN message for each channel. When a driver receives a DRV_OPEN message, it must check the dwFlags member of the accompanying VIDEO_OPEN_PARMS structure to determine which video channel to open. The member’s value can be one of the following constants, which are defined in msvideo.h:

  • VIDEO_EXTERNALIN

  • VIDEO_IN

  • VIDEO_OUT

  • VIDEO_EXTERNALOUT

Besides testing for these constants, the driver must also return the detected constant as the return value for DriverProc, as illustrated in the vidOpen function within each sample user-mode driver. This constant then becomes the value passed to the driver as the dwDriverID parameter, when a client makes subsequent calls to DriverProc to send the driver user-mode video capture driver messages.


Configuring Video Channels

User-mode video capture drivers can allow users to specify configuration parameters for each video channel. After a client opens a channel, it can send a DVM_DIALOG message for the opened channel. In response to the message, the driver displays a dialog box that allows the user to specify parameters for the channel type. Typically, the driver provides a different dialog box for each channel type. If the driver does not support a channel type, or the driver does not allow user modification to the channel’s parameters, it can return the DV_ERR_NOTSUPPORTED error code.

The sample video capture driver, bravado.dll, uses dialog boxes to obtain the following channel configuration information.
Channel | Information Obtained
VIDEO_EXTERNALIN | Hue, video source standard, connector number, cable format.
VIDEO_IN | Data format, destination image size. (Also see Setting the Video Data Format.)
VIDEO_OUT | Not supported.
VIDEO_EXTERNALOUT | Saturation, brightness, contrast, red, green, blue.

User-mode drivers should store channel configuration parameters for each user by writing them into the registry, under the path HKEY_CURRENT_USER \Software \Microsoft \Multimedia \Video Capture \DriverName, where DriverName represents the name of your driver. For more information about obtaining and storing channel configuration parameters, see Configuring Video Channels, Using VCUser.lib.

The driver should provide default values for all channel configuration parameters, and it should use the default values if it has not stored values for the current user in the previously listed registry path.

Changing the format can alter the dimensions of the active portion of the frame buffer, and can also affect bit depth and color space representation. Additionally, changing between NTSC and PAL video standards can affect image dimensions. Therefore, clients typically send a DVM_FORMAT message to request the current format after they have sent a DVM_DIALOG message for the VIDEO_EXTERNALIN channel.

Clients can also request a driver to store a channel’s configuration parameters in a file, or to restore the parameters from a previously created file, by sending a DVM_CONFIGURESTORAGE message.


Setting the Video Data Format

Developers of video capture clients expect video capture data to be stored as device independent bitmaps (DIBs). DIB contents are described by BITMAPINFO and BITMAPINFOHEADER structures, which are discussed in the Win32 SDK. A client can specify a data format, or query the driver for the current format, by sending the driver a DVM_FORMAT message and including the address of a BITMAPINFOHEADER structure.

You can also allow users to modify the format. Typically, you include format options in the dialog boxes associated with VIDEO_IN and VIDEO_OUT channels, and your driver displays these dialog boxes when it receives DVM_DIALOG messages. For more information, see Configuring Video Channels.

The sample video capture drivers do not support DVM_FORMAT for the VIDEO_OUT channel, because the VIDEO_OUT channel is handled by Video Compression Manager drivers.

Your driver should provide default values for all format variables. These default values should represent a format that can be handled most efficiently by the video capture hardware. The driver should use the default values if client-specified (via DVM_FORMAT) or user-specified (via DVM_DIALOG) values are not available.

Following is a typical scenario in which a client modifies the format because of limited system storage space:

  1. Prior to opening an input stream, a client sends a DVM_FORMAT message, with the VIDEO_CONFIGURE_GET flag set, to determine the current format.

  2. The client calculates the amount of storage needed to store a captured video stream if the current format is used.

  3. The client attempts to allocate enough storage to save the stream. The allocation fails because there is not enough storage space available.

  4. The client sends a DVM_FORMAT message to change the format to one that requires less storage space.

  5. The client again attempts to allocate enough storage to save the stream. This time the allocation succeeds.

  6. The client opens the stream and saves the input data. (For details, see Transferring Video Capture Data.)


Setting Source and Destination Rectangles

Video capture drivers can support the use of source and destination rectangles. A rectangle’s typical use is dependent on the channel for which it is defined, as illustrated by the following table.
Channel | Source Rectangle | Destination Rectangle
VIDEO_EXTERNALIN | Specifies the portion of the analog image to be digitized. | Specifies the portion of the frame buffer to receive digitized input data.
VIDEO_IN | Specifies the portion of the frame buffer to be copied to the client. | Not applicable.
VIDEO_OUT | Not applicable. | Specifies the portion of the frame buffer to copy client data into.
VIDEO_EXTERNALOUT | Specifies the portion of the frame buffer to display inside the overlay area. | Specifies the portion of the display device to use as the overlay area.

A driver does not have to support all rectangles for all channels. Two messages, DVM_SRC_RECT and DVM_DST_RECT, are provided to allow clients to set and query the size of rectangles. Drivers that define rectangles on some or all channels must support these two messages. Even if your driver does not allow client modification of rectangle sizes, it should allow the client to send these messages to determine a rectangle’s predefined size. Rectangles are always specified using screen coordinates, which are described in the Win32 SDK.

After a client changes a VIDEO_EXTERNALOUT rectangle’s coordinates, it typically sends a DVM_UPDATE message to request the driver to update the display.

Clients send the DVM_GET_CHANNEL_CAPS message to determine the rectangular capabilities that a driver provides for a particular channel. In response to this message, your driver indicates its overlay, clipping, and scaling capabilities, along with allowable re-sizing parameters.


Setting Palettes

Video capture drivers can allow clients to specify a logical color palette and, optionally, an RGB555-format translation table. Two messages, DVM_PALETTE and DVM_PALETTERGB555, are defined.

The DVM_PALETTE message allows a client to specify a logical palette or to obtain the current palette from the driver. When a client specifies a logical palette, the driver typically creates a translation table by using the CreatePalette and GetNearestPaletteIndex functions described in the Win32 SDK, as illustrated in the sample video capture drivers. By sending a DVM_PALETTERGB555 message, a client can specify both a palette and an RGB555 translation table.

Drivers use the translation tables when converting frame buffer data into DIBs. (The palette location corresponding to an RGB color is found by using an RRRRRGGGGGBBBBB binary value as an index into the translation table, where the five most significant bits of each color component are used to create the index).

Drivers typically create a default palette when processing the DRV_OPEN message.

For more information about color palettes, see the Win32 SDK.


Transferring Video Capture Data

The APIs provided for capturing video images enable clients to receive single captured frames and to receive streams of frames that are captured at a client-specified rate. The next section discusses transferring single frames, and the following section explains the algorithm for transferring streams of captured data.


Transferring Single Frames

Application developers sometimes need to transfer single frames of video information. For instance, a developer might want to record animated sequences that are generated one frame at a time, or to display a single image, such as a photograph.

Clients can transfer single frames of video information by sending the DVM_FRAME message, along with the address of a VIDEOHDR structure. Clients specify the VIDEO_IN channel to obtain a frame from the device’s frame buffer, or the VIDEO_OUT channel to send a frame to the frame buffer.

If the channel is VIDEO_IN, the client includes the address of an empty data buffer. By calling the kernel-mode driver, the user-mode driver transfers the contents of the device’s frame buffer into the data buffer. If the channel is VIDEO_OUT, the client includes the address of a filled data buffer. The user-mode driver sends the data buffer’s contents to the kernel-mode driver, which in turn transfers the contents to the device’s frame buffer. The sample video capture drivers support single frame transfers only on the VIDEO_IN channel.

When handling single-frame transfers, drivers must take into account current settings for video format, source or destination rectangles, and color palettes.


Transferring Streams of Captured Data

Video capture drivers must provide the capability of handling video capture input streams. Video capture drivers do not support output streams. Output streams are handled by Video Compression Manager drivers.

To transfer a stream of captured video input data, a client must:

  1. Establish settings for video format, source or destination rectangles, and color palettes.

  2. Initialize the appropriate video channels.

  3. Allocate data buffers, prepare them for use, and pass them to the driver to be filled.

  4. Request the driver to start filling and returning the data buffers.

  5. Copy data from the buffers and pass them back to the driver for re-use.

  6. Request the driver to end the transfer operation.

The first streaming message a user-mode video capture driver should receive a is DVM_STREAM_INIT, along with a channel specification. The following table lists the operations that a driver should perform, based on the specified channel.
Channel | Operation to Perform for DMV_STREAM_INIT Message
VIDEO_EXTERNALIN | Set up hardware to allow capture operations.
VIDEO_IN | Save client-specified capture rate and event notification information.
VIDEO_OUT | Not used.
VIDEO_EXTERNALOUT | Set up hardware to allow overlay operations.

Clients dynamically allocate buffers to receive captured data. Before a client can use a data buffer in a streaming operation, it must pass the buffer’s address to the user-mode driver by calling DVM_STREAM_PREPAREHEADER, so the driver can prepare the buffer for use. The client then sends a DVM_STREAM_ADDBUFFER message for each prepared buffer, which requests the driver to add the buffer’s address to a queue of buffers waiting to be filled.

To start the input operation, the clients sends a DVM_STREAM_START message. The user-mode driver passes the request to the kernel-mode driver, which causes input operations to begin, typically by enabling device interrupts so that an interrupt occurs each time the frame buffer has been filled. Each time the frame buffer is filled, its contents are copied into the next available client buffer. The user-mode driver then sends the client an MM_DRVM_DATA callback message. The client copies the data from the buffer and re-adds the buffer to the user-mode driver’s buffer queue by sending another DVM_STREAM_ADDBUFFER message. The client can send a DVM_STREAM_GETERROR message to determine if the driver has dropped any captured frames because of a lack of available buffers.

When the client is ready to stop the input stream, it sends a DVM_STREAM_STOP message. It can also send DVM_STREAM_RESET, which indicates to the user-mode driver that it should not fill any remaining data buffers. The client can then send a DVM_STREAM_UNPREPAREHEADER message for each buffer and de-allocate the buffers. Finally, the client sends a DVM_STREAM_FINI message to close the streaming session.

For all stream messages except DVM_STREAM_INIT and DVM_STREAM_FINI, the client-specified channel should always be VIDEO_IN.


Notifying Clients from Video Capture Drivers

User-mode video capture drivers are responsible for notifying clients upon the completion of certain driver events. When a client sends a DVM_STREAM_INIT message, it indicates the type of notification, if any, it expects to receive. A client can specify any of the following notification targets:

  • A callback function

  • A window handle

User-mode drivers notify clients by calling the DriverCallback function in winmm.dll. This function delivers a message to the client’s notification target. The function also delivers message parameters, if the target type accepts parameters.

Video capture drivers must send MM_DRVM_OPEN, MM_DRVM_CLOSE, MM_DRVM_DATA, and MM_DRVM_ERROR messages to clients.

If you are using VCUser.lib to develop your driver, you do not have to call DriverCallback to send callback messages. Code within VCUser.lib handles delivery of callback messages for you, if you call VC_StreamInit when processing the DVM_STREAM_INIT message.


Using VCUser.lib

This section contains the following topics that explain how to use the user-mode video capture library, VCUser.lib:

  • Introduction to VCUser.lib

  • Installing and Configuring a Kernel-Mode Driver, Using VCUser.lib

  • Opening and Closing a Device, Using VCUser.lib

  • Configuring Video Channels, Using VCUser.lib

  • Supporting Overlay Capabilities, Using VCUser.lib

  • Capturing Video Data, Using VCUser.lib

  • Playing Back Captured Video Data, Using VCUser.lib

  • Notifying Clients, Using VCUser.lib


Introduction to VCUser.lib

The VCUser.lib library exports a set of functions that provide a convenient interface between a user-mode video capture driver and its kernel-mode counterpart. If you link your user-mode driver with VCUser.lib, your driver can easily communicate with a kernel-mode driver that is using VCKernel.lib, the companion library for kernel-mode video capture drivers.

The VCUser.lib library is typically used by:

  • User-mode video capture drivers that support the user-mode video capture driver messages.

  • User-mode video codecs that support output operations by making use of decompressing hardware. An example driver is the sample msyuv.dll driver provided with the Windows NT DDK. For more information about video codecs, see Video Compression Manager Drivers.

Typically, VCUser.lib library functions communicate with a kernel-mode driver by calling the DeviceIoControl function, described in the Win32 SDK, to send I/O control codes to the driver. Your driver typically calls VCUser.lib functions in response to the receipt of standard driver messages or user-mode video capture driver messages.

To use VCUser.lib with your user-mode driver
  1. Include vcuser.h and vcstruct.h in your driver.

  2. Link your driver with VCUser.lib.


Installing and Configuring a Kernel-Mode Driver, Using VCUser.lib

User-mode drivers are responsible for installing their associated kernel-mode drivers and for providing kernel-mode drivers with configuration parameter values. The first VCUser.lib function that a user-mode driver should call is VC_OpenProfileAccess. This function, which specifies the kernel-mode driver’s name and sets up storage for access to the Service Control Manager and the registry, should be called when the driver receives a DRV_LOAD message.

Next, the driver should call VC_ConfigAccess. If the client has Administrators privilege and is thus allowed to install drivers, this function establishes a connection to the Service Control Manager. The driver should call the function when it receives a DRV_QUERYCONFIGURE message.

When the driver receives a DRV_CONFIGURE message, it should call VC_InstallDriver to install the kernel-mode driver. This function allows you to specify a callback function, from which your driver can make calls to VC_WriteProfile to store driver configuration parameters in the registry. If your kernel-mode driver encounters installation errors, it should write an error code into the registry. Your user-mode driver can call VC_ReadProfile to check the error code or to obtain the stored configuration information.

Upon receipt of a DRV_REMOVE message, your driver should call VC_RemoveDriver to remove the kernel-mode driver’s installation. The last message your driver can receive is DRV_FREE, at which time it should call VC_CloseProfileAccess to remove its connection to the Service Control Manager.


Opening and Closing a Device, Using VCUser.lib

When your user-mode driver receives a DRV_OPEN message it should call VC_OpenDevice to open a device. The following two restrictions apply to opening a video capture device:

  1. A user-mode video capture driver can only allow one instance of a device to be open at time.

  2. A kernel-mode video capture driver can only support one hardware device.

There are two ways to specify a device when calling VC_OpenDevice ¾ by device name or by index number. Because a kernel-mode driver can only support one hardware device, you specify a device’s name by specifying the kernel-mode driver’s name, such as “bravado”, without appending a device number.

Video capture device objects are assigned names of vidcap0, vidcap1, vidcap2, and so on. By specifying an index number instead of a device name, you can open any video capture device without knowing its name. For example, specifying an index of “1” opens the device “vidcap1”. The sample video capture driver msyuv.dll uses this index number in order to search for a device that supports a specific data format. For more information about device objects, see the Kernel-Mode Drivers Design Guide.

Your driver receives a DRV_OPEN message for each video channel (VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT) that the client will be using, but VC_OpenDevice should only be called once.

If your hardware provides overlay capabilities, then prior to calling VC_OpenDevice your driver must determine characteristics of the user’s display device, such as the display’s horizontal and vertical resolutions, and bits per pixel. The kernel-mode driver needs this information when constructing and positioning an overlay image. The user-mode driver can obtain characteristics of the user’s display device by calling GetDeviceCaps, which is described in the Win32 SDK. Your user-mode driver should call VC_WriteProfile to store the characteristics in the registry, where your kernel-mode driver can access them when needed.

Upon receipt of a DRV_CLOSE message, your driver should call VC_CloseDevice. Your driver will receive a DRV_CLOSE message for each open video channel, but VC_CloseDevice should only be called once.


Configuring Video Channels, Using VCUser.lib

Video channel configuration parameters should be stored in the registry, individually for each user. To store and retrieve a user’s channel configuration parameters, a user-mode driver can call VC_WriteProfileUser and VC_ReadProfileUser. The driver generally obtains channel configuration parameters, and stores them in the registry, when it receives DVM_DIALOG messages.

The VCUser.lib library provides three functions for passing channel configuration parameters to the kernel-mode driver. The VC_ConfigDisplay function is used for passing parameters associated with the output display. The VC_ConfigFormat function is used for passing data format parameters. The VC_ConfigSource function is used for passing parameters associated with the input source. For more information about channel configuration parameters, see Configuring Video Channels.


Supporting Overlay Capabilities, Using VCUser.lib

To determine the hardware’s overlay capabilities, a user-mode driver can call VC_GetOverlayMode. This function indicates whether or not overlay capabilities are provided, and also indicates the type of support provided for color keying and rectangle specifications. Based on the returned information, the driver can call VC_SetKeyColourPalIdx, VC_SetKeyColourRGB, or VC_GetKeyColour to set or retrieve the overlay’s key color. The driver should call VC_SetOverlayRect to specify the area to be overlaid and VC_SetOverlayOffset to specify which portion of the frame buffer to display in the overlay area. To enable the hardware’s overlay capabilities, the driver calls VC_Overlay.


Capturing Video Data, Using VCUser.lib

To enable the hardware’s video capture capability, your driver should call VC_Capture. To obtain a single frame of capture data, your driver can call VC_Frame.

To continuously obtain captured data as a stream, VCUser.lib provides a set of input stream functions, which your driver can call when it receives user-mode video capture driver messages that specify stream operations. The input stream functions are:

VC_StreamInit
Initializes VCUser.lib’s capture streaming capabilities.
VC_StreamAddBuffer
Adds empty buffers to a buffer queue.
VC_StreamStart
Starts capturing frames and copying them into queued buffers, and notifying the client each time a buffer is filled.
VC_GetStreamPos
Returns the amount of time that has passed since the stream started.
VC_GetStreamError
Returns the number of times a frame was dropped because a buffer was not available.
VC_StreamStop
Stops capturing frames.
VC_StreamReset
Returns unused buffers to the client.
VC_StreamFini
Disables VCUser.lib’s capture streaming capabilities.

For more information about capturing data, see Transferring Video Capture Data.


Playing Back Captured Video Data, Using VCUser.lib

The VCUser.lib library provides a single function, VC_DrawFrame, for playing back capture data. If the VC_GetOverlayMode function indicates that the hardware provides overlay capabilities and supports the desired data format, a driver can call VC_DrawFrame to send a bitmap to the hardware’s frame buffer for display in the overlay area.


Notifying Clients, Using VCUser.lib

User-mode drivers using VCUser.lib do not need to send callback messages to clients. When the driver calls VC_StreamInit, client-specified callback information is passed to VCUser.lib so that it can send callback messages at appropriate times.

For more information about callback messages, see Notifying Clients From Video Capture Drivers.


Designing a Kernel-Mode Video Capture Driver

Kernel-mode video capture drivers are responsible for accessing video capture hardware. They are implemented as services under the control of the Windows NT Service Control Manager. This section provides topics that explain implementing DriverEntry in a video capture driver and using VCKernel.lib.


DriverEntry in a Video Capture Driver

Like all kernel-mode multimedia drivers, kernel-mode video capture drivers must provide a DriverEntry function for handling initialization and configuration operations. This DriverEntry for multimedia drivers is the first function called after a kernel-mode driver is loaded.

For more information about the contents of a video capture driver’s DriverEntry function, see Initializing and Configuring a Driver, Using VCKernel.lib. The DriverEntry function for the sample video capture driver, bravado.sys, is located in \src\mmedia\vidcap\bravado\driver\init.c.


Using VCKernel.lib

This section contains the following topics that explain how to use the kernel-mode video capture library, VCKernel.lib:

  • Introduction to VCKernel.lib

  • Initializing and Configuring a Driver, Using VCKernel.lib

  • Opening and Closing a Device, Using VCKernel.lib

  • Configuring Video Channels, Using VCKernel.lib

  • Accessing Video Capture Hardware, Using VCKernel.lib

  • Handling Interrupts, Using VCKernel.lib

  • Synchronizing Driver Activities, Using VCKernel.lib

  • Supporting Overlay Capabilities, Using VCKernel.lib

  • Capturing Video Data, Using VCKernel.lib

  • Playing Back Captured Video Data, Using VCKernel.lib


Introduction to VCKernel.lib

The VCKernel.lib library exports a set of functions that provide a convenient interface between a kernel-mode video capture driver and its user-mode counterpart. If you link your kernel-mode driver with VCKernel.lib, your driver can easily respond to I/O request packets (IRPs) sent to it by a user-mode driver that is using VCUser.lib, the companion library for user-mode video capture drivers. (For more information about IRPs, see the Kernel-Mode Drivers Design Guide.)

The VCKernel.lib library provides a dispatch function that recognizes the I/O request codes and control codes that a user-mode driver can place inside the IRPs. The driver provides a set of driver functions used with VCKernel.lib, which the library calls in response to the receipt of the control codes.

To use VCKernel.lib with your kernel-mode driver
  1. Include vckernel.h and vcstruct.h in your driver.

  2. Link your driver with VCKernel.lib.


Initializing and Configuring a Driver, Using VCKernel.lib

Initialization and configuration operations take place in a kernel-mode driver’s DriverEntry function. Video capture drivers using VCKernel.lib, such as bravado.sys, must call VC_Init from within DriverEntry, before calling any other VCKernel.lib function. The VC_Init function creates a device object named “vidcapx”, where x is incremented from 0 for each new video capture device object, and creates an entry for the device in the registry.

After calling VC_Init, bravado.sys calls VC_ReadProfile to obtain hardware configuration parameters that were stored by the user-mode driver. These parameters are used as input to VC_GetResources, which reserves system resources for the device, and maps the device’s I/O address space and frame buffer into system address space.

Kernel-mode drivers using VCKernel.lib must provide a set of driver functions used with VCKernel.lib, so bravado.sys next calls VC_GetCallbackTable to get the address of VCKernel.lib’s callback table, and fills in the table with the addresses of driver-supplied functions.

After the callback table has been filled, device hardware can be initialized. The bravado.sys driver places initial values in device registers and then calls VC_ConnectInterrupt to connect the driver’s interrupt service routine with the device’s interrupt number. Then it confirms that interrupts can be received.

If bravado.sys detects an error during execution of its DriverEntry function, it calls VC_WriteProfile to write an error code into the registry. The user-mode driver can read the registry entry to determine if the kernel-mode driver initialized properly.

Before a kernel-mode driver is unloaded, VCKernel.lib calls VC_Cleanup, which in turn calls the driver-supplied CleanupFunc function. The VC_Cleanup function releases resources allocated by VC_Init and VC_GetResources. The driver’s CleanupFunc function might disable hardware and free any driver memory allocations. Besides being called by VCKernel.lib before the driver is unloaded, VC_Cleanup is typically called by the driver itself if the driver detects an error within DriverEntry any time after calling VC_Init.


Opening and Closing a Device, Using VCKernel.lib

Kernel-mode video capture drivers can provide functions for opening and closing devices. If the driver includes a DeviceOpenFunc function, then VCKernel.lib calls the function when a device is being opened. The function might enable hardware or allocate memory needed for I/O operations. The sample driver, bravado.sys, uses its DeviceOpenFunc function to obtain characteristics of the user’s display device, which are placed in the registry by the user-mode driver (for details, see Opening and Closing a Device, Using VCUser.lib).

If the driver includes a DeviceCloseFunc function, VCKernel.lib calls the function when the device is being closed.


Configuring Video Channels, Using VCKernel.lib

Kernel-mode video capture drivers can provide three functions for setting device characteristics that are based on the channel configuration options defined by the user-mode driver. These functions are:

ConfigDisplayFunc
Sets characteristics of the overlay display.
ConfigFormatFunc
Sets video data format characteristics.
ConfigSourceFunc
Sets characteristics of the video source.

Code in VCKernel.lib calls these functions in response to requests from the user-mode driver, which are explained in Configuring Video Channels, Using VCUser.lib.

Whenever video data format characteristics are changed, the kernel-mode driver should call VC_SetImageSize to indicate the maximum number of bytes needed to store an image, using the current format.


Accessing Video Capture Hardware, Using VCKernel.lib

After a kernel-mode video capture driver’s DriverEntry function has called VC_GetResources, which maps the device’s I/O address space and frame buffer into system address space, the driver can use VCKernel.lib functions and macros to access device hardware. The following functions read or write the device’s port address space:
VC_In

VC_Out

The following macros are useful for reading or writing the device’s frame buffer:
VC_ReadIOMemoryBlock

VC_ReadIOMemoryBYTE

VC_ReadIOMemoryULONG

VC_ReadIOMemoryUSHORT

VC_WriteIOMemoryBlock

VC_WriteIOMemoryBYTE

VC_WriteIOMemoryULONG

VC_WriteIOMemoryUSHORT


Handling Interrupts, Using VCKernel.lib

All kernel-mode video capture drivers using VCKernel.lib must provide both an interrupt service routine (ISR) and a deferred procedure call (DPC) function. The ISR is referred to as the driver’s InterruptAcknowledge function, and the DPC function is referred to as its CaptureService function.


Synchronizing Driver Activities, Using VCKernel.lib

Code that references the same objects that a driver’s interrupt service routine (ISR) references ¾ typically structures and device registers ¾ must be synchronized to avoid simultaneous attempts at referencing the same object. Likewise, code that references the same objects that the driver’s deferred procedure call (DPC) function references ¾ typically the frame buffer ¾ must be synchronized.

Kernel-mode drivers normally run at an IRQL of PASSIVE_LEVEL. When a device interrupt occurs, the IRQL increases to the device’s IRQL while the ISR executes. The ISR acknowledges the interrupt and schedules the execution of a DPC function. The DPC function finishes servicing the interrupt, typically by copying frame buffer data. It executes at an IRQL of DISPATCH_LEVEL, which is between PASSIVE_LEVEL and the device’s level.

Video capture drivers using VCKernel.lib should use the VC_SynchronizeExecution function to synchronize references to objects that the device’s ISR uses. The function uses a spin lock and the device’s IRQL to maintain exclusive use of the referenced object.

Drivers should use the VC_SynchronizeDPC function to synchronize references to objects that the device’s DPC function uses. The VC_SynchronizeDPC function uses a spin lock and the DISPATCH_LEVEL IRQL to maintain exclusive use of the referenced object.

Use of VC_SynchronizeExecution and VC_SynchronizeDPC ensures that multiple processors cannot simultaneously reference the same object, and that lower-priority code executing on the current processor cannot obtain access to the object.

For more information about spin locks, and IRQLs, see the Kernel-Mode Drivers Design Guide.


Supporting Overlay Capabilities, Using VCKernel.lib

Kernel-mode video capture drivers support a device’s overlay capabilities by providing a set of functions that VCKernel.lib can call in response to requests from a user-mode driver. If the hardware supports overlay capabilities, the driver must provide the following set of functions:

OverlayFunc
Enables overlay.
GetOverlayModeFunc
Returns overlay capabilities.
SetOverlayRectsFunc
Sets overlay display rectangles.
SetOverlayOffsetFunc
Sets the overlay offset rectangle that is used for panning.
GetKeyColourFunc
Returns the overlay area’s current key color.
SetKeyPalIdxFunc
Sets the overlay area’s key color to a palette index number.
SetKeyRGBFunc
Sets the overlay area’s key color to an RGB value.


Capturing Video Data, Using VCKernel.lib

From a kernel-mode video capture driver’s viewpoint, all captured data is presented to the user-mode driver as a stream. If the user-mode driver requests only a single frame, it opens a stream and provides only a single buffer to receive frame contents. To support video capture streams, your driver must provide the following functions, which VCKernel.lib calls in response to requests from a user-mode driver:

StreamInitFunc
Initializes a stream.
StreamStartFunc
Starts recording frames.
StreamGetPositionFunc
Returns the time since recording started.
StreamStopFunc
Stops recording frames.
StreamFiniFunc
Closes the stream.

Code in VCKernel.lib maintains a queue of buffers received from the user-mode driver, passes the address of the next available buffer to the driver’s CaptureService function each time it is called, and returns unused buffers to the user-mode driver when the user-mode driver resets the stream. (For more information about capture streams and user-mode drivers see Capturing Video Data, Using VCUser.lib.)

Your driver must also provide a CaptureFunc function, which enables and disables the hardware’s ability to capture frames.


Playing Back Captured Video Data, Using VCKernel.lib

If a device supports video playback, its kernel-mode video capture driver must provide a DrawFrameFunc function, which VCKernel.lib can call in response to a request from a user-mode driver. This function copies bitmap data in the device’s frame buffer, while performing data conversion operations, if necessary. If the device supports playback, the driver’s GetOverlayModeFunc function must provide a return value that indicates the video formats the device can accept.


Video Capture Driver Reference

The following sections describe the messages, functions, structures, and macros used by video capture drivers.

Sections relating to user-mode drivers include:

  • Messages, User-Mode Video Capture Drivers

  • Structures, User-Mode Video Capture Drivers

  • Functions, VCUser.lib

  • Structures, VCUser.lib

Sections relating to kernel-mode drivers include:

  • Functions, VCKernel.lib

  • Structures, VCKernel.lib

  • Driver Functions Used with VCKernel.lib

  • Macros, Kernel-Mode Video Capture Drivers


Messages, User-Mode Video Capture Drivers

This section describes the messages received by user-mode video capture drivers. The messages are listed in alphabetical order. They are defined in msviddrv.h and msvideo.h.


DVM_CONFIGURESTORAGE

The DVM_CONFIGURESTORAGE message requests a user-mode video capture driver to save or restore a video channel’s configuration parameters.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_CONFIGURESTORAGE
lParam1
Pointer to a client-specified unique string.
lParam2
Contains flags. The following flags are defined:
Flag | Definition
VIDEO_CONFIGURE_GET | The channel’s configuration parameters should be restored from a file.
VIDEO_CONFIGURE_SET | The channel’s configuration parameters should be saved in a file.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_CONFIGURESTORAGE message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoConfigureStorage function, which is described in the Video for Windows Development Kit.

If the value contained in lParam2 is VIDEO_CONFIGURE_SET, the driver should store the specified channel’s current configuration parameters in a file. The file name should consist of, or be based on, the string pointed to by lParam1.

If the value contained in lParam2 is VIDEO_CONFIGURE_GET, the driver should read the specified channel’s saved configuration parameters from the saved file, and use them as the channel’s current settings. You can assume the client will specify the same lParam1 value for saving and restoring the parameters.


DVM_DIALOG

The DVM_DIALOG message requests a user-mode video capture driver to display a dialog box that obtains user-specified parameters for a video channel.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (See Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_DIALOG
lParam1
Contains the handle to the parent window.
lParam2
Contains flags. The following flags are defined.
Flag | Definition
VIDEO_DLG_QUERY | See the following Comments section.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_DIALOG message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoDialog function, which is described in the Video for Windows Development Kit.

If the driver provides a dialog box for the specified channel, it should obtain user settings and store them in the registry, as explained in Configuring Video Channels.

If a client sends a DVM_DIALOG message and specifies a channel for which the driver does not provide a dialog box, the driver should return DV_ERR_NOTSUPPORTED.

If the VIDEO_DLG_QUERY flag is set in dwParam2 and the driver supports a dialog box for the specified channel, it should return DV_ERR_OK. If the flag is set and the driver does not provide a dialog box for the channel, it should return DV_ERR_NOTSUPPORTED.


DVM_DST_RECT

The DVM_DST_RECT message requests a user-mode video capture driver to set or return a video channel’s destination rectangle.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_DST_RECT
lParam1
Pointer to a RECT structure. For more information on the RECT structure, see the Win32 SDK.
lParam2
Contains flags. The following flags are defined.

Flag

Definition

VIDEO_CONFIGURE_CURRENT

The driver sets or returns the current destination rectangle description.

VIDEO_CONFIGURE_GET

The driver returns the current rectangle description to the client.

VIDEO_CONFIGURE_MAX

The driver returns the maximum allowable destination rectangle size. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_MIN

The driver returns the minimum allowable destination rectangle size. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_QUERY

The driver indicates whether or not it supports the specified request.

VIDEO_CONFIGURE_SET

The driver receives a client-specified rectangle description.

Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_DST_RECT message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoMessage function, which is described in the Video for Windows Development Kit.

The driver should test the VIDEO_CONFIGURE_SET and VIDEO_CONFIGURE_GET flags to determine whether to set or to return a rectangle description. If neither of these flags is set, the driver should return an error code.

The meaning of a destination rectangle is dependent on the specified video channel, as explained in Setting Source and Destination Rectangles.

User-mode video capture drivers using VCUser.lib can call VC_SetOverlayRect when processing the DVM_DST_RECT message for the VIDEO_EXTERNALOUT channel.


DVM_FORMAT

The DVM_FORMAT message requests a user-mode video capture driver to set a specified data format, or to return the current format, for the specified channel.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.) Typically, drivers only allow VIDEO_IN for this message.
hDriver
Driver handle.
uMsg
DVM_FORMAT
lParam1
Contains flags. The following flags are defined.

Flag

Definition

VIDEO_CONFIGURE_CURRENT

The driver sets or returns the current format.

VIDEO_CONFIGURE_GET

The driver returns the requested information to the client.

VIDEO_CONFIGURE_MAX

The driver returns the maximum-sized format. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_MIN

The driver returns the minimum-sized format. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_NOMINAL

The driver returns the nominal format. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_QUERY

The driver indicates whether or not it supports the specified request.

VIDEO_CONFIGURE_QUERYSIZE

The driver returns the size, in bytes, of the format description.

VIDEO_CONFIGURE_SET

The driver sets a client-specified format description.

lParam2
Pointer to a VIDEOCONFIGPARMS structure. For the DVM_FORMAT message, the structure members are used as follows:
lpdwReturn
Pointer to a DWORD in which the driver returns the size, in bytes, of the BITMAPINFOHEADER structure, if the VIDEO_CONFIGURE_QUERYSIZE flag is set.
lpData1
Pointer to a client-supplied BITMAPINFOHEADER structure. (For more information about the BITMAPINFOHEADER structure, see the Win32 SDK.)
dwSize1
Size, in bytes, of the BITMAPINFOHEADER structure.
lpData2
Not used.
dwSize2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_FORMAT message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoConfigure function, which is described in the Video for Windows Development Kit.

The driver should test the VIDEO_CONFIGURE_SET and VIDEO_CONFIGURE_GET flags to determine whether to set or to return a format description. If neither of these flags is set, the driver should return an error code.

When a client sends this message, it must also include the address of a BITMAPINFOHEADER structure (unless either the VIDEO_CONFIGURE_QUERY or VIDEO_CONFIGURE_QUERYSIZE flag is set). Depending on whether the client has set VIDEO_CONFIGURE_SET or VIDEO_CONFIGURE_GET, the driver either reads or writes the contents of this structure.

If the VIDEO_CONFIGURE_QUERYSIZE flag is set, the driver just returns the size of the BITMAPINFOHEADER structure.

For more information on how drivers should allow applications and users to set the video data format, see Setting the Video Data Format.

User-mode video capture drivers using VCUser.lib can call VC_ConfigFormat when processing the DVM_FORMAT message.


DVM_FRAME

The DVM_FRAME message requests a user-mode video capture driver to transfer a single frame to or from the video device.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_FRAME
lParam1
Pointer to a VIDEOHDR structure.
lParam2
Size of the VIDEOHDR structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_FRAME message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoFrame function, which is described in the Video for Windows Development Kit.

The client uses the VIDEOHDR structure to specify a buffer. The driver uses this buffer as either a destination or source for DIB-formatted data, depending on whether the specified channel is VIDEO_IN or VIDEO_OUT. For the VIDEO_IN channel, the driver transfers data from the device’s frame buffer into the specified buffer. For the VIDEO_OUT channel, the driver transfers data from the specified buffer into the device’s frame buffer.

The driver should process this message synchronously, not returning to the client until the transfer is complete.

The sample video capture drivers only support the DVM_FRAME message for the VIDEO_IN channel.

For more information about handling the DVM_FRAME message, see Transferring Single Frames.

User-mode video capture drivers using VCUser.lib can call VC_Frame when processing the DVM_FRAME message for the VIDEO_IN channel.


DVM_GET_CHANNEL_CAPS

The DVM_GET_CHANNEL_CAPS message requests a user-mode video capture driver to return the capabilities of a video channel.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_GET_CHANNEL_CAPS
lParam1
Pointer to a CHANNEL_CAPS structure.
lParam2
Size of the CHANNEL_CAPS structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_GET_CHANNEL_CAPS message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoGetChannelCaps function, which is described in the Video for Windows Development Kit.

The driver fills in the client-supplied CHANNEL_CAPS structure with channel information for the specified channel.


DVM_GETERRORTEXT

The DVM_GETERRORTEXT message requests a user-mode video capture driver to return a text string associated with an error code.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.) Can be NULL for this message.
hDriver
Driver handle.
uMsg
DVM_GETERRORTEXT
lParam1
Pointer to a VIDEO_GETERRORTEXT_PARMS structure.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h.

Comments

A client sends the DVM_GETERRORTEXT message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoGetErrorText function, which is described in the Video for Windows Development Kit.

You can define customized error codes for a video capture driver. If you do, you need to also define a text string for each error code and place the strings in a resource (.rc) file. Clients use the DVM_GETERRORTEXT message to retrieve the text string for a specified custom error code.

Drivers should call the LoadString function, described in the Win32 SDK, to retrieve a string. This function truncates the string if the supplied buffer is too small.


DVM_GETVIDEOAPIVER

The DVM_GETVIDEOAPIVER message requests a user-mode video capture driver to return the version of the video capture API used by the driver.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.) Can be NULL for this message.
hDriver
Driver handle.
uMsg
DVM_GETVIDEOAPIVER
lParam1
Pointer to a DWORD to receive the version.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_GETVIDEOAPIVER message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoMessage function, which is described in the Video for Windows Development Kit.

The driver should respond to this message by returning the VIDEOAPIVERSION constant in the address pointed to by the lParam1 parameter. The VIDEOAPIVERSION constant is defined in msviddrv.h.


DVM_PALETTE

The DVM_PALETTE message requests a user-mode video capture driver to store or return a logical palette.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.) Typically, drivers only allow VIDEO_IN for this message.
hDriver
Driver handle.
uMsg
DVM_PALETTE
lParam1
Contains flags. The following flags are defined.

Flag

Definition

VIDEO_CONFIGURE_CURRENT

The driver sets or returns the current palette.

VIDEO_CONFIGURE_GET

The driver returns the requested information to the client.

VIDEO_CONFIGURE_MAX

The driver returns the maximum-sized palette. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_MIN

The driver returns the minimum-sized palette. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_NOMINAL

The driver returns the nominal palette. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_QUERY

The driver indicates whether or not it supports the specified request.

VIDEO_CONFIGURE_QUERYSIZE

The driver returns the size, in bytes, of the current palette.

VIDEO_CONFIGURE_SET

The driver sets a client-specified palette description.

lParam2
Pointer to a VIDEOCONFIGPARMS structure. For the DVM_PALETTE message, the structure members are used as follows:
lpdwReturn
Pointer to a DWORD in which the driver returns the size, in bytes, of the currently stored palette, if the VIDEO_CONFIGURE_QUERYSIZE flag is set.
lpData1
Pointer to a client-supplied LOGPALETTE structure. (For more information about the LOGPALETTE structure, see the Win32 SDK.)
dwSize1
Size, in bytes, of the LOGPALETTE structure.
lpData2
Not used.
dwSize2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_PALETTE message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoConfigure function, which is described in the Video for Windows Development Kit.

The driver should test the VIDEO_CONFIGURE_SET and VIDEO_CONFIGURE_GET flags to determine whether to set or to return a format description. If neither of these flags is set, the driver should return an error code.

When a client sends this message, it must also include the address of a LOGPALETTE structure (unless either the VIDEO_CONFIGURE_QUERY or VIDEO_CONFIGURE_QUERYSIZE flag is set). Depending on whether the client has set VIDEO_CONFIGURE_SET or VIDEO_CONFIGURE_GET, the driver either writes or reads the contents of this structure.

If the VIDEO_CONFIGURE_QUERYSIZE flag is set, the driver just returns the size of the currently stored palette, including the palette entry array.

For more information on how drivers can handle palettes and color translation tables, see Setting Palettes.

User-mode video capture drivers using VCUser.lib can call VC_ConfigFormat when processing the DVM_PALETTE message.

See Also

DVM_PALETTERGB555


DVM_PALETTERGB555

The DVM_PALETTERGB555 message requests a user-mode video capture driver to store a logical palette and an RGB555 translation table.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.) Typically, drivers only allow VIDEO_IN for this message.
hDriver
Driver handle.
uMsg
DVM_PALETTERGB555
lParam1
Contains flags. The following flags are defined.

Flag

Definition

VIDEO_CONFIGURE_SET

The driver sets a client-specified palette description and translation table.

VIDEO_CONFIGURE_QUERY

The driver indicates whether or not it supports the DVM_PALETTERGB555 message.

lParam2
Pointer to a VIDEOCONFIGPARMS structure. For the DVM_PALETTERGB555 message, the structure members are used as follows:
lpdwReturn
Pointer to a DWORD in which the driver returns the size, in bytes, of the currently stored palette, if the VIDEO_CONFIGURE_QUERYSIZE flag is set.
lpData1
Pointer to a client-supplied LOGPALETTE structure. (For more information about the LOGPALETTE structure, see the Win32 SDK.)
dwSize1
Size, in bytes, of the LOGPALETTE structure.
lpData2
Pointer to a 32-kilobyte RGB555 translation table.
dwSize2
Size of the translation table. Must be 32768.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_PALETTERGB555 message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoConfigure function, which is described in the Video for Windows Development Kit.

This message does not support the VIDEO_CONFIGURE_GET flag. Clients can send DVM_PALETTE to obtain the current palette.

For more information on how drivers can handle palettes and color translation tables, see Setting Palettes.

User-mode video capture drivers using VCUser.lib can call VC_ConfigFormat when processing the DVM_PALETTERGB555 message.


DVM_SRC_RECT

The DVM_SRC_RECT message requests a user-mode video capture driver to set or return a video channel’s source rectangle.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_SRC_RECT
lParam1
Pointer to a RECT structure. For more information on the RECT structure, see the Win32 SDK.
lParam2
Contains flags. The following flags are defined.

Flag

Definition

VIDEO_CONFIGURE_CURRENT

The driver sets or returns the current source rectangle description.

VIDEO_CONFIGURE_GET

The driver returns the current rectangle description to the client.

VIDEO_CONFIGURE_MAX

The driver returns the maximum allowable source rectangle size. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_MIN

The driver returns the minimum allowable source rectangle size. (Used only with VIDEO_CONFIGURE_GET.)

VIDEO_CONFIGURE_QUERY

The driver indicates whether or not it supports the specified request.

VIDEO_CONFIGURE_SET

The driver receives a client-specified rectangle description.

Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_SRC_RECT message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoMessage function, which is described in the Video for Windows Development Kit.

The driver should test the VIDEO_CONFIGURE_SET and VIDEO_CONFIGURE_GET flags to determine whether to set or to return a rectangle description. If neither of these flags is set, the driver should return an error code.

The meaning of a source rectangle is dependent on the specified video channel, as explained in Setting Source and Destination Rectangles.

User-mode video capture drivers using VCUser.lib can call VC_SetOverlayOffset when processing the DVM_SRC_RECT message on the VIDEO_EXTERNALIN channel.


DVM_STREAM_ADDBUFFER

The DVM_STREAM_ADDBUFFER message requests a user-mode video capture driver to add an empty input buffer to its input buffer queue.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_ADDBUFFER
lParam1
Pointer to a VIDEOHDR structure.
lParam2
Size of the VIDEOHDR structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_ADDBUFFER message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamAddBuffer function, which is described in the Video for Windows Development Kit.

The VIDEOHDR structure describes the buffer. Before a buffer can be added to the driver’s queue, the client must prepare it by sending a DVM_STREAM_PREPAREHEADER message. Drivers can confirm that the buffer has been prepared by testing the VHDR_PREPARED flag in the VIDEOHDR structure’s dwFlags member. Code in msvfw32.dll and avicap32.dll checks this flag before sending a DVM_STREAM_ADDBUFFER message.

Drivers assume that the VIDEOHDR structure and the data buffer pointed to by its lpData member have been allocated with GlobalAlloc, using the GMEM_MOVEABLE and GMEM_SHARE flags, and locked with GlobalLock. (For further information about GlobalAlloc and GlobalLock, see the Win32 SDK.)

For information about using data buffers with video capture streams, see Transferring streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call VC_StreamAddBuffer when processing the DVM_STREAM_ADDBUFFER message.


DVM_STREAM_ALLOCBUFFER

The DVM_STREAM_ALLOCBUFFER message requests a user-mode video capture driver to allocate a buffer from a device’s local, non-system memory.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_ALLOCBUFFER
lParam1
Pointer to a location to receive the address of the returned buffer.
lParam2
Requested buffer size, in bytes.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_ALLOCBUFFER message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoMessage function, which is described in the Video for Windows Development Kit.

If the hardware provides on-board memory that can be used as buffer space to receive captured video data, the driver should allocate a buffer using the size specified by lParam2, return the buffer’s address in the location pointed to by lParam1, and provide a return value of DV_ERR_OK. If no buffer space is available, the driver should return NULL in the location pointed to by lParam1. If the hardware does not provide on-board memory, the driver should return DV_ERR_NOTSUPPORTED.

Note: The video capture driver libraries, VCUser.lib and VCKernel.lib, do not support this message. Therefore, the sample video capture drivers do not support the message either.

See Also

DVM_STREAM_ADDBUFFER

DVM_STREAM_FREEBUFFER


DVM_STREAM_FINI

The DVM_STREAM_FINI message requests a user-mode video capture driver to close a capture stream on a video channel.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_EXTERNALIN, VIDEO_IN, and VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_FINI
lParam1
Not used.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_FINI message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamFini function, which is described in the Video for Windows Development Kit.

If all the input buffers sent with DVM_STREAM_ADDBUFFER haven’t been returned to the client, the driver should fail the message. If all buffers have been returned, the driver requests the kernel-mode driver to disable the hardware’s data capture and overlay operations.

After the driver has finished closing the VIDEO_IN channel, it must send an MM_DRVM_CLOSE callback message.

For more information about video capture streams, see Transferring streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call the following functions to terminate a stream, based on the specified channel.
Channel | VCUser.lib Function
VIDEO_EXTERNALIN | VC_Capture
VIDEO_IN | VC_StreamFini
VIDEO_EXTERNALOUT | VC_Overlay

See Also

DVM_STREAM_INIT


DVM_STREAM_FREEBUFFER

The DVM_STREAM_FREEBUFFER message requests a user-mode video capture driver to free a buffer that was allocated in response to a DVM_STREAM_ALLOCBUFFER message.

Parameters
dwDriverID
Video channel identifier. One of VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_FREEBUFFER
lParam1
Buffer address returned in response to a previous DVM_STREAM_ALLOCBUFFER message.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_FREEBUFFER message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoMessage function, which is described in the Video for Windows Development Kit.

If the hardware provides on-board memory that can be used as buffer space to receive captured video data, the driver can support the DVM_STREAM_ALLOCBUFFER and DVM_STREAM_FREEBUFFER messages. The DVM_STREAM_FREEBUFFER message is used to deallocate buffer space that was allocated in response to DVM_STREAM_ALLOCBUFFER. If the hardware does not provide on-board memory, the driver should return DVM_ERR_NOTUSUPPORTED.

Note: The video capture driver libraries, VCUser.lib and VCKernel.lib, do not support this message. Therefore, the sample video capture drivers do not support the message either.


DVM_STREAM_GETERROR

The DVM_STREAM_GETERROR message requests a user-mode video capture driver to return a capture stream’s current error status.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_GETERROR
lParam1
Pointer to a DWORD to receive an error code.
lParam2
Pointer to a DWORD to receive the number of frames dropped.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_GETERROR message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamGetError function, which is described in the Video for Windows Development Kit.

When the driver receives this message it should return an error code (defined in msvideo.h) indicating the current status of the capture stream.

If the driver has had to drop captured frames because the client has not sent enough buffers to receive them, the driver should return DV_ERR_NO_BUFFERS in the address pointed to by lParam1, and should return the number of dropped frames in the address pointed to by lParam2. The driver should then reset its count of dropped frames to zero.

For more information about video capture streams, see Transferring streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call VC_GetStreamError when processing the DVM_STREAM_GETERROR message.


DVM_STREAM_GETPOSITION

The DVM_STREAM_GETPOSITION message requests a user-mode video capture driver to return the current stream position.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_GETPOSITION
lParam1
Pointer to an MMTIME structure. (The MMTIME structure is defined in mmsystem.h and described in the Win32 SDK.)
lParam2
Size, in bytes, of the MMTIME structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_GETPOSITION message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamGetPosition function, which is described in the Video for Windows Development Kit.

The client specifies a requested time format in the MMTIME structure’s wType member. If the driver cannot support the requested format, it uses a format it can support, and places the format type in wType. Video-capture drivers generally use the millisecond time format.

The driver should initialize the stream position when it receives a DVM_STREAM_START or a DVM_STREAM_RESET message.

For more information about video capture streams, see Transferring streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call VC_GetStreamPos when processing the DVM_STREAM_GETPOSITION message.


DVM_STREAM_INIT

The DVM_STREAM_INIT message requests a user-mode video capture driver to initialize a video channel for streaming.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_EXTERNALIN, VIDEO_IN, and VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_INIT
lParam1
Pointer to a VIDEO_STREAM_INIT_PARMS structure.
lParam2
Size, in bytes, of the VIDEO_STREAM_INIT_PARMS structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_INIT message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamInit function, which is described in the Video for Windows Development Kit.

Typical stream initialization operations include requesting the kernel-mode driver to enable the hardware’s data capture and overlay operations.

The VIDEO_STREAM_INIT_PARMS structure contains the client-specified capture frequency. It also contains callback information for use when notifying clients of events. For more information about notifying clients, see Notifying Clients from Video Capture Drivers.

After the driver finishes initializing the VIDEO_IN channel, it must send an MM_DRVM_OPEN callback message.

For more information about video capture streams, see Transferring streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call the following functions to initialize a stream, based on the specified channel.
Channel | VCUser.lib Function
VIDEO_EXTERNALIN | VC_Capture
VIDEO_IN | VC_StreamInit
VIDEO_EXTERNALOUT | VC_Overlay


DVM_STREAM_PREPAREHEADER

The DVM_STREAM_PREPAREHEADER message requests a user-mode video capture driver to prepare a data buffer for use.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_PREPAREHEADER
lParam1
Pointer to a VIDEOHDR structure.
lParam2
Size of the VIDEOHDR structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_PREPAREHEADER message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamPrepareHeader function, which is described in the Video for Windows Development Kit.

Use of this message is meant to ensure that the specified buffer is accessible by the kernel-mode driver. If the driver returns DV_ERR_NOTSUPPORTED, then msvfw32.dll or avicap32.dll will prepare the buffer. For most drivers, this behavior is sufficient. If the driver does perform buffer preparation, it should return DV_ERR_OK, which causes msvfw32.dll or avicap32.dll to set the VHDR_PREPARED flag in the VIDEOHDR structure’s dwFlags member.

Drivers assume that the VIDEOHDR structure and the data buffer pointed to by its lpData member have been allocated with GlobalAlloc, using the GMEM_MOVEABLE and GMEM_SHARE flags, and locked with GlobalLock. (For further information about GlobalAlloc and GlobalLock, see the Win32 SDK.)

For information about using data buffers with video capture streams, see Transferring Streams of Captured Data.


DVM_STREAM_RESET

The DVM_STREAM_RESET message requests a user-mode video capture driver to stop input of a capture stream, return all buffers to the client, and set the current position to zero.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_RESET
lParam1
Not used.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_RESET message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamReset function, which is described in the Video for Windows Development Kit.

When a driver receives DVM_STREAM_RESET, it should restore the state that it established in response to a DVM_STREAM_INIT message. For each unused buffer that was received with a DVM_STREAM_ADDBUFFER message, the driver must set VHDR_DONE in the dwFlags member of the buffer’s VIDEOHDR structure and then send an MM_DRVM_DATA callback message.

After a client sends DVM_STREAM_RESET, you should expect it to send either a DVM_STREAM_START message to restart the stream or a DVM_STREAM_FINI message to terminate the stream.

For more information about video capture streams, see Transferring streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call VC_StreamReset when processing the DVM_STREAM_RESET message.


DVM_STREAM_START

The DVM_STREAM_START message requests a user-mode video capture driver to start a video stream.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_START
lParam1
Not used.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_START message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamStart function, which is described in the Video for Windows Development Kit.

When a driver receives this message, it should begin transferring frame buffer contents to queued data buffers. Typically, a user-mode driver creates a separate thread to handle communication with the kernel-mode driver. Data buffers should be filled and returned to the client in the order they are received from the client.

Each time a buffer has been filled, the user-mode driver should set VHDR_DONE in the dwFlags member of the buffer’s VIDEOHDR structure, and then send an MM_DRVM_DATA callback message to the client.

For more information about video capture streams, see Transferring Streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call VC_StreamStart when processing the DVM_STREAM_START message.


DVM_STREAM_STOP

The DVM_STREAM_STOP message requests a user-mode video capture driver to stop a video stream.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_STOP
lParam1
Not used.
lParam2
Not used.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_STOP message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamStop function, which is described in the Video for Windows Development Kit.

When a driver receives this message it stops the input stream, typically by requesting the kernel-mode driver to disable capture interrupts. The driver retains its queue of empty buffers. If a buffer has been partially filled, the driver marks it as done and places the actual length of the data in the dwBytesUsed member of the buffer’s VIDEOHDR structure.

If the client has not previously sent a DVM_STREAM_START message, the DVM_STREAM_STOP message has no effect and the driver should return DV_ERR_OK.

For more information about video capture streams, see Transferring Streams of Captured Data.

User-mode video capture drivers using VCUser.lib can call VC_StreamStop when processing the DVM_STREAM_STOP message.


DVM_STREAM_UNPREPAREHEADER

The DVM_STREAM_UNPREPAREHEADER message requests a user-mode video capture driver to remove the preparation from a data buffer.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_IN. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_STREAM_UNPREPAREHEADER
lParam1
Pointer to a VIDEOHDR structure.
lParam2
Size of the VIDEOHDR structure.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_STREAM_UNPREPAREHEADER message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoStreamUnprepareHeader function, which is described in the Video for Windows Development Kit.

Use of this message is meant to remove the preparation that was performed by means of a DVM_STREAM_PREPAREHEADER message. If the driver returns DV_ERR_NOTSUPPORTED, then msvfw32.dll or avicap32.dll will remove the preparation. For most drivers, this behavior is sufficient. If the driver does remove buffer preparation, it should return DV_ERR_OK, which causes msvfw32.dll or avicap32.dll to clear the VHDR_PREPARED flag in the VIDEOHDR structure’s dwFlags member.

If the driver receives a DVM_STREAM_UNPREPAREHEADER message for a buffer that has not been prepared, the driver should just return DV_ERR_OK.

For information about using data buffers with video capture streams, see Transferring streams of Captured Data.


DVM_UPDATE

The DVM_UPDATE message requests a user-mode video capture driver to update the overlay display.

Parameters
dwDriverID
Video channel identifier. For this message, the driver should only accept VIDEO_EXTERNALOUT. (For details, see Opening Video Channels.)
hDriver
Driver handle.
uMsg
DVM_UPDATE
lParam1t
Handle to a client window.
lParam2
Handle to a display device context.
Return Value

The driver should return DV_ERR_OK if the operation succeeds. Otherwise, it should return one of the DV_ERR error codes defined in msvideo.h. Custom error codes are also allowed (see DVM_GETERRORTEXT).

Comments

A client sends the DVM_UPDATE message by calling the driver’s DriverProc entry point, passing the specified parameter values. Applications can send this message by calling the videoUpdate function, which is described in the Video for Windows Development Kit.

If the hardware supports color keying, the driver should repaint the current key color onto the overlay area. If the key color has not yet been specified, the driver should specify it. The driver should create a brush using the key color, convert the screen coordinates describing the overlay area into client coordinates, and repaint the overlay area, as illustrated in the sample drivers.

Typically, a client sends this message whenever its window receives a WM_PAINT, WM_MOVE, WM_POSITIONCHANGED, or WM_SIZE message. It also typically sends the message after sending a DVM_SRC_RECT or DVM_DST_RECT message.

User-mode video capture drivers using VCUser.lib can call the following functions to set or to determine the key color:
VC_GetKeyColour

VC_SetKeyColourPalIdx

VC_SetKeyColourRGB


MM_DRVM_CLOSE

The MM_DRVM_CLOSE callback message notifies a client that a user-mode video capture driver has finished processing a DVM_STREAM_FINI message.

Parameters
dwMsg
MM_DRVM_CLOSE
dwParam1
Not used.
dwParam2
Not used.
Comments

A user-mode video capture driver sends an MM_DRVM_CLOSE message to its client, by means of a callback, when the driver finishes processing a DVM_STREAM_FINI message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MM_DRVM_CLOSE message only if the client has previously specified a notification target with a DVM_STREAM_INIT message.

Drivers using VCUser.lib do not have to call DriverCallback to send callback messages. Code within VCUser.lib handles delivery of callback messages for the driver.

For more information about notifying clients, see Notifying Clients from Video Capture Drivers.


MM_DRVM_DATA

The MM_DRVM_DATA callback message notifies a client that a user-mode video capture driver has filled a buffer with capture data.

Parameters
dwMsg
MM_DRVM_DATA
dwParam1
Pointer to a VIDEOHDR structure describing the data buffer that has been filled.
dwParam2
Not used.
Comments

A user-mode video capture driver sends an MM_DRVM_DATA message to its client, by means of a callback, when the driver has filled a buffer with capture data. The driver uses the dwParam1 argument to indicate which buffer has been filled. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MM_DRVM_DATA message only if the client has previously specified a notification target with a DVM_STREAM_INIT message. The VIDEOHDR structure was received along with a DVM_STREAM_ADDBUFFER message.

Drivers using VCUser.lib do not have to call DriverCallback to send callback messages. Code within VCUser.lib handles delivery of callback messages for the driver.

For more information about notifying clients, see Notifying Clients from Video Capture Drivers.


MM_DRVM_ERROR

The MM_DRVM_ERROR video capture message is sent to a client when a user-mode video capture driver detects an error.

Parameters
dwMsg
MM_DRVM_ERROR
dwParam1
Number of frames dropped.
dwParam2
Not used.
Comments

A user-mode video capture driver sends an MM_DRVM_ERROR message to its client, by means of a callback, when the driver detects an error. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

A driver can send this message for any reason, but the message is typically used to report dropped frames. The driver drops frames if the client has not provided enough buffers to receive them. Drivers use the dwParam1 argument to indicate the number of dropped frames.

The driver sends the MM_DRVM_ERROR message only if the client has previously specified a notification target with a DVM_STREAM_INIT message.

Drivers using VCUser.lib do not have to call DriverCallback to send callback messages. Code within VCUser.lib handles delivery of callback messages for the driver.

For more information about notifying clients, see Notifying Clients from Video Capture Drivers.


MM_DRVM_OPEN

The MM_DRVM_OPEN callback message notifies a client that a user-mode video capture driver has finished processing a DVM_STREAM_INIT message.

Parameters
dwMsg
MM_DRVM_OPEN
dwParam1
Not used.
dwParam2
Not used.
Comments

A user-mode video capture driver sends an MM_DRVM_OPEN message to its client, by means of a callback, when the driver finishes processing a DVM_STREAM_INIT message. The driver sends the message to the client by calling DriverCallback, passing the specified parameters.

The driver sends the MM_DRVM_OPEN message only if the client has specified a notification target with the DVM_STREAM_INIT message.

Drivers using VCUser.lib do not have to call DriverCallback to send callback messages. Code within VCUser.lib handles delivery of callback messages for the driver.

For more information about notifying clients, see Notifying Clients from Video Capture Drivers.


Structures, User-Mode Video Capture Drivers

This section describes the structures used by Win32-based video capture drivers. The structures are listed in alphabetic order.


CHANNEL_CAPS

typedef struct {
DWORD dwFlags;
DWORD dwSrcRectXMod;
DWORD dwSrcRectYMod;
DWORD dwSrcRectWidthMod;
DWORD dwSrcRectHeightMod;
DWORD dwDstRectXMod;
DWORD dwDstRectYMod;
DWORD dwDstRectWidthMod;
DWORD dwDstRectHeightMod;
} CHANNEL_CAPS;

The CHANNEL_CAPS structure is used by user-mode video capture drivers to return the capabilities of a video channel. Clients request channel capabilities by sending a DVM_GET_CHANNEL_CAPS message. The structure is defined in msvideo.h.

Members
dwFlags
Returns flags providing information about the channel. The following flags are defined.

Flag

Definition

VCAPS_OVERLAY

Indicates the channel is capable of overlay. This flag is used only for VIDEO_EXTERNALOUT channels.

VCAPS_SRC_CAN_CLIP

Indicates the source rectangle can be set smaller than the maximum dimensions.

VCAPS_DST_CAN_CLIP

Indicates the destination rectangle can be set smaller than the maximum dimensions.

VCAPS_CAN_SCALE

Indicates the source rectangle can be a different size than the destination rectangle.

dwSrcRectXMod
Returns the granularity allowed when positioning the source rectangle in the horizontal direction.
dwSrcRectYMod
Returns the granularity allowed when positioning the source rectangle in the vertical direction.
dwSrcRectWidthMod
Returns the granularity allowed when setting the width of the source rectangle.
dwSrcRectHeightMod
Returns the granularity allowed when setting the height of the source rectangle.
dwDstRectXMod
Returns the granularity allowed when positioning the destination rectangle in the horizontal direction.
dwDstRectYMod
Returns the granularity allowed when positioning the destination rectangle in the vertical direction.
dwDstRectWidthMod
Returns the granularity allowed when setting the width of the destination rectangle.
dwDstRectHeightMod
Returns the granularity allowed when setting the height of the source rectangle.
Comments

For the video channel specified with the DVM_GET_CHANNEL_CAPS message, the driver fills in the CHANNEL_CAPS structure with the granularity, in number of pixels, of the positioning points, heights, and widths of source and destination rectangles. If, for example, the device only allows source rectangle positioning on 8-bit x-coordinate boundaries, the value returned for dwSrcRectXMod should be eight. If the device allows arbitrarily positioned rectangles, with arbitrary sizes, the structure members should all be set to one.

Rectangle dimensions indicated by modulus operators are considered advisory. When an application tries to set a rectangle size with a DVM_SRC_RECT or DVM_DST_RECT message, you can assume it will check the return value to ensure that the driver accepted the request. For example, if dwDstRectWidthMod is set to 64, the application might try to set destination rectangles with pixel widths of 64, 128, 192, 256, and so on. The driver might support only a subset of these sizes. If the application tries to specify an unsupported size, the driver should return DV_ERR_NOTSUPPORTED.


VIDEO_GETERRORTEXT_PARMS

typedef struct {
DWORD dwError;
LPWSTR lpText;
DWORD dwLength;
} VIDEO_GETERRORTEXT_PARMS;

The VIDEO_GETERRORTEXT_PARMS structure is used by user-mode video capture drivers to return error message text in response to a DVM_GETERRORTEXT message. The structure is defined in msviddrv.h.

Members
dwError
Contains the error number.
lpText
Pointer to a buffer into which the driver places the error text string.
dwLength
Length of the buffer.
Comments

The client specifies the error number, along with the address and length of the string buffer. The driver fills in the string buffer with the error text.


VIDEO_OPEN_PARMS

typedef struct {
DWORD dwSize;
FOURCC fccType;
FOURCC fccComp;
DWORD dwVersion;
DWORD dwFlags;
DWORD dwError;
LPVOID pV1Reserved;
LPVOID pV2Reserved;
DWORD dnDevNode;
} VIDEO_OPEN_PARMS;

The VIDEO_OPEN_PARMS structure defines the type of channel to open on a video-capture device. User-mode video capture drivers receive this structure along with a DRV_OPEN message. The structure is defined in msviddrv.h.

Members
dwSize
Contains the size of the VIDEO_OPEN_PARMS structure.
fccType
Contains a four-character code identifying the type of channel being opened. Must be “vcap”.
fccComp
Not used.
dwVersion
Contains the current version of the video capture API, which is defined by VIDEOAPIVERSION in msviddrv.h.
dwFlags
Specifies flags used to indicate the type of channel. The following flags are defined.

Flag

Definition

VIDEO_EXTERNALIN

Data path from an external video source, such as a TV, VCR, or camera, into a frame buffer.

VIDEO_IN

Data path from the frame buffer to system memory.

VIDEO_OUT

Data path from system memory to the frame buffer.

VIDEO_EXTERNALOUT

Data path from the frame buffer to an output display, such as an overlay window on a user’s screen.

dwError
Error value supplied by the driver if the open operation fails.
pV1Reserved
Reserved.
pV2Reserved
Reserved.
dnDevNode
Devnode for PnP devices.
Comments

The VIDEO_OPEN_PARMS structure is identical to the ICOPEN structure used by installable compressors, allowing a single driver to handle both video capture and compression messages.

For more information about the VIDEO_OPEN_PARMS structure, see DriverProc in User-Mode Video Capture Drivers and Opening Video Channels.


VIDEO_STREAM_INIT_PARMS

typedef struct {
DWORD dwMicroSecPerFrame;
DWORD dwCallback;
DWORD dwCallbackInst;
DWORD dwFlags;
DWORD hVideo;
} VIDEO_STREAM_INIT_PARMS;

The VIDEO_STREAM_INIT_PARMS structure contains information needed to initialize a video capture stream. User-mode video-capture drivers receive this structure along with a DVM_STREAM_INIT message. The structure is defined in msviddrv.h.

Members
dwMicroSecPerFrame
Contains the client-specified capture frequency, in microseconds. Represents how often the driver should capture a frame.
dwCallback
Contains either the address of a callback function, a window handle, or NULL, based on flags set in the dwFlags member.
dwCallbackInst
Contains client-specified instance data passed to the callback function, if CALLBACK_FUNCTION is set in dwFlags.
dwFlags
Contains flags. Can contain one (or none) of the following flags.

Flag

Definition

CALLBACK_WINDOW

Indicates dwCallback contains a window handle.

CALLBACK_FUNCTION

Indicates dwCallback contains a callback function address.

hVideo
Contains a handle to a video channel.
Comments

The dwCallback, dwCallbackInst, and dwFlags members contain information needed by the driver to deliver callback messages. For more information, see Notifying Clients from Video Capture Drivers.


VIDEOCONFIGPARMS

typedef struct {
LPDWORD lpdwReturn;
LPVOID lpData1;
DWORD dwSize1;
LPVOID lpData2;
DWORD dwSize2;
} VIDEOCONFIGPARMS;

The VIDEOCONFIGPARMS structure is used to send or return message-specific configuration parameters. User-mode video capture drivers receive this structure along with DVM_FORMAT, DVM_PALETTE, and DVM_PALETTERGB555 messages. The structure is defined in msviddrv.h.

Members
lpdwReturn
Pointer to a DWORD to be filled with a message-specific return value.
lpData1
Pointer to message-specific data.
dwSize1
Size, in bytes, of data passed in lpData1.
lpData2
Pointer to message-specific data.
dwSize2
Size, in bytes, of data passed in lpData2.
Comments

For message-specific information about the contents of the lpdwReturn, lpData1, and pData2 members, see the descriptions of DVM_FORMAT, DVM_PALETTE, and DVM_PALETTERGB555.


VIDEOHDR

typedef struct {
LPBYTE lpData;
DWORD dwBufferLength;
DWORD dwBytesUsed;
DWORD dwTimeCaptured;
DWORD dwUser;
DWORD dwFlags;
DWORD dwReserved[4];
} VIDEOHDR;

The VIDEOHDR structure describes a client-supplied buffer for receiving video capture data. Drivers receive this structure along with DVM_STREAM_ADDBUFFER, DVM_STREAM_PREPAREHEADER, and DVM_STREAM_UNPREPAREHEADER messages. The structure is defined in msvideo.h.

Members
lpData
Pointer to a buffer.
dwBufferLength
Length of the buffer.
dwBytesUsed
Number of bytes used in the buffer.
dwTimeCaptured
Capture time for the frame, in milliseconds, relative to the capture time of the stream’s first frame.
dwUser
Contains 32 bits of client-specified data.
dwFlags
Contains flags. The following flags are defined.

Flag

Definition

VHDR_DONE

Set by the device driver to indicate it is finished with the data buffer and is returning the buffer to the client.

VHDR_PREPARED

Indicates whether or not the buffer has been prepared for use. See DVM_STREAM_PREPAREHEADER.

VHDR_INQUEUE

Set by the driver to indicate the buffer is in the driver’s buffer queue.

VHDR_KEYFRAME

Set by the device driver to indicate a key frame.

dwReserved[4]
Reserved for use by the device driver. Typically, drivers use this area to maintain a buffer queue.
Comments

The client supplies values for the lpData, dwBufferLength, and dwUser members. The driver fills in the dwBytesUsed, dwTimeCaptured, and dwFlags members. Drivers using VCUser.lib do not need to maintain the dwFlags member, because code within VCUser.lib handles the flags.


Functions, VCUser.lib

This section describes the functions provided to user-mode video capture drivers that are using VCUser.lib. The functions are listed in alphabetic order.


VC_Capture

BOOL
VC_Capture(
VCUSER_HANDLE vh,
BOOL bAcquire
);

The VC_Capture function requests the kernel-mode driver to enable or disable acquisition of video capture data into the frame buffer.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
bAcquire
Set to TRUE to enable capture, or FALSE to disable capture.
Return Value

Returns TRUE if successful. Otherwise returns FALSE.

Comments

User-mode video capture drivers should call VC_Capture with bAcquire set to TRUE when processing a DVM_STREAM_INIT command for the VIDEO_EXTERNALIN channel. They should call the function with bAcquire set to FALSE when processing a DVM_STREAM_FINI command for the VIDEO_EXTERNALIN channel.

Disabling capture when overlay is enabled has the effect of freezing the captured video.

The VC_Capture function calls DeviceIoControl (described in the Win32 SDK) to send either an IOCTL_VIDC_CAPTURE_ON or an IOCTL_VIDC_CAPTURE_OFF control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives one of these control codes, its CaptureFunc function is called.


VC_CloseDevice

VOID
VC_CloseDevice(
VCUSER_HANDLE vh
);

The VC_CloseDevice function closes a video capture device that was previously opened with VC_OpenDevice.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

None.

Comments

The VC_CloseDevice function closes the kernel-mode driver handle that was obtained with VC_OpenDevice. It also removes the worker thread and event handles that VCUser.lib creates for internal use when the driver calls VC_OpenDevice.

A user-mode driver should call VC_CloseDevice when it receives a DRV_CLOSE message. Typically, the driver receives multiple DRV_CLOSE messages, because DRV_CLOSE is sent each time the client closes one of the video channels (VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, or VIDEO_EXTERNALOUT). The driver only needs to call VC_CloseDevice for the client’s last DRV_CLOSE message, as illustrated by the sample video capture drivers.


VC_CloseProfileAccess

VOID
VC_CloseProfileAccess(
PVC_PROFILE_INFO pProfile
);

The VC_CloseProfileAccess function removes registry access information obtained from VC_OpenProfileAccess.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
Return Value

None.

Comments

The VC_CloseProfileAccess function closes the connection to the Service Control Manager that was created by a previous call to VC_ConfigAccess.

After calling VC_CloseProfileAccess, a user-mode driver cannot access its kernel-mode driver or the registry.

A user-mode driver should call VC_CloseProfileAccess when its DriverProc function receives a DRV_FREE message.

See Also

VC_OpenProfileAccess


VC_ConfigAccess

BOOL
VC_ConfigAccess(
PVC_PROFILE_INFO pProfile
);

The VC_ConfigAccess function creates a connection to the Service Control Manager, if the client has Administrator privilege.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
Return Value

Returns TRUE if the client has Administrators privilege. Otherwise returns FALSE.

Comments

A user-mode driver must call VC_ConfigAccess when it receives a DRV_QUERYCONFIGURE message. Because a user must have Administrator privilege in order to perform driver configuration operations, the driver should provide a failure return value for DriverProc in response to DRV_QUERYCONFIGURE, if VC_ConfigAccess returns FALSE. This notifies the caller that configuration operations cannot be performed.


VC_ConfigDisplay

BOOL
VC_ConfigDisplay(
VCUSER_HANDLE vh,
PCONFIG_INFO pConfig
);

The VC_ConfigDisplay function sends display-configuration information to a kernel-mode video capture driver.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pConfig
Pointer to a CONFIG_INFO structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

User-mode video capture drivers use the VC_ConfigDisplay function to send channel configuration parameters to the kernel-mode driver. Typically, this function is used to send video overlay parameters associated with the VIDEO_EXTERNALOUT channel.

This VC_ConfigDisplay function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_CONFIG_DISPLAY control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its ConfigDisplayFunc function is called.

For more information about channel configuration, see Configuring Video Channels.


VC_ConfigFormat

BOOL
VC_ConfigFormat(
VCUSER_HANDLE vh,
PCONFIG_INFO pConfig
);

The VC_ConfigFormat function sends format-configuration information to a kernel-mode video capture driver.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pConfig
Pointer to a CONFIG_INFO structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

User-mode video capture drivers use the VC_ConfigFormat function to send data format parameters to the kernel-mode driver. Typically, a driver calls VC_ConfigFormat when its DriverProc function receives a DVM_FORMAT message, or after it displays a dialog box to allow the user to change the format.

The VC_ConfigFormat function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_CONFIG_FORMAT control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its ConfigFormatFunc function is called.

For more information about changing the video format, see Configuring Video Channels and Setting the Video Data Format.


VC_ConfigSource

BOOL
VC_ConfigSource(
VCUSER_HANDLE vh,
PCONFIG_INFO pConfig
);

The VC_ConfigSource function sends source-configuration information to a kernel-mode video capture driver.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pConfig
Pointer to a CONFIG_INFO structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

User-mode video capture drivers use the VC_ConfigSource function to send channel configuration parameters to the kernel-mode driver. Typically, this function is used to send video source parameters associated with the VIDEO_EXTERNALIN channel.

The VC_ConfigSource function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_CONFIG_SOURCE control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its ConfigSourceFunc function is called.

For more information about channel configuration, see Configuring Video Channels.


VC_DrawFrame

BOOL
VC_DrawFrame(
VCUSER_HANDLE vh,
PDRAWBUFFER pDraw
);

The VC_DrawFrame function requests a kernel-mode video capture driver to place video data into the frame buffer so that it can be displayed.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pDraw
Pointer to a DRAWBUFFER structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The driver supplies a DRAWBUFFER structure describing the frame to be drawn.

Drivers can call VC_GetOverlayMode to determine if the hardware supports playback of compressed data formats.

The VC_DrawFrame function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_DRAW_FRAME control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its DrawFrameFunc function is called.


VC_Frame

BOOL
VC_Frame(
VCUSER_HANDLE vh,
LPVIDEOHDR lpvh
);

The VC_Frame function requests a kernel-mode video capture driver to capture and return a single frame from the VIDEO_IN channel.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
lpvh
Pointer to a VIDEOHDR structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The VC_Frame function returns a single frame by opening a capture stream and supplying only a single data buffer, thereby stopping the stream after one transfer. The stream is then closed. The client is not aware of this stream and, from the client’s point of view, the operation appears to behave synchronously.

The driver supplies a VIDEOHDR structure describing an empty buffer. The kernel-mode driver fills the buffer with the contents of the frame buffer.

For more information about video capture data transfers, see Transferring Video Capture Data.


VC_GetKeyColour

DWORD
VC_GetKeyColour(
VCUSER_HANDLE vh
);

The VC_GetKeyColour function requests a kernel-mode video capture driver to return the device’s current key color.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

If the hardware supports a key color, the function returns the key color. Otherwise the function returns zero. See the following Comments section.

Comments

Before calling VC_GetKeyColour, the driver should call VC_GetOverlayMode and test the VCO_KEYCOLOUR_RGB flag to determine if, based on the current data format, the kernel-mode driver has stored the key color as an RGB color or as a palette index number. The VC_GetKeyColour function’s return value is either an RGBQUAD type or a palette index number, based on the VCO_KEYCOLOUR_RGB flag.

The VC_GetKeyColour function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_GET_KEY_COLOUR control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its GetKeyColourFunc function is called.

See Also

VC_SetKeyColourPalIdx, VC_SetKeyColourRGB


VC_GetOverlayMode

ULONG
VC_GetOverlayMode(
VCUSER_HANDLE vh
);

The VC_GetOverlayMode function requests a kernel-mode video capture driver to return the device’s overlay, color keying, and decompression capabilities.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

Returns an unsigned long value containing flags that indicate the types of overlay capabilities supported by the hardware. Zero indicates that overlay is not supported. A nonzero return value can contain the following flags.
Flag | Definition
VCO_KEYCOLOUR | Indicates the device supports a key color.
VCO_KEYCOLOUR_FIXED | Indicates the key color cannot be modified.
VCO_KEYCOLOUR_RGB | If set, indicates the key color must be specified as an RGB color. If clear, indicates the key color must be specified as a palette index number.
VCO_SIMPLE_RECT | Indicates the device supports a single rectangular overlay region.
VCO_COMPLEX_REGION | Indicates the device supports a complex (multi-rectangle) overlay region.
VCO_CAN_DRAW_Y411 | Indicates the device can display bitmaps that contain YUV 4:1:1-formatted data.
VCO_CAN_DRAW_S422 | Indicates the device can display bitmaps that contain YUV 4:2:2-formatted data.

Comments

The VCO_CAN_DRAW_Y411 and VCO_CAN_DRAW_S422 flags indicate the types of compressed formats the hardware supports for playback by means of the VC_DrawFrame function.

The VC_GetOverlayMode function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_OVERLAY_MODE control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its GetOverlayModeFunc function is called.


VC_GetStreamError

ULONG
VC_GetStreamError(
VCUSER_HANDLE vh
);

The VC_GetStreamError function returns the count of frames that have been dropped from the currently active capture stream since the last time VC_GetStreamError was called.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

Returns the number of dropped frames.

Comments

When transferring streams of captured data, a kernel-mode driver drops a frame if a hardware interrupt indicates the frame buffer is full, but the queue of client-supplied buffers is empty. Code within VCUser.lib keeps track of dropped frames. You do not have to provide any code to handle dropped frames.


VC_GetStreamPos

BOOL
VC_GetStreamPos(
VCUSER_HANDLE vh,
LPMMTIME pTime
);

The VC_GetStreamPos function requests a kernel-mode video capture driver to return the current position within the capture stream.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pTime
Pointer to an MMTIME structure. (The MMTIME structure is described in the Win32 SDK.)
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The VC_GetStreamPos function returns the time, in milliseconds, since VC_StreamStart was called, by filling in the MMTIME structure.

The VC_GetStreamPos function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_GET_POSITION control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its StreamGetPositionFunc function is called.


VC_InstallDriver

LRESULT
VC_InstallDriver(
PVC_PROFILE_INFO pProfile,
PPROFILE_CALLBACK pCallback,
PVOID pContext
);

The VC_InstallDriver function installs a kernel-mode driver and allows modification of configuration parameters.

Parameters
pProfile
Address of a VC_PROFILE_INFO structure returned by VC_OpenProfileAccess, containing the kernel-mode driver’s name.
pCallback
Pointer to a driver-supplied function that is called before the kernel-mode driver is reloaded. Can be NULL.

The function should return TRUE if the installation should continue, or FALSE if the installation should be terminated. It uses the following prototype definition:

BOOL pCallback (PVOID pContext);
pContext
Pointer to a driver-defined structure that is passed as input to the function pointed to by pCallback. Can be NULL.
Return Value

Returns one the following values.
Value | Definition
DRVCNF_OK | The driver is correctly loaded and started. System restart is not required.
DRVCNF_CANCEL | An error occurred.

Comments

Under Windows NT, kernel-mode drivers are considered to be services under the control of the Service Control Manager. The VC_InstallDriver function establishes a kernel-mode driver as a service by performing the following operations, in order:

  1. Calling OpenSCManager to create a connection to the local Service Control Manager. OpenSCManager is called with a desired access type of SC_MANAGER_ALL_ACCESS, which requires Administrators privilege. (OpenSCManager is described in the Win32 SDK.)

  2. Calling CreateService to create a kernel-mode driver service and obtain a service handle. The function sets the service’s start type to SERVICE_DEMAND_START, so it will not automatically reload when the system is restarted. (CreateService is described in the Win32 SDK.)

  3. Unloading the kernel-mode driver, if it is loaded.

  4. Calling the driver-supplied function specified by the pCallback parameter. Generally, drivers use this function to modify configuration parameters in the registry, using VC_WriteProfile.

  5. Reloading and restarting the kernel-mode driver, setting its start type to SERVICE_SYSTEM_START, so it will automatically reload and restart when the system is restarted.

A result of calling CreateService is the creation of a driver subkey under the \Services registry key. The path to the subkey is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

Typically, a user-mode video capture driver calls VC_InstallDriver when its DriverProc function receives a DRV_CONFIGURE message.


VC_OpenDevice

VCUSER_HANDLE
VC_OpenDevice(
PWCHAR pDeviceName,
int DeviceIndex
);

The VC_OpenDevice function opens a specified video capture device.

Parameters
pDeviceName
Pointer to a UNICODE string containing the name of the device to open. If NULL, DeviceIndex specifies the device.
DeviceIndex
Index number used to identify the device, if pDriverName is NULL. If pDriverName points to a string, DeviceIndex must be zero (see the following Comments section).
Return Value

Returns a handle to the kernel-mode driver, if the operation succeeds. Otherwise returns NULL.

Comments

A user-mode driver should call VC_OpenDevice when it receives a DRV_OPEN message. Typically, the driver receives multiple DRV_OPEN messages, because DRV_OPEN is sent each time the client opens one of the video channels (VIDEO_EXTERNALIN, VIDEO_IN, VIDEO_OUT, or VIDEO_EXTERNALOUT). The driver only needs to call VC_OpenDevice for the client’s first DRV_OPEN message, as illustrated by the sample video capture drivers.

If pDeviceName points to a string, that string is used as the device name. Because the current implementation does not allow a single kernel-mode video capture driver to support more than one device, a device number is not appended to this string. The DeviceIndex value must be zero. The pDriverName value must be a pointer to the same string that was specified as input to VC_OpenProfileAccess.

If pDeviceName is NULL, then the DeviceIndex parameter specifies an index value. This value is appended to the device name “vidcap”, which is defined by DD_VIDCAP_DEVICE_NAME_U in ntddvidc.h. For kernel-mode drivers using VCKernel.lib, this name is used by VC_Init when creating a device object. Thus, specifying a DeviceIndex value allows a user-mode driver to attempt to open a video capture device without specifying a particular device name, such as “bravado”. See the sample msyuv.dll driver for an example of using the DeviceIndex parameter. (For more information about device objects, see the Kernel-Mode Drivers Design Guide.)

The VC_OpenDevice function calls CreateFile, described in the Win32 SDK, to open the device. As a result of this call, the kernel-mode driver receives an IRP_MJ_CREATE function code. When a kernel-mode driver using VCKernel.lib receives this function code, its DeviceOpenFunc function is called.


VC_OpenProfileAccess

PVC_PROFILE_INFO
VC_OpenProfileAccess(
PWCHAR DriverName
);

The VC_OpenProfileAccess function creates a VC_PROFILE structure that is used for storing information needed to access the Service Control Manager and the registry.

Parameters
DriverName
Pointer to a UNICODE driver name string.
Return Value

Returns a pointer to a VC_PROFILE_INFO structure, if the operation succeeds. Otherwise returns NULL. (This structure is private to VCUser.lib.)

Comments

A user-mode driver should call VC_OpenProfileAccess when its DriverProc function receives a DRV_LOAD message.

The string specified for DriverName should be the name of the kernel-mode driver file, without an extension. For example, the string used for the sample Bravado driver is “bravado”. This name is written into the registry by VC_InstallDriver.

See Also

VC_CloseProfileAccess


VC_Overlay

BOOL
VC_Overlay(
VCUSER_HANDLE vh,
BOOL bOverlay
);

The VC_Overlay function requests a kernel-mode video capture driver to enable or disable the hardware’s overlay capabilities, using the current rectangle and key color settings.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
bOverlay
Set TRUE to enable overlay, or FALSE to disable overlay.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

User-mode video capture drivers using VCUser.lib should call the VC_Overlay function with bOverlay set to TRUE when processing a DVM_STREAM_INIT command for the VIDEO_EXTERNALOUT channel. They should call the function with bOverlay set to FALSE when processing a DVM_STREAM_FINI command for the VIDEO_EXTERNALOUT channel.

The VC_Overlay function calls DeviceIoControl (described in the Win32 SDK) to send either an IOCTL_VIDC_OVERLAY_ON or an IOCTL_VIDC_OVERLAY_OFF control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives one of these control codes, its OverlayFunc function is called.

See Also

VC_SetOverlayRect


VC_ReadProfile

DWORD
VC_ReadProfile(
PVC_PROFILE_INFO pProfile,
PWCHAR ValueName,
DWORD dwDefault
);

The VC_ReadProfile function reads the DWORD value associated with the specified value name, under the driver’s \Parameters registry key.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
ValueName
Pointer to a UNICODE string identifying the name of a registry value.
dwDefault
Specifies a default value that is returned if an error occurs locating or reading the requested value.
Return Value

Returns the value assigned to the ValueName string, if the operation succeeds. If the ValueName string does not exist in the registry, cannot be accessed, or is not a REG_DWORD type, the function returns the value specified by dwDefault.

Comments

The value name and value are read from the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

To store values under a driver’s \Parameters registry key, call VC_WriteProfile.

Note: A function named VC_ReadProfile is also provided by VCKernel.lib for kernel-mode video capture drivers. To see that function’s description, click here.

See Also

VC_ReadProfileString, VC_ReadProfileUser, VC_WriteProfile, VC_WriteProfileUser


VC_ReadProfileString

BOOL
VC_ReadProfileString(
PVC_PROFILE_INFO pProfile,
PWCHAR ValueName,
PWCHAR ValueString,
DWORD ValueLength
);

The VC_ReadProfileString function reads the string value associated with the specified value name, under the driver’s \Parameters registry key.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
ValueName
Pointer to a UNICODE string identifying the name of a registry value.
ValueString
Pointer to a buffer to receive the requested string value.
ValueLength
Length, in bytes, of the buffer pointed to by ValueString.
Return Value

Returns TRUE if the operation succeeds. If the ValueName string does not exist in the registry, cannot be accessed, is not a REG_SZ (string) type, or if the specified buffer is not large enough to hold the returned string, the function returns FALSE.

Comments

If the operation succeeds, the VC_ReadProfileString function copies the string value into the specified buffer.

The value name and value are read from the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

See Also

VC_ReadProfile, VC_ReadProfileUser, VC_WriteProfile, VC_WriteProfileUser


VC_ReadProfileUser

DWORD
VC_ReadProfileUser(
PVC_PROFILE_INFO pProfile,
PWCHAR ValueName,
DWORD dwDefault
);

The VC_ReadProfileUser function reads the DWORD value associated with the specified value name, under the user’s profile information in the registry.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
ValueName
Pointer to a UNICODE string identifying the name of a registry value.
dwDefault
Specifies a default value that is returned if an error occurs locating or reading the requested value.
Return Value

Returns the value assigned to the ValueName string, if the operation succeeds. If the ValueName string does not exist in the registry, cannot be accessed, or is not a REG_DWORD type, the function returns the value specified by dwDefault.

Comments

The value name and value are read from the registry path HKEY_CURRENT_USER \Software \Microsoft \Multimedia \Video Capture \DriverName, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

To store values under this registry key, call VC_WriteProfileUser.

See Also

VC_ReadProfile, VC_ReadProfileString, VC_WriteProfile, VC_WriteProfileUser


VC_RemoveDriver

LRESULT
VC_RemoveDriver(
PVC_PROFILE_INFO pProfile
);

The VC_RemoveDriver function unloads a kernel-mode video capture driver and marks the kernel-mode driver service for deletion.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
Return Value

Returns one the following error values.
Value | Definition
DRVCNF_OK | Indicates the driver service has been marked for deletion.
DRVCNF_CANCEL | Indicates an error occurred.

Comments

The VC_RemoveDriver function performs the following operations, in order:

  1. Unloads the kernel-mode driver, if it is loaded.

  2. Sets the kernel-mode driver service’s start type to SERVICE_DEMAND_START, so it will not automatically reload when the system is restarted. (For details, see ChangeServiceConfig in the Win32 SDK.)

  3. Calls DeleteService, described in the Win32 SDK, to mark the kernel-mode driver service for deletion.

A user-mode driver should call VC_RemoveDriver when its DriverProc function receives a DRV_REMOVE message. Before calling VC_RemoveDriver, the driver should call VC_ConfigAccess to determine if the client has Administrators privilege.

The profile information handle specified by pProfile remains open after this function removes the registry entry. Use VC_CloseProfileAccess to close the profile information handle after calling VC_RemoveDriver.


VC_SetKeyColourPalIdx

BOOL
VC_SetKeyColourPalIdx(
VCUSER_HANDLE vh,
WORD PaletteIndex
);

The VC_SetKeyColourPalIdx function requests a kernel-mode video capture driver to set the overlay destination image’s key color to a specified palette index.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
PaletteIndex
Index into the current color palette. This value should be one that can be passed to the PALETTEINDEX macro to obtain a COLORREF value. The PALETTEINDEX macro and the COLORREF type are described in the Win32 SDK.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

Before setting the key color, the driver should call VC_GetOverlayMode and test the VCO_KEYCOLOUR_FIXED flag to determine if the key color can be set. If the color can be set, the driver should test the VCO_KEYCOLOUR_RGB flag to determine if, based on the current data format, the kernel-mode driver stores the key color as an RGB color or as a palette index number. If the key color is stored as an RGB color, the driver should call VC_SetKeyColourRGB to set it. If the key color is stored as a palette index, the driver should call VC_SetKeyColourPalIdx to set it.

The VC_SetKeyColourPalIdx function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_SET_KEY_PALIDX control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its SetKeyPalIdxFunc function is called.

See Also

VC_GetKeyColour, VC_SetKeyColourRGB


VC_SetKeyColourRGB

BOOL
VC_SetKeyColourRGB(
VCUSER_HANDLE vh,
PRGBQUAD pRGB
);

The VC_SetKeyColourRGB function requests a kernel-mode video capture driver to set the overlay destination image’s key color to a specified RGB color.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pRGB
Pointer to an RGBQUAD structure containing an RGB color specification. (The RGBQUAD structure is described in the Win32 SDK.)
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

Before setting the key color, the driver should call VC_GetOverlayMode and test the VCO_KEYCOLOUR_FIXED flag to determine if the key color can be set. If the color can be set, the driver should test the VCO_KEYCOLOUR_RGB flag to determine if, based on the current data format, the kernel-mode driver stores the key color as an RGB color or as a palette index number. If the key color is stored as an RGB color, the driver should call VC_SetKeyColourRGB to set it. If the key color is stored as a palette index, the driver should call VC_SetKeyColourPalIdx to set it.

The VC_SetKeyColourRGB function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_SET_KEY_RGB control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its SetKeyRGBFunc function is called.

See Also

VC_GetKeyColour, VC_SetKeyColourPalIdx


VC_SetOverlayOffset

BOOL
VC_SetOverlayOffset(
VCUSER_HANDLE vh,
PRECT prc
);

The VC_SetOverlayOffset function requests a kernel-mode video capture driver to set the overlay offset rectangle. This rectangle defines which portion of the frame buffer appears in the overlay window.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
prc
Pointer to a RECT structure containing screen coordinates that describe the offset rectangle. (The RECT structure is described in the Win32 SDK.)
Return Value

Returns TRUE if the operation succeeds. Otherwise, returns FALSE.

Comments

User-mode drivers using VCUser.lib typically call VC_SetOverlayOffset when a client sends the DVM_SRC_RECT message for the VIDEO_EXTERNALOUT channel. If the source image is larger than the overlay window, the client typically provides scroll bars to allow the user to pan across the source image, and sends DVM_SRC_RECT messages when the user scrolls the view.

The prc parameter specifies the portion of the frame buffer to send to the overlay window. The kernel-mode driver should display the top left corner of the specified offset rectangle in the top left corner of the overlay window.

The VC_SetOverlayOffset function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_OVERLAY_OFFSET control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its SetOverlayOffsetFunc function is called.


VC_SetOverlayRect

BOOL
VC_SetOverlayRect(
VCUSER_HANDLE vh,
POVERLAY_RECTS pOR
);

The VC_SetOverlayRect function requests a kernel-mode video capture driver to set the overlay region of an output display.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pOR
Pointer to an OVERLAY_RECTS structure describing one or more overlay rectangles, using screen coordinates.
Return Value

Returns TRUE if the operation succeeds. Otherwise, returns FALSE.

Comments

User-mode drivers using VCUser.lib typically call the VC_SetOverlayRect function when a client sends the DVM_DST_RECT message for the VIDEO_EXTERNALOUT channel. The function specifies the portion of the display device to use as an overlay area.

If the OVERLAY_RECTS structure contains a single rectangle, the rectangle defines the overlay region. If the structure contains more than one rectangle, the first rectangle defines the bounding rectangle for the overlay region, and additional rectangles represent parts of the overlay region. All rectangles are specified using screen coordinates.

The driver should specify complex (multiple) rectangles only if the kernel-mode driver returns the VCO_COMPLEX_RECT flag when the VC_GetOverlayMode function is called. The sample video capture drivers do not accept complex rectangle specifications.

The VC_SetOverlayRect function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_OVERLAY_RECTS control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its SetOverlayRectsFunc function is called.


VC_StreamAddBuffer

BOOL
VC_StreamAddBuffer(
VCUSER_HANDLE vh,
LPVIDEOHDR lpvh
);

The VC_StreamAddBuffer function adds a buffer to a video capture driver’s queue of buffers used to receive video capture input frames.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
lpvh
Pointer to a VIDEOHDR structure defining the buffer to be queued.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A user-mode video capture driver using VCUser.lib should call VC_StreamAddBuffer when its DriverProc function receives a DVM_STREAM_ADDBUFFER message.

If the driver has not called VC_StreamStart to start reading frames, the specified buffer is added to the user-mode driver’s buffer queue. If VC_StreamStart has been called, the VC_StreamAddBuffer function tries to send the buffer to the kernel-mode driver. To limit the amount of memory the kernel-mode driver must lock, a maximum of two buffers are queued to the kernel-mode driver at one time. Any additional buffers are queued in the user-mode driver until the kernel-mode driver’s queue contains less than two buffers. The VC_StreamAddBuffer function always returns after the buffer is queued, without waiting for captured data to be placed in the buffer.

To send a buffer to the kernel-mode driver, the VC_StreamAddBuffer function calls DeviceIoControl (described in the Win32 SDK), specifying an IOCTL_VIDC_ADD_BUFFER control code. If the kernel-mode driver is using VCKernel.lib, and if the specified buffer length is smaller than the image size value specified with VC_SetImageSize, the buffer is not queued. If the buffer size is acceptable, VCKernel.lib places the buffer’s I/O request packet (IRP) in a queue, where it stays until needed to receive a captured frame. (For more information about IRPs, see the Kernel-Mode Drivers Design Guide.)

If the kernel-mode driver fails to lock the buffer in the client’s address space (because the buffer is too large for the system’s memory resources), then VCUser.lib sends IOCTL_VIDC_CAP_TO_SYSBUF, IOCTL_VIDC_PARTIAL_CAPTURE, and IOCTL_VIDC_FREE_SYSBUF, control codes to request the kernel-mode driver to copy frame buffer data into a system-allocated buffer. The data is then copied to the client’s buffers as smaller, partial frame sections.


VC_StreamFini

BOOL
VC_StreamFini(
VCUSER_HANDLE vh
);

The VC_StreamFini ends a streaming operation that was initiated by calling VC_StreamInit.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A user-mode video capture driver using VCUser.lib should call VC_StreamFini when its DriverProc function receives a DVM_STREAM_FINI message.

A driver should call the VC_StreamFini function only after it has called VC_StreamStop. The function removes the thread that was created by VC_StreamInit.

The VC_StreamFini function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_STREAM_RESET control code, followed by an IOCTL_VIDC_STREAM_FINI control code, to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives the VIDC_STREAM_FINI control code, its StreamFiniFunc function is called.


VC_StreamInit

BOOL
VC_StreamInit(
VCUSER_HANDLE vh,
PVCCALLBACK pCallback,
ULONG FrameRate
);

The VC_StreamInit function initializes a video capture stream.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
pCallback
Pointer to a client notification target (callback function or window handle) received as input with a DVM_STREAM_INIT message.
FrameRate
Capture rate (microseconds per frame) received as input with a DVM_STREAM_INIT message.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A user-mode video capture driver using VCUser.lib should call VC_StreamInit when its DriverProc function receives a DVM_STREAM_INIT message.

This function performs the following operations:

  • Stores the pCallback value for use when notifying the client. For more information about notifying clients, see Notifying Clients from Video Capture Drivers.

  • Creates a separate user-mode worker thread to handle stream operations.

The VC_StreamInit function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_STREAM_INIT control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its StreamInitFunc function is called. The FrameRate value is passed to the kernel-mode driver along with the IOCTL_VIDC_STREAM_INIT message.

When the initialization operation is complete, the worker thread sends the client an MM_DRVM_OPEN callback message.


VC_StreamReset

BOOL
VC_StreamReset(
VCUSER_HANDLE vh
);

The VC_StreamReset function stops the capture stream, if VC_StreamStop has not been called, and then cancels all queued buffers.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A user-mode video capture driver using VCUser.lib should call VC_StreamReset when its DriverProc function receives a DVM_STREAM_RESET message.

The VC_StreamReset function calls DeviceIoControl (described in the Win32 SDK) to send an IOCTL_VIDC_STREAM_RESET control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its StreamStopFunc function is called if the capture stream has not been previously stopped. Then VCKernel.lib returns unused buffers to the user-mode driver. Code in VCUser.lib marks all queued buffers as done by setting VHDR_DONE in the dwFlags member of each buffer’s VIDEOHDR structure, and sends an MM_DRVM_DATA callback message to the client for each buffer.

See Also

VC_StreamStop


VC_StreamStart

BOOL
VC_StreamStart(
VCUSER_HANDLE vh
);

The VC_StreamStart function starts a capture stream.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A user-mode video capture driver using VCUser.lib should call VC_StreamStart when its DriverProc function receives a DVM_STREAM_START message.

A driver cannot call VC_StreamStart unless it has previously called VC_StreamInit.

If the driver has previously called VC_StreamAddBuffer to add buffers to its local queue, the VC_StreamStart function calls DeviceIoControl (described in the Win32 SDK), specifying an IOCTL_VIDC_ADD_BUFFER control code, to send up to two buffers to the kernel-mode driver.

The VC_StreamStart function then calls DeviceIoControl to send an IOCTL_VIDC_STREAM_START control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its StreamStartFunc function is called to start the capturing of input data.

Each time the kernel-mode driver fills a buffer and returns it to the user-mode driver, code in VCUser.lib sets VHDR_DONE in the dwFlags member of the buffer’s VIDEOHDR structure, and sends an MM_DRVM_DATA callback message to the client.


VC_StreamStop

BOOL
VC_StreamStop(
VCUSER_HANDLE vh
);

The VC_StreamStop function stops a capture stream.

Parameters
vh
Handle to the kernel-mode driver, obtained from VC_OpenDevice.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A user-mode video capture driver using VCUser.lib should call VC_StreamStop when its DriverProc function receives a DVM_STREAM_STOP message.

The VC_StreamStop function then calls DeviceIoControl to send an IOCTL_VIDC_STREAM_STOP control code to the specified kernel-mode driver. When a kernel-mode driver using VCKernel.lib receives this control code, its StreamStopFunc function is called.

After your driver calls VC_StreamStop, kernel-mode driver will fill and return the buffer it is currently using, but it will not dequeue any more buffers.


VC_WriteProfile

BOOL
VC_WriteProfile(
PVC_PROFILE_INFO pProfile,
PWCHAR ValueName,
DWORD Value
);

The VC_WriteProfile function assigns the specified DWORD value to the specified value name, under the driver’s \Parameters registry key.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
ValueName
Pointer to a UNICODE string identifying the name of a registry value.
Value
Value to be assigned to the ValueName in the registry.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The value name and value are written to the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

If the \Parameters subkey does not exist, it is created. If the specified value name does not exist under the \Parameters subkey, it is created.

To read values from a driver’s \Parameters registry key, call VC_ReadProfile.

Note: A function named VC_WriteProfile is also provided by VCKernel.lib for kernel-mode video capture drivers. To see that function’s description, click here.

See Also

VC_ReadProfile, VC_ReadProfileString, VC_ReadProfileUser, VC_WriteProfileUser


VC_WriteProfileUser

BOOL
VC_WriteProfileUser(
PVC_PROFILE_INFO pProfile,
PWCHAR ValueName,
DWORD Value
);

The VC_WriteProfileUser function assigns the specified DWORD value to the specified value name, under the user’s profile information in the registry.

Parameters
pProfile
Address of the VC_PROFILE_INFO structure returned by VC_OpenProfileAccess.
ValueName
Pointer to a UNICODE string identifying the name of a registry value.
Value
Value to be assigned to ValueName in the registry.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The value name and value are written to the registry path HKEY_CURRENT_USER \Software \Microsoft \Multimedia \Video Capture \DriverName, where DriverName is the driver name specified as input to VC_OpenProfileAccess. If the specified value name does not exist, it is created.

To read values from this registry key, call VC_ReadProfileUser.

See Also

VC_ReadProfile, VC_ReadProfileString, VC_ReadProfileUser, VC_WriteProfile


Structures, VCUser.lib

This section describes the structures available to user-mode video capture drivers using VCUser.lib.


CONFIG_INFO

typedef struct _CONFIG_INFO {
ULONG ulSize;
BYTE ulData[1];
} CONFIG_INFO, *PCONFIG_INFO;

The CONFIG_INFO structure is used to describe a customized structure of configuration information. It is defined in vcstruct.h.

Members
ulSize
Size of the customized structure.
ulData[1]
First data member of the customized structure.
Comments

The CONFIG_INFO structure is a generic structure that allows you to define customized structures for storing configuration information, and to pass pointers to those structures from your user-mode driver to your kernel-mode driver with calls to VC_ConfigDisplay, VC_ConfigFormat, and VC_ConfigSource.

To pass configuration information to your kernel-mode driver, define customized structures, fill them, and then cast them to the PCONFIG_INFO type when passing them to VC_ConfigDisplay, VC_ConfigFormat, or VC_ConfigSource. See the sample video capture drivers for examples.

Because the structures are passed from user mode to kernel mode, you cannot include pointers as structure members.


DRAWBUFFER

typedef struct _DRAWBUFFER {
PUCHAR  lpData;
ULONG   ulWidth;
ULONG   ulHeight;
ULONG   Format;
RECT    rcSource;
} DRAWBUFFER, *PDRAWBUFFER;

The DRAWBUFFER structure describes the data, frame size, and format for drawing a video frame. It is defined in vcstruct.h.

Members
lpData
Pointer to frame data.
ulWidth
Frame width in pixels.
ulHeight
Frame height in pixels.
Format
Video data format.
rcSource
Rectangle describing portion of the frame to be drawn.
Comments

User-mode video capture drivers use the DRAWBUFFER structure when calling VC_DrawFrame. The ulWidth and ulHeight members describe the entire frame. The rcSource member specifies the portion of the frame to be drawn.


OVERLAY_RECTS

typedef struct _OVERLAY_RECTS {
    ULONG ulCount;      // total number of rects in array
    RECT rcRects[1];    // ulCount rectangles
} OVERLAY_RECTS, *POVERLAY_RECTS;

The OVERLAY_RECTS structure describes the rectangles constituting the active area of an overlay display. The structure is defined in vcstruct.h.

Members
ulCount
Number of rectangles specified by rcRects.
rcRects[1]
Array of RECT structures containing rectangle descriptions. Rectangles are specified in screen coordinates. The RECT structure is described in the Win32 SDK.
Comments

User-mode video capture drivers use the OVERLAY_RECTS structure when calling VC_SetOverlayRect.


VCCALLBACK

typedef struct _VCCALLBACK {
    DWORD dwCallback;
    DWORD dwFlags;
    HDRVR hDevice;
    DWORD dwUser;
} VCCALLBACK, *PVCCALLBACK;

The VCCALLBACK structure contains callback information needed by the VC_StreamInit function. The structure is defined in vcuser.h.

Members
dwCallback
Contains either the address of a callback function, a window handle, or NULL, based on flags set in the dwFlags member. The driver should copy the value of the VIDEO_STREAM_INIT_PARMS structure’s dwCallback member into this member.
dwFlags
Contains flags. Can contain one (or none) of the following flags.

Flag

Definition

CALLBACK_WINDOW

Indicates dwCallback contains a window handle.

CALLBACK_FUNCTION

Indicates dwCallback contains a callback function address.

The driver should copy the value of the VIDEO_STREAM_INIT_PARMS structure’s dwFlags member into this member.

hDevice
Contains a handle to a video channel. The driver should copy the value of the VIDEO_STREAM_INIT_PARMS structure’s hVideo member into this member.
dwUser
Contains client-specified instance data passed to the callback function, if CALLBACK_FUNCTION is set in dwFlags. The driver should copy the value of the VIDEO_STREAM_INIT_PARMS structure’s dwCallbackInst member into this member.
Comments

As indicated by the preceding member descriptions above, the VCCALLBACK structure is used to pass VIDEO_STREAM_INIT_PARMS structure members to VC_StreamInit.


Functions, VCKernel.lib

This section describes the functions available to kernel-mode video capture drivers using VCKernel.lib. The functions are listed in alphabetical order.


VC_AccessData

BOOLEAN
VC_AccessData(
PDEVICE_INFO pDevInfo,
PUCHAR pData,
ULONG Length,
PACCESS_ROUTINE pAccessFunc,
PVOID pContext
);

The VC_AccessData function provides a means by which kernel-mode video capture drivers can be protected from encountering access violations when referencing user-mode data.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pData
Pointer to user-mode data.
Length
Length of data specified by pData.
pAccessFunc
Pointer to a callback function that accesses the specified data. The callback function must use the following prototype format:
BOOLEAN AccessFunc (PDEVICE_INFO pDevInfo, PUCHAR pData, ULONG Length, PVOID pContext);
pContext
Pointer to driver-supplied context information that is passed to the callback function.
Return Value

If the data specified by pData can be accessed, the function returns the value returned by the callback function. Otherwise the function returns FALSE.

Comments

The kernel-mode driver supplies the address of data to be accessed, along with the address of a callback function that performs an operation on the data, such as copying a bitmap into the frame buffer. The VC_AccessData function wraps the callback function in an exception handler so that if an access violation occurs, the kernel-mode driver can continue to execute. If an access violation occurs, the exception handler returns FALSE.

The function can only be called within the context of the user’s thread. It must not be called from the InterruptAcknowledge or CaptureService functions, because they execute within a system context.


VC_AllocMem

PVOID
VC_AllocMem(
PDEVICE_INFO pDevInfo,
ULONG Length
);

The VC_AllocMem function allocates a specified amount of nonpaged memory for use by a kernel-mode video capture driver.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Length
Size, in bytes, of memory to be allocated.
Return Value

Returns a pointer to the allocated memory, if the operation succeeds. Otherwise returns NULL.

Comments

The VC_AllocMem function calls ExAllocatePool, with a pool type of NonPagedPool. The allocated memory can be referenced by the driver’s InterruptAcknowledge and CaptureService function.


VC_Cleanup

VOID
VC_Cleanup(
PDRIVER_OBJECT pDriverObject
);

The VC_Cleanup function deallocates the system resources that were allocated by a previous call to VC_Init.

Parameters
pDriverObject
The driver object pointer received as input to the driver’s DriverEntry function.
Return Value

None.

Comments

A kernel-mode video capture driver should call VC_Cleanup if a failure occurs within its DriverEntry function after VC_Init has been called.

Additionally, the VCKernel.lib library specifies VC_Cleanup as the function to be called when the kernel-mode driver is unloaded (by assigning the function’s address to the driver object’s DriverUnload member).

The VC_Cleanup function performs the following operations, in the order listed:

  1. Cancels outstanding IRPs.

  2. Calls the kernel-mode driver’s CleanupFunc function.

  3. Unmaps mapped I/O memory space.

  4. Removes the device’s interrupt objects.

  5. Releases system resources (interrupt number, DMA channel, and so on) reserved for the device.

  6. Removes the device object associated with the device.

For more information about driver objects, device objects and interrupt objects, see the Kernel-Mode Drivers Design Guide.


VC_ConnectInterrupt

BOOLEAN
VC_ConnectInterrupt(
PDEVICE_INFO pDevInfo,
ULONG Interrupt,
BOOLEAN bLatched
);

The VC_ConnectInterrupt function creates a connection between the specified interrupt number and a kernel-mode video capture driver’s interrupt service routine.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Interrupt
Interrupt number to use.
bLatched
Set to TRUE if the interrupt is latched or FALSE if the interrupt is level-sensitive.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

The VC_ConnectInterrupt function calls HalGetInterruptVector to obtain a system interrupt vector and IoConnectInterrupt to connect VCKernel.lib’s generic ISR to the interrupt vector. (When an interrupt occurs, VCKernel.lib’s generic ISR calls the driver’s InterruptAcknowledge function.)

Before calling VC_ConnectInterrupt, a kernel-mode driver must call VC_GetResources. It must also call VC_GetCallbackTable and fill in the returned callback table.


VC_Delay

VOID
VC_Delay(
int nMillisecs
);

The VC_Delay function delays execution of a kernel-mode video capture driver’s current thread.

Parameters
nMillisecs
Delay interval, in milliseconds.
Return Value

None.

Comments

The VC_Delay function puts the calling kernel-mode thread into a non-alertable wait state, in kernel mode, for at least the specified number of milliseconds.

To call VC_Delay, your driver’s IRQL must be less than DISPATCH_LEVEL.

See Also

VC_Stall


VC_FreeMem

VOID
VC_FreeMem(
PDEVICE_INFO pDevInfo,
PVOID pData,
ULONG Length
);

The VC_FreeMem function frees memory space that was allocated by calling VC_AllocMem.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pData
Pointer that was returned by VC_AllocMem.
Length
Size of memory allocation that was requested using VC_AllocMem.
Return Value

None.

Comments

The VC_FreeMem function calls ExFreePool to deallocate the specified memory space.


VC_GetCallbackTable

PVC_CALLBACK
VC_GetCallbackTable(
PDEVICE_INFO pDevInfo
);

The VC_GetCallbackTable function returns the address of VCKernel.lib’s callback table.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Pointer to a VC_CALLBACK structure.

Comments

The VC_GetCallbackTable function returns the address of a VC_CALLBACK structure. Kernel-mode video capture drivers using VCKernel.lib must call this function and then fill in the VC_CALLBACK structure with the addresses of the driver’s callback functions. Code in VCKernel.lib uses the structure as a dispatch table when calling the driver’s callback functions.

The driver must call VC_GetCallbackTable and fill in the table from within its DriverEntry function. Because the table contains the addresses of interrupt handlers, it should be filled in before the driver attempts to initialize hardware, in case the initialization process generates interrupts.


VC_GetFrameBuffer

PUCHAR
VC_GetFrameBuffer(
PDEVICE_INFO pDevInfo
);

The VC_GetFrameBuffer function returns a pointer to the system address space corresponding to the device’s frame buffer.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Pointer to the system address space to which the frame buffer is mapped.

Comments

A kernel-mode driver must call the VC_GetFrameBuffer function to obtain the mapped address of the device’s frame buffer. The frame buffer’s bus-relative physical address range is mapped to nonpaged system space by the VC_GetResources function.


VC_GetHWInfo

PVOID
VC_GetHWInfo(
PDEVICE_INFO pDevInfo
);

The VC_GetHWInfo function returns a pointer to the driver-specified structure that was allocated by a previous call to VC_Init.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Pointer to the driver-specified structure.

Comments

When a kernel-mode video capture driver calls VC_Init, it specifies the size of a structure for which nonpaged space is allocated within the device object’s device extension area. The driver can call VC_GetHWInfo to obtain the address of the area that was allocated for this structure. Drivers can use this structure to store device-specific context information.

For more information about device objects and device extensions, see the Kernel-Mode Drivers Reference.


VC_GetResources

BOOLEAN
VC_GetResources(
PDEVICE_INFO pDevInfo,
PDRIVER_OBJECT pDriverObject,
PUCHAR pPortBase,
ULONG NrOfPorts,
ULONG Interrupt,
BOOLEAN bLatched,
PUCHAR pFrameBuffer,
ULONG FrameLength,
 );

The VC_GetResources function reserves system resources for a device, and maps the device’s I/O address space and frame buffer into system address space.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pDriverObject
The driver object pointer received as input to the driver’s DriverEntry function.
pPortBase
Bus-relative physical base address of the device’s I/O port address space.
NrOfPorts
Number of port addresses.
Interrupt
Interrupt number.
bLatched
Set to TRUE if the interrupt is latched or FALSE if the interrupt is level-sensitive.
pFrameBuffer
Bus-relative physical base address of the device’s frame buffer.
FrameLength
Length of the frame buffer.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE.

Comments

A kernel-mode video capture driver using VCKernel.lib must call VC_GetResources from within its DriverEntry function. The VC_GetResources function performs the following operations, in the order listed:

  1. Determines the bus type.

  2. Maps the device’s I/O port space to system space.

  3. Maps the device’s frame buffer to system space.

  4. Reserves the interrupt number, mapped I/O port space, and mapped frame buffer space for use by the device.

The VC_GetResources function calls HalTranslateBusAddress, MmMapIoSpace, and IoReportResourceUsage.


VC_In

BYTE
VC_In(
PDEVICE_INFO pDevInfo,
BYTE bOffset
);

The VC_In function reads one byte from a device’s mapped I/O port address space.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
bOffset
Offset from the mapped I/O port’s base address.
Return Value

Byte contents of the specified port address offset.

Comments

A kernel-mode video capture driver specifies the base address of a device’s I/O port address space when calling VC_GetResources. The bOffset parameter to VC_In specifies an offset from that base address.

See Also

VC_Out


VC_Init

PDEVICE_INFO
VC_Init(
PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING szRegistryPathName,
ULONG HWInfoSize
);

The VC_Init function creates a device object and stores the device name in the registry.

Parameters
pDriverObject
The driver object pointer that was received as input to the driver’s DriverEntry function.
szRegistryPathName
Pointer to a string containing the registry path to the driver’s subkey. Use the path name that was received as the RegistryPathName argument to DriverEntry.
HWInfoSize
Size, in bytes, of a driver-defined structure used for storing device-specific context information. Can be zero.
Return Value

Returns a pointer to a DEVICE_INFO structure.

Comments

A kernel-mode video capture driver using VCKernel.lib must call VC_Init from within its DriverEntry function, before calling any other VCKernel.lib functions.

The DEVICE_INFO structure pointer returned by VC_Init is used as input to subsequent calls to other VCKernel.lib functions. Contents of the DEVICE_INFO structure are not available to the kernel-mode driver.

The driver uses the HWInfoSize parameter to specify the size of a driver-defined structure. The VC_Init function allocates nonpaged space for this structure within the device object’s device extension area. The driver can call VC_GetHWInfo to obtain the address of the area that was allocated for the structure. Drivers can use this structure to store device-specific context information. (For more information about device objects and device extensions, see the Kernel-Mode Drivers Design Guide.)

The VC_Init function calls IoCreateDevice to create a device object. The device’s name is “vidcap”, which is defined by DD_VIDCAP_DEVICE_NAME_U in ntddvidc.h, with an appended number (0, 1, 2, and so on). The function writes this device name into the registry, under the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters, where DriverName is the driver name that the user-mode driver specified as input to VC_OpenProfileAccess when it installed the kernel-mode driver.


VC_Out

VOID
VC_Out(
PDEVICE_INFO pDevInfo,
BYTE bOffset,
BYTE bData
);

The VC_Out function writes one byte into a device’s mapped I/O port address space.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
bOffset
Offset from the mapped I/O port’s base address.
bData
Data byte to be written.
Return Value

None.

Comments

A kernel-mode video capture driver specifies the base address of a device’s I/O port address space when calling VC_GetResources. The bOffset parameter to VC_Out specifies an offset from that base address.

See Also

VC_In


VC_ReadProfile

DWORD
VC_ReadProfile(
PDEVICE_INFO pDevInfo,
PWCHAR szValueName,
DWORD dwDefault
);

The VC_ReadProfile function reads the DWORD value associated with the specified value name, under the driver’s \Parameters registry key.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
szValueName
Pointer to a UNICODE string identifying the name of a registry value.
dwDefault
Specifies a default value that is returned if an error occurs locating or reading the requested value.
Return Value

Returns the value assigned to the ValueName string, if the operation succeeds. Otherwise the function returns the value specified by dwDefault.

Comments

The value name and value are read from the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

To store values under a driver’s \Parameters registry key, call VC_WriteProfile.

Note: A function named VC_ReadProfile is also provided by VCUser.lib for user-mode video capture drivers. To see that function’s description, click here.


VC_SetImageSize

VOID
VC_SetImageSize(
PDEVICE_INFO pDevInfo,
int ImageSize
);

The VC_SetImageSize function specifies the maximum number of bytes needed to store an image using the current format.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
ImageSize
Maximum number of bytes needed to store an image.
Comments

A kernel-mode video capture driver should call VC_SetImageSize whenever a new data format is selected. The number specified for ImageSize should represent the largest number of bytes that an image can be, using the current format. In other words, the value should represent the smallest size that a buffer can be in order to store an entire capture frame, using the current format.

Code within VCKernel.lib uses the specified ImageSize value to determine if each buffer received with an IOCTL_VIDC_ADD_BUFFER control code is large enough to receive the frame buffer contents. Additionally, if the system’s memory resources are too low to allow locking of client-specified buffers into the client’s address space, VCKernel.lib allocates a system buffer of ImageSize size to receive the frame buffer contents, which are then copied into the client’s buffers in smaller segments.

See Also

VC_StreamAddBuffer


VC_Stall

VOID
VC_Stall(
int nMicrosecs
);

The VC_Stall function stalls the current processor for the specified number of microseconds.

Parameters
nMicrosecs
Number of microseconds to stall.
Return Value

None.

Comments

The VC_Stall function causes the current processor to execute a processor-specific wait loop until the specified time has passed. The function is useful for pausing between device access operations, if there is a potential for fast processors to send instructions to the device at a speed that is too high for the device.

Delays longer than 25 microseconds are not recommended.

Your driver can call VC_Install when executing at any IRQL.

See Also

VC_Delay


VC_SynchronizeDPC

BOOLEAN
VC_SynchronizeDPC(
PDEVICE_INFO pDevInfo,
PSYNC_ROUTINE pSync,
PVOID pContext
);

The VC_SynchronizeDPC function synchronizes access to objects that are referenced by a kernel-mode video capture driver’s CaptureService function.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pSync
Pointer to a callback function. The callback function must use the following prototype format:
BOOLEAN pSync(PVOID pContext);
pContext
Pointer to context information to be passed to the callback function.
Return Value

Returns the callback function’s return value.

Comments

A driver’s CaptureService function executes at an IRQL of DISPATCH_LEVEL. If other code within the driver must access the same objects (frame buffers, data structures, and so on) that the CaptureService function references, then all code that references the objects, including the code within CaptureService, must be synchronized by using VC_SynchronizeDPC.

To use VC_SynchronizeDPC, place each piece of code that references the objects to be protected into a callback function. Specify each callback function as a pSync parameter to a VC_SynchronizeDPC call. Typically, you use the pContext parameter to indicate the objects to be referenced.

The VC_SynchronizeDPC function acquires a spin lock and executes the callback function at an IRQL of DISPATCH_LEVEL. If you access an object only within code that is included in VC_SynchronizeDPC callbacks, then other processors cannot simultaneously access the object, and lower-priority code on the current processor cannot obtain access.

Code that calls VC_SynchronizeDPC must be executing at an IRQL of DISPATCH_LEVEL or lower.

See Also

VC_SynchronizeExecution


VC_SynchronizeExecution

BOOLEAN
VC_SynchronizeExecution(
PDEVICE_INFO pDevInfo,
PSYNC_ROUTINE pSync,
PVOID pContext
);

The VC_SynchronizeExecution function synchronizes access to objects that are referenced by a kernel-mode video capture driver’s InterruptAcknowledge function.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pSync
Pointer to a callback function. The callback function must use the following prototype format:
BOOLEAN pSync(PVOID pContext);
pContext
Pointer to context information to be passed to the callback function.
Return Value

Returns the callback function’s return value.

Comments

A driver’s InterruptAcknowledge function executes at a device IRQL (DIRQL). If other code within the driver must access the same objects (generally, device registers) that the InterruptAcknowledge function references, then all code that references the objects, except the code within InterruptAcknowledge, must be synchronized by using VC_SynchronizeExecution.

To use VC_SynchronizeExecution, place each piece of code that references the objects to be protected into a callback function. Specify each callback function as a pSync parameter to a VC_SynchronizeExecution call. Typically, you use the pContext parameter to indicate the objects to be referenced.

The VC_SynchronizeExecution function calls the system’s KeSynchronizeExecution function to acquire a spin lock and execute the callback function at the same DIRQL that the InterruptAcknowledge function uses. If you access an object only within InterruptAcknowledge or within code that is included in VC_SynchronizeExecution callbacks, then other processors cannot simultaneously access the object, and lower-priority code on the current processor cannot obtain access.

Code that calls VC_SynchronizeExecution must be executing at the device’s DIRQL or lower. A driver’s CaptureService function, which executes at an IRQL of DISPATCH_LEVEL, can call VC_SynchronizeExecution.

The driver’s InterruptAcknowledge function should not call VC_SynchronizeExecution, because the Windows NT I/O Manager handles its synchronization.

See Also

VC_SynchronizeDPC


VC_WriteProfile

BOOL
VC_WriteProfile(
PDEVICE_INFO pDevInfo,
PWCHAR szValueName,
DWORD ValueData
);

The VC_WriteProfile function assigns the specified DWORD value to the specified value name, under the driver’s \Parameters registry key.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
szValueName
Pointer to a UNICODE string identifying the name of a registry value.
ValueData
Value to be assigned to ValueName in the registry.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns false.

Comments

The value name and value are written to the registry path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters, where DriverName is the driver name specified as input to VC_OpenProfileAccess.

To read values from a driver’s \Parameters registry key, call VC_ReadProfile.

Note: A function named VC_WriteProfile is also provided by VCUser.lib for user-mode video capture drivers. To see that function’s description, click here.


Structures, VCKernel.lib

This section describes the structures available to kernel-mode video capture drivers using VCKernel.lib.


VC_CALLBACK

typedef struct _VC_CALLBACK {
    BOOLEAN (*DeviceOpenFunc)(PDEVICE_INFO);
    BOOLEAN (*DeviceCloseFunc)(PDEVICE_INFO);
    BOOLEAN (*ConfigFormatFunc)(PDEVICE_INFO, PCONFIG_INFO);
    BOOLEAN (*ConfigDisplayFunc)(PDEVICE_INFO, PCONFIG_INFO);
    BOOLEAN (*ConfigSourceFunc)(PDEVICE_INFO, PCONFIG_INFO);
    DWORD   (*GetOverlayModeFunc) (PDEVICE_INFO);
    BOOLEAN (*SetKeyRGBFunc)(PDEVICE_INFO, PRGBQUAD);
    BOOLEAN (*SetKeyPalIdxFunc)(PDEVICE_INFO, ULONG);
    BOOLEAN (*SetOverlayRectsFunc)(PDEVICE_INFO, POVERLAY_RECTS);
    BOOLEAN (*SetOverlayOffsetFunc)(PDEVICE_INFO, PRECT);
    ULONG   (*GetKeyColourFunc)(PDEVICE_INFO);
    BOOLEAN (*CaptureFunc)(PDEVICE_INFO, BOOL);
    BOOLEAN (*OverlayFunc)(PDEVICE_INFO, BOOL);
    BOOLEAN (*StreamInitFunc)(PDEVICE_INFO, ULONG);
    BOOLEAN (*StreamFiniFunc)(PDEVICE_INFO);
    BOOLEAN (*StreamStartFunc)(PDEVICE_INFO);
    BOOLEAN (*StreamStopFunc)(PDEVICE_INFO);
    ULONG   (*StreamGetPositionFunc)(PDEVICE_INFO);
    BOOLEAN (*InterruptAcknowledge)(PDEVICE_INFO);
    ULONG (*CaptureService)(PDEVICE_INFO, PUCHAR, PULONG, ULONG);
    BOOLEAN (*DrawFrameFunc)(PDEVICE_INFO, PDRAWBUFFER);
    BOOLEAN (*CleanupFunc)(PDEVICE_INFO);
} VC_CALLBACK, * PVC_CALLBACK;

The VC_CALLBACK structure is a dispatch table used by VCKernel.lib to call functions provided by kernel-mode video capture drivers. The structure is defined in vckernel.h.

Members
DeviceOpenFunc
Pointer to a kernel-mode video capture driver’s DeviceOpenFunc function.
DeviceCloseFunc
Pointer to a kernel-mode video capture driver’s DeviceCloseFunc function.
ConfigFormatFunc
Pointer to a kernel-mode video capture driver’s ConfigFormatFunc function.
ConfigDisplayFunc
Pointer to a kernel-mode video capture driver’s ConfigDisplayFunc function.
ConfigSourceFunc
Pointer to a kernel-mode video capture driver’s ConfigSourceFunc function.
GetOverlayModeFunc
Pointer to a kernel-mode video capture driver’s GetOverlayModeFunc function.
SetKeyRGBFunc
Pointer to a kernel-mode video capture driver’s SetKeyRGBFunc function.
SetKeyPalIdxFunc
Pointer to a kernel-mode video capture driver’s SetKeyPalIdxFunc function.
SetOverlayRectsFunc
Pointer to a kernel-mode video capture driver’s SetOverlayRectsFunc function.
SetOverlayOffsetFunc
Pointer to a kernel-mode video capture driver’s SetOverlayOffsetFunc function.
GetKeyColourFunc
Pointer to a kernel-mode video capture driver’s GetKeyColourFunc function.
CaptureFunc
Pointer to a kernel-mode video capture driver’s CaptureFunc function.
OverlayFunc
Pointer to a kernel-mode video capture driver’s OverlayFunc function.
StreamInitFunc
Pointer to a kernel-mode video capture driver’s StreamInitFunc function.
StreamFiniFunc
Pointer to a kernel-mode video capture driver’s StreamFiniFunc function.
StreamStartFunc
Pointer to a kernel-mode video capture driver’s StreamStartFunc function.
StreamStopFunc
Pointer to a kernel-mode video capture driver’s StreamStopFunc function.
StreamGetPositionFunc
Pointer to a kernel-mode video capture driver’s StreamGetPositionFunc function.
InterruptAcknowledge
Pointer to a kernel-mode video capture driver’s InterruptAcknowledge function.
CaptureService
Pointer to a kernel-mode video capture driver’s CaptureService function.
DrawFrameFunc
Pointer to a kernel-mode video capture driver’s DrawFrameFunc function.
CleanupFunc
Pointer to a kernel-mode video capture driver’s CleanupFunc function.
Comments

A kernel-mode video capture driver using VCKernel.lib is responsible for filling in VCKernel.lib’s VC_CALLBACK structure. The driver obtains the structure’s address by calling VC_GetCallbackTable. The driver should obtain the structure’s address and fill in the table from within its DriverEntry function, before hardware initialization is attempted.


Driver Functions Used with VCKernel.lib

This section describes, in alphabetic order, the driver-supplied functions that kernel-mode video capture drivers must provide, if they are using VCKernel.lib.


CaptureFunc

BOOLEAN
CaptureFunc(
PDEVICE_INFO pDevInfo,
BOOL bCapture
);

The CaptureFunc function is provided by kernel-mode video capture drivers to enable and disable capturing video data. The CaptureFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
bCapture
Set to TRUE if data capture is to be enabled, and FALSE if it is to be disabled.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s CaptureFunc function when the driver receives an IOCTL_VIDC_CAPTURE_ON or IOCTL_VIDC_CAPTURE_OFF control code. User-mode drivers using VCUser.lib send this control code by calling VC_Capture.

The driver might also call the CaptureFunc function itself, while transferring streams of captured data. The driver typically disables data acquisition between the time the frame buffer has been filled and the time the driver has finished copying its contents, and then re-enables it.

Support for a CaptureFunc function is required. The driver must place the address of its CaptureFunc function in the VC_CALLBACK structure supplied by VCKernel.lib. If the driver does not support the function, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST if the driver receives an IOCTL_VIDC_CAPTURE_ON or IOCTL_VIDC_CAPTURE_OFF control code.

If possible, the function should disable data acquisition in a manner that freezes the current overlay display.


CaptureService

ULONG
CaptureService(
PDEVICE_INFO pDevInfo,
PUCHAR pBuffer,
PULONG pTimeStamp,
ULONG BufferLength
);

The CaptureService function is a kernel-mode video capture driver’s deferred procedure call (DPC) function, used for copying captured video data from the frame buffer to client-supplied buffers. The function is driver-defined, and the CaptureService name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pBuffer
Pointer to a page-locked buffer. Can be NULL (see the following Comments section).
pTimeStamp
Address of a ULONG location to return the time, in milliseconds, at which the frame was captured
BufferLength
Size, in bytes, of the buffer specified by pBuffer.
Return Value

If the driver has finished using the buffer pointed to by pBuffer, it should return the number of bytes written to the buffer. If the driver returns zero and pBuffer is not NULL, VCKernel.lib will return the same buffer the next time it calls CaptureService.

Comments

The VCKernel.lib library provides a generic DPC function that is called each time the driver’s InterruptAcknowledge function returns TRUE. This generic DPC function calls the driver’s CaptureService function, which is responsible for copying the current frame buffer contents into a client buffer. The function executes at an IRQL of DISPATCH_LEVEL.

The CaptureService function receives, in pBuffer, a pointer to the next available buffer, which has been page-locked. (This buffer usually is client-supplied, but if the system has limited memory resources, the buffer might be one that VCKernel.lib allocated from system space. This situation is irrelevant to the driver.)

If pBuffer is NULL, no buffers are available. In this case, the driver should drop the frame contents (typically by just returning zero), and VCKernel.lib will increment its count of dropped frames.

If pBuffer is not NULL, and if the driver does not completely fill the specified buffer with a single frame, it can return zero to indicate that VCKernel.lib should specify the same buffer the next time it calls CaptureService.

The driver returns a time stamp that is relative to the beginning of the stream and is reset by the driver’s StreamStartFunc function. The time stamp should be recorded by the driver’s InterruptAcknowledge function ¾ not its CaptureService function ¾ because there can be a time delay before the latter function is called.

If other code within the kernel-mode driver references the same objects that the CaptureService function references, the driver must use VC_SynchronizeDPC to synchronize access to the objects.

Support for a CaptureService function is required. The driver must place the address of its CaptureService function in the VC_CALLBACK structure supplied by VCKernel.lib.

For more information about deferred procedure calls (DPCs) see the Kernel Mode Drivers Design Guide.


CleanupFunc

BOOLEAN
CleanupFunc(
PDEVICE_INFO pDevInfo
);

The CleanupFunc function performs operations that must be completed before a kernel-mode video capture driver is unloaded. The function is provided by the driver, and the CleanupFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. (The VCKernel.lib library does not currently test the return value.)

Comments

A kernel-mode driver’s CleanupFunc function is called by the VCKernel.lib library’s VC_Cleanup function. The CleanupFunc function might disable hardware and deallocate memory space that was allocated by the driver’s DriverEntry function.

Support for a CleanupFunc function is optional. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib.

See Also

DeviceCloseFunc


ConfigDisplayFunc

BOOLEAN
ConfigDisplayFunc(
PDEVICE_INFO pDevInfo,
PCONFIG_INFO pConfig
);

The ConfigDisplayFunc function sets characteristics of the overlay display. The function is provided by kernel-mode video capture drivers, and the ConfigDisplayFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pConfig
Pointer to a CONFIG_INFO structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s ConfigDisplayFunc function when the driver receives an IOCTL_VIDC_CONFIG_DISPLAY control code. User-mode drivers using VCUser.lib send this control code by calling VC_ConfigDisplay.

A kernel-mode driver typically uses its ConfigDisplayFunc function to set display hardware parameters, based on information that the user-mode driver has provided in the CONFIG_INFO structure.

Support for a ConfigDisplayFunc function is required, if the device provides overlay capabilities. The driver must place the address of its ConfigDisplayFunc function in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a ConfigDisplayFunc function receives an IOCTL_VIDC_CONFIG_DISPLAY control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


ConfigFormatFunc

BOOLEAN
ConfigFormatFunc(
PDEVICE_INFO pDevInfo,
PCONFIG_INFO pConfig
);

The ConfigFormatFunc function sets video data format characteristics within a kernel-mode video capture driver. The function is provided by the driver, and the ConfigFormatFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pConfig
Pointer to a CONFIG_INFO structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s ConfigFormatFunc function when the driver receives an IOCTL_VIDC_CONFIG_FORMAT control code. User-mode drivers using VCUser.lib send this control code by calling VC_ConfigFormat.

A kernel-mode driver typically uses its ConfigFormatFunc function to set format-based information, such as color translation table contents, scaling parameters, and image size, based on information that the user-mode driver has provided in the CONFIG_INFO structure. The function should also call VC_SetImageSize to notify VCKkernel.lib of the maximum image size.

Support for a ConfigFormatFunc function is required. The driver must place the address of its ConfigFormatFunc function in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a ConfigFormatFunc function receives an IOCTL_VIDC_CONFIG_FORMAT control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


ConfigSourceFunc

BOOLEAN
ConfigSourceFunc(
PDEVICE_INFO pDevInfo,
PCONFIG_INFO pConfig
);

The ConfigSourceFunc function sets characteristics of the video source. The function is provided by kernel-mode video capture drivers, and the ConfigSourceFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pConfig
Pointer to a CONFIG_INFO structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s ConfigSourceFunc function when the driver receives an IOCTL_VIDC_CONFIG_SOURCE control code. User-mode drivers using VCUser.lib send this control code by calling VC_ConfigSource.

A kernel-mode driver typically uses its ConfigSourceFunc function to set video source hardware parameters, based on information that the user-mode driver has provided in the CONFIG_INFO structure.

Support for a ConfigSourceFunc function is required. The driver must place the address of its ConfigSourceFunc function in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a ConfigSourceFunc function receives an IOCTL_VIDC_CONFIG_SOURCE control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


DeviceCloseFunc

BOOLEAN
DeviceCloseFunc(
PDEVICE_INFO pDevInfo
 );

The DeviceCloseFunc function performs operations that must be completed when a video capture device is closed. The function is provided by the kernel-mode driver, and the DeviceCloseFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Coments

The VCKernel.lib library calls the kernel-mode driver’s DeviceCloseFunc function when it receives an IRP_MJ_CLOSE function code, which indicates the client is closing the device. The DeviceCloseFunc function might disable hardware and deallocate memory space that the driver allocated when the device was opened.

Support for a DeviceCloseFunc function is optional. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib.

See Also

CleanupFunc


DeviceOpenFunc

BOOLEAN
DeviceOpenFunc(
PDEVICE_INFO pDevInfo
 );

The DeviceOpenFunc function performs operations that must be completed when a video capture device is opened. The function is provided by the kernel-mode driver, and the DeviceOpenFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Coments

The VCKernel.lib library calls the kernel-mode driver’s DeviceOpenFunc function when it receives an IRP_MJ_CREATE function code, which indicates a client is opening the device. User-mode drivers using VCUser.lib send this function code by calling VC_OpenDevice. The DeviceOpenFunc function might enable hardware or allocate memory space. The sample driver, bravado.sys, uses its DeviceOpenFunc function to obtain characteristics of the user’s display device, which are placed in the registry by the user-mode driver (see Opening and Closing a Device, Using VCUser.lib).

Support for a DeviceOpenFunc function is optional. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib.

See Also

DeviceCloseFunc


DrawFrameFunc

BOOLEAN
DrawFrameFunc(
PDEVICE_INFO pDevInfo,
PDRAWBUFFER pDraw
 );

The DrawFrameFunc function copies bitmap data into the frame buffer. The function is provided by the kernel-mode driver, and the DrawFrameFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pDraw
Pointer to a DRAWBUFFER structure.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s DrawFrameFunc function when the driver receives an IOCTL_VIDC_DRAW_FRAME control code. User-mode drivers using VCUser.lib send this control code by calling VC_DrawFrame.

The pDraw parameter points to a DRAWBUFFER structure describing the bitmap data to be copied into the frame buffer. The driver should read the structure’s Format member to determine if the specified format is one that either the device accepts for playback, or that the driver can convert into one the device accepts. (You should use the driver’s GetOverlayModeFunc function to indicate the supported formats.)

To protect the driver from access violations, you should place code that references the supplied bitmap in a routine that can be called by using VC_AccessData. To obtain the frame buffer’s address, the driver should call VC_GetFrameBuffer.

The driver should read data from the bitmap, convert it if necessary, and place it in the frame buffer. Current settings for key color and overlay rectangle description should not be changed.

Support for a DrawFrameFunc function is required, if the device supports playback. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a DrawFrameFunc function receives an IOCTL_VIDC_DRAW_FRAME control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.

The sample kernel-mode driver, bravado.sys, supports video playback, but its companion user-mode driver, bravado.dll, does not. Instead, the msyuv.dll codec calls bravado.sys to play back YUV-formatted compressed data.


GetKeyColourFunc

ULONG
GetKeyColourFunc(
PDEVICE_INFO pDevInfo
 );

The GetKeyColourFunc function returns the current key color. The function is provided by the kernel-mode driver, and the GetKeyColourFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns the current key color. See the following Comments section.

Comments

The VCKernel.lib library calls a kernel-mode driver’s GetKeyColourFunc function when the driver receives an IOCTL_VIDC_GET_KEY_COLOUR control code. User-mode drivers using VCUser.lib send this control code by calling VC_GetKeyColour.

The driver returns the key color as either an RGBQUAD type or as a palette index number. The return type must be consistent with the value of the VCO_KEYCOLOUR_RGB flag returned by the driver’s GetOverlayModeFunc function. That is, if the driver sets the flag, it must return an RGBQUAD-typed key color.

Support for a GetKeyColourFunc function is required, if the device supports a key color. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a GetKeyColourFunc function receives an IOCTL_VIDC_GET_KEY_COLOUR control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


GetOverlayModeFunc

DWORD
GetOverlayModeFunc(
PDEVICE_INFO pDevInfo
 );

The GetOverlayModeFunc function returns the device’s overlay capabilities. The function is provided by the kernel-mode driver, and the GetOverlayModeFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns a DWORD value containing flags. The following flags are defined.
Flag | Definition
VCO_KEYCOLOUR | Indicates the device supports a key color.
VCO_KEYCOLOUR_FIXED | Indicates the key color cannot be modified.
VCO_KEYCOLOUR_RGB | If set, indicates the key color must be specified as an RGB color. If clear, indicates the key color must be specified as a palette index number.
VCO_SIMPLE_RECT | Indicates the device supports a single rectangular overlay region.
VCO_COMPLEX_REGION | Indicates the device supports a complex (multi-rectangle) overlay region.
VCO_CAN_DRAW_Y411 | Indicates the device can display bitmaps that contain YUV 4:1:1-formatted data.
VCO_CAN_DRAW_S422 | Indicates the device can display bitmaps that contain YUV 4:2:2-formatted data.

Comments

The VCKernel.lib library calls a kernel-mode driver’s GetOverlayModeFunc function when the driver receives an IOCTL_VIDC_OVERLAY_MODE control code. User-mode drivers using VCUser.lib send this control code by calling VC_GetOverlayMode.

The driver sets the appropriate flags in the return value, based on the hardware’s capabilities.

Support for a GetOverlayModeFunc function is required. The driver must place the function’s address in the VC_CALLBACK structure supplied by VCKernel.lib.


InterruptAcknowledge

BOOLEAN
InterruptAcknowledge(
PDEVICE_INFO pDevInfo
 );

The InterruptAcknowledge function is a kernel-mode video capture driver’s interrupt service routine (ISR), used to acknowledge a device interrupt. The function is driver-defined, and the InterruptAcknowledge name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if a frame is available and it is time to capture another frame. Otherwise returns FALSE. (See the following Comments section.)

Comments

The VCKernel.lib library provides a generic ISR that is called each time a device interrupt occurs on the interrupt number that the driver passed to VC_ConnectInterrupt. This generic ISR calls the driver’s InterruptAcknowledge function.

Typically the InterruptAcknowledge function re-enables the device interrupt, if a capture stream operation is in progress.

The function is responsible for determining if it is time to capture a frame. It should compare the time since the last frame was captured to the client-specified time between frames. (The driver receives the client-specified time between frames as input to its StreamInitFunc function.)

If it is now time to capture a frame, and if a full frame is available, the function must return TRUE. Returning TRUE causes the generic ISR to call IoRequestDPC, which schedules the driver’s CaptureService (DPC) function, which in turn is responsible for capturing the frame.

Like all device ISRs under Windows NT, the driver’s InterruptAcknowledge function executes at the device’s IRQL and should be written to execute as quickly as possible. You should place data transfer operations in the driver’s CaptureService function. The InterruptAcknowledge function should record the frame’s time stamp.

If other code within the kernel-mode driver references the same objects that the InterruptAcknowledge function references, the driver must use VC_SynchronizeExecution to synchronize access to the objects.

Support for an InterruptAcknowledge function is required. The driver must place the address of its InterruptAcknowledge function in the VC_CALLBACK structure supplied by VCKernel.lib.

For more information about interrupt service routines (ISRs) and deferred procedure calls (DPC’s) see the Kernel Mode Drivers Design Guide.


OverlayFunc

BOOLEAN
OverlayFunc(
PDEVICE_INFO pDevInfo,
BOOL bOverlay
);

The OverlayFunc function is provided by kernel-mode video capture drivers to enable and disable the overlay display. The OverlayFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
bOverlay
Set to TRUE if data capture is to be enabled, and FALSE if it is to be disabled.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s OverlayFunc function when the driver receives an IOCTL_VIDC_OVERLAY_ON or IOCTL_VIDC_OVERLAY_OFF control code. User-mode drivers using VCUser.lib send this control code by calling VC_Overlay.

Support for an OverlayFunc function is required, if the device provides overlay capabilities. The driver must place the address of its OverlayFunc function in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not support the OverlayFunc function receives an IOCTL_VIDC_OVERLAY_ON or IOCTL_VIDC_OVERLAY_OFF control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


SetKeyPalIdxFunc

BOOLEAN
SetKeyPalIdxFunc(
PDEVICE_INFO pDevInfo,
ULONG palidx
);

The SetKeyPalIdxFunc function sets the overlay destination image’s key color to the specified palette index number. The function is provided by the kernel-mode driver, and the SetKeyPalIdxFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
palidx
Palette index number.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s SetKeyPalIdxFunc function when the driver receives an IOCTL_VIDC_SET_KEY_PALIDX control code. User-mode drivers using VCUser.lib send this control code by calling VC_SetKeyColourPalIdx.

You can assume that the driver’s SetKeyPalIdxFunc function will be called only if a previous call to its GetOverlayModeFunc function has returned with the VCO_KEYCOLOUR flag set and the VCO_KEYCOLOUR_RGB flag cleared.

Support for a SetKeyPalIdxFunc function is required, if the device supports a modifiable key color. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a SetKeyPalIdxFunc function receives an IOCTL_VIDC_SET_KEY_PALIDX control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.

For more information about palette index numbers, see VC_SetKeyColourPalIdx.

See Also

GetKeyColourFunc, SetKeyRGBFunc


SetKeyRGBFunc

BOOLEAN
SetKeyRGBFunc(
PDEVICE_INFO pDevInfo,
PRGBQUAD pRGB
);

The SetKeyRGBFunc function sets the overlay destination image’s key color to the specified RGB color. The function is provided by the kernel-mode driver, and the SetKeyRGBFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pRGB
Pointer to an RGBQUAD structure containing an RGB color specification. (The RGBQUAD structure is described in the Win32 SDK.)
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s SetKeyRGBFunc function when the driver receives an IOCTL_VIDC_SET_KEY_PALIDX control code. User-mode drivers using VCUser.lib send this control code by calling VC_SetKeyColourRGB.

You can assume that the drivers SetKeyRGBFunc function will be called only if a previous call to its GetOverlayModeFunc function has returned with both the VCO_KEYCOLOUR and VCO_KEYCOLOUR_RGB flags set.

Support for a SetKeyRGBFunc function is required, if the device supports a modifiable key color. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a SetKeyRGBFunc function receives an IOCTL_VIDC_SET_KEY_RGB control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.

See Also

GetKeyColourFunc, SetKeyPalIdxFunc


SetOverlayOffsetFunc

BOOLEAN
SetOverlayOffsetFunc(
PDEVICE_INFO pDevInfo,
PRECT prc
);

The SetOverlayOffsetFunc function sets the overlay offset rectangle. This rectangle defines which portion of the frame buffer appears in the overlay window. The function is provided by the kernel-mode driver, and the SetOverlayOffsetFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
prc
Pointer to a RECT structure containing screen coordinates that describe the offset rectangle. (The RECT structure is described in the Win32 SDK.)
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s SetOverlayOffsetFunc function when the driver receives an IOCTL_VIDC_OVERLAY_OFFSET control code. User-mode drivers using VCUser.lib send this control code by calling VC_SetOverlayOffset.

For more information about implementing the offset rectangle, see VC_SetOverlayOffset.

Support for a SetOverlayOffsetFunc function is required, if the device supports overlay and panning of the source image. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not support a SetOverlayOffsetFunc function receives an IOCTL_VIDC_OVERLAY_OFFSET control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.

See Also

SetOverlayRectsFunc


SetOverlayRectsFunc

BOOLEAN
SetOverlayRectsFunc(
PDEVICE_INFO pDevInfo,
POVERLAY_RECTS pOR
);

The SetOverlayRectsFunc function sets the overlay region. The function is provided by the kernel-mode driver, and the SetOverlayRectsFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
pOR
Pointer to an OVERLAY_RECTS structure describing one or more overlay rectangles, using screen coordinates.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DEVICE_CONFIGURATION_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s SetOverlayRectsFunc function when the driver receives an IOCTL_VIDC_OVERLAY_RECTS control code. User-mode drivers using VCUser.lib send this control code by calling VC_SetOverlayRect.

After the user-mode driver enables overlay by calling VC_Overlay, the kernel-mode driver should display captured video source images inside the overlay area of the output display. (If the device supports color keying, then the user-mode driver typically repaints the overlay area with the key color when it receives a DVM_UPDATE message.)

Drivers for devices that support scaling generally do not scale the video source image to the overlay area. Instead, scaling of the captured image is typically based on the capture format and controlled by the ConfigFormatFunc function. Thus, the image might not fit into the overlay rectangle. If the driver’s SetOverlayOffsetFunc function has not been called to set an offset rectangle, the driver should align the top left corner of the video source with the top left corner of the overlay region. If the source rectangle is larger than the overlay rectangle, the driver should crop the source as necessary at the bottom and right sides.

If a format has not been specified, the sample driver, bravado.sys, does scale the captured image to the overlay rectangle, in order to support applications that only overlay the source image without capturing frames.

Support for a SetOverlayRectsFunc function is required, if the device supports overlay. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not support a SetOverlayRectsFunc function receives an IOCTL_VIDC_OVERLAY_RECTS control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.

See Also

SetOverlayOffsetFunc


StreamFiniFunc

BOOLEAN
StreamFiniFunc(
PDEVICE_INFO pDevInfo
);

The StreamFiniFunc function ends a streaming operation that was initiated by the StreamInitFunc function. The function is provided by the kernel-mode driver, and the StreamFiniFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s StreamFiniFunc function when the driver receives an IOCTL_VIDC_STREAM_FINI control code. User-mode drivers using VCUser.lib send this control code by calling VC_StreamFini.

You can assume that your driver’s StreamStopFunc is called, and all pending buffers are returned to the client, before the driver’s StreamFiniFunc function is called. The StreamFiniFunc function should be used to deallocate resources that were allocated by the StreamInitFunc function. If the StreamFiniFunc function does not need to perform any operations, it can just return TRUE.

Support for a StreamFiniFunc function is required, if the device and driver support capture streams. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not support a StreamFiniFunc function receives an IOCTL_VIDC_STREAM_FINI control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


StreamGetPositionFunc

ULONG
StreamGetPositionFunc(
PDEVICE_INFO pDevInfo
);

The StreamGetPositionFunc function returns the current position within the capture stream. The function is provided by the kernel-mode driver, and the StreamGetPositionFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns the current stream position, in milliseconds. This is the time that has passed since StreamStartFunc was called.

Comments

The VCKernel.lib library calls a kernel-mode driver’s StreamGetPositionFunc function when the driver receives an IOCTL_VIDC_GET_POSITION control code. User-mode drivers using VCUser.lib send this control code by calling VC_GetStreamPos.

Support for a StreamGetPositionFunc function is required, if the device and driver support capture streams. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not support a StreamGetPositionFunc function receives an IOCTL_VIDC_GET_POSITION control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


StreamInitFunc

BOOLEAN
StreamInitFunc(
PDEVICE_INFO pDevInfo,
ULONG FrameRate
);

The StreamInitFunc function initializes a video capture stream. The function is provided by the kernel-mode driver, and the StreamInitFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
FrameRate
Capture rate (microseconds per frame).
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s StreamInitFunc function when the driver receives an IOCTL_VIDC_STREAM_INIT control code. User-mode drivers using VCUser.lib send this control code by calling VC_StreamInit. You can assume VCKernel.lib will not call the StreamInitFunc function if a stream has already been started.

The driver should store the frame rate so that it is accessible to the InterruptAcknowledge function. It might also verify that it has received a format selection. In deciding which operations to place in a driver’s StreamInitFunc function and which to place in its StreamStartFunc function, remember that StreamInitFunc is called once for each stream, which StreamStartFunc can be called multiple times.

Support for a StreamInitFunc function is required, if the device and driver support capture streams. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not support a StreamInitFunc function receives an IOCTL_VIDC_STREAM_INIT control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


StreamStartFunc

BOOLEAN
StreamStartFunc(
PDEVICE_INFO pDevInfo
);

The StreamStartFunc function starts capturing frames for a capture stream. The function is provided by the kernel-mode driver, and the StreamStartFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s StreamStartFunc function when the driver receives an IOCTL_VIDC_STREAM_START control code. User-mode drivers using VCUser.lib send this control code by calling VC_StreamStart. You can assume VCKernel.lib will not call the StreamStartFunc function unless the driver’s StreamInitFunc function has been previously called.

The driver’s StreamStartFunc function must reset the stream position to zero milliseconds. The function must also enable interrupts. Once interrupts are enabled, the following events occur:

  • When the device interrupts, VCKernel.lib’s generic ISR is called, which in turn calls the driver’s InterruptAcknowledge function. If, based on the client-specified frame rate, it is time to capture a frame, then VCKernel.lib’s generic DPC function is queued.

  • The I/O manager calls the generic DPC function, which dequeues one of the IRPs that was queued when the user-mode driver called VC_StreamAddBuffer. Then the generic DPC function calls the driver’s CaptureService function, passing the buffer address.

  • After the CaptureService function returns, VCKernel.lib’s generic DPC function calls IoCompleteRequest to return the buffer’s IRP to the user-mode driver.

Support for a StreamStartFunc function is required, if the device and driver support capture streams. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a StreamStartFunc function receives an IOCTL_VIDC_STREAM_START control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


StreamStopFunc

BOOLEAN
StreamStopFunc(
PDEVICE_INFO pDevInfo
);

The StreamStopFunc function stops capturing frames for a capture stream. The function is provided by the kernel-mode driver, and the StreamStopFunc name is a placeholder for a driver-specified function name.

Parameters
pDevInfo
Pointer to the DEVICE_INFO structure returned by VC_Init.
Return Value

Returns TRUE if the operation succeeds. Otherwise returns FALSE. If FALSE, VCKernel.lib sets the Win32 error code value to STATUS_DRIVER_INTERNAL_ERROR.

Comments

The VCKernel.lib library calls a kernel-mode driver’s StreamStopFunc function when the driver receives an IOCTL_VIDC_STREAM_STOP control code. User-mode drivers using VCUser.lib send this control code by calling VC_StreamStop. You can assume VCKernel.lib will not call the StreamStopFunc function unless the driver’s StreamInitFunc function has been previously called.

The driver’s StreamStopFunc function must stop the stream, typically by disabling device interrupts.

Support for a StreamStopFunc function is required, if the device and driver support capture streams. If a driver does support the function, it must place its address in the VC_CALLBACK structure supplied by VCKernel.lib. If a driver that does not provide a StreamStopFunc function receives an IOCTL_VIDC_STREAM_STOP control code, VCKernel.lib sets the Win32 error code value to STATUS_INVALID_DEVICE_REQUEST.


Macros, Kernel-Mode Video Capture Drivers

This section describes the macros that are available to kernel-mode video capture drivers. They are listed in alphabetic order and are defined in vckernel.h.


VC_ReadIOMemoryBlock

VC_ReadIOMemoryBlock(dst, src, cnt)

The VC_ReadIOMemoryBlock macro reads a specified number of bytes from a device’s mapped I/O memory space into system memory space.

Parameters
dst
Pointer to a buffer to receive data copied from mapped I/O space.
src
Address of the first mapped I/O memory location from which data is to be copied.
cnt
Number of bytes to be copied from I/O space.
Return Value

None.

Comments

Kernel-mode video capture drivers typically use the VC_ReadIOMemoryBlock macro to read a block of memory within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_ReadIOMemoryBlock macro to read I/O space helps ensure driver portability across system platforms.


VC_ReadIOMemoryBYTE

VC_ReadIOMemoryBYTE(p)

The VC_ReadIOMemoryBYTE macro reads a single byte from a device’s mapped I/O memory space.

Parameters
p
Address of the mapped I/O memory location from which data is to be copied.
Return Value

Returns the byte value read from the specified memory address.

Comments

Kernel-mode video capture drivers typically use the VC_ReadIOMemoryBYTE macro to read a location within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_ReadIOMemoryBYTE macro to read I/O space helps ensure driver portability across system platforms.


VC_ReadIOMemoryULONG

VC_ReadIOMemoryULONG(p)

The VC_ReadIOMemoryULONG macro reads a single unsigned longword value from a device’s mapped I/O memory space.

Parameters
p
Address of the mapped I/O memory location from which data is to be copied.
Return Value

Returns the unsigned longword value read from the specified memory address.

Comments

Kernel-mode video capture drivers typically use the VC_ReadIOMemoryULONG macro to read a location within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_ReadIOMemoryULONG macro to read I/O space helps ensure driver portability across system platforms.


VC_ReadIOMemoryUSHORT

VC_ReadIOMemoryUSHORT(p)

The VC_ReadIOMemoryUSHORT macro reads a single unsigned word value from a device’s mapped I/O memory space.

Parameters
p
Address of the mapped I/O memory location from which data is to be copied.
Return Value

Returns the unsigned word value read from the specified memory address.

Comments

Kernel-mode video capture drivers typically use the VC_ReadIOMemoryUSHORT macro to read a location within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_ReadIOMemoryUSHORT macro to read I/O space helps ensure driver portability across system platforms.


VC_WriteIOMemoryBlock

VC_WriteIOMemoryBlock(dst, src, cnt)

The VC_WriteIOMemoryBlock macro writes a specified number of bytes from system memory space into a device’s mapped I/O memory space.

Parameters
dst
Address of the first mapped I/O memory location into which data is to be copied.
src
Pointer to a buffer from which data is to be copied.
cnt
Number of bytes to be copied into I/O space.
Return Value

None.

Comments

Kernel-mode video capture drivers typically use the VC_WriteIOMemoryBlock macro to write a block of memory within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_WriteIOMemoryBlock macro to write into I/O space helps ensure driver portability across system platforms.


VC_WriteIOMemoryBYTE

VC_WriteIOMemoryBYTE(p, b)

The VC_WriteIOMemoryBYTE macro writes a single byte into a device’s mapped I/O memory space.

Parameters
p
Address of the mapped I/O memory location into which data is to be copied.
b
Byte value to be written.
Return Value

None.

Comments

Kernel-mode video capture drivers typically use the VC_WriteIOMemoryBYTE macro to write into a location within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_WriteIOMemoryBYTE macro to write into I/O space helps ensure driver portability across system platforms.


VC_WriteIOMemoryULONG

VC_WriteIOMemoryULONG(p, l)

The VC_WriteIOMemoryULONG macro writes an unsigned longword into a device’s mapped I/O memory space.

Parameters
p
Address of the mapped I/O memory location into which data is to be copied.
l
Unsigned longword value to be written.
Return Value

None.

Comments

Kernel-mode video capture drivers typically use the VC_WriteIOMemoryULONG macro to write into a location within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_WriteIOMemoryULONG macro to write into I/O space helps ensure driver portability across system platforms.


VC_WriteIOMemoryUSHORT

VC_WriteIOMemoryUSHORT(p, w)

The VC_WriteIOMemoryUSHORT macro writes an unsigned word into a device’s mapped I/O memory space.

Parameters
p
Address of the mapped I/O memory location into which data is to be copied.
w
Unsigned word value to be written.
Return Value

None.

Comments

Kernel-mode video capture drivers typically use the VC_WriteIOMemoryUSHORT macro to write into a location within the frame buffer. Drivers can obtain the frame buffer’s base address by calling VC_GetFrameBuffer.

Using the VC_WriteIOMemoryUSHORT macro to write into I/O space helps ensure driver portability across system platforms.


Video Compression Manager Drivers

Video compression drivers provide low-level compression, decompression, and rendering services. The algorithms used by these drivers can be hardware- or software-based. These drivers are used for the following services:

  • Compressing and decompressing data

  • Rendering compressed data to the display

  • Handling text and custom data.

The following topics describe the interface for these drivers:

  • Writing video compression, decompression, and rendering drivers

  • How a driver handles the system messages for the installable-driver interface

  • How a driver handles messages specific to compressing, decompressing, and rendering data

  • Alphabetic reference to the messages and data structures used to write compression, decompression, and rendering drivers.


Compression Driver Architecture

Compression drivers are DLLs that compress or decompress video or other types of data in response to requests from the system or applications. Applications never access the compression driver directly. Instead, an application calls a corresponding Win32 compression function that directs the system to send the request to the driver in the form of a message.

The typical compression driver combines both compression and decompression functions. When a compression driver receives a request, it usually receives video or other data that needs to be compressed or decompressed. The video data can be either still bitmaps or motion-video frames. The driver typically receives compressed data from an application that has opened an AVI file; the driver is expected to return the uncompressed data to the application. The driver typically receives uncompressed data from a video source, such as a disk file or a video device.

Some compression drivers also write directly to the display or display driver. Such drivers, called rendering drivers, can replace the display driver or take over some of the responsibilities of the driver. These drivers handle a set of messages, the ICM_DRAW messages, in addition to the decompression messages defined for the services that return the decompressed video to the client application. Rendering drivers can reside in the same DLLs as compressors and decompressors, or they can reside in a separate DLL.


The DriverProc Function, Installable Compression Drivers

The DriverProc Function, Compression Drivers

Compression drivers provide a DriverProc entry point to process messages related to requests for video compression and decompression. The DriverProc function processes messages sent by the system to the driver as the result of an application call to a video compression and decompression function. For example, when an application opens a video compression and decompression driver, the system sends the specified driver a DRV_OPEN message. The driver’s DriverProc function receives and processes this message. Your DriverProc function should return ICERR_UNSUPPORTED for any messages that it does not handle.


Handling DRV_OPEN and DRV_CLOSE, Installable Compression Drivers

Handling DRV_OPEN and DRV_CLOSE, Compression Drivers

Like other installable drivers, client applications must open a video compression and decompression driver before using it, and close it when finished so the driver will be available to other applications. When a driver receives an open request, it returns a value that the system will use for dwDriverID sent with subsequent messages. When your driver receives other messages, it can use this value to identify instance data needed for operation. Your drivers can use this data to maintain information related to the client that opened the driver.

Compression and decompression drivers should support more than one client simultaneously. If you support more than one client simultaneously, though, remember to check the dwDriverID parameter to determine which client is being accessed.

If the driver is opened for configuration by the Drivers option of the Control Panel, lParam2 contains zero. When opened this way, your driver should respond to the DRV_CONFIGURE and DRV_QUERYCONFIGURE messages.

If opened for compression or decompression services, lParam2 contains a pointer to an ICOPEN data structure.


Compressor Configuration

Video compression and decompression drivers can receive a series of configuration messages. System configuration messages are typically sent by the Drivers option of the Control Panel to configure the hardware. Video compression- and decompression-specific configuration messages are typically initiated by the client application or from dialog boxes displayed by your driver. Your driver should use these messages to configure the driver.


Configuration Messages Sent by the System

Installable drivers can supply a configuration dialog box for users to access through the Drivers option in the Control Panel. If your driver supports different options, it should allow user configuration. Any hardware-related settings should be stored in the system registry in a section having the same name as the driver.

Like other installable drivers, your driver will receive DRV_CONFIGURE and DRV_QUERYCONFIGURE messages from the Drivers option of the Control Panel. If your driver controls hardware that needs to be configured, it should return a nonzero value for the DRV_QUERYCONFIGURE system message and display a hardware configuration dialog box for the DRV_CONFIGURE system message.


Messages for Configuring the Driver State

The video compression-specific and decompression-specific configuration messages are typically initiated by the client application or from dialog boxes displayed by your driver. Your driver should use these messages to configure the driver.

If your driver is configurable, it should support the ICM_CONFIGURE message for driver configuration. It should also use this message to set parameters for compression or decompression. Any options the user selects in the dialog box displayed for ICM_CONFIGURE should be saved as part of the state information referenced by the ICM_GETSTATE and ICM_SETSTATE messages.

The ICM_GETSTATE and ICM_SETSTATE messages query and set the internal state of your compression or decompression driver. State information is device dependent, and your driver must define its own data structure for it. While the client application reserves a memory block for the information, it will obtain the size needed for the memory block from your driver. If your driver receives ICM_GETSTATE with a NULL pointer for dwParam1, the client application is requesting that your driver return the size of its state information. Conversely, if your driver receives ICM_GETSTATE with dwParam1 pointing to a block of memory, and dwParam2 specifying the size of the memory block, the client application is requesting that your driver transfer the state information to the memory block.

When your driver receives ICM_SETSTATE, with dwParam1 pointing to a block of memory and dwParam2 specifying the size of the memory block, the client application is requesting that your driver restore its configuration from the state information contained in the memory block. Before setting the state, your driver should verify that the state information applies to your driver. One technique for verifying the data is to reserve the first DWORD in the state data structure for the four-character code used to identify your driver. If you set this DWORD for data returned for ICM_GETSTATE, you can use it to verify the data supplied with ICM_SETSTATE. If ICM_SETSTATE has a NULL pointer for dwParam1, it indicates that your driver should return to its default state.

State information should not contain any data that is absolutely required for data decompression¾any such data should be part of the format you return for the ICM_DECOMPRESS_GET_FORMAT message.


Messages Used to Interrogate the Driver

The client application sends the ICM_ABOUT message to display your driver’s About dialog box. The client application sets dwParam1 to -1; the application is querying whether your driver supports display of an About box. Your driver returns ICERR_OK if it does, and it returns ICERR_UNSUPPORTED if it does not. Your driver should only display an About box if the client application specifies a window handle in dwParam1. The window handle indicates the parent of the dialog box.

The client application uses the ICM_GETINFO message to obtain a description of your driver. Your driver should respond to this message by filling in the ICINFO structure it receives with the message. The flags your driver sets in the structure tell the client application which capabilities the driver supports. Your driver will not typically use the szDriver[128] member. This member is used to specify the module that contains the driver. Set the flags corresponding to the capabilities of your driver in the low-ordered word of the dwFlags member. You can use the high-ordered word for driver-specific flags.


Configuration Messages for Compression Quality

For the video compression and decompression interface, quality is indicated by an integer in the range of 0 to 10,000. A quality level of 7500 typically indicates an acceptable image quality. A quality level of 0 typically indicates a very low quality level (possibly even a totally black image). As the quality level moves from an acceptable level to low quality, the image might have a loss of color as the colors in the color table are merged, or as the color resolution of each pixel decreases. If your driver supports temporal compression (it needs information from the previous frame to decompress the current frame), low and high quality might imply how much this type of compression can degrade image quality. For example, your driver might limit the compression of a high-quality image to preserve sharp detail and color fidelity. Conversely, your driver might sacrifice these qualities to obtain very compressed output files.

If your driver supports quality values, it maps the values to its internal definitions used by the compression algorithms. Thus, the definition of image quality will vary from driver to driver, and, quite possibly, from compression algorithm to compression algorithm. Even though the values are not definitive, your driver should support as many individual values as possible.

The client application obtains the capabilities for compression quality with the ICM_GETDEFAULTQUALITY and ICM_GETQUALITY messages. If your driver supports quality levels, it should respond to the ICM_GETDEFAULTQUALITY message by returning a value between 0 and 10000 that corresponds to a good default quality level for your compressor. Your driver should return the current quality level for the ICM_GETQUALITY message.

The client application sends the ICM_SETQUALITY message to set the quality level of your driver. Your driver should pass the quality value directly to the compression routine.

If your driver supports quality levels, it should set the VIDCF_QUALITY flag when it responds to the ICM_GETINFO message.


Configuration Messages for Key-Frame Rate and Buffer Queue

The client application uses the ICM_GETDEFAULTKEYFRAMERATE message to obtain the driver’s recommendation for the key-frame spacing for compressing data. (A key frame is a frame in a video sequence that does not require information from a previous frame for decompression.) If the client application does not specify another value, this value determines how frequently the client application sends an uncompressed image to your driver with the ICCOMPRESS_KEYFRAME flag set. If your driver supports this option, it should specify the key-frame rate in the DWORD pointed to by dwParam1 and return ICERR_OK. If it does not support this option, return ICERR_UNSUPPORTED.

The client application uses ICM_GETBUFFERSWANTED to determine if your driver will maintain a queue of buffers. Your driver might maintain a queue of buffers if it renders the decompressed data and is designed to keep its hardware pipelines full. If your driver supports this option, it should specify the number of buffers in the DWORD pointed to by dwParam1 and return ICERR_OK. If it does not support this option, return ICERR_UNSUPPORTED.


Decompressing Video Data

The client application sends a series of messages to your driver to coordinate decompressing video data. The coordination involves the following activities:

  • Setting the driver state

  • Specifying the input format and determining the decompression format

  • Preparing to decompress video

  • Decompressing the video

  • Ending decompression

The following messages are used by video compression and decompression drivers for these decompression activities.
Message | Description
ICM_DECOMPRESSEX | Decompresses a frame of data into a buffer provided by the client application.
ICM_DECOMPRESSEX_BEGIN | Prepares a driver for decompressing data.
ICM_DECOMPRESSEX_END | Cleans up after decompressing.
ICM_DECOMPRESS_GET_FORMAT | Obtains a suggestion for a good format for the decompressed data.
ICM_DECOMPRESSEX_QUERY | Determines if a driver can decompress a specific input format.
ICM_DECOMPRESS_GET_PALETTE | Returns the color table of the output data structure.

The video decompressed with these messages is returned to the client application, which handles the display of data. If you want your driver to control the video timing or directly update the display, use the ICM_DRAW messages. If you return the decompressed video to the client application, your driver can decompress data using either software or hardware with the ICM_DECOMPRESS messages.


Restoring the Driver State

The client application restores the driver state by sending ICM_SETSTATE. The client application recalls the state information from the ‘strd’ data chunk of the AVI file. (The information was originally obtained with the ICM_GETSTATE message.) The client application does not validate any data in the state information. It simply transfers the state information to your driver.

The client application sends the information to your driver in a buffer pointed to by dwParam1. The size of the buffer is specified in dwParam2. The organization of the data in the buffer is driver dependent. If dwParam1 is NULL, your driver should return to its default state.

Note All information required for decompressing the image data should be part of the format data. Only optional compression parameters can be included with the state information.


Specifying the Input Format and Determining the Decompression Format

Depending on how the client application will use the decompressed data, it will send either ICM_DECOMPRESS_GET_FORMAT or ICM_DECOMPRESSEX_QUERY to specify the input format and determine the decompression format. The client application sends ICM_DECOMPRESS_GET_FORMAT to have your driver suggest the decompressed format. The client application sends ICM_DECOMPRESSEX_QUERY to determine if your driver supports a format it is suggesting.

The ICM_DECOMPRESS_GET_FORMAT message sends a pointer to a BITMAPINFO data structure in dwParam1. This structure specifies the format of the incoming compressed data. The input format was obtained by the client application from the ‘strf’ chunk in the AVI file. While the output format is specified by dwParam2, your driver must use the message to determine how the parameter is defined.

If your driver receives ICM_DECOMPRESS_GET_FORMAT, both dwParam1 and dwParam2 point to BITMAPINFO data structures. The input format data is contained in the dwParam1 structure. Your driver should fill in the dwParam2 BITMAPINFO data structure with information about the format it will use to decompress the data. If your driver can handle the format, return the number of bytes used for the dwParam2 structure as the return value. If your driver cannot handle the input format, or the input format from the ‘strf’ chunk is incorrect, your driver should return ICERR_BADFORMAT to fail the message.

If you have format information in addition to that specified in the BITMAPINFOHEADER structure, you can add it immediately after this structure. If you do this, update the biSize member to specify the number of bytes used by the structure and your additional information. If a color table is part of the BITMAPINFO information, it follows immediately after your additional information. Return ICERR_OK when your driver has finished updating the data format.

If your driver receives ICM_DECOMPRESSEX_QUERY, dwParam1 points to an ICDECOMPRESSEX data structure containing the input format data. If lpbiDst is NULL, your decompression driver can use any output format. In this case, the client application is querying whether your driver can decompress the input format, and the output format doesn’t matter. If lpbiDst points to a BITMAPINFO structure, the suggested format will be the native or best format for the decompressed data. For example, if playback is on an 8-bit device, the client application will suggest an 8-bit DIB.

If your driver supports the specified input and output format (which might also include stretching the image according to the values specified for the xDst, yDst, dxDst, dyDst, xSrc, ySrc, dxSrc, and dySrc members), or if it supports the specified input with NULL specified for lpbiDst, return ICERR_OK to indicate the driver accepts the formats.

Your driver does not have to accept the formats suggested. If you fail the message by returning ICERR_BADFORMAT, the client application suggests alternate formats until your driver accepts one. If your driver exhausts the list of formats usually used, the client application requests a format with ICM_DECOMPRESS_GET_FORMAT.

If you are decompressing to 8-bit data, your driver also receives the ICM_DECOMPRESS_GET_PALETTE message. Your driver should add a color table to the BITMAPINFO data structure and specify the number of palette entries in the biClrUsed member. The space reserved for the color table is always 256 colors.


Preparing to Decompress Video

When the client application is ready, it sends the ICM_DECOMPRESSEX_BEGIN message to the driver. The client application uses dwParam1 to point to an ICDECOMPRESSEX structure and sets dwParam2 to its size. The lpbiSrc and lpbiDest members of the ICDECOMPRESSEX data structure describe the input and output formats. If either of the formats is incorrect, your driver should return ICERR_BADFORMAT. Your driver should create any tables and allocate any memory that it needs to decompress data efficiently. When done, return ICERR_OK.


Decompressing the Video

The client application sends ICM_DECOMPRESSEX each time it has an image to decompress. The client application uses the flags in the file index to ensure the initial frame in a decompression sequence is a key frame.

The ICDECOMPRESSEX data structure specified in dwParam1 contains the decompression parameters. The value specified in dwParam2 specifies the size of the structure.

The format of the input data is specified in a BITMAPINFOHEADER structure pointed to by lpbiInput. The input data is in a buffer specified by lpInput. The lpbiOutput and lpOutput members contain pointers to the format data and buffer used for the output data.

The client application sets the ICDECOMPRESS_HURRYUP flag in the dwFlags member to direct your driver to decompress the data at a faster rate. The ICDECOMPRESS_PREROLL flag indicates the client application is sending frames in advance of a frame that will actually be displayed. The client application will not display any data decompressed with these flags. This might let your driver avoid decompressing a frame or data, or let it minimally decompress when it needs information from this frame to prepare for decompressing a following frame.

The ICDECOMPRESS_UPDATE flag indicates the client application has specified that the screen be updated.

The ICDECOMPRESS_NULLFRAME flag indicates the frame does not have any data.

The ICDECOMPRESS_NOTKEYFRAME flag indicates the frame is not a key frame.


Ending Decompression

Your driver receives ICM_DECOMPRESSEX_END when the client application no longer needs data decompressed. For this message, your driver should free the resources it allocated for the ICM_DECOMPRESSEX_BEGIN message.


Other Messages Received During Decompression

Decompression drivers also receive the ICM_DRAW_START and ICM_DRAW_STOP messages. These messages tell the driver when the client application starts and stops drawing the images. Most decompression drivers can ignore these messages.


Supporting the ICM_DECOMPRESS and ICM_DECOMPRESSEX Messages

The ICM_DECOMPRESSEX messages replace the ICM_DECOMPRESS messages and add extended decompression capabilities. These messages let drivers decompress images described with source and destination rectangles.

The ICM_DECOMPRESSEX messages serve the same purpose as the ICM_DECOMPRESS, ICM_DECOMPRESS_BEGIN, ICM_DECOMPRESS_END, and ICM_DECOMPRESS_QUERY messages, except that the ICM_DECOMPRESSEX messages use the ICDECOMPRESSEX structure. This structure contains the image input format and data, the output format and data, the source rectangle, and the destination rectangle. For compatibility, your driver should support the ICM_DECOMPRESS messages as well as the ICM_DECOMPRESSEX messages.

The ICM_DECOMPRESSEX messages also let applications decompress to a frame buffer under the following conditions:

  • The decompressor supports a frame buffer.

  • MSVideo can access the frame buffer.

  • The decompressor supports decompression to the format of the frame buffer (for example, it supports the correct bit depth and it can handle upside-down formats).

  • The palette must be an identity palette if the device uses 8-bit color depth.

  • The decompressor must support 48-bit pointers if the linear frame buffer is simulated with a banked display card. MSVideo sets the biCompression member of the BITMAPINFOHEADER structure to 1632 when the decompressor needs to use 48-bit pointers to access the image.


Compressing Video Data

When used to compress video data, your driver receives a series of messages, similar to decompressing data. The client application sends messages to your driver to coordinate the following activities:

  • Obtaining the driver state

  • Specifying the input format and determining the compression format

  • Preparing to compress video

  • Compressing the video

  • Ending compression

The following messages are used by video compression drivers.
Message | Description
ICM_COMPRESS | Compresses a frame of data into the buffer provided by the client application.
ICM_COMPRESS_BEGIN | Prepares for compressing data.
ICM_COMPRESS_END | Cleans up after compressing.
ICM_COMPRESS_GET_FORMAT | Obtains the driver’s suggested output format of the compressed data.
ICM_COMPRESS_GET_SIZE | Obtains the maximum size of one frame of data when it is compressed in the output format.
ICM_COMPRESS_QUERY | Determines if a driver can compress a specific input format.

The video compressed with these messages is returned to the client application. When compressing data, your driver can use either software or hardware to do the compression.

Note When MSVideo recompresses a file, each frame is decompressed to a full frame before it is passed to the compressor.


Obtaining the Driver State

The client application obtains the driver state by sending ICM_GETSTATE. The client application determines the size of the buffer needed for the state information by sending this message with dwParam1 set to NULL. Your driver should respond to the message by returning the size of the buffer it needs for state information.

After it determines the buffer size, the client application resends the message with dwParam1 pointing to a block of memory it allocated. The dwParam2 parameter specifies the size of the memory block. Your driver should respond by filling the memory with its state information. If your driver uses state information, include only optional decompression parameters with the state information. State information typically includes the setup specified by the user in the dialog box resulting from an ICM_CONFIGURE message. Any information required for decompressing the image data must be included with the format data. When done, your driver should return the size of the state information.

The client application does not validate any data in the state information. It simply stores the state information in the ‘strd’ data chunk of the AVI file.


Specifying the Input Format and Determining the Compression Format

The client application uses the ICM_COMPRESS_GET_FORMAT or ICM_COMPRESS_QUERY message to specify the input format and determine the compression (output) format. The client application sends ICM_COMPRESS_GET_FORMAT to have your driver suggest the compressed format. The client application sends ICM_COMPRESS_QUERY to determine if your driver supports a format it is suggesting.

Both messages have a pointer to a BITMAPINFO data structure in dwParam1. This structure specifies the format of the incoming uncompressed data. The contents of dwParam2 depend on the message.

To have your driver suggest the format, the client application determines the size of the buffer needed for the compressed data format by sending ICM_COMPRESS_GET_FORMAT. When requesting the buffer size, the client application uses dwParam1 to point to a BITMAPINFO structure and sets dwParam2 to NULL. Based on the input format, your driver should return the number of bytes needed for the format buffer. Return a buffer size at least large enough to hold a BITMAPINFOHEADER data structure and a color table.

The client application gets the output format by sending ICM_COMPRESS_GET_FORMAT with valid pointers to BITMAPINFO structures in both dwParam1 and dwParam2. Your driver should return the output format in the buffer pointed to by dwParam2. If your driver can produce multiple formats, the format selected by your driver should be the one that preserves the greatest amount of information rather than one that compresses to the most compact size. This will preserve image quality if the video data is later edited and recompressed.

The output format data becomes the ‘strf’ chunk in the AVI RIFF file. The data must start out like a BITMAPINFOHEADER data structure. You can include any additional information required to decompress the file after the BITMAPINFOHEADER data structure. A color table (if used) follows this information.

If you have format data following the BITMAPINFOHEADER structure, update the biSize member to specify the number of bytes used by the structure and your additional data. If a color table is part of the BITMAPINFO information, it follows immediately after your additional information.

If your driver cannot handle the input format, it returns ICMERR_BADFORMAT to fail the message.

If your driver receives ICM_COMPRESS_QUERY, the dwParam1 parameter points to a BITMAPINFO data structure containing the input format data. The dwParam2 parameter will either be NULL or contain a pointer to a BITMAPINFO structure describing the compressed format specified by the client application. If dwParam2 is NULL, your compression driver can use any output format. (The client application is just querying whether your driver can handle the input.) If dwParam2 points to a BITMAPINFO structure, the client application is suggesting the output format.

If your driver supports the specified input and output format, or it supports the specified input with NULL specified for dwParam2, return ICERR_OK to indicate the driver accepts the formats. Your driver does not have to accept the suggested format. If you fail the message by returning ICERR_BADFORMAT, the client application suggests alternate formats until your driver accepts one. If your driver exhausts the list of formats typically used, the client application requests a format with ICM_COMPRESS_GET_FORMAT.


Initialization for the Compression Sequence

When the client application is ready to start compressing data, it sends the ICM_COMPRESS_BEGIN message. The client application uses dwParam1 to point to the format of the data being compressed, and uses dwParam2 to point to the format for the compressed data. If your driver cannot handle the formats, or if they are incorrect, your driver should return ICERR_BADFORMAT to fail the message.

Before the client application starts compressing data, it sends ICM_COMPRESS_GET_SIZE. For this message, the client application uses dwParam1 to point to the input format and uses dwParam2 to point to the output format. Your driver should return the worst-case size (in bytes) that it expects a compressed frame to occupy. The client application uses this size value when it allocates buffers for the compressed video frame.

Client applications might send the ICM_COMPRESS_FRAMES_INFO message to set the compression parameters. The dwParam1 parameter specifies a pointer to an ICCOMPRESSFRAMES structure. For this message, the GetData and PutData members are not used. The dwParam2 parameter specifies the size of the structure. A compressor can use this message to make decisions about the amount of space allocated for each frame while compressing.


Compressing the Video

The client application sends ICM_COMPRESS for each frame it wants compressed. It uses dwParam1 to point to an ICCOMPRESS structure containing the parameters used for compression. Your driver uses the buffers pointed to by the members of ICCOMPRESS for returning information about the compressed data.

Your driver returns the actual size of the compressed data in the biSizeImage member in the BITMAPINFOHEADER data structure pointed to by the lpbiOutput member of ICCOMPRESS.

The format of the input data is specified in a BITMAPINFOHEADER structure pointed to by lpbiInput. The input data is in a buffer specified by lpInput. The lpbiOutput and lpOutput members contain pointers to the format data and buffer used for the output data. Your driver must indicate the size of the compressed video data in the biSizeImage member in the BITMAPINFO structure specified for lpbiOutput.

The dwFlags member specifies flags used for compression. The client application sets the ICCOMPRESS_KEYFRAME flag if the input data should be treated as a key frame. (A key frame is one that does not require data from a previous frame for decompression.) When this flag is set, your driver should treat the image as the initial image in a sequence.

The lpckid member specifies a pointer to a buffer used to return the chunk ID for data in the AVI file. Your driver should assign a two-character code for the chunk ID only if it uses a custom chunk ID.

The lpdwFlags member specifies a pointer to a buffer used to return flags for the AVI index. The client application will add the returned flags to the file index for this chunk. If the compressed frame is a key frame (a frame that does not require a previous frame for decompression), your driver should set the AVIIF_KEYFRAME flag in this member. Your driver can define its own flags, but they must be set in the high word only.

The lFrameNum member specifies the frame number of the frame to compress. If your driver is performing fast temporal compression, check this member to see if frames are being sent out of order or if the client application is having a frame recompressed.

The dwFrameSize member indicates the maximum size (in bytes) for the compressed frame. If it specifies zero, your driver determines the size of the compressed image. If it is nonzero, your driver should try to compress the frame to within the specified size. This might require your driver to sacrifice image quality (or make some other trade-off) to obtain the size goal. Your driver should support this if it sets the VIDCF_CRUNCH flag when it responds to the ICM_GETINFO message.

The dwQuality member specifies the compression quality. Your driver should support this if it sets the VIDCF_QUALITY flag when it responds to the ICM_GETINFO message.

The format of the previous data is specified in a BITMAPINFOHEADER structure pointed to by lpbiPrev. The input data is in a buffer specified by lpPrev. Your driver will use this information if it performs temporal compression (that is, it needs the previous frame to compress the current frame). If your driver supports temporal compression, it should set the VIDCF_TEMPORAL flag when it responds to the ICM_GETINFO message. If your driver supports temporal compression and does not need the information in the lpbiPrev and lpPrev members, it should set the VIDCF_FASTTEMPORALC flag when it responds to the ICM_GETINFO message. The VIDCF_FASTEMPORALC flag can decrease the processing time because your driver does not need to access data specified in lpbiPrev and lpPrev.

When your driver has finished compressing the data, it returns ICERR_OK.


Ending Compression

Your driver receives ICM_COMPRESS_END when the client application no longer needs data compressed, or when the client application is changing the format or palette. After sending ICM_COMPRESS_END, the client application must send ICM_COMPRESS_BEGIN to continue compressing data. The client application does not need to send an ICM_COMPRESS_END message for each ICM_COMPRESS_BEGIN message. It can send ICM_COMPRESS_BEGIN to restart compression without sending ICM_COMPRESS_END.

When the driver is no longer needed, the system closes it by sending DRV_CLOSE.


Rendering Directly to Video Hardware

Drivers that can render video directly to hardware should support the ICM_DRAW messages in addition to the ICM_DECOMPRESS messages. The ICM_DRAW messages render data directly to hardware rather than into a data buffer returned to the client application by the decompression driver.

Your driver receives a series of messages from the client application to coordinate the following activities to render a video sequence:

  • Setting the driver state

  • Specifying the input format

  • Preparing to decompress video

  • Decompressing the video

  • Ending decompression

The following ICM_DRAW messages are used by renderers for these decompression activities.
Message | Description
ICM_DRAW | Decompresses a frame of data and draws it.
ICM_DRAW_BEGIN | Prepares to draw data.
ICM_DRAW_END | Cleans up after decompressing an image to the screen.
ICM_DRAW_REALIZE | Realizes a palette.
ICM_DRAW_QUERY | Determines if the driver can render data in a specific format.
ICM_DRAW_SUGGESTFORMAT | Has the driver suggest an output format.

The video decompressed with the ICM_DRAW messages is retained by your driver, which handles the display of data. These messages control only the decompression process. The messages used to control the drawing are described separately. Your driver will receive the ICM_DRAW messages only if it sets the VIDCF_DRAW flag when it responds to the ICM_GETINFO message.


Setting the Driver State

The client application restores the driver state by sending ICM_SETSTATE. The process for handling this message is the same as for the ICM_DECOMPRESS messages.


Specifying the Input and Output Formats

Because your driver handles the drawing of video, the client application does not need to determine the output format. The client application must only determine if your driver can handle the input format. It sends ICM_DRAW_QUERY to determine if your driver supports the input format. The input format is specified with a pointer to a BITMAPINFO data structure in dwParam1. The dwParam2 parameter is not used.

If your driver supports the specified input format, return ICERR_OK to indicate the driver accepts the format. If your driver does not support the format, return ICERR_BADFORMAT.


Suggesting the Output Format

Your driver might also receive the ICM_DRAW_SUGGESTFORMAT message. Typically, this message is sent by the decompression portion of a driver to the drawing portion of a driver to obtain the best decompressed format for the data when the drawing portion can handle several formats. The dwParam1 parameter of ICM_DRAW_SUGGESTFORMAT points to an ICDRAWSUGGEST structure and the dwParam2 parameter specifies its size.

The lpbiIn member specifies a pointer to the structure containing the compressed input format. The lpbiSuggest member specifies a pointer to a buffer used to return the suggested output format.

The dxSrc, dySrc, dxDst, and dyDst members specify the width and height of the source and destination rectangles.

If returning format information, return ICERR_OK. If the lpbiSuggest member is NULL, return the amount of memory required for the suggested output format structure.


Preparing to Draw Data

When the client application is ready, it sends the ICM_DRAW_BEGIN message to the driver to prepare the driver for decompressing the stream. Your driver should create any tables and allocate any memory that it needs to decompress data efficiently.

The client application sets dwParam1 to the ICDRAWBEGIN data structure. The size of this structure is contained in dwParam2. If the ICDRAW_QUERY flag is set in the dwFlags member, the client application is interrogating your driver to determine if can decompress the data with the parameters specified in the ICDRAWBEGIN data structure. Your driver should return ICM_ERR_OK if it can accept the parameters. It should return ICM_ERR_NOTSUPPORTED if it does not accept them.

When the ICDRAW_QUERY flag is set, ICM_DRAW_BEGIN will not be paired with ICM_DRAW_END. Your driver will receive another ICM_DRAW_BEGIN message without this flag to start the actual decompression sequence.

Your driver can ignore the palette handle specified in the hpal member.

The hwnd and hdc members specify the handle of the window and DC used for drawing. These members are valid only if the ICDRAW_HDC flag is set in the dwFlags member.

The xDst and yDst members specify the x- and y-position of the upper-right corner of the destination rectangle. (This is relative to the current window or display context.) The dxDst and dyDst members specify the width and height of the destination rectangle. These members are valid only if the ICDRAW_HDC flag is set. The ICDRAW_FULLSCREEN flag indicates the entire screen should be used for display and overrides any values specified for these members.

The xSrc, ySrc, dxSrc, and dySrc members specify a source rectangle used to clip the frames of the video sequence. The source rectangle is stretched to fill the destination rectangle. The xSrc and ySrc members specify the x- and y-position of the upper-right corner of the source rectangle. (This is relative to a full-frame image of the video.) The dxSrc and dySrc members specify the width and height of the source rectangle.

Your driver should stretch the image from the source rectangle to fit the destination rectangle. If the client application changes the size of the source and destination rectangles, it will send the ICM_DRAW_END message and specify new rectangles with a new ICM_DRAW_BEGIN message. For more information about handling the source and destination rectangles, see the StretchDIBits function.

The lpbi member specifies a pointer to a BITMAPINFOHEADER data structure containing the input format.

The dwRate member specifies the decompression rate in an integer format. To obtain the rate in frames-per-second, divide this value by the value in dwScale. Your driver uses these values when it handles the ICM_DRAW_START message.

If your driver can decompress the data with the parameters specified in the ICDRAWBEGIN data structure, your driver should return ICERR_OK and allocate any resources it needs to efficiently decompress the data. If your driver cannot decompress the data with the parameters specified, your driver should fail the message by returning ICERR_NOTSUPPORTED. When this message fails, your driver does not receive an ICM_DRAW_END message, so it should not prepare its resources for other ICM_DRAW messages.


Drawing the Data

The client application sends ICM_DRAW each time it has data to decompress. (Your driver should use this message to decompress data. It should wait for the ICM_DRAW_START message before it begins to render the data.) The client application uses the flags in the file index to ensure the first frame in a series of decompressed frames starts with a key-frame boundary. Your driver must allocate the memory it needs for the decompressed data.

The ICDRAW data structure specified in dwParam1 contains the decompression parameters. The value specified in dwParam2 specifies the size of the structure. The format of the input data is specified in a BITMAPINFOHEADER structure pointed to by lpFormat. The input data is in a buffer specified by lpData. The number of bytes in the input buffer is specified by cbData.

The client application sets the ICDRAW_HURRYUP flag in the dwFlags member to direct your driver to decompress data at a faster rate. For example, the client application might use this flag when the video is starting to lag behind the audio. If your driver cannot speed up its decompression and rendering performance, it might be necessary to avoid rendering a frame of data. The client application sets the ICDRAW_UPDATE flag and sets lpData to NULL to have your driver update the screen based on data previously received.

The client application sets the ICDRAW_PREROLL flag if it is sending data in advance of the data to be rendered. For example, if the client application will display frame 10 of a compressed sequence, it sets this flag for the first 9 frames. This sends the key frame and other intermediate frames to the driver so it can decompress the tenth frame.

The client application sets the ICDRAW_UPDATE flag to have the driver refresh the screen.

The ICDRAW_NOTKEYFRAME flag indicates the data is not a key frame.

The ICDRAW_NULLFRAME flag indicates the previous frame should be repeated.

When your driver has finished decompressing the data, it returns ICERR_OK. After the driver returns from this message, the client application deallocates or reuses the memory containing the format and image data. If your driver needs the format or image data for future use, it should copy the data it needs before it returns from the message.


Ending Drawing

Your driver receives ICM_DRAW_END when the client application no longer needs data decompressed or rendered. For this message, your driver should free the resources it allocated for the ICM_DRAW_BEGIN message. Your driver should also leave the display in the full-screen mode.

After sending ICM_DRAW_END, the client application must send ICM_DRAW_BEGIN to continue decompressing data. The client application does not have to send an ICM_DRAW_END message for each ICM_DRAW_BEGIN message. The client application can use ICM_DRAW_BEGIN to restart decompression without sending ICM_DRAW_END.


Rendering the Data

The client application sends the following messages to control the driver’s internal clock for rendering the decompressed data.
Message | Description
ICM_DRAW_GETTIME | Obtains the value of the driver’s internal clock if it is handling the timing of drawing frames.
ICM_DRAW_SETTIME | Sets the driver’s internal clock if it is handling the timing of drawing frames.
ICM_DRAW_START | Starts the internal clock of a driver if it handles the timing of drawing frames.
ICM_DRAW_STOP | Stops the internal clock of a driver if it handles the timing of drawing frames.
ICM_DRAW_WINDOW | Informs the driver the display window has been moved, hidden, or displayed.
ICM_DRAW_FLUSH | Flushes any frames that are waiting to be drawn.
ICM_DRAW_RENDERBUFFER | Draws a frame waiting to be drawn.
ICM_DRAW_CHANGEPALETTE | Changes the palette.

The client application sends ICM_DRAW_START to have your driver start (or continue) rendering data at the rate specified by the ICM_DRAW_BEGIN message. The ICM_DRAW_STOP message pauses the internal clock. Neither of these messages use dwParam1, dwParam2, or a return value.

The client application uses ICM_DRAW_GETTIME to obtain the value of the internal clock. Your driver returns the current-time value (this is usually frame numbers for video) in the DWORD pointed to by dwParam1. The current time is relative to the start of drawing.

The client application uses ICM_DRAW_SETTIME to set the value of the internal clock. Typically, the client application uses this message to synchronize the driver’s clock to an external clock. Your driver should set its clock to the value (this is usually frame numbers for video) specified in the DWORD pointed to by dwParam1.

The client application sends ICM_DRAW_FLUSH to have your driver discard any frames that have not been drawn.

The client application sends ICM_DRAW_RENDERBUFFER to have your driver draw the image currently buffered.

The client application sends ICM_DRAW_CHANGEPALETTE when the palette changes in the movie.


Notifying Applications of Compression and Decompression Status

A client application sends the ICM_SET_STATUS_PROC message to receive notification messages about the progress of compression or decompression. When received, drivers should periodically send notification messages to the callback function specified with the message during compression or decompression. Support of this function is optional but highly recommended if compression or decompression takes longer than approximately one tenth of one second.

The dwParam1 parameter of the message specifies a pointer to an ICSETSTATUSPROC structure and dwParam2 specifies the size of the ICSETSTATUSPROC data structure. The lParam member specifies a constant passed to the status procedure when it is called. The fpfnStatus member specifies a pointer to the status function. This is NULL if status messages should not be sent. The status function has the following prototype:

    LONG MyStatusProc(LPARAM lParam, UINT message)

The lParam parameter specifies the constant specified in the lParam member of the ICSETSTATUSPROC structure. The message parameter specifies one of the following messages the driver sends.
Message | Description
ICSTATUS_START | Indicates the operation is starting.
ICSTATUS_STATUS | Indicates the operation is proceeding, and is l percent done.
ICSTATUS_END | Indicates the operation is finishing.
ICSTATUS_YIELD | Indicates a length operation is proceeding.


Using Installable Compressors for Nonvideo Data

Installable compressors are not necessarily limited to video data. By using a different value than ‘vidc’ in the fccType member, you can specify that your installable driver expects to handle a type of data that is not video. Four-character codes for nonvideo data should also be registered.

While VidEdit does not support data that is not audio or video, MCIAVI does provide limited support for other data types, using installable renderers. If you create a stream with a four-character code type that does not represent audio or video, its type and handler information are used to search for a driver capable of handling the data. The search follows the same procedure used for installable compressor drivers.

Writing a driver to render nonvideo data is very similar to rendering video, with the following differences:

  • The format used is not a BITMAPINFO structure. The format is defined by the class of decompressor.

  • The ICM_DECOMPRESS messages are not used. All data is rendered using the ICM_DRAW messages because there is no defined decompressed form for arbitrary data.


Testing Video Compression and Decompression Drivers

You can exercise both the compression and decompression capabilities of a driver with the VidEdit editing tool. You can also exercise the decompression capabilities of a driver with MCIAVI. (One way to test the decompression capabilities is to preview an unedited file in VidEdit. In this case, VidEdit uses MCIAVI to decompress the file.)


Messages, Compression Drivers

The following topics describe‹This section describes› the messages used by compression drivers.


ICM_ABOUT

The ICM_ABOUT message is sent to a video compression driver to display its About dialog box.

Parameters
dwParam1
Specifies a handle to a window (HWND) that should correspond to the parent of the displayed dialog box.

If dwParam1 is -1, the driver returns ICERR_OK if it has an About dialog box but it should not display the dialog box. The driver returns ICERR_UNSUPPORTED if it does not display a dialog box.

dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver supports this message. Otherwise, returns ICERR_UNSUPPORTED.


ICM_COMPRESS

The ICM_COMPRESS message is sent to a video compression driver to compress a frame of data into an application-supplied buffer.

Parameters
dwParam1
Specifies a pointer to an ICCOMPRESS data structure. The following members of ICCOMPRESS specify the compression parameters:

The lpbiInput member contains the format of the uncompressed data; the data itself is in a buffer pointed to by lpInput.

The lpbiOutput member contains a pointer to the output (compressed) format, and lpOutput contains a pointer to a buffer used for the compressed data.

The lpbiPrev member contains a pointer to the format of the previous frame, and lpPrev contains a pointer to a buffer used for the previous data. These members are used by drivers that do temporal compression.

The driver should use the biSizeImage member of the BITMAPINFOHEADER structure associated with lpbiOutput to return the size of the compressed frame.

The lpckid member points to a DWORD. If the pointer is not NULL, the driver should specify a two-character code for the chunk ID in the DWORD. The chunk ID should correspond to the chunk ID used in the AVI file.

The lpdwFlags member points to a DWORD. The driver should fill the DWORD with the flags that should go in the AVI index. In particular, if the returned frame is a key frame, your driver should set the AVIIF_KEYFRAME flag.

The dwFrameSize member contains the size into which the compressor should try to make the frame fit. The size value is used for compression methods that can make tradeoffs between compressed image size and image quality.

The dwQuality member contains the specific quality the compressor should use, if it supports quality.

dwParam2
Specifies the size of the ICCOMPRESS structure.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.


ICM_COMPRESS_BEGIN

The ICM_COMPRESS_BEGIN message is sent to a video compression driver to prepare it for compressing data.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO data structure indicating the input format.
dwParam2
Specifies a pointer to a BITMAPINFO data structure indicating the output format.
Return Value

Returns ICERR_OK if the specified compression is supported. Otherwise, returns ICERR_BADFORMAT if the input or output format is not supported.

Comments

The driver should set up any tables or memory that it needs to compress the data formats efficiently when it receives the ICM_COMPRESS message.

The ICM_COMPRESS_BEGIN and ICM_COMPRESS_END messages do not nest. If your driver receives an ICM_COMPRESS_BEGIN message before compression is stopped with ICM_COMPRESS_END, it should restart compression with new parameters.


ICM_COMPRESS_END

The ICM_COMPRESS_END message is sent to a video compression driver to end compression. The driver should clean up after compressing and release any memory allocated for ICM_COMPRESS_BEGIN.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

The ICM_COMPRESS_BEGIN and ICM_COMPRESS_END messages do not nest. If your driver receives an ICM_COMPRESS_BEGIN message before compression is stopped with ICM_COMPRESS_END, it should restart compression with new parameters.


ICM_COMPRESS_FRAMES_INFO

The ICM_COMPRESS_FRAMES_INFO message is sent to a compression driver to set the parameters for the pending compression.

Parameters
dwParam1
Specifies a pointer to an ICCOMPRESSFRAMES structure. For this message, the GetData and PutData members are not used.
dwParam2
Specifies the size of the structure pointed to by dwParam1.
Return Value

Returns ICERR_OK if successful.

Comments

A compressor can use this message to make decisions about the amount of space allocated for each frame while compressing.


ICM_COMPRESS_GET_FORMAT

The ICM_COMPRESS_GET_FORMAT message is sent to a video compression driver to obtain the output format of the compressed data.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO data structure indicating the input format.
dwParam2
Specifies zero or a pointer to a BITMAPINFO data structure used to return the output format.
Return Value

If dwParam2 is zero, the driver returns the size of the output format. If dwParam2 is not zero, the driver returns a status value (either ICERROR_OK or an error status).

Comments

If dwParam2 is zero, the driver should only return the size of the output format.

If dwParam2 is nonzero, the driver should fill the BITMAPINFO data structure with the default output format corresponding to the input format specified for dwParam1. If the compressor can produce several formats, the default format should be the one that preserves the greatest amount of information.

For example, the Microsoft Video Compressor can compress 16-bit data into either an 8-bit palettized compressed form or a 16-bit true-color compressed form. The 16-bit format more accurately represents the original data, and thus is returned for this message.


ICM_COMPRESS_GET_SIZE

The ICM_COMPRESS_GET_SIZE message is sent to a video compression driver to obtain the maximum size of one frame of data when it is compressed in the output format.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO data structure indicating the input format.
dwParam2
Specifies a pointer to a BITMAPINFO data structure indicating the output format.
Return Value

Returns the maximum number of bytes a single compressed frame can occupy.

Comments

Typically, applications send this message to determine how large a buffer to allocate for the compressed frame.

The driver should calculate the size of the largest possible frame based on the input and target formats.


ICM_COMPRESS_QUERY

The ICM_COMPRESS_QUERY message is sent to a video compression driver to determine if it can compress a specific input format, or if it can compress the input format to a specific output format.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO data structure describing the input format.
dwParam2
Specifies a pointer to a BITMAPINFO data structure describing the output format, or zero. Zero indicates any output format is acceptable.
Return Value

Returns ICERR_OK if the specified compression is supported. Otherwise, returns ICERR_BADFORMAT, indicating that the input or output format is not supported.

Comments

On receiving this message, the driver should examine the BITMAPINFO structure associated with dwParam1 to see if it can compress the input format. The driver should return ICERR_OK only if it can compress the input format to the output format specified for dwParam2. (If any output format is acceptable, dwParam2 is zero.)


ICM_CONFIGURE

The ICM_CONFIGURE message is sent to a video compression driver to display its configuration dialog box.

Parameters
dwParam1
Specifies a handle to a window (HWND) that should correspond to parent of the displayed dialog box.

If dwParam1 is -1, a driver returns ICERR_OK if it has a configuration dialog box but it should not display the dialog box. The driver returns ICERR_UNSUPPORTED if it does not display a dialog box.

dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver supports this message. Otherwise, returns ICERR_UNSUPPORTED.

Comments

This message is distinct from the DRV_CONFIGURE message used for hardware configuration. The dialog box for this message should let the user configure the internal state referenced by ICM_GETSTATE and ICM_SETSTATE. For example, this dialog box might let the user change parameters affecting the quality level and other similar compression options.


ICM_DECOMPRESS

The ICM_DECOMPRESS message is sent to a video compression driver to decompress a frame of data into an application-supplied buffer.

Parameters
dwParam1
Specifies a pointer to an ICDECOMPRESS structure.
dwParam2
Specifies the size of the ICDECOMPRESS structure.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

If the driver is to decompress data directly to the screen instead of a buffer, it receives the ICM_DRAW message.

The driver should return an error if this message is received before the ICM_DECOMPRESS_BEGIN message.


ICM_DECOMPRESS_BEGIN

The ICM_DECOMPRESS_BEGIN message is sent to a video compression driver for decompressing data. When the driver receives this message, it should allocate buffers and do any time-consuming operations so it can process ICM_DECOMPRESS messages efficiently.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO data structure describing the input format.
dwParam2
Specifies a pointer to a BITMAPINFO data structure describing the output format.
Return Value

Returns ICERR_OK if the specified decompression is supported. Otherwise, returns ICERR_BADFORMAT indicating that the input or output format is not supported.

Comments

To have the driver decompress data directly to the screen, an application sends the ICM_DRAW_BEGIN message.

ICM_DECOMPRESS_BEGIN and ICM_DECOMPRESS_END do not nest. If your driver receives an ICM_DECOMPRESS_BEGIN message before decompression is stopped with ICM_DECOMPRESS_END, it should restart decompression with new parameters.


ICM_DECOMPRESS_END

The ICM_DECOMPRESS_END message is sent to a video decompression driver to have it clean up after decompressing.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

The driver should free any resources allocated for ICM_DECOMPRESS_BEGIN.

ICM_DECOMPRESS_BEGIN and ICM_DECOMPRESS_END do not nest. If your driver receives ICM_DECOMPRESS_BEGIN before decompression is stopped with ICM_DECOMPRESS_END, it should restart decompression with new parameters.


ICM_DECOMPRESS_GET_FORMAT

The ICM_DECOMPRESS_GET_FORMAT message is sent to a video decompression driver to obtain the format of the decompressed data.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO data structure describing the input format.
dwParam2
Specifies zero or a pointer to a BITMAPINFO data structure used to return the output format.
Return Value

If dwParam2 is zero, the driver returns the size of the output format. If dwParam2 is not zero, the driver returns a status value (either ICERROR_OK or an error status).

Comments

If dwParam2 is zero, the driver returns only the size of the output format. An application sets dwParam2 to zero to determine the size of the buffer it needs to allocate.

If dwParam2 is nonzero, the driver should fill the BITMAPINFO data structure with the default output format corresponding to the input format specified for dwParam1. If the compressor can produce several different formats, the default format should be the one that preserves the greatest amount of information.

For example, if a driver can produce either 24-bit full-color images or 8-bit gray-scale images, the default should be 24-bit images. This ensures the highest possible image quality if the video data must be edited and recompressed.


ICM_DECOMPRESS_GET_PALETTE

The ICM_DECOMPRESS_GET_PALETTE message is sent to a video decompression driver to obtain the color table of the output BITMAPINFOHEADER structure.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFOHEADER data structure indicating the input format.
dwParam2
Specifies zero or a pointer to a BITMAPINFOHEADER data structure used to return the color table. The space reserved for the color table is always at least 256 colors.
Return Value

Returns the size of the output format or an error code.

Comments

If dwParam2 is zero, the driver returns only the size of the output format. Applications specify zero to determine the size of the output format.

If dwParam2 is nonzero, the driver sets the biClrUsed member of the BITMAPINFOHEADER data structure to the number of colors in the color table. The driver fills the bmiColors members of the BITMAPINFO data structure with the actual colors.

The driver should support this message only if it uses a palette other than the one specified in the input format.


ICM_DECOMPRESS_QUERY

The ICM_DECOMPRESS_QUERY message is sent to a video compression driver to determine if the driver can decompress a specific input format, or if it can decompress the input format to a specific output format.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO structure describing the input format.
dwParam2
Specifies zero or a pointer to a BITMAPINFO structure used to return the output format. Zero indicates that any output format is acceptable.
Return Value

Returns ICERR_OK if the specified decompression is supported. Otherwise returns ICERR_BADFORMAT, indicating that the input or output format is not supported.


ICM_DECOMPRESS_SET_PALETTE

The ICM_DECOMPRESS_SET_PALETTE message specifies a palette for a video decompression driver to use if it is decompressing to a format that uses a palette.

Parameters
DwParam1
Address of a BITMAPINFOHEADER structure whose color table contains the colors that should be used if possible. If zero, use the default set of output colors.
DwParam2
Not used.
Return Values

Returns ICERR_OK if the decompression driver can precisely decompress images to the suggested palette using the set of colors as they are arranged in the palette. Returns ICERR_UNSUPPORTED otherwise.

Comments

This message should not affect decompression already in progress; rather, colors passed using this message should be returned in response to future ICM_DECOMPRESS_GET_FORMAT and ICM_DECOMPRESS_GET_PALETTE messages. Colors are sent back to the decompression driver in a future ICM_DECOMPRESS_BEGIN message.

This message is used primarily when a driver decompresses images to the screen and another application that uses a palette is in the foreground, forcing the decompression driver to adapt to a foreign set of colors.


ICM_DECOMPRESSEX

The ICM_DECOMPRESSEX message is sent to a video compression driver to decompress a frame of data directly to the screen, decompress to an upside-down DIB, or decompress images described with source and destination rectangles. This message is similar to ICM_DECOMPRESS, except that it uses the ICDECOMPRESSEX structure to decompression information.

Parameters
dwParam1
Specifies a pointer to an ICDECOMPRESSEX structure.
dwParam2
Specifies the size of the ICDECOMPRESSEX structure.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

If the driver is to decompress data directly to the screen instead of a buffer, it receives the ICM_DRAW message.

The driver should return an error if this message is received before the ICM_DECOMPRESSEX_BEGIN message.


ICM_DECOMPRESSEX_BEGIN

The ICM_DECOMPRESSEX_BEGIN message is sent to a video compression driver for decompressing data. When the driver receives this message, it should allocate buffers and do any time-consuming operations so that it can process ICM_DECOMPRESSEX messages efficiently.

Parameters
dwParam1
Specifies a pointer to an ICDECOMPRESSEX data structure describing the input and output formats.
dwParam2
Specifies the size of an ICDECOMPRESSEX data structure.
Return Value

Returns ICERR_OK if the specified decompression is supported. Otherwise returns ICERR_BADFORMAT, indicating that the input or output format is not supported.

Comments

To have the driver decompress data directly to the screen, an application sends the ICM_DRAW_BEGIN message.

The ICM_DECOMPRESSEX_BEGIN and ICM_DECOMPRESSEX_END messages do not nest. If your driver receives an ICM_DECOMPRESSEX_BEGIN message before decompression is stopped with ICM_DECOMPRESSEX_END, it should restart decompression with new parameters.


ICM_DECOMPRESSEX_END

The ICM_DECOMPRESSEX_END message is sent to a video decompression driver to have it clean up after decompressing.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

The driver should free any resources allocated for ICM_DECOMPRESSEX_BEGIN.

The ICM_DECOMPRESSEX_BEGIN and ICM_DECOMPRESSEX_END messages do not nest. If your driver receives ICM_DECOMPRESSEX_BEGIN before decompression is stopped with ICM_DECOMPRESSEX_END, it should restart decompression with new parameters.


ICM_DECOMPRESSEX_QUERY

The ICM_DECOMPRESSEX_QUERY message is sent to a video compression driver to determine if the driver can decompress a specific input format, or if it can decompress the input format to a specific output format.

Parameters
dwParam1
Specifies a pointer to an ICDECOMPRESSEX structure describing the input format.
dwParam2
Specifies zero or a pointer to an ICDECOMPRESSEX structure used to return the output format. Zero indicates that any output format is acceptable.
Return Value

Returns ICERR_OK if the specified decompression is supported. Otherwise, returns ICERR_BADFORMAT indicating that the input or output format is not supported.


ICM_DRAW

The ICM_DRAW message is sent to a rendering driver to decompress a frame of data and draw it to the screen.

Parameters
dwParam1
Specifies a pointer to an ICDRAW structure.
dwParam2
Specifies the size of the ICDRAW structure.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

If the ICDRAW_UPDATE flag is set in the dwFlags member of the ICDRAW data structure, the area of the screen used for drawing is invalid and needs to be updated. This depends on the contents of dwFlags.

If the ICDRAW_UPDATE flag is set and dwFlags is NULL, the driver should update the entire destination rectangle with the current image. If the driver does not keep an offscreen image, it can fail this message.

If the ICDRAW_UPDATE flag is set and dwFlags is nonNULL, the driver should draw the data and make sure the entire destination is updated. If the driver does not keep an offscreen image, it can fail this message.

If the ICDRAW_HURRYUP flag is set in the dwFlags member, the calling application is requesting the driver to proceed as quickly as possible, possibly not even updating the screen.

If the ICDRAW_PREROLL flag is set in the dwFlags member, this video frame is merely preliminary information and should not be displayed, if possible. For instance, if play is to start from frame 10, and frame 0 is the nearest previous key frame, frames 0 through 9 will have the ICDRAW_PREROLL flag set.

If the driver is to decompress data into a buffer instead of drawing directly to the screen, ICM_DECOMPRESS is sent instead.


ICM_DRAW_BEGIN

The ICM_DRAW_BEGIN message is sent to a rendering driver to prepare it for drawing data.

Parameters
dwParam1
Specifies a pointer to an ICDRAWBEGIN data structure describing the input format.
dwParam2
Specifies the size of the ICDRAWBEGIN data structure describing the input format.
Return Value

Returns ICERR_OK if the driver supports drawing the data to the screen in the manner and format specified. Otherwise, returns ICERR_BADFORMAT if the input or output format is not supported, or ICERR_NOTSUPPORTED if the message is not supported.

Comments

If the driver is to decompress data into a buffer instead of drawing directly to the screen, ICM_DECOMPRESS_BEGIN is sent rather than this one.

If the driver does not support drawing directly to the screen, it should return ICERR_NOTSUPPORTED.

The ICM_DRAW_BEGIN and ICM_DRAW_END messages do not nest. If your driver receives ICM_DRAW_BEGIN before decompression is stopped with ICM_DRAW_END, it should restart decompression with new parameters.


ICM_DRAW_CHANGEPALETTE

The ICM_DRAW_CHANGEPALETTE message is sent to a rendering driver if the palette of the movie is changing.

Parameters
dwParam1
Points to a BITMAPINFO structure that contains the new format and optional color table.
dwParam2
Not used. Set to zero.
Return Value

Returns ICERR_OK if successful.

Comments

This message should be supported by installable rendering handlers if they draw palettized DIBs.


ICM_DRAW_END

The ICM_DRAW_END message is sent to rendering drivers to clean up after decompressing an image to the screen.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful. Otherwise, returns an error number.

Comments

The ICM_DRAW_BEGIN and ICM_DRAW_END messages do not nest. If your driver receives ICM_DRAW_BEGIN before decompression is stopped with ICM_DRAW_END, it should restart decompression with new parameters.


ICM_DRAW_FLUSH

The ICM_DRAW_FLUSH message is sent to a rendering driver to flush any frames that are waiting to be drawn.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

None.

Comments

This message is used only by hardware that does its own asynchronous decompression, timing, and drawing.


ICM_DRAW_GET_PALETTE

The ICM_DRAW_GET_PALETTE message is sent to a rendering driver to obtain a palette.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

The driver should return a palette handle. It should return NULL or IC_UNSUPPORTED if it doesn’t have a handle to return.


ICM_DRAW_GETTIME

The ICM_DRAW_GETTIME message is sent to a rendering driver to obtain the current value of its internal clock if it is handling the timing of drawing frames.

Parameters
dwParam1
Specifies a pointer to a LONG data type used to return the current time. The return value should be specified in samples. This corresponds to frames for video.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful.

Comments

This message is generally only supported by hardware that does its own asynchronous decompression, timing, and drawing. The message will also be sent only if the hardware is being used as the synchronization master.


ICM_DRAW_QUERY

The ICM_DRAW_QUERY message is sent to a rendering driver to determine if it can render data in a specific format.

Parameters
dwParam1
Specifies a pointer to a BITMAPINFO structure describing the input format.
dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver can render data in the specified format. Otherwise, returns ICERR_BADFORMAT indicating that the format is not supported.

Comments

This message asks if the driver recognizes the format for drawing operations. The ICM_DRAW_BEGIN message determines if the driver can draw the data.


ICM_DRAW_REALIZE

The ICM_DRAW_REALIZE message is sent to a rendering driver to realize its palette used while drawing.

Parameters
dwParam1
Specifies a handle to the display context used to realize the palette.
dwParam2
Specifies TRUE if the palette is to be realized in the background. Specifies FALSE if the palette is to be realized in the foreground.
Return Value

Returns ICERR_OK if the palette is realized. If this message is not supported, the driver returns ICERR_UNSUPPORTED.

Comments

Drivers need to respond to this message only if the drawing palette is different from the decompressed palette.

If this message is not supported, the palette associated with the decompressed data is realized.


ICM_DRAW_RENDERBUFFER

The ICM_DRAW_RENDERBUFFER message is sent to a rendering driver to draw the frames that have been passed to it.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

None.

Comments

This message is typically used to perform a “seek” operation when, rather than playing a sequence of video frames, the driver must be specifically instructed to display each video frame passed to it.

This message is used only by hardware that does its own asynchronous decompression, timing, and drawing.


ICM_DRAW_SETTIME

The ICM_DRAW_SETTIME message is sent to a rendering driver to inform it of what frame it should be drawing if it is handling the timing of drawing frames.

Parameters
dwParam1
Specifies a LONG data type containing the sample number corresponding to the frame the driver should be rendering. The value will be specified in samples. This corresponds to frames for video.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful.

Comments

This message is generally only supported by hardware that does its own asynchronous decompression, timing, and drawing. The message will only be sent if the hardware is not being used as the synchronization master.

Typically, the driver will compare the specified “correct” value with its internal clock, and take actions to synchronize the two if the difference is significant.


ICM_DRAW_START

The ICM_DRAW_START message is sent to a rendering driver to start its internal clock for the timing of drawing frames.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

None.

Comments

This message is used only by hardware that does its own asynchronous decompression, timing, and drawing.

When it receives this message, the driver should start rendering data at the rate specified with ICM_DRAW_BEGIN.

The ICM_DRAW_START and ICM_DRAW_STOP messages do not nest. If your driver receives ICM_DRAW_START before rendering is stopped with ICM_DRAW_STOP, it should restart rendering with new parameters.


ICM_DRAW_START_PLAY

The ICM_DRAW_START_PLAY message provides the start and end times of a play operation to a rendering driver.

Parameters
dwParam1
Start time.
dwParam2
End time.
Return Values

This message does not return a value.

Comments

This message precedes any frame data sent to the rendering driver.

Units for lFrom and lTo are specified with the ICM_DRAW_BEGIN message. For video data this is normally a frame number. For more information about the playback rate, see the dwRate and dwScale members of the ICDRAWBEGIN structure.

If the end time is less than the start time, the playback direction is reversed.


ICM_DRAW_STOP

The ICM_DRAW_STOP message is sent to a rendering driver to stop its internal clock for the timing of drawing frames.

Parameters
dwParam1
Not used.
dwParam2
Not used.
Return Value

None.

Comments

This message is used only by hardware that does its own asynchronous decompression, timing, and drawing.

The ICM_DRAW_STOP and ICM_DRAW_START messages do not nest. If your driver receives ICM_DRAW_START before rendering is stopped with ICM_DRAW_STOP, it should restart rendering with new parameters.


ICM_DRAW_STOP_PLAY

The ICM_DRAW_STOP_PLAY message notifies a rendering driver when a play operation is complete.

Parameters
dwParam1
Not used.
dwParam2
Not used.

Return Value

This message does not return a value.

Remarks

This message is used when the play operation is complete. The ICM_DRAW_STOP message is used to end timing.


ICM_DRAW_SUGGESTFORMAT

The ICM_DRAW_SUGGESTFORMAT message is sent to a rendering driver to have it suggest a decompressed format that it can draw.

Parameters
dwParam1
Specifies a pointer to an ICDRAWSUGGEST data structure.
dwParam2
Specifies the size of the ICDRAWSUGGEST data structure.
Return Value

Returns ICERR_OK if successful. If lpbiSuggest is NULL, returns the amount of memory to hold the format that would have been suggested.

Comments

The driver should examine the format specified in the lpbiIn member of the ICDRAWSUGGEST structure and use the lpbiSuggest member to return a format it can draw. The returned format should be as similar as possible to the input format.

For example, if the driver can draw only 8 or 16-bit RGB data, and the input format is 320-by-240-by-16-bit MSVideo1 compressed data, the renderer suggests the 320-by-240-by-16-bit RGB format.

Optionally, the driver can use the installable compressor handle passed in hicDecompressor to make more complex selections. For example, if the input format is 24-bit JPEG data, a renderer could query the compressor to find out if it can decompress to a YUV format (which might be drawn more efficiently) before selecting the format to suggest.


ICM_DRAW_WINDOW

The ICM_DRAW_WINDOW message is sent to a rendering driver when the window specified for ICM_DRAW_BEGIN has moved, or has become totally obscured. This message is used by overlay drivers so they can draw when the window is obscured or moved.

Parameters
dwParam1
Points to the destination rectangle in screen coordinates. If dwParam1 points to an empty rectangle, drawing should be turned off.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful.

Comments

This message is only supported by hardware that does its own asynchronous decompression, timing, and drawing.

The rectangle is empty if the window is totally hidden by other windows. Drivers should turn off overlay hardware when the rectangle is empty.


ICM_GET

The ICM_GET message retrieves a buffer of driver-defined status information from a video compression driver.

Parameters
dwParam
Address of a block of memory to be filled with driver-defined status information. If NULL, the driver should return the amount of memory required by the driver information.
DwParam2
Size, in bytes, of the block of memory.
Return Value

Returns the amount of memory, in bytes, required to store the status information.

Comments

The structure used to represent status information is driver-specific and defined by the driver.


ICM_GETBUFFERSWANTED

The ICM_GETBUFFERSWANTED message is sent to a video compression driver to have it return information about how many samples the driver will pre-buffer.

Parameters
dwParam1
Specifies a pointer to a DWORD. The driver uses the DWORD to return the number of samples it needs to get in advance of when they will be presented.
dwParam2
Not used.
Return Value

Returns ICERR_OK if successful. Otherwise, returns ICERR_UNSUPPORTED.

Comments

Typically, this message is only used by a driver that uses hardware to render data and must ensure hardware pipelines remain full. For example, if a driver controls a video decompression board that can hold ten frames of video, it could return ten for this message. This instructs an application to try and stay ten frames ahead of the frame it currently needs.


ICM_GETDEFAULTKEYFRAMERATE

The ICM_GETDEFAULTKEYFRAMERATE message is sent to a video compression driver to obtain its default (or preferred) key frame spacing.

Parameters
dwParam1
Specifies a pointer to a DWORD used to return the preferred key frame spacing.
dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver supports this message. Otherwise, returns ICERR_UNSUPPORTED.


ICM_GETDEFAULTQUALITY

The ICM_GETDEFAULTQUALITY message is sent to a video compression driver to obtain its default quality setting.

Parameters
dwParam1
Specifies a pointer to a DWORD used to return the default quality value.
dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver supports this message. If not, returns ICERR_UNSUPPORTED.

Comments

Quality values range from 0 through 10,000.


ICM_GETINFO

The ICM_GETINFO message is set to a video compression driver to have it return information describing the driver.

Parameters
dwParam1
Specifies a pointer to an ICINFO data structure used to return information.
dwParam2
Specifies the size of the ICINFO data structure.
Return Value

Returns the size of the ICINFO data structure, or zero if an error occurs.

Comments

Typically, this message is sent by applications to display a list of the installed compressors.

The driver should fill in all members of the ICINFO structure except the szDriver member.


ICM_GETQUALITY

The ICM_GETQUALITY message is sent to a video compression driver to obtain its current quality setting.

Parameters
dwParam1
Specifies a pointer to a DWORD used to return the current quality value.
dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver supports this message. If not, returns ICERR_UNSUPPORTED.

Comments

Quality values range from 0 through 10,000.


ICM_GETSTATE

The ICM_GETSTATE message is sent to a video compression driver to have it fill a block of memory describing the compressor’s current configuration.

Parameters
dwParam1
Specifies a pointer to a block of memory to be filled with the current state, or NULL. If NULL, return the amount of memory required by the state information.
dwParam2
Specifies the size of the block of memory.
Return Value

If dwParam1 is NULL, the driver returns the size of the configuration information. If dwParam1 is not NULL, and the value received in dwParam2 is less than size of the configuration information, the driver returns zero. Otherwise the driver returns the size of the information it returns in the structure supplied by dwParam1.

Comments

Client applications send this message with dwParam1 set to NULL to determine the size of the memory block required for obtaining the state information.

The data structure used to represent state information is driver-specific and is defined by the driver.


ICM_SET_STATUS_PROC

The ICM_SET_STATUS_PROC message is periodically sent to an application’s status callback function during lengthy operations.

Parameters
dwParam1
Specifies a pointer to an ICSETSTATUSPROC structure.
dwParam2
Specifies the size of the ICSETSTATUSPROC data structure.
Return Value

Returns ICERR_OK if successful.

Comments

Support of this function is optional but highly recommended if compression or decompression takes longer than approximately one tenth of one second.


ICM_SETQUALITY

The ICM_SETQUALITY message is sent to a video compression driver to set the quality level for compression.

Parameters
dwParam1
Specifies the new quality value.
dwParam2
Not used.
Return Value

Returns ICERR_OK if the driver supports this message. If not, returns ICERR_UNSUPPORTED.

Comments

Quality values range from 0 through 10,000.


ICM_SETSTATE

The ICM_SETSTATE message is sent to a video compression driver to set the state of the compressor.

Parameters
dwParam1
Specifies a pointer to a block of memory containing configuration data, or NULL. If NULL, the driver should return to its default state.
dwParam2
Specifies the size of the block of memory.
Return Value

Returns the number of bytes actually used by the compressor. A return value of zero generally indicates an error.

Comments

Because the information used by ICM_SETSTATE is private and specific to a given compressor, client applications should use this message only to restore information previously obtained with the ICM_GETSTATE message.


Callback Functions, Compression Drivers

The following topics describe the callback functions used by compression drivers.


Status

LONG Status(LPARAM lParam, UINT message, LONG l);

This is the prototype for a status function used by the ICM_SET_STATUS_PROC message.

Parameters
lParam
Contains the constant specified with the status callback address.
message
Specifies one of the following values:

Value

Meaning

ICSTATUS_START

Indicates a lengthy operation is starting.

ICSTATUS_STATUS

Indicates the operation is proceeding, and is l percent done.

ICSTATUS_END

Indicates a lengthy operation is finishing.

ICSTATUS_YIELD

Indicates a lengthy operation is proceeding. Essentially, this is just the same as ICSTATUS_STATUS without a specific percentage value.

l
Message-specific data.
Return Value

Returns zero if processing should continue, or a nonzero value if it should end.


Structures, Compression Drivers

The following topics describe the structures used by compression drivers.


ICCOMPRESS

typedef struct {
    DWORD dwFlags;
    LPBITMAPINFOHEADER lpbiOutput;
    LPVOID lpOutput;
    LPBITMAPINFOHEADER lpbiInput;
    LPVOID lpInput;
    LPDWORD lpckid;
    LPDWORD lpdwFlags;
    LONG lFrameNum;
    DWORD dwFrameSize;
    DWORD dwQuality;
    LPBITMAPINFOHEADER lpbiPrev;
    LPVOID lpPrev;
} ICCOMPRESS;

The ICCOMPRESS structure is used with the ICM_COMPRESS message to specify compression parameters.

Members
dwFlags
Specifies flags used for compression. At present, only the ICCOMPRESS_KEYFRAME flag is defined and indicates that the input data should be treated as a key frame.
lpbiOutput
Specifies a pointer to a BITMAPINFOHEADER structure containing the output (compressed) format. The biSizeImage member of the BITMAPINFOHEADER structure must be filled in with the size of the compressed data.
lpOutput
Specifies a pointer to the buffer where the driver should write the compressed data.
lpbiInput
Specifies a pointer to a BITMAPINFOHEADER structure containing the input format.
lpInput
Specifies a pointer to the buffer containing input data.
lpckid
Specifies a pointer to a buffer used to return the chunk ID for data in the AVI file.
lpdwFlags
Specifies a pointer to a buffer used to return flags for the AVI index.
lFrameNum
Specifies the frame number of the frame to compress.
dwFrameSize
Specifies zero, or the desired maximum size (in bytes) for compressing this frame.
dwQuality
Specifies the compression quality.
lpbiPrev
Specifies a pointer to a BITMAPINFOHEADER structure containing the format of the previous frame. Normally, this is the same as the input format.
lpPrev
Specifies a pointer to the buffer containing the previous frame.


ICCOMPRESSFRAMES

typedef struct {
    DWORD dwFlags;
    LPBITMAPINFOHEADER lpbiOutput;
    LPARAM lOutput;
    LPBITMAPINFOHEADER lpbiInput;
    LPARAM lInput;
    LONG lStartFrame;
    LONG lFrameCount;
    LONG lQuality;
    LONG lDataRate;
    LONG lKeyRate;
    DWORD dwRate;
    DWORD dwScale;
    DWORD dwOverheadPerFrame;
    DWORD dwReserved2;
    LONG (CALLBACK* GetData) (LPARAM lInput, LONG lFrame, LPVOID lpBits,
      LONG len);
    LONG (CALLBACK* PutData) (LPARAM lOutput, LONG lFrame, LPVOID lpBits,
      LONG len);
} ICCOMPRESSFRAMES;

The ICCOMPRESSFRAMES structure is used with the ICM_COMPRESS_FRAMES_INFO message to specify compression parameters.

dwFlags
Specifies flags used for compression. At present, only the ICDECOMPRESSFRAMES_PADDING flag is defined and indicates that padding is used with the frame.
lpbiOutput
Specifies a pointer to a BITMAPINFOHEADER structure containing the output format.
lOutput
Specifies a parameter returned by the function specified by the PutData member.
lpbiInput
Specifies a pointer to a BITMAPINFOHEADER structure containing the input format.
lInput
Specifies a parameter returned by the function specified by the GetData member.
lStartFrame
Specifies the starting frame number to be compressed.
lFrameCount
Specifies the number of frames to compress.
lQuality
Specifies the quality.
lDataRate
Specifies the maximum data rate in bytes per second.
lKeyRate
Specifies the maximum spacing, in frames, between key frames.
dwRate
Specifies the compression rate in an integer format. To obtain the rate in frames-per-second, divide this value by the value in dwScale.
dwScale
Specifies the value used to scale dwRate to frames-per-second.
dwOverheadPerFrame
Reserved.
dwReserved2
Reserved.
GetData
Specifies the callback function used get frames of data to compress. (Not used.)
PutData
Specifies the callback function used to send frames of compressed data. (Not used.)


ICDECOMPRESS

typedef struct {
    DWORD dwFlags;
    LPBITMAPINFOHEADER lpbiInput;
    LPVOID lpInput;
    LPBITMAPINFOHEADER lpbiOutput;
    LPVOID lpOutput;
    DWORD ckid;
} ICDECOMPRESS;

The ICDECOMPRESS structure is used with the ICM_DECOMPRESS message to specify the parameters for decompressing the data.

Members
dwFlags
Specifies applicable flags. The following flags are defined:

Flag

Meaning

ICDECOMPRESS_HURRYUP

Indicates the data is just buffered and not drawn to the screen. Use this flag for the fastest decompression.

ICDECOMPRESS_UPDATE

Indicates the screen is being updated.

ICDECOMPRESS_PREROLL

Indicates this frame is not drawn because it is prior to the point in the movie where play begins.

ICDECOMPRESS_NULLFRAME

Indicates this frame does not have any data, and the decompressed image should be left the same.

ICDECOMPRESS_NOTKEYFRAME

Indicates that this frame is not a key frame.

lpbiInput
Specifies a pointer to a BITMAPINFOHEADER structure containing the input format.
lpInput
Specifies a pointer to a data buffer containing the input data.
lpbiOutput
Specifies a pointer to a BITMAPINFOHEADER structure containing the output format.
lpOutput
Specifies a pointer to a data buffer where the driver should write the decompressed image.
ckid
Specifies the chunk ID from the AVI file.


ICDECOMPRESSEX

typedef struct {
    DWORD dwFlags;
    LPBITMAPINFOHEADER lpbiSrc;
    LPVOID lpSrc;
    LPBITMAPINFOHEADER lpbiDst;
    LPVOID lpDst;
    int xDst;
    int yDst;
    int dxDst;
    int dyDst;
    int xSrc;
    int ySrc;
    int dxSrc;
    int dySrc;
} ICDECOMPRESSEX;

The ICDECOMPRESSEX structure is used with the ICM_DECOMPRESSEX message to specify the parameters for decompressing the data.

Members
dwFlags
Specifies applicable flags. The following flags are defined:

Flag

Meaning

ICDECOMPRESS_HURRYUP

Indicates the data is just buffered and not drawn to the screen. Use this flag for the fastest decompression.

ICDECOMPRESS_UPDATE

Indicates the screen is being updated.

ICDECOMPRESS_PREROLL

Indicates this frame will not actually be drawn, because it is before the point in the movie where play will start.

ICDECOMPRESS_NULLFRAME

Indicates this frame does not have any data, and the decompressed image should be left the same.

ICDECOMPRESS_NOTKEYFRAME

Indicates that this frame is not a key frame.

lpbiSrc
Specifies a pointer to a BITMAPINFOHEADER structure containing the input format.
lpSrc
Specifies a pointer to a data buffer containing the input data.
lpbiDst
Specifies a pointer to a BITMAPINFOHEADER structure containing the output format.
lpDst
Specifies a pointer to a data buffer where the driver should write the decompressed image.
xDst
Specifies the x-coordinate of the destination rectangle within the DIB specified by lpbiDst.
yDst
Specifies the y-coordinate of the destination rectangle.
dxDst
Specifies the width of the destination rectangle.
dyDst
Specifies the height of the destination rectangle.
xSrc
Specifies the x-coordinate of the source rectangle, within the DIB specified by lpbiSrc.
ySrc
Specifies the y-coordinate of the source rectangle.
dxSrc
Specifies the width of the source rectangle.
dySrc
Specifies the height of the source rectangle.


ICDRAW

typedef struct {
    DWORD dwFlags;
    LPVOID lpFormat;
    LPVOID lpData;
    DWORD cbData;
    LONG lTime;
} ICDRAW;

The ICDRAW structure is used with the ICM_DRAW message to specify the parameters for drawing video data to the screen.

Members
dwFlags
Specifies the flags from the AVI file index. The following flags are defined:

Flag

Meaning

ICDRAW_HURRYUP

Indicates the data is just buffered and not drawn to the screen. Use this flag for the fastest decompression.

ICDRAW_UPDATE

Indicates the driver should update the screen based on data previously received. In this case, the lpData parameter should be ignored.

ICDRAW_PREROLL

Indicates that this frame of video occurs before actual playback should start. For instance, if playback is to begin on frame 10, and frame 0 is the nearest previous key frame, frames 0 through 9 are sent to the driver with the ICDRAW_PREROLL flag set. The driver needs this data so it can display frame 10 properly, but frames 0 through 9 need not be individually displayed.

ICDRAW_NULLFRAME

Indicates the previous frame should be repeated.

ICDRAW_NOTKEYFRAME

Indicates the image is not a key frame.

lpFormat
Specifies a pointer to a structure containing the data format. For video, this is a BITMAPINFOHEADER structure.
lpData
Specifies the data to be rendered.
cbData
Specifies the number of bytes of data to be rendered.
lTime
Specifies the time, in samples, when this data should be drawn. For video data, this is usually a frame number. See dwRate and dwScale of the ICDRAW structure for details.


ICDRAWBEGIN

typedef struct {
    DWORD dwFlags;
    HPALETTE hpal;
    HWND hwnd;
    HDC hdc;
    int xDst;
    int yDst;
    int dxDst;
    int dyDst;
    LPBITMAPINFOHEADER lpbi;
    int xSrc;
    int ySrc;
    int dxSrc;
    int dySrc;
    DWORD dwRate;
    DWORD dwScale;
} ICDRAWBEGIN;

The ICDRAWBEGIN structure is used with the ICM_DRAW_BEGIN message to specify the parameters used to decompress the data.

Members
dwFlags
Specifies any of the following flags:

Flag

Meaning

ICDRAW_QUERY

Set when an application must determine if the device driver can handle the operation. The device driver does not actually perform the operation.

ICDRAW_FULLSCREEN

Indicates the full screen is used to draw the decompressed data.

ICDRAW_HDC

Indicates a window or display context is used to draw the decompressed data.

ICDRAW_ANIMATE

Indicates the palette might be animated.

ICDRAW_CONTINUE

Indicates drawing is a continuation of the previous frame.

ICDRAW_MEMORYDC

Indicates the display context is offscreen.

ICDRAW_UPDATING

Indicates the frame is being updated rather than being played.

hpal
Specifies a handle of the palette used for drawing.
hwnd
Specifies the handle of the window used for drawing.
hdc
Specifies the handle of the display context used for drawing. If NULL is specified, a display context to the specified window should be used.
xDst
Specifies the x-position of the destination rectangle.
yDst
Specifies the y-position of the destination rectangle.
dxDst
Specifies the width of the destination rectangle.
dyDst
Specifies the height of the destination rectangle.
lpbi
Specifies a pointer to a BITMAPINFOHEADER data structure containing the input format.
xSrc
Specifies the x-position of the source rectangle.
ySrc
Specifies the y-position of the source rectangle.
dxSrc
Specifies the width of the source rectangle.
dySrc
Specifies the height of the source rectangle.
dwRate
Specifies the decompression rate in an integer format. To obtain the rate in frames-per-second, divide this value by the value in dwScale.
dwScale
Specifies the value used to scale dwRate to frames-per-second.


ICDRAWSUGGEST

typedef struct {
    DWORD dwFlags;
    LPBITMAPINFOHEADER lpbiIn;
    LPBITMAPINFOHEADER lpbiSuggest;
    int dxSrc;
    int dySrc;
    int dxDst;
    int dyDst;
    HIC hicDecompressor;
} ICDRAWSUGGEST;

The ICDRAWSUGGEST structure is used with the ICM_DRAW_SUGGESTFORMAT message.

Members
dwFlags
Specifies applicable flags. Set this to zero.
lpbiIn
Specifies a pointer to the structure containing the compressed input format.
lpbiSuggest
Specifies a pointer to a buffer used to return the suggested format that the draw device would like to receive.
dxSrc
Specifies the source width.
dySrc
Specifies the source height.
dxDst
Specifies the destination width.
dyDst
Specifies the destination height.
hicDecompressor
Specifies a decompressor that can work with the format of data in lpbiIn.


ICINFO

typedef struct {
    DWORD dwSize;
    DWORD fccType;
    DWORD fccHandler;
    DWORD dwFlags;
    DWORD dwVersion;
    DWORD dwVersionICM;
    char szName[16];
    char szDescription[128];
    char szDriver[128];
} ICINFO;

The ICINFO structure is filled by a video compression driver when it receives the ICM_GETINFO message.

Members
dwSize
Should be set to the size of the ICINFO structure.
fccType
Specifies a four-character code representing the type of stream being compressed or decompressed. Set this to ‘vidc’ for video streams.
fccHandler
Specifies a four-character code identifying a specific compressor.
dwFlags
Specifies any flags. The following flags are defined for video compressors:

Flag

Meaning

VIDCF_QUALITY

Indicates the driver supports quality values.

VIDCF_CRUNCH

Indicates the driver supports crunching to a frame size.

VIDCF_TEMPORAL

Indicates the driver supports interframe compression.

VIDCF_DRAW

Indicates the driver supports drawing.

VIDCF_FASTTEMPORALC

Indicates the driver can do temporal compression and doesn’t need the previous frame.

VIDCF_FASTTEMPORALD

Indicates the driver can do temporal decompression and doesn’t need the previous frame.

VIDCF_COMPRESSFRAMES

Indicates the driver wants the “compress all frames” message.

dwVersion
Specifies the version number of the driver.
dwVersionICM
Specifies the version of the ICM supported by this driver; it should be set to ICVERSION.
szName[16]
Specifies the short name for the compressor. The name in the zero-terminated string should be suitable for use in list boxes.
szDescription[128]
Specifies a zero-terminated string containing the long name for the compressor.
szDriver[128]
Specifies a zero-terminated string for the module that contains the driver. Typically, a driver will not need to fill this out.


ICOPEN

typedef struct {
    DWORD dwSize;
    DWORD fccType;
    DWORD fccHandler;
    DWORD dwVersion;
    DWORD dwFlags;
    DWORD dwError;
    LPVOID pV1Reserved;
    LPVOID pV2Reserved;
    DWORD dnDevNode;
} ICOPEN;

The ICOPEN structure is sent to a video compression or decompression driver with the DRV_OPEN message.

Members
dwSize
Specifies the size of the structure.
fccType
Specifies a four-character code representing the type of stream being compressed or decompressed. For video streams, this should be ‘vidc’.
fccHandler
Specifies a four-character code identifying a specific compressor.
dwVersion
Specifies the version of the installable driver interface used to open the driver.
dwFlags
Contains flags indicating why the driver is opened. The following flags are defined:

Flag

Meaning

ICMODE_COMPRESS

The driver is opened to compress data.

ICMODE_DECOMPRESS

The driver is opened to decompress data.

ICMODE_QUERY

The driver is opened for informational purposes rather than for actual compression.

ICMODE_DRAW

The device driver is opened to decompress data directly to hardware.

dwError
Specifies error return values.
pV1Reserved
Reserved.
pV2Reserved
Reserved.
dnDevNode
Device node for Plug and Play devices.
Comments

This structure is the same as that passed to video-capture drivers when they are opened. This lets a single installable driver function as either an installable compressor or a video-capture device. By examining the fccType member of the ICOPEN structure, the driver can determine its function. For example, an fccType value of ‘vidc’ indicates that it is opened as an installable video compressor.


ICSETSTATUSPROC

typedef struct {
    DWORD   dwFlags;
    LPARAM  lParam;
    LONG (CALLBACK *Status) (LPARAM lParam, UINT message, LONG l);
} ICSETSTATUSPROC;

The ICSETSTATUSPROC structure is used with the ICM_SET_STATUS_PROC message.

Members
dwFlags
Specifies applicable flags. Set this to zero.
lParam
Specifies a constant passed to the status procedure when it is called.
Status
Specifies a pointer to the status function. This is NULL if status messages should not be sent.


Joystick Drivers

The joystick is an input device that provides absolute position information. It is an additional supported input device and not a replacement for the mouse. For this chapter, the term joystick refers to any absolute position device; for example, a light pen, a digitizing tablet, and a touch screen could all use the joystick driver interface.

All joystick function calls are routed through the WINMM module. WINMM loads the joystick driver and passes application requests to it. The joystick driver must handle the standard tasks handled by all installable drivers, as well as the following joystick-specific tasks:

  • Returning the device’s button configuration and movement range

  • Returning position and button-press information

  • Accepting calibration values and adjusting position information accordingly

The joystick driver interface enables a driver to handle one or two devices. Each device can have one to three axes and one to four buttons. The joystick interface accommodates both analog and digital devices.

Note: Providing drivers for non-interrupt, analog joysticks under Windows NT is difficult because the device polling required by such drivers is incompatible with NT’s operation. The use of digital joysticks is recommended.


Writing a Joystick Driver

A joystick driver must include the standard DriverProc entry point function. This function handles the standard and joystick-specific messages sent by WINMM.

For joystick messages, the hDriver parameter contains a handle to the driver and the dwDriverID parameter contains the driver ID created by the driver.


Handling Joystick Driver Errors

The following list summarizes the joystick error codes defined in mmsystem.h:

MMSYSERR_NOERROR
JOYERR_PARMS
JOYERR_NOCANDO
JOYERR_UNPLUGGED


The DRV_OPEN Message

On the DRV_OPEN system message, the lParam2 parameter specifies which device is being opened. This value is JOYSTICKID1 for the first device and JOYSTICKID2 for the second device.

The value that DriverProc returns for the DRV_OPEN message becomes the dwDriverID parameter for all subsequent messages sent to the driver. The driver can use this value for any purpose; for example, to identify a structure containing information about the client.


Joystick-Specific Messages Handled by DriverProc

The DriverProc function for a joystick driver handles the following joystick-specific messages:

JDD_GETDEVCAPS
JDD_GETNUMDEVS
JDD_GETPOS
JDD_GETPOSEX
JDD_SETCALIBRATION


Number of Devices

WINMM sends a JDD_GETNUMDEVS message to the driver to determine how many devices the driver supports. The lParam1 and lParam2 parameters are not used. In response to the JDD_GETNUMDEVS message, return the maximum number of devices supported by the driver.

The driver should return the number of supported devices, which is not necessarily the same as the number of connected devices. This allows a user to connect a device after the driver has been loaded. The joystick interface provides a JOYERR_UNPLUGGED error value that a driver should return when an application polls an unplugged device.


Device Capability Information

The JDD_GETDEVCAPS message requests information about the coordinate range and button configuration of a device. In response to this message, the driver fills in the JOYCAPS structure, which is described in the Win32 SDK.. (A pointer to it is passed to the lParam1 parameter.) The JOYCAPS structure contains information that describes the coordinate range, button configuration, and manufacturer of the device.

The minimum and maximum position values should reflect a set of logical coordinates that is independent of minute differences between copies of a hardware device. For example, users of joystick services should always see a full range of logical coordinates returned from a calibrated joystick. Calibration values should not affect the minimum and maximum values returned by a driver.

If a hardware device does not support a given axis, return zero values for the minimum and maximum coordinate values for that axis. For example, a driver for a two-dimensional device would set the wZmin and wZmax members of the JOYCAPS structure to zero.


Accepting New Calibration Settings

The Joystick application in the Control Panel calculates calibration values for the device. Use the calibration values to convert the actual values returned by the hardware device to the logical values expected by the joystick interface. The driver establishes the logical value range in its response to the JDD_GETDEVCAPS message.

The calibration settings consist of base and delta values for each coordinate. The base value represents the lowest logical value the driver returns; the delta value is the multiplier to use when converting the actual value returned by the device to a logical value appropriate for the established value range.

Immediately after WINMM loads the joystick driver, it reads the calibration values from the registry and sends them to the driver with the JDD_SETCALIBRATION message. WINMM sends the JDD_SETCALIBRATION message with two JOYCALIBRATE structures (pointers to them are passed in the lParam1 and lParam2 parameters). The first structure contains the new calibration settings that the driver should adopt. The driver should fill the second structure with the previous calibration settings.


Calculation of Calibration Values

To calculate the base and delta calibration values, the Joystick application sets the base value to 0 and the delta values to 1. It then polls the joystick driver while the user holds the joystick at each corner of the device coordinate space. This produces the actual value range returned by the device for each coordinate.

After retrieving the coordinate ranges specified by the joyGetDevCaps function, the application uses the following formulas to calculate new base and delta values.

wDelta = (wTargetMax - wTargetMin) / (wActualMax - wActualMin)
wBase = wActualMin * wDelta - wTargetMin

In the following example, the joystick returns x coordinate values in the range 43 to 345, compared to a logical value range of 0 to 65535:

wDelta = (65535-0) / (345-43)
       = 216
wBase  = 43 * 216 - 0
       = 9288


Providing Position and Button-State Information

The JDD_GETPOS and JDD_GETPOSEX messages request device coordinate and button information. The driver fills in JOYINFO or JOYINFOEX structure, described in the Win32 SDK, and passes a pointer to it in the lParam1 parameter.

The coordinate values should be within the range the driver established when responding to the JDD_GETDEVCAPS message. When calculating the coordinate values, use the base- and delta-calibration values passed to the driver with the JDD_SETCALIBRATION message.


Messages, Joystick Drivers

The following topics describe the messages used by Win32-based joystick drivers.


JDD_GETDEVCAPS

The JDD_GETDEVCAPS message is sent to get joystick device capability information.

Parameters

LPARAM lParam1
Specifies a pointer to a JOYCAPS structure, which is described in the Win32 SDK. The driver fills this structure with the capabilities of the device.
LPARAM lParam2
Specifies the size of the structure pointed to by lParam1 in bytes.

Return Value

If JDD_GETDEVCAPS succeeds, it returns MMSYSERR_NOERROR. Otherwise, it returns JOYERR_PARMS.

Comments

Compare the structure size value passed to lParam2 with the size of the JOYCAPS structure with which the driver was compiled. If lParam2 is larger than the expected structure size, fill the remaining with zeros.

If lParam2 is zero, return MMSYSERR_NOERROR without writing anything to the location specified by lParam1.


JDD_GETNUMDEVS

The JDD_GETNUMDEVS message requests the number of devices supported by the joystick driver.

Parameters

LPARAM lParam1
Not used.
LPARAM lParam2
Not used.

Return Value

JDD_GETNUMDEVS returns the maximum number of devices supported by the driver. This value might not match the number of physical devices actually connected to the computer.


JDD_GETPOS

The JDD_GETPOS message is sent to get the current joystick position and button-state information.

Parameters

LPARAM lParam1
Specifies a pointer to a JOYINFO structure, which is described in the Win32 SDK.
LPARAM lParam2
Not used.

Return Value

If the driver successfully retrieves the information, JDD_GETPOS returns MMSYSERR_NOERROR. If the joystick is unplugged, JDD-GETPOS returns JOYERR_UNPLUGGED.

Comments

The coordinate values returned in the JOYINFO structure should be mapped into the coordinate range identified by the driver. The minimum and maximum values for each axis are defined in the JOYCAPS structure, which the driver fills out in response to the JDD_GETDEVCAPS message.


JDD_GETPOSEX

The JDD_GETPOSEX message is sent to get the current joystick position and button-state information.

Parameters

LPARAM lParam1
Specifies a pointer to a JOYINFOEX structure, which is described in the Win32 SDK.
LPARAM lParam2
Not used.

Return Value

If the driver successfully retrieves the information, JDD_GETPOSEX returns MMSYSERR_NOERROR. If the joystick is unplugged, JDD_GETPOSEX returns JOYERR_UNPLUGGED.

Comments

The JDD_GETPOSEX message, along with the JOYINFOEX structure is used to obtain extended information not available with the JDD_GETPOS message and the JOYINFO structure.


JDD_SETCALIBRATION

The JDD_SETCALIBRATION message requests the driver to set calibration information. The driver uses the calibration values to map the physical device coordinates to the logical coordinate range established by the driver.

Parameters

LPARAM lParam1
Specifies a pointer to a JOYCALIBRATE structure containing the new calibration values to adopt.
LPARAM lParam2
Specifies a pointer to a JOYCALIBRATE structure. The driver should fill this structure with the existing calibration values.

Return Value

JDD_SETCALIBRATION returns MMSYSERR_NOERROR.

Comments

The calibration settings consist of base and delta values for each coordinate. The base value represents the lowest logical value the driver returns; the delta value is the multiplier to use when converting the actual value returned by the device to a logical value appropriate for the established value range.


Structures, Joystick Drivers

The following topics describe the structures used by Win32-based joystick drivers.


JOYCALIBRATE

typedef struct joycalibrate_tag {
    WORD wXbase;
    WORD wXdelta;
    WORD wYbase;
    WORD wYdelta;
    WORD wZbase;
    WORD wZdelta;
} JOYCALIBRATE;

The JOYCALIBRATE structure contains calibration values for the three axes of an absolute position device.

Members

wXbase
Specifies a base calibration value for the x axis.
wXdelta
Specifies a delta calibration value for the x axis.
wYbase
Specifies a base calibration value for the y axis.
wYdelta
Specifies a delta calibration value for the y axis.
wZbase
Specifies a base calibration value for the z axis.
wZdelta
Specifies a delta calibration value for the z axis.

Comments

Use the base and delta values returned in this structure to convert actual device values to the logical value range specified by the driver.

The base values represent the lowest logical value that the driver returns for a given axis. The delta values are multipliers the driver should use when mapping the value returned by the device into the value range established by the driver.

The following formula is used to calculate the delta values:

Delta = (LogicalMax - LogicalMin) / (DeviceMax - DeviceMin)

The LogicalMax and LogicalMin members represent the maximum and minimum logical coordinate values for the axis, as defined by the JOYCAPS structure, which is described in the Win32 SDK. DeviceMax and DeviceMin represent the actual values returned by the device.

The following formula is used to calculate the base values:

Base = (DeviceMin * Delta) - LogicalMin

The DeviceMin member represents the minimum value returned by the device, Delta represents the delta value calculated using the first formula, and LogicalMin represents the minimum value returned by the driver (as defined by JOYCAPS).


Chapter 1 General Network Architecture of Windows

Chapter 1 General Network Architecture of Windows NT

This chapter provides a general description of the network architecture of Microsoft® Windows NT® and other Microsoft operating systems that support the Win32 interface as well as kernel-mode network drivers and their operating environment. The following are described:

  • Windows NT Network Architecture and the OSI Model in Section 1.1

  • NT Network Driver Types in Section 1.2

  • Network Interface Card (NIC) Drivers in Section 1.2.1

  • Transport Drivers in Section 1.2.2

  • Windows NT Network Driver Operating Environment in Section 1.3

The Windows NT network components described in this manual include miniport and full NIC drivers that are written to the Network Driver Interface Specification(NDIS), NIC drivers using WAN extensions, protocol drivers and the TDI, Streams under Windows NT, Windows Sockets transport independence, and network component binding.


1.1 Windows NT Network Architecture and the OSI Model

The network architecture is based conceptually on the seven-layer networking model developed by the ISO. Introduced in 1978, the ISO OSI reference model describes networking as a series of protocol layers with a specific set of functions allocated to each layer. Each layer offers specific services to the higher layers, shielding the higher layers from the details of how the services are implemented. There is a well-defined interface between each pair of adjacent layers. The interface defines the services offered by the lower layer to the higher one and how those services will be accessed.

The OSI Reference Model

Network drivers under Windows NT and other Microsoft operating systems that support the Win32 interface fit comfortably within this conceptual framework. Such network drivers implement the protocols and interfaces of the bottom four layers of this network architecture.

The Physical Layer is the lowest layer of the OSI model. This layer is concerned with the reception and transmission of the unstructured raw bit stream over a physical medium. It describes the electrical/optical, mechanical, and functional interfaces to the physical medium. The physical layer carries the signals for all of the higher layers. Under Windows NT, the physical layer is implemented by the NIC, its transceiver, and the medium to which it is attached. For network components that use the serial ports, the physical layer can also include low-level network software that defines how the serial stream of bits is divided into packets of data.

OSI divides the Data Link Layer into two sublayers: LLC and MAC. The LLC sublayer provides error-free transfer of data frames from one node to another. This sublayer is responsible for the establishing and terminating logical links, controlling frame flow, sequencing frames, acknowledging frames, and retransmitting unacknowledged frames. The LLC sublayer uses frame acknowledgement and retransmission to provide virtually error-free transmission over the link to the layers above. The MAC sublayer manages access to the network media, checks frame errors, and address recognition of received frames. Under the Windows NT network architecture, the functions allocated to the Logical Link Control sublayer are implemented in the transport driver, while those allocated to the Media Access Control sublayer are implemented in the network interface card. The NIC is controlled by a software device driver called the NIC driver. Windows NT ships with NIC drivers for a number of popular network interface cards.

The Network Layer controls the operation of the subnet. It decides which physical path the data should take based on network conditions, priority of service, and other factors. This includes routing, traffic control, frame fragmentation and re-assembly, logical to physical address mapping, and usage accounting.

The Transport Layer ensures that messages are delivered error-free, in sequence, and with no loss or duplication. It relieves the higher layer protocols from any concern with the transfer of data between them and their peers. A minimal Transport Layer is required in protocol stacks that include a reliable Network or Logical Link Control sublayer that provides virtual circuit capability. For example, because the Windows NT NetBEUI transport driver includes an OSI-compliant LLC sublayer, its Transport Layer functions are minimal. If the protocol stack does not include an LLC sublayer, and if the Network Layer is unreliable and/or supports datagrams (as with TCP/IP’s IP layer or NWLink’s IPX layer), the Transport Layer should include frame sequencing and acknowledgment, as well as retransmission of unacknowledged frames.

In the Windows NT model, the Logical Link Control, Network and Transport layers are implemented by software drivers known as Transport Drivers (sometimes referred to as protocols, protocol drivers, or protocol modules). Windows NT ships with TCP/IP, IPX/SPX, NetBEUI, and AppleTalk Transport Drivers.


1.2 Types of NDIS Drivers

Windows NT supports three types of network drivers:

  • Network Interface Card or NIC drivers

  • Intermediate protocol drivers

  • Upper level protocol drivers

Network drivers under Windows NT support peer-to-peer and client-server communication between a local node and a remote node on a LAN or WAN. Windows NT uses drivers at one or more network layers to pass data packets and make the protocol translations necessary for error-free communication between nodes.

In some special cases, a single monolithic driver can include the functions of all driver types.

This document describes the design of the Windows NT network drivers. It also describes the NDIS services available to such drivers and how they use these services.

The following figure illustrates Windows NT network driver components.

The types of drivers described include:

  1. NIC drivers ¾ NIC drivers directly manage network interface cards, referred to as NICs. The NIC drivers interface directly to the hardware at their lower edge and at their upper edge present an interface to allow upper layers to send packets on the network, to handle interrupts, to reset the NIC, to halt the NIC and to query and set the operational characteristics of the driver. NIC drivers can be either miniports or legacy full NIC drivers. The WAN miniport is a special type of miniport. A WAN miniport has a few additional and different requirements and gives upper-layer applications remote access to a wide area network using connection-oriented services supplied by WAN NDIS services.

  2. Intermediate protocol drivers ¾ Intermediate protocol drivers interface between an upper-level driver such as a legacy transport driver and a miniport. To the upper-level driver, an intermediate driver looks like a miniport. To a miniport, the intermediate driver looks like a protocol driver. An intermediate protocol driver can layer on top of another intermediate driver although such layering could have a negative effect on system performance. A typical reason for developing an intermediate protocol driver is to perform media translation between an existing legacy transport driver and a miniport that manages a NIC for a new media type unknown to the transport driver. For instance, an intermediate driver could translate from LAN protocol to ATM protocol.

  3. Upper level protocol driver ¾ An upper level protocol driver implements a TDI interface, or possibly another application-specific interface at its upper-edge to provide services to users of the network. Such a driver allocates packets, copies data from the sending application into the packet, and sends these packets to the lower level driver by calling NDIS. At its lower edge, this type of driver provides a protocol interface to receive incoming packets from the next lower level driver. This highest-level driver transfers received data to the destination application.

A WAN miniport can operate in one of two modes. A miniport that operates in pass-through mode implements PPP framing, encryption, and compression at the miniport level rather than in NDIS. A WAN miniport only operates in this mode if its NIC performs the compression, encryption, and PPP framing in its hardware, otherwise these operations are best done by NDIS.

While an intermediate protocol driver presents a miniport driver interface at it upper edge, the list of functions it must provide is different from that of a lowest level miniport. For instance, an intermediate protocol driver does not need to have an ISR routine or a DPC routine. However, it can be thought of as two drivers in one with a private interface between its protocol piece and its miniport piece.


1.2.1 NIC Drivers

The NIC driver provides an interface to the network interface card. A transport driver communicates with a NIC driver through the NDIS interface. For example, when the transport driver has a packet to transmit, it calls the NIC driver by means of the NDIS interface library, and passes down the packet. The NIC driver then forwards the packet to the NIC, instructing it to transmit the packet contents. Similarly, when the network interface card receives appropriately addressed data, the NIC issues a hardware interrupt that is handled by the NIC driver. The NIC driver will call the NDIS interface library to indicate the presence of the received packet to the protocol driver.

There are two types of NDIS NIC drivers:

  • Miniport NIC drivers ¾ Miniport drivers implement the hardware-specific operations necessary to manage a NIC, including sending and receiving data on the NIC. Operations common to all lowest level NIC drivers, such as synchronization, is generally provided by NDIS. Miniports do not call operating system routines directly; their interface to the operating system is NDIS.

  • Full NIC legacy drivers ¾ Full NIC drivers have been written to perform both hardware-specific operations and all the synchronization and queuing operations usually done by NDIS. Full NIC drivers, for instance, maintain their own binding information for indicating received data. A miniport does not keep track of bindings. It merely passes packets up to NDIS and NDIS makes sure that these packets are passed to the correct protocols.

Both types make extensive use of the functions and services offered by the NDIS interface library (the NDIS.SYS export library). Driver writers are encouraged to write the smaller, more easily developed, miniport NIC drivers. Full NIC drivers are supported only for compatibility with previous versions of NT and development of new full NIC drivers is strongly discouraged.

Unlike other types of NT device drivers (video drivers, for example), NIC drivers do not call Windows NT kernel-mode functions directly. Instead, the NDIS library exports a full set of functions that encapsulate all necessary NT kernel operating system functions. In almost all cases, the functions exported by the NDIS library are the only NT kernel-mode functions called by a NIC driver. The NDIS library maintains state information and parameters for both protocol and NIC drivers, including pointers to functions, handles and parameter blocks for linkage, and other system values.

In past documentation, the NDIS library is sometimes referred to as the NDIS wrapper because these functions provide an effective wrapper around the NIC driver.

Windows NT also supports WAN extensions for drivers that manage network interface cards over wide-area links. Windows NT provides support for wide-area links using built-in serial ports where those are available.

Introduced with the initial release of Windows NT, full NIC drivers require the device driver writer to program at a more basic level, dealing with kernel-mode issues of multiprocessor support and processor and thread synchronization. Full NIC drivers require driver developers to write a large amount of code to deal with issues that are common across all NDIS drivers. In previous documentation, full NIC drivers were often referred to as MAC drivers.

Windows NT supports miniport NIC drivers to allow developers to write only the code that is specific to the network hardware, merging the common functions into additional services provided by the NDIS library. Miniport NIC drivers are smaller and faster, requiring much less work to write. Miniport drivers are able to defer handling of many issues to the NDIS library.


1.2.2 Transport Drivers

A transport driver implements a network protocol stack such as IPX/SPX or TCP/IP, offering its services over one or more network interface cards.This transport driver services application-layer clients at its upper edge and connects to one or more NIC driver(s) or intermediate NDIS driver(s) at its lower edge.

Windows NT transport drivers are written to comply with the TDI at their upper edge, and make use of the NDIS interface library (the ndis.sys export library) for communicating with NIC drivers at their lower edge. TDI describes a low-level network interface that is exposed at the upper edge of all Windows NT transport drivers. Requiring that all Windows NT transport drivers expose a single common interface (TDI) simplifies the task of developing transport drivers because only that interface needs to be coded. However, because transport drivers expose only the TDI interface, they can be used only by applications that use TDI. Because TDI is a relatively new network interface, and most existing applications are written to use other existing standard interfaces, such as NetBIOS and Sockets, Windows NT includes emulator modules for two popular existing network interfaces, NetBIOS and Sockets.

Each of these emulator modules exposes its native set of functions, which are accessible through standard call mechanisms in user mode. When called, the emulator module maps the native function and its associated parameters and procedural rules to one or more TDI functions, and then calls the indicated transport driver through TDI.

For increased performance, the TCP/IP and IPX/SPX transport drivers are implemented as native TDI transport drivers. Windows NT provides a Streams environment to enable quick and easy porting of existing Streams-based transport drivers from other environments to this Windows NT native environment.


1.3 Windows NT Network Driver Operating Environment

In Windows NT, the various network software components are linked together, or bound, in a logical hierarchy. For example, each transport driver can be bound to one or several network adapters (through associated NIC drivers). Higher level network components, such as the Windows NT redirector and server, can be bound to one or several transport drivers.

When network components are installed, information is written to the Windows NT configuration registry that describes the order in which the network components should be loaded, and how those network components are to be bound together. The application that manages the network component installation and binding is the Windows NT network control panel application (NCPA).

When the NCPA is used to install a network component, such as a transport or NIC driver, it looks for a special file called an .inf file in the user-specified path. If the component being installed does not ship with Windows NT, the NCPA will locate, load, and run the file oemsetup.inf. A network driver developer must provide such a file, which is a script that performs several component installation functions, including the following:

  • Establish a Manufacturer Name registry key under HKEY_LOCAL_MACHINE\SOFTWARE, create a CurrentVersion\NetRules subkey, and write the component’s binding rules under this subkey.

  • Establish a second registry key for the component under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services, create Parameters and other needed subkeys, and write the component’s configuration information under this subkey.

  • Copy all component related files to the appropriate system directories.

When the inf script completes, NCPA will perform a binding analysis to determine how the various components are to be bound. This analysis uses a combination of binding rules compiled from the system default RawRules value stored in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Ncpa\CurrentVersion\RawRules and the values contained in the NetRules subkeys of all installed network components.

RawRules defines default binding rules that apply before any component-specific NetRules are considered. RawRules includes the following value entries:

  • devClass defines classes that include an NDIS NIC driver (ndisdriver), an NDIS aware transport driver (ndisTransport), a NetBIOS transport driver (netBiosTransport) as a subclass of ndisTransport, and an NT network service (LmNetService), such as the NT redirector or server.

  • bindable indicates that ndisTransport class components can bind downward to ndisDriver class components, and LmNetService class components can bind downward to netBiosTransport class components.

The NetRules define component-specific extensions to the binding rules established by RawRules. These rules are defined by the component developer. NetRules value-data items include:

  • class allows a component to define one or more new component classes.

  • bindable allows a component to define more possible binding combinations.

  • type specifies to which class (or classes) this component belongs. This critical information element enables the NCPA to determine where this component fits into the binding hierarchy.

The NCPA compiles all of the binding rules (RawRules combined with all the NetRules for individual components) and analyzes them to determine how all the network components should be bound together. When this process is complete, the NCPA uses the resulting information to create (or update) the registry Linkage subkey (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
<network component name>\Linkage) of each network component. The NCPA includes the following value-data items to each component’s Linkage subkey:

  • Bind lists the device name of the network components to which this component will bind.

  • Export lists the device names that should be added to the Windows NT object namespace to enable access to this component. This line contains one device name per component to which this component is downwardly bound.

  • Route contains a list of strings where each data string in this value item indicates the exact path through the network component stack represented by the binding.

The NCPA binding engine creates all possible bindings as directed by the binding rules. After the binding process completes and control is returned to the user, the user can use the NCPA Bindings dialog box to view the resulting network component bindings and, if desired, manually disable (and later re-enable) selected bindings. Using the NCPA Bindings dialog to disable a binding causes the associated entry to be removed from the registry Linkage key Bind value of the selected component.

At system boot, the Windows NT Service Controller (loader) uses registry information to load the network components according to the binding hierarchy¾from bottom to top. As each network component is loaded, it is given an opportunity to initialize. During the initialization process, the component can read its registry Linkage key Bind value to discover which lower-level components are available, and bind to those components.

For more information about the Windows NT binding method, see later chapters in this section that discuss network component bindings.

Overview of Driver Binding


Chapter 2 Network Driver Programming Considerations

This chapter describes general issues to consider when writing any network driver for Microsoft® Windows NT®.

Network drivers written for Windows NT and other operating systems that support the Win32 interface share many of the same design goals as Windows NT including:

  • Portable across platforms

  • Scalable to multiprocessor systems

  • Simple configuration of hardware and software

  • Object-based interfaces

  • Support for asynchronous I/O

The following sections explain some of the implications of these design goals for Windows NT network driver writers:

  • Portability in Section 2.1

  • Multiprocessor Support in Section 2.2

  • IRQLs in Section 2.3

  • Synchronization and Notification in Section 2.4

  • Packet Structure in Section 2.5

  • Using Shared Memory in Section 2.6

  • Asynchronous I/O and Completion Functions in Section 2.7


2.1 Portability

NDIS drivers should be written so that they are easily portable across all platforms that support Microsoft Windows NT and other Microsoft operating systems that support the Win32 interface. In general, porting from one hardware platform to another should only require recompilation with a system-compatible compiler.

Driver developers should avoid calling operating system-specific functions since this will make their driver unportable. Instead, the NDIS equivalents of such calls should be used. Restricting support calls to NDIS functions allows the code to be ported between Microsoft operating systems that support NDIS. NDIS exports a rich set of support functions for writing drivers, and it should not be necessary to call the operating system directly.

Drivers should be written in C. Further, driver code should be restricted to the ANSI C standard and should avoid using any language features that are not supported by other system-compatible compilers. Driver code should not be written using any features that the ANSI C standard designates as “implementation defined”.

Drivers should avoid any dependencies on data types whose size and layout vary across platforms. Driver code should not call any C runtime library functions instead of NDIS-provided functions.

No floating point operations are allowed in kernel-mode. It is a fatal error to attempt such operations.

If a driver contains code to support platform-specific features, such code should be encapsulated between #ifdef and #endif statements.


2.2 Multiprocessor Support

Writing code to safely run on machines with more than one, concurrently executing processors is an essential part of writing a portable Windows NT driver. A network driver must be multiprocessor-safe, as well as use the provided NDIS library functions.

A major advantage of miniport NIC drivers over the legacy, full-NIC drivers is that the NDIS library functions for miniports handle many of these multiprocessor considerations. The NDIS library queues requests so the miniport NIC driver need not contain code for this. In contrast, the driver developer of a full-NIC driver must have the necessary functions and protections to make the network driver multiprocessor-safe.

In a uniprocessor environment, a single processor runs only one machine instruction at a time, even though it is possible for a NIC or other device to interrupt the current execution stream when packets arrive or as timer interrupts occur. Typically, when manipulating data structures such as packet queues, a driver disables interrupts on the NIC, performs the manipulation, and then re-enables interrupts. Many threads in a uniprocessor environment appear to run simultaneously but actually run in interleaved time slices.

In a multiprocessor environment, processors simultaneously run several machine instructions. A driver must synchronize so that when one driver function manipulates common data structures, the same or another driver function on another processor does not attempt to modify shared data at the same time. All driver code is re-entrant in an SMP machine. To eliminate this resource protection problem, Windows NT device drivers use spin locks.


2.3 IRQLs

Every driver function called by NDIS runs at a system-determined IRQL, one of PASSIVE_LEVEL < DISPATCH_LEVEL < DIRQL. For instance, the initialization function, halt function, reset function, and sometimes the shutdown function run at PASSIVE_LEVEL. Interrupt code runs at DIRQL. All other NDIS driver functions run at DISPATCH_LEVEL.

The IRQL at which a driver runs affects which NDIS functions it can call. Certain functions can only be called at IRQL PASSIVE_LEVEL. Others can be called at DISPATCH_LEVEL or lower. A driver writer should check every NDIS function for IRQL restrictions.

Any driver function that shares resources with the driver's ISR must be able to raise its IRQL to DIRQL to prevent race conditions. NDIS provides such a mechanism.


2.4 Synchronization And Notification

Synchronization is necessary whenever two threads of execution share resources that can be accessed at the same time either in a uniprocessor machine or on an SMP machine. For instance, on a uniprocessor machine, if one driver function is accessing a shared resource, and is interrupted by another function that runs at a higher IRQL, such as an ISR, the shared resource must be protected to prevent race conditions that leave the resource in an indeterminate state. On an SMP machine, two threads could be running simultaneously on different processors and attempting to modify the same data. Such accesses must be synchronized.

NDIS provides spin locks that can be used to synchronize access to shared resources between threads that execute at the same IRQL. When two threads that share a resource execute at different IRQLs, NDIS provides a mechanism for temporarily raising the IRQL of the lower IRQL code so that access to the shared resource can be serialized.

Notification is necessary when a thread is dependent on the occurrence of an event outside of the thread. For instance, a driver might need to be notified when some time period has passed so that it can check its device. Or a NIC driver might have to perform a periodic operation such as polling. Timers provide such a mechanism.

Events provide a mechanism that two threads of execution can use to synchronize operations. For instance, a miniport NIC driver may want to test the interrupt on its NIC by writing to the device. It must wait for an interrupt to notify the driver that the operation was successful. Events can be used to synchronize an operation between the thread waiting for the interrupt to complete and the thread that handles the interrupt.

A description of these NDIS mechanisms follows.

Spin Locks

A spin lock provides a synchronization mechanism for protecting resources shared by kernel-mode threads running at IRQL > PASSIVE_LEVEL in either a uniprocessor or a multiprocessor machine. A spin lock handles synchronization among various threads of execution running concurrently on an SMP machine. A thread acquires a spin lock before accessing protected resources. The spin lock keeps any thread but the one holding the spin lock from using the resource. A thread that is waiting on the spin lock loops attempting to acquire the spin lock until it is released by the thread that holds the lock.

Another characteristic of spin locks is the associated IRQL. Attempted acquisition of a spin lock temporarily raises the IRQL of the requesting thread to the IRQL associated with the spin lock. This prevents all lower IRQL threads on the same processor from preempting the executing thread. Threads running at a higher IRQL can preempt the executing thread, but these threads cannot acquire the spin lock because it has a lower IRQL than they do. Therefore, no other threads on the same processor attempt to acquire the spin lock until it has been both acquired and released by the executing thread. A well-written network driver will minimize the amount of time a spin lock is held.

A typical use for a spin lock is to protect a queue. For example, the miniport send function, MiniportSend, might queue packets passed to it by a protocol driver. Because other driver functions also use this queue, MiniportSend must protect the queue with a spin lock so that only one thread at a time can manipulate the links or contents. MiniportSend acquires the spin lock, adds the packet to the queue and then releases the spin lock. Using a spin lock ensures that the thread holding the lock is the only thread modifying the queue links while the packet is safely added to the queue. When the NIC driver takes the packets off the queue, such an access is protected by the same spin lock. When executing instructions that modify the head of the queue or any of the link fields making up the queue, the driver must protect the queue with a spin lock.

A driver must take care not to overprotect a queue. For example, the driver can perform some operations (for example, filling in a field containing the length) in the network driver-reserved field of a packet before it queues the packet. The driver can do this outside the region protected by the spin lock, but must do it before queuing the packet. Once the packet is on the queue and the executing thread releases the spin lock, the driver must assume that other threads can dequeue the packet immediately.

Avoiding Deadlock Problems

A problem to be avoided when using spin locks is deadlock. Windows NT does not restrict a network driver from simultaneously holding more than one spin lock. However, if one section of the driver attempts to acquire Spin Lock A while holding Spin Lock B, and another section attempts to acquire Spin Lock B while holding Spin Lock A, deadlock results. If it acquires more than one spin locks, a driver should avoid deadlock by enforcing an order of acquisition. That is, if a driver enforces acquiring Spin Lock A before Spin Lock B, the situation described above will not occur.

Using spin locks impacts performance and, in general, a driver should not use many spin locks. Occasionally, functions that are usually distinct (for example, send and receive functions) have minor overlaps for which two spin locks can be used. Use of more than one spin lock might be a worthwhile tradeoff in order to allow the two functions to operate independently on separate processors.

Timers

Timers are used for polling or timing out operations. A driver creates a timer and associates a function with the timer. The associated function is called when the period specified in the timer expires. Timers can be one-shot or periodic. Once a periodic timer is set, it will continue to fire at the expiration of every period until explicitly cleared. A one-shot timer must be reset each time it fires.

Timers are created and initialized by calling NdisMInitializeTimer, and set by calling NdisMSetTimer or for a periodic timer, by calling NdisMSetPeriodicTimer. If a nonperiodic timer is used, it must reset by calling NdisMSetTimer. A timer is cleared by calling NdisMCancelTimer.

Events

Events are used to synchronize operations between two threads of execution. An event is allocated by a driver and initialized by calling NdisInitializeEvent. A thread running at IRQL PASSIVE_LEVEL calls NdisWaitEvent to put itself into a wait state. When a driver thread waits on an event, it specifies a maximum time to wait as well as the event to be waited on. The thread’s wait is satisfied when NdisSetEvent is called causing the event to be signaled, or when the specified maximum wait-time interval expires, whichever occurs first.

Typically, the event is set by a cooperating thread that calls NdisSetEvent. Events are unsignaled when they are created and must be set in order to signal waiting threads. Events remain signaled until NdisResetEvent is called.


2.5 Packet Structure

NDIS packets are allocated by a protocol driver, filled with data, and passed to the next lower NDIS driver so that the data can be sent on the network. Some lowest level NIC drivers allocate packets to hold received data and pass the packet up to interested higher-layer drivers. Sometimes, a protocol driver allocates a packet and passes it to a NIC driver with a request that the NIC driver copy received data into the provided packet. NDIS provides functions for allocating and manipulating the substructures that make up a packet.

A packet is composed of the following:

  • A packet descriptor that contains private areas for the miniport NIC driver and a protocol driver, a set of flags associated with the packet and whose meaning is defined by a cooperating miniport(s) and protocol driver(s), the number of physical pages that contain the packet, the total length of the packet, and a pointer to the first buffer descriptor that maps the first buffer in the packet.

  • A set of buffer descriptors. A buffer descriptor describes the starting virtual address of each buffer, the buffer’s byte offset into the page pointed to by the virtual address, the total number of bytes in the buffer and a pointer to the next buffer descriptor, if any.

  • The virtual range, possibly spanning more than one page, that makes up the buffer described by the buffer descriptor. These virtual pages map to physical memory.

A busmaster NIC driver that allocates shared memory for receiving incoming packets or a protocol driver that allocates memory for sending a packet must insure that any buffer used to contain incoming or outgoing data is cache-aligned. This is necessary so that the miniport can flush the buffer to assure coherency before sending a packet and to flush a received buffer before indicating the data to an upper layer.


2.6 Using Shared Memory

Miniport NIC drivers for busmaster DMA devices must allocate shared memory for use by the NIC and the NIC driver. Special precautions are necessary when sharing cached memory between such a driver and its NIC. On certain architectures, specific steps must be taken to insure that the memory is coherent because the NIC can access the shared physical memory directly, while the NIC driver accesses memory through the cache. This can cause differences in what the driver and the NIC detect in memory, even if they look at the same location.

The use of noncached shared memory avoids most of the problems associated with cached shared memory. However, noncached memory is a scarce system resource and allocation of such memory can be limited. When data written to physical memory must use a small write queue, even noncached data might not appear in physical memory immediately. Additionally, there are cases when using noncached memory is neither recommended nor possible. For example, data that the NIC driver reads repeatedly should always be cached to improve performance. Or, as another example, a NIC driver might be required to pass a received data packet to a number of different protocol drivers. Cached memory would be highly recommended in these cases. The NIC driver should also use cached memory for any data passed in by a protocol driver.

NdisMAllocateSharedMemory can be called by a busmaster NIC driver to allocate memory for permanent sharing between the network adapter and the NIC driver. This function returns a virtual address and a physical address for the shared memory. The addresses are valid until a call to NdisMFreeSharedMemory frees the memory.

When shared cached memory is used, NDIS provides a function that must be called by the NIC driver to insure coherency between what the NIC sees and what the NIC driver sees. Before accessing the shared memory on a send or on a receive, the NIC driver must call NdisFlushBuffer and also NdisMUpdateSharedMemory to ensure cache coherence.


2.7 Asynchronous I/O and Completion Functions

Because of the latency inherent in some network operations, many of the upper-edge functions provided by a NIC driver and the lower-edge functions of a protocol driver are designed to support asynchronous operation. Rather than wasting CPU cycles waiting in a loop for some time-consuming task to finish or a hardware event to signal, network drivers rely on the ability to handle most operations asynchronously.

Asynchronous network I/O is supported by using a completion function. The following example illustrates using a completion function for a network send operation, but this same mechanism exists for many other operations performed by a protocol or NIC driver.

When a protocol driver calls NDIS to send a packet, resulting in a call to the NIC driver’s MiniportSend function, the NIC driver can try to complete this request immediately and return an appropriate status value as a result. For synchronous operation, the possible responses are NDIS_STATUS_SUCCESS for successful completion of the send, NDIS_STATUS_RESOURCES, and NDIS_STATUS_FAILURE indicating a failure of some kind.

But a send operation can take some time to complete while the NIC driver (or NDIS) queues the packet and waits for the NIC to indicate the result of the send operation. The NIC driver MiniportSend function can handle this operation asynchronously by returning a status value of NDIS_STATUS_PENDING. When the NIC driver completes the send operation, it calls the completion function, NdisMSendComplete, passing a pointer to the packet descriptor that was sent. This information is passed to the protocol driver, signaling completion.

Most driver operations that can require an extended time to complete support asynchronous operation with a similar completion function. Such functions have names of the form NdisM Xxx Complete. Along with the obvious send and receive functions, completion functions are available for setting and querying configuration, resetting hardware, indicating status, indicating received data, and transferring received data.


Chapter 3 Installation and Binding

Every NDIS driver, whether a NIC driver, an intermediate driver or a protocol driver must provide a setup file, sometimes called an .inf script, containing information about how that driver is loaded and about its relationships to other network drivers. During system setup, the network is configured on the machine, and the setup file for each network component writes information into the registry that describes how this component fits within the set of installed network drivers that are bound into network stack(s). This registry information is read by NDIS and the network drivers during driver initialization.

After initial system setup, whenever the network control panel application (NCPA) is run to reconfigure the network, for example to install a new driver, a new binding analysis is carried out, and NCPA writes modified registry contents that reflect the new configuration and the new binding relationships.

This describes what binding means in the context of NDIS driver installation and how binding information is established during setup and configuration, as well as what binding rules must be put into the registry for various network components.


3.1 Bindings And Dependencies

Relationships between network components can be expressed as dependencies and as bindings. A network component has binding relationships with other network components and dependencies on other network components or groups of components, as follows:

  • A dependency expresses a relationship between components that determines the order in which those components are loaded by the Service Controller.

  • A binding relationship has meaning for NDIS and network components but not for the Service Controller. A binding relationship exists if a network component can open or initialize a next lower component to form a path for transmitting network data from a local network client onto the network media.

Binding relationships are determined by a system software component called the binding engine. When the binding engine runs, it generates binding information for all network components in the registry. In addition, the binding engine generates dependencies for each of these components based on the binding information each component's .inf script wrote into the registry. The binding engine writes these dependencies into the registry, and later the Service Controller reads them to determine the load order of dependent components.

If a network component has a dependency that cannot be derived from the binding information, the network component's .inf script must write the dependency at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
\Services\NameOfComponent\Linkage as the value for OtherDependencies. Such a value will be added to a component’s dependency information by the binding engine.

If a network component does not have a …\NetRules entry in the registry, the binding engine does not generate any bindings or derived dependencies. Such a component must write its dependencies directly to HKEY_LOCAL_MACHINE\SERVICES\CurrentControlSet\Services
\NameOfComponent\ DependOnService.


3.2 Binding Overview

At system installation, the process of installing and properly binding together network components proceeds in several steps:

  1. When setup runs, it selects and runs the .inf files for the drivers to be installed. The setup file for each network driver must create the following registry keys into which binding information is written:

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
    \NameOfComponent\Linkage

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
    \NameOfComponent\Parameters

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft
    \NameOfComponent\CurrentVersion\NetRules

    Such a setup file must write the net rules for its component to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NameOfComponent
    \CurrentVersion\NetRules. If there are other dependencies, these are written to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
    \Services\    NameOfComponent\Linkage as the value of the OtherDependencies entry.

    If the setup file is for a NIC driver or for an NDIS intermediate driver that exports a virtual NIC, it must write the medium type of the NIC(s) it exports to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
    \Services\    NameOfComponent\Parameters as the value of MediaType. MediaType is read by transports and intermediate drivers to eliminate bindings to NICs of media type(s) not supported by such drivers. Valid media types are defined in ntddndis.h and in the registry as the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
    \NDIS\MediaTypes     value entry. For more information about setting up experimental medium types, see also Section 3.4.8.

  2. Next, the binding engine runs and performs an analysis of the relationship between the network components installed in the system. The binding engine reads the values of the NetRules entries from the registry and writes them to its information database along with rules imposed by the system, called the built-in rules, and the values of the OtherDependencies entries, if any, stored in the registry. The binding engine does its analysis based on this registry information and writes the result to the registry for each network component under its HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
    \    NetworkComponentName\Linkage key as the values of the Bind, Export, and Route entries.

  3. When the Service Controller runs, it loads network drivers based on the dependencies found in the registry. For instance, NetBT has a dependency on TCPIP that is stored in the OtherDependencies entry of its …\Linkage key.

  4. NDIS and network components read the binding information in the registry during driver initialization. NDIS uses binding information to instruct some components, such as NIC drivers, to initialize the underlying adapters. NDIS reads this binding information in the registry to discover what adapters are managed by each NIC driver and calls each such NIC driver at its MiniportInitialize function once for each such adapter NDIS discovers in the registry.

    If it does not register a ProtocolBindAdapter function with NDIS, a transport protocol driver or an intermediate driver either reads the registry to discover what underlying adapter(s) it can bind to, or the driver can be statically bound to an underlying NIC and, as a result, always "know" to what adapter name it will bind itself. Otherwise, a transport's or intermediate driver's ProtocolBindAdapter function will be called when NDIS discovers the NIC(s) to which the transport or intermediate driver can bind itself are up and available for I/O operations.

Any network component can also specify that its bindings must be reviewed after the binding analysis is complete. To request a second execution of a component’s setup file, the component's .inf script writes a value of one at the Review entry of the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NameOfComponent
\CurrentVersion registry key. During its binding-review processing, the .inf reads the results of the binding operation that was stored in the registry and adds to or modifies the information. See Section 3.5 for a more complete discussion of a binding review.

After initial system setup, the NCPA can be run to modify the network configuration. For instance, the user can run NCPA to add or delete a network card and its driver. If this occurs, the binding engine is run again and any ensuing new binding results are written into the registry. Whenever NCPA is run to carry out a requested operation, the .inf scripts for all the network components also will be run, just as at system setup time, including all requested review passes.


3.3 Registry Contents After Binding Analysis

As already mentioned in the preceding section, the binding engine writes the results for each component of the previously described steps into the registry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
\NameOfComponent\Linkage key. The following three value entries are written to this key.

Bind
Specifies a list of the device name(s) of the component(s) to which the binding engine has determined NameOfComponent can bind.

For a NIC driver, this list contains the name of one or more underlying network adapters it can manage.

For an intermediate driver, this list includes the NICs exported by all the NIC drivers for all network adapters on the machine. Because the binding engine includes the names of all NICs exported by any network driver, this list will include the name exported by the NDIS intermediate driver’s upper-edge miniport component. See Section 3.5 for a discussion of the review process in which this binding is deleted from the registry.

For a transport protocol driver, this is a list of the name(s) of all exported underlying NICs or virtual NICs to which it could possibly bind. This list can include devices that are incompatible with the transport’s operation, usually because the NIC implements a medium the transport does not support. See Section 3.5 for a discussion of the review process and the deletion of such devices from the Bind list. See also Section 3.4.8 for information about defining experimental media types.

For a service, this list includes the name(s) of one or more device(s) exported by transport protocol(s) to which the service can bind itself.

Export
Specifies a list of the device name(s) that an overlying component can use to bind itself to NameOfComponent.

Usually these device names are formed by concatenating the name of the exporting component with the Export entries of underlying components with which the exporting component can bind. However, a component can export a single, unconcatenated pseudo-device name to which still higher level components bind, but operate by multiplexing over several underlying devices. See Section 3.4.4 for more information about the BindForm net rule.

Route
Specifies the path through the network stack from NameOfComponent to the underlying NIC, as a concatenated list of exported device names configured beneath NameOfComponent.


3.4 Minimum Binding Requirements

The developer of a network driver must at a minimum ensure that the following required net rules, and possibly some optional rules, are written into the registry by the driver’s setup file.

When system setup runs a network driver’s .inf script, this file writes that driver's net rules as value entries in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NameOfComponent
\CurrentVersion\NetRules. The following list summarizes the predefined names of possible value entries in the NetRules key and whether each is required or optional:

  • type - required

  • use - optional

    A use value is optional in the sense that no error messages will be generated if it is not defined in a component’s .inf file. However, if it is undefined, NCPA assumes that the use value for this component is defined as a Service. Therefore, as a practical matter, a use value entry is required for every transport, intermediate driver, and NIC driver.

  • bindform - required

  • class - optional

    This entry is not required unless the driver is defining a subclass of either of the predefined classes, ndisDriver or Transport.

  • bindable - optional

    This entry is used to limit the underlying devices to which a particular component will bind itself. NIC drivers provide a bindable value in the driver .inf file to specify that they bind only to specific types of adapters.

    Transport protocols generally do not provide a bindable value.

The registry must also contain the following net rules for each adapter in the system:

  • type

  • bindform

  • class

The setup file for any driver that manages an adapter writes the net rules for that adapter into the registry HKEY_LOCAL\SOFTWARE\Microsoft\WindowsNT
\CurrentVersion\NetworkCards\CardNumber\NetRules key. It is unnecessary to write a use net rule value for an adapter. Because the net rules for an adapter are written under the preceding key, adapter is assumed to be the value of use.

Information about the syntax for each of the preceding value entries follows.


3.4.1 Net Rules Syntax

The binding engine reads the value entries in the NetRules key of the registry for each network component, converts the specified rules from external form to an internal form, and then executes the binding algorithm. The binding engine operates on the network component’s own net rules, on the system-defined built-in rules, and on any specified OtherDependencies. Rules specified for one network component in the registry can affect any other network component.

All the value entries presented subsequently are of the registry types REG_SZ, REG_MULTI_SZ, or REG_DWORD:

  • Entries of type REG_SZ can have only a single value in a given component.

  • Entries of type REG_MULTI_SZ can have as many values as necessary, presented as a list with list items separated by spaces.

  • Entries of type REG_DWORD can have a single ULONG integer value.

All user-defined names provided by a network component in its .inf file must conform to the following conventions:

  • A name cannot begin with an uppercase letter, a number, or an underscore character.

  • Characters following the first character in a name use standard C-language variable name limitations (for example, no $ or %, no characters greater than 0x7F, no characters less than A).

  • Strings in quotation marks are invalid.

  • Names cannot be longer than 30 characters.

  • Names must be unique.

    If name collisions occur, the binding algorithm of the NCPA generates unpredictable results.

The preceding restrictions do not apply to object names. Such a value is specified as a string in double quotation marks.

The following sections define the syntax of the required and optional net rules value entries, mentioned already in Section 3.4, as written to the registry at ..\SOFTWARE\Microsoft\NameOfComponent\CurrentVersion
\NetRules.


3.4.2 Type Net Rule

The syntax for the type entry is defined as follows:

type: REG_SZ : ProductName Class {LowerClass}

ProductName
Specifies a name, assigned by the developer, that meets the requirements described in the preceding section, including the requirement that this name must be unique across all other components.
Class
Indicates the component class to which this component belongs. A component class can be any of the predefined Adapter, Driver, Transport, or Service, or it can be a newly defined class. A Class specification always concatenates one of these predefined values. For example, ndisDriver and netBiosTransport are valid Class specifications used by the system-supplied network drivers.
LowerClass
This optional specification is used to indicate the behavior of the lower-edge of the component. If it is omitted, the Class name is taken to indicate both the upper-edge and lower-edge behavior.

Examples of the Type Net Rule

The following examples show how type is specified in the registry for certain system network drivers under their respective ..\NameOfComponent\NetRules keys:

  1. A NIC driver for an elnkii adapter has the entry
    type : REG_SZ : elnkiiSys ndisDriver elnkiiDriver

    In the preceding type specification, elnkiiSys is the name of the driver. The Class value ndisDriver indicates both that the elnkii NIC driver is part of the generic class Driver and that it is an NDIS-compliant driver. The LowerClass value elnkiiDriver is a developer-assigned name that describes the lower-edge behavior of this driver; that is, this driver manages a NIC whose adapter type is elnkii.

  2. A NIC driver for an ee16 adapter has the entry
    type : REG_SZ : ee16Sys ndisDriver ee16Driver

    The preceding type specification is semantically identical to the elnkii driver's, except for the ee16-specific values.

  3. A NetBIOS Nbf transport has the entry
    type : REG_SZ : nbf netBiosTransport rasCapableTransport

    The preceding type specification indicates that the product name is nbf. The Class value netBiosTransport indicates this component is part of the generic class Transport and that it handles NetBIOS-type addresses. The LowerClass value rasCapableTransport indicates that higher-level components can use this transport to support RAS operations.


3.4.3 Use Net Rule

The syntax for the use entry is defined as follows:

use: REG_SZ : ComponentRole {yes|no|none} {yes|no|none}

ComponentRole
Can be one of service, transport, driver, or adapter.

A NIC driver identifies its use as driver.

yes|no|none
Optionally specifies whether a driver group name is used in place of specific driver dependencies, as one of the following values:
yes
Specifies that this driver’s load order is dependent on the ServiceLoadOrder of the component to which it binds.
no
Specifies that this driver component is dependent specifically on underlying named drivers to which it binds. This is the assumed value if nothing is specified.
none
Specifies that no dependencies should be generated for this driver.
yes|no|none
Optionally specifies whether transport group names are used in place of specific transport dependencies, as one of the following values:
yes
Specifies that this transport protocol’s load order is dependent on the ServiceLoadOrder of the component to which it binds.
no
Specifies this transport driver component is dependent specifically on underlying named components to which it binds. This is the assumed value if nothing is specified.
none
Specifies that no dependencies should be generated for this transport protocol.

NIC drivers typically specify only driver as the value of their respective use net rules.

Examples of the Use Net Rule

The following examples show how use is specified in the registry for certain system network drivers, also shown in the preceding type rule examples, under their respective ..\NameOfComponent\NetRules keys:

  1. The NIC driver for the elnkii adapter has the entry
    use : REG_SZ : driver

  2. The NIC driver for the ee16 adapter also has the entry
    use : REG_SZ : driver

  3. The nbf transport has the entry
    use : REG_SZ : transport none none


3.4.4 BindForm Net Rule

The syntax for the bindform entry is defined as follows:

bindform : REG_SZ: ObjectName [yes|no] [yes|no] [container|simple|streams]

ObjectName
Specifies the name of the object in the Windows NT namespace. This name must be unique so that it can be exported.
yes|no
The initial yes XOR no specifies whether the binding information will appear in this component's Linkage key of the registry. A value must be explicitly specified.

The subsequent yes XOR no specifies whether the device name appears in binding strings generated by the binding engine. A value must be explicitly specified. NIC drivers always select no because NIC driver names never appear as the …\Linkage\Export value for such a driver. Instead, a NIC driver exports the name(s) of the NIC(s) that the driver controls as described later in Section 3.5.

container|simple|streams
Specifies how binding string names are formed for the …\Linkage\Export and …\Linkage\Bind entries. A value must be explicitly specified, as follows:
container
Indicates that component-supplied values in the Linkage key are expressed as \Device\ComponentX\ComponentY where the components are delimited by backslashes. If this option is selected, the Object Manager must be called to create container objects of this name format.
simple
Indicates that component-supplied values in the Linkage key are expressed as \Device\ComponentX_ComponentY where the components are delimited by underscores. If this option is selected, the Object Manager is not called to create names.
streams
Indicates that the word Streams appears in the binding string immediately ahead of the component that selected this option.

Examples of Bindform Net Rule

The following examples show how bindform is specified in the registry for certain system network drivers, some also shown in the preceding use rule examples, under their respective ..\NameOfComponent\NetRules keys:

  1. The NIC driver for the ee16 adapter has the entry
    bindform : REG_SZ: “Ee16Sys” yes no container

  2. The Nbf transport has the entry
    bindform : REG_SZ : “Nbf” yes yes simple

  3. The TCP/IP transport has the entry
    bindform : REG_SZ : “Tcpip” yes yes container


3.4.5 Class Net Rule

The syntax for the class entry is defined as follows:

class: REG_MULTI_SZ : ClassName....[[ExistingClassName|basic]....{no|yes}] ….

ClassName
Specifies the class to which this driver belongs. There is only one ClassName per class net rule. There must be at least one and can be more than one set of the following values specified after ClassName.
ExistingClassName
Optionally specifies a parent class of which the given ClassName is a subclass.

Sometimes, a transport supplies an ExistingClassName to declare itself part of a known subset of the parent class. Usually, such a subclass has some special capability that is not a feature of the whole parent class of drivers. For instance, Microsoft’s Nbf transport declares its ClassName as rasCapableTransport and its ExistingClassName (parent class) as netBiosTransport to indicate that it is a NetBIOS transport that also has RAS capabilities.

basic
Specifies that this component is a primitive class, rather than a subclass of a larger parent class.

NIC drivers always select basic for the second value in the class specification.

Transport drivers select basic if the driver is not a subclass of a parent class.

no|yes
Specifies yes if this component is a logical endpoint that exports only a single name, even if it is layered above several network components. The assumed value is no if nothing is specified.

Network drivers usually allow this option to default to no unless the driver is designed to multiplex over several underlying devices and the driver exports a single device name to still higher level network components. For instance, a transport protocol might multiplex over several underlying NICs but only export a single device name. A driver that controls a family of NICs might do the same thing.

Examples of Class Net Rule

The following examples show how class is specified in the registry for certain system network drivers, also shown in the preceding bindform rule examples, under their respective ..\NameOfComponent\NetRules keys:

  1. The NIC driver for the ee16 adapter has the entry
    class : REG_MULTI_SZ : ee16Driver basic

  2. The Nbf transport has the entry
    class : REG_MULTI_SZ : rasCapableTransport netBiosTransport

  3. The TCP/IP transport has the entry
    class : REG_MULTI_SZ : tcpipTransport basic tcpipService basic yes


3.4.6 Bindable Net Rule

The syntax for a bindable entry is defined as follows:

bindable: REG_MULTI_SZ : [FromClass ToClass [non|exclusive] Value [non|exclusive] Value]….

FromClass
Matches the ClassName specification in the class net rule for this component. FromClass binds to ToClass.
ToClass
Specifies the ClassName of another component on which this component depends.

The ToClass value must be the ClassName specification of another component under that component's class net rule.

non|exclusive
Specifies the (in)dependencies between the FromClass and ToClass components. One of these values must be specified for each FromClass and ToClass in the list, as follows:
  • The initial non XOR exclusive specifies whether the component designated by FromClass can bind itself to other components than the given ToClass.

  • The subsequent non XOR exclusive specifies whether the component designated by ToClass can be bound to by components other than FromClass.

A NIC driver specifies one or more bindable net rules, each with a FromClass that is the driver name, such as xxxDriver, and a ToClass the name of an adapter the driver controls, such as xxx1Adapter, xxx2Adapter, and so forth. Usually, a NIC driver specifies non for its FromClass, because NIC drivers typically can bind to (in other words, control) more than one model of adapter. The NIC driver typically specifies exclusive for ToClass, because an adapter can be bound to only by one NIC driver at a time.

The bindable entry for a transport protocol driver is more complex because it is bound to by one or more services and because the transport binds itself to one or more underlying NDIS drivers. A transport typically specifies non for the FromClass (transport) if it can bind to other NIC drivers and non for the underlying NIC driver if the NIC can be bound to by other transports. Otherwise, a transport specifies exclusive for the FromClass if that transport can bind itself only to a specific NIC driver designated by the ToClass, and exclusive for the ToClass if the underlying NIC is a transport-dedicated device. Such a pair of closely coupled, interdependent drivers are very rare.

Value
Indicates the relative importance of the binding, effectively specifying whether this binding should be chosen or dropped if there is another competing binding of lesser or greater value. A Value in the range 1 to 100 can be used to weight the choice of a binding. A higher value indicates a higher importance is assigned to a particular binding.

A value other than 100 is occasionally used to force an exclusive binding between components to form a monolithic network stack. A NIC driver, intermediate, or transport driver typically specifies Value as 100. With this default, the various bindings expressed in the registry are used without any relative weighting among them.

Examples of Bindable Net Rule

The following examples show how bindable is specified in the registry for certain system network drivers, also shown in the preceding bindform rule examples, under their respective ..\NameOfComponent\NetRules keys:

  1. The NIC driver for the ee16 adapter has the entry
    bindable : REG_MULTI_SZ : ee16Driver ee16Adapter non exclusive 100

    This entry specifies that the ee16 driver can bind to an ee16 adapter but possibly also to other adapters. The ee16 adapter, however, is limited to binding to the ee16 driver.

  2. The Nbf transport has no bindable entry in the registry. This transport does not attempt to limit the devices it binds to, nor the services that can bind to it.

  3. The TCP/IP transport has the entry
    bindable : REG_MULTI_SZ : tcpipService tcpipTransport non exclusive 100 tcpipTransport ndisDriver non non 100

    This rule specifies that the higher level tcpService can possibly bind itself to other transport drivers (non), but the tcpipTransport driver can only be bound to by the tcpService (exclusive). This rule also specifies the interdependency between the TCP/IP transport and lower network drivers. The tcpipTransport driver can bind itself to any (or all) lower network component(s) in the class ndisDriver, but this transport potentially can bind itself to other underlying types of drivers (non) as well. As expected, any driver in the class ndisDriver can be bound to by more transports (non) than just the tcpipTransport.


3.4.7 Net Rules for NICs

NIC drivers also must add entries to the registry that specify the net rules for the adapter type that the NIC driver exports. For instance, a NIC driver that manages an elnkii adapter must specify the elnkii NIC’s net rules. For any NIC, the only required net rule entries are:

  • type

  • class

  • bindform

These entries are stored in the registry in the NIC-specific HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT
\CurrentVersion\NetworkCards\CardNumber\NetRules key.

The required net rules entries for an adapter appear in the registry as follows:

type
Specifies an adapter name, followed by a specification of its type.
Typically an adapter name is formed from the adapter type, concatenated with the word Adapter to look like the following:
type : REG_SZ : elnkii elnkiiAdapter
class
Specifies the class to which the NIC belongs as a name that contains the predefined Adapter, usually followed by basic indicating this is a primitive class. However, a parent class can be specified at the discretion of the driver writer, particularly for a driver that handles any one of several models of related adapters.
A typical class net rule in the registry for a NIC looks like the following:
class : REG_SZ : elnkiiAdapter basic
bindform
Specifies the ObjectName of the NIC as visible in the Windows NT object namespace under the \Device node, whether generated binding information should appear under the ..\Linkage key in the registry (usually yes), whether the name of the NIC should be concatenated into binding strings (usually yes), followed by container.
For NICs, container is always chosen for the fourth field because the name of the NIC is delimited by slashes in binding strings. A typical bindform entry in the registry for a NIC looks like the following:
bindform : REG_SZ : "EE161" yes yes container

There is no bindable net rule specified in this registry key for an adapter because every adapter, as the lowest level component in a binding stack, is bound to by other network components. An adapter does not bind itself to any network component.

No use net rule for an adapter is necessary. The fact that a NIC's net rules appear at ..\NetworkCards\CardNumber\NetRules implies that the ComponentRole is always adapter.


3.4.8 Experimental Media Types

A current list of the system-defined NdisMedium Xxx types and their corresponding values appears in the registry under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
\NDIS\MediaTypes.

To add an experimental medium type and associated identifier for an under-development NIC driver, use the system registry editor, as follows:

  1. Select the Edit menu Add Value option.

  2. Enter a Value Name of the form NdisMedium MyNewMedium, where MyNewMedium is anything. Select the Data Type REG_DWORD. Click on OK.

  3. Enter a hexadecimal value for the new medium in the range 0xffff0000 through 0xffffffff.

Defining an experimental medium type in this manner allows the NIC driver to return NdisMedium MyNewMedium temporarily in response to queries when the miniport initializes.

When the miniport for the new medium type is done, contact Microsoft to formally register the new medium type as one of the standard system-supported media type constants.


3.5 Binding Review

When a network component’s .inf script runs and it sets one as the value of the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft
\NameOfComponent\CurrentVersion\Review entry, this setup file is run a second time after the binding engine has written the results of its analysis to the registry. During its review pass, a driver’s setup file reviews the Bind, Export, and Route values written by the binding engine, and deletes any inappropriate bindings, a process known as “pruning.”

A review can be also requested by setting the Review entry to one to do other operations for a network component after the bindings have been generated. For instance, the system TCP/IP transport uses its review pass to read the available adapters it has to work with and to assign IP addresses to them.

Unless restricted by a component's net rules, the binding engine will generate a list of possible adapters for any transport protocol driver that, according to the system's built-in rules, lists all available NICs in the machine, including NICs of media types that the transport might not support.

In other words, a network component must have an .inf file that requests a review pass during which the generated bindings are reviewed and any unusable bindings pruned if the net rules it specifies cannot restrict the bindings generated by the binding engine.

If pruning of unusable bindings is not done before driver initialization, when a transport protocol, for instance, calls NdisOpenAdapter, a miniport for an underlying adapter can select a medium the transport does not support. When this occurs, the transport's call to NdisOpenAdapter fails, and an error message is written to the event log. This error message can be avoided if the transport prunes bindings in a review pass prior to driver initialization to delete any NICs that are not possible partners. Developers should write an .inf script that reviews and prunes bindings for every component for which net rules cannot be used to restrict the generated bindings list.

NIC miniports seldom, if ever, request a review pass. They usually set the Review entry to zero because the net rules for NIC drivers can be used to restrict the list of generated bindings between the miniport and the NICs in the machine.


3.5.1 Registry Entries for an NDIS Intermediate Driver

After installation, the registry must contain an HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NameOfComponent
\CurrentVersion\NetRules entry that describes an NDIS intermediate driver's upper-edge miniport behavior and, if the driver also registers a set of ProtocolXxx functions, a …NetRules entry that describes its lower-edge protocol behavior. The form of these entries should match those previously described for miniport and transport protocol drivers, respectively.

In addition, after the binding engine runs, the registry must contain an entry for both the miniport and protocol behavior of such an intermediate driver at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
\Services\NameOfComponent and another entry for each virtual NIC that such a driver creates. The following illustrates how these entries should appear for an intermediate driver that emulates a LAN miniport at its upper edge and an ATM protocol driver at its lower edge that creates a single virtual NIC.

For such an intermediate driver, an entry for the miniport upper edge would be defined as follows at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
\Services\LaneMiniport\:
DisplayName : REG_SZ : ATM LanEmulationMiniport
ErrorControl : REG_DWORD : 1
Group : REG_SZ : NDIS
ImagePath : REG_SZ : \SystemRoot\system32\drivers\atmlane.sys
Start : REG_SZ : 2
Type : REG_SZ : 1
Linkage
Bind : REG_MULTI_SZ : \Device\LaneMiniport1
Export : REG_MULTI_SZ : \Device\LaneMiniport1
Route : REG_MULTI_SZ : “LaneMiniport1”

Its entry for the protocol upper edge would be defined as follows at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
\Services\LaneProtocol\:
DisplayName : REG_SZ : ATMLanEmulationProtocolDriver
ErrorControl : REG_DWORD : 1
Group : REG_SZ : TDI
Start : REG_SZ : 3
Type : REG_SZ : 4
Linkage\
Bind : REG_MULTI_SZ : \Device\AtmMiniport1
Export : REG_MULTI_SZ : \Device\LaneProtocol
Route : REG_MULTI_SZ : “AtmMiniport”

Such a driver also would have an entry that represents the virtual NIC created by the upper-edge miniport, defined as follows at ..SYSTEM\CurrentControlSet\Services\LaneMiniport1:
ErrorControl : REG_DWORD : 1
Group : REG_SZ : NDIS
Start : REG_SZ : 3
Type : REG_SZ : 4
Linkage
Bind : REG_MULTI_SZ : \Device\LaneMiniport1
Export : REG_MULTI_SZ : \Device\LaneMiniport1
Route : REG_MULTI_SZ : “LaneMiniport1”
Parameters
……..

A Type value of 4 tells the Service Controller that the component described should not be started.

Pruning the Registry For an Intermediate Driver

As described previously in Section 3.5, the result of the binding analysis for each component that identifies its class as a Transport can be a list of all the NIC device names that appear in the Export list of each NIC driver installed on the machine. Because an NDIS intermediate driver appears as a transport protocol at its lower edge and as a miniport at its upper edge, the binding analysis will list the upper-edge-exported adapter name for the intermediate driver itself as one of the NICs to which the lower edge miniport can bind itself.

Consequently, such an intermediate driver must request a Review pass when its .inf file runs and use the review to prune the name of its upper edge virtual NIC from the list of possible bindings.


Chapter 1 Introduction to NIC Drivers

This introduces Network Interface Card (NIC) drivers complying with NDIS 4.0 for Windows NT® and other Microsoft Operating Systems that support the Win32 interface. The following are described:

  • NDIS in Section 1.1

  • Key Features of NDIS in Section 1.2

  • Types of NIC Drivers in Section 1.3

  • NIC Driver Architecture in Section 1.4

  • Network Interface Card Support in Section 1.5

  • NDIS Management Information and OIDs in Section 1.6


1.1 Network Driver Interface Specification (NDIS)

NDIS describes the interface by which one or more NIC drivers communicate with one or more underlying network interface cards, with one or more overlying protocol drivers, and with the operating system. NDIS supports the following:

  • A NIC driver to receive a network packet from any one of several upper layer drivers for transmission on the network through any one of several network adapters

  • A NIC driver to accept a network packet from any one of several underlying network adapters, and pass it up to one or more upper layer drivers that are interested in receiving it

  • An upper layer driver to set specific configuration parameters for a network adapter or a NIC driver

  • Upper layer drivers to query a NIC driver for specific configuration and statistics from the underlying network adapter or the NIC driver

  • A NIC driver to asynchronously inform an overlying driver of changes in network status or NIC status

NDIS defines a fully abstracted environment for NIC driver development. For every external function that a NIC driver needs to perform, it can rely on NDIS routines to perform the operation. This includes the entire range of tasks performed by a NIC driver, from communicating with protocol drivers, to registering and intercepting NIC hardware interrupts, and communicating with underlying NICs through manipulating registers, port I/O, and so forth. Therefore, NIC drivers can be written entirely in platform-independent high-level languages such as C. These drivers can then be recompiled with a system-compatible compiler to run in any NDIS environment.

To provide this level of abstraction and portability, Windows NT provides an NDIS export library referred to here as the NDIS library or NDIS. All interactions between a NIC driver and protocol drivers, a NIC driver and the operating system, and a NIC driver and the network adapters that it controls are through calls to NDIS functions. NDIS is packaged in an export library (ndis.sys) as a set of functions, with emphasis on in-line macros for maximum performance. All NDIS drivers, including highest-level NDIS protocol drivers, intermediate NDIS drivers, and NIC drivers link against this library. When called, an NDIS function calls an associated function in a higher-level driver, in an intermediate NDIS driver, in a NIC driver, in the operating system, or else performs an internal-to-NDIS local action.

Figure 1.1 shows an overview of this architecture.

Figure 1.1 NDIS Driver Architecture

NDIS forms a wrapper around a NIC driver. At the upper edge, NDIS interfaces between the NIC driver and various intermediate NDIS drivers, legacy transport drivers written to the LAN protocol and native-media-aware protocol drivers. For instance, a packet sent by an intermediate NDIS driver or native-media-aware protocol driver is sent down to the NIC driver by calling NDIS. NDIS then calls the NIC driver to send the packet over the network. NDIS provides such services to the NIC driver as serializing sends and requests, ensuring that the driver completes one send before it receives another send request. This frees the NIC driver from creating and maintaining queues and simplifies the task of the NIC driver writer. NDIS provides support for full-duplex operations so that a miniport can be executing packet sends and receives simultaneously on an SMP machine. NDIS also provides support for multipacket sends and multipacket receive indications.

While all these NDIS features are available to most miniports, they are not available to legacy full NIC drivers. Consequently, legacy full NIC drivers do not benefit from some features, such as multipacket sends and receives, as well as features planned for future versions of NDIS. For these reasons, driver writers should write a miniport NIC driver rather than a full NIC driver, and existing full NIC drivers should be converted into miniports.

NDIS provides common functions to the interface between the NIC driver and network interface cards. This lower edge set of functions allows the NIC driver to manage the NIC hardware through NDIS functions that call a set of OS-specific components. NDIS allows the NIC driver to be abstracted from the operating system and hardware architecture on which it runs so that a single driver is portable across platforms on which NDIS is implemented.

The NDIS library also includes a set of Filter Library routines for the most common types of network media. The filter libraries provide an environment where a protocol driver can specify destination addresses or address types for packets that it is interested in receiving. Thereafter, each incoming packet will be routed to one, several, or all bound upper layer drivers based on the packet’s destination address. The NIC driver simply passes an incoming packet up by calling an NDIS function. NDIS does the work of routing the packet to interested upper network layers. If the NIC driver prefers, it can use a null filter and indicate received packets to all bound protocols.


1.2 Key Features of NDIS

Key features of NDIS include the following:

Single NIC Driver ¾ In NDIS, only one instance of a NIC driver is used to control all network adapters that driver can support. This single instance maintains state information that describes each of many underlying NICs that it controls.

NDIS Library ¾ As discussed in the previous section, the NDIS library (ndis.sys) provides a fully abstracted interface to which a NIC driver is written. This allows NIC driver code to be easily ported to other NDIS-compliant platforms, usually with a simple recompilation. The NIC driver and upper layer driver need not know each other’s entry points. Instead, each driver registers its respective entry points as appropriate, and NDIS forwards calls from the requesting driver to the corresponding entry point in the target driver. Then, each driver can simply make NDIS function calls as appropriate, and these calls are forwarded to another network driver or to the operating system on which it executes.

Symmetric Multiprocessor Support ¾ NDIS provides all of the support functions that allow intermediate NDIS, highest level protocol drivers and NIC drivers to operate safely on, and take advantage of, any platform that has concurrently executing processors.

Loopback Support ¾ If process A is sending a message to process B on the same machine, the data frames handed down by the protocol driver must be “looped back” and forwarded up through the upper layer drivers to process B. A miniport driver of a NIC that does not have hardware-loopback support can specify to NDIS that it does not support loopback. NDIS will then loopback such messages destined for a process on the same machine. If a protocol driver supports loopback, it is performed within the protocol driver and will therefore never be passed down to NDIS or to an underlying NIC driver.

Multiprotocol Driver Support ¾ An NDIS NIC driver supports any number of overlying drivers that are bound through NDIS to the NIC driver’s underlying network interface card(s). A full NIC driver must maintain binding information for every bound protocol, and indicate a received packet to all upper layer drivers (and other NDIS clients, such as the Microsoft Network Monitor) bound to the NIC on which the packet was received. A miniport NIC driver, however, does not have to maintain binding information; this work is done within NDIS. By making a single NDIS call, these NIC drivers can indicate an incoming packet to any number overlying transport drivers. Miniport NIC drivers either use filter libraries or a null filter; they do not keep binding information nor are they aware of how many upper layer drivers can receive a packet. A miniport NIC driver simply makes an NDIS function call and allows NDIS to correctly deliver the packet up.

Administration ¾ NDIS defines how NDIS clients can query all information that the NIC driver maintains about itself and each of the NICs it controls.

Types of Send ¾ A miniport NIC driver can choose to implement a send interface that can only accept one packet per send request or a send interface that accepts many packets per send request.

Options Flags ¾ On a single-packet send, the packet descriptor, passed from an upper layer driver to a NIC driver, not only describes the network packet to be transmitted, but it also includes flags that can represent additional information to the NIC driver, such as packet-priority information, or a request for a certain quality of service for the packet transmission. If a miniport NIC driver implements a multipacket send, the sender can explicitly pass priority, time to send, and other media-specific information within the NDIS-defined OOB block associated with each packet descriptor.

Full Duplex Operation ¾ NDIS provides support to allow a miniport to send and receive packets simultaneously on SMP machines. If a miniport operates in full-duplex mode, it indicates this to NDIS when it is queried about its operating characteristics during driver initialization. Choosing to run full-duplex, rather than with serialized sends and receives, puts additional requirements on a miniport, but such a NIC driver improves its performance in Windows NT SMP platforms. These requirements are described in Chapter 4.

ARCNET and WAN Support ¾ NDIS allows ARCNET and WAN NIC drivers to determine if a bound upper layer driver supports their native frame format, or if the NIC driver must translate between the upper layer driver supported frame format (such as Ethernet or Token Ring) and the NIC’s native frame format.


1.3 Types of NIC Drivers

NDIS supports two types of NIC drivers:

  • Miniport NIC drivers

  • Full NIC drivers (sometimes called MAC drivers or full MAC drivers in earlier documentation)

While the NDIS V4.0 library will continue to support NDIS V3.0 full NIC drivers for legacy netcards, only NDIS miniport NIC drivers can take advantage of the enhanced functionality and performance characteristics of NDIS V4.0 for their NICs. Only NDIS miniport NIC drivers can take advantage of the enhancements and functionality planned for future versions of the NDIS library.

For new NICs, the smaller, easier-to-write miniport should be developed to take advantage of new features in new versions of NDIS that are not available to full NIC drivers. Legacy full NIC drivers required driver developers to write a large amount of code to deal with issues that are common across all NDIS drivers. About fifty percent of the code written for a full NIC driver is common to all NDIS drivers, while the other half is specific to the hardware. Writing a full NIC driver places the full burden of coding multiprocessor support and other more difficult operating system programming issues on the NIC driver developer.

To make development of NIC drivers easier, this common code is merged into the NDIS library. NDIS removes all concerns for open bindings, queuing of requests, queuing of transmits, and many synchronization issues from the NIC driver writer. By providing this support for much smaller and simpler miniport drivers, developers can write only hardware-specific code.


1.4 NIC Driver Architecture

NDIS NIC and upper layer drivers use the NDIS library (ndis.sys) to communicate with each other, and to call operating system functions. When the operating system calls the NIC driver’s DriverEntry routine, the NIC driver calls NDIS to register its entry points and to obtain a handle representing the association between the driver and NDIS. In turn, NDIS calls the miniport NIC driver once for each NIC it supports.

When each protocol driver initializes, it calls NDIS to bind to a specified NIC. NDIS returns a handle for the resulting binding to the protocol driver. Once initialized, the protocol driver can submit requests to a given underlying NIC by calling the NIC driver functions through related NDIS library functions, specifying this binding handle. NDIS in turn, will pass the request to the appropriate NIC driver, using the handle representing the NDIS-NIC association.

To communicate with its remote-node peer, an NDIS protocol driver packages messages from an originating component (such as the redirector) into network data packets. The protocol driver then submits these packets by calling NDIS. Almost all NDIS function names have the prefix “Ndis...”, except for filter library functions. Functions that are exclusively called by miniport NIC drivers have the prefix “NdisM...”. Those called by NDIS intermediate drivers have the prefix “NdisIM...”.

“Upper-edge” or miniport function names are documented with a “Miniport” prefix. “Lower-edge” or protocol driver function names are documented with a “Protocol” prefix. All of the MiniportXxx and ProtocolXxx functions mentioned here are metanames: the NDIS-defined driver functions actually can have any names chosen by the driver writer as long as they have the required functionality.

To send a packet, NDIS functions call NIC driver upper-edge functions or possibly an intermediate NDIS driver such as a LAN emulation driver. If the call is passed to a NIC driver, the NIC driver prepares the packet for transmission on the network and queues it to be transmitted by an underlying NIC. If the packet is passed from a protocol driver to an NDIS intermediate driver that exports a LAN-miniport interface on its upper edge, the intermediate NDIS driver, which exports the native-media protocol interface at its lower edge, translates the packet to the underlying native-media format, and again calls NDIS to send the packet. This time, the packet is passed by NDIS to a NIC driver.

When a network packet arrives from a remote network node, the NIC captures the packet and signals its NIC driver. The NIC driver then calls the NDIS library which, in turn, calls an intermediate NDIS driver’s or a highest level protocol driver’s lower-edge functions to notify bound protocol drivers of the incoming packet and either pass the entire packet or subsequently transfer the packet data.

The requirements imposed on NDIS drivers dictate that they need to call, or be called by, various platform-specific operating system components. Some examples include:

  • When a driver needs a buffer, it must ask the operating system to allocate memory.

  • When a NIC driver is ready to transmit a packet, it might use programmed I/O to communicate with the appropriate NIC and, if necessary, to transfer the packet from system memory to the NIC’s transmit buffer.

  • After a packet is received by the NIC, the NIC will generate a hardware interrupt to pass control to the NIC driver.

The NDIS library provides Ndis Xxx functions that support all of these operations. When a driver needs operating system support, it calls NDIS functions that, in turn, can call kernel-mode support routines. When a NIC interrupts, the interrupt is passed to the NDIS library first. NDIS, in turn, calls the appropriate NIC driver’s MiniportISR function.

NDIS defines several hundred primitives used by the various NDIS components. The NDIS library can be thought of as the standard C library, providing a rich framework for writing NIC drivers. Only a small subset of these NDIS functions is likely to be called by any specific NIC or protocol driver.

NIC drivers are only required to implement a small number of functions. Miniport NIC drivers have a smaller number of NDIS-defined functions than legacy full NIC drivers.


1.5 Network Interface Card Support

Windows NT currently supports the following types of network interface cards:

  • Ethernet (802.3)

  • Token Ring (802.5)

  • FDDI

  • LocalTalk

  • ARCNET

  • WAN (point-to-point and WAN cards)

  • Wireless

When NDIS calls a miniport at its initialization function, it passes an array of NDIS-supported mediums. The NIC driver selects the medium that it supports; or, if the NIC driver can support more than one medium type, it selects the one that it prefers and returns its choice to NDIS. When a higher level NDIS protocol driver calls NdisOpenAdapter to bind to a specified NIC, it provides a list of medium types on which it can operate. NDIS uses the information from the NIC driver and from the upper layer driver to bind them appropriately. This binding provides the path by which packets are passed down the stack and sent out over the network and by which received packets are passed up the stack to higher level drivers.

The NDIS medium type defined for ARCNET is NdisMediumArcnet878_2. This medium type definition provides full compatibilitiy with Novell’s ARCNET specification for Raw Arcnet.

Raw ARCNET is the format defined in the ARCNET Packet Header Definition Standard published by Novell, and is the one used by NetWare. A transport driver that supports this format is responsible for selecting from three different frame formats (short, long, or exception), given the length of the data it wishes to send. It is then responsible for performing all of the associated fragmenting, padding, setting the split flag and sequence number, and so forth, related to use of this standard.

The NdisMediumArcnet878_2 format allows a transport driver to be aware that it is running over ARCNET, without having to deal with the details of fragmenting and unfragmenting. With NdisMediumArcnet878_2, the on-the-wire format is the same as for Raw ARCNET. However, the transport driver is shielded from most of the details of the frame format. The NIC driver is responsible for fragmenting the frame, selecting the appropriate frame format, and inserting the padding, split flag, sequence number, and other related fields.

An ARCNET full NIC driver, in its MacOpenAdapter routine, first searches the MediumArray list for the medium type NdisMediumArcnet878_2. If found, it selects this medium type. If the transport driver does not list NdisMediumArcnet878_2, or if the NIC driver does not support it, the NIC driver then searches for NdisMedium802_3. If it finds this type, it selects it and must then perform 802.3 to ARCNET encapsulation on packets it sends, and corresponding de-encapsulation on packets it receives. The NDIS library handles this internally for miniports.

The Windows NT Tcpip and Nwlink transport drivers support NdisMediumArcnet878_2. The Windows NT Nbf (also known as NetBEUI) transport does not.

Currently, miniport NIC drivers for ATM media should implement LAN-emulation functions within themselves, and report their medium type as Ethernet 802.3 or 802.5. Native-mode ATM is not currently supported.

Types of NICs

NIC features determine the NDIS functions a driver for such a NIC uses to initialize its adapter and the functions it uses to send and receive packets. The ways in which each of these types of network adapter is programmed is described in this documentation. The two general categories of NICs are:

  1. Busmaster DMA devices

    These devices can directly access host memory, meaning the NIC has an on-board DMA controller that manages the sending and receiving of data from host memory to the network and from the network to host memory without using the CPU.

    On a send, the miniport driver sets up the NIC to map the outgoing buffers and then causes the device to start its transfer to or from this memory. The NIC DMA controller transfers the data from shared system memory onto the network and interrupts the CPU when the send is complete. On a receive, the DMA controller transfers incoming data to host memory before notifying the host with an interrupt.

    Such a device typically has a ring buffer for which the NIC’s miniport allocates a set of buffers and can be programmed to efficiently handle several packets. A miniport driver that manages a busmaster DMA NIC typically supports multipacket sends and receives since such a miniport’s device can efficiently handle several packets and improve its I/O throughput over sending and receiving a single packet at a time.

  2. Non-busmaster DMA devices

    There are three general types of NIC in this category.

    • A NIC that contains on-board shared memory

      The miniport that manages such a device must map the adapter shared memory to host memory and then copy outgoing packets to this device memory or copy incoming frames from device memory to buffers supplied by upper layer protocol drivers or other NIC drivers. Such a device generally cannot improve its performance by supporting multipacket sends and receives.

    • A slave DMA NIC

      The miniport that manages such a device uses the system DMA controller to manage the transfer of packets to and from the network. The miniport that manages such a NIC sets up its device for sends and receives in a different manner than a busmaster DMA device. Transfer of the data is with the cooperation of the host CPU.

    • A NIC that uses programmed I/O

      The miniport that manages a PIO device uses NDIS functions to move outgoing frames byte-by-byte, word-by-word, or long-by-long to device registers and then causes the device to send the data. The driver for such a device does not benefit from NDIS support for multipacket sends and receives. Miniports for this type of NIC should implement single packet sends and receives, as described in Chapter 4.

In summary, whether the NIC supports busmaster DMA affects the following:

  • Whether the miniport supports multipacket or single-packet sends and receives

  • What kind of memory is used, whether the memory is shared or not shared, and how much is allocated at driver initialization

  • The steps by which the miniport sends data

  • The steps by which the miniport receives data

  • Whether the miniport supports passing media-specific information


1.6 NDIS Management Information and OIDs

Each NIC driver contains its own MIB, an information block in which the driver stores dynamic configuration information and statistical information that a management entity can query or set. Configuration information includes data such as an Ethernet multicast address list, and statistical information includes data such as the number of broadcast packets received. Each information element within the MIB is referred to as an object. To refer to each such managed object, NDIS defines an Object Identifier. Therefore, if a management entity wants to query or set a particular managed object, it must provide the specific OID for that object.

The MIB tracks three classes of objects:

  1. Those general to all NDIS NIC drivers

  2. Those specific to all NDIS NIC drivers for a given medium type, such as Ethernet or Token Ring

  3. Those specific to a particular vendor implementation

The general and mandatory media-specific OIDs are documented in the Network Driver Reference. For these system-defined OID_XXX codes, this documentation indicates whether an NDIS driver’s MiniportQueryInformation function can be queried on the associated management object or the MiniportSetInformation function can receive a request to set the management object. In legacy full NIC drivers, the MacRequest function is called to both query and set values, while MacQueryGlobalStatistics is called for global statistics queries. The implementation-specific OIDs for a particular NIC driver should be listed and described in the documentation accompanying a given NIC driver.

Besides being classified as either operational characteristics (for example, multicast address list) or statistics (for example, broadcast packets received), objects are classified as either mandatory or optional. All operational characteristics objects for either general or media-specific classes are mandatory, but only some statistics objects are mandatory. All implementation-specific objects are classified as mandatory.

Figure 1.2 shows the structure of an OID.

Figure 1.2 NDIS OIDs

The first 3 bytes of the OID provide a key to the OID’s various classifications, with the fourth byte identifying the specific information management object within those classifications.

For a listing and descriptions of all NDIS general and media-specific OIDs, refer to the Network Driver Reference.


Chapter 2 Overview of NIC Driver Operations

This provides an overview of NDIS miniport NIC drivers. The design goals of a miniport are briefly discussed along with a description of the common functions called by and implemented in such a miniport, as follows:

  • Miniport Design Goals in Section 2.1

  • Miniport Driver Structure in Section 2.2

  • Miniport NIC Driver Functions in Section 2.3

  • Miniport NIC Driver Operations in Section 2.4

In addition to providing a standard interface between each NIC and higher-level protocol drivers, NDIS also includes functions that supply services needed by many different network interface cards. Since a miniport needs only hardware-specific functions, the driver writer can choose to implement those NDIS-defined upper-edge functions that are necessary to accomplish this.


2.1 Miniport Design Goals

Miniport drivers allow developers to write code that is specific to the hardware. Functionality that is common across many NIC drivers is provided by the NDIS library. NIC driver writers should write a miniport driver.

While full NIC drivers must handle multiprocessor support and complex synchronization issues explicitly with spin locks and other mechanisms, a miniport usually need not deal with these issues. Consequently, they are much simpler to write and test. The NDIS library functions for miniports(prefaced with NdisM... ) handle these multiprocessor synchronization issues as part of the NDIS library. The following items are merged into the NDIS library for support of miniports:

  • Filter packages

  • Opens, closes, and queuing of all sorts

  • Timers to detect hung hardware

  • Synchronization and multiprocessor support

  • Support for multipacket sends and receives

  • Support for full-duplex operation

Miniports must support one of the NDIS-defined media types. Drivers for other media types must emulate one of these specified media types and run in emulation mode. The NDIS library supports the list of media types noted in Chapter 1 for miniports as well as the NULL filter.

A miniport must meet the following requirements:

Portability

The miniport should call only Ndis Xxx functions that allow the driver to run on CISC and RISC platforms. The miniport driver must not contain any architecture-dependent code. NDIS shields the driver from architectural differences among microprocessors.

Multiprocessor Support

Any driver must be multiprocessor safe. For most situations, multiprocessor support is automatically provided by the NDIS library.

Functional Requirements

The driver must be able to register itself with NDIS, make requests for operating system support, and allocate, initialize, manipulate, inspect, and deallocate packet descriptors. A packet descriptor is a data structure with both public and private components that contains a pointer to a linked list of buffers.


2.2 Miniport Driver Structure

A miniport driver includes driver objects, driver code, and the NDIS library code. The Windows NT DDK provides ndis.h as the main header file for miniports. This file defines the entry points for the miniport, the NDIS library functions, and common data structures. The Network Reference contains the definitions of the miniport, protocol, and Ndis Xxx functions, the common data structures, and OIDs.

Miniport Adapter Object

NDIS uses a software object called a logical adapter to represent each NIC in the system. The logical adapter corresponds to a Windows NT device object managed by the I/O subsystem. The device object that represents the adapter contains information about an associated NIC, such as name, network address, and the base address of the NIC. It also contains data about the current state of the driver in relation to the hardware, such as number of bindings, binding handles, filter database, and logical adapter references. NDIS passes a handle to this structure to MiniportInitialize; the miniport NIC driver will supply this handle in subsequent calls to NDIS. This structure is maintained by NDIS and is opaque to the miniport.

When a miniport NIC driver is called to initialize an adapter it manages, it creates its own internal data structure to represent the adapter. The driver uses this structure to keep device-specific state information it needs to manage the NIC. The miniport NIC driver will pass a handle to this structure to NDIS when it calls NdisMSetAttributes or NdisMSetAttributesEx and, when NDIS calls the miniport’s entry points, it will pass this handle, identifying the correct adapter to the driver. This structure is owned and maintained by the miniport NIC driver.

Network Object Identifiers

The miniport NIC driver also maintains a set of objects identified by a system-defined object identifier (OID) that represents information about the driver’s capabilities and current status. To query this information, a higher level driver calls NdisRequest and passes the OID to the NDIS library with a buffer large enough to hold the returned data. The OID specifies the type of information requested by the caller, such as the size of lookahead buffer the miniport supports. The request is passed to the miniport’s MiniportQueryInformation entry point. If the caller does not provide a large enough buffer for the information, the miniport writes the number of bytes needed for the data, along with a relevant failure status code.

For more information about OIDs, see the Network Driver Reference.

Miniport NIC Driver Code

The typical miniport NIC driver must have a small number of functions to communicate through NDIS with upper layers and the hardware. The complete list is described in Section 2.3; not all functions are required. Chapter 3 describes which functions are optional, which are required, and why.

Many of a miniport’s functions can operate either synchronously or asynchronously. The asynchronous functions have corresponding completion functions in the NDIS library that must be called when an operation is finished. See the Network Driver Reference for more information about these functions.

The DDK includes sample code for miniports that manage several types of network cards. These sample drivers can be tailored to a driver developer’s needs. The sample drivers contain functions that can be adapted to a new but similar driver. There are always hardware-dependent functions that must be written by the driver developer. However, many functions (primarily those that communicate with the NDIS library instead of a network interface card) are fairly standard. For these driver functions, the code in a sample driver might be usable with little or no modification.

These samples include the following:

  • NE2000, a 16-bit ISA card that uses programmed I/O.

  • NE3200, a 32-bit EISA bus-mastering card that uses 16-bit DMA transfers.

  • Lance (AMD 7990) Ethernet chip.

  • IBM Token Ring 16/4 Adapter.

  • IBM Token Ring 16/4 Adapter II.

  • DC21X4 busmaster DMA Adapter
NDIS Library

The NDIS library is packaged in ndis.sys, a kernel-mode export library (an export library is a .sys file that functions similarly to a dynamic-link library), as a set of abstract functions, with emphasis on macros for maximum performance. Both protocol and NIC drivers, including WAN NIC drivers, link against this library. The NDIS library functions are defined in the Network Driver Reference.


2.3 Miniport NIC Driver Functions

This section provides an overview of the functions exported by and called by a miniport NIC driver. A miniport driver developer must implement a number of upper-edge functions that can be called from NDIS. Miniport drivers also typically call a common set of NDIS functions.

  • Upper-edge driver functions supported by miniport drivers are discussed in Section 2.3.1

  • NDIS library functions called by miniport drivers are discussed in Section 2.3.2

For details on how these functions work together, see Section 2.4. For a more detailed explanation of the requirements of each miniport function, see Chapter 3 and Chapter 4.


2.3.1 Miniport NIC Driver Upper-Edge Functions

The miniport NIC driver must export some or all of the functions described in the following table. NDIS calls these functions on behalf of other layers of the network software, such as a higher level protocol driver that is bound to a NIC managed by the NIC driver.

Some of the NIC driver functions are inherently synchronous, while others can complete either synchronously or asynchronously. When a miniport function returns the status NDIS_STATUS_PENDING, the miniport must later complete the request by calling the appropriate NdisM...Complete function. NDIS takes care of any further coordination with other layers, calling the completion functions of those layers for asynchronous requests as required.

Names of the miniport driver functions discussed here use Miniport as a generic prefix. Functions written for an actual miniport driver should be named with a more descriptive prefix in place of the Miniport prefix to make debugging easier. Because the addresses of the functions (not the names) are passed to the NDIS library during initialization, function naming is at the discretion of the driver developer.
Function | Description | Async Option
DriverEntry | Called by the operating system to activate and initialize the NIC driver. | Not Applicable
MiniportCheckForHang | Checks the internal state of the network interface card. | No
MiniportDisableInterrupt | Disables the network interface card from generating interrupts. | No
MiniportEnableInterrupt | Enables the network interface card to generate interrupts. | No
MiniportHalt | Deallocates and deregisters resources used for the NIC, and halts the NIC so it is no longer functioning. | No
MiniportHandleInterrupt | Deferred processing function called to complete interrupt-driven I/O processing. | No
MiniportInitialize | Initializes the network interface card. | No
MiniportISR | Runs at a high priority as the interrupt service function for the network interface card. | No
MiniportQueryInformation | Queries the capabilities and current status of the miniport driver. | Yes
MiniportReconfigure | Not used. | No
MiniportReset | Issues a hardware reset to the network interface card. | Yes
MiniportSend | Transmits a packet through the network interface card onto the network. | Yes
MiniportSetInformation | Changes (sets) information about the miniport driver or its NIC. | Yes
MiniportTransferData | Copies the contents of a packet received by the network interface card into a given packet buffer. | Yes
MiniportSendPackets | Transmits an array of packets through the network interface card onto the network. | Yes
MiniportReceivePacket | Receives a packet from an upper layer that was previously passed up by a call to NdisMIndicateReceivePacket | No
MiniportAllocateMemoryComplete | Called to indicate that a preceding call to NdisMAllocateMemoryAsync is now complete. | No

The corresponding NDIS completion function that is associated with each possibly asynchronous upper-edge driver function is listed here.
Function | Asynchronous Completion Function
MiniportQueryInformation | NdisMQueryInformationComplete
MiniportReset | NdisMResetComplete
MiniportSend | NdisMSendComplete
MiniportSetInformation | NdisMSetInformationComplete
MiniportTransferData | NdisMTransferDataComplete


2.3.2 NDIS Functions Called by Miniports

Windows NT furnishes NDIS library functions as an export library (ndis.sys). Both protocol and NIC drivers link to this export library.

The NDIS library provides the following kinds of functions for the miniport NIC driver:

  • Initialization and Registration in Section 2.3.2.1

  • Hardware Configuration (for ISA, EISA, MCA, and PCI buses and PCMCIA cards) in Section 2.3.2.2

  • I/O Port in Section 2.3.2.3

  • DMA-related in Section 2.3.2.4

  • Interrupt Handling in Section 2.3.2.5

  • Synchronization Functions in Section 2.3.2.6

  • Status in Section 2.3.2.7

  • Send and Receive in Section 2.3.2.8

  • Packet and Buffer Handling in Section 2.3.2.9

The following sections summarize these NDIS library functions commonly used by a miniport driver.


2.3.2.1 Initialization and Registration Functions

A miniport driver configures itself using information in the registry and the configuration functions provided by NDIS. The module name that the miniport driver uses in the registry must be unique so that it does not conflict with the module names for other drivers. Keyword names are not restricted because keywords for one module are independent of those for another module. An NDIS keyword is a synonym for the name of a value entry under a registry key.
Function | Definition
NdisMInitializeWrapper | Initializes the NDIS library data structures for this miniport driver.
NdisMRegisterMiniport | Provides the NDIS library with information about the miniport driver.
NdisMSetAttributes | Informs the NDIS library of the type of network interface card supported by the miniport driver and passes the handle to the miniport’s context area which will be passed in subsequent calls to MiniportXxx functions.
NdisMSetAttributesEx | Changes the default NIC timeout behavior as well as passing the same information passed to NdisMSetAttributes. NDIS intermediate drivers must call this function, rather than NdisMSetAttributes.
NdisMQueryInformationComplete | Indicates that a prior call to MiniportQueryInformation is complete.
NdisMSetInformationComplete | Informs the NDIS library that the previous MiniportSetInformation operation has completed.
NdisOpenConfiguration | Opens the registry and obtains a handle to the Parameters key that describes a NIC managed by the driver.
NdisReadConfiguration | Uses the Parameters handle obtained by calling NdisOpenConfiguration to read key values stored in the registry at the keyword passed to NdisReadConfiguration.
NdisWriteConfiguration | Uses the Parameters handle obtained by calling NdisOpenConfiguration to write key values into the registry at the keyword passed to NdisWriteConfiguration.
NdisCloseConfiguration | Closes the handle to the registry that was opened with a call to NdisOpenConfiguration.


2.3.2.2 Hardware Configuration Functions

The NDIS library provides bus-related functions for EISA, MCA, and PCI. These functions are called by MiniportInitialize to obtain bus-specific information needed to initialize the NIC.
Function | Definition
NdisReadEisaSlotInformation | Reads the slot information for an EISA network interface card and copies it into a buffer that the NIC driver provides.
NdisReadEisaSlotInformationEx | Reads the slot information and a set of functions information for an EISA network interface card that supports alternative configurations and copies these configurations into a buffer that the NIC driver provides.
NdisReadMcaPosInformation | Reads data from the POS register on an MCA network interface card and copies this information into a buffer that the NIC driver provides.
NdisImmediateReadPciSlotInformation | Reads a specified number of bytes from the configuration space of a specified PCI device.
NdisImmediateWritePciSlotInformation | Writes a specified number of bytes to the configuration space of a specified PCI device.
NdisMPciAssignResources | Returns a list of resources claimed for a specified PCI device.
NdisReadPciSlotInformation | Reads a specified number of bytes from the configuration space of a specified PCI device.
NdisWritePciSlotInformation | Writes a specified number of bytes to the configuration space of a specified PCI device.


2.3.2.3 I/O Port Functions

The NDIS library provides a set of I/O functions that a miniport NIC driver calls to access I/O ports. These calls provide a standard portable interface that supports the various operating environments for NDIS drivers. Functions are provided for mapping ports, for claiming I/O resources and for reading from and writing to the mapped and unmapped I/O ports.
Function | Definition
NdisMRegisterIoPortRange | Sets up access to I/O ports for using NdisRawReadPort Xxx and NdisRawWritePort Xxx.
NdisMDeregisterIoPortRange | Removes access to I/O ports previously registered with NdisMRegisterIoPortRange.
NdisMMapIoSpace | Maps a range of device memory for subsequent PIO operations.
NdisMUnmapIoSpace | Releases a region mapped by a call to NdisMMapIoSpace.
NdisImmediateReadSharedMemory | Reads a single buffer of data from a shared memory address before the driver has called NdisMMapIoSpace.
NdisImmediateWriteSharedMemory | Writes a single buffer of data to a shared memory address before the driver has called NdisMMapIoSpace.
NdisRawReadPort Xxx | Read a single piece of data from an I/O port. Specific functions include: NdisRawReadPortUchar NdisRawReadPortUlong NdisRawReadPortUshort
NdisRawReadPortBuffer Xxx | Read a buffer of data a piece at a time from an I/O port. Specific functions include: NdisRawReadPortBufferUchar NdisRawReadPortBufferUlong NdisRawReadPortBufferUshort
NdisRawWritePort Xxx | Writes a single piece of data to an I/O port. Specific functions include: NdisRawWritePortUchar NdisRawWritePortUlong NdisRawWritePortUshort
NdisRawWritePortBuffer Xxx | Writes a buffer of data to I/O port. Specific functions include: NdisRawWritePortBufferUchar NdisRawWritePortBufferUlong NdisRawWritePortBufferUshort
NdisImmediateReadPort Xxx | Reads a piece of data from an I/O port before the driver has mapped the port by calling NdisMRegisterIoPortRange. Specific functions include: NdisImmediateReadPortUchar NdisImmediateReadPortUshort NdisImmediateReadPortUlong
NdisImmediateWritePort Xxx | Writes a piece of data from an I/O port before the driver has mapped the port by calling NdisMRegisterIoPortRange. Specific functions include: NdisImmediateWritePortUchar NdisImmediateWritePortUshort NdisImmediateWritePortUlong


2.3.2.4 DMA-Related Functions

A NIC driver calls NDIS functions for DMA operations between the host and the network interface card. For example, miniport NIC drivers allocate and manage virtual memory buffers in the host for DMA transfers. The NDIS library provides the functions that allow the drivers to allocate, construct, and inspect such buffers as described in the following table.

For busmaster DMA transfers, a NIC driver needs to send the physical addresses (also called logical addresses, since the actual microprocessor addresses are system mapped to device-accessible physical ranges) of virtual memory buffers to its network interface card. The driver can obtain these addresses by calling NdisMStartBufferPhysicalMapping. A portable miniport driver has limited use of logical addresses and sometimes only has knowledge of a virtual address when the network interface card requires a physical address. The NIC driver must call NDIS to map the virtual address to an appropriate logical address. The resultant mapping is temporary and the NIC driver must manage it using map registers.
Function | Definition
NdisMAllocateMapRegisters | Allocates map registers for use with busmaster DMA devices.
NdisMAllocateSharedMemory | Allocates memory to be shared between the system and a busmaster DMA NIC. It is called during driver initialization at IRQL PASSIVE_LEVEL.
NdisMAllocateSharedMemoryAsync | Called at raised IRQL, such as in a miniport’s HandleInterruptHandler, to allocate shared memory for a busmaster DMA NIC.
NdisMCompleteBufferPhysicalMapping | Releases the map registers used by a previous call to NdisMStartBufferPhysicalMapping for a busmaster DMA operation.
NdisMCompleteDmaTransfer | Indicates to the NDIS library that a slave DMA operation has completed.
NdisMDeregisterDmaChannel | Releases a miniport driver’s claim on its DMA channel.
NdisFlushBuffer | Called by the driver before sending data to the NIC or transferring data from the NIC to ensure coherence between cache memory and host physical memory during busmaster DMA operations.
NdisMFreeMapRegisters | Frees map registers for busmaster DMA that were previously allocated by NdisMAllocateMapRegisters.
NdisMFreeSharedMemory | Frees memory that was previously allocated by NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync.
NdisQueryMapRegisterCount | Returns the total number of map registers possible. The driver calls NdisQueryMapRegisterCount before calling NdisMAllocateMapRegisters to more realistically choose how many registers to allocate.
NdisMReadDmaCounter | Reads the current value of the system DMA controller counter.
NdisMRegisterDmaChannel | Sets up a DMA channel for future slave DMA operations.
NdisMSetupDmaTransfer | Sets up the host system DMA controller for a slave DMA transfer.
NdisMStartBufferPhysicalMapping | Creates a physical address mapping for a busmaster DMA operation.
NdisMUpdateSharedMemory | Ensures that data to be read from a shared memory region is up-to-date during busmaster DMA operations.


2.3.2.5 Interrupt Handling Functions

The NDIS library provides several interrupt handling functions, defined in the following table, for use in a multiprocessor environment. These functions relate to NIC interrupts on a host processor.
Function | Definition
NdisMDeregisterInterrupt | Stops an interrupt handler from receiving interrupts. The operating system disconnects the interrupt service function from its associated NIC interrupt.
NdisMRegisterInterrupt | Associates the miniport interrupt service function (MiniportISR) with interrupts generated by its network interface card.
NdisMSynchronizeWithInterrupt | Any NIC driver function that shares resources with the MiniportISR or MiniportDisableInterrupt functions must synchronize access to these resources to prevent race conditions. Functions that must synchronize with MiniportISR and MiniportDisableInterrupt do so by calling NdisMSynchronizeWithInterrupt passing the address of a MiniportSynchronizeISR function. The MiniportSynchronizeISR function runs at DIRQL so that it can safely access the shared resource.


2.3.2.6 Synchronization Functions

The following table defines the synchronization functions included in the NDIS library for miniport drivers.

The timer functions allow the miniport to implement an operation that occurs at a driver-requested interval. For instance, a driver can use a timer to poll its NIC. Events can be used to synchronize operations between two threads, at least one running at IRQL PASSIVE_LEVEL. Spin locks are used to synchronize access to shared resources, as is the NdisMSynchronizeWithInterrupt function.
Function | Definition
NdisMCancelTimer | Cancels a timer that was previously set with NdisMSetTimer.
NdisMInitializeTimer | Initializes a timer object and associates a MiniportTimer function with the object.
NdisMSetTimer | Sets a timer to go off after a specified interval.
NdisMSetPeriodicTimer | Sets a timer that goes off automatically at a specified interval of until cancelled with a call to NdisMCancelTimer.
NdisSleep | Causes the caller’s thread to block for the specified interval. A driver calls NdisSleep during initialization or when halting the adapter, for instance, while waiting for the NIC to complete initialization. NdisSleep is only called at IRQL PASSIVE_LEVEL.
NdisStallExecution | Causes the caller’s thread to stall for a specified interval not to exceed 50 milliseconds. NdisStallExecution is only called at raised IRQL when NdisSleep cannot be used.
NdisInitializeEvent | Creates and initializes an event which can then be used to synchronize driver operations.
NdisSetEvent | Signals the specified event.
NdisResetEvent | Resets the specified event to the unsignaled state.
NdisWaitEvent | Causes the caller to wait until the specified event is signaled or the specified time interval expires.
NdisAcquireSpinLock | Acquires a spin lock to protect access to shared resources between nonISR driver functions in an SMP-safe way. Miniports that are running at IRQL < DISPATCH_LEVEL call this function to acquire a spin lock.
NdisReleaseSpinLock | Releases a spin lock previously acquired by calling NdisAcquireSpinLock.
NdisDprAcquireSpinLock | Acquires a spin lock at IRQL DISPATCH_LEVEL. Protects access to shared resources between nonISR driver functions in an SMP-safe way. Faster than calling NdisAcquireSpinLock for driver functions running at IRQL DISPATCH_LEVEL.
NdisDprReleaseSpinLock | Releases a spin lock previously acquired by calling NdisDprAcquireSpinLock.
NdisMSynchronizeWithInterrupt | Any NIC driver function that shares resources with the MiniportISR or MiniportDisableInterrupt functions must synchronize access to the resources with these two functions to prevent race conditions. Such functions that must synchronize with MiniportISR and MiniportDisableInterrupt do so by calling NdisMSynchronizeWithInterrupt with a MiniportSynchronizeISR function that also runs at DIRQL while it accesses the shared resource.


2.3.2.7 Status Functions

The NDIS library provides status functions defined in the following table for the miniport driver.
Function | Definition
NdisMIndicateStatus | Indicates to the NDIS library that the network interface card status has changed.
NdisMIndicateStatusComplete | Indicates to the NDIS library that the status change is complete.
NdisMQueryInformationComplete | Indicates that a prior call to MiniportQueryInformation is complete.
NdisMSetInformationComplete | Indicates that a prior call to MiniportSetInformation is complete.


2.3.2.8 Send and Receive Functions

A miniport driver sends an array of packets on the network in its MiniportSendPackets function or sends a single packet in its MiniportSend function.

A miniport NIC driver transfers one or more received packets by calling NdisMIndicateReceivePacket and passing a complete packet or packets. Or, a miniport calls a filter-specific NdisM Xxx IndicateReceive function, forcing interested upper layer drivers to copy the data for a single received frame, either in NdisM Xxx IndicateReceive if the entire packet is passed as lookahead data, or by calling its MiniportTransferData function if the frame is larger than the lookahead buffer.

If the NIC driver indicates up an entire packet, the miniport’s MiniportReturnPacket function will subsequently be called to return the packet(s). The following table describes NDIS library functions that the miniport driver calls when sending and receiving packets.
Function | Definition
NdisMIndicateReceivePacket | Indicates up one or more packets to interested upper layers.
NdisMArcIndicateReceive NdisMEthIndicateReceive NdisMFddiIndicateReceive NdisMTrIndicateReceive | Indicates that a packet of a specific media type is being received.
NdisMArcIndicateReceiveComplete NdisMEthIndicateReceiveComplete NdisMFddiIndicateReceiveComplete NdisMTrIndicateReceiveComplete | Indicates that the previous packet receive operation is complete.
NdisMSendComplete | Indicates that the previous packet send operation is complete. This function is used when the previous MiniportSend function operated asynchronously.
NdisMSendResourcesAvailable | Indicates to the NDIS library that the miniport driver has available resources for send operations. A miniport calls this function in its MiniportSend function if it completes a send synchronously or in its MiniportHandleInterrupt when it detects that an asynchronous send has completed. NdisMSendResourcesAvailable can only be called when the driver has not called NdisMSendComplete for a pending send.
NdisMTransferDataComplete | Informs the NDIS library that the previous MiniportTransferData request is complete. This function is used when the previous call to the MiniportTransferData function returned NDIS_STATUS_PENDING.


2.3.2.9 Packet and Buffer Functions

Miniport NIC drivers have a need to allocate buffers for receiving incoming data. The driver of a busmaster DMA device that indicates the entire incoming packet up to interested protocols must allocate and manage packets for this purpose. A NIC driver might need to allocate buffer space to stage data on a send if the packet it receives from an upper layer is small and fragmented. On a send, all miniport NIC drivers receive a complete packet and must call NDIS functions to manipulate those packets and extract the data to be sent. A miniport that transfers incoming data to an upper layer driver receives a packet from the upper layer and must manipulate the packet structure to fill it with received data. NDIS provides functions that miniport drivers can call to allocate, construct, and inspect packets.

NDIS defines a packet as a packet descriptor with a chain of one or more buffers containing the network packet data. The NDIS library describes a packet descriptor by defining the NDIS_PACKET structure. The descriptor consists of two parts: a public component that a miniport driver can see, and a private component that is invisible to the driver. The public component allows a miniport driver to maintain context information about the packet. A miniport driver can use this component to link packets into data transfer queues.
Function | Definition
NdisAllocatePacketPool | Allocates and initializes a block of nonpaged packet pool. The caller supplies the requested number of packet descriptors and the size in bytes of each fixed-size packet.
NdisAllocatePacket | Allocates a fixed-size packet descriptor from the packet pool returned by NdisAllocatePacketPool.
NdisAllocateBufferPool | Returns a handle with which the caller can allocate buffer descriptors with NdisAllocateBuffer.
NdisAllocateBuffer | Creates a buffer descriptor mapping a specified virtual range within an already allocated, nonpaged memory block, given a handle returned by NdisAllocateBufferPool.
NdisAdjustBufferLength | Modifies the length of the range specified in a given buffer descriptor.
NdisCopyBuffer | Creates a buffer descriptor for a specified range, given a handle for a block of already allocated memory and a pointer to the buffer descriptor for the block.
NdisCopyFromPacketToPacket | Copies a specified number of bytes from one packet to another, given a specified source packet and a destination packet and specified offsets in each.
NdisCreateLookaheadBufferFromSharedMemory | Returns the virtual address of a buffer within a block of memory shared with a busmaster DMA NIC. Allows a driver to map a portion of received data to indicate up to interested protocols as a read-only lookahead buffer.
NdisDestroyLookaheadBufferFromSharedMemory | Releases a buffer acquired by calling NdisCreateLookaheadBufferFromSharedMemory.
NdisQueryPacket | Returns a set of information that describes a packet and a pointer to the initial buffer in the chain.
NdisQueryBuffer | Returns the base virtual address and size of a buffer that is mapped by a given buffer descriptor.
NdisQueryBufferOffset | Returns the base virtual address of the range specified in a given buffer descriptor.
NdisGetFirstBufferFromPacket | Returns pointers to the buffer descriptor and base virtual address for the initial buffer chained to a given packet, along with the size of the initial buffer and full buffer, in case the buffer is fragmented.
NdisGetNextBuffer | Returns the next buffer descriptor in a chain, given the current buffer descriptor.
NdisGetPacketFlags | Returns the flags, if any, set by a protocol driver in a given packet.
NdisFreePacketPool | Releases a block of nonpaged pool allocated by calling NdisAllocatePacketPool.
NdisFreePacket | Releases a packet allocated by calling NdisAllocatePacket.
NdisFreeBufferPool | Releases a handle obtained by calling NdisAllocateBufferPool.
NdisFreeBuffer | Releases a buffer descriptor allocated by calling NdisAllocateBuffer.
NdisChainBufferAtFront | Links a given buffer descriptor at the front of a chain of buffer descriptors for a given packet.
NdisChainBufferAtBack | Links a given buffer descriptor at the end of a chain of buffer descriptors for a given packet.
NdisUnchainBufferAtFront | Removes a given buffer descriptor from the front of a chain of buffer descriptors for a given packet and returns a pointer to that buffer descriptor.
NdisUnchainBufferAtBack | Removes a given buffer descriptor from the back of a chain of buffer descriptors for a given packet and returns a pointer to that buffer descriptor.
NdisReinitializePacket | Removes any chained buffers from a given packet and reinitializes it for reuse.
NdisRecalculatePacketCounts | Resets the valid count for a given packet so that the next call to NdisQueryPacket recalculates the counts.


2.4 Miniport NIC Driver Operations

To begin network communications, the miniport must register itself with the NDIS library and initialize its device. The miniport NIC driver also allocates memory for the packets and buffers it needs. After this initialization step, protocol drivers can bind to the NIC managed by the miniport and submit requests to the underlying NIC miniport. To submit requests, a protocol driver calls the NDIS library supplying the handle to the NIC returned from its call to NdisOpenAdapter. The NDIS library, in turn, calls one of the upper-edge functions in the miniport.

A Typical Miniport Operation

Sending a packet across the network is a typical operation involving the miniport. The following describes a common asynchronous operation:

  1. To communicate with its remote-node peer, a highest-level protocol driver on a local node repackages messages from upper level network layers into data packets. The protocol driver then calls NDIS with the packet.

  2. Assuming no intermediate NDIS driver between the highest level protocol driver and the miniport NIC driver, NDIS calls the miniport’s MiniportSend function with a pointer to the packet.

  3. The NIC driver sets up its device, starts the send operation and returns NDIS_STATUS_PENDING to NDIS.

  4. When the NIC has finished transmitting the packet, it generates an interrupt, which causes a call to the NDIS library.

  5. The NDIS library calls the MiniportISR function for the NIC.

  6. The miniport NIC driver performs only the minimum necessary work in MiniportISR and returns, deferring most of the interrupt handling to its MiniportHandleInterrupt function.

  7. The NDIS library’s deferred processing function runs and then calls MiniportHandleInterrupt to complete interrupt processing for the transmission. MiniportHandleInterrupt completes the send and calls NdisMSendComplete. NDIS calls the appropriate protocol driver’s ProtocolSendComplete function. A NIC driver can wait to call NdisMSendComplete until it has completed several transmits.

When return data is available from the remote node, the NIC captures the data and interrupts. NDIS intercepts the interrupt and signals its miniport drive either by calling MiniportISR or MiniportHandleInterrupt. The miniport copies the data to a buffer and calls NdisM...IndicateReceive. NDIS notifies the protocol driver of incoming packets by calling its ProtocolReceive function and subsequently the miniport’s MiniportTransferData function is called to transfer the data to a protocol driver-supplied packet.

NDIS Library Operations

The Ndis Xxx functions for miniports in the NDIS library assist in handling multiprocessor synchronization operations, protocol filters, and hardware problems on the NIC.

  • The NDIS library queues requests from different processors and sends them to the miniport when the driver is free.

  • The NDIS library multiplexes requests from different protocols onto a single miniport NIC driver for a specific network card.

  • The NDIS library has a built-in deadman timer that calls the MiniportCheckForHang function periodically to detect problems on the network interface card.
Miniport Driver Operations

The NDIS library maintains a driver-descriptor block for each miniport NIC driver that registers itself on a network. The descriptor block contains the addresses of the upper-edge functions exported by the miniport. With the exception of MiniportISR, MiniportInitialize, possibly MiniportShutdown and possibly MiniportHalt, miniport processing occurs at IRQL DISPATCH_LEVEL. Typical operations are:

  • Initializing the NDIS library and registering the NIC driver.

  • Registering a network interface card.

  • Sending data.

  • Indicating and, possibly transferring received data.

  • Indicating status.

  • Querying and setting NIC and driver capabilities.

  • Querying global statistics (per network interface card).

  • Resetting a network interface card.

  • Removing a network interface card.

  • Unloading and deregistering drivers and terminating the NDIS library.

For help in following the descriptions, refer to the function definitions in Section 2.3. For more descriptions of the functions, refer to the Network Driver Reference.


2.4.1 Initializing the NDIS Library and Registering Drivers

Initializing NDIS library and registering a miniport

To initialize the NDIS library, the operating system must ensure that the configuration registry has appropriate information for each miniport. When a driver is loaded, the operating system uses the binding information in the registry, creates a driver object for each miniport driver, and calls the DriverEntry function of that driver. The miniport in turn calls NdisMInitializeWrapper, passing the address of the driver object and the associated registry path to the driver-specific information. NdisMInitializeWrapper can then perform its initialization chores.

After NdisMInitializeWrapper returns, the miniport calls NdisMRegisterMiniport to register itself as a driver on the network, passing in the addresses of its entry points. The NDIS library stores the addresses of the MiniportXxx functions that are supplied by the miniport in the driver object.

Before receiving and sending packets on the network, the NDIS library must register all network interface cards.


2.4.2 Registering Network Interface Cards

Registering a network interface card

The NDIS library calls a driver’s initialization function once for each of the installed network cards supported by the driver. MiniportInitialize reads the configuration registry database to determine parameters for the NIC it is initializing, by calling NdisOpenConfiguration, NdisReadConfiguration, and NdisCloseConfiguration. For bus-type-specific configuration information about specialized network cards, EISA-based drivers can call NdisReadEisaSlotInformation, MCA-based drivers can call NdisReadMcaPosInformation, and PCI-based drivers can call NdisReadPciSlotInformation, as examples.

When the miniport has the appropriate configuration data, it stores the information in a previously allocated NIC-specific context structure and then makes a call to NdisMSetAttributes or NdisMSetAttributesEx to pass a handle to this context structure, information about its NIC and if the extended version is called, to change the default behavior of the check-for-hang logic.

After NdisMSetAttributes(Ex) returns, the miniport can further initialize the NIC by calling other NDIS functions appropriate to the adapter, such as NdisRawWritePort Xxx, NdisRawWrite Xxx, and functions to set up device memory and claim I/O resources such as NdisMAllocateSharedMemory, NdisMMapIoSpace, and NdisMRegisterIoPortRange. When it has completely initialized the network card, if its NIC generates interrupts, the miniport must set up interrupt handling by calling NdisMRegisterInterrupt. If the miniport has a non-interrupting device, it calls NdisMInitializeTimer to set up a polling timer for its device.

Finally, the driver must register a shutdown handler by calling NdisMRegisterAdapterShutdownHandler.


2.4.3 Sending Data

MiniportSendPackets receives a pointer to an array of pointer(s) to one or more packets to be sent on the network. When MiniportSendPackets returns, ownership of the array that holds the pointers to the passed packets reverts to the caller.

The NIC driver can extract the OOB media-specific and priority information from the packet descriptor using NDIS macros supplied for this purpose.

The miniport NIC driver must set the send-completion status in each packet before returning from MiniportSendPackets. That is, the NIC driver can complete a packet synchronously and set the status to NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE, (or a driver-determined status other than NDIS_STATUS_PENDING) for that packet. Any such status indicates that the miniport is done with the packet and is returning ownership of the packet to the caller for reuse or to be freed.

If it returns NDIS_STATUS_PENDING in a packet, the driver retains ownership of that packet, and must later call NdisMSendComplete when it is done with the packet. Unless the NIC driver sets the status to other than NDIS_STATUS_PENDING before returning from MiniportSendPackets or calls NdisMSendComplete for those packets for which it returned NDIS_STATUS_PENDING, the driver owns the packet. The miniport calls NdisMSendComplete when it is finished with the packet, meaning it is returning the packet to the caller for reuse or to be freed.

A driver can indicate that it does not have any available send resources for a packet by returning NDIS_STATUS_RESOURCES for that packet. In this case NDIS will queue and then resubmit the packet later when the miniport NIC driver calls either NdisMSendComplete or NdisMSendResourcesAvailable. Once a miniport specifies the return status of NDIS_STATUS_RESOURCES for a packet that is a member of a packet array, NDIS assumes this same status for any subsequent packets in that array.

Sending data synchronously

MiniportSend transmits a single packet through the network interface card onto the network. MiniportSend translates the packet into a data frame and carries out the send operation. Before the miniport actually sends the packet, it can optionally query the packet by calling NdisGetPacketFlags to read information in the Flags member of the packet header structure that a protocol driver passes to the NIC driver. This Flags member contains information about the send that is not included in the packet data. The Flags are used to communicate information between a cooperating protocol and miniport driver that have defined the flags. The driver can also query the media-specific and priority in the OOB data substructure of the packet descriptor, if the driver supports such features.

If MiniportSend completes synchronously, completing the send operation before it returns, a status code other than NDIS_STATUS_PENDING or NDIS_STATUS_RESOURCES is returned. Completing the send means the miniport driver is finished with the packet and is returning it to the caller for reuse or to be freed.

In the asynchronous case, the miniport returns NDIS_STATUS_PENDING and must call NdisMSendComplete when it is done with the packet to indicate that it is relinquishing the packet for reuse or to be freed.

When the miniport calls NdisMSendComplete or NdisMSendResourcesAvailable, it indicates to NDIS that send resources are now available and the miniport is ready to accept a new packet.

The miniport NIC driver returns NDIS_STATUS_RESOURCES to indicate that it does not have enough internal resources to send a packet. In this case, NDIS queues the packet internally and resubmits it to the NIC driver at a later time when more resources are available, either when the miniport calls NdisMSendComplete or NdisMSendResourcesAvailable, whichever occurs first.


2.4.4 Indicating and Transferring Received Data

A miniport NIC driver indicates one or more packets to interested protocol drivers by calling NdisMIndicateReceivePacket. A pointer to the complete packet is passed up to interested protocol drivers. Each of the packets indicated up must be completed separately. The return status is set in each packet before NdisMIndicateReceivePacket returns. Any packet for which NdisMIndicateReceivePacket returns a pending status is kept by the upper-layer(s) and must be returned later to the miniport’s MiniportReturnPacket function. If NdisMIndicateReceivePacket returns any status but NDIS_STATUS_PENDING, ownership of the packet reverts to the miniport NIC driver.

Ownership of the array containing pointers to the indicated packets reverts to the miniport NIC driver on return from NdisMIndicateReceivePacket, regardless of the return status of any individual packet.

Indicating and transferring received data synchronously

When the NIC receives a data packet over the network, its miniport indicates the packet up by calling NdisM(Arc/Eth/Fddi/Tr)IndicateReceive, depending on the type of network (ARCNET, Ethernet, FDDI, or Token Ring). The miniport indicates up a partial packet or possibly the complete packet if it is small enough. NDIS indicates the packet to all interested protocols. If it is a partial packet, and a protocol is interested in the complete packet, it calls NdisTransferData to transfer the remaining net packet data, passing a protocol-allocated packet descriptor.

NDIS then initiates the transfer of the data to the protocol-allocated buffers chained to the packet descriptor by calling the miniport’s MiniportTransferData function. MiniportTransferData copies the received data frames into the buffers that were passed to MiniportTransferData.

MiniportTransferData is another possibly asynchronous function. If the function completes synchronously, it simply writes the received data into the provided packet and returns a status other than NDIS_STATUS_PENDING.

Indicating and transferring received data asynchronously

If MiniportTransferData is unable to complete the transfer data operation before returning, it returns NDIS_STATUS_PENDING. The miniport must call NdisMTransferDataComplete when the data has been transferred.

Once all incoming data frames have been received by the NIC, the miniport calls NdisM(Arc/Eth/Fddi/Tr)IndicateReceiveComplete. This results in a call to the overlying protocol driver to indicate that post-receive processing can be performed.


2.4.5 Indicating Status

Indicating status

When the miniport needs to indicate a status change, it issues a call to NdisMIndicateStatus. NdisMIndicateStatus forwards the call to the bound protocol drivers, which interpret the general status code. Then, the protocol drivers, NDIS library, or the Configuration Manager can log the status change, take corrective action, or both.

A miniport driver must indicate when it is done transmitting status changes. Following one or more NdisMIndicateStatus calls, the miniport calls NdisMIndicateStatusComplete. NDIS forwards the completion call to the protocol drivers so they can perform any necessary postprocessing.


2.4.6 Querying and Setting Miniport NIC Driver Capabilities

2.4.6 Querying and Setting Miniport Driver Capabilities

Querying and setting miniport capabilities (per binding)

For querying and setting NIC operating characteristics and/or statistics information, the NIC driver’s MiniportQueryInformation or MiniportSetInformation is called. The MiniportQueryInformation function fills in results and returns an appropriate status code. If the call is to MiniportSetInformation, the miniport updates its operating characteristics. See the Network Driver Reference for more information on OIDs.

These two functions are potentially asynchronous. If they behave synchronously, they return immediately with a status code other than NDIS_STATUS_PENDING. If asynchronously, the function returns NDIS_STATUS_PENDING and when the miniport later completes the requested operation, it calls NdisMQueryInformationComplete from MiniportQueryInformation or NdisMSetInformationComplete from MiniportSetInformation.

The NDIS library guarantees that the miniport will have only one outstanding query or set request at a time so there is no need for the miniport to queue requests.


2.4.7 Resetting a Network Interface Card

Resetting a network interface card

When the NDIS library determines that a NIC must be reset, either because a send operation or request operation has timed out or because that NIC driver’s MiniportCheckForHang function has returned TRUE, the miniport’s MiniportReset function is called. MiniportReset issues a hardware reset to the network interface card and updates its own software state.

MiniportReset completes synchronously with a success or error status code, or asynchronously with NDIS_STATUS_PENDING. If the function is asynchronous, the miniport later calls NdisMResetComplete to allow postprocessing.

The NIC driver is guaranteed that NDIS will not send any requests or sends while a reset is in progress. Also, NDIS will complete any currently outstanding requests or sends, so the miniport need not explicitly complete them.


2.4.8 Unloading a Miniport NIC Driver

When MiniportHalt is called, it should carry out all operations necessary to unload the driver before returning. Part of unloading is releasing resources that have been claimed by the driver. The diagram above illustrates a possible set of calls that could be made by a MiniportHalt function. The illustrated set is a subset of the possible calls that could be made in such a function. The actual list depends on previous actions of the miniport. These same calls can be made in MiniportInitialize, if it cannot successfully initialize the network adapter, either because of hardware problems or because it cannot acquire a resource the miniport needs. In such a case, MiniportInitialize should unload the driver by backing out its previous actions. Otherwise, MiniportHalt backs out the actions of MiniportInitialize.

MiniportHalt or MiniportInitialize should call NdisTerminateWrapper to return the handle it acquired by calling NdisMInitializeWrapper.

If the miniport NIC driver registered an interrupt, it should call NdisMDeregisterInterrupt. If the miniport NIC driver registered a shutdown handler, it should call NdisMDeregisterAdapterShutdownHandler. If the miniport set up a timer or timers, it should call NdisMCancelTimer for each timer it created.

The miniport also calls NdisFreeMemory to free any memory it allocated by calling NdisAllocateMemory, NdisMAllocateSharedMemory, or NdisMAllocateSharedMemoryAsync. It calls NdisFreePacketPool if it called NdisAllocatePacketPool and so on. If it allocated or reserved any hardware resources, these should be returned, for example, by calling NdisMDeregisterIoPortRange, or NdisMFreeMapRegisters if the NIC is a busmaster DMA NIC.


Chapter 3 DriverEntry and Initialization

This chapter describes the miniport NIC DriverEntry function and the MiniportInitialize function. Both are mandatory functions provided by any NDIS miniport driver in Windows NT® and in any Microsoft operating system that supports Win32.

Other miniport functions that are related to initialization; the reset function, shutdown function and the halt function, as well as the query and set requests are described here.

The contents include:

  • A description of the DriverEntry function in Section 3.1

  • A description of the MiniportInitialize function in Section 3.2

  • A description of the MiniportQueryInformation function in Section 3.3

  • A description of the MiniportSetInformation is called in Section 3.4

  • A description of the MiniportReset function in Section 3.5

  • A description of the MiniportShutdown function in Section 3.6

  • A description of the MiniportHalt function in Section 3.7


3.1 Driver Entry Routine

3.1 DriverEntry Function

Every miniport driver must provide a function called DriverEntry. By convention, DriverEntry is the transfer address for a driver. If a driver does not use the name DriverEntry, the driver developer must define the name of its entry function to the linker(s) so that the transfer address can be linked into the OS loader.

DriverEntry is called by the I/O system when a driver is loaded. This function creates an association between the miniport NIC driver and the NDIS library and registers the miniport’s characteristics with NDIS.

The declaration of a Windows NT DriverEntry is:

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

Two arguments are passed to DriverEntry:

  • A pointer to the driver object, which was created by the I/O system

  • A pointer to the registry path, which specifies where driver-specific parameters are stored.

DriverEntry calls NdisMInitializeWrapper and then NdisMRegisterMiniport. DriverEntry passes both pointers it received to NdisMInitializeWrapper, which returns a wrapper handle. DriverEntry passes the wrapper handle to NdisMRegisterMiniport.

The registry contains data that is persistent across system boots, as well as configuration information generated anew at each system boot. During driver installation, data describing the driver and the NIC is stored in the registry. The registry contains adapter characteristics that are read by the NIC driver to initialize itself and the NIC. See the Kernel-Mode Driver Design Guide for more about the registry and the Programmer’s Guide for more information about the .inf files that install the driver and write to the registry.


3.1.1 Initializing the Wrapper

DriverEntry associates the miniport NIC driver with NDIS by calling NdisMInitializeWrapper, and passing the pointer to the driver object and to the registry path. NdisMInitializeWrapper allocates a structure to represent this association, stores the miniport-specific information it needs in this structure and returns NdisWrapperHandle, which is a handle to this structure representing the miniport NIC driver. The driver must retain and pass this handle to NdisMRegisterMiniport when it registers its entry points, and to NdisTerminateWrapper if it cannot successfully complete its DriverEntry function, if it cannot successfully initialize its NIC, or when it is unloaded. NDIS will use NdisWrapperHandle to identify the miniport. While the miniport must retain this handle, it should never attempt to access or modify the contents of the structure associated with this handle.


3.1.2 Registering Miniport Functions

After DriverEntry has called NdisMInitializeWrapper to create an association with the NDIS library, it must register the miniport’s exported MiniportXxx functions. DriverEntry must allocate, zero-initialize and fill in the members of a structure of type NDIS_MINIPORT_CHARACTERISTICS. NDIS_MINIPORT_CHARACTERISTICS specifies the NDIS version with which the miniport is compatible and the addresses of the required and optional entry points of the upper-edge functions that the miniport provides. The NDIS 4.0 version can be specified with the compiler switch, -DNDIS40_MINIPORTS. Or the version can be set in source files as follows.

#define NDIS40_MINIPORT

This define statement must appear before the #include <ndis.h> statement.

NDIS retains the function addresses to subsequently call the appropriate miniport functions.

Any address member of NDIS_MINIPORT_CHARACTERISTICS must be initialized, that is, set to the address of a miniport-supplied function or to NULL. Valid version numbers are 4.0 or 3.0. The current version is 4.0, although the NDIS library continues to support legacy drivers written for version 3.0. The miniport must supply function addresses for the mandatory entry points or NDIS will unload the driver.

The entry points are passed to NDIS by supplying the symbolic name of the miniport’s functions. These functions are defined in ndis.h and described here as MiniportXxx. Because the miniport supplies the addresses, not the names, of its driver-specific entry points, driver developers are free to name these functions whatever they wish. In this documentation, these functions are given generic names, such as MiniportISR, suggestive of their purpose.

However, the developer can give these functions different names. A reasonable miniport-development strategy is to give these functions names that make debugging easier. For instance, the miniport NIC driver foo.sys could name the entry points FooISR, FooSend, etc.

Some MiniportXxx functions are optional. For instance, if a NIC does not generate interrupts, the miniport must poll the NIC. A miniport that polls does not need a MiniportISR function, a MiniportEnableInterrupt function or a MiniportDisableInterrupt function, but it must have a MiniportTimer function.

The possible entry points in NDIS_MINIPORT_CHARACTERISTICS are shown in the following table. For each entry, the table specifies whether the function is optional or mandatory and the reasons that a miniport might or might not supply a particular optional function.

Table 3.1 Filling in NDIS_MINIPORT_CHARACTERISTICS
Structure Member | Required or Optional | Reason
CheckForHangHandler | Optional | If not supplied, the miniport relies on NDIS to determine if the adapter is hung based on sends and requests that timeout.
DisableInterruptHandler | Optional | If the adapter does not generate an interrupt, this entry point is not required.
EnableInterruptHandler | Optional | If the adapter does not generate an interrupt, this entry point is not required.
HaltHandler | Required |
HandleInterruptHandler | Optional | A miniport will not supply this entry point if its device does not generate interrupts or if the miniport chooses to poll the adapter state with a miniport-supplied timer function.
InitializeHandler | Required |
ISRHandler | Optional | A miniport will not supply this entry point if its device doesn’t interrupt or if it chooses to poll the adapter state with a miniport-supplied timer function.
QueryInformationHandler | Required |
ReconfigureHandler | Not Used |
ResetHandler | Required |
SendHandler | Optional | Not supplied if a LAN miniport provides a SendPackets handler. A LAN miniport must either provide a SendPacketsHandler or a SendHandler. A WAN miniport must have a WanSendHandler.
SendPacketsHandler | Optional | Not supplied if a LAN miniport supplies a SendHandler. A LAN miniport must either provide a SendPacketsHandler or a SendHandler. A WAN miniport must have a WanSendHandler.
SetInformationHandler | Required |
TransferDataHandler | Optional | Must be supplied if a miniport indicates the initial portions of received network packets in lookahead buffers from which received data is copied by interested upper layers. If a LAN miniport does not have a TransferDataHandler, it must have a ReturnPacketHandler. WAN miniports do not have a TransferDataHandler.
ReturnPacketHandler | Optional | Must be supplied if a miniport supports multipacket receive indications by calling NdisMIndicateReceivePacket and always indicates full network packets to higher level drivers. If a LAN miniport does not have a ReturnPacketHandler, it must have a TransferDataHandler. A WAN miniport does not have a ReturnPacketHandler.
AllocateCompleteHandler | Optional | Not supplied if the miniport does not control a busmaster DMA NIC and allocate memory at raised IRQL with NdisMAllocateSharedMemoryAsync.

DriverEntry calls NdisMRegisterMiniport, passing a pointer to its NDIS_MINIPORT_CHARACTERISTICS structure, the size of this structure and the handle returned by NdisMInitializeWrapper that identifies the miniport to NDIS.

NDIS will fail the call if the NDIS version specified in the structure is not a supported version of 4.0 or 3.0 or if the length of the structure does not match the length required for the specified version.

NDIS copies and retains the information passed in the characteristics structure. For instance, NDIS stores the function addresses passed by the miniport and uses these addresses to call the miniport when conditions requiring the attention of the miniport occur.

DriverEntry is the only miniport function that can be specified with the NDIS_INIT_FUNCTION macro. Code marked this way is no longer mapped after the system initialization phase has completed. DriverEntry is called when the miniport is first loaded and will never be called subsequently.

The miniport returns the status of its call to NdisMRegisterMiniport as the return status from DriverEntry.


3.1.3 Filling In NDIS_MINIPORT_CHARACTERISTICS

Since some of the handlers passed in NDIS_MINIPORT_CHARACTERISTICS are optional this section describes the criteria for choosing when to register such an optional MiniportXxx function. The optional MiniportXxx functions are:

  • MiniportISR

  • MiniportEnableInterrupt and MiniportDisableInterrupt

  • MiniportCheckForHang

  • MiniportSend versus MiniportSendPackets

  • MiniportTransferData versus MiniportReturnPacket

  • MiniportAllocateComplete


3.1.3.1 Providing Interrupt Support

A miniport can, but is not always required to, provide an interrupt handler. For instance, a miniport that manages a non-interrupting device and polls its device with a MiniportTimer function cannot depend on interrupts and thus does not have an interrupt handler. A miniport for a NIC with a shared interrupt must always provide an interrupt handler. The definition of the interrupt function follows.

VOID
 MiniportISR(
 OUT PBOOLEAN InterruptRecognized,
 OUT PBOOLEAN QueueMiniportHandleInterrupt,
 IN NDIS_HANDLE MiniportAdapterContext
 );

When any network device interrupts, NDIS fields the interrupt; that is, NDIS is always called first when a NIC interrupts. NDIS determines which miniport the interrupt belongs to. Then NDIS either calls the appropriate miniport’s MiniportISR function, or handles the interrupt for the miniport if the miniport has specified in its call to NdisMRegisterInterrupt that it does not want to have its ISR called. If NDIS does not call a miniport’s ISR function, NDIS disables further interrupts by calling the miniport’s MiniportDisableInterrupt function and then queues the miniport’s MiniportHandleInterrupt DPC function.

If a miniport must or chooses to handle its device interrupts, it registers its ISR handler in NDIS_MINIPORT_CHARACTERISTICS and indicates that it wants its ISR handler called by setting the RequestIsr argument of NdisMRegisterInterrupt to TRUE.

A miniport for a NIC that interrupts must provide an ISR handler if any of the following is true.

  • If the interrupt is shared, a miniport must have an ISR and must set RequestIsr to TRUE when it calls NdisMRegisterInterrupt. In this case, when the device interrupts, the ISR of the driver for each device on the bus is called. Each ISR must determine if the interrupt was generated by its device. A miniport’s ISR returns TRUE in the InterruptRecognized argument to MiniportISR if its NIC generated the interrupt and, in addition, if the miniport wants its DPC queued, it returns TRUE in the QueueMiniportHandleInterrupt argument. If the miniport does not recognize the interrupt, its ISR returns FALSE as soon as possible in the InterruptRecognized argument, and FALSE in QueueMiniportHandleInterrupt. For some types of interrupts, a miniport can return InterruptRecognized equal to TRUE and QueueMiniportHandleInterrupt equal to FALSE, for instance if it can quickly and completely process the interrupt in the ISR.

  • If there is state in the device registers that must be captured at DIRQL, a miniport must have an ISR. When NDIS handles an interrupt, it does not save any NIC state on behalf of the miniport. Therefore, state that disappears when the ISR returns control can be captured only if the miniport registers and uses an ISRHandler.

If a device interrupts but the interrupt is not shared, the miniport can request that NDIS completely process the interrupt by specifying RequestIsr to FALSE in the miniport’s NdisMRegisterInterrupt call. When NDIS gets the interrupt, NDIS calls the miniport’s MiniportDisableInterrupt function, to disable further interrupts, queues the miniport’s MiniportHandleInterrupt function and returns TRUE to notify the I/O system that it recognizes the interrupt. MiniportHandleInterrupt must re-enable interrupts as soon as practical, otherwise, NDIS reenables interrupts by calling the miniport’s MiniportEnableInterrupt function if one exists.

Even if a miniport of a device that interrupts sets RequestIsr to FALSE, the miniport sometimes must provide an ISRHandler. If a NIC can generate an interrupt during the miniport’s initialization or when it is halting, such an interrupt will not be handled by NDIS but will be passed to the miniport. Therefore such a miniport must provide a MiniportISR function.

In summary, a miniport must provide an ISR handler if it manages a device that can interrupt during initialization or during a halt operation, if its NIC shares an IRQ, or if, when its device interrupts, it must capture interrupt-specific data at DIRQL. A miniport can specify that NDIS handle its interrupts if interrupts are not shared, if there is no device state that must be captured in the ISR at DIRQL, and if it can guarantee its NIC will not interrupt during initialization or halting. The miniport requests that NDIS handle interrupts by calling NdisMRegisterInterrupt with RequestIsr set to FALSE and SharedInterrupt set to FALSE.

If a driver manages a device that does not interrupt, it typically polls its device using a timer. This type of driver must initialize any timers it requires for its operation in MiniportInitialize. See Section 3.2.5 for more information.

Enabling and Disabling Interrupts

If the NIC does not generate an interrupt, the miniport does not need to provide a MiniportEnableInterrupt or MiniportDisableInterrupt function. If a miniport always handles its interrupts in a MiniportISR function, and schedules a MiniportHandleInterrupt function, it might not provide specific MiniportEnableInterrupt or MiniportDisableInterrupt functions. Instead, such a miniport can choose to disable interrupts within its ISR and enable them within its DPC, without providing specific functions for this purpose. If a miniport specifies that NDIS handle its interrupts, it must supply a MiniportDisableInterrupt function and if it does not reenable interrupts in its MiniportHandleInterrupt function, it must have a MiniportEnableInterrupt function.

Combining Interrupts and Polling

Sometimes a driver developer can improve the performance of a driver by combining polling a NIC with the use of interrupts. For instance, a driver can be designed to enable interrupts for receive indications but disable them for send-complete indications. A miniport might set a timer and, at timer expiration, check for send completions. In addition, when an interrupt on a receive occurs, the miniport also checks for send-completes. This is a reasonable strategy when network traffic is high: the number of interrupts handled is minimized, and no packets are missed.

A driver that implements this strategy can also sample the traffic on the network and, if traffic is high, it uses the strategy just described. If traffic on the network is low, the driver should enable both send-complete and receive interrupts because the number of interrupts is expected to be lower than in a high traffic environment and the possibility of adversely affecting the driver’s send performance exists.

A driver that uses both polling and interrupts disables interrupts when it is using polling to manage its device and enables interrupts when using interrupts to manage its device. The driver also can have NDIS process its device’s interrupts when they are enabled, subject to the same restrictions as already described. That is, that the interrupt is not shared, that any information needed by the miniport can be captured in its MiniportHandleInterrupt function, and that its NIC will never interrupt during initialization or halting.


3.1.3.2 Serial or Full-Duplex Operation

A miniport can choose to run fully serialized or full-duplex unless it reports its medium type as NdisMediumWan. A WAN miniport driver always runs fully serialized.

If a miniport runs in full-duplex mode, it can send and receive simultaneously (assuming it is running in an SMP machine). If it does not run in full-duplex mode, the miniport is assured that sends and receives are serialized by NDIS.

A miniport that runs full-duplex indicates its support of this option when MiniportQueryInformation is called with the OID_GEN_MAC_OPTIONS request. Then, it sets the NDIS_MAC_OPTION_FULL_DUPLEX flag.


3.1.3.3 Choosing a Send Function

A miniport NIC driver can handle sends in either of two ways:

  1. By supplying a MiniportSendPackets function

    In this case, the miniport receives more than one packet in a single call, passed as an array of pointers to packets to send to its adapter.

  2. By supplying a MiniportSend function

    In this case, a miniport receives a single packet at its MiniportSend function for sending to its adapter.

A miniport NIC driver developer can choose to support one or the other but not both of these functions, that is, when it calls NdisMRegisterMiniport, it supplies either a MiniportSend or a MiniportSendPackets handler but not both. The choice is based on whether the miniport’s NIC can handle multipacket sends, in which case, the performance of the driver is better than if it handles single packets.

If a miniport NIC driver supplies both entry points in NDIS_MINIPORT_CHARACTERISTICS, NDIS will always call MiniportSendPackets to pass send requests to the miniport.

To recapitulate, a miniport NIC driver must decide whether it will accept several send packets or only one packet per send request. If it can accept several send packets, it registers a SendPacketsHandler with NdisMRegisterMiniport. Otherwise, the miniport registers a SendHandler.

Generally, a miniport provides a MiniportSendPackets function if either of the following are true:

  • The NIC has hardware support that can achieve enhanced performance by transmitting several packets at a time; that is, a busmaster DMA device with a ring. For such a NIC, the miniport can set up the ring with some number of packets and transmit them in a single operation.

  • Although the driver’s NIC can only send one packet at a time, improved performance can be attained by queuing several packets internally to the driver and avoiding the latency of additional calls to MiniportSend incurred when packets are sent one at a time.


3.1.3.4 Choosing a Receive Function

Miniports choose between two methods of passing incoming packets to upper layers.

  • Miniports call NdisMIndicateReceivePacket to pass a pointer to an array containing one or more pointers to one or more received packets. When a miniport indicates an array of packet pointers, entire network packet(s) are passed up to the receiving layer. That is, when the miniport calls NdisMIndicateReceivePacket, it makes the entire network packet available to upper layers including any OOB data associated with each packet descriptor. When NdisMIndicateReceivePacket returns, ownership of the array that contains the pointers to packet descriptors reverts to the miniport. Actual packet descriptors and the miniport-allocated resources they describe may or may not be returned. Any upper layer can choose to keep a packet and return it later, consume the data in the packet and return it immediately, or return the packet immediately unread because it is not interested in the data. This means that when NdisMIndicateReceivePacket returns, the miniport must not reference any of the individual packets whose status in the OOB data is set to NDIS_STATUS_PENDING.

  • Miniports call a filter-specific NdisM Xxx IndicateReceive with a pointer to a lookahead buffer. An interested protocol can either copy the packet (if the packet is small and the entire packet is indicated up) or call NdisTransferData from its ProtocolReceive function and let the MiniportTransferData function copy the entire packet.

If a miniport indicates an array of packets by calling NdisMIndicateReceivePacket, ownership of the packets can pass to the receiving protocol driver. Such a miniport must provide a MiniportReturnPacket function. MiniportReturnPacket will be called for each packet passed up to a protocol driver for which the protocol driver returns NDIS_STATUS_PENDING in order to return ownership of the packet resources to the miniport after the protocol has consumed the data. Any miniport that supports multipacket receive indications:

  • Must allocate enough packet descriptors and insure that it has available buffers while waiting for an upper layer to return previously indicated packets. Packet descriptors must be allocated from packet pool.

  • Must allocate enough memory to contain one or more complete network packets and buffer descriptors mapping the memory so that it can pass a packet or packets to upper layers. Miniports that force an interested protocol to copy received data only need a buffer to hold the received data or lookahead buffer, which in some cases is buffer space on the NIC. Buffer descriptors must be allocated from buffer pool.

If a miniport uses NdisM Xxx IndicateReceive to indicate a receive packet up to an upper-layer, that miniport must provide a MiniportTransferData function. The miniport retains ownership of the memory containing the indicated data and MiniportTransferData will be called to move the received data from the miniport’s buffer to a packet supplied by the receiving protocol driver. The buffer containing the received data can be either a staging buffer or can be buffer space on the NIC.

A miniport chooses to indicate an array of packets by calling NdisMIndicateReceivePacket

  • If its NIC is a high performance busmaster DMA device that receives data in a ring of buffers for which multiple packets can be allocated, filled, and indicated up

  • Or, the miniport supports passing media-specific information with incoming packets

    In this case, the miniport must pass an entire packet, described by a packet descriptor with an associated OOB data block, up to an interested protocol(s) since the media-specific information is stored in the OOB block. A miniport for a slow NIC that wants to pass media-specific information may choose the multipacket receive technique but indicate up one-element packet arrays to avoid queuing receives.


3.1.3.5 AllocateCompleteHandler

A busmaster DMA NIC miniport must supply a MiniportAllocateComplete function if it calls NdisMAllocateSharedMemoryAsync to allocate buffer space. NdisMAllocateSharedMemoryAsync can be called at raised IRQL, for instance from a driver’s MiniportHandleInterrupt function. Rather than forcing a driver to allocate all the buffer space it will ever require in MiniportInitialize, a driver can call NdisMAllocateSharedMemoryAsync when it runs short of buffers. Usually only a miniport that calls NdisMIndicateReceivePacket will have such a need.

Since a miniport that indicates up a complete packet, typically a miniport that manages a busmaster DMA device with a ring buffer, relinquishes ownership of indicated packets to upper layers, it relies on the upper layers to return the packets promptly. If the miniport runs short of buffers for received data because packets are not returned promptly, it can allocate a buffer in its MiniportHandleInterrupt function at raised IRQL by calling NdisMAllocateSharedMemoryAsync. The driver must then provide an AllocateCompleteHandler to be called when the asynchronous allocation is complete.


3.1.3.6 CheckForHang Handler

A miniport optionally supplies a MiniportCheckForHang function when it calls NdisMRegisterMiniport. If a miniport does not register such a handler, it relies on NDIS to detect that its NIC is hung.

If a miniport supplies a MiniportCheckForHang function, this function will be called, by default, every two seconds or at a driver-requested timeout interval. The miniport’s MiniportCheckForHang function determines if its hardware is hung. MiniportCheckForHang returns TRUE if it detects that the NIC is hung, otherwise it returns FALSE.

If a miniport does not register a MiniportCheckForHang function, NDIS concludes that a miniport is hung if:

  • A send packet has been pending longer than twice the timeout period. The miniport was passed a packet to send, it returned NDIS_STATUS_PENDING, and did not call NdisMSendComplete for the pending packet in a period equal to twice the timeout period.

  • A request sent to MiniportQueryInformation or MiniportSetInformation has not completed in a period equal to twice the timeout period.

When a miniport calls NdisMSendComplete, NDIS updates its internal state information it keeps to track pending sends so that it does not incorrectly conclude that a NIC is hung.

A NIC driver can change the default behavior of NDIS with respect to timeout in several ways by calling NdisMSetAttributesEx rather than NdisMSetAttributes in its MiniportInitialize function. By calling NdisMSetAttributesEx, the miniport can:

  • Change the default two-second timeout interval to a driver-specified interval. NDIS will call the driver’s MiniportCheckForHangHandler at this new interval. Miniports for slow NICs can call NdisMSetAttributesEx to lengthen the timeout period to match the device’s real behavior. This also changes the timeout period used by NDIS internally.

  • Specify that NDIS ignore packet timeout altogether.

  • Specify that NDIS ignore request timeout altogether.

If the miniport specifies that NDIS should ignore packet timeout and request timeout, the miniport is responsible for detecting when a NIC is hung.

Since NdisMSetAttributesEx must be called in MiniportInitialize, this is the only time that a driver can change the default behavior of NDIS with respect to timeouts.

If MiniportCheckForHang returns TRUE, or if a send or a request has pended more than twice the timeout period, NDIS calls MiniportReset.


3.2 Initialization Function

MiniportInitialize is the first NIC driver function called after DriverEntry. MiniportInitialize is called once for each adapter in the machine that is supported by the miniport NIC driver. If a miniport has one adapter to manage, its MiniportInitialize is called once, if it has two, it is called twice, and so on. The miniport is called only for those adapters of a type it supports; that is, the miniport will not be called for an adapter it does not know how to manage.

If a new NIC supported by a previously loaded miniport is plugged into the system, that miniport’s MiniportInitialize function will be called to initialize the newly installed device. A consequence is that a MiniportInitialize function and any functions it calls, all of which run at IRQL PASSIVE_LEVEL, can be specified as pageable. Code is specified as pageable by using the NDIS_PAGABLE_FUNCTION macro. Initialization code cannot be specified with the NDIS_INIT_FUNCTION macro, since code marked this way is no longer mapped once the initial system boot is complete. Only the DriverEntry function and any functions only called from DriverEntry can be passed to the NDIS_INIT_FUNCTION macro. For more information about specifying code as pageable or as initialization-only code, see The Programmer’s Guide.

The declaration of the initialization function is as follows:

NDIS_STATUS
 MiniportInitialize (
OUT PNDIS_STATUS OpenErrorStatus,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE ConfigurationHandle
 );

The following arguments are passed to MiniportInitialize.

  • An array of NdisMedium Xxx at MediumArray. The miniport must choose the medium it supports or prefers and return the index of that medium in SelectedMediumIndex. If the miniport does not find a medium it can support in MediumArray, it must return a failure status of NDIS_STATUS_UNSUPPORTED_MEDIA.

  • A handle, MiniportAdapterHandle, that NDIS uses to refer to the miniport. The miniport must retain this handle so that it can pass it in subsequent calls to NDIS, for instance, in such calls as NdisMRegisterAdapterShutdownHandler and NdisMInitializeTimer.

  • A configuration handle that identifies the registry key containing NIC-specific information associated with this miniport. The miniport must retain and pass this handle to NdisOpenConfiguration to open the Parameters subkey of the registry that describes the miniport’s adapter. The installation script for a NIC driver, at a minimum, must store a NIC’s I/O bus type and, if not an ISA bus, bus number in this subkey.
Experimental Medium Types

NDIS media types and associated identifiers are contained in the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NDIS
\MediaTypes. This key contains a current list of the system-defined media types. Only media found in this key will be passed to MiniportInitialize at MediumArray. To specify a currently unsupported experimental media type and identifier for an under-development miniport, the experimental media type can be added to the registry as already described in Part 1, Chapter 3.


3.2.1 Keeping State

Typically, a miniport allocates memory for a driver-internal structure in which it stores adapter-specific context, such as the miniport adapter handle provided by NDIS in the initialization call, the configuration information derived from calling NdisReadConfiguration and any other state information the miniport requires. For instance, a driver that allocates a staging buffer for received data in its MiniportInitialize function could attach the buffer to this structure. If the driver queues sends, the listhead for this queue could be part of this structure.

A miniport calls NdisAllocateMemory to allocate storage for such a structure and NdisZeroMemory to clear this storage before initializing it. The miniport subsequently passes a pointer to this structure to NdisMSetAttributes or NdisMSetAttributesEx. NDIS retains the pointer to this adapter-specific context and passes it to the miniport’s MiniportXxx functions, for example MiniportSend, MiniportISR, MiniportInterruptHandler, and others. Therefore, a miniport uses this context area to maintain whatever adapter-specific runtime state it requires to process sends, receives, and requests.

A miniport allocates such a structure for each adapter it supports. If the miniport supports more than one adapter, when the miniport’s MiniportInitialize function is called once for each adapter, it will create an adapter-specific context area each time it is called.


3.2.2 Reading Configuration Information

For all types of adapters, MiniportInitialize should do the following:

  • Allocate and zero-initialize a structure to contain adapter-specific information as already described.

  • Call NdisOpenConfiguration to open and get a handle to \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DeviceName\Parameters in order to read whatever the installation script wrote to the registry about the adapter managed by the miniport.

  • Call NdisReadConfiguration to read the device-specific value entries in the subkeys of the associated Parameters stored in the registry. The miniport calls NdisReadConfiguration once for each keyword to retrieve the associated value. NdisReadConfiguration finds the value entry name that matches the keyword and returns the value and NDIS_STATUS_SUCCESS to the miniport.

  • Obtain bus-type-specific information with bus-type-specific functions if such functions exist for the type of I/O bus on which the adapter is connected.

    For example, if the bus-type is EISA, the miniport calls NdisReadEisaSlotInformation to obtain such information as the major and minor revision numbers of the adapter or information about the IRQ. This EISA-specific information is defined in the NDIS_EISA_SLOT_INFORMATION structure and a single NDIS_EISA_FUNCTION_INFORMATION structure. The NDIS_EISA_SLOT_INFORMATION structure specifies revision number, compressed device identification, and the number of possible functions. If the device is configurable it will have more than one EISA function. Each NDIS_EISA_FUNCTION_INFORMATION structure describes such things as IRQ, the bus-relative memory range used by the device, etc. If the NIC has only one function, the miniport calls NdisReadEisaSlotInformation. If the number of functions are not known, a miniport can call NdisReadEisaSlotInformation to determine the number of functions and then call NdisReadEisaSlotInformationEx with a buffer big enough to contain all the possible functions (configurations). Or, if the number of possible configurations the device has is known, the miniport can just call NdisReadEisaSlotInformationEx without making the other call first. Once read, the miniport picks a configuration from the set of possible configurations and uses it in subsequent calls to claim hardware resources in the registry, to program the device, and to register the interrupt to support that configuration.

  • If the card is software-programmable, call NdisReadNetworkAddress to read the network address of the adapter from the registry so that the address subsequently can be written to the adapter EPROM.

  • After a miniport has obtained the values it requires from the registry, call NdisCloseConfiguration. When NdisCloseConfiguration returns, the handle to the registry subkey is no longer valid.

After the miniport has read the information it needs to describe the characteristics of the NIC and stored the information as required into the adapter-specific structure, the miniport calls NdisMSetAttributes or NdisSetAttributesEx. The miniport passes a handle to the adapter-specific structure it uses to represent its NIC, the bus-type for its NIC, and a Boolean that specifies whether its NIC is a busmaster DMA device. A driver calls NdisMSetAttributesEx instead of NdisMSetAttributes to change the default behavior of NDIS with respect to timeouts and Token Ring errors. See the definition of these functions in the Network Driver Reference for details. NDIS will pass the handle to this adapter-specific context area back to the miniport when it calls any MiniportXxx function.


3.2.3 Claiming Resources

After a miniport has determined its hardware resources and has called NdisMSetAttributes(Ex), it claims the resources it will use in the registry. The miniport must call NdisMSetAttributes(Ex) before the following steps are taken. Every miniport, no matter what type of NIC it manages, must at a minimum:

  • Allocate the memory it needs to manage its device. This memory is in addition to that allocated for its adapter-specific structure. For instance, it might allocate memory to receive incoming frames and perhaps staging buffers for outgoing data.

  • Register the I/O ports it is claiming for its NIC by calling NdisMRegisterIoPortRange.


3.2.3.1 Allocating Memory

Generally a miniport should allocate any buffers, packet descriptors and buffer descriptors that it requires to send and receive data at initialization time. The type and amount of memory that must be allocated is dependent on the type of NIC as well as dependent on the type of receive paradigm supported by the driver.

Memory can be shared or unshared. Generally, miniports allocate unshared memory. A driver allocates shared memory only when that memory will be accessed by a busmaster DMA NIC. Miniports for busmaster DMA NICs must allocate shared memory for any buffers that will be accessed both by the miniport and by the NIC.

Memory can be cached or noncached, but noncached memory is a scarce system resource and is, therefore, harder to allocate. A miniport must ensure that any cached memory it uses is coherent before reading received data from the buffer or before sending data from the buffer. For example, a busmaster DMA NIC shares cached memory with the miniport for that NIC. The miniport for such a NIC must flush its buffers to ensure cache coherency on sends and receives.

Cache coherency is no problem for a miniport that manages a slave DMA device because the NDIS calls that such a driver makes do the flush on behalf of the caller. Thus, such a miniport for a slave DMA NIC need not make explicit flush requests.

Cached versus noncached memory is not an issue for miniports for non-DMA NIC types.

Generally, a miniport for a DMA NIC should allocate cached memory. There are exceptions that are noted in Chapter 4, Section 4.6.4.

The functions that can be called to allocate memory include:

  • NdisAllocateMemory allocates a buffer whose length is specified by the caller. The memory is not shared, but it can be cached or noncached depending on the caller’s request. The memory can be contiguous and within a particular physical address boundary.

  • NdisMAllocateSharedMemory allocates a piece of shared memory of a length specified by the caller. The memory can be cached or noncached. This call returns both a physical address pointer that the NIC uses and a virtual address pointer that the miniport uses. NdisMAllocateSharedMemory must be called after NdisMSetAttributes(Ex) and after NdisMAllocateMapRegisters.

  • NdisAllocatePacketPool allocates and initializes a block of nonpaged system memory from which packet descriptors are allocated.

  • NdisAllocatePacket allocates a packet descriptor from the packet pool allocated by calling NdisAllocatePacketPool.

  • NdisAllocateBufferPool returns a handle with which the caller can allocate buffer descriptors with NdisAllocateBuffer.

  • NdisAllocateBuffer allocates memory for a single buffer descriptor from the memory allocated by calling NdisAllocateBufferPool.

A miniport that will indicate received data using NdisMIndicateReceivePacket must indicate that received data mapped by buffer descriptors that are chained to a packet descriptor, which is a structure of type NDIS_PACKET. Therefore, such a miniport, in its MiniportInitialize function calls NdisAllocatePacketPool. It calls NdisAllocatePacket to allocate a packet descriptor for each packet it requires to indicate a receive, NdisAllocateBufferPool and then NdisAllocateBuffer to allocate a buffer descriptor mapping each buffer chained to the packet descriptor, and NdisAllocateSharedMemory or NdisAllocateMemory(Async) to allocate the actual buffers. A driver must create packets by explicitly calling NdisAllocatePacketPool, NdisAllocatePacket, NdisAllocateBufferPool, and NdisAllocateBuffer. It is an error for a driver to create packet descriptors outside of NDIS.

Busmaster DMA NIC miniports typically use NdisMIndicateReceivePacket since they can realize increased performance by allocating and filling many packets at one time. After it has called NdisMSetAttributes(Ex), the miniport for such a device calls NdisMAllocateMapRegisters to reserve some number of map registers and, then, calls NdisMAllocateSharedMemory to allocate shared memory for its receive buffers since the driver shares that memory with the NIC.

Miniports for nonDMA NICs generally use the NdisM Xxx IndicateReceive functions for indicating up received data one buffer at a time which requires interested protocols to request and copy any additional received data into buffers the protocol driver supplies. If the miniport for a nonDMA NIC supports media-specific information and priority that must be passed to bound protocols, it must create a complete packet descriptor for the buffered received data and indicate it up by calling NdisMIndicateReceivePacket. In either case, such a miniport allocates nonshared memory for buffers with NdisAllocateMemory since the NIC driver does not share the memory with the NIC. If the NIC driver indicates the entire packet up, it must allocate buffer(s) to stage received data and chain them to a packet descriptor before indicating the packet. If the NIC driver forces interested upper layers to copy the data, it may or may not stage the data into a receive buffer.

A miniport for a busmaster DMA NIC allocates shared memory in its MiniportInitialize function. On a send, the miniport puts the data to be sent into the shared memory; the NIC’s on-board DMA controller reads the data from the host memory and sends it out across the network. On a receive, the DMA controller receives the incoming data into the memory and the miniport indicates the received data to interested protocol drivers. Therefore the memory must be shared between the busmaster DMA controller and the miniport since both can access the memory without the intervention of the other. When incoming data is received by the NIC, transfer of the data to this memory is done by the NIC’s DMA controller without the knowledge or control of the miniport, which is usually notified by a receive interrupt when data is available to be indicated up.

A miniport for a PIO NIC either reads received data from the NIC directly into an interested protocol’s buffer or into a staging buffer it allocates by calling NdisAllocateMemory. The miniport calls NdisRawReadPort Xxx to read the data from the port to a receive buffer either in its MiniportTransferData function if the buffer belongs to the protocol or in its MiniportHandleInterrupt function into a staging buffer. On a send, the miniport uses NdisRawWritePort Xxx to write the data from the packet sent to its MiniportSend or MiniportSendPackets function into the NIC registers it claimed at initialization time.

When a miniport controls a memory-mapped NIC, the NIC receives incoming data into its on-board memory and signals the miniport; or the miniport writes outgoing data to this on-board memory and causes the device to send the data out on the network. The miniport must therefore map the NIC’s on-board memory to system-space memory on the host so that it can transfer incoming data from the board, and outgoing data to the board. Transfer is done using NdisMoveToMappedMemory and NdisMoveFromMappedMemory. Unlike a busmaster DMA miniport, the memory to which and from which the driver transfers data is part of the NIC, but the driver uses mapped virtual system-space addresses. When data is received, the miniport indicates the receive as one or more virtual ranges mapped by buffer descriptors and when/if its MiniportTransfer function is called, it copies the data from the on-board memory to the protocol driver’s supplied buffer. If no protocol driver transfers the data, it is discarded when the miniport returns from its MiniportHandleInterrupt function or its polling MiniportTimerXxx function. If the miniport uses a staging buffer, it is allocated with NdisAllocateMemory.

A miniport that manages a slave DMA NIC calls NdisAllocateMemory to allocate memory from which it transfers data to the system DMA controller on a send, and to which received data is transferred on a receive.


3.2.3.2 Registering Ports

A miniport must claim any hardware resources it needs to manage an adapter. A miniport, regardless of NIC type, calls NdisMRegisterIoPortRange to claim the I/O port addresses it requires to write to or read from the port using NdisRawWrite(Read)Port Xxx. A call to NdisMRegisterIoPortRange does two things:

  1. Claims the I/O port for the NIC in the registry so no other device can use it.

  2. Maps the I/O port so that the miniport can write to and read from the device registers using NdisRawWrite(Read)Port Xxx. The NdisRawWrite(Read)Xxx functions cannot be used until NdisMRegisterIoPortRange completes successfully.

If a miniport is not ready to claim an I/O port but simply wants to perform an operation such as checking whether a device is present, the miniport should call NdisImmediateRead(Write)Port Xxx. NdisImmediateRead(Write)Port Xxx will temporarily claim, map, perform the requested operation and release the port before returning.


3.2.3.3 Busmaster DMA Device Initialization

A miniport that uses busmaster DMA to send and receive data must allocate and set up a mapping register or registers to map the shared memory buffers used for DMA. A miniport calls NdisQueryMapRegisterCount to determine how many map registers are available for the whole system. The miniport then calls NdisMAllocateMapRegisters to claim a number of these available system map registers. A miniport should allocate only as many map registers as it might need for a single DMA transfer since the registers are a shared system resource. Claiming all of the registers can adversely affect overall system performance and allocating more than can be used on a single transfer simply ties up resources that could be used by another driver.

When the miniport calls NdisMAllocateMapRegisters it must specify the largest buffer that must be mapped, corresponding to the maximum DMA capacity of the NIC. Allocating map registers is done once by a miniport, during initialization. After the registers are successfully allocated, they belong to the miniport until deallocated when the driver unloads.

A call to allocate shared memory will fail unless the caller has first allocated map registers. The amount of memory allocated for packet pool, buffer descriptors, and shared memory allocated for buffers, should be driven by the number of map registers and, possibly, by whether the driver is running on a uniprocessor or SMP machine. The type of machine is determined by calling NdisSystemProcessorCount. Typically, machines with more than one processor are server machines that will handle a higher level of network traffic than a uniprocessor machine that is, most likely, a client machine. This information is helpful in deciding how many packets and buffers to preallocate.

Besides the memory allocated for its adapter-specific structure, a busmaster DMA miniport should also set up the packet pool it will use for indicated packets. During initialization, the miniport must:

  • Allocate packet pool for packet descriptors by calling NdisAllocatePacketPool. It must call NdisAllocatePacket to allocate single packet descriptors from the packet pool.

  • Allocate buffer pool for buffer descriptors to map the buffers that will make up the packet by calling NdisAllocateBufferPool.

  • Allocate shared memory for the number of buffers that will be used for receiving data incoming from the network by calling NdisMAllocateSharedMemory. The memory must be shared memory because it is accessible both by the NIC and the miniport. When the memory is used for sending and receiving, the NIC is using host memory. The NIC uses the physical address of the memory; the miniport uses the virtual address.

If the miniport supports multipacket receive indications up to NDIS, it must also allocate memory for the array used to pass the pointers to the packets, which can be of any type.


3.2.3.4 Programmed I/O Device Initialization

A miniport that manages a PIO device performs sends and receives by writing and reading of data to and from a port range. To do this, the miniport must read the registry by making Ndis Xxx Configuration calls or a bus-type-specific Ndis Xxx call, and then claim the I/O ports it requires by calling NdisMRegisterIoPortRange. Then the driver uses NdisMRead(Write)Port Xxx calls to move data.

Unless the driver supports multipacket receive indications, any additional memory it needs to manage its device can be nonshared, allocated by calling NdisAllocateMemory. If the driver indicates using NdisMIndicateReceivePacket, it must allocate at least one packet as described in Section 3.2.3.3.


3.2.3.5 Adapter-Shared Memory Device Initialization

3.2.3.5 Memory-Mapped Device Initialization

Like a PIO device, a miniport for a NIC with on-board RAM must call NdisMRegisterIoPortRange to register its claimed port address(es). This call returns a mapped virtual port address(es) that a miniport can use to access the registers on the adapter. The port addresses mapped by this function are only used to manage the device with NdisRawWrite(Read)Port Xxx calls (clear interrupts, set and clear requested functions) but not to move data as is done for a PIO device.

To move data to and from the board, a miniport for this type of device must map the NIC device memory onto a host address space. During initialization, the miniport reads the physical address and size of the on-device memory from the registry by calling NdisReadConfiguration or bus-type-specific Ndis Xxx call(s). Once the configuration is determined, the miniport must call:

  • NdisSetPhysicalAddressHigh and NdisSetPhysicalAddressLow to set the 32 bit physical address and then call

  • NdisMMapIoSpace with this physical address to map the device memory into a miniport-accessible host address space. NdisMMapIoSpace returns a virtual address pointer with which the miniport can refer to this device memory.

Such a driver uses NdisRead(Write)Register Xxx to read from or write to the NIC’s registers.

If the driver indicates using NdisMIndicateReceivePacket, it must allocate at least one packet with chained buffer(s) as described in Section 3.2.3.3.


3.2.3.6 Slave DMA Device Initialization

A miniport that manages a slave DMA device calls the following:

  • NdisMRegisterDmaChannel to claim a channel to use for sends and receives

  • NdisMAllocateSharedMemory to allocate memory from which it will write data to and read data from the DMA controller.

The memory is mapped for the DMA transfer when the send or receive occurs. If the driver indicates using NdisMIndicateReceivePacket, it must allocate at least one packet as described in Section 3.2.3.3.


3.2.4 Registering An Interrupt

After a miniport has called NdisMSetAttributes or NdisMSetAttributesEx, claimed its hardware, and claimed other I/O resources it needs to use that device, if the adapter it manages interrupts, the miniport must call NdisMRegisterInterrupt to identify the interrupt its device uses and to claim an interrupt vector/level for its NIC. The miniport allocates resident storage for an opaque variable of type NDIS_MINIPORT_INTERRUPT and passes a pointer to the variable to NdisMRegisterInterrupt. The miniport passes information about its interrupt such as the bus-relative interrupt vector and interrupt level, whether its MiniportISR should be called at every interrupt, whether its interrupt is shared, and its interrupt mode.

The interrupt mode specified by the miniport is either NdisInterruptLevelSensitive or NdisInterruptLatched. NdisMRegisterInterrupt makes HAL calls on behalf of the miniport to claim an interrupt vector and then connects to the interrupt by calling the I/O system. When NDIS connects to the interrupt, it supplies its own ISR function to be called first on any interrupt. Therefore, when an interrupt occurs, control always goes to NDIS first. Within the NDIS ISR, the decision is made (based on the value of RequestIsr supplied to NdisMRegisterInterrupt) whether to pass the interrupt to the miniport’s MiniportISR or MiniportDisableInterrupt function or to handle the interrupt within NDIS and then to queue MiniportHandleInterrupt.

NdisMRegisterInterrupt has no effect on the state of the NIC. However, since the miniport is now connected to the interrupt, when NdisMRegisterInterrupt returns, the miniport can possibly receive interrupts depending on the characteristics of the NIC. If its NIC could generate interrupts immediately, the miniport should perform whatever adapter-specific steps it requires to prepare itself to handle interrupts before calling NdisMRegisterInterrupt. That is, the miniport must create and initialize any structures it needs, perhaps initializing any spin locks it needs to synchronize access to shared data. When a miniport has registered its interrupt and is prepared to handle interrupts, the miniport can enable interrupts in a device-specific manner, for instance by resetting the NIC.

It is good practice for a miniport to test its device during initialization to make sure the device is capable of generating interrupts. When NdisMRegisterInterrupt returns, a miniport can perform this test. If the device is not operating correctly, the miniport should free all claimed resources and return NDIS_STATUS_FAILURE from MiniportInitialize so that the miniport can be unloaded by the system.

If the miniport wants to change the default timeout, it must do this by calling NdisMSetAttributesEx. This function must be called in MiniportInitialize. After MiniportInitialize returns, the driver cannot change the default timeout behavior. See the Section 3.1.3.6 for a discussion of the miniport’s CheckForHang function.

The miniport should finally call NdisMRegisterAdapterShutdownHandler to register a shutdown handler function with NDIS, unless the miniport is a WAN miniport. WAN miniports do not register such a handler.

A miniport passes NdisMRegisterAdapterShutdownHandler, the symbolic address of its MiniportShutdown function, the miniport adapter handle assigned by NDIS and context to be passed to MiniportShutdown when it is called. NdisMRegisterAdapterShutdownHandler is typically called last from MiniportInitialize, just before the miniport returns from MiniportInitialize.


3.2.5 Polling Timers

Some miniports control devices that do not generate interrupts, that is, devices that must be polled. Such a miniport does not call NdisMRegisterInterrupt. Rather, the miniport allocates one or more timer objects that will be used to poll the device.

Some miniports for NICs that interrupt choose to turn off some or all interrupts and poll their device instead. Such a miniport must also set up a timer or timers.

MiniportInitialize calls NdisMInitializeTimer to initialize a timer object for each timer the miniport will require. NdisMInitializeTimer associates a miniport-supplied timer function with the timer being initialized. When the timer expires, this MiniportTimer function is called.

NDIS provides two types of timers, one-shot timers and periodic timers. When a one-shot timer is set by calling NdisMSetTimer, and the timer expires, it must be reset by a call to NdisMSetTimer. This requirement can cause the wait time to drift due to the time it takes on each expiration to reset the timer. A periodic timer, once set, continues to expire every requested time interval. Such a periodic timer is well suited to polling within a miniport. Once set with a call to NdisMSetPeriodicTimer, the timer will continue to expire causing the associated timer function to be called until the timer is reset in a subsequent call to NdisMSetPeriodicTimer or cleared with a call to NdisMCancelTimer.

If a timer function shares resources with any other miniport function, access must be protected.

If the function(s) with which a timer function shares resources runs at the same IRQL (DISPATCH_LEVEL), access to those resources is synchronized with a spin lock. Any function that needs to acquire the previously initialized spin lock does so by calling NdisDprAcquireSpinLock and NdisDprReleaseSpinLock, both of which are faster than calling NdisAcquireSpinLock and NdisReleaseSpinLock.

If the timer function shares resources with MiniportISR or MiniportDisableInterrupt, access must be synchronized by calling NdisMSynchronizeWithInterrupt. The miniport performs any timer-related operations on the shared resources, typically device registers, within its MiniportSynchronizeISR function, whose address is passed to NdisMSynchronizeWithInterrupt.


3.2.6 Synchronization During Initialization

When a miniport initializes its NIC, it might need to wait for a state change before continuing execution. NDIS provide two mechanisms that can be used.

MiniportInitialize can call NdisMSleep to delay execution for a specified interval expressed in microseconds. When MiniportInitialize calls NdisMSleep, it is put into a suspended state. When the sleep interval expires, MiniportInitialize resumes execution. MiniportInitialize should not call NdisStallExecution, a function that is only called at raised IRQL and only for intervals less than 50 microseconds.

MiniportInitialize can also synchronize with events. MiniportInitialize can create an event by calling NdisInitializeEvent. Then after MiniportInitialize writes to its NIC, it calls NdisWaitEvent. When an interrupt occurs and MiniportHandleInterrupt runs, it clears the interrupt on the NIC and sets the event by calling NdisSetEvent. MiniportInitialize, which was waiting on the event, now has its wait satisfied, so it clears the event by calling NdisResetEvent and continues with its execution.


3.2.7 Handling Errors During Initialization

If a miniport returns a failure status at any time during its initialization; for instance, if it cannot obtain the resources it needs, cannot claim the hardware resources its NIC needs in the registry, or if it cannot find a medium it supports, it must take care to back out actions it has taken that affect the overall system. The miniport should deallocate any memory it has allocated by calling NdisFreeMemory, it should release I/O resources it has claimed by calling NdisMDeregisterIoPortRange and if it has called NdisOpenConfiguration, it should call NdisCloseConfiguration. If the miniport has allocated map registers, or a DMA channel, it should call NdisMFreeMapRegisters or NdisMDeregisterDmaChannel.

A miniport that fails during initialization can record the problem in the error log by calling NdisWriteErrorLogEntry with an error code and error-type-specific values.


3.3 Query Information Handler

If MiniportInitialize returns NDIS_STATUS_SUCCESS, NDIS calls MiniportQueryInformation to get and save the operational characteristics of the driver. The miniport must support all the mandatory general and media-specific operational OIDs.

The set of mandatory general operational OIDs include:

  • OID_GEN_SUPPORTED_LIST

  • OID_GEN_HARDWARE_STATUS

  • OID_GEN_MEDIA_SUPPORTED

  • OID_GEN_MEDIA_IN_USE

  • OID_GEN_MAXIMUM_LOOKAHEAD

  • OID_GEN_MAXIMUM_FRAME_SIZE

  • OID_GEN_LINK_SPEED

  • OID_GEN_TRANSMIT_BUFFER_SPACE

  • OID_GEN_RECEIVE_BUFFER_SPACE

  • OID_GEN_TRANSMIT_BLOCK_SIZE

  • OID_GEN_RECEIVE_BLOCK_SIZE

  • OID_GEN_VENDOR_ID

  • OID_GEN_VENDOR_DESCRIPTION

  • OID_GEN_CURRENT_PACKET_FILTER

  • OID_GEN_CURRENT_LOOKAHEAD

  • OID_GEN_DRIVER_VERSION

  • OID_GEN_MAXIMUM_TOTAL_SIZE

  • OID_GEN_MAC_OPTIONS

  • OID_GEN_MEDIA_CONNECT_STATUS

  • OID_GEN_MAXIMUM_SEND_PACKETS

In particular, the miniport’s MiniportQueryInformation function must be prepared to respond to OID_GEN_MAXIMUM_LOOKAHEAD, OID_GEN_MAXIMUM_SEND_PACKETS, and OID_GEN_MAC_OPTIONS.

If the miniport indicates received data by calling Ndis Xxx IndicateReceive, it should respond to OID_GEN_MAXIMUM_LOOKAHEAD with the maximum number of bytes the NIC can provide as lookahead data. If that value is different from the size of the lookahead buffer supported by bound protocols, NDIS will call MiniportSetInformation to set the size of the lookahead buffer provided by the miniport to the minimum of the miniport and protocol(s) values.

If the driver always indicates up full packets with NdisMIndicateReceivePacket, it should set this value to the maximum total packet size, which excludes the header.

If a miniport registers a MiniportSendPackets function, MiniportQueryInformation will be called with the OID_GEN_MAXIMUM_SEND_PACKETS request. The miniport must respond with the maximum number of packets it is prepared to handle on a single send request. The miniport should pick a maximum that minimizes the number of packets that it either has to return to NDIS or queue internally because it has no resources (its device is full). A miniport for a busmaster DMA device should attempt to pick a value that keeps its device filled under anticipated loads.

When MiniportQueryInformation is called with OID_GEN_MAC_OPTIONS, the bit mask returned by MiniportQueryInformation will specify which optional operations the miniport performs. The set of flags include:

  • NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA

    Indicates to a protocol driver that it is free to access indicated data by any means. If a miniport indicates data out of on-board shared memory, it must not set this flag.

  • NDIS_MAC_OPTION_FULL_DUPLEX

    If set, indicates the miniport supports simultaneous receives and sends on SMP machines. See Section 4.5 of this part for more information on full-duplex operation.

  • NDIS_MAC_OPTION_NO_LOOPBACK

    If set, indicates the miniport will not loopback a packet passed to MiniportSend(Packets) that is directed to a receiver on the same machine, and that the miniport expects that NDIS will perform loopback. If NDIS performs loopback of a packet, the packet is not passed down to the miniport. A miniport always sets this flag unless its NIC performs hardware loopbacks.

  • NDIS_MAC_OPTION_RECEIVE_SERIALIZED

    If set, the miniport does not indicate any newly received packet up until the previously received packet has been fully processed, including transferring the data. Most miniports, except those that indicate up packets by calling NdisMIndicateReceivePacket, set this flag.

  • NDIS_MAC_OPTION_TRANSFERS_NOT_PEND

    If set, a miniport never returns NDIS_STATUS_PENDING from MiniportTransfer.

A miniport must never use the flag NDIS_MAC_OPTION_RESERVED, which is reserved for NDIS internal use.

MiniportQueryInformation will also be queried with a media-specific OID to determine the NIC’s current address. For instance, the miniport for a NIC of type 802.3 will be queried with OID_802_3_CURRENT_ADDRESS.

The miniports for certain media types will receive additional OIDs that are media-specific. As an example, a miniport whose NIC is of type 802.3 will be queried with OID_802.3_MAXIMUM_LIST_SIZE. See the Network Driver Reference for a list and description of the general operational and media-specific OIDs.


3.4 Set Information Handler

Besides being called during initialization, MiniportSetInformation is called in two additional cases:

  1. During a hardware reset, and

  2. As a result of a protocol call to NdisCloseAdapter.

MiniportSetInformation is called during a hardware reset operation as described in the next section. In this case, MiniportSetInformation is called to reset the miniport to its initial state with respect to its addresses.

NDIS calls MiniportSetInformation when a miniport’s NIC is closed by a protocol’s NdisCloseAdapter call. Such a miniport will be requested to update its addressing information.


3.5 Hardware Reset

A miniport must register a MiniportReset function with NdisMRegisterMiniport. The definition of MiniportReset is:

NDIS_STATUS
 MiniportReset(
 OUT PBOOLEAN AddressingReset,
 IN NDIS_HANDLE MiniportAdapterContext
);

MiniportReset will be called if NDIS determines that the miniport’s NIC has hung, either because the miniport’s MiniportCheckForHang function returned TRUE or because NDIS detected a pending send packet or request that did not complete in an agreed upon timeout period.

MiniportReset should:

  • First disable further interrupts

  • Null out the packets associated with any sends in progress; for instance, on a ring buffer for a busmaster DMA device, the pointers to send buffers should be cleared. The miniport relinquishes pending packet ownership, no matter the state of the send. NDIS will complete any pending sends to higher layers with an appropriate status.

  • Restore the hardware state and the miniport’s internal state back to the initial state it had with respect to multicast addresses and registered filters. Either the miniport takes responsibility for doing this or it relies on NDIS.

If the miniport returns FALSE in AddressingReset, the miniport will return its addressing values to their initial state. If the miniport returns TRUE in AddressingReset, NDIS will call the miniport’s MiniportSetInformation function to set addresses and filters to their initial state. Note that it is the choice of the driver writer whether the miniport will save this addressing state so that it can restore it on a reset or whether it will rely on NDIS.


3.6 Shutdown Handler

All NIC drivers should register a shutdown handler during initialization. The definition of MiniportShutdown is:

VOID
 MiniportShutdown(
 IN PVOID ShutdownContext
);

A driver’s shutdown handler should at least do the following:

  • Insure that the hardware is not trying to copy any data to memory after returning from the shutdown handler. That is, if there is a DMA operation in progress, the operation should be terminated.

  • Reset its hardware to its initial state.

The shutdown handler can be called as a result of a user operation, in which case it runs at IRQL PASSIVE_LEVEL. It can also be called as a result of an unrecoverable system error. If so, MiniportShutdown can be running at any IRQL. Only Ndis Xxx functions that can be called at any IRQL are safe to call from MiniportShutdown.


3.7 Halt Handler

A miniport must supply a MiniportHalt function to NdisMRegisterMiniport. The definition of MiniportHalt is:

VOID
 MiniportHalt(
 IN NDIS_HANDLE MiniportAdapterContext
);

MiniportHalt can be called:

  • If any request to set up multicast addresses, or to set MAC_OPTIONS, fails

  • If all the protocol drivers bound to the miniport unbind

  • During an unload operation; for instance, a NetStop command has caused an unload operation to be carried out

  • During system shutdown

MiniportHalt should undo everything that MiniportInitialize did including the following:

  • Free ports

  • Release all the hardware resources it claimed

  • Free interrupt resources by calling NdisMDeregisterInterrupt

  • Free any memory it has allocated

  • Terminate the wrapper

  • Stop the NIC, unless the MiniportShutdown function has already restored the NIC to its initial state


Chapter 4 Transferring Data

This describes how a miniport handles sends and receives. It includes design information on the following.

  • Interrupt Handlers in Section 4.1

  • Deferred Procedure Handlers in Section 4.2

  • Out-Of-Band data in Section 4.3

  • Sending packets in Section 4.4. The miniport can choose from two different paradigms.

  • Full-duplex operation in Section 4.5.

  • Receiving data in Section 4.6. The miniport can choose from two different paradigms

  • Keeping statistics in Section 4.7

  • Reporting hardware status to higher level drivers in Section 4.8


4.1 Handling an Interrupt

MiniportISR is passed two arguments, InterruptRecognized and QueueMiniportHandleInterrupt, that must be set before returning. MiniportISR must set InterruptRecognized to TRUE if it recognizes the interrupt or FALSE if it does not. If the interrupt is shared, and the miniport does not recognize the interrupt, it returns FALSE and the interrupt will be passed to the next possible ISR function for this interrupt until a function recognizes the interrupt, or all possible functions have been called. If MiniportISR recognizes the interrupt and wants its MiniportHandleInterrupt queued, it must set QueueMiniportHandleInterrupt to TRUE. NDIS does the actual queuing of the MiniportHandleInterrupt function when MiniportISR returns. If the miniport sets InterruptRecognized to FALSE, the value of QueueMiniportHandleInterrupt is assumed to be FALSE.

While an ISR is executing, all other interrupts on the processor at the same or lower IRQL are masked off. Therefore, it is important for a miniport to minimize the time spent in MiniportISR. Otherwise, the overall performance of the system degrades, as well as I/O throughput for the miniport.

It is important for a miniport that manages a NIC that shares an interrupt with other devices on the same bus to quickly determine if the interrupt belongs to it. If it does not, the miniport should set InterruptRecognized to FALSE and return immediately.

If the interrupt is shared and belongs to the miniport, or if the interrupt is not shared but is recognized by the miniport (it is not spurious), the miniport should disable interrupts on its NIC, and then capture the interrupt-specific information it needs from the NIC registers, either immediately in MiniportISR if absolutely necessary, or preferably in the MiniportHandleInterrupt function, which runs at a lower IRQL.

Interrupts must be disabled by MiniportISR to ensure that when MiniportHandleInterrupt runs, no information in the device has been overwritten by a subsequent interrupt. MiniportISR should read and save only that information which cannot be accessed after it returns. Having done this, MiniportISR should return.

If the miniport does not want its MiniportHandleInterrupt function queued, and it disabled interrupts previously in MiniportISR, it must reenable the interrupt on the NIC before returning from MiniportISR.

Generally, MiniportISR determines and records the reason for the interrupt, copies any volatile interrupt-specific information it needs to handle the interrupt into a permanent structure, usually somewhere in the MiniportAdapterContext passed to MiniportISR, and clears the interrupt on the NIC.

Synchronizing with an ISR Function

A miniport’s MiniportISR function and its MiniportDisableInterrupt function run at DIRQL. Other miniport code runs at IRQL <= DISPATCH_LEVEL. To prevent race conditions, any miniport function that shares resources with the MiniportISR or MiniportDisableInterrupt function must synchronize access to the shared resources. A function synchronizes with MiniportISR and MiniportDisableInterrupt by calling NdisMSynchronizeWithInterrupt, supplying a MiniportSynchronizeISR function. MiniportSynchronizeISR runs at DIRQL and can safely access the shared resource.

For example, MiniportHandleInterrupt calls NdisMSynchronizeWithInterrupt before clearing an interrupt status register. This NDIS call takes a SynchronizeFunction argument. Within NdisMSynchronizeWithInterrupt, NDIS calls the miniport’s SynchronizeFunction at DIRQL, so that this function can safely clear the interrupt status register and be sure that MiniportISR or MiniportDisableInterrupt are not modifying the same register at the same time. SynchronizeFunction should execute as quickly as possible like the MiniportISR and MiniportDisableInterrupt functions.

The execution of MiniportISR and MiniportDisableInterrupt, both of which run at DIRQL, is synchronized with a spin lock associated with the interrupt object.


4.2 DPC Handler

Every miniport that manages a device that interrupts must have a MiniportHandleInterrupt function. Even if NDIS completely handles the interrupt, it will always queue a DPC for the miniport, causing the miniport’s MiniportHandleInterrupt handler to run. If MiniportISR processes an interrupt, it controls whether MiniportHandleInterrupt is queued by the value it returns in the InterruptRecognized and QueueMiniportHandleInterrupt arguments. If MiniportISR sets both to TRUE, MiniportHandleInterrupt is queued for execution at IRQL DISPATCH_LEVEL; if it sets InterruptRecognized to FALSE, MiniportHandleInterrupt will not be queued.

When MiniportHandleInterrupt is called, interrupts have been disabled on the NIC. MiniportHandleInterrupt should read whatever data it needs to finish processing the interrupt-driven I/O operation, and then reenable interrupts on the NIC, either by letting NDIS call the miniport’s MiniportEnableInterrupt function after it returns control or by enabling the interrupt from within the MiniportHandleInterrupt function, which is faster. Until the interrupts are reenabled, no further interrupts can occur on the NIC. So that no receive data is missed, MiniportHandleInterrupt should attempt to reenable interrupts as quickly as possible.

Assuming it uses interrupts, a miniport handles data receives and send completes in the context of MiniportHandleInterrupt. For instance, if MiniportHandleInterrupt determines that the reason for the interrupt is a receive, it will indicate the data up by calling NDIS in the context of the DPC. Section 4.6 describes how a miniport processes receives.

If the miniport determines that the reason for the interrupt is a send completion, the miniport calls NdisMSendComplete to signify the completion of a packet previously sent to MiniportSend or MiniportSendPackets which returned NDIS_STATUS_PENDING. The send operations of a miniport are described in Section 4.4.

When a miniport is processing a receive, it must not reenable interrupts until MiniportTransferData either has transferred the incoming data to a protocol driver-supplied packet if any protocol driver wants the data or has passed up an entire miniport-allocated packet or array of packets containing the received data. If a miniport-allocated packet is passed up to a protocol driver, and the protocol keeps the packet, the miniport must supply a new empty packet for the next receive. After it has completely processed the receive and is prepared for the next receive, the miniport can reenable interrupts.


4.3 Packet Out-Of-Band Data

Every NDIS_PACKET-type descriptor has an associated out-of-band data block in which media-specific and priority information is passed. This substructure is defined as:

typedef struct _NDIS_PACKET_OOB_DATA {
Union {
 ULONGLONG TimeToSend;
ULONGLONG TimeSent
}
ULONGLONG TimeReceived;
UINT HeaderSize;
UINT SizeMediaSpecificInformation;
PVOID MediaSpecificInformation
NDIS_STATUS Status;
}; NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA

A miniport NIC driver that has support for OOB data should use the NDIS macros provided for this purpose. It should never attempt to calculate offsets or otherwise depend on knowing the exact structure of a packet's OOB data.

PNDIS_OOB_DATA_FROM_PACKET( Packet )
Returns a pointer to the NDIS_PACKET_OOB_DATA substructure. This pointer can be used to access members of NDIS_PACKET_OOB_DATA by member name.
NDIS_SET_PACKET_STATUS( Packet, Status )
Sets the Status member.
NDIS_SET_PACKET_TIME_TO_SEND( Packet, TimeToSend )
Sets the TimeToSend member.
NDIS_SET_PACKET_TIME_SENT( Packet, TimeSent )
Sets the TimeSent member.
NDIS_SET_PACKET_TIME_RECEIVED( Packet, TimeReceived )
Sets the TimeReceived member.
NDIS_SET_PACKET_HEADER_SIZE( Packet, HeaderSize )
Sets the HeaderSize member.
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO( Packet, MediaSpecificInfo, SizeMediaSpecificInfo )
Sets the MediaSpecificInformation and SizeMediaSpecificInformation)
NDIS_GET_PACKET_STATUS( Packet )
Returns the Status member.
NDIS_GET_PACKET_TIME_TO_SEND( Packet )
Returns the TimeToSend member.
NDIS_GET_PACKET_TIME_SENT( Packet )
Returns the TimeSent member.
NDIS_GET_PACKET_TIME_RECEIVED( Packet )
Returns the TimeReceived member.
NDIS_GET_PACKET_HEADER_SIZE( Packet )
Returns the HeaderSize member.
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO( Packet, pMediaSpecificInfo, pSizeMediaSpecificInfo )
Returns media-specific information in a buffer whose address is provided by the caller and the size in bytes of that information stored in a caller-supplied variable.

OOB Data on a Send

If a miniport supports media specific information and/or priority, when it receives a packet to send it might do one or more of the following:

  • Get TimeToSend

  • Get SizeMediaSpecificInformation and MediaSpecificInformation

Before returning, MiniportSendPackets sets the Status member for each packet it was passed. It sets Status to something other than NDIS_STATUS_PENDING if it completes the send synchronously. Otherwise, MiniportSendPackets sets the Status member to NDIS_STATUS_PENDING. Later, when the send completes, the status of the send must be returned in the Status parameter to NdisMSendComplete. After MiniportSendPackets returns, the Status member of the OOB data, as well as all other members, are read-only to the NIC driver.

If MiniportSend is called, and the send is synchronous, status is returned as the status of MiniportSend. The OOB data is never used to return the status of a MiniportSend operation.

In summary:

  • All but the Status member of the OOB data is read-only to MiniportSendPackets. Status is written to return the status of the send for each packet that is completed synchronously.

  • When a packet is sent asynchronously, either by MiniportSend or MiniportSendPackets, the final status of the send is always returned as a parameter to NdisMSendComplete.

  • All the OOB data is read-only to MiniportSend. MiniportSend always returns the status of the send as the status of MiniportSend. If the status returned is NDIS_STATUS_PENDING, the status is returned later as a parameter to NdisMSendComplete.

OOB Data on a Receive

If a miniport passes media-specific information and/or priority, it must indicate received data by indicating up an entire packet(s) with NdisMIndicateReceivePacket. Before indicating the packet, the miniport must write the Status member of the OOB. The miniport must always set the Status member, either to NDIS_STATUS_RESOURCES, if it doesn’t want the higher layer driver(s) to keep the packet, otherwise to NDIS_STATUS_SUCCESS.

In addition, the miniport might write one or more of the following:

  • Set TimeSent if it can be determined, for instance by an ATM NIC driver.

  • Set TimeReceived.

  • Set HeaderSize. Typically, a miniport only needs to set this once per packet descriptor when the packet descriptor is initialized.

  • Set the pointer to MediaSpecificInformation and the SizeMediaSpecificInfo.

When NdisMIndicateReceivePacket returns, the OOB data is read-only to the miniport. The miniport should read the Status member to determine which packets are now available for reuse because they have already been consumed by higher layer(s). Such packets will have a status other than NDIS_STATUS_PENDING. Any packet with a Status of NDIS_STATUS_PENDING is owned by a higher layer driver(s), is currently unavailable to the miniport, and will be returned later to the miniport at its MiniportReturnPacket function.

If the miniport writes a status of NDIS_STATUS_RESOURCES in any packet in the array of packets that it indicates up, that packet and any packet(s) following it in the array will be returned to the miniport when NdisMIndicateReceivePacket returns.


4.4 Sending Packets

A miniport can use either of two ways to send packets:

  1. A miniport supplies a MiniportSendPackets function. In this case, the miniport is passed one or more packets at a time, passed in as an array of pointers to the packet descriptors.

  2. A miniport supplies a MiniportSend function. In this case, MiniportSend is always passed a single packet descriptor.

If a miniport registers both a MiniportSendPackets function and a MiniportSend function with NdisMRegisterMiniport, NDIS will call only MiniportSendPackets.

Multipacket Sends

The miniport that supports multipacket sends declares the following miniport function.

VOID
MiniportSendPackets(
 IN NDIS_HANDLE MiniportAdapterContext,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberofPackets
);

Each PacketArray is a pointer to an array of pointers to one or more packet descriptors. Each packet descriptor has associated OOB data, as described in Section 4.3. The OOB block allows a protocol driver to specify a time-to-send to a cooperating miniport (TimeToSend) that supports priority sends. The Status member of the OOB block is set by a miniport before returning from MiniportSendPackets to indicate the return value of the call. The other members of the NDIS_PACKET_OOB_DATA block are read-only to a miniport.

Any miniport that provides MiniportSendPackets and returns NDIS_STATUS_PENDING for any packet, must save the pointers to the individual packets for which it returns this pending status. When MiniportSendPackets returns, the array containing the pointers will be returned to the caller.

A miniport that does not have resources to send a packet can set the Status member to NDIS_STATUS_RESOURCES. In this case, NDIS will queue the packet(s) and then resubmit the packet(s) to the miniport when it completes a send by calling NdisMSendComplete or calls NdisMSendResourcesAvailable, whichever is called first.

The miniport sets the Status field of every packet before it returns from MiniportSendPackets, whether the packet is completed synchronously or asynchronously.

If the miniport completes the send synchronously, it should set the Status member of the OOB data in the packet with the status of the send, which is either NDIS_STATUS_SUCCESS, NDIS_STATUS_FAILURE, NDIS_STATUS_RESOURCES or a miniport-determined status. The miniport sets the Status member to NDIS_STATUS_PENDING if it keeps the packet and will complete the send later. If the miniport returns a status of NDIS_STATUS_RESOURCES for a packet in the array, NDIS will re-queue that packet and any following packets in the array and submit them later when the miniport indicates that it has available resource.

After MiniportSendPackets returns, the Status field of the OOB data is off-limits. This means that when the miniport later completes a packet for which it returned NDIS_STATUS_PENDING, that miniport must return the status of the send in the Status parameter of its call to NdisMSendComplete. The miniport must not write the Status member of the OOB data for any packets that are completed by calling NdisMSendComplete.

Single-Packet Sends

For a single packet send, a miniport NIC driver declares the following function.

NDIS_STATUS
MiniportSend(
 IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET Packet,
IN UINT Flags
 );

The Flags parameter has meaning as defined by cooperating protocol drivers and miniports; NDIS does not define nor make any assumptions about this parameter. However, NDIS provides a function, NdisGetPacketFlags, that is called by the miniport to retrieve the flags value. The miniport retains the packet until the send completes.

If a miniport can complete the send immediately, it returns NDIS_STATUS_SUCCESS, (or NDIS_STATUS_FAILURE if the send failed for any reason, or possibly a miniport-determined failure status) and relinquishes ownership of the packet back to the sender. The miniport always returns the send status as the status of MiniportSend; it never sets the Status member of the OOB data block for such a packet.

If a miniport cannot complete the send immediately, it returns NDIS_STATUS_PENDING. In this case, the miniport retains ownership of the packet until the send completes. For instance, the miniport might have queued the packet internally or started but not completed the send before returning a pending status. When the send completes, the miniport must call NdisMSendComplete passing a pointer to the packet descriptor that has been sent and relinquishing ownership of the packet resources back to the sender for reuse or to be freed.

Alternatively, if the miniport does not have resources for the send, it can return NDIS_STATUS_RESOURCES, in which case NDIS will queue the packet and resend it to MiniportSend later when the miniport calls NdisMSendResourcesAvailable or NdisMSendComplete indicating that it can now accept packets.


4.4.1 Steps in Sending

The sequence of calls made by any miniport NIC driver to send a packet or packets is dependent to some degree on the type of NIC the miniport manages. However, there are certain steps that miniports for all types of devices have in common. All miniports do the following:

  • Call NdisQueryPacket to get a pointer to the first buffer descriptor in the packet.

  • Call NdisQueryBuffer to get the base virtual address for the buffer whose buffer descriptor was returned from NdisQueryPacket.

  • Call NdisGetNextBuffer to get the next buffer descriptor in a chain of descriptors in a packet.

  • Call NdisRawWritePort Xxx to at least program the NIC and possibly to move the data.

A miniport is not required to check the packet size; rather the miniport should assume that a protocol driver will never send a packet that is too large. The protocol driver queries the miniport during initialization to determine the maximum packet size it supports and is responsible for passing only packets of a size supported by the miniport.

Whether a miniport exports MiniportSendPackets or MiniportSend, a miniport can be passed a packet for which it has no send resources. The miniport can:

  1. Return the packet with a status of NDIS_STATUS_RESOURCES in which case NDIS internally queues the packet and all subsequent packets in a packet array. Later if the miniport completes a send for which it previously returned a pending status, it must call NdisMSendComplete, or, before it completes the send, call NdisMSendResourcesAvailable when the miniport has available send resources. NdisMSendResourcesAvailable can be called only in the window between the return of NDIS_STATUS_PENDING and a call to NdisMSendComplete.

    If a miniport supports full-duplex operation, neither NdisMSendComplete nor NdisMSendResourcesAvailable can be called from MiniportSend or MiniportSendPackets. They may, for instance, be called from MiniportHandleInterrupt when an interrupt is processed. When the miniport indicates that it has available send resources, NDIS will send the the first packet on its send queue to MiniportSend or an array of queued packets to MiniportSendPackets.

  2. The miniport can queue the send request internally. A miniport can use the miniport-reserved fields in the packet header to link queued packets, perhaps linked to its adapter-specific context structure. If the miniport queues a packet, it must return NDIS_STATUS_PENDING as the status for the queued packet. A miniport might implement packet queuing to avoid the latency incurred when the packet is returned to NDIS and later resubmitted to the miniport, resulting in increased throughput. WAN miniport NIC drivers always queue packets, internally. It is an error for a WAN miniport to return NDIS_STATUS_RESOURCES.

In the case of MiniportSend, if the sending protocol driver and the miniport have agreed upon a set of flags that affect how a packet is sent, the miniport should use the Flags passed to MiniportSend to handle the send appropriately. NDIS does not define the meaning or actions associated with the flags

In the case of MiniportSendPackets, the miniport is responsible for sending packets in the priority order if specified by the protocol driver. NDIS does not order packets it receives from a protocol driver but simply passes them on to the miniport in the order they were received.

Sending a Packet on a Busmaster DMA Device

Generally, a miniport that manages a busmaster DMA device exports MiniportSendPackets to get the best performance from its NIC.

A busmaster DMA miniport calls NdisQueryPacket to read the packet length and to obtain a pointer to the first buffer descriptor, as well as the number of buffer descriptors and the physical segment count.

The miniport may need to see if it has available transmit buffers and available transmit buffer descriptors mapping buffers in the ring. If either is unavailable, the miniport either fails the call with NDIS_STATUS_RESOURCES, returning the packet to NDIS or queues the packet internally and returns NDIS_STATUS_PENDING.

A DMA device can have an upper limit on the number of physical segments that it can map for a single DMA operation. If a miniport is given a packet to send that is too fragmented, it must copy the buffers chained to the packet into a single transmit buffer. A miniport should allocate such a staging buffer and a buffer descriptor to map it in MiniportInitialize. A miniport calls NdisMoveMemory to move data from each protocol-supplied buffer into this transmit staging buffer. A miniport must only copy send data when the packet is too fragmented, since copying to a staging buffer is an expensive operation and would adversely affect performance if done too frequently.

As a general guideline, if the buffer to be sent is < 256 bytes, the miniport can get improved performance by copying the data into a staging buffer and passing a miniport-allocated descriptor for the buffer to NdisMStartBufferPhysicalMapping.

A miniport performs the following steps to send a packet using DMA.

  1. Calls NdisMStartBufferPhysicalMapping to map the previously allocated shared memory buffer whose contents will be sent. This call takes a Boolean argument, WriteToDevice, that indicates whether data is being moved from host memory to the NIC or from the NIC to host memory. For a send, WriteToDevice is set to TRUE. The miniport must also supply the index of the map register it is using to map the buffer and this index must be one of the registers returned from calling NdisMAllocateMapRegisters at miniport-initialization time.

  2. If the miniport allocated cached memory for its transmit buffers, it calls NdisFlushBuffer, supplying the address of the buffer being sent. NdisFlushBuffer flushes the processor cache line, ensuring that the memory contents seen by the NIC and the miniport are the same. To ensure cache-coherency on all platforms, the miniport should also call NdisMUpdateSharedMemory.

  3. Programs the NIC for the transmission, for instance, by writing to the control registers.

  4. When the send completes, the miniport calls NdisMCompleteDmaTransfer to release the map register. If the NIC interrupts at the completion of the send, this call is made from MiniportHandleInterrupt. If the NIC is polled, this call is made from the MiniportTimer function supplied to NdisMSetPeriodicTimer or NdisMSetTimer (if the miniport does not use a periodic timer), when the timer expires and the send is complete.

Sending a Single Packet on a PIO Device

MiniportSend or MiniportSendPackets is called with a pointer to a packet descriptor or to an array of pointers to packet descriptors specifying the data to be sent. A miniport that controls a NIC that sends data using programmed I/O performs the following steps in its send function:

  1. Calls NdisQueryPacket to get the length of the packet. Checks that there are transmit resources available to send the packet now. If not, returns NDIS_STATUS_RESOURCES or queues the packet internally and returns from its send handler. If a packet is queued in the miniport, the miniport must return NDIS_STATUS_PENDING for that packet. The miniport should assume that it will always receive a packet of the proper size, that is, the protocol driver will not attempt to send a packet that is larger than the miniport supports. The protocol driver determines this size during system initialization. If the miniport supports OOB data, it reads any relevant data from the OOB block using NDIS macros. If send resources are available, the NIC driver continues.

  2. Calls NdisQueryPacket to get a pointer to the first buffer descriptor in the packet.

  3. Calls NdisQueryBuffer to obtain the virtual address of the buffer containing the data to be sent and the length in bytes of that buffer.

  4. Calls NdisRawWritePortUlong to write the length of the buffer to the port.

  5. Calls NdisRawWritePortBufferUlong to write the buffer whose address was returned from NdisQueryBuffer.

  6. Calls NdisGetNextBuffer to get the next buffer in the packet.

  7. Repeats steps three through six until all the buffers in the packet have been successfully sent.

  8. When MiniportSend is done with a packet, it returns the status as the status of MiniportSend. If the status is not NDIS_STATUS_PENDING, the miniport relinquishes the packet descriptor and the resources it specifies back to the caller. MiniportSendPackets returns the status of the send in the Send member of the OOB block for the packet descriptor. If the miniport returned a pending status for any packet, it must call NdisMSendComplete when it is done with the packet resources and is ready to relinquish it to the caller. If the miniport calls NdisMSendResourcesAvailable to indicate it has resources to accept a new send request; it must make this call before making a call to NdisMSendComplete.

Sending a Packet Using On-Board Memory

A miniport that supports such a device calls NdisMMapIoSpace in MiniportInitialize to map the adapter’s on-board memory onto host memory and to give the miniport a virtual address with which to refer to the memory. A miniport sends data on this type of device by moving the buffers to be sent into the mapped-memory and then writing to the port (or registers) it claimed by calling NdisMRegisterIoPortRange in a device-specific way to cause the NIC to put the data on the medium. If the miniport does not have any send resources available, it either queues the packet(s) or returns the packet(s) to NDIS as described earlier.

If the miniport has send resources available, the sequence of calls it makes is:

  • NdisQueryPacket to get the start and length of the packet containing the buffers to be sent.

  • NdisQueryBuffer to get the buffer descriptor.

  • NdisMoveToMappedMemory to move a buffer at a time to the mapped adapter memory buffer.

  • NdisGetNextBuffer to retrieve the next buffer from the packet and again, call NdisMoveToMappedMemory. Repeat these two calls until all the buffers have been transferred.

  • NdisMRawWritePort Xxx or possibly NdisWriteRegister Xxx to cause the send to occur in a device-specific manner.

The miniport should use the length returned from NdisQueryPacket to make sure that there is enough room in the on-board memory to contain all the buffers in the packet before starting the copy. If the adapter memory does not have enough free space for the current packet, the miniport can return a status of NDIS_STATUS_RESOURCES if there is not enough free on-board memory, in which case, NDIS will queue the packet and resend it later when the miniport indicates that it has available resources. Alternatively, the miniport can return NDIS_STATUS_PENDING, queueing the packet internally.

Sending a Packet on a Slave DMA Device

A slave DMA NIC uses the system DMA controller to transfer data from a packet supplied to its send handler, typically MiniportSend, to the NIC for transmission on the network.

For any packet for which the miniport has send resources, a miniport for a slave DMA NIC does the following:

  • Calls NdisMSetupDmaTransfer to set up the system DMA controller for the send. The miniport passes the handle to a DMA channel it allocated during miniport initialization by calling NdisMRegisterDmaChannel, a pointer to the host memory buffer containing the data to be sent, an offset into the buffer, the length of data, and a Boolean that indicates the direction of the transfer; in this case, from host memory to the device. On return from NdisMSetupDmaTransfer, the controller is programmed to transfer the data including ensuring that the buffer is cache-coherent.

  • Performs device-dependent steps to cause the transfer, for instance by calling NdisRawWritePort Xxx.

  • When the transfer is complete, the miniport calls NdisMCompleteDmaTransfer.

Generally, the miniport will set up the complete buffer for transfer. However, if the size of the buffer is greater than the DMA constraints of the device, the miniport uses the length and offset parameters to send a subrange of the buffer. If the buffer to be sent is < 256 bytes, the miniport can get improved performance by copying the data into a staging buffer and passing a miniport-allocated descriptor for the buffer to NdisMSetupDmaTransfer.


4.4.2 Completion Options

MiniportSendPackets is passed an array of packets (although a single packet could be passed this way). The status must be set individually in the OOB data block for each packet descriptor in the array before returning.

For instance, the miniport might return NDIS_STATUS_PENDING for a packet in the array to indicate that the miniport is not done with such a packet. It is also possible for the miniport to reject one or more packets in an array due to a lack of available resources, such as transmit buffer space in the NIC. In this case, MiniportSendPackets sets the Status member for the first rejected packet in the array to NDIS_STATUS_RESOURCES before returning.

NDIS assumes that, if this status is set for one element, it is set in all subsequent array elements, and NDIS then requeues the packets for resubmission to the miniport later. In effect, the miniport relinquishes ownership, temporarily or permanently, of any packet in an array of packets for which it sets a Status other than NDIS_STATUS_PENDING before returning from MiniportSendPackets. Otherwise, the miniport owns those packet resources until it later relinquishes ownership by calling NdisMSendComplete, passing the final status of the send as a parameter to this call.

MiniportSend returns the status of the send operation as the status of the function. It relinquishes ownership of any packet for which it returns a status other than pending. As is true for MiniportSendPackets, if MiniportSend returns NDIS_STATUS_PENDING for any packet, the miniport must call NdisMSendComplete later when the send completes. In such a case, the status of the completed send is returned as the Status parameter to NdisMSendComplete.


4.4.3 Packet Ownership

A miniport can complete a send synchronously by returning NDIS_STATUS_SUCCESS, by returning NDIS_STATUS_FAILURE, or by returning a driver-determined status. In all these cases, the miniport relinquishes ownership of the packet(s) after it returns from its MiniportSendPackets or MiniportSend function.

If a miniport does not have the available internal resources to accept and process a send packet, it can return NDIS_STATUS_RESOURCES. In this case, the miniport also temporarily relinquishes ownership of the packet(s) for which it returns this status. NDIS will resubmit the send request for this and, if an array of packets was submitted, for all subsequent packets in the same array, when the miniport calls either of the following:

  • NdisMSendComplete to notify NDIS that it has completed a previously submitted send.

  • NdisMSendResourcesAvailable to notify NDIS that the miniport now has available resources to accept a send request.

If the miniport has the resources to send a packet but cannot complete the send immediately, it returns NDIS_STATUS_PENDING. In this case, the miniport:

  • Retains ownership of the packet.

  • Must call NdisMSendComplete when it completes the transmit operation, passing the status of the operation as the Status parameter to the call. When the miniport calls NdisMSendComplete, passing a pointer to the completed packet, it gives up ownership and must not make any further references to the packet. Ownership reverts to the protocol driver that originally allocated the packet.

If a miniport implements a MiniportSendPackets function, and returns NDIS_STATUS_PENDING for any of the packets, it must call NdisMSendComplete once for each such packet.


4.4.4 Synchronizing Memory Before Sending Data

If a miniport manages a busmaster DMA device, it must ensure that any data passed to the NIC for sending over the network is actually present in physical memory before the NIC reads such data from this shared memory. Therefore, the miniport must call NdisFlushBuffer to force flushing of processor cache. The timing of the flush call is not critical but must be made in MiniportSend after the data is written to shared memory and before it is released to the NIC.

For example, to send a packet on the network, MiniportSendPackets usually gives a busmaster DMA NIC the physical addresses of the packet segments in shared memory, and then calls NdisFlushBuffer and NdisMUpdateSharedMemory. The call to NdisFlushBuffer specifies the WriteToDevice parameter as TRUE, meaning the direction of the transfer is from the host to the NIC. After the miniport has called NdisFlushBuffer and NdisMUpdateSharedMemory, it can release the data to the NIC, usually by writing to a NIC register.

Because cache-line tearing is not a problem for transmitted data, the miniport can flush the data without concern for cache fill size.

Miniports that manage PIO devices are guaranteed that the correct data is transferred to a NIC and therefore such miniports do not call NdisFlushBuffer.

Miniports for slave DMA NICs are guaranteed that NDIS will ensure cache coherency during DMA transfers.

It is generally preferable to allocate cached memory for transmit data, because noncached memory is a scarce system resource, so allocating a large block is not always possible. Any miniport that uses noncached memory must allocate it during driver initialization.

A miniport should always assume that buffers set up by a protocol driver are cached memory.


4.5 Full-Duplex Operation

A miniport that runs in full-duplex mode and can simultaneously send and receive on an SMP machine runs faster than a miniport that is serialized on SMP platforms. A miniport that does not support full-duplex is not designed to handle simultaneous sends and receives. For such a miniport, NDIS serializes execution of all its MiniportXxx functions that run at IRQL <= DISPATCH_LEVEL. That is, all the MiniportXxx functions but MiniportISR and MiniportDisableInterrupts are serialized.

In a miniport that operates in full-duplex mode, all entry points but MiniportSend, or MiniportSendPackets if it supports multipacket sends, are serialized. That is, the miniport’s send function can execute concurrently with any of the other MiniportXxx functions in an SMP machine. The only exception is MiniportReset. If a miniport is resetting, it will not get any send requests. Otherwise, possible concurrent execution of the MinportSend(Packets) and other MiniportXxx functions implies the following:

  • If there are any resources that are shared between a miniport’s send function and any other MiniportXxx function, that resource must be protected by a spin lock. The only exception to this is the MiniportReset path which is serialized by NDIS with respect to the MiniportSend or MiniportSendPackets function and all other MiniportXxx functions.


4.6 Receiving Data

Miniports choose between two methods of passing incoming data to upper layers.

  • Miniports call NdisMIndicateReceivePacket with a pointer to an array of pointers to one or more packets containing the received data. When a miniport indicates received data by calling NdisMIndicateReceivePacket, each entire packet is passed up to the receiving layer. When NdisMIndicateReceivePacket returns, the miniport relinquishes ownership of any packets for which the returned status is NDIS_STATUS_PENDING. The miniport does, however, retain ownership of the array containing the pointers. This means that when NdisMIndicateReceivePacket returns, the miniport must read the Status member of the OOB block for each packet descriptor, and must not reference any resources specified by a packet descriptor if the Status member is set to NDIS_STATUS_PENDING. Pending packets will be returned to the MiniportReturnPacket function when interested protocol driver(s) have consumed the received data. If the Status member is NDIS_STATUS_SUCCESS, the packet descriptor and all the resources it specifies have been returned to the miniport. Any packets for which the miniport set the Status to NDIS_STATUS_RESOURCES before calling NdisMIndicateReceivePacket, will be returned to the miniport when NdisMIndicateReceivePacket returns.

  • Miniports call NdisM Xxx IndicateReceive with a pointer to a lookahead buffer. Interested protocol drivers copy the data in the buffer. If the packet is small and the entire packet is indicated up in the lookahead buffer, the receive is complete when NdisM Xxx IndicateReceive returns. If there is more received data for the protocol, it will call NdisTransferData from within the protocol driver’s ReceivePacket handler causing the miniport to be called at its MiniportTransferData function to transfer the remainder of the received data.

Miniports that indicate receives by calling NdisMIndicateReceivePacket do not need a TransferData handler. Miniports that indicate receives by calling NdisM Xxx Receive do not need a ReturnPacket handler.


4.6.1 Multipacket Receives

A miniport can allocate and manage an array of packet descriptors, fill the chained buffers with incoming data and then pass pointers to the filled in packets to interested protocol drivers by calling NdisMIndicateReceivePacket. Support of NdisMIndicateReceivePacket requires that the miniport allocate and set up packet descriptor for a full network packet, rather than a lookahead buffer.

The miniport passes status, possibly the time a packet is received, possibly the time the packet was sent, and possibly other media-specific infomation to upper-layers by setting members in the OOB data block associated with each packet descriptor. The miniport must set the Status member of the OOB block to either NDIS_STATUS_SUCCESS, if the miniport is willing to allow a protocol to keep the packet and return it later to MiniportReturnPacket, or to NDIS_STATUS_RESOURCES if the miniport is running short of free packet descriptors or buffers and wants to force any interested upper layer driver to copy the packet data before NdisMIndicateReceivePacket returns. See Section 4.3 for details.

When NdisMIndicateReceivePacket returns:

  • If the Status member in the OOB block associated with a packet is anything except NDIS_STATUS_PENDING, the packet descriptor and all the resources it describes are returned to the miniport.

  • If the Status member is NDIS_STATUS_PENDING, ownership of the miniport-allocated packet resources transfers to the protocol driver or possibly intermediate protocol driver until the packet descriptor is returned to the miniport’s MiniportReturnPacket function.

  • If the miniport set the Status member for any packet in the array to NDIS_STATUS_RESOURCES before calling NdisMIndicateReceivePacket, that packet and any subsequent packets in the array will be returned to the miniport when higher level drivers have copied the indicated packet(s) data. The Status member for all such packets will be set to NDIS_STATUS_SUCCESS.

A miniport that manages a busmaster DMA adapter will typically indicate packets by calling NdisMIndicateReceivePacket because such a miniport’s NIC usually has sufficient ring buffer space to receive multiple packets. The miniport can get a performance boost processing several packets at a time.

Usually, a miniport for any other type of device, such as a PIO device or an adapter-shared-memory device, calls NdisMIndicateReceivePacket only if it needs to pass priority, media-specific information or receive-time. NonDMA NICs typically only receive one packet at a time and must be reset between single receives. Such a miniport may only indicate up one packet at a time with NdisMIndicateReceivePacket.

Because the miniport passes ownership of the packet(s) up when it calls NdisMIndicateReceivePacket, such a miniport must practice a reasonable buffer-management strategy to insure that it has available buffers when new data arrives on the network. Typically, the miniport preallocates a set of buffers in MiniportInitialize the size of its ring plus perhaps a few extra, and allocates sufficient buffer descriptors to map these buffers plus a few packet descriptors for making subsequent receive indications.

If a miniport determines that it is running low on receive buffer space, it can use either of the following two strategies.

  1. When it calls NdisMIndicateReceivePacket, the miniport should set the Status member of the OOB block for the first packet descriptor it indicates up that it does not want the protocol(s) to keep to NDIS_STATUS_RESOURCES. This tells NDIS that the miniport is not willing to give up ownership of this packet's resources, nor of any subsequent packets in the array, to the protocol driver to whom the packet is indicated. NDIS will ensure that the packet is copied by the protocol driver rather than passed to the protocol driver. That is, NDIS calls ProtocolReceive rather than ProtocolReceivePacket, forcing the protocol driver to transfer the data immediately. Since the miniport does not have to provide a MiniportTransferData function if it supports the multipacket receive paradigm described here, NDIS intercepts the protocol driver’s transfer request and performs the transfer.

  2. Or, the miniport maintains a high water mark and low water mark for its set of free buffers. When the number of buffers approaches the low-water mark, the miniport calls NdisMAllocateSharedMemoryAsync to allocate more buffers and NdisAllocateBuffer to allocate buffer descriptors to map these buffers. This NDIS function can be called at IRQL DISPATCH_LEVEL, even in the MiniportHandleInterrupt function for instance, unlike NdisMAllocateSharedMemory which can be called only at IRQL < DISPATCH_LEVEL, for instance, in the MiniportInitialize function. When the set of free buffers exceeds the high-water mark, the miniport returns excess buffers by calling NdisMFreeSharedMemory to free the buffers and NdisFreeBuffer to free the buffer descriptors that map these buffers.


4.6.2 Single-Packet Receives

During initialization, MiniportQueryInformation is called to determine the miniport’s or NIC’s operational characteristics, including the minimum size of lookahead buffer it will indicate up. It must indicate up at least this minimum amount of data but it can indicate up more if more data is available on the NIC. For instance, a DMA adapter transfers all the data directly into memory, so the miniport for such a device usually indicates the complete received buffer up, allowing a protocol driver to copy the buffer on the indication without having to make a call to NdisTransferData to get the rest of the data.

On the other hand, miniports that manage PIO devices pass a minimum lookahead buffer up to protocol drivers, and the rest of the data is not read until the miniport gets called at its MiniportTransferData function. Because transferring data in this manner is slow, such a miniport optimizes for the case that no protocol accepts the packet and the minimum number of protocols request calls to MiniportTransferData.

A miniport that calls Ndis Xxx IndicateReceive must have a MiniportTransferData function and must be prepared to be called to copy incoming data into a packet with chained buffers passed down by a protocol driver. MiniportTransferData is called in the context of the miniport’s NdisM Xxx IndicateReceive call. After NdisM Xxx IndicateReceive returns control, the miniport can receive new data; that is, it can reenable receive interrupts.

MiniportTransferData is passed a packet descriptor that can specify a single buffer or a chain of buffers. The miniport must divide up the incoming data into these buffers. If the buffer(s) supplied by the protocol driver are insufficient, the miniport should fail the transfer call with an appropriate status. The protocol driver should not make this mistake because the miniport indicates the total size of the incoming network packet in its NdisM Xxx IndicateReceive call. Data transfer can complete synchronously if all the data is transferred inside MiniportTransferData or MiniportTransferData can return NDIS_STATUS_PENDING. If the miniport returns a pending status, it must later call NdisMTransferDataComplete when all the data has been transferred. Again, the miniport must not reenable interrupts until the transfer of data is complete so that the current received data is not overwritten by an incoming receive.

A miniport must periodically make a call to NdisM Xxx ReceiveComplete. This call causes NDIS to call bound protocol drivers’ ProtocolReceiveComplete functions. This notifies a protocol driver that it can process the data it has received but not yet processed fully, such as making indications to clients. A recommended strategy for a miniport is to make this call in its MiniportHandleInterrupt function every ten receives or before exiting MiniportHandleInterrupt if fewer than ten receives are processed.


4.6.3 Steps in Receiving Data

The way in which received data is processed depends to some extent on the type of NIC. The process for receiving data is described for each such device type.

Some operations are common to all the devices. These include:

  • Miniports call NdisRawWritePort Xxx functions to program their NIC.

  • Miniports that indicate up a complete packet structure must call NdisAdjustBufferLength to adjust the buffer descriptor that describes a buffer to the actual length of the data in that buffer before indicating up the buffer, and must call this function again to reset it to the actual buffer length when the packet is returned.

  • Miniports do not enable more receives until the current data has been indicated up and a new set of buffers allocated to the receive ring, or copied (or not, if the protocol driver is not interested) by the protocol driver to which the data was indicated.

Packet Management

Some miniports, such as a miniport for an Ethernet device, always receive a fixed and known size frame. If the frame size is always the same, the miniport can allocate packets with a chained buffer(s) and the same packets can be reused for receives.

If, however, a miniport controls a NIC on which variable length frames are received, the size of a packet needed to handle a receive can vary from one receive to the next.

If a miniport must handle variable-length receives, it must do the following:

  1. Chain and unchain buffers from a packet by calling Ndis(Un)ChainBufferAtBack or Ndis(Un)ChainBufferAtFront to build a larger, or make a smaller, packet indication.

  2. Call NdisAdjustBufferLength before indicating a packet to set the size in the buffer descriptor for the last buffer in a chain if the full buffer is not used and NdisAdjustBufferLength when the packet is returned to reset the length to the actual full length of the buffer.

Buffers that are unchained from a packet descriptor should be kept in a free list to ensure they don’t get “lost” and so they are available when needed.

Receiving Data on a Busmaster DMA Device

When a packet or packets is received, the miniport does the following:

  1. Calls NdisFlushBuffer and NdisMUpdateSharedMemory to ensure that the incoming data is coherent in the memory area to be accessed by the miniport.

  2. Calls NdisAdjustBufferLength to adjust the length in the buffer descriptor field that maps the NIC receive/ring buffer to match the actual length of the data in the buffer before indicating up. When the packet is returned, the miniport must again call NdisAdjustBufferLength to adjust the length back to the full as-allocated length of the receive/ring buffer.

  3. If the miniport indicates up an array of packets by calling NdisMIndicateReceivePacket, it must set the Status member of the OOB block associated with each packet descriptor. If the miniport determines that it wants to force the protocol driver to copy the indicated packet(s) because the miniport is running short of receive buffers, it should set the Status member in the OOB block to NDIS_STATUS_RESOURCES. Otherwise, the miniport should set the Status member to NDIS_STATUS_SUCCESS, NDIS_STATUS_FAILURE, NDIS_STATUS_PENDING, or a driver-determined status.

  4. If the miniport indicates up a single packet by calling NdisM Xxx IndicateReceive, it indicates all the received data, indicating its full size as the lookahead size.

  5. After the packet is indicated up (Step 3) or copied (Step 4), the miniport replaces the buffer descriptors on the ring. The buffer descriptor is replaced either with the buffer descriptor(s) from a previously allocated and currently available packet, with the buffer descriptor(s) from a packet that the protocol driver was forced to copy and was returned, or with a buffer descriptor allocated by calling NdisAllocateBuffer for a buffer descriptor to describe a buffer allocated by calling NdisMAllocateSharedMemoryAsync. Miniports for NICs that receive variable-size frames must chain and unchain packets depending on the amount of incoming data to build varying size packets. Some miniports receive data on a medium that uses fixed-size frames. This kind of miniport can allocate fixed -ize buffers once, chain them to packet descriptor(s), and reuse them without being required to chain and unchain buffers.

If the miniport calls NdisMAllocateSharedMemoryAsync to allocate a buffer, it will be called at its MiniportAllocateComplete function when the allocation is complete.

Receiving Data on a PIO Device

A miniport for a PIO device reads data from the NIC ports using NdisRawReadPort Xxx using ports previously claimed with NdisMRegisterIoPortRange. Typically such a device indicates the lookahead portion of a received packet to bound protocol drivers by calling a filter-specific Ndis Xxx IndicateReceive function and transfers the rest of the data to interested protocol drivers in its MiniportTransferData function. After the data is transferred, the data can be overwritten by new data after receive interrupts are re-enabled.

A miniport for a PIO NIC should insure that new data is not missed because of the time delay in transferring current data to an upper layer protocol driver. If a NIC has a relatively small on-board FIFO buffer, the miniport can preallocate a buffer in its MiniportInitialize function and then stage the received data into this buffer to be indicated up. Staging is done to try to keep the FIFO as empty as possible and available for new incoming data.

Alternatively, the miniport can indicate up data copied directly from the NIC port and move the rest of the received data directly from the NIC when its MiniportTransferData is called. This receive technique incurs a delay equal to the time between when the received data was indicated up by calling Ndis Xxx IndicateReceive and when MiniportTransferData is called and moves the received data into the buffer provided to MiniportTransferData.

If the miniport calls NdisMIndicateReceivePacket, it transfers the data into a buffer(s) chained to a preallocated packet before it makes the call.

Receiving Data on a Memory-Mapped Device

A miniport that manages a NIC with on-board memory typically transfers the received data in MiniportTransferData directly from the NIC to a buffer supplied in the MiniportTransferData call. Like a PIO NIC miniport, the miniport can stage the receive if there is a possibility that new data will be lost because of the latency incurred by the direct transfer from the NIC to the protocol driver-supplied buffer(s).

The miniport calls NdisM Xxx IndicateReceive to indicate up an amount of data equal to the lookahead size or, if the buffer is small and the miniport has the complete buffer, it indicates up the complete buffer. MiniportTransferData calls NdisMoveFromMappedMemory to transfer data from the NIC’s on-board memory to the protocol driver’s packet or to a staging buffer. After the data is transferred, the miniport can reenable interrupts so that new data can be received.

If the miniport calls NdisMIndicateReceivePacket, it transfers the data into a buffer(s) chained to a preallocated packet before it makes the call.


4.6.4 Cache Considerations for Receiving Data

If a miniport allocates cached memory, it must do the following:

  • Make receive buffers cache-aligned.

  • Flush the cache before starting the receive by calling NdisFlushBuffer and NdisMUpdateSharedMemory.

A miniport that manages a busmaster DMA device receives data into memory shared with the NIC. The miniport must ensure that incoming data is present in shared memory (not in processor cache) before it begins to read that data. To accomplish this, the miniport for such a DMA device must call NdisFlushBuffer and NdisMUpdateSharedMemory for instance in its MiniportHandleInterrupt function, before indicating the receive or otherwise causing a read of the data.

A miniport must also prevent cache-line tearing by allocating cache-aligned memory for receive buffers. If a miniport allocates a large block of memory from which it reallocates smaller buffers for received data, the start of each receive buffer must be cache-aligned so that the buffers do not cross cache blocks. A miniport calls NdisGetCacheFillSize during initialization and saves this value in its adapter-specific context area. The miniport uses this value to round up the size of a receive buffer to a cache-line size or a multiple of that size when it allocates such a buffer. The miniport must also use the cache block size to calculate where in the preallocated block of memory to begin allocating receive buffers.

In general, a miniport for a busmaster DMA NIC should allocate cached shared memory, a faster and more available resource. There are two cases where a miniport must use noncached memory.

  • If the NIC managed by the miniport receives consecutive packets into immediately adjacent memory, the end of one packet can occupy the same cache block as the start of the next packet. Therefore, the miniport must allocate noncached memory because reading the first packet can cause stale cache data for the start of the second one.

  • If releasing the buffers involves writing to them, for instance, if the receive buffer is contiguous with a header that contains an ownership bit indicating whether the buffer belongs to the adapter or to the miniport, the adapter may write to that bit before releasing the buffer. A miniport that uses such a technique must allocate these buffers from noncached memory.


4.7 Keeping Statistics

Every miniport NIC driver should maintain both the mandatory general statistics and the media-specific statistics OIDs for their particular media. The general mandatory statistics OIDs that apply to all miniport NIC drivers include the following.

  1. OID_GEN_XMIT_OK

  2. OID_GEN_RCV_OK

  3. OID_GEN_XMIT_ERROR

  4. OID_GEN_RCV_ERROR

  5. OID_GEN_RCV_NO_BUFFER

MiniportQueryInformation must return the corresponding value it has accumulated for each of these statistics when it receives a request with one of these OIDs.


4.8 Reporting Hardware Status

A miniport NIC driver can indicate changes in hardware status to upper layers by calling NdisMIndicateStatus. NdisMIndicateStatus takes both a general status code and a buffer containing media-specific information that further defines the reason for the status change. NDIS will report this status change to bound protocol drivers. NDIS does not interpret or otherwise intercept the status code. The miniport NIC driver can make one or more such calls, however, when it has finished sending status, it must call NdisMIndicateStatusComplete to let interested protocol drivers know that it is done reporting status. The protocol driver or configuration manager can log the status or take corrective action, as appropriate.

NdisMIndicateStatus takes any valid NDIS_STATUS_XXX value. The miniport NIC driver is responsible for indicating status codes that make sense to a protocol or higher level driver. However, a protocol driver will ignore any status values it is not interested in or that don’t make sense in the context of its operations.

For instance, a miniport NIC driver can report NDIS_STATUS_RING_STATUS to report a ring failure. The StatusBuffer argument to NdisMIndicateStatus further details the ring status. The contents of StatusBuffer encodes a set of possible ring conditions that are the reason for the status report, such as NDIS_RING_SIGNAL_LOSS and NDIS_RING_HARD_ERROR.

A miniport NIC driver can also be interrogated by an upper layer driver or by NDIS about the miniport’s hardware status. When MiniportQueryInformation receives OID_GEN_HARDWARE_STATUS, it responds with any of the applicable statuses defined in NDIS_HARDWARE_STATUS. These include:

  • NdisHardwareStatusReady

  • NdisHardwareStatusInitializing

  • NdisHardwareStatusReset

  • NdisHardwareStatusClosing

  • NdisHardwareStatusNotReady

The miniport NIC driver can be queried so that NDIS can synchronize operations between layers of NDIS drivers, for instance by determining whether a NIC is ready to accept packets.


Chapter 5 WAN Miniport NIC Drivers

This chapter describes NDIS functions for supporting WAN NIC miniport drivers and the features of WAN miniport drivers. WAN miniports use the set of NDIS functions used by nonWAN miniports and functions that do the following:

  • Replace a set of NDIS functions used by nonWAN miniports

  • Provide additional WAN-specific capabilities to WAN miniports

WAN NIC miniport drivers are developed using the requirements and functions described in Chapter 2, Chapter 3, and Chapter 4 of this part with the exceptions and additional considerations described in this chapter. The differences between WAN miniports and LAN miniports are described in Section 5.2.1.

Windows NT® RAS enables remote workstations to dial in to the LAN and transparently access resources on the LAN.


5.1 Windows NT RAS Architecture

This section shows the relationship of Windows NT RAS and how drivers for wide area network cards such as ISDN, X.25, and Switched 56 adapters use the services of NDIS and NDISWAN to communicate in a WAN environment.

The following describes the NDISWAN intermediate NDIS driver and the NDISTAPI driver and provide a more detailed view of the entire RAS system.

The components of RAS that are shown in this picture are described next.

RAS Functions

This set of functions allows user-mode applications to make RAS connections. After a RAS connection is established, applications can connect to network services using standard network interfaces such as Windows Sockets, NetBIOS, Named Pipes, or RPC.

PPP Authentication and Control Protocols

PPP Authentication (PAP, CHAP) and network configuration protocols (IPCP, IPXCP, NBFCP, LCP, and so forth) are provided by the RAS system component. A WAN NIC driver implements only PPP media-specific framing. However, if a WAN NIC driver manages a NIC that implements these capabilities in hardware, it replaces this NDISWAN capability. The miniport informs NDISWAN during driver initialization when it responds to the OID_WAN_GET_INFO query.

User-Mode TAPI

This component presents an SPI interface to user-mode applications and converts user-mode requests to corresponding TAPI OIDs.

NDISTAPI

This component implements TAPI with a kernel-mode portion of the TAPI interface. The ndistapi.sys component communicates with WAN drivers by routing TAPI-related OID requests with the NdisRequest function to the appropriate WAN driver.

NDISWAN

The NDISWAN intermediate NDIS driver supports PPP protocol/link framing, compression, and encryption. NDISWAN converts an NDIS_PACKET from an upper layer transport driver to an NDIS_WAN_PACKET and passes the reformatted packet to an underlying WAN NIC miniport driver. The ndiswan.sys driver communicates with WAN NIC drivers through two interfaces:

  1. The NDIS WAN interface

  2. The NDIS miniport NIC driver interface
Serial Driver

This component is a standard Windows NT device driver for internal serial ports or multiport serial cards. The built-in asynchronous WAN miniport NIC driver for Windows NT uses the internal serial driver for modem communications. Any driver that exports the same functions as the Windows NT serial driver will work with the built-in async WAN NIC driver.

X.25 vendors can choose to implement serial driver emulators for the X.25 card. In this case, each virtual circuit on the X.25 card appears as a serial port (with an X.25 PAD attached to it). The connection interface must correctly emulate serial signals such as DTR, DCD, CTS, RTS, and DSR.

X.25 vendors who choose to implement a serial driver emulator for their X.25 card must also make an entry for their PAD in the pad.inf file. This file contains the command/response script needed to make a connection through the X.25 PAD. See the Remote Access Service Administrator’s Guide for details on the pad.inf file.

NDIS WAN Miniport NIC Driver

ISDN, Switched 56, and X.25 vendors should write a WAN NIC driver for their adapters.


5.2 Implementing WAN Miniports

In order to support such media as ISDN, Frame Relay, Switch 56, and so forth with NT RAS, vendors must supply a WAN miniport NIC driver. The driver should be written to the current version of NDIS, which is 4.0, however, existing drivers written to NDIS 3.0 will continue to run. WAN NIC drivers call the WAN-specific variations of a few NDIS functions. While a WAN miniport calls many of the same NDIS functions as a LAN NIC driver, equivalent WAN-specific NDIS calls replace certain LAN NIC driver NDIS calls. In addition, a WAN miniport driver provides a WAN-specific send function whose definition differs from the definition of the MiniportSend function implemented by LAN miniport NIC drivers. These WAN-specific calls are described here.

WAN miniport drivers communicate with the NDISWAN intermediate NDIS driver through the NDIS library. NDISWAN can provide common services such as data compression, encryption, loopback, simple PPP framing, and so forth, so that the WAN NIC drivers need only features specific to the medium (for example, Q931 signalling required for ISDN). If a WAN miniport manages a NIC with compression, PPP, and encryption capabilities, it can specify to NDISWAN that the driver will offload these operations. When the miniport responds to the OID_WAN_GET_INFO query during initialization, it indicates that it will perform one or more of these functions.

A WAN NIC driver is an NDIS miniport NIC driver that:

  • Performs connection-management operations passed from the NDISWAN intermediate NDIS driver to the WAN NIC driver.

  • Accepts send requests and indicates received data to the NDISWAN driver.

The following sections define the interface between the NDISWAN driver and a WAN NIC driver. Note that in addition to the information in this chapter, developers of WAN NIC drivers also should refer to the previous chapters on LAN miniport NIC drivers and to the Network Driver Reference for details on the many NDIS functions, for instance, NdisMSetTimer, NdisOpenConfiguration, and so forth, that are provided for all miniport NIC drivers.


5.2.1 Differences Between WAN and LAN Miniports

There are several differences in the way a WAN miniport interfaces with NDIS as compared to a LAN miniport driver described in the previous chapters. Such differences affect how a WAN driver is implemented.

  • A WAN miniport must not register a MiniportTransferData handler with NdisMRegisterMiniport. Instead, a WAN miniport always passes an entire packet to the NdisMWanIndicateReceive function. When NdisMWanIndicateReceive returns, the packet has been copied and the WAN miniport can reuse the packet resources it allocated.

  • WAN miniports provide a MiniportWanSend function instead of a MiniportSend function. The MiniportWanSend function accepts an additional parameter that specifies a specific data channel on which a packet is to be sent.

  • WAN miniports never return NDIS_STATUS_RESOURCES as the status of MiniportSend or any other MiniportXxx function and cannot call NdisMSendResourcesAvailable.

  • WAN miniports support a set of WAN-specific OIDs to set and query operating characteristics.

  • WAN miniports support a set of WAN-specific status indications which are passed to NdisMIndicateStatus. These status indications report changes in the status of a link.

  • WAN miniports call alternative WAN-specific NDIS functions to complete the WAN-specific NDIS calls for send and receive. The two completion calls are:

    NdisMWanIndicateReceiveComplete

    NdisMWanSendComplete

  • WAN miniport drivers use an NDIS_WAN_PACKET instead of an NDIS_PACKET-type descriptor.

  • WAN miniport drivers keep a WAN-specific set of statistics.

  • WAN miniport drivers never do loopback; it is always provided by NDIS.

  • WAN miniport drivers cannot be full-duplex miniports.


5.2.2 NDISWAN Overview

NDISWAN is an intermediate NDIS driver that presents an 802.3 miniport NIC driver interface to the protocol driver(s) above it and a protocol driver interface to a WAN miniport below it that manages a WAN-capable NIC. NDISWAN has a private interface to the kernel-mode NDISTAPI driver for dynamically setting up and tearing down TAPI links.

NDISWAN converts an out-going packet from LAN to PPP format. NDISWAN uses simple HDLC framing. Most of the media-specific framing must be done in the miniport. NDISWAN gets hints about the WAN miniport NIC driver framing from the miniport’s response to the OID_WAN_MEDIA_SUBTYPE.

Before a WAN miniport can send or receive packets on the network, an application must set up a connection that originates on the sending node or accept a connection that originates on a remote node by making or accepting a call. The setup, supervision and tear-down of a connection is done through TAPI. TAPI requests and status indications to TAPI all go through NDISWAN. See Chapter 6 for details of setting up a miniport to use TAPI services.

After a connection is established with a remote node, packets can be exchanged. The typical path of a WAN connection from a protocol driver to the miniport NIC driver begins when a protocol driver calls NdisSend with a packet. NDISWAN examines the packet header and decides over which link the packet will be sent. NDISWAN copies the typically discontiguous data packet into a contiguous buffer with the appropriate header and tail padding specified by the WAN miniport during its initialization and registration. The link then has several possible configuration options: header compression, data compression, and encryption. These configuration options are applied in that order on the link. On the receive side, the reverse path is applied. Anytime a configuration option such as compression or encryption is enabled, the WAN miniport NIC driver is informed.

Finally, before sending the packet to the WAN miniport’s MiniportWanSend function, NDISWAN does simple PPP HDLC framing unless the WAN miniport has indicated that it will perform these operations in the adapter. Simple PPP HDLC framing is PPP's HDLC framing without the FCS, bit or byte stuffing, and any beginning or end flags. (See later sections on media-specific framing for more information.)

After the packet is received by the WAN miniport, it should send the packet out on the WAN link. The default number of outstanding packets the WAN miniport can have per data channel is specified by the driver as the MaxTransmit value it provides when it responds to the OID_WAN_GET_INFO request. However, the miniport can manage the send window dynamically and on a per-line basis when it calls NdisMIndicateStatus with a line-up indication by providing a new nonzero value for the SendWindow member of the NDIS_MAC_LINE_UP structure passed in the call. If a WAN miniport that makes a line-up indication sets the value of the SendWindow to zero, NDISWAN uses the default value MaxTransmit for that line.

The WAN miniport NIC driver might have to modify or add to the header as well as add to the end (tail) of the packet (for example, to add the FCS). Appropriate padding at the beginning and end of the packet is provided for. The WAN miniport can alter the data in the packet in any way it sees fit to send it on the WAN medium.


5.2.3 Adapters, Bindings, and Links

WAN miniports can set up and manage many links per binding and thus many links per NIC.

Like other miniports, every WAN miniport must have at least one NIC for which it allocates and maintains a NIC-specific context area. The NIC-specific context area is simply a way to store, retrieve, and use information about the hardware specifics of the NIC, such as interrupt, bus type, I/O range, and memory windows, as well as to maintain runtime state. A miniport NIC driver should specify one NIC-specific context area for each adapter in the system that it supports.

From a WAN miniport’s point-of-view, there is only one binding per adapter even if several protocols above the driver are bound to a particular adapter. Because WAN miniports are ignorant of such protocols (this work is handled by NDISWAN), the WAN miniports do not maintain bindings for their NICs. In effect, NDISWAN binds to one or more WAN NIC driver(s) and one or more protocol driver(s) bind to NDISWAN.

Links are logical, point-to-point bidirectional communication channels. There can be many links per adapter. Links are dynamically brought up and torn down. The link speed and quality of the link can be different for each connection. However, it is assumed that the padding and link capabilities are universal for all links on an adapter. If a WAN NIC driver specifies a 20-byte header padding and 4-byte tail padding, they must remain constant for all links on that adapter. If a WAN NIC driver specifies that it does not want PPP Address and Control field compression, it is assumed true for all links on that adapter. If a WAN driver specifies that it can do the older RAS framing, it is assumed true for all links on that NIC.

Protocols bind once to NDISWAN and do not bind to the WAN miniport NIC drivers. This saves memory and simplifies the WAN miniport driver. Because there will typically be several protocols in a given system and there could be more than one WAN miniport NIC driver, the reduction in the number of bindings save memory (that is, each protocol doesn't have to bind to each WAN miniport adapter). In the preceding figure, if each protocol bound to each WAN miniport NIC driver, there would be nine bindings. Also, since protocols can rely on only having a single WAN binding, this can simplify some protocols.

A WAN driver must be an NDIS miniport NIC driver. Full NIC drivers are not supported for WAN adapters. This and the previous chapters on miniport NIC drivers provide the design information a driver developer needs to write a WAN miniport NIC driver.

Miniport NIC drivers must add the following line to their sources file before building to identify the driver as a miniport.

C_DEFINES=/DNDIS_MINIPORT_DRIVER

For WAN drivers, the include paths should include ndiswan.h as well as ndis.h.

If the NDIS miniport NIC driver supports connections through TAPI, the driver should also include ndistapi.h.


5.3 WAN Driver-Specific Services

The upper-edge interface provided by a WAN miniport NIC driver is identical to a LAN miniport NIC driver except for the send request. In addition, a WAN miniport receives and responds to a set of WAN-specific OIDs passed to its MiniportQueryInformation and MiniportSetInformation functions. These are described next.


5.3.1 WAN-Specific Queries

MiniportQueryInformation is called to determine WAN-specific capabilities and current status. In a WAN miniport this function is the same as in a standard miniport NIC driver, except that the WAN driver recognizes WAN-specific OIDs.

If the WAN driver completes NdisMQueryInformation asynchronously by returning a status of NDIS_STATUS_PENDING, it must complete the query later by calling NdisMQueryInformationComplete. The WAN driver controls the buffers pointed to by InformationBuffer, BytesWritten, and BytesNeeded until the request completes. If the InformationBufferLength is insufficient for the information required by the OID, the WAN driver should fail the request and set BytesNeeded to the number of bytes that the OID requires.

No other requests will be submitted to the WAN driver until the current query request has been completed.

The NDISWAN driver does not pass all OIDs through to a WAN miniport. For instance, the NDISWAN driver will intercept all queries with the following OIDs:

OID_GEN_CURRENT_PACKET_FILTER
OID_GEN_PROTOCOL_OPTIONS
OID_802_5_CURRENT_FUNCTIONAL
OID_802_3_MULTICAST_LIST
OID_FDDI_LONG_MULTICAST_LIST
OID_FDDI_SHORT_MULTICAST_LIST

However, a WAN NIC driver must recognize and respond properly to the WAN OIDs summarized next.

OID_WAN_MEDIUM_SUBTYPE

This OID is queried to determine the media subtypes supported by a WAN miniport NIC driver. The WAN miniport’s MiniportQueryInformation function should return one of the following subtypes as enumerated in NDIS_WAN_MEDIUM_SUBTYPE:

  • NdisWanMediumHub

  • NdisWanMediumX_25

  • NdisWanMediumIsdn

  • NdisWanMediumSerial

  • NdisWanMediumFrameRelay

  • NdisWanAtm

  • NdisWanSonet

  • NdisWanMediumSW56K
OID_WAN_GET_INFO

This OID is queried to determine a range of information about the operating characteristics of a WAN miniport. The structure of this OID is documented in the Network Driver Reference. The information returned by the WAN miniport includes:
Parameter | Contents
MaxFrameSize | The driver sets this to the maximum frame size in bytes it can send and receive. Typically this is 1500 but driver should handle 32 bytes larger than its anticipated maximum. The value does not include the driver’s own framing overhead or the PPP HDLC overhead.
MaxTransmit | The default maximum number of outstanding packets the WAN miniport can handle on a line. The NDISWAN driver will insure that the miniport does not exceed that number, that is, NDISWAN will hold send packets if the maximum is reached unless the miniport has reset the maximum in a subsequent line-up indication. The miniport can change this value dynamically and on a per-line basis by setting the SendWindow member in line-up indications to a nonzero value.
HeaderPadding | The amount of required buffer space, in bytes, at the beginning of the largest packet.
TailPadding | The amount of required buffer space, in bytes, at the end of the largest packet.
Endpoints | The maximum number of links this adapter can support. This is an adapter-specific characteristic.
MemoryFlags | Zero if the adapter is not a DMA device. If the adapter supports DMA, this flag can be set to NDIS_MEMORY_NONCACHED and/or NDIS_MEMORY_CONTIGUOUS.
HighestAcceptableAddress | If the WAN miniport’s adapter supports DMA, set this flag to NDIS_PHYSICAL_ADDRESS_CONST (-1,-1), otherwise, if the adapter supports DMA and up to 24-bit addresses, set this flag to NDIS_PHYSICAL_ADDRESS_CONST (0x1000000,0). Subsequently, memory within the specified range will be allocated for all the packets to be transmitted by the NIC. Thus, memory can just be DMAed directly to the NIC instead of having to be double-buffered through a CPU-memory copy.
FramingBits | The driver sets bits that specify the types of framing the driver supports.

OID_WAN_PERMANENT_ADDRESS

The WAN miniport is queried for the address of the NIC that is encoded in the hardware.

OID_WAN_CURRENT_ADDRESS

The WAN miniport is queried for the address the NIC is currently using.

The WAN miniport NIC driver should return a unique address for each adapter. The address is presented to the protocols as an Ethernet address. The least significant bit of the first byte must not be a 1, or it could be interpreted as an Ethernet multicast address. If the vendor has an assigned Ethernet vendor code, they should be used to assure that the address does not conflict with another vendor's address.

OID_WAN_GET_LINK_INFO

The WAN miniport returns all the parameter values that were set on a previous NDIS_WAN_SET_LINK_INFO call. If NDIS_WAN_SET_LINK_INFO has not yet been called, the WAN miniport should return the current state of the link. If the link is not up or does not exist, an error NDIS_STATUS_INVALID_DATA should be returned.

The OID specifies the following types of link information.
Parameter | Contents
MiniportLinkContext | Returns the handle the miniport returned in an NDIS_LINE_UP indication.
MaxSendFrameSize | The largest buffer the WAN miniport driver will receive. A driver can fail any packets given to its MiniportWanSend function if the size of the packet exceeds this size.
MaxRecvFrameSize | The largest packet, excluding padding, that will be accepted by the driver from the network. Larger packets may be dropped.
HeaderPadding | The amount of padding, in bytes, in the head of the frame.
TailPadding | The amount of padding at the tail of the frame.
SendFramingBits | A bit mask that describes what types of framing should be sent. Must be identical to RecvFramingBits.
RecvFramingBits | A bit mask that describes what type of framing should be received.
SendCompressionBits | Reserved.
RecvCompressionBits | Reserved.
SendACCM | Specific to asynchronous media types. If present, specifies the ASCII character to be used for byte stuffing on sends.
RecvACCM | Specific to asynchronous media types. Specifies the ASCII character to be used for byte stuffing on receives.


5.3.2 Registering as a WAN Miniport

A WAN NIC driver identifies itself as a WAN miniport when it registers itself. When the miniport registers with NDIS in its DriverEntry function by calling NdisMRegisterMiniport, it indicates that it requires WAN services by setting the flag NDIS_USE_WAN_WRAPPER in the Reserved member of the NDIS_WAN_MINIPORT_CHARACTERISTICS structure.

Following execution of the DriverEntry function and after MiniportInitialize has completed the initialization of one or more adapters, the NDISWAN driver calls the WAN miniport’s MiniportQueryInformation function, with an OID_WAN_GET_INFO request. A miniport that is TAPI-capable should set the TAPI_PROVIDER bit of the FramingBits member of the NDIS_WAN_INFO structure passed into this call. This lets the miniport receive the TAPI OIDs necessary to set up, use, and tear down TAPI connections.

A WAN miniport that provides its own framing should set the PASS_THROUGH_MODE bit. If this bit is set, NDISWAN passes frames, uninterpreted and unmodified.


5.3.3 Sending On a WAN Miniport

MiniportWanSend transmits a packet through the adapter onto the network.

Ownership of both the packet descriptor and the packet data is transferred to the WAN NIC driver until this request is completed, either synchronously or asynchronously. If the WAN miniport returns NDIS_STATUS_PENDING, it must later indicate completion of the request by calling NdisMWanSendComplete. If the WAN miniport returns a status other than NDIS_STATUS_PENDING, the request is considered to be complete, and ownership of the packet immediately reverts to the caller.

Unlike LAN miniports, the WAN driver cannot return a status of NDIS_STATUS_RESOURCES to indicate that it does not have enough resources currently available to process the transmit. Instead, the WAN miniport should queue the send internally for a later time and perhaps lower the SendWindow value on the line by making a line-up indication. The NDISWAN driver will insure that the WAN miniport driver never has more than SendWindow packets outstanding. If a WAN miniport makes a line-up indication for a particular line, and sets the SendWindow to zero, NDISWAN reverts to using the default value of the transmit window passed as the MaxTransmit value provided to an earlier OID_WAN_GET_INFO request.

It is also an error for the WAN miniport NIC driver to call NdisMSendResourcesAvailable.

The packet passed to MiniportWanSend will contain simple HDLC PPP framing if PPP framing is set. For SLIP or RAS framing, the packet contains only the data portion with no framing whatsoever. Simple HDLC PPP framing is discussed later in more detail.

A WAN NIC driver must not attempt to provide software loopback or promiscuous mode loopback. Both of these are fully supported in the NDISWAN driver.

The MacReserved x members as well as the WanPacketQueue member of the NDIS_WAN_PACKET is fully available for use by the WAN miniport.

The available header padding is simply CurrentBuffer-StartBuffer. The available tail padding is EndBuffer-(CurrentBuffer+CurrentLength). The header and tail padding is guaranteed to be at least the amount requested, but it can be more.

See NDIS_WAN_PACKET in the Network Driver Reference for details of the WAN packet descriptor structure.

A WAN miniport calls NdisMWanSendComplete to indicate that it has completed a previous transmit operation for which it returned NDIS_STATUS_PENDING. This does not necessarily imply that the packet has been transmitted, although, with the exception of intelligent adapters, it generally has. It does however, mean the miniport is ready to release ownership of the packet.

When a WAN miniport calls NdisMWanSendComplete, it passes back the original packet to indicate which send operation was completed. If MiniportWanSend returns a status other than NDIS_STATUS_PENDING, it does not call NdisMWanSendComplete for that packet.


5.3.4 Setting WAN Miniport State

Calls to MiniportSetInformation changes information maintained by the miniport. This function definition and operation is the same as in a LAN miniport NIC driver except that certain WAN-specific OIDs must be recognized.

No other requests will be submitted to the WAN driver until the current set request is complete. If the WAN driver does not complete the call immediately (by returning NDIS_STATUS_PENDING), it must call NdisMSetInformationComplete to complete the call.

Any of the settable NDIS global OIDs can be used, although a WAN miniport cannot set the NDIS_MAC_OPTION_FULL_DUPLEX flag in response to an OID_GEN_MAC_OPTIONS request. The following WAN-specific OID is passed to MiniportSetInformation.

OID_WAN_SET_LINK_INFO

This OID is used to set the link characteristics. The parameters in the structure passed for this OID are described previously for OID_WAN_GET_LINK_INFO.


5.4 WAN Miniport-Specific NDIS Library Requests

The functions described here are NDIS functions that only a WAN NIC driver can call and OIDs that only a WAN miniport will receive. A WAN miniport calls NdisMWan Xxx functions instead of the comparable function used by a LAN-miniport NIC driver. Otherwise, a WAN miniport can call any NdisM Xxx that has no WAN counterpart.

Only the WAN-specific NDIS calls are described next. For a complete summary of all functions that a WAN miniport driver can call, see the preceding information about miniport NIC drivers.


5.4.1 Indicating Received Data

A WAN miniport calls NdisMWanIndicateReceive to indicate that a packet has arrived and that the entire packet (there is no lookahead) is available for inspection. When this call is made, NDISWAN indicates the arrival of the packet to the ProtocolReceive handlers of bound higher-level drivers.

Since the entire packet is always passed up, the miniport driver will never receive a transfer-data call (the data is copied by NDISWAN and then passed up to the next higher driver). The entire packet is always passed up due to compression and encryption that might have been applied to the packet. Also, because the link is point-to-point, at least one bound protocol will always want to look at the packet.

The data contained in the header is the same as that received on the NIC. The NIC driver will not remove any headers or trailers from the data it receives. The transmitting driver cannot add padding to the packet.

A WAN miniport calls NdisMWanIndicateReceiveComplete to indicate the end of one or more receive indications so that protocols can postprocess received packets. As a result, NDISWAN calls the ProtocolReceiveComplete handler(s) of bound protocols to notifying each protocol that it can now process the received data. In its receive-complete handler, a protocol need not operate under the severe time constraints that it does in its receive handler.

The protocol should assume that interrupts are enabled during the call to ProtocolReceiveComplete. In an SMP machine, the receive handler and the receive complete handler can be running concurrently on different processors.

Note that a WAN driver need not deliver NdisMWanIndicateReceiveComplete indications in one-to-one correspondence with NdisMWanIndicateReceive indications. It can issue a single receive-complete indication after several receive indications have occurred. For example, a WAN miniport could call NdisMWanIndicateReceiveComplete from its receive handler every ten packets or before exiting the handler, whichever occurs first.


5.4.2 WAN-Specific Status Indications

Most WAN NIC drivers indicate status with some special status codes and buffer contents. A WAN miniport calls NdisMIndicateStatus when changes in the status of a network adapter occur. These status indications are generated by WAN NIC drivers, and NDIS protocol drivers bound to such a miniport can ignore these indications. However, processing these indications correctly usually results in improved performance for protocols and for WAN NIC drivers. Such a status change is passed to a bound protocol’s ProtocolStatus handler (or to a configuration manager) to log the change in status or to take corrective action.

The following four types of WAN-specific indications can be sent to bound protocol drivers with NdisMIndicateStatus:

  1. Line up

  2. Line down

  3. Fragment

  4. TAPI line event

The status indication provides two basic pieces of information:

  • An overall status code indicating the general change in status. There are a limited number of defined general status codes; this list is subject to future expansion.

  • A specific reason for the general change, which can be adapter-specific. For example, notification that the link speed of an X.25 link has just decreased by a factor of two or that a new link is coming up or an existing link is going to the idle state.

WAN status indications use a general status of NDIS_STATUS_WAN_XXX. The three types of WAN indications are NDIS_STATUS_LINE_UP, NDIS_STATUS_LINE_DOWN, and NDIS_STATUS_FRAGMENT.

All TAPI status indications must use a GeneralStatus value of NDIS_STATUS_TAPI_INDICATION. The specific event that is the reason for the status indication is reported in a structure of type NDIS_TAPI_EVENT. The general type of TAPI status indications of interest to miniport developers are changes in line states, changes in call states, the arrival of an incoming call or the closing by a remote node or by the miniport of an existing call or line. See Chapter 6 for more details.

Line Up Status

A WAN miniport calls NdisMIndicateStatus with a GeneralStatus value of NDIS_STATUS_WAN_LINE_UP to indicate that a new data channel has become active. Until this occurs, the NIC will accept frames and can let them succeed or fail, but it is unlikely that such packets will actually be received by any remote-node process. Until a line is up, protocols should reduce their timers and retry counts so that any outgoing connection attempts quickly fail.

A WAN miniport makes a line-up indication before it returns from an OID_TAPI_GET_ID request. The line-up indication is made if OID_TAPI_GET_ID request contains a new DeviceClass and ulSelect is set to LINECALLSELECT_CALL. The NdisLinkContext returned from NdisMIndicateStatus for the line-up indication is passed back in the NDIS_TAPI_GET_ID structure as the DeviceID when the WAN miniport returns from the OID_TAPI_GET_ID request. See Chapter 6, Section 6.12 for a discussion of making a line-up indication.

The status code for the line-up indication is NDIS_STATUS_WAN_LINE_UP. The buffer at StatusBuffer is formatted as an NDIS_MAC_LINE_UP structure.

Line Down Status

A WAN miniport makes a line-down indication whenever a link goes down. The NdisLinkHandle for the line is no longer valid. When this occurs, bound protocols should reduce their timers and retry counts until the next line-up indication.

The status code for the line-down indication is NDIS_STATUS_WAN_LINE_DOWN. The buffer at StatusBuffer is formatted as an NDIS_MAC_LINE_DOWN structure.

Fragment Indication

A WAN miniport makes a fragment indication whenever it receives a partial packet from a remote peer. Otherwise, NDIS will be unaware of this particular error. The protocol should send frames to the remote that notify the remote of the fragment indication, rather than waiting for a timeout to occur.

The status code for the fragment indication is NDIS_STATUS_WAN_FRAGMENT. The buffer at StatusBuffer is formatted as an NDIS_MAC_FRAGMENT structure.

NDISWAN monitors dropped packets by counting the number of fragment indications on the link.


5.5 WAN Packet Framing

This section provides information on WAN packet framing.

  • Asynchronous Framing is discussed in Section 5.5.1

  • X.25 Framing is discussed in Section 5.5.2

  • Frame Relay Framing is discussed in Section 5.5.3

  • ISDN and Switched 56K Framing is discussed in Section 5.5.4


5.5.1 Asynchronous Framing

Asynchronous Framing with Compression Turned Off

Microsoft provides a sample asynchronous miniport NIC driver that supports SLIP and RAS compression.


5.5.2 X.25 Framing

X.25 Framing with Protocol Field Compression On or Off

Address and Control Field Compression should not be enabled for X.25. If Protocol Field Compression is turned ON, the NLPID should be removed. It is the responsibility of the X.25 driver or hardware to provide for transparency, add any padding, add any FCS, and add any end flag.


5.5.3 Frame Relay Framing

Frame Relay Framing with Protocol Field Compression On or Off

Address and Control Field Compression should not be enabled for Frame Relay. If Protocol Field Compression is turned ON, the NLPID should be removed. It is the responsibility of the Frame Relay driver or hardware to provide for transparency, add any padding, add any FCS, and add any end flags.


5.5.4 ISDN and Switched 56K Framing

Initially, ISDN B channels (not D channels) should be used. Multiple B channel support is done through NDISWAN’s multilink support. Initially, bit-synchronous HDLC framing with NRZ encoding should be used. Transparency should be provided by the driver or ISDN hardware. It is also the responsibility of the ISDN driver or hardware to provide for NRZ encoding, to calculate the FCS to add the PPP end flag (0x7E), and to insert any inter-frame time fill. Switched 56K drivers should frame in the same manner as ISDN.


Chapter 6 NDIS Extensions to Support Telephonic Services

This chapter describes how miniport WAN NIC drivers implement Telephonic services using the functions available through the NDISTAPI driver and NDISWAN driver. The design of the NDISTAPI driver is based on the Service Provider Interface established in the Windows Telephony specification. See the Win32 SDK for detatils.


6.1 Overview of NDISTAPI

The NDISTAPI defines how WAN miniport NIC drivers implement telephonic services. It is closely related to the Service Provider Interface established in Windows Telephony, with telephony-capable NDIS miniport NIC drivers providing kernel-mode support for user-mode TAPI service providers. This document presents an overview of how a TAPI-capable WAN miniport initializes, brings up lines, sets up calls and closes calls using TAPI and WAN OIDs. This description touches briefly on the concepts embodied in Windows Telephony, but the reader should to consult the documentation published with the Telephony SDK for an in-depth discussion.

A TAPI-capable WAN miniport registers and initializes itself as a user of both WAN and TAPI services. When registration and initialization are complete, a user-level application can make telephonic requests to the user-mode KMDDSP service provider module which converts SPI requests to corresponding OIDs. The KMDDSP module passes the OIDs to the NDISTAPI driver, the kernel-mode component of TAPI. NDISTAPI routes the OIDs to the correct NIC driver in order to set up, monitor, and tear down lines, and calls. NDISTAPI passes the OIDs in NdisRequest calls to the NDISWAN intermediate driver. NDISWAN passes these NDIS requests on, resulting in a call to the MiniportQueryInformation function or the MiniportSetInformation function of a TAPI-capable miniport.

A TAPI-capable WAN miniport communicates changes in the states of lines and calls, for instance a change from the proceeding state to the connected state, the arrival of an incoming call, or a remote disconnection. State changes are communicated to upper-layers by passing appropriate NDIS_STATUS_TAPI_XXX and NDIS_STATUS_WAN_XXX status messages to NdisMIndicateStatus.

The relationship among these components is shown in the following diagram. The kernel-mode flow of communication is from NDISTAPI to NDISWAN, through NDIS to a TAPI-capable WAN miniport.


6.2 Line Devices, Addresses, and Calls

A TAPI-capable WAN NIC driver models lines, addresses, and calls. A line device is an abstraction of a physical device having one or more communications channels (used for signaling and/or information). Modeling of the line devices is left to the discretion of the WAN miniport NIC driver developer. For example, consider a BRI-ISDN line with two 64K “B” channels. A WAN miniport NIC driver could choose to model this as any one of the following:

  • One line device with two channels

  • Two line devices, each with its own channel

  • Two line devices that each support one or two channels, selecting from a common pool

  • Two single-channel line devices and one two-channel line device

In the latter two models, channels can be assigned to different line devices at different times.

Each line device is assigned one or more addresses. An address corresponds to a telephone directory number. An address ID is a number between zero and the number of addresses on the line minus one.

From the TAPI client’s point of view, the number of line devices and the number of corresponding addresses remains constant between the time the client calls lineInitialize (to initialize a TAPI client session) and the time it calls lineShutdown (to end a TAPI client session). However, the status of line devices and addresses will commonly change over the life of the client’s telephony session. For example, a line device might be taken out of service for some reason, and become available later. Several requests and notification messages are available to allow client applications to track line and address status and capabilities.

A call represents a connection between two or more addresses. Unlike line devices and addresses, calls are dynamic in that they can be created and destroyed. As with lines and addresses, there are both requests and notification messages defined to allow client applications to follow call state and information changes.


6.3 Set and Query Requests

TAPI requests are passed to the WAN miniport as OID_TAPI_XXX requests with an associated structure that describes the action to be taken. The NdisRequestQueryInformation request type is specified in a call to NdisRequest when the miniport NIC driver should pass information back to the caller. The NdisRequestSetInformation request type is specified for requests that do not return any information other than completion status. An NdisRequest call from NDISWAN is routed by the NDIS library to the target miniport’s MiniportQueryInformation function or MiniportSetInformation function.

All requests sent to a WAN miniport by the NDISTAPI driver can be completed either synchronously or asynchronously. If a NIC driver determines that it cannot complete the request immediately, then it can simply return NDIS_STATUS_PENDING and call NdisMQueryInformationComplete or NdisMSetInformationComplete when it has completed the request.


6.4 Maintaining State Information

A miniport must track changes in the state of the links it manages such as the current call, address, and line state, and report any changes to NDISWAN by calling NdisMIndicateStatus.

A miniport must always send a call state-change message whenever the state of a call changes. The reported call states have the form LINECALLSTATE_XXX and LINECALLINFOSTATE_XXX. There is no filtering of call state messages.

Some typical call states a miniport reports include LINECALLSTATE_IDLE, LINECALLSTATE_CONNECTED, LINECALLSTATE_OFFERING, LINECALLSTATE_DISCONNECTED, LINECALLINFOSTATE_CALLERID and LINECALLINFOSTATE_MEDIAMODE.

Changes in the line state or the address state are only indicated if enabled by a previous OID_TAPI_SET_STATUS_MESSAGES request(s) to the miniport.

Address state changes that a miniport can be enabled to report have the form LINEADDRESSSTATE_XXX and include such states as LINEADDRESSSTATE_INUSEMANY and LINEADDRESSSTATE_FORWARD.

Line state changes a miniport can be enabled to report have the form LINEDEVSTATE_XXX and include such states as LINEDEVSTATE_RINGING, LINEDEVSTATE_CONNECTED, LINEDEVSTATE_OPEN and LINEDEVSTATE_CLOSE.

A miniport WAN NIC driver can receive one or more OID_TAPI_SET_STATUS_MESSAGES request(s) to inform the miniport of which status changes it must report. Initially, a miniport should consider all line and address status indications disabled with the exception of LINEDEVSTATE_REINIT, which is never disabled.

When a miniport calls NdisMIndicateStatus to report such state changes, the GeneralStatus argument for reporting TAPI status changes is always NDIS_STATUS_TAPI_INDICATION. The StatusBuffer parameter points to a structure of type NDIS_TAPI_EVENT. The miniport must pass the htLine and htCall members that identify the line and call to which the state change applies. The ulMsg member of the NDIS_TAPI_EVENT structure is set to, for instance, LINE_CALLSTATE and ulParam1 describes the state being reported.


6.5 Setting Up Handles

After the NDISTAPI driver has initialized a miniport as a provider, the miniport can expect a request to open a line device at its MiniportQueryInformation function specifying OID_TAPI_OPEN and the appropriate parameters. Upon receiving an open request the miniport would typically do the following:

  • Mark the line device as open in the miniport’s internal data structures.

  • Save the NDISTAPI driver’s handle to the line device (passed in the htLine member), which is to be used in subsequent event notifications.

  • Return its own handle to the line device (passed back in the hdLine member).

Once again, the impetus for this request would be a client process calling lineOpen.

After a client process has successfully opened a line, it can place a call on that line by calling lineMakeCall. This will result in the NDISTAPI driver’s calling NdisRequest with an OID_TAPI_MAKE_CALL, specifying the NDISTAPI-supplied handle to the line in the htLine member, and the NDISTAPI-supplied handle to the call in the htCall member. This request is passed to the miniport’s MiniportQueryInformation function, specifying OID_LINE_MAKE_CALL. If the NIC driver can successfully create the call, it passes back its own handle to the call in the hdCall member.

The two-stage ht Xxx/hd Xxx (“handle of TAPI object Xxx/handle of Driver object Xxx”) handle scheme provides both the NDISTAPI driver and the WAN miniport NIC driver the flexibility of defining their own means of reference for the same logical object. Requests made to NIC drivers specify driver handles (hdLines and hdCalls), while notifications made to the NDISTAPI driver contain TAPI handles (htLines and htCalls). In the already mentioned cases where the miniport NIC driver is defining its driver handle to some object (for example, hdLine, hdCall), it is typically to the advantage of the NIC driver to specify a pointer to the appropriate driver-internal data structure that represents the object.

When the client process has finished with the call it will call lineDrop to drop the call, and then lineDeallocateCall to free the call instance. This will result in the NDISTAPI driver sending two more requests, OID_TAPI_DROP and OID_TAPI_CLOSE_CALL, respectively, through NDISWAN to the miniport NIC driver to drop the call and deallocate the call instance. It is also possible that the miniport will get an OID_TAPI_CLOSE_CALL request without a preceding OID_TAPI_DROP, in which case the miniport should drop the call and deallocate the call instance as if it had received both OIDs.

Finally, when the last TAPI client has called lineClose, the NDISTAPI driver will call NdisRequest to send the miniport an OID_TAPI_CLOSE request at its MiniportSetInformation function. An OID_TAPI_CLOSE can also be received on an active line. In this case, the miniport must complete or abort all outstanding calls and asynchronous requests on the device and close the line. An OID_TAPI_CLOSE request with a valid hdLine cannot be failed.


6.6 TAPI Registration

A WAN miniport sets itself to accept TAPI commands in two steps. When the miniport registers with NDIS in its DriverEntry function by calling NdisMRegisterMiniport, it indicates that it requires WAN services by setting the flag NDIS_USE_WAN_WRAPPER in the Reserved member of the NDIS_WAN_MINIPORT_CHARACTERISTICS structure.

Following execution of the DriverEntry function and after MiniportInitialize has been called to complete the initialization of one or more adapters, the NDISWAN driver will make an NdisRequest call to the WAN miniport’s MiniportQueryInformation function, with an OID_WAN_GET_INFO request. A miniport that is TAPI-capable should set the TAPI_PROVIDER bit of the FramingBits member of the NDIS_WAN_INFO structure passed in this call.

The TAPI-capable WAN miniport sets the MaxTransmit member of the NDIS_WAN_INFO structure to the default maximum number of outstanding send packets the miniport will accept. NDIS will hold send packets after this maximum is reached. However, a TAPI-capable miniport can dynamically provide a new send window that is different from this initialization-time default value. When the miniport indicates a line up status to NdisMIndicateStatus, it can provide a new nonzero value for that line by setting the SendWindow passed in the NDIS_MAC_LINE_UP structure. If the miniport passes a SendWindow of zero, NDISWAN uses the default value, MaxTransmit, that was set at initialization time for that line.

As the WAN driver’s MiniportInitialize is called to initialize each adapter it manages, NDISWAN calls NDISTAPI to register each TAPI-capable adapter. This creates an association that NDISTAPI can use to send telephonic requests through NDISWAN to the miniport for specific adapters that have been registered.


6.7 TAPI Initialization

After a WAN miniport has registered, been initialized and indicated that it is a TAPI provider, MiniportQueryInformation is called with an OID_TAPI_PROVIDER_INITIALIZE request. There is a one-to-one relationship between a provider and a TAPI-capable adapter. The NDIS_TAPI_PROVIDER_INITIALIZE structure passed in the call contains a ulDeviceIDBase representing an offset. The miniport should add this offset to its zero-based line device identifiers when it refers to a device in subsequent OID responses. If the miniport manages two devices and it is passed a ulDeviceIDBase of n, those devices will be referenced in future calls as n and n+1, for instance in the ulDeviceID member passed to the miniport in the structure for an OID_TAPI_OPEN.

The miniport should fill in two members of the OID structure before returning from MiniportQueryInformation. The ulNumLineDevs is set to the number of line devices supported by the adapter. The NIC driver should also set the ulProviderID member to a unique (per adapter) value. Since there is currently no means to guarantee unique ulProviderID values, a good strategy is to pass the NDIS handle passed to MiniportInitialize as the ulProviderID. The ulRequestID member of the structure is reserved for NDISTAPI and is opaque to the miniport.

The OID_TAPI_PROVIDER_INITIALIZE request sent to registered WAN miniport NIC drivers is always the result of a higher-level telephony module being loaded and initialized by the first client process. A miniport will only receive the OID_TAPI_PROVIDER_INITIALIZE request once per adapter when the first client process on the machine makes a lineInitialize request.

Communicating Device Characteristics

After the WAN miniport has returned the number of devices it supports, its MiniportQueryInformation function will be called to return the capabilities of its devices with an OID_TAPI_GET_DEV_CAPS and to return its device configuration with an OID_TAPI_GET_DEV_CONFIG. This information describes how the WAN miniport models its device. For instance, the same ISDN adapter can be modelled as:

  • 1 line, with 1 address, supporting 2 calls per address

  • 1 line, with 2 addresses on this line, supporting one call per address

  • 2 lines, each with 1 address, supporting 1 call per address

A miniport will be queried for its characteristics using these already mentioned OIDs, once for each device that it indicated in its response to OID_TAPI_PROVIDER_INITIALIZE.


6.8 Line Open

A line is opened as a result of an application making a lineOpen request. The request is passed to the miniport by TAPI to NDISTAPI to NDISWAN through NDIS as an OID_TAPI_OPEN request. This request passes the NDIS_TAPI_OPEN structure specifying a device identifier, ulDeviceID, the device on which the line is to be opened. In addition, the structure contains htLine, a handle by which NDISTAPI refers internally to the line. The miniport must retain this line handle and return it in any status indications or responses to queries for that line, for instance when it reports a state change using NdisMIndicateStatus.

The miniport creates a structure to represent the line in which to keep its line-specific context. The miniport should return a value representing this context in the hdLine member of NDIS_TAPI_OPEN before returning from MiniportQueryInformation. The line-specific value in hdLine will be passed to the miniport in subsequent calls concerning this line, in the OIDs passed to MiniportQueryInformation and MiniportSetInformation requests, for instance, in an OID_TAPI_MAKE_CALL.

Setting Modes On a Line

If an application makes a lineOpen request with owner privilege, the application indicates it wants to receive incoming calls. The application must specify the valid types of incoming calls it will accept on that line. For instance, an application could request to receive such types as g3fax, interactive voice, datamodem, and so forth. MiniportSetInformation can be called one or more times with the OID_SET_DEFAULT_MEDIA_DETECTION request to set up the types the miniport should accept on behalf of a client.

If more than one application sets the media types, and one application’s list is different from another application’s list, the miniport will be called to set the media-detection types to the union of those requested by the two applications. If an incoming call appears with a media mode type not specified in the preceding successfully completed SET_DEFAULT_MEDIA_DETECTION request for that line, the miniport should not indicate the new call to TAPI. If a miniport does not receive a SET_DEFAULT_MEDIA_DETECTION request for a line, it should not indicate any incoming calls; that line is to be used only for outbound calls.

A miniport NIC driver receives the OID_TAPI_CONDITIONAL_MEDIA_DETECTION request to determine whether it supports the indication of new incoming calls of a specified media mode type or types, without actually committing the miniport. If the miniport can monitor for the indicated set of media modes and can support the capabilities indicated in LineCallParams and can place a call of the specified type, it returns NDIS_STATUS_SUCCESS. If so, the miniport will most likely receive subsequent requests such as OID_TAPI_OPEN, OID_SET_DEFAULT_MEDIA_DETECTION, and so forth.


6.9 Accepting an Incoming Call

After a miniport has successfully completed an OID_TAPI_PROVIDER_INITIALIZE request, an OID_TAPI_OPEN request, and an OID_TAPI_SET_DEFAULT_MEDIA_DETECTION, it can receive an incoming call on the previously opened line. If the media type matches one it has been set to detect, the miniport should call NdisMIndicateStatus to notify upper layers of the offered call, passing NDIS_STATUS_TAPI_INDICATION as the GeneralStatus, and a pointer to the NDIS_TAPI_EVENT structure as the StatusBuffer.

The ulMsg member of NDIS_TAPI_EVENT is set to LINE_NEWCALL, and the ulParam1 is set to hdCall, the driver-assigned handle for the call. The htCall, ulParam2, and ulParam3 are set to zero. NDISTAPI will supply an htCall in ulParam2 before returning from NdisMIndicateStatus. The miniport should retain this handle and use it in any subsequent indications pertaining to this call instance. For instance, the miniport will supply this value as the ConnectionWrapperID when it indicates a line up to NDISWAN.

After the miniport has given a LINE_NEWCALL status indication, it should call NdisMIndicateStatus, with a LINECALLSTATE_XXX message, typically a LINECALLSTATE_OFFERING message. The ulParam3 of this message is set with the media mode of the incoming call.

If the call is accepted by an application, MiniportSetInformation will be called with an OID_TAPI_ACCEPT followed by an OID_TAPI_ANSWER request or simply with an OID_TAPI_ANSWER request.

If the miniport receives an OID_TAPI_ACCEPT request, it should allocate and initialize a call-state context for the call, but it should not answer the call until it receives an answer request. Later, if it receives an OID_TAPI_ANSWER request, it should do the following:

  • Answer the call.

  • Set the state of the line to connected and call NdisMIndicateStatus to report this new status if reporting of this status is enabled.

If the miniport receives an OID_TAPI_ANSWER request without first receiving an OID_TAPI_ACCEPT request, it should behave as if it had received both requests.

It is possible that a WAN NIC driver will never receive an OID_TAPI_ANSWER request or an OID_TAPI_ACCEPT request for an incoming call. This could occur when client applications are busy with other calls, or when clients are not interested in calls of the type indicated (for example, the media mode), and so forth. After waiting a certain amount of time for an answer or accept request or detecting that the remote party disconnected, the WAN NIC driver should notify the NDISTAPI driver that the call has transitioned to the idle state. Then, at some later time, an OID_TAPI_CLOSE_CALL request will be sent to the WAN NIC driver to free up the call instance. It is also possible that after the miniport offers the incoming call, it will receive an OID_TAPI_CLOSE_CALL before it can time out the new call offer.


6.10 Making a Call

A miniport with an open line can also receive a request to initiate a call in one of two ways. Either the miniport receives:

  • An OID_TAPI_MAKE_CALL request that contains the destination address or that specifies that a predefined default number should be used

  • Or, an OID_TAPI_MAKE_CALL request that does not include a destination address

    In this case, the OID_TAPI_MAKE_CALL will be followed by an OID_TAPI_DIAL request that includes the destination address

When MiniportQueryInformation is called with an OID_TAPI_MAKE_CALL request, NDISTAPI passes the line identifier for the line on which the call is to be made in the hdLine member of the NDIS_TAPI_MAKE_CALL structure. The hdLine value is the value returned by the miniport to an OID_TAPI_OPEN request.

The miniport should store the htCall value passed in this request with the call context it maintains and pass it in subsequent status calls and responses related to this call instance. For instance, the miniport will supply this value as the ConnectionWrapperID when it indicates a line up to NDISWAN.

The ulDestAddressSize can be zero for undialed addresses, such as for a hot phone, which always automatically connects to a predefined number, or when dialing will be done subsequently with an OID_TAPI_DIAL request.

If the bUseDefaultLineCallParams member of NDIS_TAPI_MAKE_CALL is set to FALSE, the miniport should use the call parameters in the LineCallParams member. If bUseDefaultLineCallParams is TRUE, the previously set default call parameters should be used.

The miniport must check the state of the line on which the make-call request is made to ensure that it can make the requested call. If not, the miniport should fail the call.

MiniportQueryInformation returns when either:

  • It has made the call,

  • Or, if the number will be dialed as a result of a subsequent OID_TAPI_DIAL request, the miniport allocates a structure and sets itself in a state, probably the dialtone state, to subsequently complete the call.

The miniport must return a call-specific context value in the hdCall member if MiniportQueryInformation returns NDIS_STATUS_SUCCESS. This unique hdCall value will be passed to the miniport in subsequent requests related to this call.

If a call is made in two steps by first making an OID_TAPI_MAKE_CALL request to MiniportQueryInformation, followed by an OID_TAPI_DIAL request to MiniportSetInformation, the destination number is passed to the miniport in the NDIS_TAPI_DIAL structure. The OID_TAPI_DIAL request requires an hdCall member to be passed in the NDIS_TAPI_DIAL structure; its value will match the handle the miniport returned from a previous OID_TAPI_MAKE_CALL request.


6.11 Unsolicited Event Handling

During the course of a telephonic session, the WAN miniport NIC driver will most likely need to notify the NDISTAPI driver of unsolicited events. Typical examples of unsolicited events are an incoming call on an open line, a state transition on an existing call (for example, from ringing to connected), or a change in line-device status (from in-service to out-of-service or vice versa).

The NDIS_STATUS_TAPI_INDICATION status is used when reporting spontaneously occurring events on the line or on a call on the line to the NDISTAPI driver. The miniport calls NdisMIndicateStatus, specifying a GeneralStatus of NDIS_STATUS_TAPI_INDICATION and a StatusBuffer parameter which points to an initialized structure of type NDIS_TAPI_EVENT.

This structure contains the following members:
Structure Member | Meaning
htLine | Specifies the NDISTAPI driver’s handle for the line on which the event occurred.
htCall | For line-related events where there is no call, this parameter should be zero; otherwise, it is the NDISTAPI driver’s handle for the call related to the reported event.
ulMsg | Specifies the event type, of the form LINE_XXX.
ulParam1 | An event-specific parameter, for instance, a pointer to a structure that describes the specific event.
ulParam2 | Same as above.
ulParam3 | Same as above.

The notification messages of concern to miniport developers are the following:

  • LINE_ADDRESSSTATE

  • LINE_CALLINFO

  • LINE_CALLSTATE

  • LINE_CLOSE

  • LINE_DEVSPECIFIC

  • LINE_LINEDEVSTATE

  • LINE_NEWCALL

  • LINE_CALLDEVSPECIFIC


6.12 Line-Up Indication

From the telephony standpoint, the life of a call is a series of state transitions. For outgoing calls, a typical sequence of states might be: dialing, proceeding, ringing, connected, disconnected, and finally, idle. For an incoming call, this sequence might look as follows: offering, ringing, connected, disconnected, and finally, idle. In either case, the transition to the connected state is of particular interest, since this is the state in which data can flow across the wire.

A WAN miniport calls NdisMIndicateStatus with a GeneralStatus value of NDIS_STATUS_WAN_LINE_UP to indicate that a new data channel has become active. The buffer at StatusBuffer is formatted as an NDIS_MAC_LINE_UP structure. Prior to indicating a line up, the adapter will accept frames and can let them succeed or fail, but it is unlikely that such packets will actually be received by any remote system. Before a line-up indication, protocols are encouraged to reduce their timers and retry counts so that any outgoing connection attempts quickly fail.

An NDIS_STATUS_WAN_LINE_UP status must be completed prior to returning from the OID_TAPI_GET_ID request. The NdisLinkContext returned from the NDIS_STATUS_WAN_LINE_UP indication is passed back in the NDIS_TAPI_GET_ID structure as the DeviceID.

The NDIS_STATUS_WAN_LINE_UP must also be completed prior to indicating the LINECALLSTATE_CONNECTED to NDISWAN. Otherwise, the user-mode client of the connection might attempt to send data on a connection before NDISWAN is aware of the line.

When the line-up indication completes, NDISWAN and the miniport have exchanged handles, one that NDISWAN uses to subsequently direct send packets onto a data channel and the other that the miniport uses to identify the correct data channel for sends and in any status indications it makes to NDISWAN. Before NDISWAN gets a line-up indication and establishes a data channel for a particular call instance, it will not pass send packets on to the miniport’s MiniportWanSend function.

A miniport indicates a line-up status in the context of an OID_TAPI_GET_ID request if either of the following occurs:

  • The ulSelect member of NDIS_TAPI_GET_ID structure is set to LINECALLSELECT_CALL, and

  • This request is the first OID_TAPI_GET_ID request for the hdCall member of the NDIS_TAPI_GET_ID structure

or

  • The ulSelect member of NDIS_TAPI_GET_ID structure is set to LINECALLSELECT_CALL, and

  • This request is not the first OID_TAPI_GET_ID request for the hdCall member of the NDIS_TAPI_GET_ID structure, but the DeviceClass is different from any previous get-id request for this call instance, and

  • The miniport supports separate data channels for different device classes.

The StatusBuffer passed to NdisMIndicateStatus should point to a buffer of type NDIS_MAC_LINE_UP. This structure has the following definition:

typedef struct _NDIS_MAC_LINE_UP {
IN ULONG LinkSpeed;
IN NDIS_WAN_QUALITY Quality;
IN USHORT SendWindow;
IN NDIS_HANDLE ConnectionWrapperID;
IN NDIS_HANDLE NdisLinkHandle;
IN OUT NDIS_HANDLE NdisLinkContext;
} NDIS_MAC_LINE_UP, *PNDIS_MAC_LINE_UP;

The WAN miniport NIC driver should set the value of the ConnectionWrapperID member in the NDIS_MAC_LINE_UP structure to that of the htCall (the TAPI call handle) associated with the call because it is guaranteed to be unique across all telephony-capable miniports. The ConnectionWrapperID is valid until the miniport issues a line-down indication.

The NdisLinkHandle represents the miniport’s handle for this data channel on this call instance. Typically it references the state the miniport maintains for this call. NDISWAN will retain this handle and return it to the miniport on any sends on this data channel. The NdisLinkContext is set to NULL when the miniport calls NdisMIndicateStatus. It will be set to a value supplied by NDISWAN before returning from NdisMIndicateStatus. This opaque handle must be retained by the miniport and supplied in subsequent calls to NDISWAN regarding this link, for instance to NdisMWanIndicateReceive.

The NdisLinkContext must be returned in the DeviceID member of NDIS_TAPI_GET_ID when MiniportQueryInformation returns from the OID_TAPI_GET_ID request. This context binds the call connection and the device class as seen by NDISTAPI with a handle that NDISWAN recognizes and has tied to a particular data channel. NDISWAN uses this handle to ensure that packets from a TAPI client are directed by NDISWAN to the correct miniport data channel.

Changing the Send Window

The SendWindow member of the NDIS_MAC_LINE_UP structure specified in a line-up indication acts as a throttle, controlling how many packets NDISWAN passes to a MiniportWanSend function for a particular line before it starts queuing packets. The MaxTransmit member of NDIS_WAN_INFO returned in response to the OID_WAN_GET_INFO request during driver initialization represents a default value for this window for all lines supported by an adapter. The miniport can reset the send window value on a per-line basis when it makes the first line-up indication for a new line. It can also change this send window value for a line dynamically by making a subsequent line-up indication to NDISWAN with a new nonzero SendWindow. That is, the miniport does not have to wait for an incoming or outgoing call to generate a line-up indication. If a miniport specifies a SendWindow of zero, NDISWAN will revert to using the default MaxTransmit value as the send window.

If the miniport makes a line-up indication to change the SendWindow, it must fill in both the NdisLinkHandle and NdisLinkContext members with the values that apply to the data channel on which the send window is to be changed. LinkSpeed and Quality are set to zero or possibly to new values. Both LinkSpeed and Quality can also be changed dynamically and independently by making a line-up indication and resetting the value(s).


6.13 Shutdown

A session begins when a miniport successfully completes an OID_TAPI_PROVIDER_INITIALIZE request. Within a session, one or more lines can be opened with an OID_TAPI_OPEN request, and on a line, one or more calls can be established by OID_TAPI_MAKE_CALL, OID_TAPI_DIAL, or OID_TAPI_ANSWER requests. During the time a line is up, many calls can be established and then closed or dropped. During a session, one or more lines can go through transitions from open to closed many times. How a miniport handles such transitions is described here.

Closing a Call

An in-process call can be closed either by the local node or by the remote node. The call can be closed on the local node, either because the last application with a handle to the call has closed the handle, or perhaps because the miniport driver’s MiniportHalt or MiniportReset has been called. If the remote node hangs up an in-process call, the miniport must indicate this status change to upper layers.

If an application on the local node closes the call, MiniportSetInformation will be called either with an OID_TAPI_DROP request followed by an OID_TAPI_CLOSE_CALL, or it will simply be called with an OID_TAPI_CLOSE_CALL.

If the miniport receives an OID_TAPI_DROP request, it must do the following:

  1. Drop the call.

  2. Change the status of the call to LINECALLSTATE_IDLE and report the state change by calling NdisMIndicateStatus.

The handles for the dropped call, htCall and hdCall remain valid. The miniport must not deallocate, reuse, or otherwise modify the state associated with the hdCall that it created for the call instance, since the miniport can receive queries related to the call, for instance a request for statistics related to the call. If MiniportHalt or MiniportReset is called, the miniport should disconnect the call but retain the state maintained for the call instance until the miniport receives an OID_TAPI_CLOSE_CALL request.

MiniportSetInformation can also receive an OID_TAPI_CLOSE_CALL request when the last application with an open handle to the call instance closes the handle. The miniport cannot fail this close call request. After MiniportSetInformation returns after processing this request, htCall is no longer valid.

A miniport must be prepared to handle timing issues involving such occurrences as getting an OID_TAPI_CLOSE_CALL before getting an OID_TAPI_DROP. If the miniport gets an OID_TAPI_CLOSE_CALL without a preceding OID_TAPI_DROP, it should behave as if it had received both a drop and a close call request. If the miniport receives a drop after a close call request has already closed the referenced call, the call handle is no longer valid and the miniport should return an appropriate error message.

If the miniport detects that the remote node has dropped the call, the miniport should change the call state to LINECALLSTATE_DISCONNECTED and indicate the change by calling NdisMIndicateStatus passing the htCall for this call instance. After the state change is propagated to the connected application on the local node, the miniport will receive an OID_TAPI_DROP followed by an OID_TAPI_CLOSE_CALL, or perhaps just the OID_TAPI_CLOSE_CALL request. The miniport should handle the drop and close call requests as described earlier.

Closing a Line

A line is closed when the last application with an open handle to the line has closed the handle. When the miniport subsequently receives an OID_TAPI_CLOSE, it should tear down the line instance, that is, any state related to the line maintained by the miniport can be reused or deallocated because the information is no longer valid.

An OID_TAPI_CLOSE call must succeed if the line handle, htLine, passed in the call exists and is still valid. A miniport will never receive an OID_TAPI_CLOSE request for a line if there are calls on the line. All existing calls on a line will be closed with an OID_TAPI_CLOSE_CALL request before the line-close request is made.

Existing calls must be closed when a line is closed; the miniport should drop those calls and clean up their state. The miniport should also change the state of the line to idle. Any handle the miniport has for the line is no longer valid. If the miniport receives any requests which pass the htLine handle or any htCall after the close request is received, such request(s) must be failed with an appropriate status.

Closing a Session

Session termination can be initiated by either the upper layers or a WAN miniport NIC driver. When the last client process has detached from the higher-level Telephony module, the NDISTAPI driver will be informed that it needs to terminate its session with each of the registered adapters. The NDISTAPI driver does this by calling NdisRequest specifying OID_TAPI_PROVIDER_SHUTDOWN. Upon receiving this request, NDISWAN will call the miniport NIC driver’s MiniportSetInformation function, which should terminate any related activities it has in progress on the specified adapter and release any relevant resources.

Driver-initiated session termination can occur when the miniport NIC driver is being unloaded in its MiniportHalt function. Typically, the miniport NIC driver would complete any outstanding NDISTAPI driver requests and notify the NDISWAN that its line devices are going off-line by calling NdisMIndicateStatus. NDISWAN would, in turn, call the NDISTAPI driver to deregister the adapter. If the miniport NIC driver were reloaded again later, it would go through the same initialization process described previously.

The miniport NIC driver might also initiate session termination if it underwent some dynamic reconfiguration that necessitated a complete reinitialization of all clients and drivers. For example, if an adapter’s line-device modeling (for example, the number of line devices supported) was changed on the fly, the miniport would call NdisMIndicateStatus with a LINE_LINEDEVSTATE event and the LINEDEVSTATE_REINIT flag set.


6.14 The NDISTAPI Interface

The following is a summary of the OID requests that define the interface between the NDISTAPI driver, NDISWAN and underlying WAN NIC miniport drivers. Each of these operations has a corresponding SPI function defined in the Telephony Software Development Kit, documented in the Win32 SDK.

WAN miniport developers should consult the SPI specifications when designing and implementing drivers. Note that function definitions and parameters might differ slightly between the NDISTAPI specification and the SPI specification, in which case the developer should adhere to the NDISTAPI driver Interface specification.
OID | Optional or Required
OID_TAPI_ACCEPT | Optional
OID_TAPI_ANSWER | Required
OID_TAPI_CLOSE | Required
OID_TAPI_CLOSE_CALL | Required
OID_TAPI_CONDITIONAL_MEDIA_DETECTION | Required
OID_TAPI_CONFIG_DIALOG | Optional
OID_TAPI_DEV_SPECIFIC | Optional
OID_TAPI_DIAL | Optional
OID_TAPI_DROP | Required
OID_TAPI_GET_ADDRESS_CAPS | Required
OID_TAPI_GET_ADDRESS_ID | Required
OID_TAPI_GET_ADDRESS_STATUS | Required
OID_TAPI_GET_CALL_ADDRESS_ID | Required
OID_TAPI_GET_CALL_INFO | Required
OID_TAPI_GET_CALL_STATUS | Required
OID_TAPI_GET_DEV_CAPS | Required
OID_TAPI_GET_DEV_CONFIG | Optional
OID_TAPI_GET_EXTENSION_ID | Optional
OID_TAPI_GET_ID | Required
OID_TAPI_GET_LINE_DEV_STATUS | Required
OID_TAPI_MAKE_CALL | Required
OID_TAPI_NEGOTIATE_EXT_VERSION | Optional
OID_TAPI_OPEN | Required
OID_TAPI_PROVIDER_INITIALIZE | Required
OID_TAPI_PROVIDER_SHUTDOWN | Required
OID_TAPI_SECURE_CALL | Optional
OID_TAPI_SELECT_EXT_VERSION | Optional
OID_TAPI_SEND_USER_USER_INFO | Optional
OID_TAPI_SET_APP_SPECIFIC | Required
OID_TAPI_SET_CALL_PARAMS | Required
OID_TAPI_SET_DEFAULT_MEDIA_DETECTION | Required
OID_TAPI_SET_DEV_CONFIG | Optional
OID_TAPI_SET_MEDIA_MODE | Required
OID_TAPI_SET_STATUS_MESSAGES | Required

Definitions for these OIDs are found in ntddndis.h. Definitions for NDIS_TAPI_EVENT messages are found in ndistapi.h.

The extent to which a given WAN NIC driver supports this interface depends in large part on the discretion of the miniport developer and the capabilities of the underlying hardware. The functional requirements are quite simple for several of the requests; for example, the OID_TAPI_SET_APP_SPECIFIC request only requires the driver to associate a ULONG value with a specified call instance, which can be retrieved later through an OID_TAPI_GET_CALL_INFO request.

Requests that the NIC driver developer chooses not to support should be completed with the NDIS_STATUS_TAPI_OPERATIONUNAVAIL status code. The NIC driver can indicate what types of requests it supports (which can vary over time depending on line, address, or call state) by setting or clearing specific bits in the members of structures present in some of the OID_TAPI_GET_XXX requests (for example, the ulAddrCapFlags member in the LINE_ADDRESS_CAPS structure).


6.14.1 Query Information Requests

Query information-type requests differ from set information-type requests in that one or more members in the request structure, denoted by the OUT prefix in the structure definition, are reserved for information to be passed back to the calling process. Members prefixed with IN should be considered read-only. The first member in all query information requests, ulRequestID, is reserved for future use, and should be ignored.

Definitions for the following structures are found in ndistapi.h.

OID_TAPI_DEV_SPECIFIC

This request is used as a general extension mechanism to enable WAN NIC drivers to provide access to features not described in other requests. The meaning of the extensions are device-specific, and taking advantage of these extensions requires the application to be fully aware of them. This function uses an NDIS_TAPI_DEV_SPECIFIC structure.

OID_TAPI_GET_ADDRESS_CAPS

This request queries the specified address on the specfied line device to determine its telephony capabilities. This request uses an NDIS_TAPI_GET_ADDRESS_CAPS structure.

OID_TAPI_GET_ADDRESS_ID

This request returns the address ID associated with the address in a different format on the specified line. This request uses an NDIS_TAPI_GET_ADDRESS_ID structure.

OID_TAPI_GET_ADDRESS_STATUS

This request queries the specified address for its current status and uses an NDIS_TAPI_GET_ADDRESS_STATUS structure.

OID_TAPI_GET_CALL_ADDRESS_ID

This request retrieves the address ID for the indicated call using an NDIS_TAPI_GET_CALL_ADDRESS_ID structure.

OID_TAPI_GET_CALL_INFO

This request returns detailed information about the specified call using an NDIS_TAPI_GET_CALL_INFO structure.

OID_TAPI_GET_CALL_STATUS

This request returns the current status of the specified call using an NDIS_GET_CALL_STATUS structure.

OID_TAPI_GET_DEV_CAPS

This request queries a specified line device to determine its telephony capabilities. The returned information is valid for all addresses on the line device. The request uses an NDIS_TAPI_GET_DEV_CAPS structure.

OID_TAPI_GET_DEV_CONFIG

This request returns a data structure object, the contents of which are specific to the line (WAN miniport NIC driver) and device class, giving the current configuration of a device associated one-to-one with the line device. This request uses an NDIS_TAPI_GET_DEV_CONFIG structure.

OID_TAPI_GET_EXTENSION_ID

This request returns the extension ID that the WAN miniport NIC driver supports for the indicated line device. The request uses an NDIS_TAPI_GET_EXTENSION_ID structure.

OID_TAPI_GET_ID

This request returns a device ID for the specified device class associated with the selected line, address, or call. The request uses an NDIS_TAPI_GET_ID structure.

OID_TAPI_GET_LINE_DEV_STATUS

This request queries the specified open line device for its current status. The information returned is global to all addresses on the line. The request uses an NDIS_TAPI_GET_LINE_DEV_STATUS structure.

OID_TAPI_MAKE_CALL

This request places a call on the specified line to the specified destination address. Optionally, call parameters can be specified if anything but default call-setup parameters are requested. The request uses an NDIS_TAPI_MAKE_CALL structure.

OID_TAPI_NEGOTIATE_EXT_VERSION

This request returns the highest version extension number the miniport is willing to operate under for this device, given the range of possible extension versions. The request uses an NDIS_TAPI_NEGOTIATE_EXT_VERSION structure.

OID_TAPI_OPEN

This request opens the line device whose device ID is given, returning the NIC driver’s handle for the device. The NIC driver must retain the NDISTAPI driver’s handle for the device for use in subsequent calls to the LINE_EVENT callback procedure. The request uses an NDIS_TAPI_OPEN structure.

OID_TAPI_PROVIDER_INITIALIZE

This request initializes the WAN miniport NIC driver to handle TAPI operations. The request uses an NDIS_TAPI_PROVIDER_INIT structure.


6.14.2 Set Information Requests

Set information requests return no information other than completion status. All request structure members should be considered as read-only, and should not be modified by WAN miniport NIC drivers. The first member in all query information requests, ulRequestID, is reserved for future use, and should be ignored.

Definitions for the following structures are found in ndistapi.h.

OID_TAPI_ACCEPT

This request accepts the specified offered call. It may optionally send the specified user-to-user information to the calling party. The request uses an NDIS_TAPI_ACCEPT structure.

OID_TAPI_ANSWER

This request answers the specified offering call. It may optionally send the specified user-to-user information to the calling party. The request uses an NDIS_TAPI_ANSWER structure.

OID_TAPI_CLOSE

This request closes the specified open line device after completing or aborting all outstanding calls and asynchronous requests on the device. The request uses an NDIS_TAPI_CLOSE structure.

OID_TAPI_CLOSE_CALL

This request deallocates the call after completing or aborting all outstanding asynchronous requests on the call. The request uses an NDIS_TAPI_CLOSE_CALL structure.

OID_TAPI_CONDITIONAL_MEDIA_DETECTION

This request is made by the NDISTAPI driver whenever a client application uses LINEMAPPER as the dwDeviceID in the lineOpen function to request that lines be scanned to find one that supports the desired media mode or modes and call parameters.

The NDISTAPI driver scans based on the union of the desired media modes and the other media modes currently being monitored on the line, to give the miniport the opportunity to indicate if it cannot simultaneously monitor for all of the requested media modes. If the miniport can monitor for the indicated set of media modes, and support the capabilities indicated in LineCallParams, and can support a call of the specified type at that time, it replies with a “success” indication. The miniport leaves the active media monitoring modes for the line unchanged. The request uses an NDIS_TAPI_CONDITIONAL_MEDIA_DETECTION structure.

OID_TAPI_DIAL

This request dials the specified dialable number on the specified call. The request uses an NDIS_TAPI_DIAL structure.

OID_TAPI_DROP

This request drops or disconnects the specified call. User-to-user information can optionally be transmitted as part of the call disconnect. This request can be initiated by the application at any time. When OID_TAPI_DROP returns with success, the call should be idle. The request uses an NDIS_TAPI_DROP structure.

OID_TAPI_PROVIDER_SHUTDOWN

This request shuts down the miniport. The miniport should terminate any in-progress activities and make line-down indications to NDISWAN for any lines left open. The request uses an NDIS_TAPI_PROVIDER_SHUTDOWN structure.

OID_TAPI_SECURE_CALL

This request secures the call from any interruptions or interference that may affect the call’s media stream. The request uses an NDIS_TAPI_SECURE_CALL structure.

OID_TAPI_SELECT_EXT_VERSION

This request selects the indicated extension version for the indicated line device. Subsequent requests operate according to that extension version. The request uses an NDIS_TAPI_SELECT_EXT_VERSION structure.

OID_TAPI_SEND_USER_USER_INFO

This request sends user-to-user information to the remote party on the specified call. The request uses an NDIS_TAPI_SEND_USER_INFO structure.

OID_TAPI_SET_APP_SPECIFIC

This request sets the application-specific member of the specified call’s LINECALLINFO structure. The request uses an NDIS_TAPI_SET_APP_SPECIFIC structure.

OID_TAPI_SET_CALL_PARAMS

This request sets certain call parameters for an existing call. The request uses an NDIS_TAPI_SET_CALL_PARAMS structure.

OID_TAPI_SET_DEFAULT_MEDIA_DETECTION

This request informs the miniport of the new set of media modes to detect for the indicated line (replacing any previous set). The request uses an NDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION structure.

OID_TAPI_SET_DEV_CONFIG

This request restores the configuration of a device associated one-to-one with the line device from a data previously obtained using OID_TAPI_GET_DEV_CONFIG. The contents of this data structure are specific to the line (WAN NIC driver) and device class. The request uses an NDIS_TAPI_SET_DEV_CONFIG structure.

OID_TAPI_SET_MEDIA_MODE

This request changes a call’s media mode as stored in the call’s LINE_CALL_INFO structure. The request uses an NDIS_TAPI_SET_MEDIA_MODE structure.

OID_TAPI_SET_STATUS_MESSAGES

This request specifies which notification messages the NIC driver should generate for events related to status changes for the specified line or any of its addresses. By default, address and line-status reporting is initially disabled for a line. The request uses an NDIS_TAPI_SET_STATUS_MESSAGES structure.

OID_TAPI_CONFIG_DIALOG

This request retrieves the name of a user-mode dynamic link library that can be loaded and called to configure the specified device. The configuration DLL shall export the following function by name:

LONG
 WINAPI
 ConfigDialog(
IN HWND hwndOwner,
IN ULONG ulDeviceID,
IN LPCSTR lpszDeviceClass
);

This request uses an NDIS_TAPI_CONFIG_DIALOG structure.


Chapter 7 NDIS NIC Driver Installation

This describes special installation requirements for two types of NDIS NIC drivers, in particular the following:

  • WAN miniport drivers in Section 7.1

  • PCMCIA NIC drivers in Section 7.2


7.1 WAN Miniport Driver Installation

It is the responsibility of each hardware vendor to install the appropriate device driver(s) into the Windows NT system. The following sections describe the installation and configuration expected for serial and NDIS WAN NIC drivers.

Installation of WAN adapters for RAS occurs in two phases:

  1. The user installs a WAN adapter using vendor-specific setup program.

  2. The user configures Remote Access to use the installed adapter.

The Remote Access configuration utility reads the Windows NT HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\RAS\TAPI DEVICES registry key in order to find available ports.

Furthermore, the setup program should prevent port name contention by reading the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\RAS\TAPI DEVICES registry key prior to port configuration. The setup program can then prevent the user from selecting a port name that is already in use. For example, if COM1 is already registered under the DEVICEMAP key, do not allow the user to select the name “COM1” for subsequent ports.


7.1.1 Installing NDIS WAN drivers

This section provides information on how to install an NDIS WAN driver such that Remote Access setup can be configured to use that driver.

Write a configuration utility that installs the driver. Package the configuration utility as a DLL that can be started from the network control panel by using an installation script (.inf file). Users can then install the NIC by using the network control panel application, clicking the “Add Adapter” button, and installing the correct driver.

Port Naming

ISDN Adapters: ISDN1, ISDN2, ... ISDNm
Switched 56 Adapters: Sw56_1, Sw56_2, ... Sw56_m
X.25 Adapters: X25_1, X25_2, ... X25_m.

where m represents the number of the card starting with one.

Windows NT Registry

Service providers should store their configured data in the following location and format in the registry. If the TAPI DEVICES key does not already exist, the service provider setup script should create the key and add the entries corresponding to the addresses configured by the user. This installation script should remove the service provider key and the corresponding value if (or when) the user deletes the WAN miniport from the network control panel application.

A detailed description of each key follows the registry layout shown here:

HKEY_LOCAL_MACHINE
SOFTWARE
MICROSOFT
RAS
TAPI DEVICES
Service Provider 1
Media Type: REG_SZ: media_type
Address: REG_MULTI_SZ: address1 address2 ..addressN

Service Provider 2
Media Type: REG_SZ: media_type
Address: REG_MULTI_SZ: address1 address2 ..addressN

. . .

Service Provider N
Media Type: REG_SZ: media_type
Address: REG_MULTI_SZ: address1 address2 ..addressN

Service Provider N
This is the name of the service provider. This name corresponds to the service name in the registry, for example, ISDNDRV, X25DRV, SW56DRV, and so forth.
Media Type
This is the name of the media type, for example ISDN, X25_, Sw56_, and so forth. RAS setup uses the ServiceProvider and MediaType entries to make the friendly name in the absence of a friendly name, for example, X25DRVX25_1, SW56DRVSw56_1, and so forth.
Address
This is a list of addresses that uniquely identifies each end point. Each address is a string made up of the following three elements:
  • Net adapter number ¾ this corresponds to the network card number associated with the device adapter. For example, if there are two ISDN cards associated with the same service provider, each card has a corresponding net adapter number. These net adapter numbers are enumerated in the NetworkCards key in the HKEY_LOCAL_MACHINE
    \SOFTWARE\Microsoft\Windows NT\CurrentVersion registry tree.

  • Line Number ¾ this is the number that identifies the particular line within each adapter.

  • End Point ¾ if a line supports more than one end point (for example, channel number in the case of ISDN or port number for serial ports), this value corresponds to that end point, otherwise it defaults to zero.

The following example shows how the registry might look if there were an ISDN adapter with one line supporting either or both channels; an X25 adapter supporting 4 virtual circuits; and two Switched 56K adapters, one with a single line and another with two lines, both adapters supported by the same driver. The NetworkCards key is assumed to have assigned the numbers 1, 2, 3, 4 to these adapters.

Example

ISDNDRV
Media Type: REG_SZ: ISDN
Address REG_MULTI_SZ “1 1 0” “1 1 1” “1 1 3”
X25DRV
Media Type: REG_SZ: X25_
Address: REG_MULTI_SZ: “2 1 0” “2 1 1" “2 1 2” “2 1 3”
SW56DRV
Media Type: REG_SZ: Sw56_
Address: REG_MULTI_SZ: “3 1 0” “4 1 0” “4 2 0”


7.1.2 Installing Serial Drivers

This provides information on how to install a serial driver (or serial driver emulator) so Remote Access setup can be configured to use that driver.

Write a configuration utility that installs the driver. Package the configuration utility as a DLL that can be started from the network control panel by using an installation script (.inf file). Users can then install the hardware by using the network control panel application, clicking the “Add Adapter” button, and installing the correct driver.

Port Naming

Serial Adapters (serial driver emulators): COM1, COM2, ... COMm

Windows NT Registry

For an X.25 adapter with a serial driver emulator, merge the [X.25_card_name] script section in with the Remote Access pad.inf script located in the RAS subdirectory of the Windows NT system directory, for example, winnt35\system32\ras. The Remote Access Service Administrator's Guide provides details on how to write a pad.inf script.

Indicate which COM ports the driver exposes to the system by storing information in the Windows NT registry's hardware devicemap as shown in the following example:

HKEY_LOCAL_MACHINE
    HARDWARE
        DEVICEMAP
            SERIALCOMM
                Serial0    REG_SZ  COM1
                Serial1    REG_SZ  COM2
                    .
                    .
                    .
                Serialn    REG_SZ  COMn

If there are one or more serial ports in the local system, the SERIALCOMM key will already exist. The serial driver installation script should then add the number of supported serial ports to the existing list of ports. When a serial driver is loaded, it must add the supported ports to the SERIALCOMM key because this information is updated every time the system is restarted.


7.2 PCMCIA Network Interface Card Support in Windows NT

This describes how to set up a PCMCIA network interface card in Windows NT platforms. This covers only PCMCIA NIC cards, and does not discuss how other PCMCIA devices (modems and SCSI controllers, for example) are configured or installed.

The pcmcia.sys DLL provides the supports services for PCMCIA devices in Windows NT. It configures the PCMCIA slots based on information it gathers from the registry. This information consists of the following:

  • IO Ports

  • Interrupts

  • Shared Memory Windows

  • Attribute Memory Windows

When Windows NT boots, pcmcia.sys communicates with each PCMCIA slot and determines whether the PCMCIA device is a network adapter. Based on this information, pcmcia.sys uses the tuple on the PCMCIA card (Manufacturer Id String, Device ID String, and CRC values) to locate card information in the registry. Each supported card must have an entry in the PCMCIA section of the registry. The layout of this section of the registry can be in either of the following two forms:

Format 1:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
\Pcmcia\DataBase

Manufacturer_Id_String

Device_Id_String

Driver: REG_SZ: DriverName // without the .sys extension

<Optional Parameters, if any>

For example:

3Com Corporation

3C589

Driver: REG_SZ: elnk3 // without the .sys extension

Format 2:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
\Pcmcia\DataBase

Manufacturer_Id_String

Device_Id_String

CRC

Driver: REG_SZ: DriverName

<Optional Parameters, if any

For example:

Xircom

Credit Card 10-Base-T

1234

Driver: REG_SZ: xcsiips

AttributeMemorySize: REG_DWORD: 0x1000


7.2.1 Optional Parameters in the PCMCIA Database

The optional parameters in the database inform pcmcia.sys how the slot is to be configured. This configuration information is needed since most network cards do not specify the correct tuple information. The following are the valid parameters in this section:

AttributeMemorySize: REG_DWORD: Size

CardMemorySize: REG_DWORD: Size

This information is stored in the driver section of the registry. Since only a single set of optional parameters can be associated with a PCMCIA card, this implies that only one card of a particular type can be in the machine.


7.2.2 Driver Section of the Registry

The actual configuration data is obtained from the driver section of the registry. For network cards, the driver section is represented by a short name selected by the NIC driver developer. For example, if ELNK3 PCMCIA is in the system, then its section is:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
 \ELNK32\Parameters

To distinguish a NIC driver section of a PCMCIA card from an ISA card when both use the same NIC driver, the following should be added by the .inf file to the parameters section:

Pcmcia: REG_DWORD: 1

The BusType value should be 1 (for NdisInterfaceIsa).

pcmcia.sys looks for specific keywords to configure the slot. The following are valid keywords.

IoBaseAddress: REG_DWORD: IOAddress

InterruptNumber: REG_DWORD: IRQ

MemoryMappedBaseAddress: REG_DWORD: host_address

MemoryMappedSize: REG_DWORD: size_of_window

PCCARDMemoryWindowOffset: REG_DWORD:
card_offset_for_window

PCCARDAttributeMemoryAddress : REG_DWORD : host_address

PCCARDAttributeMemorySize : REG_DWORD : size_of_window

PCCARDAttributeMemoryOffset : REG_DWORD :
card_offset_for_window

For more than one memory window, use:

MemoryMappedBaseAddress_x

MemoryMappedSize_x

PCCARDMemoryWindowOffset_x

where x is 1, 2, 3, and so forth.


Chapter 8 Network Interface Card Detection

This chapter provides an overview of the basic scenarios for detecting network interface cards. The chapter is divided into the following general categories:

  • Overview of Network Interface Card Detection in Section 8.1

  • Architecture of NIC Detection in Section 8.2

  • NIC Detection Usage in Section 8.3

  • OEM Interfaces for NIC Installation in Section 8.4

  • Initial Setup Detection Process and Secondary NIC Detection Installation Processes in Section 8.5


8.1 Overview of Network Interface Card Detection

The process of detecting a network interface card includes the following:

  • Initial Setup Detection (primary network interface card and NIC driver installation)

  • Configuration Querying (secondary NIC installation)

  • Configuration Verification (secondary NIC installation)

  • Resource Validation (secondary NIC installation)

The initial setup detection, configuration querying, and configuration verification are supported by DLLs associated with network interface cards. For more information about these DLLs, see Chapter 9. The resource validation process is implemented by functions contained in code for Setup or the NCPA.


8.2 Architecture of NIC Detection

Windows NT has a netdtect.sys support driver that allows user applications to perform a narrow range of physical I/O and memory operations. The driver exports its capabilities to user applications running in the Win32 subsystem. This is accomplished through the netdtect.dll, which verifies that interface resources, such as IRQs, are available for use.

Every NIC has an associated DLL that instructs the support driver DLL on how to interact with that network adapter. This allows the installer to interact directly with a NIC even though the NIC-specific driver has not been loaded.

The Windows NT NIC level DLL interface allows a variety of NICs to be controlled by a single DLL. For information on the entry points for the NIC detection DLLs, see Chapter 9.


8.3 NIC Detection Usage

This describes the usage model for the components used in detecting network adapters .

Primary NIC Installation

The initial setup detection process occurs once, during primary system installation. This process attempts to locate one instance of a known NIC. When this instance is located, the process ends.

This process is the primary objective of the Windows NT NIC-detection software. The results of this process determine subsequent recommendations made to the user.

The user interface allows the user to bypass initial detection, because this method can eliminate a possible halt or failure, particularly on an ISA bus machine.

Secondary NIC Installation

Any time a NIC is installed, either during main or OEM installation, the secondary detection processes are used to confirm proper installation. These secondary detection processes include configuration querying, configuration verification, and resource validation. Configuration querying presents the user with the current card settings. The configuration verification ensures the NIC can accept user selections. Resource validation ensures that the IRQ and other critical hardware configuration parameters do not conflict with other devices in the same computer.


8.4 OEM Interfaces for NIC Installation

This describes OEM interfaces for installing network interface cards.

Adding NICs to Existing Windows NT System

Windows NT supplies the basic driver and DLL used to communicate with the hardware. In addition, DLLs that support a family of NICs used in Windows NT platforms are supplied. The complete list of NIC-detection DLLs used by the NCPA and the Windows NT setup program is extracted from the registry during runtime. All DLL information is merged with information from the .inf files located in the system32 directory. The cumulative results of this merge are then used for autodetection of NICs and to provide installation selections to the user.

There are two cases in which an OEM will add new NICs to the Windows NT system. In the first scenario, the OEM makes a NIC part of the primary system by installing it and its driver directly on the Windows NT release media (such as CD-ROM, diskette, tape, and so forth) or on a preinstalled network share point. In the second scenario, the OEM provides support for adding NIC drivers to a running installation or for adding NIC drivers supplied as add-on diskettes during installation.

The NIC detection capabilities described here are intended to be extended by OEMs and ISVs. These capabilities include an API that supports one or more NICs from one or more DLLs.

Adding NICs during Primary Installation

OEMs can add information into the Windows NT registry before NIC detection commences. To facilitate this operation, the primary network installation file ntlanman.inf calls the oemhook.inf file. This call is made through the Win32 setup.exe shell command. No errors are reported if oemhook.inf is not found.

The oemhook.inf file allows an OEM to provide automatic NIC detection during the primary system installation of Windows NT. This file can perform any operations the OEM deems necessary.

Adding NICs during Secondary Installation

NICs can also be added after the system has been completely installed, or during a later phase of installation (in custom mode). This type of secondary installation uses the network control panel application (NCPA). In secondary installation, the supporting DLLs are not used for automatic detection of network adapters. Their primary purpose is to provide the installation .inf file with the proper NIC parameter definitions and available value ranges.


8.5 Network Interface Card Detection and the Installation Process

This summarizes NIC detection and the installation process for network interface cards.

Initial Setup Detection Process

The initial setup for detection of network adapters is a process of iterating through known NICs until a clear indication of physical presence is given by a DLL. This process is performed by the nlanman.inf file during primary installation. For custom installations, the user has the option to bypass this detection scan.

The main purpose of initial setup NIC detection is to locate the primary NIC used for joining a domain and for server-initialization operations. When such a NIC is located, the process terminates. The user is then given the choice of using the selected NIC or another NIC from the known types. When the scan is completed and the user has selected a NIC type, the remaining functions of network setup continue with minimum change.

Secondary NIC Detection Installation Processes

The capability to install NIC driver software and to interact with a network adapter is controlled by the .inf and .dll files. During NIC detection, these files are present for all NICs supported by Microsoft. For OEM installations, the .inf file is automatically copied while the .dll files are copied from the .inf file.

Configuration Querying

In the configuration querying process, when a NIC type is chosen, the appropriate DLL is selected and called. The DLL then obtains the current hardware-strapped (used set with jumpers on the card) and software-selected settings for the network adapter. The NIC settings include the following:

  • I/O port addresses

  • IRQ number

  • Shared memory address (where applicable)

  • Memory window status for ROMs (disabled or memory address)

The DLL entry point also returns the confidence factor, which is a number that indicates how reliably the software can detect the configuration. If the number representing the confidence factor is below 70, the configuration dialog is presented and overrides any mode. If the number is 100, indicating complete certainty of detecting the configuration, the configuration dialog is not presented unless a system-resource conflict is detected or the system is running in custom mode.

Querying During Express Mode If express mode is used and the NIC configuration query succeeds, setup proceeds with the installation of the other network components. If the query fails, setup displays the NIC configuration dialog for the card in question.

Querying During Custom Mode If custom mode is used and the NIC configuration succeeds, setup fills the NIC configuration dialog with the known settings and displays the dialog. If the query fails, the configuration dialog appears with the default settings.

Querying During OEM Installation For OEM installation, querying is done in the same manner as for custom mode. The user is given the opportunity to change settings.

Verifying Configuration

As previously mentioned, a DLL entry point allows setup to return the so-called confidence factor. Another DLL entry point allows setup to verify that the entered settings are valid. Verification is always done, even if the user has not changed any of the settings reported by querying the configuration.

Resource Validation

Like other hardware devices, NICs require dedicated hardware resources for their operation. For CISC- and RISC-based platforms, these resources can include IRQs, I/O addresses, shared memory, and ROM space buffer addresses.

The NIC-detection driver, netdtect.sys, is used to claim and free these hardware resources at the Windows NT kernel level. If a resource claim succeeds, the parameter setting is considered to be permitted by the current state of the system.


Chapter 9 NIC Detection Functions

This describes system-supplied support for detecting a network adapter in any Windows NT platform, including the following:

  • Functions exported in a NIC-detection dynamic-link library in Section 9.1

  • Auxiliary functions exported by the network control panel application in Section 9.1

  • Functions exported by the netdetect DLL in Section 9.3

9.1 NIC Detection DLL Functions

This describes the exported functions available in any NIC-detection support DLL. Special parameters are used to allow a single DLL to support a large number of distinct types of NICs.

There are specific functions unique to the detection support DLL, msncdet.dll. Besides providing basic support for all NIC types used in Windows NT, this DLL exports functions aimed at monitoring available system hardware resources, such as IRQs.

An OEM’s setup.inf file typically contains the name of the DLL that checks the NICs it supports. It also contains the string tokens representing those NIC types.


9.1.1 Identify

NcDetectIdentify returns information about the NICs supported by the DLL being addressed. The calling function furnishes a numeric index value and the function responds with the appropriate zero-terminated string. The return value is zero if no error occurs; otherwise, the appropriate winerror.h value appears. The function prototype is:

LONG NcDetectIdentify(
LONG lIndex,
WCHAR *pwchBuffer,
LONG cwchBuffSize
);

Parameters

lIndex
Specifies an index number indicating the information to be retrieved. The information applies either to the DLL as a whole or to a specific NIC. The following index numbers are defined for this parameter:
  • Index zero causes the function to output the manufacturer’s identification for the DLL.

  • Index one causes the function to output a date and version string.

  • Indexes numbered from 1000, in blocks of 100 values (modulo 100), indicate NICs. For example:
1000
Causes the function to output the internal short alphabetic token string representing the first supported NIC.
1001
Causes the function to output the displayable name of the NIC (language-sensitive).
1002
Causes the function to output the name of the manufacturer.
1100
Causes the function to output the alphabetic token for the second NIC type supported.

The values of lIndex are referred to in function prototypes as lNetcardId in the following sections.

pwchBuffer
Points to the buffer in which the retrieved information is returned.
cwchBuffSize
Indicates the size in bytes of the buffer.

Comments

To determine all NICs supported by a given DLL, the calling function can repeatedly call NcDetectIdentify, starting at an lIndex of 1000 and incrementing by 100 with each call. When ERROR_NO_MORE_ITEMS is returned, the enumeration is at an end. If the buffer is too small, the function returns ERROR_INSUFFICIENT_BUFFER. This error code is consistent throughout all NIC detection functions that return wide-character data into a buffer.

When the calling function has the list of possible NICs, it can retrieve information about a particular adapter. To accomplish this, it supplies the particular NIC-information index value to NcDetectIdentify and reads the information retrieved by the function.

If the calling function wants to examine the hardware, it performs a NIC-detection search. This search must be ordered to prevent unavoidable hardware interlocks that can occur if tests are made in the wrong order. Depending on the algorithm used by the calling function, the search can be made in a variety of ways.

The calling function can elect to make calls to NcDetectIdentify for all recognized NICs to determine search order. In this case, each call specifies nn03, where nn = 00 specifies the base network information index value of the NIC. In response to each call, NcDetectIdentify outputs a numeric search order value for the specific NIC, ranging from 0 to 1000. This value can then be used by the user interface to sort its attempts to detect network adapters. The lower the number, the faster the user interface logic attempts to detect the card. Any NIC for which a search order is not specified has a default search order value of 500.


9.1.2 Query Configuration Parameters

NcDetectQueryMask returns a buffer of Unicode strings, each of which is delimited by a Unicode NUL and is specified as (NUL), a zero-length string. The end of the buffer is indicated by an additional NUL. The function prototype is:

LONG NcDetectQueryMask(
LONG lNetcardId,
WCHAR *pwchBuffer,
LONG cwchBuffSize
);

The Unicode strings are grouped into sets of three. In order of arrangement, group elements are as follows:

  1. The internal name of the parameter. This name must not contain special characters or blanks.

  2. One of the following values:

    Zero
    Parameter cannot be set using software.
    One
    Parameter can be set using software.
    Two
    Parameter cannot be set using software and is not normally displayed to users.

  3. A number from 0 to 100 indicating the confidence the detection DLL has in detecting the setting of the given parameter. A setting of 100 implies complete confidence.

Comments

For example, using the string (nul) to represent a binary zero character would appear as follows:

IRQ(nul)1(nul)70(nul)
IOADDR(nul)0(nul)0(nul)
MEMADDR(nul)0(nul)0(nul)
MEMADDRLENGTH(nul)2(nul)100(nul)
(nul)

This buffer defines four parameter names: IRQ, IOADDR, MEMADDR, and MEMADDRLENGTH. Pointers to these strings are used as parameters of other functions.


9.1.3 Find First/Next Card by Type

NcDetectFirstNext is used to iterate the instances of a particular type of network interface card. The function prototype is as follows:

LONG NcDetectFirstNext(
LONG lNetcardId,
INTERFACE_TYPE itBusType,
LONG lBusNumber,
BOOL fFirst,
PVOID *ppvToken,
LONG *lConfidence
);

Parameters

lNetcardId
Specifies the index for the NIC obtained by the calling function through NcDetectIdentify. This value is always an integral of 100, starting with 1000.
itBusType
Specifies an enumerated value that describes the type of bus on which the NIC can be found. Again, this information is derived from the registry by the upper-level application.
lBusNumber
Indicates the bus being searched by the upper-level detection algorithm. This number starts at zero and can continue through the number of buses found by ntdetect.com and stored in the registry in the hardware hive.
fFirst
Set to TRUE to start the iteration, and FALSE not to start it.
ppvToken
Points to a variable set to the address of a token to be used to obtain a handle for the NIC in question.
lConfidence
Points to a variable set to a value from 0 to 100 indicating the confidence level of the underlying routine’s ability to distinguish the current presence of a particular NIC type.


9.1.4 Open a Handle

NcDetectOpenHandle obtains a handle to a particular NIC that has been reported by the detection DLL interface. The function prototype is as follows:

LONG NcDetectOpenHandle(
PVOID pvToken,
PVOID *ppvHandle
);

Parameters

ppvToken
Points to a token output by NcDetectFirstNext and used to obtain a handle to the NIC.
ppvHandle
Points to a variable in which the opaque handle to be created by the detection DLL is returned.


9.1.5 Using the DLL to Create a Handle

NcDetectCreateHandle requests the DLL to provide a handle for a NIC that is not present in the system. This is necessary when the user overrides detection and continues to configure a network card. The function prototype is as follows:

LONG NcDetectCreateHandle(
LONG lNetcardId,
INTERFACE_TYPE itBusType,
LONG lBusNumber,
PVOID *ppvHandle
);

Parameters

lNetcardId
Specifies a valid NIC type number.
itBusType
Specifies an enumerated value that describes the type of bus on which the NIC can be found.
lBusNumber
Specifies the zero-based number of the bus being searched by the upper-level detection algorithm.
ppvHandle
Points to a variable in which this function return the opaque handle.


9.1.6 Close Handle

NcDetectCloseHandle releases a handle output by NcDetectOpenHandle. The function prototype is as follows:

LONG NcDetectCloseHandle(
PVOID ppvHandle
);

Parameters

ppvHandle
Points to the handle returned by a successful call to NcDetectOpenHandle.


9.1.7 Query Configuration

NcDetectQueryCfg checks the current configuration of a NIC. The function prototype is as follows:

LONG NcDetectQueryCfg(
PVOID pvHandle,
WCHAR *pwchBuffer,
LONG cwchBuffSize
);

Parameters

ppvHandle
Points to the handle returned by a successful call to NcDetectOpenHandle.
pwchBuffer
Specifies the base virtual address of a buffer in which this function returns the data.
cwchBuffSize
Specifies the size in bytes of the buffer.

Comments

Configuration parameter values are output in the given buffer as pairs of strings, each delimited by a (nul) string. For example:

IRQ(nul)0x310(nul)IOADDR(nul)0x368(nul)MEMADDR(nul)0xD8000
(nul)(nul)

The values occur in pairs, with the value item name displaying first and the currently detected setting appearing second. This format implies that the names of certain common parameters are clearly defined by this interface, such as the following:

  • IRQ

  • IOADDR

  • IOADDRLENGTH

  • MEMADDR

  • MEMADDRLENGTH

  • TRANSCEIVER

For more information about these names, see Section 9.13.

A DLL and its .inf file or installer program can use any other values for reporting and setting configuration information. However, all such privately defined values must begin with a dollar sign ($).

Standard C language numeric literal conventions are used for all numbers. They are assumed to be decimal unless prefixed with 0x. All string values are case-insensitive. No ordering of return parameters can be assumed.

If no value for a particular parameter is detected, that parameter should not appear in the result buffer.


9.1.8 Verify Configuration

NcDetectVerifyCfg outputs a buffer of configuration information similar in format to NcDetectQueryCfg. The function prototype is as follows:

LONG NcDetectVerifyCfg(
PVOID pvHandle,
WCHAR *pwchBuffer
);

Parameters

ppvHandle
Points to the handle returned by a successful call to NcDetectOpenHandle.
pwchBuffer
Specifies the base virtual address of a buffer in which this function returns the data.

Comments

The function returns NO_ERROR if the configuration matches values that are either set on the NIC or that can be set during driver initialization. An error should be returned if critical (nondefault) parameters are missing from the buffer.


9.1.9 Query Parameter Ranging

NcDetectParamRange accepts a NIC ID number and a pointer to the name of a parameter. It returns its results in an array of LONGs (plValues), along with a highest subscript for the given parameter. The function prototype is as follows:

LONG NcDetectParamRange(
LONG lNetcardId,
WCHAR *pwchParam,
LONG *plValues,
LONG *plValueCount
);

Parameters

lNetcardId
Specifies the index for the NIC obtained by the calling function through NcDetectIdentify. This value is always an integral of 100, starting with 1000.
pwchParam
Points to a buffered Unicode string naming the parameter.
plValues
Points to a buffer in which the values for the given parameter are returned.
plValueCount
On entry to the function, the plValueCount parameter indicates the capacity of the buffer, in units of LONGs (buffer size / 4). On exit, the function sets the parameter to the highest value used, plus one (limit of resulting plValues array).

Comments

For example, the IRQ parameter of the Ungermann-Bass EOTP card might return the following in the buffer at plValues:

{2.3,5,7.12}


9.1.10 Query Displayable Parameter Name

NcDetectParameterName accepts the name of a parameter and outputs a language-sensitive displayable name. The function prototype is as follows:

LONG NcDetectParameterName(
WCHAR *pwchParam,
WCHAR *pwchBuffer,
LONG cwchBufferSize
);

Parameters

pwchParam
Points to a buffered Unicode string naming the parameter.
pwchBuffer
Points to a buffer in which this function returns a locale-specific displayable name.
cwchBuffSize
Specifies the size in bytes of the buffer at pwchBuffer.

Comments

If the function returns ERROR_INVALID_PARAMETER, the upper-level components are responsible for providing a displayable name.


9.1.11 Resource Claiming Functions for msncdet.dll

The functions listed next are only available in the NIC-detection DLL, msncdet.dll. These functions report the availability of and control the allocation of bus-related and hardware platform-related resources, such as IRQs and I/O space addresses.

Claiming a Resource

NcDetectResourceClaim allows the calling function to attempt to claim system hardware resources that are in use by a NIC. This is necessary to verify that NIC configuration parameters are actually available for use in the current system configuration. For example, msncdet.dll keeps a record of resources claimed by the process to which it is attached. When asked to claim or report resources later, msncdet.dll considers newly claimed resources and reports them accordingly. In other words, the information is used to augment the data extracted from the registry hardware hive.

The function prototype is as follows:

LONG NcDetectResourceClaim(
INTERFACE_TYPE ifType,
LONG lBusNumber,
LONG lType,
LONG lValue,
LONG lRange,
LONG lFlags,
BOOL fClaim
);

Parameters

ifType
Indicates the type of bus (for example, ISA, EISA, MCA, PCI).
lBusNumber
Indicates the zero-based number of the bus being addressed.
lType
Indicates the system-level resource being claimed as one of the following:
NETDTECT_IRQ_RESOURCE
Claim an interrupt vector or level.
NETDTECT_MEMORY_RESOURCE
Claim a memory-mapped range.
NETDTECT_PORT_RESOURCE
Claim an I/O port range.
NETDTECT_DMA_RESOURCE
Claim a channel in the system DMA controller.
lValue
Specifies the value of the resource type being claimed.
lRange
Specifies the size of the area being claimed, in address units. This applies only to certain parameters, such as memory windows. For example, only the lValue parameter would be meaningful for an IRQ parameter, while the lRange parameter would be ignored. Both values are required for an IOADDR (port) parameter.
lFlags
Specifies either of the following if IType is set to NETDTECT_IRQ_RESOURCE:
CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE
Interrupt occurs when signal is asserted.
CM_RESOURCE_INTERRUPT_LATCHED
Interrupt is edge-triggered.
fClaim
Allows the calling function to verify whether a resource can be claimed, without actually claiming it. If the parameter is TRUE, the resource is claimed. If the parameterer is FALSE, the resource is not claimed. The result is the same in either case.

Freeing a Resource

NcDetectResourceFree allows the calling function to release resources previously claimed through the use of NcDetectResourceClaim. The function prototype is as follows:

LONG NcDetectResourceFree(
INTERFACE_TYPE ifType,
LONG lBusNumber,
LONG lType,
LONG lValue,
LONG lRange,
LONG lFlags
);

The name of a resource (parameter) and its values must exactly match a previously claimed value, or ERROR_INVALID_PARAMETER is returned.


9.1.12 NIC Information String Indexes

This summarizes the definition and format of the strings that can be output by the NcDetectIdentify function of a NIC_detection DLL:
Index* | Description | Format
nn00 | Option string for netcard in INF | Unicode string; no embedded delimiters
nn01 | Displayable name of netcard | Unicode string
nn02 | Displayable name of manufacturer | Unicode string
nn03 | Search order | Unicode decimal numeric string

* nn = 10 or more; nn00 = base card index (lNetcardId)


9.1.13 Known NIC Configuration Parameter Strings

This summarizes the currently known text configuration parameter strings, their meanings, and their possible ranges and values:
Parameter Name | Description | Possible Values and Range
IRQ | Interrupt request | Normal range 2-15
IOADDR | Base address of device control area in I/O space | Usually 0x100-0x400, range 2-32
IOADDRLENGTH | Number of bytes of addressable I/O space used by device | Usually 4, 8, or 16
MEMADDR | Buffer RAM address | 0xA0000-0xF0000, range 4096-65536
MEMADDRLENGTH | Size of device memory window | 0-256k (may be greater, but not for network interface cards)
TRANSCEIVER | Transceiver control; specific to 3Com Etherlink II card | 0 (internal) or 1 (external)

The preceding list cannot be definitive, because vendors create new parameters as needed. Furthermore, definitions, values, and ranges can be highly platform- and hardware-specific.


9.2 Auxiliary Functions Exported by NCPA

This describes auxiliary functions exported by the network control panel application (NCPA). It assumes the reader is familiar with the nature and syntax of the installation script language, described in the Programmer's Guide.

The system-supplied setup.exe is an interpreter of the INF language. As such, setup.exe requires all information passing between it and its extension DLLs to be in text form. The NCPA has a set of auxiliary functions that provide such translation. These functions are used to provide access to other functions too complex to write in the INF language.

Some NCPA-provided functions are used in NIC detection to encapsulate the functionality. The functions are divided into two classes: those that are essentially wrappers, and those that have algorithms to utilize the functions described here.

One of the primary purposes of these auxiliary functions is to handle dynamically installed NIC-detection DLLs. Addition of new DLL names is done automatically by INF routines in the utility.inf file.

Msncdet.dll should always appear first in such a list. All the DLLs in this list are automatically loaded and queried about available network interface cards during system installation.


9.2.1 Detection Wrapper Functions

Setup wrapper functions generally return a INF list of the form:

{ <error code>, arg, ... }

As in most other symbolic languages, the parameters can be numbers, strings, atoms, or other lists. If an error occurs, setup returns only the error code.

Examples of how to call these functions are given in the system ntlanman.inf and utility.inf files. Details of the implementation are not discussed in this section. Instead, emphasis is placed on access and usage.

Each of the wrapper functions is accessed through the NCPA.CPL export CPlSETUP. The standard form for calling this function from an .inf file is as follows:

LibraryProcedure <ResultVarName>
    $(NcpaCplHandleVar) 	CPlSetup $(!STF_HWND)
    <FuncToBeRun> [other args]

Any additional arguments follow immediately behind the name, which is not case-sensitive, of the function to be run.

The resulting list is stored in the ResultVarName parameter. The first element of the list is the error code associated with the call, or zero if no error has occurred. The remaining elements depend on the function called.

If an error occurs, the variable FLibraryErrCtl is checked. If its value is one, the error is returned to the calling .inf file. If its value is undefined or something other than one, setup displays a DLL export call error message. This situation should be avoided, because it can be cryptic to the casual user. It allows a user to retry an operation that continues to fail.

The following summarizes these wrapper functions:

DTSTART

The DTSTART function must be called by the setup.inf file to initiate detection operations. The primary purpose of this function is to bind all known NIC detection DLLs and to create the data structures necessary to properly access and iterate the DLLs. Only the function name is required for the call and any subsequent calls are ignored. The format is as follows:

DTSTART

DTEND

DTEND should be called by setup.inf to terminate detection operations. It is called automatically when setup unbinds from NCPA.CPL. The format is as follows:

DTEND

DTRESET

DTRESET causes all detected NIC information to be discarded, and the internal iteration control to be reset. In other words, the detection code status after DTRESET is the same as that after DTSTART. The format is as follows:

DTRESET

DTCARD

DTCARD attempts to detect the next NIC on the next bus during the iteration cycle. The format is as follows:

DTCARD

The result, if detection succeeds (error code == 0), is a list in the following form:

{ error code,
  OPTIONNAME,
  card index,
  numeric card type code,
  detection confidence level,
  bus interface type,
  bus number
}

The card index returned is an integer that the .inf script must use to refer to the specific NIC in all future operations.

DTPARAMS

DTPARAMS returns parameter information for a particular NIC option. The format is as follows:

DTPARAMS <NIC option name>

The result is a list describing the NIC parameters. The basic result has the form:

{ error code
  parameter description list
}

The parameter description list has the form:

{   {parameter 1 name,setting control,confidence,{v1, v2, v3, ...} },
    {parameter 2 name,setting control,confidence,{v1, v2, v3, ...} },
    ....
}

The setting control is an integer that determines whether the option can be set using software. The confidence is an integer from 0 to 100. Each of the vn values is a valid setting for the appropriate parameter.

DTQUERY

DTQUERY takes the index of a previously detected NIC and returns the currently detectable configuration parameters.

DTVERIFY

DTVERIFY takes a set of configuration parameters for a NIC and returns an indication of the validity of the parameters.

DTSTRING

DTSTRING takes an option name and an integer and returns the corresponding string from the owning DLL.

DTPNAME

DTPNAME takes an option name and a parameter name and returns the external (displayable) version of the name.


9.2.2 File ncparam.inf

The file ncparam.inf is similar to utility.inf in its provision of a standard set of INF sections that can be called and used through the Win32 Setup Shell command. These INF sections interface to the detection-wrapper functions described previously.

The details of the interaction between a NIC .inf file and ncparam.inf are very complex. Provided in sample files, the DDK contains comments for versions of utility.inf, ncparam.inf, and one or more ISA NIC .inf files. These should be read carefully to understand the use of ncparam.inf.


9.3 Functions Exported by NETDETECT.DLL

Detection of network interface cards is comprised of several layered components. At the upper level is the setup.inf installation script. Underlying setup.inf are Microsoft's and other vendor’s hardware-specific NIC detection DLLs. These DLLs are used to call netdtect.dll.

The exported functions in netdtect.dll should be used for all NIC-detection operations in detection DLLs.

Information about driver interface specifications in this section are primarily directed at ISA-bus devices, because the ISA bus does not have any configuration information associated with it. Information from other buses such as EISA, MCA, and PCI are stored directly into the Windows NT registry.

The system-supplied netdtect.sys provides the following functions:

  • Resource checking

  • Port access

  • Memory Access

  • Interrupt trapping

A vendor’s NIC detection DLL typically uses the port and space access functions in netdtect.dll to attempt to locate specific NICs. It scans possible I/O port locations for a known signature or some other characteristic to detect a particular NIC. If NIC detection hangs the Windows NT operating system, the user is instructed to bypass automatic detection and explicitly specify the card instead.

Usually, NIC detection proceeds one new NIC at a time, with previous NICs having registered their resources with the I/O subsystem. Registry information is used to prevent access to ports or mapped memory in use by another driver.

Special routines are provided for NICs that cannot determine their interrupt without generating a physical interrupt. The detection DLL informs the driver to trap all interrupts for such a NIC that are not reserved in the current configuration. The detection DLL causes the NIC to generate an interrupt. A routine is provided for the detection DLL to determine which interrupts have occurred.

The following describe the exported functions of netdtect.dll.


9.3.1 Resource Checking and Claiming Functions

The functions DetectCheckPortUsage and DetectCheckMemoryUsage check to see if a port/memory range is in use by some device in the system other than the current NIC driver. If the port or memory is available, the return value is STATUS_SUCCESS. Otherwise, either of the functions return an error code. The function prototypes are as follows:

NTSTATUS DetectCheckPortUsage(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG Port,
IN ULONG Length
);

NTSTATUS DetectCheckMemoryUsage(
IN INTERFACE_TYPE InterfaceType,
N ULONG BusNumber,
IN ULONG BaseAddress,
IN ULONG Length
);

A successful return implies the I/O port or memory range is available for use.


9.3.2 Port Access Functions

Port access functions read or write data from or to I/O ports (for example, 0x300) on the NIC. The return code for each function indicates whether the data has been successfully read or written.

A detection DLL first ensures that such a port is available by using DetectCheckPortUsage before calling any of these functions, declared as follows:

NTSTATUS DetectReadPortUchar(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG Port,
OUT PUCHAR Value
);

NTSTATUS DetectWritePortUchar(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG Port,
IN UCHAR Value
);

NTSTATUS DetectReadPortUshort(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG Port,
OUT PUSHORT Value
);

NTSTATUS DetectWritePortUshort(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG Port,
IN USHORT Value
);

NTSTATUS DetectReadPortUlong(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG Port,
OUT PULONG Value
);

NTSTATUS DetectWritePortUlong(
IN ULONG BusNumber,
IN ULONG Port,
N ULONG Value
);

Depending on the function, each reads or writes a byte, two-byte, or four-byte value for a given I/O port range.


9.3.3 Memory Access Functions

Memory access functions read from or write data to mapped device memory (for example, 0xCC000). Data is stored in a caller-supplied buffer. The return code for each function indicates whether the operation was completed successfully.

A detection DLL must verify the accessiblity of such a memory range by calling DetectCheckMemoryUsage before using either of these functions, declared as follows:

NTSTATUS DetectReadMappedMemory(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
IN ULONG BaseAddress,
IN ULONG Length,
OUT PVOID Data
);

NTSTATUS DetectWriteMappedMemory(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BaseAddress,
IN ULONG Length,
IN PVOID Data
);


9.3.4 Interrupt Trapping Functions

Interrupt-trapping functions determine the interrupt level or vector that a network interface card uses. A detection DDL calls DetectSetInterruptTrap to register interrupt handlers on each of the interrupts specified. After calling this function, the detection DLL causes the NIC to generate an interrupt. Then, the detection DLL calls DetectQueryInterruptTrap to determine on which level interrupts have occurred.

The detection DLL waits an appropriate length of time for an interrupt and then performs the specific operations to dismiss the interrupt on the NIC. During interrupt trapping, DetectQueryInterruptTrap can be called several times. When the detection DLL is through trapping interrupts, it calls DetectRemoveInterruptTrap.

DetectSetInterruptTrap Function

DetectSetInterruptTrap obtains a provided array of vector or level numbers. The detection DLL attempts to register interrupt handlers on each of the interrupts specified. A subset of these typically succeeds, based on the other devices in the system. The operation fails if all of the interrupts are in use simultaneously. The function prototype is as follows:

NTSTATUS DetectSetInterruptTrap(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
OUT PHANDLE TrapHandle,
IN UCHAR InterruptList[ ],
IN ULONG InterruptListLength
);

DetectQueryInterruptTrap

DetectQueryInterruptTrap determines which of the interrupts previously passed to DetechSetInterruptTrap have occurred. Each entry in the given InterruptList[ ] displays one of the following:
Value | Meanin
0 | No interrupt has occurred.
1 | One interrupt has occurred.
2 | More than one interrupt has occurred.
3 | The interrupt is in use by another device.

DetectQueryInterruptTrap can be called repeatedly to return the results since the most recent call. The function prototype is as follows:

NTSTATUS DetectQueryInterruptTrap(
IN HANDLE TrapHandle,
IN OUT UCHAR InterruptList[ ]
);

DetectRemoveInterruptTrap Function

DetectRemoveInterruptTrap removes handlers originally set up with DetectSetInterruptTrap. The function prototype is as follows:

NTSTATUS DetectRemoveInterruptTrap(
IN HANDLE TrapHandle
);


Chapter 1 NDIS Intermediate Drivers

An NDIS intermediate driver usually exports MiniportXxx functions at its upper edge and ProtocolXxx functions at its lower edge. Less commonly, an intermediate driver can export MiniportXxx functions at its upper edge and a private interface to an underlying non-NDIS driver at its lower edge.

An intermediate driver is typically layered over one or more NDIS NIC drivers and under a transport driver (possibly multilayered) that supports TDI at its upper edge. Theoretically, an intermediate driver could be layered above or below another intermediate driver, although such an arrangement is unlikely to exhibit good performance.

Two examples of intermediate drivers are:

  • A LAN-emulator intermediate driver layered below a legacy transport driver and above a miniport NIC driver for a non-LAN medium

    Such a driver receives packets in a LAN format at its upper edge, translates them to another NIC-native medium format and sends them on to an NDIS miniport for that NIC. On receives, this intermediate driver translates packets indicated up from the underlying NIC driver to a LAN-compatible format and indicates these converted packets to the upper level transport driver.

    For example, NDISWAN has many of these properties. NDISWAN translates packets from the overlying transports' LAN format to WAN packet format and packets from the underlying NIC drivers' WAN packet format to LAN packet format. NDISWAN also optionally performs compression, encryption and PPP formatting if this is not supported by the underlying NIC hardware. NDISWAN contains a private interface for communication between NDISTAPI and the NIC driver. It also maps protocol bindings to active call connections.

  • An intermediate driver layered below NDIS that depends on an underlying driver of a device other than a NIC

    For example, an intermediate driver might handle network I/O requests for a device connected to a serial port. Such an intermediate driver would export a set of MiniportXxx functions to communicate with NDIS at its upper edge and use standard Windows NT IRPs to communicate with the underlying serial device driver at its lower edge.

Figure 1.1 shows these two types of intermediate drivers.

Figure 1.1 Supported Intermediate Driver Configurations

An NDIS intermediate driver interfaces to NDIS to forward packets sent by a driver above and to pass them to a driver below. When an intermediate driver receives packets from an underlying driver, it indicates them to the driver above either by calling a filter-specific NdisM Xxx IndicateReceive function or NdisMIndicateReceivePacket.

An intermediate driver calls NDIS to open and establish a binding to an underlying NIC driver or intermediate NDIS driver that exports a set of MiniportXxx functions at its upper edge. An intermediate driver provides MiniportSetInformation and MiniportQueryInformation functions to process set and query requests from higher level driver(s) and, perhaps, to pass them through to a lower level NDIS driver by calling NdisRequest.

An intermediate driver calls NDIS-provided functions to send packets on to still lower level NDIS drivers to the net. For instance, an intermediate driver must call NdisSend or NdisSendPackets to send a packet or array of packets. If the intermediate driver is layered over a nonNDIS NIC driver, the send interface is opaque to NDIS after it calls the MiniportSend or MiniportSendPackets function of the intermediate driver.

NDIS provides a set of Ndis Xxx functions and macros that hides the details of the underlying operating system. For instance, an intermediate driver can call NdisMInitializeTimer to create a timer for synchronization purposes and NdisInitializeListHead to create a linked list. Intermediate drivers use NDIS functions in order to be more portable across Microsoft operating systems that support the Win32 interface.

Pageable and Discardable Code

As explained in Part 1, every MiniportXxx or ProtocolXxx function runs at a particular IRQL. The possible IRQLs for these functions range between PASSIVE_LEVEL up to and including DISPATCH_LEVEL in intermediate drivers.

Intermediate driver functions that always run at IRQL PASSIVE_LEVEL can be marked as pageable using the NDIS_PAGABLE_FUNCTION macro. Driver developers are encouraged to designate code as pageable whenever possible, freeing system space for code that must be memory-resident. A driver function that runs at IRQL PASSIVE_LEVEL can be made pageable as long as it neither calls nor is called by any function that runs at IRQL >= DISPATCH_LEVEL, for instance a function that acquires a spin lock. Acquiring a spin lock causes the IRQL of the acquiring thread to be raised to IRQL DISPATCH_LEVEL. A function, such as ProtocolBindAdapter, that runs at IRQL PASSIVE_LEVEL, must not call NDIS functions that run at IRQL >= DISPATCH_LEVEL if ProtocolBindAdapter is marked as pageable code. For more information about NDIS functions that run at raised IRQL, see the Network Driver Reference, which specifies the IRQL for each of the Ndis Xxx functions.

The DriverEntry function of an intermediate driver should be specified as initialization-only code, using the NDIS_INIT_FUNCTION macro. Code identified with this macro is assumed to only run once at system initialization time, and as a result, is only mapped during that time. After DriverEntry returns, code marked with the NDIS_INIT_FUNCTION macro is discarded.

Synchronizing Access to Shared Resources

Access to any driver-allocated shared resource must be synchronized if the resource can be simultaneously shared by two driver functions or if the intermediate driver can run on an SMP machine such that the same driver function can be attempting to simultaneously access the resource from more than one processor. For instance, if a driver maintains a shared queue, a spin lock can be used to serialize access to that queue. The spin lock should be initialized when the queue is created by calling NdisAllocateSpinLock.

However, care should be taken not to overprotect a shared resource, such as a queue. For example, some read operations can be done without serialization. Any operation that manipulates the queue links however, must be serialized. Spin locks always should be used sparingly and held for as short a time as possible. See the Kernel-Mode Driver Design Guide for an in-depth discussion of spin locks.


1.1 Intermediate Driver DriverEntry Function

An intermediate driver’s initial required entry point must be explicitly named DriverEntry so that the loader can properly identify it. All other exported driver functions, described here as MiniportXxx and ProtocolXxx can have any developer-specified name since they are passed as addresses to NDIS. The definition of DriverEntry is that of any Windows NT kernel-mode driver.

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

If the driver exports a set of standard kernel-mode driver functions in addition to the ProtocolXxx functions, it must write the addresses of those standard functions in the driver object passed to DriverEntry.

In an intermediate driver, DriverEntry must at a minimum:

  1. Call NdisMInitializeWrapper and save the handle returned at NdisWrapperHandle.

  2. Call either NdisIMRegisterLayeredMiniport or NdisMRegisterMiniport to register the driver’s MiniportXxx functions, passing the handle obtained in Step 1.

  3. Call NdisRegisterProtocol to register the driver’s ProtocolXxx functions if the driver subsequently binds itself to an underlying NDIS driver.

DriverEntry can initialize spin locks for any globally shared resources the intermediate driver allocates, such as structures and memory areas the driver uses to track connections and to track sends in progress.

If DriverEntry fails to allocate any resources the driver needs to carry out network I/O operations, it should release any previously allocated resources and return an appropriate error status. For example, if DriverEntry has called NdisMInitializeWrapper, it must call NdisTerminateWrapper.

An intermediate driver’s DriverEntry function can perform some global initialization steps. However, if an intermediate driver provides a ProtocolBindAdapter function, which opens and binds to an underlying device as described in Section 1.2, such a driver can defer allocating binding-related system resources to ProtocolBindAdapter. ProtocolBindAdapter performs the bind and allocates resources as needed for the device passed at DeviceName. DriverEntry must always initialize the wrapper and register as a miniport driver. It also must register as a protocol driver if the intermediate driver also exports a set of ProtocolXxx functions.

If the intermediate driver only exports a set of MiniportXxx functions, it registers only those functions with the NDIS library, as described next.


1.1.1 Registering as an NDIS Intermediate Driver

An NDIS intermediate driver must register its MiniportXxx functions and its ProtocolXxx functions, if any, with NDIS in the context of its DriverEntry function. To register its MiniportXxx functions, an intermediate driver must make either of two possible NDIS registration calls, NdisMRegisterMiniport and NdisIMRegisterLayeredMiniport. Both Ndis..Register..Miniport calls export the intermediate driver’s MiniportXxx functions. Which of these functions is called determines whether the intermediate driver or NDIS controls when the intermediate driver initializes as a miniport and, as a result, when the intermediate driver's virtual NIC(s) are made available for requests from higher level drivers.

  • If an intermediate driver calls NdisMRegisterMiniport, NDIS will call the driver’s MiniportInitialize function to request initialization of its virtual NIC.

  • If an intermediate driver calls NdisIMRegisterLayeredMiniport, the driver controls when a virtual NIC is initialized and, thus, when the driver is ready to accept sends and requests on that NIC.

An example of an intermediate driver that would select NdisIMRegisterLayeredMiniport over NdisMRegisterMiniport is one that needs to set up certain state after querying the underlying NIC driver before it is ready to accept requests from higher level drivers. By delaying the initialization of its virtual NIC(s), such an intermediate driver can bind to the underlying NIC driver(s), negotiate over the network to establish its operating characteristics and then, when it is ready to accept external requests directed to its virtual NIC(s), the intermediate driver makes each virtual NIC available by calling NdisIMInitializeDeviceInstance.

NDIS then calls the intermediate driver’s MiniportInitialize function in the context of NdisIMInitializeDeviceInstance for the specified virtual device instance. If the intermediate driver exports more than one virtual NIC, the driver must call NdisIMInitializeDeviceInstance for each NIC that it makes available for network requests. An intermediate driver could use this capability to make more or fewer of its virtual NICs available, depending on network traffic.

If the NDIS intermediate driver also exports a set of ProtocolXxx functions, it must also register these functions with the NDIS library by calling NdisRegisterProtocol.


1.1.1.1 Registering an Intermediate Driver as a Miniport

An intermediate driver calls one of the two registration routines to export its MiniportXxx functions.

NdisIMRegisterLayeredMiniport is declared as follows:

NDIS_STATUS
NdisIMRegisterLayeredMiniport(
IN NDIS_HANDLE NdisWrapperHandle,
IN PNDIS_MINIPORT_CHARACTERISTICS MiniportCharacteristics,
IN UINT CharacteristicsLength,
OUT PNDIS_HANDLE DriverHandle
);

The DriverHandle returned by NdisIMRegisterLayeredMiniport must be retained by the intermediate driver and input to NDIS when the driver calls NdisIMInitializeDeviceInstance to request a call to the intermediate driver’s MiniportInitialize function, which then initializes a virtual NIC. An intermediate driver calls NdisIMInitializeDeviceInstance after it either has successfully bound to one or more underlying NIC drivers or has layered itself above the driver of a nonNIC device so the intermediate driver is ready to initialize its miniport component to accept I/O requests on its virtual NIC.

The call to NdisMRegisterMiniport is almost identical except for the DriverHandle parameter, which is specific to NdisIMRegisterLayeredMiniport.

NDIS_STATUS
NdisMRegisterMiniport(
IN NDIS_HANDLE NdisWrapperHandle,
IN PNDIS_MINIPORT_CHARACTERISTICS MiniportCharacteristics,
IN UINT CharacteristicsLength
);

NdisWrapperHandle was returned by a previous call to NdisMInitializeWrapper.

The intermediate driver must:

  1. Zero-initialize a structure of type NDIS_MINIPORT_CHARACTERISTICS with NdisZeroMemory.

  2. Store the addresses of the mandatory MiniportXxx functions as well as any optional MiniportXxx functions the driver exports. Any other MiniportXxx entry points must be set to NULL.

While valid major versions are 0x03 or 0x04 for other types of NDIS drivers, an intermediate driver must provide a major version of 4.0 and provide a version 4.0 MiniportCharacteristics structure no matter whether it calls NdisIMRegisterLayeredMiniport or NdisMRegisterMiniport and whether it exports any new-for-V4.0 MiniportXxx functions.

The following entries in MiniportCharacteristics must be set to a valid MiniportXxx function address or NULL:

HaltHandler
This function is called by NDIS, for instance, if the underlying NIC has timed out and NDIS has halted the NIC driver or perhaps because the operating system is performing a controlled shutdown of the system.
InitializeHandler
This function is called as a result of the intermediate driver calling NdisIMInitializeDeviceInstance to initialize its miniport operations for the virtual NIC being initialized, or in the context of the call to NdisMRegisterMiniport.
QueryInformationHandler
This function receives OID_XXX requests originating from or passed through by an overlying driver that has called NdisRequest with a request type of NdisRequestQueryInformation.
ResetHandler
NDIS can call an intermediate driver’s MiniportReset function at the behest of a higher-level protocol driver that has called NdisReset. Typically, however, a protocol driver does not initiate resets. Generally, NDIS initiates a reset of an underlying NIC driver and calls an intermediate driver’s ProtocolStatus and ProtocolStatusComplete function to inform such an intermediate driver that the underlying miniport is resetting its NIC.
SetInformationHandler
This function processes OID_XXX requests made by or passed through an overlying driver that has called NdisRequest with a request type of NdisRequestSetInformation.
SendHandler
NDIS calls this function to transmit a single packet to the underlying NIC (or device) driver. A MiniportSend function (or a MiniportWanSend function) is required if the intermediate driver does not supply a MiniportSendPackets function. A MiniportSendPackets function should always be provided at SendPacketsHandler, rather than this handler, unless the intermediate driver will always be layered between drivers that transmit a single packet at a time or will bind itself to underlying WAN NIC driver(s). See Section 1.6 for more discussion of this topic.
SendPacketsHandler
This function receives an array of one or more pointers to packet descriptors specifying packets for transmission over the network. Every intermediate driver should supply a MiniportSendPackets function, rather than supplying a MiniportSend function, unless the intermediate driver will bind itself to underlying WAN NIC driver(s) and must supply a MiniportWanSend function. Otherwise, a MiniportSendPackets function provides the best performance whether the intermediate driver is layered over a NIC driver that can transmit several packets at a time or only one packet at a time and whether it is layered under a protocol driver that sends one packet at a time or several packets at a time. See Section 1.6 for a more complete discussion.
TransferDataHandler
This function is called to transfer the remaining part of a received packet that was not previously indicated up in the lookahead buffer passed by the intermediate driver to NdisM Xxx IndicateReceive. This indicated packet can be a converted packet previously received at the intermediate driver’s ProtocolReceive function or ProtocolReceivePacket handler. This handler is required if the intermediate driver indicates received packets to its overlying driver by calling any medium-specific NdisM Xxx IndicateReceive function except for NdisMWanIndicateReceive. If the intermediate driver always indicates packets to the overlying driver by calling NdisMIndicateReceivePacket it need not provide a MiniportTransferData function.
ReturnPacketHandler
This function receives a returned packet descriptor that was previously indicated to a higher level driver by calling NdisMIndicateReceivePacket, thereby relinquishing control of the resources indicated to the higher level driver. After each such indication is consumed by the higher level driver, the intermediate-allocated packet descriptor and the resources it describes will be returned to the MiniportReturnPacket function. A MiniportReturnPacket function is not supplied if the intermediate driver always indicates packets up by calling a medium-specific NdisM Xxx IndicateReceive function or if it always sets the status in the OOB data block associated with each packet descriptor to NDIS_STATUS_RESOURCES before calling NdisMIndicateReceivePacket.
CheckForHangHandler
This function is called at an NDIS-determined or, alternatively, at an intermediate-driver-determined interval. If supplied, the MiniportCheckForHang function is called every 2 (or at a driver-requested interval) seconds. See the Network Driver Reference for more information about the MiniportCheckForHang function or Part 2 of this manual. Generally, an NDIS intermediate driver does not provide a MiniportCheckForHang function because such a driver has no way to determine if an underlying NIC is hung. An intermediate driver probably would provide this handler if it was layered over a non-NDIS driver whose state is inaccessible to NDIS.

The following miniport handler functions are never supplied by an intermediate driver, because such drivers do not manage interrupting devices, do not allocate buffers at raised IRQL, or in the case of the ReconfigureHandler, because NDIS currently does not call a MiniportReconfigure function:

  • DisableInterruptHandler

  • EnableInterruptHandler

  • HandleInterruptHandler

  • ISRHandler

  • AllocateCompleteHandler

  • ReconfigureHandler


1.1.1.2 Registering an Intermediate Driver as a Protocol

An intermediate driver registers its ProtocolXxx functions with NDIS by calling NdisRegisterProtocol declared as follows:

VOID
NdisRegisterProtocol(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE NdisProtocolHandle,
IN NDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
IN UINT CharacteristicsLength
);

Before calling NdisRegisterProtocol, the intermediate driver must:

  1. Zero a structure of type NDIS_PROTOCOL_CHARACTERISTICS. An intermediate driver can use a 4.0 ProtocolCharacteristics or a 3.0 ProtocolCharacteristics structure.

  2. Store the addresses of the mandatory ProtocolXxx functions as well as any optional ProtocolXxx functions the driver supports.

The NdisProtocolHandle returned by this call is opaque to an intermediate driver. The handle must be retained by the intermediate driver and provided as an input parameter in future calls made by the protocol part of the intermediate driver to NDIS, for instance to open underlying adapter.

The possible optional and required functions an intermediate driver can export are listed below:

BindAdapterHandler
NDIS calls this function to request the intermediate driver to bind to an underlying NIC or virtual NIC whose name is passed as a parameter to this handler, particularly if the intermediate driver registered its MiniportXxx functions with NdisIMRegisterLayeredMiniport. Supplying a ProtocolBindAdapter function makes an intermediate driver PnP-ready. See Section 1.2 for more information on dynamic binding.
UnbindAdapterHandler
This is a required function if an intermediate driver supplies a ProtocolBindAdapter function. ProtocolUnbindAdapter is called by NDIS to close a binding to the underlying NIC or virtual NIC whose name is passed to this function. ProtocolUnbindAdapter calls NdisCloseAdapter and deallocates resources when the binding is successfully closed.
OpenAdapterCompleteHandler
This is a required function. If an intermediate driver’s call to NdisOpenAdapter returns NDIS_STATUS_PENDING, ProtocolOpenAdapterComplete is subsequently called to complete the binding.
CloseAdapterCompleteHandler
This is a required function. If an intermediate driver’s call to NdisCloseAdapter returns NDIS_STATUS_PENDING, ProtocolCloseAdapterComplete is subsequently called to complete the unbinding.
ReceiveHandler
This is a required function. ProtocolReceive is called with a pointer to a lookahead buffer containing data received over the network. If this buffer contains less than the full net packet, ProtocolReceive calls NdisTransferData with a packet descriptor to get the remainder of the net packet. If the underlying driver calls NdisMIndicateReceivePacket to indicate receives, the lookahead buffer passed to ProtocolReceive will always contain a full net packet.
ReceivePacketHandler
This is an optional function. A ProtocolReceivePacket function is provided if the intermediate driver will be layered over any NIC driver that indicates an array of pointer(s) to one or more packet descriptors or that supplies out-of-band data with its receive indications by calling NdisMIndicateReceivePacket. If a developer is unsure of the environment in which the intermediate driver will execute, this function should be provided because the intermediate driver will achieve better performance over any underlying NIC driver that makes multipacket receive indications.
ReceiveCompleteHandler
This is a required function. ProtocolReceiveComplete is called when any packets previously indicated to ProtocolReceive can be postprocessed.
TransferDataCompleteHandler
This is a required function if the ProtocolReceive function ever calls NdisTransferData. If a previous call to NdisTransferData to copy the remainder of a received packet returned NDIS_STATUS_PENDING, ProtocolTransferDataComplete is called when the transfer operation is done.
ResetCompleteHandler
This is a required function. ProtocolResetComplete is called when a reset operation, begun with a call to NdisReset that returned NDIS_STATUS_PENDING, is done. Usually, intermediate drivers do not call NdisReset, but the drivers above them might, so an intermediate driver could forward such a reset request to the underlying NDIS driver.

RequestCompleteHandler

This is a required function. ProtocolRequestComplete is called upon completion of a query/set operation, begun with a call to NdisRequest that returned NDIS_STATUS_PENDING, is done.

SendCompleteHandler
This is a required function. ProtocolSendComplete is called for each packet transmitted with a call to NdisSend that returned NDIS_STATUS_PENDING as the status of the send operation. If an array of packets is sent by calling NdisSendPackets, ProtocolSendComplete is called once for each packet passed to NdisSendPackets. The intermediate driver can determine the status of a send operation that calls NdisSendPackets only from the status argument input to ProtocolSendComplete.
StatusHandler
This is a required function. ProtocolStatus is called by NDIS with status notifications initiated by an underlying NIC driver.
StatusCompleteHandler
This is a required function. ProtocolStatusComplete is called by NDIS to indicate that a status change, previously indicated to ProtocolStatus, is now complete.

An UnloadHandler entry is ignored in Windows NT platforms; it should be set to NULL. The TranslateHandler member of the characteristics structure is reserved for system use and should also be set to NULL.


1.2 Dynamic Binding in an Intermediate Driver

A PnP-ready intermediate driver can support dynamic binding to underlying NICs by providing both a ProtocolBindAdapter and a ProtocolUnbindAdapter function. NDIS calls the ProtocolBindAdapter function of any intermediate driver that can bind to a NIC when that NIC becomes available.

VOID
ProtocolBindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
);

The handle at BindContext represents NDIS’ context for the bind request. The intermediate driver must retain this handle and pass it back to NDIS as a parameter to NdisCompleteBindAdapter when the intermediate driver has completed its binding-related activities and is ready to accept transmit requests.

Binding-time actions include allocating and initializing a NIC-specific context area for the binding, and calling NdisOpenAdapter to bind to the adapter passed at DeviceName. DeviceName can refer to a NIC managed by an underlying NIC driver, or it can be the name of a virtual NIC exported by an intermediate NDIS driver that is layered between the called intermediate driver and the NIC driver managing the adapter to which transmit requests are directed. Usually, only one intermediate NDIS driver is layered over a NIC driver, perhaps to translate between the format of the medium supported by an overlying legacy protocol driver and the format of the medium supported by an underlying NIC driver.

The intermediate driver can store the BindContext in the allocated binding-related context area or in another driver-accessible location. The BindContext value must be stored if NdisOpenAdapter can return NDIS_STATUS_PENDING. In this case, the intermediate driver cannot call NdisCompleteBindAdapter until the open-adapter operation is complete and the intermediate driver is called at its ProtocolOpenAdapterComplete function. BindContext must then be retrieved from a known location and passed by ProtocolOpenAdapterComplete to NdisCompleteBindAdapter.

SystemSpecific1 points to a registry path if the intermediate driver stores adapter-specific information in the registry. This value is passed to NdisOpenProtocolConfiguration to obtain a handle used to read and possibly write such information.

SystemSpecific2 is reserved for system use.

If an intermediate driver converts incoming packets from one medium format to another, ProtocolBindAdapter can allocate a pool of packet descriptors and buffer descriptors it will need on a per-binding basis. See Section 1.3 for information about the requirement to allocate and manage packets. Also, if an intermediate driver receives incoming data only at its ProtocolReceive function, it should allocate packet pool and buffer pool to copy received data.


1.2.1 Opening an Adapter Underlying an Intermediate Driver

ProtocolBindAdapter uses the value at DeviceName to open an underlying NIC or virtual NIC, thereby establishing a binding to the underlying NIC driver. It can also read additional configuration information it requires from the registry. NdisOpenProtocolConfiguration is called to obtain a handle to the registry key where the intermediate driver stores any adapter-specific information, and the NdisRead(Write)Configuration functions are called to read and write such information. The NdisRead(Write)Configuration functions are described in the Network Driver Reference.

Typically, ProtocolBindAdapter uses the context area it allocates to represent its binding to DeviceName as storage for any binding-specific information associated with the bound adapter.

Binding is accomplished by calling NdisOpenAdapter, declared as follows:

VOID
NdisOpenAdapter(
OUT PNDIS_STATUS Status,
OUT PNDIS_STATUS OpenErrorStatus,
OUT PNDIS_HANDLE NdisBindingHandle,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE NdisProtocolHandle,
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_STRING AdapterName,
IN UINT OpenOptions,
IN PSTRING AddressingInformation
);

An intermediate driver passes a handle at ProtocolBindingContext that represents the binding-specific context area it allocated and initialized. NDIS will pass this context back to the intermediate driver in future calls pertaining to the binding, for instance, in calls to ProtocolReceive or ProtocolStatus. Similarly, NDIS will pass a handle at NdisBindingHandle to the intermediate driver when NdisOpenAdapter returns. This handle must be retained by the intermediate driver, usually in its binding-specific context area. The intermediate driver passes this handle to NDIS in future calls relating to this binding, such as calls to NdisSend or NdisSendPackets.

ProtocolBindAdapter also passes the type(s) of medium(s) it supports at MediumArray. If NdisOpenAdapter is successful, the underlying NIC driver will select a medium it can support and return the index of the selected medium from MediumArray at SelectedMediumIndex.

ProtocolBindAdapter passes the value returned from a previous successful call to NdisRegisterProtocol at NdisProtocolHandle.

If NdisOpenAdapter returns an error, the intermediate driver should deallocate the memory used for the context area and release any other resources it allocated for the binding. Typically, ProtocolBindAdapter logs any failed binding operations with appropriate descriptive information by calling NdisWriteErrorLogEntry.


1.2.2 Initializing as a Miniport

ProtocolBindAdapter, after it has successfully opened an underlying NIC and is ready to accept requests and sends on its virtual NIC or NICs, calls NdisIMInitializeDeviceInstance one or more times to request initialization of one or more NICs. NdisIMInitializeDeviceInstance calls the intermediate driver’s MiniportInitialize function to perform the initialization of the specified virtual NIC. After MiniportInitialize returns, overlying NDIS drivers can bind to the intermediate driver's virtual NIC(s).

MiniportInitialize must allocate and initialize a virtual-NIC-specific context area. As part of initialization, MiniportInitialize must call NdisMSetAttributesEx and pass a handle to that context area. NDIS will pass this context handle in subsequent calls to the MiniportXxx functions.

MiniportInitialize also must set the NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER flag in the AttributeFlags parameter passed to NdisMSetAttributesEx. In addition, MiniportInitialize must set the NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT and NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT flags if it does not want NDIS to call the MiniportCheckForHang (or MiniportReset) function whenever NDIS times out sends and requests it is holding queued to the intermediate driver.

An intermediate driver sets the NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER flag to identify its driver type to NDIS. An important characteristic of an intermediate driver is that it always runs full-duplex, unless the intermediate driver reports the media type of its virtual NIC as NdisMediumWan. (Drivers underlying NDISWAN are never full-duplex miniports.) Running full-duplex imposes some restrictions on the calling sequence of a driver’s MiniportXxx functions. See Part 2, Chapter 4, Section 4.5, for a discussion of full-duplex operation.

Setting the timeout flags instructs NDIS that the intermediate driver will be responsible for timing out its own virtual NIC. Since the intermediate driver does not control the underlying NIC and therefore has no control over how long it takes to complete pending sends and requests, it generally does not provide a MiniportCheckForHang function nor does it time-out its virtual NIC.

However, if the intermediate driver registers an entry point at CheckForHangHandler, does not request that NDIS ignore packet and request timeouts, and does not change the time-out interval, its MiniportCheckForHang function will be called, by default, every 2 seconds. If MiniportCheckForHang returns TRUE, the MiniportReset function will be called. If the driver supplies a MiniportCheckForHang function, it can change the default 2-second interval by explicitly specifying a different TimeInSeconds when it calls NdisMSetAttributesEx.

Although the intermediate driver does not manage actual hardware and thus does not need to initialize interrupts or test hardware, it should make sure that the state information it maintains is properly initialized. If the driver requires send-related resources, for instance, new packet descriptors for packets that MiniportSend or MiniportSendPackets will transmit to the next lower layer, the packet pool can be allocated at this time if it was not done in ProtocolBindAdapter before calling NdisIMInitializeDeviceInstance.


1.2.3 Intermediate Driver Query and Set Operations

An intermediate driver, after it has successfully bound to an underlying NIC and initialized its virtual NIC(s), calls NdisRequest to query the operating characteristics of the underlying NIC driver and to set its internal state, as well as to negotiate such parameters as lookahead buffer size for the binding with the underlying NIC driver, if appropriate.

An intermediate driver can also receive query and set requests at its MiniportQueryInformation and MiniportSetInformation functions, respectively, and either respond to those requests or pass them down to the underlying driver.

The Network Driver Reference contains information about all the general, non-media-specific OIDs and about required media-specific OIDs of interest to intermediate driver developers. The following mentions typical and commonly used general category OIDs and a few media-specific OIDs.

Issuing Set and Query Requests

An intermediate driver generally issues an OID_GEN_MAXIMUM_FRAME_SIZE request to query the maximum frame size, in bytes, supported by an underlying NIC driver. The size returned does not include the header.

The intermediate driver can query a binding with OID_GEN_MAXIMUM_TOTAL_SIZE to determine the largest packet an underlying NIC driver can accommodate on the NIC it manages. The intermediate driver must always set up send packets that conform to this size. It is an error for an overlying driver to submit a larger packet than a NIC driver to which it is bound can support.

The intermediate driver can query the size of the lookahead data buffer. The OID used is OID_GEN_CURRENT_LOOKAHEAD. If the intermediate driver issues this as a query request, NDIS returns the current lookahead buffer size for the given binding to the underlying NIC driver. If the intermediate driver makes a set request, it indicates its preferred lookahead buffer size but the intermediate driver is not assured that the underlying NIC driver will conform to this.

The intermediate driver queries the underlying NIC driver for its link speed with OID_GEN_LINK_SPEED and uses the response to set any internal time-out values it maintains. If the intermediate driver is bound to a WAN NIC driver, it cannot determine the link speed until it receives a line-up indication, meaning a connection has been established between the local node and a remote node. See Part 2, Chapter 5, for a description of the line-up indication.

The intermediate driver must also issue an OID_GEN_MAC_OPTIONS to determine the set of operating characteristics of the underlying NIC driver. This OID returns such information as whether the underlying driver supports full-duplex transmits and receives.

The intermediate driver usually issues an OID_GEN_MAXIMUM_SEND_PACKETS query, particularly if the intermediate driver exports a MiniportSendPackets function. Such an intermediate driver can propagate the returned value when it, in turn, responds to an OID_GEN_MAXIMUM_SEND_PACKETS query from a still higher level driver.

An intermediate driver also can query the medium-dependent current address with a medium-specific OID, such as OID_WAN_CURRENT_ADDRESS, OID_802_3_CURRENT_ADDRESS, OID_802_5_CURRENT_ADDRESS, or OID_FDDI_LONG_CURRENT_ADDRESS.

If needed, the intermediate driver issues a set request to inform NDIS of its operating characteristics, by passing OID_GEN_PROTOCOL_OPTIONS to NdisRequest.

An intermediate driver bound to a WAN-capable NIC must also make the following set-information requests:

  • OID_WAN_PROTOCOL_TYPE to inform the underlying NIC driver of the protocol’s type. The type is supplied as a single-byte, network level protocol identifier.

  • OID_WAN_HEADER_FORMAT to inform the underlying NIC driver of the header format of the packets it sends.
Responding to Sets and Queries

Because an intermediate NDIS driver is bound to by an overlying NDIS driver, it can also receive queries and sets at its MiniportQueryInformation and MiniportSetInformation functions. In some cases, the intermediate driver just passes such requests through to the underlying NIC driver. Otherwise, it can respond to these queries and sets as appropriate to the medium it exports at its upper edge.

Typically, the general OIDs received by an intermediate driver will be the same or similar to those that the intermediate driver makes to the underlying NIC driver. The medium-specific OIDs received by an intermediate driver will be the type of the medium expected by the overlying driver.


1.3 Intermediate Driver Packet Management

An intermediate driver receives a packet descriptor with one or more chained buffers of data from a higher level driver to send on the network. The intermediate driver can repackage the data to be transmitted in a fresh packet descriptor and simply pass the packet through to the underlying driver by calling NdisSend or NdisSendPackets, or it can take some actions to modify either the contents of the chained buffer(s) or perhaps the ordering or timing of the incoming packet(s) relative to other transmissions. Even if the intermediate driver does nothing other than simply pass on incoming packet(s), for instance if it simply counts the packets, it must allocate a fresh packet descriptor and manage some or all of a new packet structure.

Every intermediate driver must allocate its own packet descriptors to replace those of the overlying driver. If an intermediate driver converts the packet from one format to another, it also can allocate buffer descriptors to map intermediate-allocated buffers into which the converted data is copied. If there is OOB data associated with the packet descriptor being copied, this data can be copied to the new OOB block associated with the intermediate-allocated packet descriptor, using the macro NDIS_OOB_DATA_FROM_PACKET, to obtain a pointer to the OOB data area and, then, calling NdisMoveMemory to move the contents into the OOB area associated with the new packet descriptor. Alternatively, such an intermediate driver can use the NDIS_GET_PACKET_XXX and NDIS_SET_PACKET_XXX macros to read specific items from the OOB data associated with the old packet descriptor and to write the OOB data for the new packet descriptor.

Packet descriptors must be allocated by calling the following NDIS functions:

  1. NdisAllocatePacketPool to allocate and initialize a block of nonpaged pool for a caller-specified number of fixed-size packet descriptors

  2. NdisAllocatePacket to allocate a packet descriptor from the pool allocated by NdisAllocatePacketPool

Depending on the purpose of the intermediate driver, such a driver can repackage buffers chained to incoming packet descriptors. For instance, an intermediate driver would allocate buffer pool and repackage incoming packet data in the following circumstances:

  • The intermediate driver receives a larger data buffer from an overlying protocol driver than can be sent in a single buffer over the underlying medium. Consequently, the intermediate driver must divide the incoming data into smaller buffers.

  • The intermediate driver changes the length of the incoming packet data by compressing or encrypting the data before forwarding each send to the underlying driver.

Such buffers can be allocated by calling the following NDIS routines:

  1. NdisAllocateBufferPool to obtain a handle with which to allocate buffer descriptors

  2. NdisAllocateMemory to allocate a buffer

  3. NdisAllocateBuffer to allocate and set up a buffer descriptor to map the buffer allocated by calling NdisAllocateMemory and to chain to the packet descriptor allocated by calling NdisAllocatePacket

Buffer descriptors are chained to a packet descriptor by calling NdisChainBufferAtBack or NdisChainBufferAtFront. The virtual address and the length of the buffer returned by NdisAllocateMemory are passed in the call to NdisAllocateBuffer to initialize the buffer descriptor that maps the buffer.

Packet descriptors to meet typical needs can be allocated as needed, at driver initialization time, or in the ProtocolBindAdapter function. An intermediate driver developer can, if necessary and for performance reasons, allocate a number of packet descriptors and possibly buffers mapped by buffer descriptors at initialization time so ProtocolReceive has preallocated resources into which to copy incoming data and so MiniportSend or MiniportSendPackets has available descriptors (and possibly buffers) for passing on incoming send packets to the next lower driver.

If an intermediate driver copies send data or received data to a new buffer or buffers, and the length of actual data in the last buffer is less than the allocated length of the buffer, the intermediate driver calls NdisAdjustBufferLength to adjust the buffer descriptor to the actual length of the data. When the packet is returned to the intermediate driver, it should again make this call to readjust the length to the full length of its buffer.

An intermediate driver can either receive incoming data from an underlying NIC driver as a complete packet, specified by a packet descriptor of type NDIS_PACKET, at its ProtocolReceivePacket function or have the data indicated to its ProtocolReceive function and copy it into an intermediate-driver-supplied packet. When the intermediate driver has a complete packet indicated to its ProtocolReceivePacket function, it can retain ownership of the packet descriptor and the resources it describes until the received data is consumed and return these resources to the underlying driver later by calling NdisReturnPackets. If ProtocolReceivePacket passes the resources it received to a higher level driver, it must, at a minimum, replace the input packet descriptor with one that the intermediate driver has allocated.

Depending on an intermediate driver’s purpose when it receives a full packet from an underlying driver, several packet-management strategies are possible. For example, it can do either of the following:

  • Copy the buffered contents into an intermediate driver-allocated buffer mapped and chained to a fresh packet descriptor, return the input packet descriptor to the underlying driver, and, then, indicate the new packet to the higher level driver.

  • Create a new packet descriptor and chain the buffers from the indicated packet descriptor to this new descriptor. This new packet descriptor can then be indicated up to the higher level driver. When this higher level driver returns the packet descriptor, the intermediate driver must unchain the buffers from its packet descriptor, chain them to the packet descriptor it originally received from the underlying driver and return the original packet descriptor and the resources it describes to the underlying driver.

Even if the intermediate driver has a ProtocolReceivePacket function, it must also have a ProtocolReceive function. NDIS calls ProtocolReceive whenever the underlying driver will not relinquish ownership of the resources it indicates with a packet descriptor, and the intermediate driver must copy the received data into its own buffers when such an indication occurs. If the underlying driver also indicates out-of-band data with receives, ProtocolReceive can call NdisQueryReceiveInformation to get the out-of-band data associated with the receive indication.

Reusing Packets

As already mentioned, after NdisSend returns NDIS_STATUS_SUCCESS for a packet descriptor submitted by an intermediate driver, such a packet descriptor is returned to ProtocolSendComplete, or an intermediate-driver-allocated packet is returned to MiniportReturnPacket, ownership of that packet descriptor and all the resources it describes is returned to the intermediate driver. If a higher level driver supplied the buffers chained to such a returned packet descriptor, the intermediate driver should return these resources back to the allocating driver promptly.

If the intermediate driver originally allocated the packet descriptor and/or the chained buffers, it can reclaim its resources and use them in subsequent sends or use them for received data that is indicated to ProtocolReceive. It is more efficient for the intermediate driver to reinitialize and reuse the packet descriptors it allocates, and to reuse any intermediate driver-allocated chained buffer descriptors and buffers, than to deallocate and, then, later reallocate these resources.

An intermediate driver reinitializes a packet descriptor by calling NdisReinitializePacket. However, the intermediate driver should first take care to remove any chained buffers and their buffer descriptors by calling NdisUnchainBufferAt Xxx. Otherwise, since NdisReinitializePacket will clear the member that points to the chain of buffers, such a call without previously deallocating or saving the chained buffers will cause a memory leak. Similarly, if there is OOB data at MediaSpecificInformation associated with the packet descriptor, this memory must be reclaimed before reinitializing the packet descriptor.

An intermediate driver can choose to individually reinitialize specific members of a packet descriptor if only a subset of the fields are used, rather than calling NdisReinitializePacket.


1.4 Restrictions on Intermediate Drivers

Previous sections have described specific actions an intermediate driver must follow to perform correctly, summarized as follows:

  1. An intermediate driver must set the NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER flag when it calls NdisMSetAttributesEx in its MiniportInitialize function. NDIS identifies that a driver is an intermediate type only through the presence of this flag and takes special steps to ensure that deferred actions, such as passing internally-queued send packets on to the intermediate driver, occur without deadlocks.

  2. An intermediate driver must always, at a minimum, replace an incoming packet descriptor for a packet that it passes down to an underlying driver on a send or up to a higher level driver on a receive with a fresh packet descriptor, allocated by the intermediate driver. It must also then replace its own packet descriptor with the one that originally was associated with the packet when it was passed to the intermediate driver, for instance, when completing a send or completing a receive indication. The intermediate driver must return the resources it has "borrowed" from a higher or lower driver promptly by returning that driver's packet descriptor and the resources it specifies.

  3. An intermediate driver is always a full-duplex driver unless it reports the media type of its virtual NIC as NdisMediumWan. Any other intermediate driver must follow the rules that apply to any full-duplex miniport driver, in particular, the following guideline:

    If any internal driver resources are shared between an intermediate driver’s send function and any other MiniportXxx function, that resource must be protected by a spin lock. The only exception to this is in the MiniportReset path, which is serialized by NDIS with respect to the MiniportSend or MiniportSendPackets function and all other MiniportXxx functions.

    As for any full-duplex miniport driver, an intermediate driver that organizes its per-binding context area into discrete receive-specific, send-specific, and shared ranges, with only the shared resource(s) protected by spin lock(s), will exhibit far better performance than a driver that must overprotect its context area because send-specific, receive-specific, and shared variables are scattered throughout.

  4. When an intermediate driver is performing as a protocol driver, that is, in a ProtocolXxx function, it cannot call any NdisM Xxx functions. It can call only the set of Ndis Xxx functions that are callable by any protocol driver and certain NdisIM Xxx functions specific to intermediate drivers. An intermediate driver in a protocol code path that needs to call an NdisM Xxx function must call NDIS to switch to a miniport context before making the call. How this is accomplished is described next.

NDIS synchronizes the execution of MiniportXxx functions as already explained in Part 2. To perform this same synchronization for an intermediate driver so that it can act as a miniport, any ProtocolXxx function in the intermediate driver must call NdisIMSwitchToMiniport before the driver calls an NdisM Xxx that indicates up to the higher level driver. For instance, before ProtocolReceive calls NdisMIndicateReceivePacket or NdisM Xxx IndicateReceive to pass a packet to the next higher driver, it must call NDIS to get into the context of a miniport.

An intermediate driver first calls NdisIMSwitchToMiniport. If NdisIMSwitchToMiniport returns TRUE, ProtocolReceive(Packets) can make its NdisM Xxx call followed by a call to NdisIMRevertBack when its miniport-only operations are complete. NdisIMSwitchToMiniport acquires a spin lock that prevents other MiniportXxx functions in the same driver from running while the spin lock is held. Therefore, it is important that the intermediate driver call NdisIMRevertBack as quickly as possible.

If NdisIMSwitchToMiniport returns FALSE, ProtocolReceive(Packets) must call NdisIMQueueMiniportCallback to schedule a callback function in which to forward the receive indication. NDIS calls the driver-supplied callback function when it obtains the necessary spin lock, thereby allowing the callback function to perform the same actions the intermediate driver would have performed if the preceding call to NdisIMSwitchToMiniport had been successful.

When NdisIMQueueMiniportCallback returns, ProtocolXxx can resume protocol operations. If NdisIMQueueMiniportCallback returns NDIS_STATUS_SUCCESS, the miniport-only operations have been completed in the MiniportCallback function before the return.

Note that the general restriction of switching to a miniport context applies to protocol actions other than just indicating up received packets. For instance, an intermediate driver's ProtocolStatus function also must make a context switch to being a miniport before the driver calls NdisMIndicateStatus to forward a status indication from a lower level driver to a higher level driver.

NdisIMSwitchToMiniport, NdisIMRevertBack and NdisIMQueueMiniportCallback must not be called from any MiniportXxx function. It is a fatal error to make such a call.


1.5 Receiving Data in an Intermediate Driver

Underlying NIC drivers can indicate packets in two ways:

  • A NIC driver calls the non-filter-specific NdisMIndicateReceivePacket, passing a pointer to an array of pointer(s) to one or more packet descriptors, relinquishing ownership of the indicated packet resources to higher level drivers. When the higher level driver(s) have consumed the data, they return the packet descriptor(s) (and the resources they specify) to the NIC driver.

  • A NIC driver calls a filter-specific NdisM Xxx IndicateReceive function, passing a pointer to a lookahead buffer and the total size of the packet.

An intermediate driver always has a ProtocolReceive function, and it can also have a ProtocolReceivePacket function, depending on the environment in which it runs.

  • ProtocolReceive is a required function that receives a pointer to a lookahead buffer. If, after the intermediate driver examines the lookahead data, it determines that the packet is intended for one or more of its overlying drivers, it must copy the data into a previously allocated packet which it will indicate to its overlying driver(s). If the size of the lookahead buffer is less than the total size of the received packet the intermediate driver must first call NdisTransferData in the context of ProtocolReceive, to copy the rest of the received data.

    It is important that ProtocolReceive execute as quickly as possible, so the intermediate driver should have packet descriptors, buffers, and buffer descriptors preallocated for this purpose. ProtocolReceive is usually called because the underlying driver called NdisM Xxx IndicateReceive. However, ProtocolReceive is also called if the underlying NIC driver indicated the received data with NdisMIndicateReceivePacket but set the status in the OOB block for an indicated packet descriptor to NDIS_STATUS_RESOURCES. For such packets indicated with NdisMIndicateReceivePacket, but passed to ProtocolReceive, the size of the lookahead buffer is always equal to the total size of the packet. Consequently, the intermediate driver will not call NdisTransferData for such an indication to ProtocolReceive. However, if the underlying NIC driver also indicates OOB data, the intermediate driver must retrieve this information in ProtocolReceive by calling NdisQueryReceiveInformation.

  • ProtocolReceivePacket is an optional function that receives a pointer to packet descriptor that always describes a full network packet. If any underlying NIC might be a busmaster DMA device, its driver will most likely indicate received packets by calling the non-filter-specific NdisMIndicateReceivePacket, and the intermediate driver should have a ProtocolReceivePacket function if it might bind itself to such an underlying NID driver. In addition, any underlying NIC driver that supports OOB data will most likely pass a packet descriptor on receives to NdisMIndicateReceivePacket so that the intermediate driver can access the OOB data associated with the packet descriptor.

    ProtocolReceivePacket examines the packet and if it determines that the packet is intended for one or more of its overlying drivers, it can retain ownership of the packet by returning a nonzero value from ProtocolReceivePacket. If it does, the intermediate driver must subsequently call NdisReturnPackets with a pointer to the corresponding packet descriptor. The driver must make this call for a particular packet descriptor as many times as the nonzero value it returned from ProtocolReceivePacket when it received that packet.

    When the intermediate driver calls NdisReturnPackets this number of times, it relinquishes ownership of the packet descriptor and associated buffers back to the underlying driver that originally indicated the receive. Therefore, an intermediate driver should call NdisReturnPackets as quickly as possible.

    If, on the other hand, an intermediate driver returns zero from ProtocolReceivePacket, it indicates that it is relinquishing the packet immediately. This could occur, for instance, if the intermediate driver copies the indicated data into buffers of its own and processes the data internally before indicating up to still higher level drivers.

Implementing a ProtocolReceivePacket Handler

When an underlying NIC driver indicates an array of one or more packets, possibly with associated OOB data, by calling NdisMIndicateReceivePacket, NDIS will usually call a bound intermediate driver’s ProtocolReceivePacket with each packet descriptor, allowing the intermediate driver to retain the resources specified by the packet descriptor and to consume the data before returning it. Two kinds of NIC drivers typically call NdisMIndicateReceivePacket with an array of packets:

  1. A NIC driver managing a busmaster DMA adapter that is capable of receiving several network packets into a ring of buffers.

  2. A NIC driver that provides out-of-band data containing media-specific information, such as packet priority, to higher level drivers in the NDIS_PACKET_OOB_DATA block associated with the packet descriptor. Such a driver need not be a driver for a busmaster DMA device.

If an intermediate driver is aware that it is (or might be) bound to such a NIC driver, it should, as mentioned earlier, have a ProtocolReceivePacket function. This allows the intermediate driver to all of the following:

  1. To receive a full network packet at every receive indication,

  2. To use NDIS macros to read the OOB data associated with the packet descriptor, rather than calling NdisQueryReceiveInformation to receive and copy the data,

  3. To retain ownership of incoming packet descriptors and direct read-only access to the buffered data specified by these descriptors, possibly making copies of the data for its clients, and then, when done with each packet descriptor,

  4. To return the packet descriptor and the resources it describes, possibly along with other retained packet descriptors, with NdisReturnPackets.

Even when an intermediate driver provides a ProtocolReceivePacket handler, there are cases when a call by a NIC driver to NdisMIndicateReceivePacket results in a call to an intermediate driver’s ProtocolReceive handler. Since a NIC driver temporarily relinquishes ownership of driver-allocated resources when it calls NdisMIndicateReceivePacket, the underlying driver is dependent on the consumers of those packets to return them with NdisReturnPackets in a timely manner. Otherwise, such a NIC driver can run short of receive resources, such as receive buffer space in the NIC. When it does, the NIC driver writes a status of NDIS_STATUS_RESOURCES into the OOB block associated with a packet descriptor in the packet array it passes to NdisMIndicateReceivePacket. Indicating a packet with this status causes NDIS to call the overlying driver's ProtocolReceive function with such a packet and with any subsequent packets in the array, thus forcing the intermediate driver to copy the packet data rather than taking ownership.

If the intermediate driver requires the OOB data associated with a packet descriptor but is called at ProtocolReceive, it must call NdisQueryReceiveInformation to copy the media-specific information into an intermediate-driver-supplied buffer and possibly the TimeSent and the TimeReceived if the underlying NIC driver provides these timestamps.

Implementing a ProtocolReceive Handler

If the NIC driver calls NdisM Xxx Indicate, ProtocolReceive is always called, and if the intermediate driver accepts the packet, ProtocolReceive must call NdisTransferData with a packet descriptor into which the lookahead buffer has been copied and into which the rest of the packet is copied. NdisTransferData must be called in the context of ProtocolReceive, and can only be called once. It is the responsibility of the intermediate driver to set up a packet descriptor with chained buffers of a sufficient size to contain all the received data. After NdisTransferData returns, the received data is no longer available from the underlying NIC driver.

If the data passed to ProtocolReceive was indicated by a call to NdisM Xxx IndicateReceive, the size of the lookahead buffer passed to ProtocolReceive is <= the size returned by a call to NdisRequest with OID_GEN_CURRENT_LOOKAHEAD. All data in the lookahead buffer is read-only to the intermediate driver. If the call to ProtocolReceive occurred because the underlying NIC driver set the status of one or more packets in a packet array to NDIS_STATUS_RESOURCES before calling NdisMIndicateReceivePacket, the size of the lookahead buffer will always be equal to the size of the full network packet so the intermediate driver need not call NdisTransferData.

ProtocolReceive must return control as quickly as possible. The intermediate driver should insure that it has packet descriptors, buffer descriptors and buffers available before it gets receive indications. If the intermediate driver examines the lookahead data and determines that the packet is not one it will copy, the intermediate driver should return NDIS_STATUS_NOT_ACCEPTED.

ProtocolReceive must not process received data as it is copied since that would adversely impact system performance, as well as the ability of the underlying NIC to accept incoming receives from the network. Instead, the intermediate driver processes the received data later in its ProtocolReceiveComplete function, which is called subsequently when the packets can be postprocessed. Typically, this occurs when the underlying NIC driver has received and indicated a NIC-driver-determined number of packets or before it exits its DPC-level receive handler. The intermediate driver must queue the copied net packets in ProtocolReceive so that they are available to ProtocolReceiveComplete for postprocessing.

Accessing OOB Information

If a received network packet is indicated to ProtocolReceive, it forces the driver to copy the received data into an intermediate-driver-supplied buffer. If the packet contains media-specific and/or timestamp information in the OOB data associated with that packet descriptor, an intermediate driver calls NdisQueryReceiveInformation to retrieve the media-specific information, as well as TimeSent and TimeReceived if such information is provided by the underlying NIC driver.

If a received packet is passed to ProtocolReceivePacket, the intermediate driver must obtain the information from the OOB data associated with the packet using NDIS-supplied macros as follows:

  • Media-specific information is read using NDIS_GET_MEDIA_SPECIFIC_INFO and written using NDIS_SET_MEDIA_SPECIFIC_INFO

  • TimeSent is read using NDIS_GET_TIME_SENT and written using NDIS_SET_TIME_TO_SEND

  • TimeReceived is read using NDIS_GET_TIME_RECEIVED

TimeSent is the time a packet was sent by the NIC on the remote node, and is retrieved and stored by the underlying NIC driver if available. TimeReceived is the time that the incoming packet was received on the underlying NIC.

Passing Packets to Higher Level Drivers

After an intermediate driver has processed a received packet, perhaps converted it to the format expected by a higher level driver, and copied relevant data into buffers chained to an intermediate-driver-allocated packet descriptor, the packet is indicated to the next higher driver as if the intermediate driver were a miniport. Certain restrictions are imposed on an intermediate driver when it is acting as a miniport, as already described in Section 1.4.


1.6 Transmitting Packets Through an Intermediate Driver

As discussed in Section 1.1.1, an intermediate driver should always provide a MiniportSendPackets function when it registers with NdisIMRegisterLayeredMiniport. If the intermediate driver is layered between two NDIS drivers that support multipacket sends, the MiniportSendPackets function can forward incoming packet arrays by calling NdisSendPackets. If the intermediate driver is layered under a transport that sends one packet at a time at NdisSend, the MiniportSendPackets function can transmit the single packet with NdisSend or NdisSendPackets without any negative performance impact.

Such an intermediate driver does not become a performance bottleneck. If the intermediate driver is layered between a transport that sends a packet array to MiniportSendPackets and a miniport that only handles a single packet at a time, MiniportSendPackets can send the array of packets it receives with NdisSendPackets without regard to the capabilities of the underlying miniport. NDIS, transparently to the intermediate driver, queues the packets in the array and sends each individual packet to the underlying miniport’s MiniportSend function when the miniport is able to accept the send request.

Because calls down to the MiniportXxx functions originate in NDIS, synchronization is guaranteed, so an intermediate driver need not make any NdisIM Xxx calls as described in Section 1.4, when forwarding requests that originate in higher level drivers to underlying drivers.

An intermediate driver will, at a minimum, replace the packet descriptor for each incoming send with its own. It must retain the original descriptor (and chained buffers if it copies them to new buffers) from the higher level driver and, when the send completes, the intermediate driver must return the original packet descriptor and the data buffers of the as-sent packet before completing the send back to the higher level driver. For more information on how to allocate packet resources and copy information from one packet to another, see Section 1.3.

MiniportSendPackets receives an array of packet descriptors arranged in an order determined by the caller of NdisSendPackets. In most cases, the intermediate driver should maintain this ordering as it passes an incoming array of packets on to the underlying NIC driver. Only an intermediate driver that adds out-of-band information to incoming packets before passing them on to the underlying driver is likely to reorder an incoming array.

NDIS always preserves the ordering of packet descriptor pointers as passed as an array to NdisSendPackets. The underlying miniport NIC driver also assumes that an array of packet pointers passed in to its MiniportSendPackets function implies the packets should be transmitted in the same order.

An intermediate driver can transmit a single packet by calling NdisSend with a pointer to the packet descriptor. An intermediate driver can transmit several packets or just one using NdisSendPackets and passing a pointer to an array of pointer(s) to one or more packet descriptors.

An intermediate driver developer, in general, should determine whether to use NdisSend or NdisSendPackets based upon the driver’s own requirements and any known characteristics of the underlying NIC driver. An intermediate driver can call NdisRequest with a RequestType of NdisQueryInformation and an OID_GEN_MAXIMUM_SEND_PACKETS to obtain the maximum number of send packets that the underlying driver will accept in a packet array. If the underlying driver returns a value of one or NDIS_STATUS_NOT_SUPPORTED, the intermediate driver can choose to use NdisSend rather than NdisSendPackets. If the underlying driver returns a value greater than one, both drivers' performance will be better if the intermediate driver sends an array of packets with NdisSendPackets.

If any underlying NIC driver returns a value greater than one, the intermediate driver also should have a MiniportSendPackets function. In addition, such an miniport should respond to an OID_GEN_MAXIMUM_SEND_PACKETS query with a comparable value to that of the underlying NIC driver.

If OOB information is passed between the intermediate driver and the NIC driver, either send function can be called, since in either case the underlying driver can read the OOB data associated with the packet descriptor using NDIS-supplied macros.

If an intermediate driver sends more packets to an underlying NIC driver then that driver has the internal resources to transmit:

  • Either the underlying NIC driver will queue the excess packets in an internal queue and transmit them when it can,

  • Or, the underlying NIC driver will return the excess packet(s) with a status of NDIS_STATUS_RESOURCES.

    In this case, NDIS will hold such packet(s) in an internal queue and resubmit them when the NIC driver next calls NdisMSendResourcesAvailable or NdisMSendComplete, whichever occurs first.

When an intermediate driver calls NdisSend from MiniportSend, it relinquishes ownership of the packet descriptor and of all the resources it describes until the send completes, either synchronously or asynchronously. If the status returned by NdisSend is other than NDIS_STATUS_PENDING, the call completes synchronously, and ownership of the packet resources reverts to the intermediate driver on return from NdisSend. The intermediate driver should return any send resources allocated by a higher level driver and propagate the result of its call to NdisSend as the result of MiniportSend.

If the status returned by NdisSend is NDIS_STATUS_PENDING, when the send subsequently completes, the final status of the send and the packet descriptor will be returned to ProtocolSendComplete. The intermediate driver should call NdisSendComplete from its ProtocolSendComplete function to propagate the send status up to the next higher driver. Since ProtocolSendComplete must call NdisIMSwitchToMiniport before completing the send to the next higher driver, it should call NdisIMRevertBack as quickly as possible.

When an intermediate driver transmits one or more packet(s) by calling NdisSendPackets, the send operation is implicitly asynchronous. The caller relinquishes ownership of its packet descriptor(s) and all the resources they describe until each packet descriptor and the final status of the send for that packet is returned to ProtocolSendComplete. ProtocolSendComplete must propagate the status of the send of each packet back to the next higher driver as described in the previous paragraph.

As a consequence, if an intermediate driver sends packets in an array using NdisSendPackets, the intermediate driver must not attempt to read the Status member(s) of the associated OOB block(s) on return from NdisSendPackets. This member is in use by NDIS to track the progress of an in-transition send request and is volatile. An intermediate driver can only obtain the status of a transmit request by examining the Status argument passed to its ProtocolSendComplete function.

If an intermediate driver requests the transmission of an array of packets of different priorities by rearranging the packets it receives from an overlying driver before transmitting them, it should place the highest priority packets at the beginning of the array. NDIS retains this ordering when it passes the packets to either the MiniportSend or to the MiniportSendPackets function of the underlying NIC driver, even if NDIS queues some of the packets internally. This ordering is maintained by NDIS for each call to NdisSendPackets.

NDIS never attempts to examine and make queuing decisions based on the OOB block associated with a packet descriptor. Unless an intermediate driver has special knowledge of the manner in which a NIC driver handles packet priority, it should assume that the NIC driver transmits the packets in the order in which it receives them, preserving the as-received order.

The structure of the Private member of an NDIS_PACKET-type descriptor is opaque to an intermediate driver and is accessed to read and, in some cases, to write using NDIS-supplied macros or functions. For example, before sending a packet, an intermediate driver can call NdisSetPacketFlags to set the intermediate-defined flags in the NDIS-private portion of the descriptor. Such flags are not defined by NDIS, but are defined by a pair of cooperating protocol and underlying NIC drivers.

Passing Media-Specific Information

More media-specific information can be passed by an intermediate driver in the OOB block associated with each NDIS_PACKET descriptor. The definition of the OOB block is:

typedef struct _NDIS_PACKET_OOB_DATA {
union {
ULONGLONG TimeToSend;
ULONGLONG TimeSent;
};
ULONGLONG TimeReceived;
UINT HeaderSize;
UINT SizeMediaSpecificInfo;
PVOID MediaSpecificInformation;
NDIS_STATUS Status;
} NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA;

The structure of individual records within the buffer at MediaSpecificInformation is defined as follows:

typedef struct MediaSpecificInformation {
UINT NextEntryOffset;
NDIS_CLASS_ID ClassId;
UINT Size;
UCHAR ClassInformation[1];
} MEDIA_SPECIFIC_INFORMATION;

The ClassId member is an NDIS-defined enumeration that defines the type of information found at ClassInformation[1]. Currently, there are two class IDs in use across Microsoft operating systems that support Win32: NdisClass802_3Priority and NdisClassWirelessWanMbxMailbox. See the Network Driver Reference for details.

If the intermediate driver knows that the underlying NIC driver to which it is sending packets uses OOB data, it can set certain OOB structure members. For instance, an intermediate driver can:

  • Request that the packet be sent at a specific time by writing the TimeToSend member using the NDIS_SET_PACKET_TIME_TO_SEND macro. This macro passes the requested time in system time units. The driver can call NdisGetCurrentSystemTime in order to obtain the current system time with which to calculate a requested send time.

  • Pass media-specific information in a caller-allocated buffer at MediaSpecificInformation using the NDIS_PACKET_SET_MEDIA_SPECIFIC_INFO macro. For instance, if an intermediate driver is bound to an underlying NIC that requires priority, it will set the ClassId member of the MediaSpecificInformation structure to NdisClass802_3Priority, and pass priority-related information in the ClassInformation member and the size in bytes of this information in Size.


1.7 Intermediate Driver Reset Operations

An intermediate driver must be prepared to handle the situation where its outstanding sends on a binding to an underlying driver can be aborted because the underlying NIC is reset.

An underlying driver resets its NIC, usually because NDIS calls the NIC driver’s MiniportReset function if NDIS times out queued sends or requests bound for the NIC. If an underlying NIC is reset, NDIS calls the ProtocolStatus function of each bound protocol and intermediate driver with a status of NDIS_STATUS_RESET_START, and then calls the same bound driver’s ProtocolStatusComplete function. When the NIC driver completes the reset, NDIS again calls ProtocolStatus with a status of NDIS_STATUS_RESET_END followed by a call to ProtocolStatusComplete.

When a NIC is reset, if a bound intermediate driver has any transmit packets pending to that NIC, NDIS completes those packets back to the intermediate driver with an appropriate status. The intermediate driver must resubmit these packets again when the reset is completed.

When an intermediate driver receives a status of NDIS_STATUS_RESET_START, it should:

  • Hold any packets ready to be transmitted until ProtocolStatus receives an NDIS_STATUS_RESET_COMPLETE notification and ProtocolStatusComplete is called.

  • Hold any received packets that are ready to be indicated up to the next higher driver until ProtocolStatus receives an NDIS_STATUS_RESET_COMPLETE notification and ProtocolStatusComplete is called.

  • Clean up any internal state it maintains for in-progress operations and NIC status.

After ProtocolStatus receives an NDIS_STATUS_RESET_END message and ProtocolStatusComplete is called, the intermediate driver can resume sending packets, making requests and making indications to higher level drivers.

Because an intermediate driver usually disables timing out of sends and requests by NDIS when it calls NdisMSetAttributesEx, its MiniportReset function is seldom called. If MiniportReset is called, possibly because a higher level driver called NdisReset, the intermediate driver should reset its internal state if necessary and it should always set AddressingReset to TRUE before it returns. When the underlying NIC is reset, NDIS will send requests to MiniportSetInformation to reset the intermediate drivers internal address state for the underlying NIC if the NIC is in a condition to continue transmitting and receiving packets. MiniportReset does not have to complete outstanding sends; NDIS will fail any outstanding sends from the higher level driver appropriately. However, associated state kept by the intermediate driver possibly should be cleaned up.

An intermediate driver can initiate a reset operation by calling NdisReset. If the reset request returns NDIS_STATUS_PENDING, ProtocolResetComplete is called when the underlying NIC or virtual NIC is reset and the driver for the NIC calls NdisMResetComplete. An intermediate driver seldom calls NdisReset unless it has special knowledge that the underlying NIC is not functioning correctly. For instance, if an intermediate driver detects that it has not received completion calls for an unusually large number of sends or requests, and if it has enough knowledge of the underlying NIC to conclude there is a problem, it can call NdisReset. Usually, however, the need for a NIC reset is detected and initiated by NDIS and the NIC driver using their time-out logic.


1.8 Intermediate Driver Unbinding Operations

An intermediate driver unbinds from the underlying NIC driver by calling NdisCloseAdapter. If an intermediate driver registers a ProtocolUnbindAdapter function, NDIS will call ProtocolUnbindAdapter if the underlying NIC is no longer available. For instance, if the underlying NIC timed out, and NDIS called the MiniportHalt function of the NIC driver for the malfunctioning NIC, NDIS will subsequently call the overlying intermediate driver’s ProtocolUnbindAdapter function.

An intermediate driver can also originate the unbind operation, for instance in ProtocolStatus, if the status it receives at GeneralStatus is NDIS_STATUS_CLOSING. An intermediate driver also must unbind from an adapter during initialization if some action subsequent to opening an adapter, such as allocating a required resource, fails, and the intermediate driver needs to unbind because it cannot carry out network operations on the binding.

An intermediate driver’s ProtocolUnbindAdapter function calls NdisCloseAdapter. The intermediate driver must not release the per-binding resources until NdisCloseAdapter returns NDIS_STATUS_SUCCESS or the driver calls NdisCompleteUnbindAdapter. If NdisCloseAdapter returns NDIS_STATUS_PENDING, the intermediate driver cannot deallocate any per-binding resources until ProtocolCloseAdapterComplete is called.

NDIS does not call ProtocolCloseAdapterComplete until all outstanding requests on the binding have completed. When ProtocolCloseAdapterComplete returns control, the ProtocolBindingContext handle the intermediate driver allocated to represent the binding is invalid.

If the underlying NIC binding that is being closed is mapped to a device exported by the intermediate driver and if that device was initialized by calling NdisIMInitializeDeviceInstance, the intermediate driver can close these devices by calling NdisIMDeInitializeDeviceInstance. The result is that the intermediate driver's virtual NIC becomes no longer available for sends or requests made by higher level drivers.

After an intermediate driver calls NdisCloseAdapter, it should fail any send requests for that binding with an appropriate error status.


1.9 Status Indications in an Intermediate Driver

An intermediate driver is required to supply a ProtocolStatus function and a ProtocolStatusComplete function. NDIS calls ProtocolStatus when an underlying NIC driver calls NdisMIndicateStatus to report a change in its hardware status. ProtocolStatus is called when the status change begins. If the action indicated by the status message is not complete when ProtocolStatus is called, NdisMIndicateStatusComplete is subsequently called by the underlying NIC driver. When this occurs, ProtocolStatusComplete is called to carry out any postprocessing for the status change. Examples of GeneralStatus reported to ProtocolStatus include:

  • NDIS_STATUS_CLOSING. This status and the actions of ProtocolStatus is discussed in Section 1.7.

  • NDIS_STATUS_RESET_START and NDIS_STATUS_RESET_END. Both these are reported to both ProtocolStatus and ProtocolStatusComplete as explained in Section 1.8.

  • NDIS_STATUS_LINE_UP, if the intermediate driver is layered over a WAN-capable NIC driver that has established a connection with a remote node. See Part 2, Chapter 5 and Chapter 6, for more information on WAN drivers.

  • NDIS_STATUS_RING_STATUS, for which StatusBuffer provides more detailed information, for instance on problems specific to a Token Ring medium.

When an intermediate driver receives a status indication, it indicates it up to the higher level driver(s) by calling NdisIndicateStatus only if the status indication causes the intermediate driver to change its internal state in a way that affects the operation of its MiniportXxx functions. That is, if the status indicated to the intermediate driver causes the driver to fail sends or requests, it can pass the status indication to the higher level driver(s) which presumably will then hold off on submitting sends and requests. If, however, the intermediate driver continues to accept sends and requests, perhaps queuing them internally, it need not pass the status indication up.


Chapter 2 NDIS Protocol Drivers

This describes the features of an NDIS driver that exports a set of ProtocolXxx functions at its lower edge. Such a protocol driver communicates with NDIS to send and receive network packets and to bind to and use an underlying miniport NIC driver or intermediate NDIS driver that exports a MiniportXxx interface at its upper edge.

Such an NDIS protocol driver might support TDI at its upper edge, or it might export a private interface to a higher level kernel-mode driver, possibly through a transport stack of drivers, including one that supports TDI at the top of the stack. For instance, an NDIS protocol driver can be the lowest module of a multimodule transport implementation of a standard protocol such as TCP/IP with TDI support in the highest module.

Protocol drivers that communicate with lower level NDIS drivers to send and receive packets always use NDIS-provided functions to communicate with the lower level NDIS drivers. For instance, a protocol driver must call NdisSend or NdisSendPackets to send a packet or packets to a lower level NDIS driver and must call NdisRequest to make or pass through query- or set-information requests with network related OID_XXXs that are supported by underlying drivers.

NDIS also provides a set of Ndis Xxx functions that hide the details of the underlying operating system. For instance, a protocol driver can call NdisInitializeEvent to create an event for synchronization purposes and NdisInitializeListHead to create a linked list. Protocol drivers that use the NDIS versions of such functions are more portable across Microsoft operating systems that support the Win32 interface. However, protocol drivers also can call OS-specific kernel-mode support routines, such as KeInitializeEvent to create an event or KeWaitForSingleObject to synchronize two threads of execution. The Windows NT kernel-mode support routines are documented in the Kernel-Mode Drivers Reference.

Pageable and Discardable Code

Every ProtocolXxx function runs at an IRQL in the range from PASSIVE_LEVEL to DISPATCH_LEVEL.

Functions that run exclusively at IRQL PASSIVE_LEVEL should be marked as pageable using the NDIS_PAGABLE_FUNCTION macro. Driver developers should designate code as pageable whenever possible, freeing system space for code that must be memory-resident. A driver function that runs at IRQL PASSIVE_LEVEL can be made pageable as long as it neither calls nor is called by any function that runs at IRQL >= DISPATCH_LEVEL, for instance a function that acquires a spin lock. Acquiring a spin lock causes the IRQL of the acquiring thread to be raised to IRQL DISPATCH_LEVEL. A driver function, such as ProtocolBindAdapter, that runs at IRQL PASSIVE_LEVEL must not call any Ndis Xxx functions that run at IRQL >= DISPATCH_LEVEL if that driver function is marked as pageable code. See the Network Driver Reference, which specifies the IRQL for each Ndis Xxx function.

The DriverEntry function of a protocol driver should be specified as initialization-only code, using the NDIS_INIT_FUNCTION macro. Code identified with this macro is assumed to only run once at system initialization time, and as a result, is only mapped during that time. After a function marked as initialization-only returns, it is discarded.

Access to any driver-allocated shared resource must be synchronized if the resource can be simultaneously shared by two driver functions or if the protocol driver can run on an SMP machine such that the same protocol driver function can be attempting to simultaneously access a resource from more than one processor. For instance, if a driver maintains a shared queue, a spin lock can be used to serialize access to that queue. The spin lock should be initialized when such a queue is created.

However, care should be taken not to overprotect a shared resource, such as an internal driver queue. Some read-only operations can be done without serializing access to a queue, but any operation that manipulates the queue links must be serialized. Spin locks always should be used sparingly and held as short a time as possible. See the Kernel-Mode Drivers Design Guide for an in-depth discussion of spin locks.


2.1 Protocol DriverEntry and Initialization

A protocol driver’s initial required entry point must be explicitly named DriverEntry so that the loader can identify it. All other exported functions, described here as ProtocolXxx, can have any developer-specified name since they are passed as addresses to NDIS. The definition of DriverEntry is that of any Windows NT kernel-mode driver.

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

If the protocol driver exports a set of standard kernel-mode driver routines, such as any (Tdi)DispatchXxx and Unload routines, in addition to the NDIS-defined ProtocolXxx functions, the protocol driver must set the entry points of its standard routines in the DriverObject passed to DriverEntry, like any other kernel-mode intermediate driver.

To set up communication with the NDIS library, a protocol's DriverEntry must register as a protocol driver by calling NdisRegisterProtocol, as described later.

DriverEntry also can initialize any spin locks the protocol requires, for example to protect state variables that track connections and sends in progress or driver-allocated queues.

If DriverEntry fails to allocate any resources the protocol needs to operate, it should release any previously allocated resources, including making a call to NdisDeregisterProtocol if necessary, and return an appropriate error status.

While a protocol driver can allocate all the resources it requires in DriverEntry, if the driver provides a ProtocolBindAdapter function as described in Section 2.3, it can defer opening and binding to an underlying NIC (or virtual NIC) driver, as well as reserving system resources to manage such a binding until the initial network I/O request occurs. Then, when ProtocolBindAdapter calls NdisOpenAdapter, it allocates resources as needed for the underlying NIC it opens.


2.1.1 Registering as an NDIS Protocol Driver

A driver registers its ProtocolXxx functions with NDIS in the context of its DriverEntry function by calling NdisRegisterProtocol, defined as follows:

VOID
NdisRegisterProtocol(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE NdisProtocolHandle,
IN PNDIS_PROTOCOL_CHARACTERISTICS
ProtocolCharacteristics,
IN UINT CharacteristicsLength
);

The NdisProtocolHandle returned by this call is opaque to a protocol driver. The handle must be retained by the protocol driver and provided as an input parameter in future calls to NDIS, to open an adapter for example.

Before making this call, DriverEntry must do the following:

  1. Zero-initialize a structure of type NDIS_PROTOCOL_CHARACTERISTICS, for instance with a call to NdisZeroMemory. This assures that unused members for optional entry points are set to NULL. If the structure is not zeroed, any unused members must be set to NULL before calling NdisRegisterProtocol.

  2. Store the addresses of the mandatory ProtocolXxx functions, as well as any optional ProtocolXxx functions the driver exports, in the characteristics structure members, as follows:

    BindAdapterHandler
    This is an optional function in protocol drivers. A PnP-ready protocol driver specifies this entry point so it can be called by NDIS at the ProtocolBindAdapter function when an adapter is available. Specifying this entry point requires a V4.0 structure at ProtocolCharacteristics.
    UnbindAdapterHandler
    This is a required function if the protocol driver registered a ProtocolBindAdapter function. Specifying this entry point also requires a V4.0 structure at ProtocolCharacteristics.
    OpenAdapterCompleteHandler
    This is a required function. If a protocol driver’s call to NdisOpenAdapter returns NDIS_STATUS_PENDING, ProtocolOpenAdapterComplete is subsequently called to complete the binding operation.
    CloseAdapterCompleteHandler
    This is a required function. If a protocol driver’s call to NdisCloseAdapter returns NDIS_STATUS_PENDING, ProtocolCloseAdapterComplete is subsequently called to complete the unbinding operation.
    ReceiveHandler
    This is a required function. ProtocolReceive is called with a pointer to a lookahead buffer. If this buffer contains less than the full, received network packet, ProtocolReceive calls NdisTransferData with a protocol-allocated packet descriptor specifiying protocol-allocated buffer(s) to obtain the remainder of the received packet.
    ReceiveCompleteHandler
    This is a required function. ProtocolReceiveComplete is called to indicate that any received packets previously indicated to ProtocolReceive can now be postprocessed.
    TransferCompleteHandler
    This is a required function unless the protocol binds itself exclusively to underlying NIC driver(s) that indicate packets with NdisMIndicateReceivePacket. ProtocolTransferDataComplete is called when a previous call to NdisTransferData returned NDIS_STATUS_PENDING and the remaining data has been copied into the protocol-supplied buffers chained to a given packet descriptor.
    ReceivePacketHandler
    This is an optional function. A ProtocolReceivePacket function should be provided if the protocol driver might be bound to a NIC driver that indicates an array of one or more packets by calling NdisMIndicateReceivePacket. Specifying this entry point requires a V4.0 structure at ProtocolCharacteristics.
    SendCompleteHandler
    This is a required function. ProtocolSendComplete is called for each packet transmitted with a call to NdisSend that returned NDIS_STATUS_PENDING as the status of the send operation. If an array of packets is sent, ProtocolSendComplete is called once for each packet passed to NdisSendPackets, whether or not it returned pending.
    ResetCompleteHandler
    This is a required function. ProtocolResetComplete is called when a protocol-initiated reset operation, begun with a call to NdisReset that returned NDIS_STATUS_PENDING, is completed.
    RequestCompleteHandler
    This is a required function. ProtocolRequestComplete is called when a protocol-initiated query or set operation, begun with a call to NdisRequest that returned NDIS_STATUS_PENDING, is completed.
    StatusHandler
    This is a required function. ProtocolStatus is called to handle status changes indicated by the underlying NDIS driver.
    StatusCompleteHandler
    This is a required function. ProtocolStatusComplete is called by NDIS, along with ProtocolStatus, to report the start and end of an NDIS- or NIC-driver-initiated reset operation.

A protocol driver should set the ProtocolCharacteristics TranslateHandler and UnloadHandler members to NULL. The TranslateHandler member is reserved for future use. NDIS never calls an entry point specified in UnloadHandler on Windows NT platforms. Instead, the Windows NT I/O Manager calls the Unload routine that the protocol driver sets in the driver object passed in to the protocol's DriverEntry function.


2.1.2 Opening an Adapter Underlying a Protocol Driver

The protocol driver reads the registry information stored during setup to build a list of names of adapters to which it will bind. The registry contains binding information written when the network is configured. DriverEntry reads this information, including the names of the adapter or adapters to which it can bind, from the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
\Services\ProtocolComponentName\Linkage key in the registry.

A protocol driver calls NdisOpenProtocolConfiguration to obtain a handle to the registry key at HKEY_LOCAL_MACHINE\SYSTEM
\CurrentControlSet\Services\DeviceInstance\Parameters\ProtocolName, where the protocol driver can store adapter-specific information. A protocol driver that stores adapter-specific information in the registry must store it under this key and use NDIS functions to access the information. Once a handle to this key is obtained, a protocol driver calls the NdisRead/WriteConfiguration functions to read and write such information. The NdisRead/WriteConfiguration functions are described in the Network Driver Reference.

If a protocol driver provides a ProtocolBindAdapter function, the SystemSpecific1 parameter passed to this function is passed uninterpreted to NdisOpenProtocolConfiguration at ProtocolSection. If a protocol driver does not provide a ProtocolBindAdapter function, the driver is assumed to know both the name of the adapter it is opening and the string value to be passed at ProtocolSection.

After a protocol driver has retrieved the information it requires from the registry and has registered by calling NdisRegisterProtocol, and before the protocol driver can send packets and receive incoming data, it must bind itself to one or more underlying NICs managed by a miniport or an intermediate NDIS driver. The protocol binds itself to an underlying NIC and the miniport that controls it by calling NdisOpenAdapter, declared as follows:

VOID
NdisOpenAdapter(
OUT PNDIS_STATUS Status,
OUT PNDIS_STATUS OpenErrorStatus,
OUT PNDIS_HANDLE NdisBindingHandle,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE NdisProtocolHandle,
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_STRING AdapterName,
IN UINT OpenOptions,
IN PSTRING AddressingInformation
);

A protocol driver passes a handle at ProtocolBindingContext to an adapter-specific context area that it allocated to store state information for the binding. NDIS will return this handle to the protocol driver in subsequent calls pertaining to the binding, for instance, in calls to ProtocolReceive or ProtocolStatus. NDIS returns a handle at NdisBindingHandle to the protocol driver. This handle must be retained by the protocol, usually in its adapter-specific context area. The protocol driver must pass this handle to NDIS in future calls relating to this binding, such as calls to NdisSend or NdisSendPackets.

The protocol driver passes the name of the adapter that it has read from the registry or that was input to the ProtocolBindAdapter function at DeviceName to NdisOpenAdapter at AdapterName. It passes the type(s) of medium(s) it supports at MediumArray. If the call to NdisOpenAdapter succeeds, the underlying NIC driver will select a medium from MediumArray and return its index at SelectedMediumIndex. The value at NdisProtocolHandle is the value returned to the protocol driver from a successful call to NdisRegisterProtocol.

If a protocol driver cannot successfully bind to an underlying adapter, it deallocates any resources it previously allocated for that adapter. If the protocol driver cannot successfully open any of the possible adapters, it should deallocate any global resources the protocol has previously allocated and return an appropriate failure status. In this case, the protocol driver will subsequently be unloaded. Typically, its DriverEntry or ProtocolBindAdapter function should log failed binding attempts with appropriate descriptive information, either by calling NdisWriteErrorLogEntry or an OS-specific support routine, such as IoWriteErrorLogEntry.


2.1.3 Protocol Driver Query and Set Operations

After a protocol driver has determined which adapters to open and has successfully bound to one or more, it can call NdisRequest to query the underlying NDIS driver for its characteristics and to set the protocol's own operating characteristics. The protocol can also negotiate certain parameters for the binding.

The driver can make requests with general OID_GEN_XXX types of OIDs, that are independent of the underlying medium. The driver can also make requests with medium-specific OIDs that are defined for a particular medium type. The Network Driver Reference documents both types of OIDs. Some OIDs are mandatory, meaning an underlying driver must support them, others are optional.

The protocol driver uses the general OID_GEN_MAXIMUM_FRAME_SIZE to query the maximum frame size, in bytes, supported by an underlying NIC driver. The size returned does not include the packet header.

The protocol driver queries a binding with the general OID_GEN_MAXIMUM_TOTAL_SIZE to determine the largest packet the underlying NIC driver can accommodate on the NIC it manages. The protocol driver must restrict the size of the packets it subsequently sends to this size. It is an error for a protocol driver to submit a larger packet to the underlying NIC driver than the NIC driver has indicated it can support.

A protocol driver should use OID_GEN_MAXIMUM_SEND_PACKETS to determine the number of send packets an underlying driver can accept each time the protocol calls NdisSendPackets. See Section 2.5 for a discussion of how this request can affect the protocol driver’s subsequent send operations.

The protocol driver can make a query request and/or a set request concerning the size of the lookahead data buffer the underlying driver supplies whenever the protocol's ProtocolReceive function is called. The OID used is OID_GEN_CURRENT_LOOKAHEAD. If the protocol driver issues this as a query request, NDIS returns the current lookahead buffer size for the given binding to the underlying NIC driver. If the protocol driver makes a set request with this OID code, it indicates its preferred lookahead buffer size, but the protocol is not assured that an underlying driver will conform to this request. If the underlying driver returned a value greater than one for a preceding OID_GEN_MAXIMUM_SEND_PACKETS query, that driver always indicates receives with a lookahead buffer containing a full network packet.

The protocol driver can query the underlying NIC driver for its link speed with OID_GEN_LINK_SPEED and use the results to set any internal time-out values it maintains. If the protocol driver is bound to NDISWAN, it cannot determine the link speed until it receives a line-up indication from the WAN NIC driver, meaning a connection has been established between the local node and a remote node.

The protocol driver must also issue an OID_GEN_MAC_OPTIONS query to determine the operating characteristics of the underlying NIC driver. This query returns such information as whether the underlying driver supports full-duplex operations. See the Network Driver Reference for the set of NDIS-defined flags that can be returned for this query.

If needed, the protocol driver issues a set request to inform NDIS of its own operating characteristics, passing OID_GEN_PROTOCOL_OPTIONS.

A protocol driver bound above a WAN-capable NIC must also make the following set-information requests:

  • OID_WAN_PROTOCOL_TYPE to inform the underlying NDISWAN driver of the type of the protocol driver. The type is supplied as a single-byte, network-level protocol identifier.

  • OID_WAN_HEADER_FORMAT to inform the underlying NDISWAN driver of the header format of the packets it sends.

The protocol driver queries the medium-dependent current address with a medium-dependent OID, such as OID_WAN_CURRENT_ADDRESS, OID_802_3_CURRENT_ADDRESS, OID_802_5_CURRENT_ADDRESS, or OID_FDDI_LONG_CURRENT_ADDRESS.


2.2 Protocol Driver Packet Management

A protocol driver receives one or more buffers of data from a client to transmit over the network. A protocol driver must, at a minimum, allocate and initialize a packet descriptor to which the client’s data buffers are chained. Packet descriptors must be allocated from packet pool, as follows:

  1. Call NdisAllocatePacketPool to allocate and initialize a block of nonpaged pool for a caller-specified number of fixed-size packet descriptors during driver initialization or when each binding is first established.

  2. Call NdisAllocatePacket to allocate a packet descriptor from the pool allocated by NdisAllocatePacketPool.

Buffer descriptor(s) mapping such buffer(s) are chained to a packet descriptor by calling NdisChainBufferAtBack or NdisChainBufferAtFront. If a protocol driver receives a data buffer from a client that must be sent in several smaller buffers, the protocol driver can copy the data into protocol-allocated buffers, map its buffers with previously allocated buffer descriptors, and chain these buffer descriptors to protocol-allocated packet descriptors. Such buffers can be allocated by calling a kernel-mode support routine or NdisAllocateMemory and can be mapped with protocol-allocated buffer descriptors as follows:

  1. Call NdisAllocateBufferPool to obtain a handle with which to allocate buffer descriptors during driver initialization or when each binding is first established.

  2. Call NdisAllocateMemory to allocate a buffer to chain to a packet descriptor allocated by calling NdisAllocatePacket.

  3. Call NdisAllocateBuffer to allocate and set up a buffer descriptor that maps the buffer allocated by calling NdisAllocateMemory.

The base virtual address and the length returned by NdisAllocateMemory are passed in the call to NdisAllocateBuffer to initialize a buffer descriptor.

Packet descriptors to meet typical transmit needs can be allocated as needed, when the driver initializes, and/or at binding time. A protocol driver developer can allocate a number of packet descriptors with chained buffer descriptors at initialization time to hold receives that can occur as soon as the protocol binds itself to an underlying NIC driver. Then, ProtocolReceive can return control to the underlying driver as quickly as possible. Otherwise, subsequently received data can be lost.

As described later, a protocol driver can either receive incoming data from an underlying NIC driver at its ProtocolReceivePacket function as a packet descriptor specifying a full network packet or have data indicated to its ProtocolReceive function, which must copy the indicated data into a protocol-supplied buffer chained to a protocol-allocated packet descriptor.

Every protocol driver must provide a ProtocolReceive function. When this function is called, the protocol driver must copy the indicated lookahead data into a protocol-allocated buffer chained to a preallocated packet descriptor, which it must pass to NdisTransferData. This call passes the packet descriptor to the underlying NIC driver to be filled if there is additional data received beyond the indicated lookahead data.

If a protocol driver binds itself only to underlying NDIS drivers that indicate arrays of packet(s) with NdisMIndicateReceivePackets, ProtocolReceivePacket need not provide packet descriptors, buffers, and buffer descriptors for incoming data. When the protocol driver has a full network packet indicated to its ProtocolReceivePacket function, it can give TDI client(s) direct read-only access to the buffered data described by an input packet descriptor until the data is consumed and the client(s) release the packet descriptor and all the resources it specifies by calling TdiReturnChainedReceives. When the protocol driver takes ownership of such packet resources, it has no need to copy data into a preallocated packet and call NdisTransferData to obtain the remaining packet data.

If a protocol driver supplies a packet descriptor with more than one chained buffer to NdisTransferData or NdisSend(Packets) and the length of actual data in the last buffer is less than the allocated length of its buffer, the protocol driver should call NdisAdjustBufferLength to set the actual length of the data in the buffer descriptor. When the packet descriptor is returned to the protocol driver, the driver should readjust the buffer descriptor’s specified length to the full length of the buffer.

Reusing Packets

Ownership of protocol-allocated packet resources for sends reverts to the protocol driver when NdisSend returns anything other than NDIS_STATUS_PENDING or when the driver's ProtocolSendComplete function is called. Then, the protocol driver can reclaim the returned packet resources for subsequent sends or for copying received data in ProtocolReceive.

It is more efficient for a protocol driver to reinitialize and reuse the packet descriptor and any chained buffer descriptors and buffers than to deallocate these resources and, then, later reallocate them again for a subsequent send or data-transfer operation. A protocol usually exhibits better performance if it saves unchained buffer descriptors and buffers for reuse, rather than deallocating and reallocating such resources.

A protocol driver reinitializes a packet descriptor by calling NdisReinitializePacket. First, the protocol driver should take care to remove any chained buffers and their buffer descriptors by calling NdisUnchainBufferAt Xxx to release the buffer descriptors and the buffers mapped by these descriptors. Otherwise, NdisReinitializePacket sets the member that points to the chained buffer descriptor(s) to NULL, so reinitializing the packet descriptor without first releasing and saving the chained buffer descriptors will cause a memory leak. Similarly, if the protocol and underlying driver use out-of-band information, the resources specified in the OOB data block associated with each packet descriptor must be reclaimed before a call to NdisReinitializePacket.

A protocol driver can choose to individually reinitialize specific driver-accessible members of a packet descriptor if only a subset are read and will be written subsequently, rather than calling NdisReinitializePacket.


2.3 Dynamic Binding in a Protocol Driver

A PnP-ready protocol driver can support dynamic binding to underlying NICs by providing both a ProtocolBindAdapter function and a ProtocolUnbindAdapter function.

If a driver registers these functions with NDIS, it indicates that it will defer opening and binding to an underlying NIC as is otherwise done in the DriverEntry function and, instead, perform these actions in its ProtocolBindAdapter function. If a protocol provides these dynamic-bind functions, whenever an underlying NIC becomes available, NDIS will call the ProtocolBindAdapter function of any protocol driver that can bind itself to that adapter, and whenever an underlying NIC is closed, NDIS will call the reciprocal ProtocolUnbindAdapter function.

ProtocolBindAdapter is declared as follows:

VOID
ProtocolBindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
);

NDIS supplies the name of the newly available adapter for ProtocolBindAdapter to open at DeviceName. NDIS passes a handle at BindContext that represents its context for the bind request. The protocol driver must retain this handle and pass it back to NDIS as a parameter to NdisCompleteBindAdapter when the driver has completed its binding-related activities and is ready to transmit and receive packets.

Binding-time actions include allocating and initializing an adapter-specific context area for the binding, and calling NdisOpenAdapter to open the adapter passed at DeviceName. DeviceName might refer to an underlying NIC or it can be the name of a virtual NIC exported by an NDIS intermediate driver that is layered between the protocol driver and the NIC driver managing the adapter to which transmit requests are directed. For instance, an intermediate NDIS driver can translate between the format of the medium supported by a legacy protocol driver and the format of the medium supported by an underlying NIC driver. The protocol-allocated context area or a comparable location should be used to store the BindContext.

The BindContext value must be stored because NdisOpenAdapter can return NDIS_STATUS_PENDING. If NdisOpenAdapter returns a pending status, the protocol driver cannot call NdisCompleteBindAdapter until the open operation is complete and the protocol driver is called at its ProtocolOpenAdapterComplete function. In this case, BindContext must be retrieved from a known location and passed to NdisCompleteBindAdapter.

SystemSpecific1 is a string that must be passed to NdisOpenProtocolConfiguration. This string is opaque to protocol drivers.

SystemSpecific2 is reserved for system use.

For more information about dynamic unbinding, see Section 2.7.


2.4 Receiving Data in a Protocol Driver

Underlying NIC drivers can indicate packets in two ways:

  • A NIC driver calls NdisMIndicateReceivePacket, passing a pointer to an array of packet descriptor pointer(s) to one or more full packets and relinquishing ownership of the resources for these packets to the overlying drivers, which can consume the data and return the packet resources at a later time.

  • A NIC driver calls a filter-specific NdisM Xxx IndicateReceive function, passing a pointer to a lookahead buffer, the size of the lookhead buffer, and the total size of the received net packet.

Every protocol driver must have at least one of two possible receive handlers and can have both of the following:

  1. ProtocolReceive is a required function that is called with a pointer to a lookahead buffer.

    After ProtocolReceive examines the lookahead data and determines that the packet is intended for one or more of its clients, it must copy the lookahead data into a protocol-allocated buffer, possibly to be chained to a protocol-allocated packet descriptor. If the size of the lookahead buffer is less than the total size of the received packet, ProtocolReceive must call NdisTransferData with the protocol-allocated packet descriptor to obtain the rest of the received data, which the underlying driver copies into the protocol-supplied buffer.

  2. ProtocolReceivePacket is an optional function that receives a pointer to packet descriptor specifying a buffered full network packet.

    ProtocolReceivePacket also examines the packet data and determines whether the packet is intended for one or more of its clients. If so, the protocol can give its TDI client(s) ownership of the indicated packet resources, including direct read-only access to the buffered net packet data, by returning a nonzero value (a count of clients to which the protocol forwarded the receive indication) from ProtocolReceivePacket. The protocol driver's client(s) must subsequently return the packet descriptor and all the resources it specifies to the underlying driver by calling TdiReturnChainedReceives with a pointer to the packet descriptor. Each client must make this call with the packet descriptor until all clients' calls total the nonzero value returned from ProtocolReceivePacket for that receive indication.

    When the protocol driver's client(s) call TdiReturnChainedReceives the required number of times, they relinquish ownership of the returned packet resources to the underlying NIC driver that originally indicated the receive.

    On the other hand, if a protocol driver returns zero from ProtocolReceivePacket, it relinquishes ownership of the packet resources when ProtocolReceivePacket returns. Relinquishing such a received packet immediately could occur, for instance, if the client for the packet has closed the connection or in some manner become unavailable, or if the protocol copies the indicated data into buffers of its own and processes the data internally before indicating up to its clients.

Implementing a ProtocolReceivePacket Handler

When an underlying NIC driver indicates an array of one or more packet descriptors with NdisMIndicateReceivePacket, NDIS will usually call a bound protocol driver’s ProtocolReceivePacket function with each packet descriptor, thereby allowing the protocol driver (or its TDI clients) to retain the packet descriptor and all the resources it specifies until the protocol or its client(s) have consumed the data and returned the packet descriptor. Two kinds of NIC drivers typically call NdisMIndicateReceivePacket with an array of packet descriptors:

  1. A NIC driver managing a busmaster DMA adapter that is capable of receiving several packets at a time into a ring of buffers

  2. A NIC driver that provides out-of-band data containing media-specific information, such as packet priority, in the NDIS_PACKET_OOB_DATA block associated with the packet descriptor

    Such a driver need not be the driver of a busmaster DMA device.

If a protocol driver is aware that it is (or might be) bound to such a NIC driver, it should have a ProtocolReceivePacket function. This allows the protocol driver to do all of the following:

  • To receive indications of full network packets from the underlying NIC driver

  • To read the OOB data associated with each packet descriptor using NDIS-supplied macros

  • To retain ownership of the input packet descriptor and direct read-only access to the buffered data specified by the descriptor if the protocol makes copies of the data for its clients, or even to forward the indicated packet descriptor to its client(s) after selecting the range of packet data of interest to the client(s)

  • To return the packet descriptor and the resources it described, possibly along with other retained packet descriptors, with NdisReturnPackets when the protocol has consumed the indicated data if it retains ownership of the input packet descriptor; otherwise, to have clients to which the protocol forwarded a receive indication return the packet descriptor with calls to TdiReturnChainedReceives

Even when a protocol driver provides a ProtocolReceivePacket handler, there are cases when a call by a NIC driver to NdisMIndicateReceivePacket results in a call to the protocol driver’s ProtocolReceive function. Since a NIC driver temporarily relinquishes ownership of driver-allocated resources when it calls NdisMIndicateReceivePacket, the underlying driver depends on the consumers of those packets to return them in a timely manner. Otherwise, the NIC driver can run short of receive resources, such as receive buffer space in the NIC. When it does, the NIC driver sets NDIS_STATUS_RESOURCES in the OOB block associated with a packet descriptor in the packet array it passes to NdisMIndicateReceivePacket. Indicating a packet with this status causes NDIS to call the overlying driver's ProtocolReceive function with such a packet and with any subsequent packets in the array, thus forcing the protocol driver to copy the packet data immediately, rather than retaining the NIC-driver-allocated packet resources.

If the protocol driver requires the OOB data associated with a packet descriptor but is called at ProtocolReceive, it must call NdisQueryReceiveInformation to copy the media-specific information into a protocol-supplied buffer and possibly the TimeSent and TimeReceived values if the underlying NIC driver provides these timestamps.

Implementing a ProtocolReceive Handler

If the underlying NIC driver calls a filter-specific NdisM Xxx IndicateReceive function, NDIS always calls the ProtocolReceive function of each bound protocol driver. If one or more of such a protocol driver's clients are the target of the packet and the lookahead buffer size is less than the total packet size, ProtocolReceive must do the following:

  1. Copy the lookahead data into an internal buffer mapped by a protocol-allocated packet descriptor.

  2. Chain buffer descriptor(s) mapping sufficient protocol-allocated buffers to contain the rest of the network packet data to a protocol-allocated packet descriptor.

  3. Call NdisTransferData with the packet descriptor, so the underlying driver copies the rest of the received data into the protocol's buffer(s).

When NdisTransferData returns STATUS_SUCCESS or the ProtocolTransferDataComplete function is called, the lookahead buffer can be chained to the packet descriptor containing this transferred data and indicated up to any interested client(s).

NdisTransferData can only be called once for each receive indication. It is the responsibility of the protocol driver to set up its packet descriptor with chained buffers of a sufficient size to contain the full network packet. After NdisTransferData returns, the received data is no longer available from the underlying NIC driver.

The size of the lookahead buffer passed to ProtocolReceive is the minimum of the size returned by a call to NdisRequest with OID_GEN_CURRENT_LOOKAHEAD and the total packet size. All data in the lookahead buffer is read-only to the ProtocolReceive.

If the call to ProtocolReceive occurred because the underlying NIC driver set the status for one or more packets in a packet array to NDIS_STATUS_RESOURCES before calling NdisMIndicateReceivePacket, the size of the lookahead buffer will always be equal to the size of the full network packet. In these circumstances, the protocol driver never calls NdisTransferData because ProtocolReceive can copy the full indication into an internal buffer immediately.

If the NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA bit is set in response to an OID_GEN_MAC_OPTIONS request, a protocol driver can use any means to copy lookahead data into an internal buffer, such as calling NdisMoveMemory. If this flag was not set, the protocol driver must call TdiCopyLookaheadData to copy the indicated data; otherwise, the results from a copy operation are indeterminate.

ProtocolReceive must execute as quickly as possible. The protocol driver must insure that it has protocol-allocated packet resources available before it receives incoming indications. After the protocol driver examines the packet and determines that the packet is not one it will copy, ProtocolReceive should simply return NDIS_STATUS_NOT_ACCEPTED.

ProtocolReceive must not process the received data as soon as it is copied since that would adversely impact system performance, as well as possibly causing dropped receives in the underlying NIC driver. Instead, the protocol driver processes the received data later in its ProtocolReceiveComplete function. Following one or more receive indications, ProtocolReceiveComplete is called when the underlying NIC driver calls NdisM Xxx IndicateReceiveComplete. Typically, this occurs when the underlying NIC driver has received and indicated a miniport-determined number of packets or before it exits its DPC-level receive handler. The protocol driver must queue the received data in ProtocolReceive so that it is available to ProtocolReceiveComplete for postprocessing.

Accessing OOB Information

When a received network packet is indicated to ProtocolReceive, it forces the driver to copy the received data into a protocol-supplied buffer. If such an indication has associated media-specific and/or timestamp information in the OOB block associated with the packet descriptor, a protocol driver can call NdisQueryReceiveInformation to copy the media-specific information, as well as any TimeSent and TimeReceived timestamps.

If a received packet is passed to ProtocolReceivePacket, the protocol driver can copy the information from the associated OOB block using NDIS-supplied macros as follows:

  • Media-specific information is copied using NDIS_GET_MEDIA_SPECIFIC_INFO.

  • TimeSent is copied using NDIS_GET_TIME_SENT.

  • TimeReceived is copied using NDIS_GET_TIME_RECEIVED.

TimeSent is the time a packet was sent by the NIC driver on the remote node, and is retrieved and stored by the underlying NIC driver on the local node if available. TimeReceived is the time that the incoming packet was received on the underlying NIC on the local node.

The current system time can be determined using NdisGetCurrentSystemTime or KeQuerySystemTime if the protocol converts these timestamps into another format.


2.5 Sending Protocol Driver-Originated Packets

A protocol driver can transmit a single packet by calling NdisSend, passing in a pointer to a packet descriptor with chained buffer descriptors mapping the buffered data to be sent. Alternatively, a protocol driver can transmit several packets using NdisSendPackets, passing in a pointer to an array of pointer(s) to one or more packet descriptors.

In general, a protocol driver developer should choose whether to call NdisSend or NdisSendPackets based upon the driver’s own requirements and on the characteristics of the underlying NIC driver.

When it has bound itself to an underlying NIC driver, any protocol driver should call NdisRequest with an OID_GEN_MAXIMUM_SEND_PACKETS query to determine the maximum number of send packets that the underlying driver will accept in a packet array. If the NIC driver supports only single-packet sends, either at its MiniportSend or its MiniportSendPackets function, the return value will be one or, possibly NDIS_STATUS_NOT_SUPPORTED. Either of these returns implies the protocol driver is likely to call NdisSend rather than NdisSendPackets. If the underlying driver returns a value greater than one, both drivers' performance will be better if the protocol driver sends an array of packets with NdisSendPackets. If OOB information is passed between the protocol driver and the NIC driver, either NDIS function can be called, since, in either case, the underlying driver can read the OOB data using NDIS-supplied macros

Whenever a protocol driver calls NdisSend, it relinquishes ownership of the given packet resources until the send completes, either synchronously or asynchronously. If the status returned by NdisSend is something other than NDIS_STATUS_PENDING, the send completes synchronously and ownership of the protocol-allocated packet resources reverts to the protocol driver. If the status returned by NdisSend is NDIS_STATUS_PENDING, when the send subsequently completes, the final status of the send and the protocol-allocated packet descriptor will be passed in to the ProtocolSendComplete function.

When a protocol driver transmits one or more packet(s) by calling NdisSendPackets, send operations are always asynchronous. The protocol driver relinquishes ownership of the packet resources that it allocated until each packet descriptor and the final status of the send for that packet is returned to ProtocolSendComplete.

As a consequence, a protocol driver never reads the Status member in the OOB block associated with a packet descriptor on return from NdisSend(Packets). The protocol cannot learn the status of its send request in this manner because this member is in use by NDIS to track the progress of an in-transition send request and is volatile until the packet descriptor is returned to ProtocolSendComplete. A protocol driver always obtains the status of a transmit request either by examining the value returned by NdisSend or from the Status parameter passed to ProtocolSendComplete.

If a protocol driver requests the transmission of an array of packets of different priorities by arranging the packets it receives from clients before transmitting them, the protocol should place the highest priority packets at the beginning of the array. NDIS always preserves the ordering of packets in any array passed to NdisSendPackets, even if NDIS queues some of the packets internally.

NDIS does not attempt to examine and make queueing decisions based on any the OOB data associated with the packet descriptors given to NdisSendPackets (or to NdisSend). Unless a protocol driver has special knowledge of the manner in which the underlying NIC driver handles packet priorities or the TimeToSend timestamps, the protocol should assume that the underlying NIC driver transmits all packets in the order in which it receives them, preserving the as-received order. Consequently, a protocol should order the packet arrays it sends according to the order in which those packets should be transmitted over the network.

Passing Media-Specific Information

Before sending a packet, a protocol driver can call NdisSetPacketFlags to set protocol-defined flags in the NDIS-private portion of the packet descriptor. Such flags are not defined by NDIS, but are defined for use by a cooperating pair of protocol and lower level NDIS drivers. The structure of the Private member of an NDIS_PACKET is opaque to all NDIS drivers and is accessed to read and, in some cases, to write using NDIS-supplied functions or macros.

More media-specific information can be passed by a protocol driver in the OOB block associated with each NDIS_PACKET-type descriptor. The definition of the OOB block is:

typedef struct _NDIS_PACKET_OOB_DATA {
union {
ULONGLONG TimeToSend;
ULONGLONG TimeSent;
};
ULONGLONG TimeReceived;
UINT HeaderSize;
UINT SizeMediaSpecificInfo;
 PVOID MediaSpecificInformation;
NDIS_STATUS Status;
} NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA;

The structure of individual records within a driver-allocated buffer at MediaSpecificInformation is defined as follows:

typedef struct MediaSpecificInformation {
UINT NextEntryOffset;
 NDIS_CLASS_ID ClassId;
 UINT Size;
 UCHAR ClassInformation[1];
} MEDIA_SPECIFIC_INFORMATION;

The ClassId member is an NDIS-defined enumeration that defines the type of information found at ClassInformation[1]. Currently, there are two class IDs in use across Microsoft operating systems that support Win32, NdisClass802_3Priority and NdisClassWirelessWanMbxMailbox. See the NetworkReference for details.

If the protocol driver knows that the underlying NIC driver to which it is sending packets uses OOB data, the protocol can set the following OOB structure members:

  • Request that the packet be sent at a specific time by setting the TimeToSend member using the NDIS_SET_PACKET_TIME_TO_SEND macro. This macro passes the requested time in system time units. The protocol can call NdisGetCurrentSystemTime in order to obtain the current system time with which to calculate a requested send time.

  • Pass media-specific information in a protocol-allocated buffer at MediaSpecificInformation using the NDIS_PACKET_SET_MEDIA_SPECIFIC_INFO macro to set this member to the address of the buffer. For instance, if a protocol driver is bound to an underlying NIC that requires priority, it will set the ClassId member of the MediaSpecificInformation structure to NdisClass802_3Priority, and pass priority-related information in the ClassInformation member and the size in bytes of this information in Size. The protocol driver is responsible for allocating a buffer to contain any media-specific data record(s) and for setting up the pointer to this buffer at MediaSpecificInformation.


2.6 Protocol Driver Reset Operations

A protocol driver can initiate a reset operation by calling NdisReset. If its reset request returns NDIS_STATUS_PENDING, ProtocolResetComplete is called when the reset operation is complete.

A protocol driver seldom calls NdisReset unless it has special knowledge that the underlying NIC is not functioning correctly. For instance, if a protocol driver detects that it has not received completion calls for an unusually large number of sends or requests, and it has enough knowledge of the underlying NIC to conclude that this indicates hardware problems, it can call NdisReset. Typically, however, the need for a reset is detected and initiated by NDIS and/or the NIC driver itself using time-out logic. Any protocol driver bound to an underlying NDIS driver that reports its medium type as NdisMediumWan cannot call NdisReset.

Typically, an underlying NIC driver resets its NIC because the NIC is timing out during send or request operations, which causes NDIS to call the NIC driver’s MiniportCheckForHang and subsequently MiniportReset function, or because the NIC driver determines the NIC's receive capability is disfunctional. If a reset is initiated by NDIS and MiniportReset returns NDIS_STATUS_PENDING, NDIS first calls the ProtocolStatus function of each bound protocol driver with a status of NDIS_STATUS_RESET_START, and then calls the same bound protocol driver’s ProtocolStatusComplete function. When the NIC driver calls NdisResetComplete, NDIS again calls ProtocolStatus with a status of NDIS_STATUS_RESET_END followed by a call to ProtocolStatusComplete.

A protocol driver must handle the possibility that outstanding sends on a binding to an underlying NIC can be cancelled because the NIC is reset. If a bound protocol driver has any transmit packets pending, NDIS will complete those packets back to the protocol driver with an appropriate status. The protocol driver must resubmit the packets again when the reset operation is completed, assuming the NIC becomes operational again.

When a protocol driver receives a status of NDIS_STATUS_RESET_START, it should do the following:

  • Hold any packets ready to be transmitted until ProtocolStatus receives an NDIS_STATUS_RESET_END notification.

  • Not make any NDIS calls directed to the underlying miniport, except calls to return resources such as returning packets with NdisReturnPackets.

After ProtocolStatus receives an NDIS_STATUS_RESET_END message and ProtocolStatusComplete is called, the protocol driver can resume sending packets and requests.


2.7 Protocol Driver Unbinding Operations

A protocol driver unbinds from an underlying NIC by calling NdisCloseAdapter. If a protocol driver registers a ProtocolUnbindAdapter function, NDIS will call this function if the underlying NIC is no longer available. For instance, if the NIC timed out, an attempt to reset the NIC failed to make it operational, and NDIS called the MiniportHalt function of the NIC driver for the malfunctioning NIC, NDIS will subsequently call the overlying protocol driver's ProtocolUnbindAdapter function.

If a protocol driver does not register this function, an unbind operation can originate in the driver's ProtocolStatus function, as the result of receiving a status of NDIS_STATUS_CLOSING at GeneralStatus. A protocol driver also should unbind from an adapter during initialization if some action subsequent to opening an adapter, such as allocating a required resource, fails, and the driver needs to unbind because it cannot carry out network operations on the binding.

If a protocol driver registers a ProtocolUnbindAdapter, this function calls NdisCloseAdapter. However, the protocol must not deallocate all its binding-specific resources until it calls NdisCompleteUnbindAdapter. If the protocol driver does not provide a ProtocolUnbindAdapter function, but calls NdisCloseAdapter, it can deallocate any previously allocated resources when either NdisCloseAdapter returns NDIS_STATUS_SUCCESS, or, if NdisCloseAdapter returns NDIS_STATUS_PENDING, when ProtocolCloseAdapterComplete is called.

NDIS delays calling ProtocolCloseAdapterComplete until all outstanding requests on the binding have completed. Once the close request completes, the ProtocolBindingContext handle the protocol driver allocated to represent the binding becomes invalid.


2.8 Status Indications in a Protocol Driver

A protocol driver is required to supply both ProtocolStatus and ProtocolStatusComplete functions. NDIS calls ProtocolStatus when an underlying NDIS driver calls NdisMIndicateStatus to report a change in its NIC. ProtocolStatus is called when the status change begins. If the action indicated by the status message is not complete when ProtocolStatus is called, NdisMIndicateStatusComplete is subsequently called by the underlying NIC driver. When this occurs, ProtocolStatusComplete is called to carry out any postprocessing indicated by the status change. Examples of GeneralStatus reported to ProtocolStatus include:

  • NDIS_STATUS_CLOSING ¾ This status and the actions of ProtocolStatus were already discussed in Section 2.7.

  • NDIS_STATUS_RESET_START and NDIS_STATUS_RESET_END ¾ In this case, both of these statuses are reported to both ProtocolStatus and ProtocolStatusComplete as explained already in Section 2.6.

  • NDIS_STATUS_LINE_UP ¾ This status is indicated if the protocol driver is layered over NDISWAN while an underlying WAN-capable NIC driver has an established connection with a remote node. The protocol driver exchanges handles for an active connection on which it can send and receive as well as query the connection for call-specific information. See Part 2, Chapter 5 and Chapter 6, for more information on WAN drivers.

  • NDIS_STATUS_RING_STATUS or any other medium-specific status value, for which StatusBuffer provides more detailed information about the status, for instance, problems specific to a Token Ring medium.


Chapter 3 TDI Transports and Their Clients

This describes, in general, the relationship between Windows NT TDI transport drivers and TDI kernel-mode clients, such as higher level network-interface emulators, redirectors, and servers, that communicate with any transport protocol stack using the system-defined TDI interface. That is, this provides an overview of the following:

  • The Transport Driver Interface (TDI) in Section 3.1

  • How TDI uses file objects to represent network entities in Section 3.2, including the following three basic TDI entities:

    1. Transport Addresses in Section 3.2.1

    2. Connection Endpoints in Section 3.2.2

    3. Control Channels in Section 3.2.3

  • The set of transport driver Dispatch routines required by TDI in Section 3.3

  • The interactions between TDI clients and their underlying transports in Section 3.4

  • The distinction between TDI requests and events in Section 3.5


3.1 The Transport Driver Interface (TDI)

Figure 3.1 shows the general relationship between TDI clients and transport drivers.

Figure 3.1 TDI Clients and Transport Providers

As this figure shows, the Transport Driver Interface (TDI) defines a kernel-mode network interface that is exposed at the upper edge of all Windows NT transport protocol stacks. The highest level protocol driver in every such stack supports the TDI interface for still higher level kernel-mode network clients.

This interface includes the following:

  • A set of standard Windows NT kernel-mode intermediate driver Dispatch routines exported by each TDI transport driver to which clients can submit I/O requests (IRPs) by making calls to support routines, such as the Zw..File routines and/or IoCallDriver

  • A set of callback routines exported by each TDI client that can be registered with the underlying transport driver to receive notifications of specific network events when they occur

  • Parameters, structures, IOCTLs, and procedural rules associated with these TDI transport and client routines

  • A set of system-supplied Tdi Xxx functions that transports and clients can call to aide in communication with each other

  • A set of system-supplied TdiBuild Xxx macros and functions that clients can use to set up requests to be submitted to their underlying transports

Requiring that all Windows NT transport drivers expose a single common interface (TDI) simplifies the task of transport driver development in that all transports need code to support only a single defined interface. It also simplies the task of client development by minimizing the amount of transport-specific code that must be written.

Windows NT includes interface modules for several popular network interfaces, such as NetBIOS and Windows Sockets. Each of these interface modules exposes a native set of primitive functions, which are accessible through standard calls from user mode. When called, the interface module maps the native (frequently user-mode) function and its associated parameters and procedural rules, to one or more calls to the underlying TDI transport driver.

Key features of TDI include the following:

High Level of Granularity
TDI accommodates all primitives and conventions from existing popular network interfaces because it is relatively granular in nature, with several small TDI-defined requests that can be mixed and matched to accommodate mapping from existing network-interface functions.
Asynchronous Operation
Most kernel-mode TDI operations are asynchronous, using client-supplied callback routines to indicate asynchronous network events as they occur and completing most client-initiated operations submitted as IRPs asynchronously as well.
32-bit Addressing and Values
Like all Windows NT kernel-mode components, TDI transports and their clients are 32-bit code. All TDI-defined structures and parameters use 32-bit pointers and values.
Flexible Addressing Scheme
TDI does not mandate any particular address format, such as the 16-character NetBIOS name defined for legacy operating systems. Instead, it features an extensible mechanism by which many address formats can be identified and used.
Extensible Communication
TDI defines a TDI_ACTION IOCTL request that any TDI transport can use to support a set of transport-determined operations initiated by requests from its clients. This allows the client to submit transport-specific requests to the underlying transport driver that are not expressly defined by TDI.
Event Notification
TDI defines a scheme by which transports can notify their clients of interesting events that occur on the network without requiring the client to submit an explicit I/O request. For certain types of events, such as connects, disconnects, and receives, the transport driver can indicate data along with the event notification when it calls its client with such a notification.

The following features can be supported by a TDI transport driver as additional options:

Data Transfer Modes
TDI supports sending and receiving data as discrete messages (message mode) or as a stream of bytes (byte-stream mode). Support for either mode (or both) depends on the transport driver writer and/or on the nature of the protocol.
Internal Buffering
TDI transports can buffer their client's sends and receives internally. Transport-internal buffering allows the TDI client to set and query driver internal buffer sizes, request nonblocking send operations, receive notification of available buffer space, and look at (peek) buffered data before receiving it.
Management Options
All transports maintain some TDI-defined state about their respective features, limits, and runtime statistics. This allows each client to dynamically query and, in some cases to set, transport-provider static information, statistics, and configuration parameters. In addition, the extensible TDI_ACTION IOCTL, mentioned previously, allows any TDI transport driver to implement unique network-management features that can be accessed by its clients through action requests to the transport.
Quality of Service (QOS)
TDI transports can provide QOS negotiation upon establishment of a network connection. In addition, such a driver can support QOS for connectionless datagram transmission. To support either, TDI-defined connection-establishment and datagram-send requests include Options and OptionsLength parameters that allow a TDI client to include a transport-specific, variable-length counted string specifying QOS options.

In fact, the Options and OptionsLength parameters can be used to pass any transport-specific connection- or datagram-related options to the transport driver at the discretion of the driver writer, not just QOS specifications.

Expedited Delivery
When sending messages, the client can flag particular messages as expedited. At the sending side, the underlying transport sends these messages ahead of non-expedited messages. At the receiving side, expedited messages are indicated to the client before and/or separate from non-expedited messages.
Chained Receive Indications
If the underlying NDIS drivers support multipacket receive indications, a TDI transport can give its clients direct read-only access to a full TSDU in a single call, and the client can retain control of the buffer containing the TSDU until it has consumed the indicated data. This feature improves the performance of both transport and client by cutting down on call overhead for receive indications.


3.2 TDI File Objects

All Windows NT I/O requests are directed to an open file object, which can represent a physical or virtual device, a data file, or any logical target of I/O requests, that can be opened by a particular process. Open file objects are process-specific. For example, the I/O Manager creates a file object for each process that opens a particular data file concurrently.

TDI uses file objects to represent entities that exist in any network communication environment. In particular, TDI uses file objects to represent the following types of TDI-defined entities:

  • To represent transport addresses opened by specific processes or groups of processes on a network node

  • To represent connection endpoints identifying specific endpoints associated with particular transport addresses, opened to set up endpoint-to-endpoint connections with remote-node peer processes

  • To represent control channels identifying transport providers, opened for the purpose of setting and querying global configuration, features, limits, and/or statistics information maintained by the underlying transport

A TDI transport driver and the underlying NDIS drivers provide a mechanism by which data can be transferred from a process on one network node to one or more processes that reside either on the same node or other network nodes. The preceding address and connection endpoint entities represented by open file objects are the means by which such transfers occur.

For example, the Windows NT redirector process opens two file objects, one representing a transport address and the other a connection endpoint associated with that address, to send SMB (Server Message Block) messages to a Windows NT server process on a remote node and to receive messages back from the server process on the remote node.


3.2.1 File Objects Represent Transport Addresses

TDI supports both unreliable connectionless and reliable connection-oriented data transfer.

Unreliable connectionless data can be sent to one or a group of processes that have opened a particular transport address on a remote node. When sending unreliable connectionless data, as datagrams, the sender need only identify the target remote-node address for the process or group of processes for them to receive each such datagram.

Reliable connection-oriented data transfers can be sent between two processes if an endpoint-to-endpoint connection (also called a virtual circuit) has been established between them. An endpoint-to-endpoint connection is a one-to-one association between two, and only two, processes. To establish such a connection, one process must identify the process with which it wants to establish the connection. Each such process must have opened a transport address and a connection endpoint on its respective network node and associated its connection endpoint with the open transport address. For more information about connection endpoints, see Section 3.2.2.

The TDI entity used to identify a specific process, or a specific group of processes is one or more open process-specific file object(s) that represent a particular transport address. Such a file object contains transport-supplied pointers to driver-maintained state that identifies the specific process and the node on which that process resides. The state a "routable" transport, such as TCP/IP, Mcsxns, AppleTalk and NWLink, maintains about such an address also contains information that identifies the network (or subnet) on which the node resides.

Certain TDI-defined transport address types accommodate explicit or implicit indications of whether the address identifies a single process (unique address), or can identify a group of processes (group address). In the case of a group address, the TDI-defined address can contain information that identifies the specific processes, that is, the node on which the process resides, and the network (or subnet) on which the node resides.

A number of address types are supported by TDI. The following describes the format and usage of three commonly-used TDI address types:

  • TDI_ADDRESS_NETBIOS - The NetBIOS-type address consists of a standard 16-character NetBIOS name, and a member that indicates if the name is registered (or to be registered) as a unique name (indicating that the name identifies a single process), or as a group name (indicating that the name can identify a group of processes).

    Since the transport driver assures that a unique name is in use by only one process on the network at a time, such a name not only identifies the process, it also implicitly identifies the node on which that process resides.

    If the registered name is a group name, it can be an address available to many processes on many stations. Thus a group name identifies all processes that registered the name as well as the nodes on which those processes reside.

  • TDI_ADDRESS_IP - The IP-type address consists of a port number and a standard Internet Protocol (IP) address. Since TCP/IP allows the same port number to be registered by processes on many nodes, the IP address is required to uniquely identify the node and the port number to uniquely identify the process on that node.

    For connectionless data transfer (using the TCP/IP UDP protocol), the same port number can be registered by many processes on the same station. In addition, certain IP addresses can be used by more than one node. Data sent to a TDI address that consists of this type of port number and IP address will be received by all nodes to which the IP address applies; on those nodes, the data will be passed to all processes that have registered the specified (UDP) port number.

  • TDI_ADDRESS_IPX - The IPX-type address consists of a four-byte network number, a six-byte node number, and a two-byte port number. Since IPX allows the same port number to be registered by processes on many nodes, an IPX address is required to uniquely identify the network and the node, while the port number can be used to uniquely identify the process on that node.


3.2.2 File Objects Represent Connection Endpoints

A file object that represents an open connection endpoint identifies a specific connection on which local and remote-node peer processes are communicating with each other across the network or will communicate with each other when the local-node process has established an endpoint-to-endpoint connection with a remote-node peer.

Any such file object must be associated with another open file object that represents a particular transport address. The transport address identifies the process, as already described in Section 3.2.1.

In a similar manner, the transport uses the file object representing the local connection endpoint to maintain state about the remote-node peer process, such as the remote-node transport address, with which its local-node client establishes an endpoint-to-endpoint connection.

A client process can establish many endpoint-to-endpoint connections between itself and other processes on remote nodes. For example, the Windows NT redirector establishes separate connections between itself and each remote Windows NT Server with which it communicates. Such a client can have several open connection endpoints all separately associated with an open transport address.

When a process wants to send data over an endpoint-to-endpoint connection, it must have a means of identifying which of all existing connections the data is to be sent over. Each open file object that represents a connection endpoint is used to discriminate among established endpoint-to-endpoint connections of the same process.


3.2.3 File Objects Represent Control Channels

A network management process can query or set global configuration or statistical information with respect to a specific transport provider. To do so, such a process must have a means of identifying a particular transport among several possible transport providers.

Such a client opens a file object that represents a control channel so that it can query the appropriate transport. In effect, the client opens a file object that represents the device object created by the underlying transport driver when the client opens a control channel, either by calling ZwCreateFile or IoGetDeviceObjectPointer.

Other TDI clients that send and receive data across the network also can open a control channel to query their underlying transports. For example, such a client might issue a query to determine the transport's limit on datagram size so the client could effectively size the buffers that it will allocate to send and receive datagrams subsequently.

In general, a TDI transport maintains global state information about its features and current statistics, rather than process-specific state information tied to a particular open file object. For example, a client that queries the current state of its open transport address passes a pointer to a client-specific file object representing that address, but the transport returns information about the number of clients that currently have the same address open.


3.3 TDI Transport Driver Routines

Every TDI-compliant transport driver is a standard Windows NT intermediate driver that must export a number of entry points called by the Windows NT I/O Manager.

Some of a TDI transport’s standard driver routines initialize and unload the driver itself. Others are standard Dispatch routines that the I/O Manager calls when TDI clients make calls to system support routines, such as ZwCreateFile and IoCallDriver.

Like other Windows NT kernel-mode drivers, a TDI transport’s DriverEntry routine sets up one or more driver-supplied Dispatch routines to handle various types of I/O requests passed in as IRPs. A TDI driver can export a single Dispatch routine to handle all incoming IRPs or a separate DispatchXxx routine to handle each IRP_MJ_XXX the driver must support. General requirements for Dispatch routines are discussed in detail in the Kernel-mode Driver Design Guide. TDI-specific requirements for Dispatch routines are summarized later in Chapter 4 and described in the Network Driver Reference.

As a TDI transport completes an operation requested by its client, the I/O Manager calls any client-supplied IoCompletion routine that the client set in the IRP before submitting it to the underlying transport.

In addition, such a transport driver must call the TDI client at preregistered entry points within the TDI client’s code when specific network events occur. These client-supplied event handlers also are summarized later in Chapter 4 and described in the Network Driver Reference.

At its lower edge, a TDI driver that is monolithic must export a set of ProtocolXxx functions to be called by the NDIS library on behalf of underlying NDIS intermediate and/or NIC drivers. These NDIS driver lower-edge functions are described in the Network Driver Reference and earlier in this manual.


3.4 TDI Kernel-Mode Client Interactions

The following figure shows how TDI clients make I/O requests to their underlying TDI transports and how transports make callbacks to their clients.

Figure 3.2 TDI Client/Transport Interactions

A TDI client interacts with its underlying transport driver as follows:

  • Creating TDI File Objects - A client calls ZwCreateFile to create or open a file object that represents a transport address, a connection endpoint, or a control channel. This call causes the Windows NT I/O Manager to allocate an IRP, to marshal the client-supplied parameters into the IRP, and to pass the IRP to the underlying transport driver’s TdiDispatchCreate routine. When the transport driver has set up all the state it maintains for the newly created file object, it calls IoCompleteRequest (or TdiCompleteRequest) with the IRP and STATUS_SUCCESS. ZwCreateFile then returns to the TDI client with a handle to the file object.

    Each client process's call to ZwCreateFile creates a separate file object, even if two clients have specified the same transport address in their calls to ZwCreateFile.

    A successful call to ZwCreateFile opens a transport address, a connection endpoint, or a control channel, depending on the EaXxx parameters the client passes in its call.

  • Submitting Requests - After the appropriate file object(s) have been created, the client can submit requests that reference those objects. For example, after it opens a file object that represents a particular transport address, the client can submit an address-information query or “send datagram from this address” request.

    Such a client uses standard I/O system mechanisms and conventions to submit all such requests:

    • The client prepares an IRP with an IRP_MJ_XXX opcode that identifies what operation the client wants the transport driver to perform. The client supplies all appropriate parameters for the given IRP_MJ_XXX, and, optionally, supplies a pointer to its own IoCompletion routine that will be called when the request is completed by the transport.

      The Windows NT DDK includes a set of TdiBuild Xxx macros (in tdikrnl.h) that can be linked into client code and used to prepare IRPs for TDI-defined IOCTL requests, as well as the TdiBuildInternalDeviceControl function to allocate such an IRP.

    • When the IRP has been set up, the client calls IoCallDriver with pointers to the IRP, to the file object that represents the address, connection endpoint, or control channel, and to the transport driver’s device object. The I/O Manager passes the IRP directly to the appropriate TdiDispatch(Xxx) routine of the transport driver.

    • When the transport driver has completed the requested operation, it calls TdiCompleteRequest or IoCompleteRequest. The I/O Manager then calls the client-supplied IoCompletion routine if the client supplied one for the IRP.

  • Handling Event Notifications - If a client has preregistered its entry point(s) for one or more event handlers, the transport driver calls each such routine when the corresponding network event occurs. For example, if the client has registered a ClientEventReceive handler on an address associated with an endpoint-to-endpoint connection, the transport calls this handler when data sent by the remote-node peer process is received on the local node.

  • Deleting TDI Objects - The client calls ZwClose to delete a file object when the address, connection endpoint, or control channel is no longer needed by the client. The close request is forwarded to the transport's TdiDispatchCleanup and, subsequently, TdiDispatchClose routines.

For more information about the kernel-mode support routines mentioned here, see the Kernel-Mode Driver Reference. For detailed information about the Tdi Xxx and ClientEventXxx routines mentioned here, see the Network Driver Reference.


3.5 TDI Requests versus Events

Many low-level network interfaces, such as NetBIOS and Windows Sockets, are primarily one-way. A client can call the underlying transport driver whenever it wants, but the transport driver cannot call the client. The only way such a transport driver can “say anything” to its client is by returning a specific error code on return from a client-initiated request. In the NetBIOS interface, for example, a transport informs its clients of a connection “hangup” by returning pending NCB_Receives (or an NCB_Receive_Any) with a specific error code (0x0A for a session being closed, for example).

TDI provides an event-notification mechanism that allows a TDI transport to call its affected kernel-mode clients whenever a specific network event occurs (receipt of a datagram, for example) if each such client has registered its ClientEventXxx handler with the transport for that type of event. The TDI client-supplied callback then takes appropriate action and returns control to the transport driver.

When a TDI transport driver calls a client's registered ClientEventXxx handler, it can pass a limited amount of data as a parameter of that call. This feature allows the client to receive messages from the transport without having to allocate a buffer.

For example, the Windows NT redirector takes advantage of this TDI feature. Many of the SMB requests that the redirector sends to the server (such as write SMBs) require little more than the standard SMB header to be present in the corresponding SMB response. The length of this header, which includes status indicators, multiplex ID, etc., is quite small, typically less than 100 bytes.

When the underlying TDI transport driver calls the redirector’s registered ClientEventReceive handler with such an SMB response message to a preceding send, the redirector need only view (not necessarily copy) the indicated message, note the SMB response status indicator, and return to the transport driver. In such a transaction, the redirector receives the SMB response message without having to allocate a buffer.


Chapter 4 TDI Routines, Macros, and Callbacks

This summarizes the routines and requests particular to TDI transport drivers. It also summarizes callback routines exported by TDI kernel-mode clients, as well as the macros and TDI functions provided by the system for transports and clients to use.

See the following topics for summaries about each category of information:

  • Transport Drivers

    TDI Driver Initialization

    TDI Driver Dispatch Routines

  • The set of TDI-defined IOCTLs that kernel-mode clients can issue to their underlying transports

    TDI IOCTL Requests

  • The event handlers that clients can register with their underlying transports

    TDI Client Callbacks

  • The set of system-supplied functions and macros that TDI transports and their clients can use

    TDI Library Functions and Macros

For more information about the sequence of interactions between TDI clients and transports when the preceding are used, see Chapter 5.

For detailed information about each request, macro, or routine, see the Network Driver Reference.


4.1 TDI Driver Initialization

Every TDI transport driver must provide an initialization routine explicitly named DriverEntry. In addition, a transport driver should have as many TdiDispatchXxx routines and internal driver functions as it needs to satisfy the requests of its kernel-mode clients. A TDI transport driver or one of the underlying protocol drivers in its transport stack also must provide the NDIS driver lower-edge functions described earlier in -Chapter 2 and in the Network Driver Reference.

Every transport driver must have a DriverEntry routine to be called by the I/O Manager when the transport is loaded. DriverEntry must be declared in the driver code to make the transport automatically loadable.

When it loads the driver, the Windows NT I/O Manger creates a driver object to represent the transport and passes a pointer to the driver object when it calls DriverEntry. The transport’s DriverEntry routine does the following:

  • Read the registry (using kernel-mode support routines) to retrieve configuration information, under the transport's own ..\DriverName\Parameters subkey. Using the registry information, configure itself as needed.

  • Set all the driver's TdiDispatchXxx entry points in the driver object. The driver’s TdiDispatchXxx routines will be called later by the I/O Manager to handle requests from TDI clients.

  • Create at least one named device object for itself with IoCreateDevice. The names of each driver’s device objects are the device names stored under the transport driver’s registry section Linkage key in the Export entry. Each transport driver’s Export entry determines what device object(s) will be created by any particular transport driver.

    Some transport drivers, such as NWLink, create only one named device object no matter how many NICs they bind themselves to. Other transport drivers create a separate named device object for each bound NIC, such as Nbf_Elnkii1, Nbf_Elnkii2, and so forth.

  • Perform any other necessary initialization tasks, such as binding to underlying NDIS intermediate and/or NIC drivers.

Any TDI transport driver must provide an Unload routine unless that driver cannot be unloaded without rendering the system unusable. Most TDI transport drivers declare an Unload routine, and their DriverEntry routines set its entry point in the driver object. For more information about transport Unload routines, see Section 4.1.2.

For more information about the kernel-mode support routines and objects used by TDI drivers, see the Kernel-mode Driver Reference.


4.1.1 Registering a TDI Transport Driver

When TDI is loaded, the configuration registry has entries for each TDI transport driver and kernel-mode client operating on a particular network. Using binding information in the registry, the operating system loads network drivers.

Then, the I/O Manager creates driver objects and calls the DriverEntry routine of each TDI transport driver. The I/O Manager calls each transport driver’s DriverEntry routine until all TDI drivers are loaded and registered on the network.

A TDI transport driver or a lower protocol component in its transport stack must register with the NDIS library by calling NdisRegisterProtocol if it will bind itself to an underlying NDIS NIC driver. When a transport driver is bound to a NIC driver or has layered itself above an NDIS protocol driver that has bound itself to a NIC driver, the TDI driver is ready to respond to any registered TDI client that submits a request to open a transport address or a control channel.

For more detailed information about network binding and driver entries in the registry, see Part 1.


4.1.2 Unloading and Deregistering a TDI Transport Driver

When a NIC is removed from the machine, Windows NT can unload all TDI transport drivers that are either bound directly to the underlying NIC driver or layered over an NDIS protocol driver that is bound to that NIC driver. To accomplish this, the system calls each TDI transport’s Unload routine.

The transport's Unload routine will be called by the I/O Manager when the Service Controller unloads the driver from memory. A transport driver’s Unload routine frees all driver-allocated resources and, if the driver interfaces to NDIS at its lower edge, unbinds itself from the underlying NDIS driver(s).

If the TDI driver exports a set of NDIS-defined ProtocolXxx functions, it must call NdisDeregisterProtocol from its Unload routine.

For more information about standard intermediate driver Unload routines, see the Kernel-Mode Driver Design Guide.


4.2 TDI Driver Dispatch Routines

All TDI requests are handled by the Windows NT I/O Manager. Such requests are formatted as IRPs either by the I/O Manager or explictly by a client and submitted to the transport driver by calling IoCallDriver. Completed IRPs are returned to the caller when the transport driver calls IoCompleteRequest or TdiCompleteRequest. Any kernel-mode TDI client can set its IoCompletion routine an IRP before it calls IoCallDriver.

There are five IRP_MJ_XXX codes used to send I/O requests to TDI transport drivers. These transports handle incoming IRPs in which the MajorFunctionCode is one of the following:

IRP_MJ_CREATE
Opens a transport address, connection endpoint, or control channel in the underlying transport.

This request originates in a client's call to ZwCreateFile.

IRP_MJ_INTERNAL_DEVICE_CONTROL
Specifies kernel-mode client requests (TDI IOCTLs), for which internal transport functions handle operations other than opening and closing file objects.

This request usually originates in a client's call to a TdiBuild Xxx macro followed by its call to IoCallDriver.

IRP_MJ_DEVICE_CONTROL
Specifies user-mode-visible IOCTL requests issued by a transport-dedicated application. Except for any transport-defined "private" IOCTLs, such requests usually are forwarded to the same internal driver functions that handle internal-device control requests.

This request originates in a call by a transport-dedicated user-mode application to DeviceIoControl.

IRP_MJ_CLEANUP
Closes an open address, connection endpoint, or control channel when the Windows NT executive is closing the last handle for the corresponding file object.

This request originates in a client's call ZwClose.

IRP_MJ_CLOSE
Closes an address, connection endpoint, or control channel if the executive is removing its last reference to the file object handle.

This request follows an IRP_MJ_CLEANUP request on the same file object.

The entry points in the TDI driver are one or more Dispatch routines that handle these IRP_MJ_XXX requests. Because a TDI client communicates with the driver only through IRPs, the driver has one or more TdiDispatchXxx routines that determine what operation to carry out and, usually, pass the client requests to appropriate internal driver functions for further processing.

A TDI transport driver exports all its TdiDispatchXxx entry points by setting them in the driver object passed in to its DriverEntry routine. The I/O Manager calls a TdiDispatchXxx routine whenever a client makes an I/O request. A transport driver can have a separate TdiDispatchXxx to handle each of the possible IRP_MJ_XXX opcodes, a single TdiDispatch routine that processes IRPs with all possible IRP_MJ_XXX opcodes, or a number of TdiDispatchXxx that handle discrete subsets of the IRP_MJ_XXX opcodes.

Since all Dispatch entry points are exported by address in the driver object, not by name, a TDI transport driver writer can name these routines anything. In this TDI documentation, TdiDispatchXxx routines have the following metanames, each describing their basic functionality, to correspond with the preceding IRP_MJ_XXX:

IRP_MJ_CREATE
TdiDispatchCreate
IRP_MJ_DEVICE_INTERNAL_CONTROL
TdiDispatchInternalDeviceControl
IRP_MJ_DEVICE_CONTROL
TdiDispatchDeviceControl
IRP_MJ_CLEANUP
TdiDispatchCleanup
IRP_MJ_CLOSE
TdiDispatchClose


4.3 TDI IOCTL Requests

As already mentioned in the preceding section, kernel-mode clients pass TDI IOCTLs in IRPs to the underlying transport driver with calls to IoCallDriver. These requests are passed with a MajorFunctionCode of IRP_MJ_INTERNAL_DEVICE_CONTROL and MinorFunctionCode that specifies one of the following TDI-defined operations:

TDI_ACCEPT
Allows the local-node client to accept an incoming connection offer from a remote-node peer so that it can send and receive connection-oriented data on the network.
TDI_ACTION
Initiates transport-specific extension operations for an address, a connection endpoint, or a control channel.
TDI_ASSOCIATE_ADDRESS
Associates an open connection endpoint with an open local transport address, which is a prerequisite to establishing an endpoint-to-endpoint connection with a remote-node peer process.
TDI_CONNECT
Initiates a connection offer from one endpoint to another on a remote node.
TDI_DISASSOCIATE_ADDRESS
Disassociates a connection endpoint for an inactive connection from its associated address object.
TDI_DISCONNECT
Terminates an established endpoint-to-endpoint connection for the local-node client or acknowledges connection termination initiated by a remote-node peer.
TDI_LISTEN
Passively listens for an incoming connection offer from a remote-node peer process.
TDI_QUERY_INFORMATION
Queries the underlying TDI transport for a specific type of information.
TDI_RECEIVE
Returns all or part of a TSDU received on an endpoint-to-endpoint connection.
TDI_RECEIVE_DATAGRAM
Returns a datagram TSDU on an open transport address.
TDI_SEND
Sends a normal or expedited TSDU to the remote-node peer on an endpoint-to-endpoint connection.
TDI_SEND_DATAGRAM
Sends a datagram to a specified remote-node address.
TDI_SET_EVENT_HANDLER
Registers a client-supplied event handler that the transport will call whenever the corresponding network event occurs.
TDI_SET_INFORMATION
Sets a specific type of information in the underlying transport.


4.4 TDI Client Callbacks

Most TDI clients' routines are highly variable and environment-dependent. However, TDI defines a set of callback routines that clients can register with the underlying TDI transport to receive notifications when network events of interest occur. A kernel-mode TDI client can register any of these ClientEventXxx routines if the TDI driver is to notify it of a particular type of network event occurrence. The client can even use such a registered ClientEventXxx handler as an alternative to issuing certain TDI_XXX IOCTL requests to the driver.

Like the TdiDispatchXxx routines described already, the client-supplied event handlers can have any names the client writer chooses. Each ClientEventXxx mentioned here is registered as an entry point passed in a client-submitted IRP to the TdiDispatchInternalDeviceControl routine.

A TDI client can have one or more of the callbacks listed next, registered at the beginning of network operations on an open transport address with the TDI transport driver by issuing successive TDI_SET_EVENT_HANDLER requests, set up with TdiBuildSetEventHandler:

ClientEventConnect
Notifies the client of a connection offer from a remote-node peer process.
ClientEventDisconnect
Notifies the local-node client that its remote-node peer is terminating the endpoint-to-endpoint connection between them.
ClientEventError
Notifies the client of an error condition in its underlying transport driver, in one of the lower protocol layers of the transport stack, or in a still lower NDIS driver that has made subsequent I/O on the client's open transport address unreliable or impossible.
ClientEventReceive
Notifies the client that its transport has received normal data from the remote-node peer on an endpoint-to-endpoint connection and makes all or part of the received data available to be copied by the client.
ClientEventChainedReceive
Notifies the client that its transport has received a normal TSDU from the remote-node peer on an endpoint-to-endpoint connection and makes the full TSDU available for consumption by the client.
ClientEventReceiveExpedited
Notifies the client that its transport has received expedited data from the remote-node peer on an endpoint-to-endpoint connection and makes all or part of the received data available to be copied by the client.
ClientEventChainedReceiveExpedited
Notifies the client that its transport has received an expedited TSDU from the remote-node peer on an endpoint-to-endpoint connection and makes the full TSDU available for consumption by the client.
ClientEventReceiveDatagram
Notifies the client that the TDI driver has received a datagram directed to a transport address that the client has opened.
ClientEventChainedReceiveDatagram
Notifies the client that the TDI driver has received a datagram directed to a transport address that the client has opened and makes the full TSDU available for consumption by the client.
ClientEventSendPossible
Notifies the client that the transport, which buffers send data internally, again has buffer space available for sends.


4.5 TDI Library Functions and Macros

Windows NT provides a small set of TDI library functions as a kernel-mode dynamic-link library (the tdi.sys export library) with which TDI drivers and kernel-mode clients link themselves. However, most of the system-supplied TdiBuild Xxx called by kernel-mode clients are implemented as macros in the tdikrnl.h header that both clients and transports include.

Any kernel-mode TDI client can use the TdiBuild Xxx macros as needed in preparing the TDI_XXX IOCTL IRPs, already mentioned in Section 4.3. After the client has set up such an IRP with a TdiBuild Xxx macro, it submits the request to the underlying TDI driver by passing the IRP to IoCallDriver. Each of these macros fills in the relevant members of the client-provided IRP, except for the Status and Information members, which the underlying transport fills in after processing its client's request. Before using one of TdiBuild Xxx IOCTL macros, a client can call TdiBuildInternalDeviceControlIrp to allocate the IRP if it has not already been allocated by a still higher level network component and given to the client.

A transport driver never uses the TdiBuild Xxx macros. However, a TDI driver can use some of the remaining TDI library routines, such as TdiCompleteRequest and TdiCopyBufferToMdl, for assistance in processing client requests.

TDI library routines and macros include the following:

TdiBuildInternalDeviceControlIrp
Allocates an IRP if the client does not receive an IRP from a higher network layer.
TdiBuildAccept
Sets up an IRP for a client-submitted TDI_ACCEPT request.
TdiBuildAction
Sets up an IRP for a client-submitted TDI_ACTION request.
TdiBuildAssociateAddress
Sets up an IRP for a client-submitted TDI_ASSOCIATE_ADDRESS request.
TdiBuildConnect
Sets up an IRP for a client-submitted TDI_CONNECT request.
TdiBuildDisassociateAddress
Sets up an IRP for a client-submitted TDI_DISASSOCIATE_ADDRESS request.
TdiBuildListen
Sets up an IRP for a client-submitted TDI_LISTEN request.
TdiBuildQueryInformation
Sets up an IRP for a client-submitted TDI_QUERY_INFORMATION request.
TdiBuildReceive
Sets up an IRP for a client-submitted TDI_RECEIVE request.
TdiBuildReceiveDatagram
Sets up an IRP for a client-submitted TDI_RECEIVE_DATAGRAM request.
TdiBuildSend
Sets up an IRP for a client-submitted TDI_SEND request.
TdiBuildSendDatagram
Sets up an IRP for a client-submitted TDI_SEND_DATAGRAM request.
TdiBuildSetEventHandler
Sets up an IRP for a client-submitted TDI_SET_EVENT_HANDLER request.
TdiBuildSetInformation
Sets up an IRP for a client-submitted TDI_SET_INFORMATION request.
TdiBuildNetbiosAddress
Sets up a NetBIOS address for a client.
TdiBuildNetbiosAddressEa
Sets up a buffered NetBIOS address that a client can pass subsequently to ZwCreateFile to open the address.
TdiReturnChainedReceives
Relinquishes control of the buffer that was passed to a ClientEventChainedReceive(Xxx) handler after the client has consumed the received TSDU.
TdiCopyBufferToMdl
Copies a range of buffered data into a destination buffer mapped by a given MDL.

Both clients and transports can use this function.

TdiCopyMdlToBuffer
Copies data from buffer(s) mapped by a given MDL into a caller-supplied destination buffer.

Both clients and transports can use this function.

TdiCopyLookaheadData
Safely copies received data indicated to the transport by a NIC driver, whatever the nature of the memory (including mapped device memory) the NIC is using.
TdiMapUserRequest
Converts an IRP passed in to a transport's TdiDispatchDeviceControl routine into a TDI_XXX IOCTL IRP if TdiMapUserRequest recognizes the minor function code specified in the input IRP.
TdiCompleteRequest
Completes an IRP with the system-defined network-specific priority boost for a transport driver.


Chapter 5 TDI Operations

Many TDI operations involve an endpoint-to-endpoint connection between a local-node client and a remote-node client. These TDI operations are connection-oriented and include the following:

  • Opening a local-node connection endpoint

  • Requesting a connection

  • Accepting a connection

  • Sending connection-oriented data

  • Receiving connection-oriented data

  • Disconnecting

  • Closing a local-node connection endpoint

TDI also permits connectionless communications between nodes on a network. This type of operation does not require the local-node client to establish an endpoint-to-endpoint connection with a remote-node client. They are faster but less reliable than connection-oriented communications. TDI connectionless operations include the following:

  • Sending a datagram

  • Receiving a datagram

The remaining TDI operations are common to connection-oriented and connectionless communication, including the following:

  • Opening a local-node transport address

  • Packaging and submitting a request

  • Setting and querying information

  • Receiving error notification

  • Requesting transport-specific actions, if the underlying transport supports any extensions to the TDI interface for its clients

  • Closing a previously opened transport address

The following sections describe the main TDI operations, as much as possible in the same order as these operations are likely to occur. See the Network Driver Reference, Part 2, for more detailed information about the TDI, transport, and client routines mentioned here.

See the Kernel-Mode Driver Reference for more detailed information about the support routines, such as ZwCreateFile and IoCallDriver, mentioned here.


5.1 Opening a Transport Address

Figure 5.1 shows how a kernel-mode client opens a transport address in its underlying transport driver.

Figure 5.1 Opening a Transport Address

A TDI client usually begins communicating with its local-node transport by opening a file object that represents a transport address. To do this, the client calls ZwCreateFile, passing the address specification in the EA (extended attributes) buffer parameter to ZwCreateFile. Within the EA buffer, the client sets the EaName member to the system-defined value TdiTransportAddress followed by an EA value of the TDI-defined type TRANSPORT_ADDRESS, set up by the client to specify the transport-specific address to be opened.

The client can specify that a particular transport address be either sharable with other clients or exclusive to the client when it calls ZwCreateFile. The initial client to successfully open a particular transport address for exclusive use prevents other clients from opening the same address until the original client closes that address.

To do subsequent connectionless communication over the network, the client can specify the underlying transport's broadcast address in the EA buffer, assuming the transport supports broadcast datagrams. Such an address cannot be opened for exclusive use by any client.

The client's call to ZwCreateFile causes the I/O Manager to create a client-process-specific file object to represent the address and to call the TDI transport driver’s TdiDispatchCreate routine with an IRP containing the client-supplied parameters to ZwCreateFile. TdiDispatchCreate parses the EA information, and the transport sets up internal state for the open address and for this client if the call succeeds.

After ZwCreateFile returns a file handle to the client and the client has obtained a pointer to the file object with ObReferenceObjectByHandle, it is ready to make certain TDI_XXX IOCTL requests to its underlying transport. For example, the client can send or receive datagrams on such an open address.

Usually, a client must first decide whether to register one or more of its ClientEventXxx handlers and whether it will use the open address to communicate with a remote-node peer process, in which case the client also must open a connection endpoint.

If the client wants to receive notifications of various network events, it can register its ClientEventXxx handler for each type of event by submitting one or more TDI_SET_EVENT_HANDLER requests, set up with TdiBuildSetEventHandler, to the underlying transport. For more information about setting up IOCTL requests and submitting them to an underlying transport, see Section 5.3.

After the client has registered its ClientEventXxx handlers, if any, on the open transport address, it is ready to make preparations for connection-oriented communication by opening a connection endpoint, as described in Section 5.2, or to begin connectionless communication, as described in Section 5.7.

When it is done with network communications on the opened transport address, the client must close the file object that represents that transport address by calling ZwClose with the handle returned by ZwCreateFile, as described later in Section 5.13.


5.2 Opening a Connection Endpoint

Figure 5.2 shows how a kernel-mode client opens a connection endpoint in its underlying transport driver.

Figure 5.2 Opening a Connection Endpoint

If a local-node client requires connection-oriented communication with a remote-node peer process, the local-node client must first open a transport address and then open a connection endpoint in its underlying transport. The client makes two calls to ZwCreateFile, first to open an address and, then, a connection endpoint.

When opening a connection endpoint, the client passes a client-supplied context for the connection in the EA (extended attributes) buffer parameter to ZwCreateFile. Within the EA buffer, the client sets the EaName member to the system-defined value TdiConnectionContext followed by an EA value that is usually the address of a client-allocated context area in which the client will subsequently maintain state about the endpoint-to-endpoint connection to be established.

The client's call to ZwCreateFile causes the I/O Manager to create a client-process-specific file object to represent the connection endpoint and to call the TDI transport driver’s TdiDispatchCreate routine with an IRP containing the client-supplied parameters to ZwCreateFile. TdiDispatchCreate parses the EA information, and the transport sets up internal state for the open connection endpoint of this client if the call succeeds.

After ZwCreateFile returns a file handle to the client and the client has obtained a pointer to the file object with ObReferenceObjectByHandle, it is ready to make certain TDI_XXX IOCTL requests to the underlying transport to establish an endpoint-to-endpoint connection with a remote-node peer.

First, the client must associate the connection endpoint with its open transport address by submitting a TDI_ASSOCIATE_ADDRESS request, set up with TdiBuildAssociateAddress, to its underlying transport.

For more information about setting up TDI_XXX IOCTL IRPs, see Section 5.3, next. For more information about how to establish an endpoint-to-endpoint connection, see Section 5.5.

When the endpoint-to-endpoint connection has been disconnected and the client no longer will use any connection-oriented communications, the client must close the connection endpoint by passing the file handle that was returned by ZwCreateFile to ZwClose, as described later in Section 5.12.


5.3 Packaging and Submitting IOCTL Requests

Figure 5.3 shows how TDI clients set up and submit TDI_XXX IOCTL requests to their underlying TDI transports.

Figure 5.3 Preparing an IOCTL IRP and Making a Request

A kernel-mode client prepares its own IOCTL IRPs for communication with the underlying transport driver, using one of the TdiBuild Xxx macros summarized in Chapter 4. The client can either obtain an IRP from a still higher layer of the network or call TdiBuildInternalDeviceControlIrp to allocate an IRP for itself.

A subsequent call to a TdiBuild Xxx macro sets the appropriate TDI_XXX code in the MinorFunctionCode of the IRP and sets the MajorFunctionCode to IRP_MJ_INTERNAL_DEVICE_CONTROL for the underlying transport driver, as well as setting the client-supplied and IOCTL-specific parameters to each TdiBuild Xxx macro in the IRP. For example, to set up the TDI_ASSOCIATE_ADDRESS request, mentioned in the preceding section, the client passes both a pointer to the file object that represents its connection endpoint and the handle to the file object that represents its transport address when it uses TdiBuildAccept.

When the IRP is set up, the client calls IoCallDriver to submit its IOCTL request to its underlying transport. The transport's TdiDispatchInternalDeviceControl routine receives the client-supplied IRP from IoCallDriver and usually forwards the client's request to an internal driver function for further processing. The transport completes the requested operation, sets the I/O status block in the IRP with the results of the operation, and calls TdiCompleteRequest (or IoCompleteRequest) with the IRP when the client's request has been satisfied.

If the client supplied the entry point of its IoCompletion routine when it called the TdiBuild Xxx macro, the client's IoCompletion routine is called when the transport completes the requested operation and calls Io/TdiCompleteRequest with the client-supplied TDI_XXX IOCTL request.


5.4 Setting and Querying Information

Figure 5.4 shows how a TDI client can make queries about the features of its underlying transport or request its underlying transport to set its state data to client-specified values.

Figure 5.4 Setting and Querying Information in the Transport

A TDI client can query certain transport driver information, such as connection-status information, activity on a particular transport address, transport-specific limits on datagram size and number, driver statistics, and internal send/receive buffer sizes if the transport buffers data internally. A client also can set some state information in the underlying transport, although it cannot override the underlying transport-determined values for certain size limits by issuing set-information requests.

If a client wants to query information concerning an open transport address or a connection endpoint, it submits a TDI_QUERY_INFORMATION request, set up with TdiBuildQueryInformation, to the underlying transport. When it uses this macro, the client passes pointers to the file object that represents the address or connection endpoint and to a client-supplied buffer in which the transport returns the requested information.

To set transport information, the client issues a TDI_SET_INFORMATION request, set up with TdiBuildSetInformation.

For each of these operations, the client also passes a system-defined TDI_QUERY_XXX value as the QType or SType parameter to the TdiBuild..Information macros to discriminate among the types of information that can be queried and set.

To query or set information that does not specifically concern an address or a connection endpoint, a client first must open a control channel in the TDI driver before submitting its query/set-information request(s). For example, a client querying information about the underlying transport's broadcast address for connectionless-data transmission or the transport's current statistics uses a control channel.

To open a control channel, the client calls ZwCreateFile with a NULL EaBuffer pointer. The client's call to ZwCreateFile causes the I/O Manager to create a client-process-specific file object to represent the control channel and to call its underlying transport’s TdiDispatchCreate routine with an IRP containing the client-supplied parameters to ZwCreateFile. TdiDispatchCreate notes the absence of EA information, and the transport sets up internal state for the open control channel and for this client if the call succeeds.

As an alternative for opening a file object that represents a control channel, the client can call IoGetDeviceObjectPointer, which returns pointers to the named device object and corresponding file object.

After ZwCreateFile returns a file handle to the client and the client has obtained a pointer to the file object with ObReferenceObjectByHandle, it is ready to make certain types of TDI_QUERY_INFORMATION or TDI_QUERY_SET_INFORMATION IOCTL requests to its underlying transport.

When its query- and/or set-information operations are complete, the client must close the open control channel by passing the file handle that was returned by ZwCreateFile to ZwClose or passing the file object pointer that was returned by IoGetDeviceObjectPointer to ObDereferenceObject, as described later in Section 5.13.


5.5 Making an Endpoint-to-Endpoint Connection

TDI clients on separate nodes of the network and their respective transports cooperate to establish an endpoint-to-endpoint connection between them. Before such a connection can be attempted, each client on its own node must do the following:

  1. Open a transport address, as already described in Section 5.1.

  2. Open a connection endpoint, as already described in Section 5.2.

  3. Associate its open connection with the open address by submitting a TDI_ASSOCIATE_ADDRESS IOCTL request to its underlying transport, as mentioned in Section 5.2 and described in Section 5.3.

Then, one of the clients makes a connection offer to the other, while the other usually listens (passively) for the connection offer to come in.

Either client also might have registered its ClientEventConnect handler with its underlying transport (see Section 5.1). The transport driver calls ClientEventConnect when any connection offer directed to its client's open transport address comes in from a remote node.

Requesting a Connection to a Remote Node

Figure 5.5 shows how a local-node TDI client initiates a connection offer to a remote-node peer process.

Figure 5.5 Requesting a Connection

The local-node client makes a connection offer to a remote-node peer process by submitting a TDI_CONNECT request, set up with TdiBuildConnect, to its underlying transport.

The local-node transport determines the client-specified target remote-node address from its client’s connect request and transmits the connection offer to the corresponding remote-node transport.

The remote-node transport notifies its client of the incoming connection offer, either by satisfying a TDI_LISTEN request previously submitted by its client or by calling the previously registered ClientEventConnect handler.

The local-node transport fails the connect IRP if the remote-node client is not listening or if the remote-node transport does not respond. The remote-node client can accept or reject the offered connection if both transports support delayed-connection acceptance, as described next.

Accepting a Connection Offer from a Remote Node

Figure 5.6 shows how a local-node client listens for a connection offer from a remote-node peer process.

Figure 5.6 Accepting a Connection (Listen Operation)

To establish an endpoint-to-endpoint connection, one client makes a connection offer, and the other indicates to its underlying transport that it is waiting for a connection offer to occur.

A local-node client can passively listen for such an incoming connection offer by submitting a TDI_LISTEN request, set up with TdiBuildListen, to its underlying transport. The client can specify the remote-node transport address from which an acceptable offer can occur when it sets up the listen IRP. If the transports support delayed-connection acceptances, the client can direct the transport to either accept an offer from a particular remote-node address immediately or allow the client to inspect the offer and decide whether to accept it.

When the transport receives a TDI_LISTEN request, it monitors connection offers coming from remote nodes that are directed to the transport address opened by the client. When it receives such a connection offer from an acceptable remote-node address, the transport copies information transmitted with the offer into a buffer the local-node client supplied with its TDI_LISTEN request and completes the IRP back to the listening client.

If the client directed the transport to accept the incoming connection offer immediately, completion of the listen request occurs as the transport notifies the remote-node transport that an endpoint-to-endpoint connection has been established with the remote-node client. In effect, the local-node client has made an endpoint-to-endpoint connection with its remote-node peer even before it "knows" that its listen IRP has been completed, and the local-node client can receive data on that endpoint-to-endpoint connection at once. Otherwise, the transport's completion of the listen request notifies the local-node client of the offer to connect, and the client either accepts or rejects the request by issuing TDI_ACCEPT or TDI_DISCONNECT request, respectively. The local-node client uses TdiBuildAccept or TdiBuildDisconnect to set up the IRP it submits to its underlying transport for a delayed-connection acceptance operation.

Accepting a connection is even simpler if the local-node client is using event handling to communicate with the underlying TDI transport driver. When its transport receives a connection offer from a remote-node client that is directed to the open transport address of the local-node client, the transport calls the registered ClientEventConnect handler of the local-node client, passing in the transport address of the offering client and any connect data the transport received with the connection offer. ClientEventConnect then accepts or rejects the connection offer immediately.


5.6 Sending and Receiving Connection-Oriented Data

TDI clients on separate nodes of the network must establish an endpoint-to-endpoint connection between them to send and receive connection-oriented data. Before either client sends data to the other, each client on its own node must do the following:

  1. Open a transport address, as already described in Section 5.1.

  2. Open a connection endpoint, as already described in Section 5.2.

  3. Associate its open connection with the open address by submitting a TDI_ASSOCIATE_ADDRESS IOCTL request to its underlying transport, as mentioned in Section 5.2 and described in Section 5.3.

  4. Establish the endpoint-to-endpoint connection between them, one by making a connection offer and the other by accepting that offer, as described in Section 5.5.

In fact, the client that accepts a connection offer can receive data from its remote-node peer as soon as the local-node transport sends a connection-acceptance frame to the remote-node transport, which can occur before the accepting client's TDI_LISTEN IRP has been fully completed back to that client.

After an endpoint-to-endpoint connection has been made by the underlying transports, either client can send data to the other across the network.

Sending Data on an Endpoint-to-Endpoint Connection

Figure 5.7 show how a kernel-mode client sends data on an endpoint-to-endpoint connection through its underlying local-node transport to a remote-node peer.

Figure 5.7 Sending Data to a Remote-Node Peer

A local-node TDI client issues a send request to transmit data from its connection endpoint to the remote-node connection endpoint. To do this, the local-node client submits a TDI_SEND IOCTL request to its transport. This IRP, which the client sets up with TdiBuildSend, contains a pointer to a client-supplied buffer containing a stream-oriented or message-oriented TSDU. This buffer can be any size up to the maximum the TDI transport driver allows. If the transport supports expedited sends, the client can request that the TSDU be transmitted as expedited data, ahead of any preceding normal sends it has already submitted to its underlying transport. If the transport driver supports internal buffering, its client can issue a non-blocking send.

The client's call to IoCallDriver with the TDI_SEND IRP forwards the IRP to the underlying transport's TdiDispatchInternalDeviceControl routine. This routine checks the MinorFunction code in the transport's I/O stack location of the IRP and usually calls a send-specific internal driver function to process the IRP further. For send requests, the internal driver function usually queues the IRP if the client has already submitted other send requests that have not yet been transmitted over the network to the remote node. The transport always queues requests to send expedited data ahead of requests to send normal TSDUs to the client's remote-node peer. The transport either copies the client-supplied data into its internal buffers or sends the specified data on the network before it completes each client-submitted TDI_SEND IRP.

If the underlying transport has failed a non-blocking send request due to insufficient internal buffer space in the transport, the driver calls its client’s registered ClientEventSendPossible handler when the transport again has available buffer space for sends. Then, ClientEventSendPossible can resubmit the TDI_SEND request that the transport previously failed.

Receiving Data on an Endpoint-to-Endpoint Connection

Figure 5.8 shows how a kernel-mode client receives data on an endpoint-to-endpoint connection through its local-node transport from its remote-node peer.

Figure 5.8 Receiving Data from a Remote-Node Peer

A local-node client can receive a TSDU, either normal or expedited, on a connection by making a TDI_RECEIVE request to the underlying transport. This IRP, which the client sets up with TdiBuildReceive, contains a pointer to a client-supplied buffer into which the transport copies all or part of the TSDU data it received from the client's remote-node peer. This buffer can be any size up to the maximum the TDI transport driver allows.

The client's call to IoCallDriver with the TDI_RECEIVE IRP forwards the IRP to the underlying transport's TdiDispatchInternalDeviceControl routine. This routine checks the MinorFunction code in the transport's I/O stack location of the IRP and usually calls a receive-specific internal driver function to process the IRP further. The internal driver function transfers received data into the client-supplied buffer until it is full or until the received TSDU data is exhausted.

However, expedited data takes precedence over normal data during receive operations. If an expedited TSDU comes in from the remote node while the transport is processing a client-submitted receive request for normal data, the transport completes the IRP back to its client with whatever normal TSDU data has already been transferred into the client-supplied buffer. Then, the transport processes the expedited receive to completion, and the client must issue another TDI_RECEIVE request to obtain the remainder of the normal TSDU.

A client can also receive data from its remote-node peer as an event notification from the underlying TDI transport driver. For these notifications, the driver removes the transport layer header from the TSDU that it receives from the remote node and calls the client’s registered ClientEventReceive, ClientEventChainedReceive, ClientEventReceiveExpedited, or ClientEventChainedReceiveExpedited handler. The client’s event handler can then copy as much of the data as possible. If ClientEventReceive or ClientEventReceiveExpedited does not receive all the data, it can do one of the following:

  • Return a not-accepted status immediately, effectively telling the transport that the received TSDU is not of interest to the client.

  • Make another TDI_RECEIVE receive request to obtain the remainder of the TSDU data.

  • Rely on subsequent driver receive-event notifications to obtain the remainder of the data.

The ClientEventChainedReceive and ClientEventChainedReceiveExpedited handlers are always given read-only access to a full TSDU by the underlying transport. Consequently, these routines have no need to issue a sequence of TDI_RECEIVE requests to the underlying transport or to process partial indications of received TSDUs.

For more information about registering ClientEventXxx handlers, see Section 5.1.


5.7 Sending and Receiving Connectionless Data

A kernel-mode client can engage in connectionless communication by sending data over the network as soon as it has successfully opened a transport address in the underlying TDI transport.

To receive data in this manner, the client must register its ClientEventReceiveDatagram and, possibly, ClientEventChainedReceiveDatagram handlers with the underlying transport or issue an explicit TDI_RECEIVE_DATAGRAM request to the underlying transport.

For more information about opening a transport address and registering an event handler on that address, see Section 5.1.

Sending a Datagram

Figure 5.9 shows how a TDI client sends a datagram to a remote-node address.

Figure 5.9 Sending a Datagram

As this figure shows, sending a datagram is similar to sending connection-oriented data. However, the local-node client submits a TDI_SEND_DATAGRAM request to its underlying transport on an open transport address, instead of a send request on an established endpoint-to-endpoint connection as already described in Section 5.6.

A TDI_SEND_DATAGRAM IRP requests the underlying transport to transmit a client-supplied datagram to an unknown number of remote-node clients at a particular remote-node transport address.

The local-node client with an open transport address can use TdiBuildSendDatagram to set up such a request. Along with the target remote-node address, the client provides buffered data that can be any size up to the maximum the TDI transport driver allows for datagram sends. The client can determine this limit by querying the underlying transport, as described already in Section 5.4.

Receiving a Datagram

Figure 5.10 shows how a kernel-mode client receives a datagram through its underlying transport from a remote node.

Figure 5.10 Receiving a Datagram

As this figure shows, receiving a datagram is similar to receiving connection-oriented data. However, the local-node client submits a TDI_RECEIVE_DATAGRAM request to its underlying transport on its open transport address, instead of a receive request on an established endpoint-to-endpoint connection as already described in Section 5.6.

However, the client can receive a datagram sent by any remote-node client that specifies the local client's open transport address as the destination of the datagram. Other local clients that have opened the same transport address can also receive the same datagram.

A TDI_RECEIVE_DATAGRAM IRP requests the underlying transport to return a datagram from any remote-node client that has sent one to the local-node client's open transport address. The local-node client can use TdiBuildReceiveDatagram to set up such a request. Along with a pointer to the file object that represents its open transport address, the client provides a buffer that can be any size up to the maximum the TDI transport driver allows for received datagrams. Because TDI transports never fragment datagrams, the local-node client usually issues one receive-datagram request to accept one datagram.

When it satisfies such a request, the transport driver copies a received datagram into the client-supplied buffer and returns the transport address of the remote-node client that sent the datagram with the completed IRP. If the received datagram is too large for the buffer, the transport returns as much data as possible and discards the remaining data.

A client can also receive a datagram from a remote node as an event notification by the underlying TDI transport driver. For these notifications, the transport removes its header from the TSDU that it receives from the remote node and calls the client’s registered ClientEventReceiveDatagram, or possibly ClientEventChainedReceiveDatagram, handler. ClientEventReceiveDatagram can do one of the following:

  • Return a not-accepted status immediately, effectively telling the transport that the TSDU is not of interest to the client.

  • Copy as much data as possible into an internal buffer and set up a TDI_RECEIVE_DATAGRAM request for the remainder of the TSDU if the transport provided only part of the data and return control.

  • Copy all of the TSDU into an internal buffer and return control.

Non-buffering transport drivers can discard data after a receive-datagram event indication if the ClientEventReceiveDatagram does not copy the data or return an IRP. Buffering transport drivers retain a certain amount of datagram information that their clients can retrieve later by making explicit TDI_RECEIVE_DATAGRAM requests.

The ClientEventChainedReceiveDatagram handler is always given read-only access to a full TSDU by the underlying transport. Consequently, this routine has no need to issue a TDI_RECEIVE_DATAGRAM request to the underlying transport or to process partial indications of received TSDUs.

For more information about registering ClientEventXxx handlers, see Section 5.1.


5.8 Connection-Oriented Versus Connectionless Transfers

TDI provides connection-oriented services at the transport layer to provide the highest quality of service at the lowest cost. Depending on the specific driver protocol, this can involve packetizing of client data, sequencing, acknowledgment, retransmission, flow-control, and error recovery.

TDI also provides connectionless services for datagrams. However, these services are lightweight: they do not provide error-free delivery or flow control. The transport layer generally does not segment or reassemble datagrams. Delivery of connectionless data is best-effort only for TDI transports.

A datagram send is an inherently unreliable form of network communication. The sending client has no way of determining how many or which remote-node clients have opened the target remote-node address or whether the remote-node transport is currently accepting datagrams. Furthermore, the local-node TDI driver can lose or duplicate a datagram at the discretion of the transport driver writer. By contrast, the local-node transport is responsible for retrying sends on an established endpoint-to-endpoint connection until the transmitted data is accepted on the remote node and acknowledged by the remote-node transport while the endpoint-to-endpoint connection is active.

Like a datagram-send, a datagram receive operation can be unreliable. That is, the local-node TDI driver can lose or duplicate a received datagram at the discretion of the transport driver writer. By contrast, the local-node transport is responsible for delivering a receive on an established endpoint-to-endpoint connection until the transmitted data is accepted (or rejected) by its client and acknowledged to the remote-node transport while the endpoint-to-endpoint connection is active.

An endpoint-to-endpoint connection remains active until it is broken by a disconnect operation, as described later in Section 5.11 or until the underlying transport's time-out logic determines that the remote node is failing to respond.


5.9 Requesting Transport-Specific Actions

Figure 5.11 shows how a TDI client can make transport-specific action requests to the underlying TDI driver if that transport defines such extensions to TDI.

Figure 5.11 Requesting Transport-Specific Extensions

A TDI client can send special or proprietary extension requests to a TDI transport driver that defines a set of transport-specific action codes for these operations. These extensions, which can concern an open address, an open connection endpoint, or an open control channel, apply only to the calling client and not to any other TDI transport clients or drivers.

To request such a transport-defined action operation, the client first must open the address, connection endpoint, or control channel, as already described in Section 5.1, Section 5.2, or Section 5.4, respectively. Then, the client can use TdiBuildAction to set up a TDI_ACTION request with a client-supplied buffer containing the transport-defined action code and corresponding action parameter block for the requested operation.


5.10 Receiving Error Notifications

In response to client-submitted IRPs, the transport driver notifies a TDI client of error conditions in the status codes that it returns. While this provides a client with request-specific errors, it would be difficult for the client to maintain a count of failed IRPs and apply internal logic to determine whether the underlying drivers are in a state that renders the client's subsequent network I/O operations unreliable.

To receive a notification of unexpected error conditions in an underlying driver or in the underlying physical medium, the client can register its ClientEventError handler with its underlying transport, as already described in Section 5.1. Then, if the transport itself or any lower driver that the transport depends on to carry out client communications over the network encounters such an error condition, the transport calls ClientEventError. This call notifies its client that network I/O on the client's open transport address is no longer reliable (or possible). Then, the client can notify its own higher level clients of the network failure and clean up all TDI-client-allocated resources for pending operations on the affected open transport address.


5.11 Disconnecting an Endpoint-to-Endpoint Connection

Figure 5.12 shows how TDI clients release an endpoint-to-endpoint connection.

Figure 5.12 Disconnecting an Endpoint-to-Endpoint Connection

Disconnection behavior is transport-specific in nature. Depending on the transport, when a connection-oriented TDI client initiates a disconnect between nodes, both nodes might need to participate in the disconnection operation. That is, when one client initiates a disconnect, the remote-node client possibly must respond to it.

During a disconnect operation, the TDI transport driver usually refuses incoming requests on the open connection endpoint and stops all activity at the specified connection endpoint unless both transports support controlled disconnects and the initiating client requests one.

As Figure 5.12 shows, one client on an endpoint-to-endpoint connection can initiate a disconnection operation by submitting a TDI_DISCONNECT request, set up with TdiBuildDisconnect, to its underlying transport. When that transport finishes processing the initiating client’s request, it notifies the remote-node transport driver that a disconnection is in progress, and this transport begins returning an appropriate status code for client-submitted I/O requests on the endpoint-to-endpoint connection.

If the responding client registered its ClientEventDisconnect handler, the TDI transport driver notifies the client when the disconnect occurs by calling this handler. Then, ClientEventDisconnect acknowledges the disconnection by making a TDI_DISCONNECT request to its underlying transport. This notification allows the responding client to clean up client-allocated state for the endpoint-to-endpoint connection promptly.

However, a disconnection operation does not close either client's open connection endpoints or transport addresses. After TDI_DISCONNECT requests have been satisfied, both clients can reuse the file objects representing these open resources in their underlying transports. For example, either client might make a subsequent connection offer to another remote node on the network, as already described in Section 5.5. Until each client closes the file objects representing its respective connection endpoint and the associated transport address as described in Section 5.12 and Section 5.13, respectively, these resources remain allocated to the client and available for client-submitted IOCTL requests to the underlying transport.


5.12 Closing a Connection Endpoint

Figure 5.13 shows how a kernel-mode client closes a connection endpoint.

Figure 5.13 Closing a Local-Node Connection Endpoint

After an endpoint-to-endpoint connection has been disconnected, as already described in Section 5.11, a client can close the connection endpoint.

When a client no longer has any use for an open connection endpoint, it must close that connection endpoint as follows:

  • Pass the file object pointer returned by ObReferenceObjectByHandle to ObDereferenceObject.

  • Pass the file handle that was returned by ZwCreateFile when the connection endpoint was opened to ZwClose.

Then, the I/O Manager submits IRPs to the transport’s TdiDispatchCleanup and, subsequently, TdiDispatchClose routines.

These transport routines immediately close the connection endpoint and free all associated transport driver resources. TdiDispatchCleanup also terminates any active connection involved with the endpoint by sending a disconnect notification to the corresponding remote-node transport.

As the preceding sentence implies, it is unnecessary for a TDI client to disassociate the connection endpoint from its associated transport address before making a close-connection-endpoint request. If necessary, the underlying transport driver simulates the effects of a disassociation.

However, a client can explicitly disassociate a connection endpoint from an open transport address before closing the connection endpoint by making a TDI_DISSOCIATE_ADDRESS request to the transport, set up with TdiBuildDisassociateAddress.

For example, a client might make a disassociate-address request and reassociate the open connection endpoint with another open transport address.


5.13 Closing a Transport Address or Control Channel

After closing any associated connection(s), a TDI client is ready to close an open transport address.

When a client no longer has any use for an open transport address or control channel, it must release that object as follows:

  • Pass the file object pointer returned by ObReferenceObjectByHandle to ObDereferenceObject.

  • Pass the file handle that was returned by ZwCreateFile when the connection endpoint was opened to ZwClose.

In a similar manner, a TDI client also can close any control channel it has opened in its underlying transport driver.

If the client opened the file object representing a control channel with a call to IoGetDeviceObjectPointer, it must pass the returned file object pointer to ObDereferenceObject to release the file object.

Then, the I/O Manager submits IRPs to the transport’s TdiDispatchCleanup and TdiDispatchClose routines.

These transport routines immediately close the transport address or control channel and free all associated client-specific transport driver resources. For example, TdiDispatchCleanup cancels any pending requests on a transport address that is being closed, deregisters any ClientEventXxx handlers on that address, and cleans up client-specific state for that address. If this client has released the last file handle for a particular transport address, the transport also releases internal state for that transport address.

After ZwClose returns to the client, it cannot submit a request to the underlying transport for the transport address or control channel it previously had open. The client-specific file object representing that address or control channel no longer exists.


Chapter 6 Transport Helper DLLs for Windows Sockets

Originally an application programming interface specific to TCP/IP, Windows Sockets has since become the primary user-mode interface to Windows NT® network transports. In addition to TCP/IP, the Windows Sockets dynamic-link library works in conjunction with several other transports that provide extensions in the form of transport-specific user-mode Windows Sockets helper (WSH) DLLs.

This describes the WSH DLL to be supplied with a new transport driver to support application calls through Windows Sockets, as follows:

  • An overview of the architectural relationships among Windows Sockets, transport drivers, and transport-specific WSH DLLs in Section 6.1

  • An overview of how Windows Sockets communicates with transport-specific WSH DLLs in Section 6.2

  • Setting up the configuration registry for WSH DLLs in Section 6.3

  • Managing synchronization for WSH DLLs in Section 6.4

  • How WSH DLLs can support additional information sent on the network with connections and disconnections in Section 6.5

  • A summary of the required and optional WSH Xxx functions in Section 6.6

For information about how applications interface with Windows Sockets 2, see the Win32 SDK.


6.1 Windows Sockets Helper DLL Architecture

Windows Socket Helper (WSH) DLLs are user-mode components provided by transport driver writers to facilitate the usage of Windows Sockets with their transports. Figure 6.1 shows an overview of this architecture.

Figure 6.1 Windows Sockets Helper DLL Architecture

Windows NT provides a dynamic-link library, msafd.dll, which is a sockets service provider. When a transport driver is installed in the system and it installs a transport-specific WSH DLL, network setup will automatically configure msafd.dll to be the service provider for that WSH DLL. When an application makes a call to a Windows Sockets function, such as WSASocket, MSAFD as the service provider will resolve the call and make a call to the appropriate WSH DLL for assistance as necessary.

Some Windows Sockets functions require no assistance from any WSH DLL. For example, sending or receiving data does not require the WSH DLL as the connections have already been established. In cases such as these, MSAFD can communicate directly with the transport by calling Win32 functions.

However, for function calls that rely on transport-specific features or where the implementation can vary from transport to transport, the transport-specific WSH DLL will be used to resolve these ambiguities. For example, WSAJoinLeaf adds a socket to an established multipoint session. Each transport implements the addition of new connections to a multipoint session differently. Consequently, MSAFD calls the appropriate WSH DLL to support the sockets interface according to the transport-specific implementation. To support application calls to WSAJoinLeaf, MSAFD calls the WSH DLL's WSHJoinLeaf function to validate the options requested and call the transport with the appropriate information to add a new socket to a multipoint session.

If a transport supports the new features of Windows Sockets 2, including multipoint session sockets, socket grouping, and logical representations of socket addresses, the corresponding WSH DLL must export the Windows Sockets 2 functions, summarized later in Section 6.6.


6.2 Communicating with a WSH DLL

Each transport that supports Windows Sockets applications supplies a user-mode Windows Sockets helper dynamic-link library to interpret network addresses and to process socket options in the architecture as outlined in Section 6.1.

For example, when any application calls socket, it specifies an address family, a socket type, and a protocol. These three arguments must uniquely identify a transport driver to support the socket. Windows Sockets searches for a match between these application-supplied arguments and the standardized configuration information stored in the registry for WSH DLLs. If it finds a match, Windows Sockets subsequently calls down through MSAFD to the WSH Xxx functions exported by the WSH DLL as necessary. Otherwise, the application's call to socket fails.

When a match is found, Windows Sockets calls LoadLibrary on the WSH DLL, and then calls GetProcAddress to retrieve the entry point for each exported WSH Xxx function. These WSH Xxx functions are called whenever necessary to query for protocols supported by the helper DLL, to translate addresses, and to process WSH-supported options as getsockopt and setsockopt calls occur. For example, when an application passes an option to setsockopt that is not explicitly supported by Windows Sockets, the appropriate WSH DLL is called to process the option. The WSH DLL takes any necessary action to support each such option the corresponding transport supports.

The following conventions permit mechanical transformations between socket addresses, application-specified as SOCKADDR structures, and the TDI-defined addresses used by underlying transport drivers.

  • TDI address types, specified as values in the AddressType member of TA_ADDRESS structures, are identical to the address family values of socket address structures.

  • The Address[ ] array of a TA_ADDRESS structure has exactly the same format as the data following sa_family in a socket address structure for a given address type.


6.3 Configuring a WSH DLL

Two places in the registry must be set up with standardized configuration information for each WSH DLL:

  1. Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
    \Services\Winsock\Parameters     is a value entry of type REG_MULTI_SZ specifying a list of protocols (transport drivers) that each have a corresponding WSH DLL. The names stored in this list match the key names for the corresponding transport drivers under ..\CurrentControlSet\Services.

  2. Under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet
    \Services\    TransportDriverName\Parameters for each such transport driver is a Winsock subkey. Windows Sockets opens this key by forming a name string from the path to its own key under the Services key, the transport name specified in the list under ..Winsock\Parameters, and the transport's ..Parameters\Winsock key, which must contain the following value entries:

    • Mapping ¾ a REG_BINARY value that describes the address family, socket type, and protocol parameter triples supported by the WSH DLL. The format for this binary data is the WINSOCK_MAPPING structure, as defined in wsahelp.h.

    • HelperDllName ¾ a REG_EXPAND_SZ value that specifies the path to the WSH DLL itself.

    • MinSockaddrLength ¾ a REG_DWORD value that specifies the smallest valid SOCKADDR size, in bytes, for the WSH DLL.

    • MaxSockaddrLength ¾ a REG_DWORD value that specifies the largest valid SOCKADDR size, in bytes, for the WSH DLL.

Storing this information in two distinct places under ..\CurrentControlSet\Services helps to localize information about a transport driver under that driver's own Parameters key. The information under ..\Services\Winsock\Parameters provides a pointer to the actual information for each transport's WSH DLL, allowing setup programs to interoperate more easily.

The Windows NT setup utilities provide functions that perform most of the necessary work to set up WSH DDL registry information for a transport driver. These routines are AddWinsockInfo and RemoveWinsockInfo in the setup file utility.inf, which can be found in the WIndows NT system directory. A transport driver's installation script can call AddWinsockInfo, passing in the key name under Services of the transport driver, the full path name of the transport-specific WSH DLL, and the minimum and maximum SOCKADDR lengths. AddWinsockInfo then stores the standardized information already described in the registry.

For an example of a transport's call to this setup function, see oemnxptc.inf, the installation script for TCP/IP, also located in the Windows NT system directory.


6.4 WSH DLL Synchronization

Windows Sockets guarantees synchronization to a WSH DLL on a per-socket basis. That is, two threads cannot be executing concurrently in WSH Xxx functions for the same socket. Consequently, a WSH DLL that maintains its state information on a per-socket basis need not do any synchronization on its own.

If a WSH DLL maintains global state or per-process state, it must synchronize access to such state information internally. Windows NT provides several user-mode synchronization mechanisms such a WSH DLL might use, including semaphores, events, and critical sections. Which type of synchronization mechanism to use is up to the WSH DLL developer.


6.5 Supporting Connect and Disconnect Data with a WSH DLL

Some transports, such as DECNet and OSI TP4, support connect and disconnect data: additional data, not in the normal network data stream, that is sent on the wire along with connect or disconnect requests. Typically, connect and disconnect data is used for operations like application-level version negotiation.

TDI supports the transmission of connect and disconnect data in the TDI_CONNECTION_INFORMATION structure, which has members specifying the UserDataLength, UserData, OptionsLength, and Options. This structure is passed in TDI connect, accept, and disconnect requests by kernel-mode TDI clients.

However, Windows Sockets does not provide input parameters for connect data to the connect function or for disconnect data to the shutdown and closesocket functions. WSH DLLS can add support for connect and disconnect data for applications that call the Windows Sockets setsockopt and getsockopt functions.

The setsockopt function can be called to specify connect and disconnect data to be sent to a remote node, and getsockopt can be called to retrieve connect and disconnect data sent from the remote node. To support this, the following socket options are defined in the Windows NT header file winsock.h:

/*
 * Options for connect and disconnect data and options. Used only by
 * non-TCP/IP transports such as DECNet, OSI TP4, etc.
 */
#define SO_CONNDATA    // connect data
#define SO_CONNOPT     // connect options
#define SO_DISCDATA    // disconnect data
#define SO_DISCOPT     // disconnect options
#define SO_CONNDATALEN // connect data length, in bytes
#define SO_CONNOPTLEN  // connect options length
#define SO_DISCDATALEN // disconnect data length
#define SO_DISCOPTLEN  // disconnect options length

How an application uses these options in conjunction with getsockopt and setsockopt depends on whether the application is a server or client (see Section 6.4.1 or 6.4.2, respectively), and on how the application uses the transmitted connect data. For transports, such as DECNet, that have a preexisting definition of connect data different from what is described here, a WSH DLL translates between the transport's semantics and the semantics expected by Windows Sockets, making calls to getsockopt and setsockopt.

Connect and disconnect options are effectively the same as connect and disconnect data from the standpoint of Windows Sockets. These are actually a buffer of data passed to the transport prior to the connect or disconnect, and a buffer containing data that is retrieved following a connect or disconnect. Consequently, an application uses connect or disconnect options in much the same manner as connect or disconnect data. The only difference is the option names used for the parameter passed to getsockopt and setsockopt; for example, SO_CONNOPT in place of SO_CONNDATA.


6.5.1 Client Applications and Connect Data

An application written to communicate through Windows Sockets with a transport that supports connect data should call setsockopt with SOL_SOCKET as the level, SO_CONNDATA as the option name, and an option value that is a pointer to a buffer containing the connect data the transport should send to the remote node with the connection request. Such an application must call setsockopt before it calls connect, and the application-supplied connect data is transmitted when the application makes its call to connect.

Besides making a call to connect, retrieving response-connect data requires two additional steps:

  1. Before calling connect, the application must inform Windows Sockets of how much space to reserve for the expected response-connect data. To do this, the application calls setsockopt with option level SOL_SOCKET, option name SO_CONNDATALEN, and option pointing to an integer that specifies the maximum number of bytes the application expects. Windows Sockets allocates a buffer of this size to contain connect data sent by the server application from the remote node.

  2. After connect has completed successfully, the application can call getsockopt with an option level of SOL_SOCKET, option name of SO_CONNDATA, and option pointing to a large enough buffer to hold the response-connect data. Then, Windows Sockets copies the response-connect data, if any, into the application’s buffer.

After an application has set up a response-connect buffer, Windows Sockets uses that buffer for the lifetime of the socket. That is, an application can retrieve response-connect data for a particular socket at any time after the socket is connected and before it is closed.


6.5.2 Server Applications and Connect Data

Server applications use connect data differently from client applications in two important ways:

  1. A server application receives the client’s connect data before it sends back its own.

  2. Because Windows Sockets does not support delayed-connection acceptances as TDI does, a server application must send the same response-connect data to all clients.

To receive connect data sent by clients, a server application calls setsockopt with the SO_CONNDATALEN option to force Windows Sockets to reserve buffer space for receiving connect data for incoming connect requests. The specified amount of connect-data buffer space is used subsequently for all incoming connects, but Windows Sockets allocates a separate buffer for each connect.

After a newly connected socket is accepted with accept, the server application calls getsockopt with the SO_CONNDATA option and a server-allocated buffer large enough to hold the expected data. Windows Sockets fills in this buffer with the connect data sent by the client.

To send response-connect data to clients, a server application calls setsockopt with the SO_CONNDATA option and a buffer of data before any incoming connections are accepted (typically before listen). Thereafter, any client that connects to the server receives that server-supplied connect-response data. The connect-response data for a server can be changed at any time with another setsockopt call, which discards the previous data and replaces it with the new data.


6.5.3 Disconnect Data

Client and server applications send and receive disconnect data in a manner similar to the way each sends and receives connect data.

To send disconnect data, a client application calls setsockopt with the SO_DISCDATA option and a buffer of disconnect data to be transmitted to the server on the remote node. This data is sent either when the client application calls shutdown to disconnect the send side of the socket or when the application calls closesocket on the socket.

To receive disconnect data, a client application must call setsockopt with the SO_DISCDATALEN option prior to a remote’s closure of the connection. This option specifies the amount of disconnect data the application expects to receive.

After the remote has disconnected, any server-supplied disconnect data can be retrieved with the SO_DISCDATA option to getsockopt. The client application passes in a buffer large enough to hold all the disconnect data, and Windows Sockets copies the received disconnect data into this buffer.


6.6 WSH DLL Function Summary

This summarizes the set of WSH Xxx functions that every WSH DLL must export. Each supplier of a new transports can implement these functions in a corresponding WSH DLL according to the features supported by each transport. Windows Sockets treats all WSH DLLs as trusted code. Consequently, any bug in a WSH Xxx function manifests itself as bug(s) in Windows Sockets.

The following summarizes the set of functions that must be exported by any transport-specific WSH DLL:
WSHEnumProtocols returns a list of protocols (Windows Sockets PROTOCOL_INFO structures) that the WSH DLL supports.

WSHGetSockAddrType parses a socket address.

WSHGetSocketInformation is called whenever getsockopt is passed an option that Windows Sockets does not explicitly support.

WSHGetWildCardSockAddr is called when Windows Sockets needs to perfom an “automatic” bind of a socket.

WSHGetWinsockMapping returns information about the address family, socket type, and protocol parameter triples supported by the WSH DLL.

WSHNotify is called to notify the helper DLL of a state transition.

WSHOpenSocket is called when a socket is opened.

WSHSetSocketInformation is called whenever setsockopt is passed an option that Windows Sockets does not explicitly support.

To support the new Windows Sockets 2 extensions for leaf/root sockets, socket groups, and logical representation of sockets, the following optional functions should be implemented:
WSHAddressToString returns a logical string representation of a socket address that can be used for display purposes.

WSHGetBroadcastSockaddr obtains a valid broadcast address for a socket.

WSHGetProviderGuid returns the GUID that identifies the protocols supported by a helper DLL.

WSHGetWSAProtocolInfo returns a pointer to protocol information for the protocol(s) supported by a helper DLL. This function is only used during setup.

WSHIoctl obtains information or performs actions based on a unique control code.

WSHJoinLeaf performs any protocol-specific actions that must be taken to add a socket to a multipoint session.

WSHOpenSocket2 performs the protocol-specific actions for creating a new socket. If this function is exported, it replaces the WSHOpenSocket function.

WSHStringToAddress converts a logical string representation of a socket address to a SOCKADDR structure.

For more detailed information about these these WSH Xxx functions, see the Network Driver Reference.


Appendix A Full NIC Drivers

This chapter describes full NIC drivers that comply with NDIS 3.0. If you are providing a network driver, it is not necessary for you to write a full NIC driver because the current version of Windows NT supports miniport NIC drivers. It is recommended that you provide a miniport NIC driver instead of the full NIC driver to save time. You need to develop fewer routines in your miniport NIC driver because there is more support for common routines in the NDIS library.


A.1 Introduction

The client for a Microsoft® Windows NT® NIC driver is a generic transport driver. A TDI driver is a transport driver. For information about TDI, see Part 3.

Both the driver and its client use calls to or from the NDIS interface library, which is a set of functions and macros that forms a wrapper for interfacing between bound transport drivers and network interface cards for a particular network. Windows NT supports the following types of network interface card:

  • Ethernet (802.3)

  • Token Ring (802.5)

  • FDDI

  • WAN (point-to-point and WAN network interface cards)

  • LocalTalk

  • DIX

  • ARCNET


A.1.1 NIC Driver Design Goals

An NDIS 3.0 NIC driver must meet the following requirements:

  • Portability. The NIC driver needs to be able to call NDIS interface library portability functions that allow it to run on both x86 and RISC-based platforms.

  • Multiprocessor Support. The NIC driver must provide multiprocessor support.

  • Functional Requirements. The NIC driver must be able to register itself with the NDIS interface library; create and delete bindings between transport drivers and network interface cards; allocate, initialize, manipulate, inspect, and deallocate packet descriptors; and make requests for Windows NT functions.

  • Spin Lock Use. The full NIC driver needs to use spin locks to synchronize its activities in uniprocessor and multiprocessor systems for safe access to shared data structures, such as packet descriptors.


A.1.2 NIC Driver Software Description

The Windows NT DDK provides ndis.h as the main header file for the NIC driver. This file defines the entry point functions for a full NIC driver, transport drivers, and the NDIS interface library. For more information about the functions and their interactions, see Section A.2, Section A.3, and Section A.4.


A.1.2.1 Full NIC Driver Code

The typical full NIC driver consists of a number of entry point functions and action functions that interact with a filter library. For more information about these functions, see Section A.1.2.6. Many of the driver functions can operate either synchronously or asynchronously. The code described in this book is based on a media-access control driver, but you can apply it to any type of NIC driver. The DDK provides a sample driver you can tailor to your needs. For more information about the sample driver, see Section A.1.2.3.


A.1.2.2 Full NIC Driver Objects

Typically the NIC driver uses a software object called a logical adapter to represent each network interface card that it controls. The logical adapter name corresponds to a Windows NT device object that the Windows NT I/O subsystem manages. The logical adapter contains information about an associated network interface card, such as name, network address, and NIC base address. It also contains data about the current state of the driver in relation to the hardware, such as number of bindings, binding handles, filter database, and logical adapter references. Additionally, the logical adapter keeps track of buffer resources used for sending and receiving data. It implements a queue for receiving data. For sending data, the logical adapter allows the NIC driver to handle data packets in one or more queues.

The NIC driver maintains a MIB, a set of objects that represent information about its capabilities and current status. The MIB identifies a managed object by a 32-bit OID. To query information in the MIB, a transport driver passes the OID and an array large enough to hold the returned data to the NIC driver. If the transport driver does not provide a large enough array for the information, the NIC driver writes the number of bytes needed for the data in the NDIS_REQUEST structure, along with the status code NDIS_STATUS_INVALID_LENGTH. For more information about this status code, see the Network Driver Reference.


A.1.2.3 Full NIC Driver Sample Code

As mentioned earlier, the DDK for Windows NT includes code for several sample miniport NIC drivers. For sample code to full NIC drivers, you will have to use the older DDK from Windows 3.1. For more information about this sample NIC driver, see Section A.4.

The sample code is based on an Ethernet driver that controls a MIPS-based network interface card using the SONIC integrated circuit, manufactured by National Semiconductor. The sample driver contains functions that you can adapt to your own purposes. There are always hardware-dependent functions that only you can write. However, many functions (primarily those that communicate with the NDIS interface library instead of a network interface card) are fairly standard. You can use these with little or no modification.


A.1.2.4 Transport Driver Code

The NDIS 3.0 transport driver is usually a true transport driver that generates and responds to requests to or from higher-level transports that encapsulate the NIC driver. The transport driver can also provide other functions, such as bridging.


A.1.2.5 NDIS Interface Library Code

All interactions between transport drivers and NIC drivers use the NDIS interface library wrapper. The entire library is packaged in a DLL as a set of abstract functions, with emphasis on macros for maximum performance. Both NDIS 3.0 transport and NIC drivers link against this DLL.

When called, an NDIS interface library function submits a request to another library function/macro, calls the operating system, or performs a local action that does not require communication with other Windows NT software subsystems. The main role of a wrapper function/macro is to exchange calls with NIC driver upper-edge functions and transport driver lower-edge functions, usually forwarding information from one driver to the other.


A.1.2.6 Filter Library Code

Filter libraries are special sets of NDIS interface library functions that route a received packet to a specific bound transport driver, based on packet target address. NDIS 3.0 currently supports the filter libraries listed below. A NIC driver can access filter library functions by including the appropriate header file.

  • Ethernet filter library (efilter.h)

  • Token Ring filter library (tfilter.h)

  • FDDI filter library (ffilter.h)

Each filter library maintains a database for a network interface card. The database associates each NIC binding with a list of addresses. The filter library processes a packet and passes it on to each transport driver that has an address entry matching the destination address of the packet.


A.1.3 Full NIC Driver Operation Overview

As Figure A.1.1 illustrates, NDIS 3.0 NIC and transport drivers use the NDIS interface library to access each other, as well as operating system functions. At the beginning of network communications, the NIC driver must register itself with the network. Each transport driver concerned with the supported network interface card then opens that card to create a binding. After this, the bound transport drivers can submit requests to the network interface card by calling NIC driver functions through related wrapper functions/macros, specifying the handle of the network interface card. For Windows NT, this handle is a pointer to an operating system-maintained object.

To start communicating with its remote node peer, a local node transport driver repackages messages from upper network layers into data packets, either TSDUs or equivalent packets. It then sends these packets to the NIC driver using calls to the NDIS interface library. The NIC driver uses its upper-edge functions to reassemble the information for transmission and inform associated network interface cards when data is ready for transmission over the network.

When return data is available from the remote node, the network interface card captures the data and signals the local node NIC driver. In turn, this driver (through the NDIS library and transport driver lower-edge functions) notifies bound transport drivers of incoming packets and subsequently transfers these packets.

NDIS 3.0 Driver Architecture


A.2 Overview of Full NIC Driver Functions

This chapter describes the main functions provided by a full NIC driver that complies with NDIS 3.0. The NIC driver functions with the prefix Mac are the routines that must be exported by a full NIC driver. Since the addresses to these functions, not the actual names, are passed during driver initialization, you may name the exported functions with some other prefix. The functions used by a full NIC driver from the NDIS interface library are also covered, including the Ethernet filter library, the Token Ring Filter library, and the FDDI filter library.

This chapter also describes the lower-edge functions exported by a transport driver because these routines call through the NDIS interface library to full NIC drivers.

For a detailed description of how the functions work together, see Section A.3.


A.2.1 Full NIC Driver Upper-Edge Functions

The full NIC driver must provide the minimum upper-edge functions defined in Table A.2.1. A bound transport driver makes all calls to these functions indirectly, through the NDIS interface library.

The full NIC driver can optionally make use of the NDIS interface filter libraries (Ethernet, Token Ring, and FDDI) to manage multiple transport driver bindings to a network interface card. If the full NIC driver uses a filter library, it must supply the action functions listed in Table A.2.2.

Some of the full NIC driver functions use synchronous processing, while others use asynchronous processing. NDIS 3.0 implements synchronous functions without a wait for an external event. Asynchronous function completion occurs when the NIC driver calls an NDIS interface library completion function, which in turn calls a transport driver completion function for postprocessing.

Names of the full NIC driver functions mentioned in these discussions use Mac as a generic prefix. For all functions written for an actual NIC driver, you may provide your own names, or you can simply substitute the name of the specific driver for Mac. For details of the functions and types mentioned in these sections, see Chapter 1 in the Network Driver Reference.

Table A.2.1 Full NIC Driver Upper-Edge Functions
Function | Definition | Async Option
DriverEntry | Called by the operating system to activate and initialize the NIC driver. | Not Appli-cable
MacAddAdapter | Called by the NDIS interface library to add support for a network card. | No
MacCloseAdapter | Called by a transport driver to close an open binding. | Yes
MacDeferredProcessingRoutine | Called by the operating system to complete interrupt processing that MacInterruptServiceRoutine does not perform. This function is the DPR for the NIC driver. | No
MacInterruptServiceRoutine | Called by the operating system as an ISR that invokes processing associated with a network card interrupt. | No
MacOpenAdapter | Called by a transport driver to open a binding between itself and a network card. | Yes
MacQueryGlobalStatistics | Called by the operating system to query current NIC driver statistics about a logical adapter. | Yes
MacRemoveAdapter | Called by the operating system to remove a registered network card. | No
MacRequest | Called by a transport driver to query or set NIC driver information. | Yes
MacReset | Called by a transport driver to issue a hardware reset to a network card. | Yes
MacSend | Called by a transport driver to instruct the NIC driver to transmit a packet through a network interface card onto the network. | Yes
MacSynchronizeFunction | Called by a NIC driver function to synchronize access to shared data by the ISR and non-ISR network card functions. | No
MacTimerFunction | Called by the operating system for time-delayed processing after the timer expires. | No
MacTransferData | Called by a transport driver to copy the data from a received packet. | Yes
MacUnload | Called by the operating system to instruct the NIC driver to unload itself. | No
MacChangeEthAddresses | Called by the Ethernet filter library to initiate processing if the multicast address list for a network card changes. | Yes
MacChangeFddiAddresses | Called by the FDDI filter library to initiate processing if the multicast address list for a network card changes. | Yes
MacChangeFilter | Called by the Ethernet, Token Ring, or FDDI filter library to initiate processing when a binding sets or clears a filter class. | Yes
MacChangeGroup | Called by the Token Ring filter library to initiate processing if the group address for all bindings changes. | Yes
MacChangeTrAddress | Called by the Token Ring filter library to initiate processing if the functional address for a network card changes. | Yes
MacCloseAction | Called by the Ethernet, Token Ring, or FDDI filter library to complete processing when a binding closes while the NIC driver is indicating a received packet to a bound transport driver. | No


A.2.2 Transport Driver

An NDIS 3.0 transport driver must include at least the lower-edge functions defined in Table A.2.3. All calls to and from these functions go through the NDIS interface library.

Names of the transport driver functions mentioned in these discussions use Protocol as a generic name to reflect naming conventions used in the original NDIS 3.0 driver design. You will not be writing or changing these functions. For additional information about these functions, see Chapter 3 in the Network Driver Reference.

Table A.2.3 Transport Driver Lower-Edge Functions
Function | Definition
DriverEntry | Called by the operating system to activate and initialize the transport driver.
ProtocolCloseAdapterComplete | Called by a NIC driver to indicate asynchronous completion of a MacCloseAdapter operation.
ProtocolOpenAdapterComplete | Called by a NIC driver to indicate asynchronous completion of a MacOpenAdapter operation.
ProtocolReceive | Called by a NIC driver to indicate that all or part of a packet is available for the transport driver to inspect.
ProtocolReceiveComplete | Called by a NIC driver to indicate that a receive operation has completed and to request postprocessing.
ProtocolRequestComplete | Called by a NIC driver to indicate asynchronous completion of a MacRequest operation.
ProtocolResetComplete | Called by a NIC driver to indicate asynchronous completion of a MacReset operation.
ProtocolSendComplete | Called by a NIC driver to indicate asynchronous completion of a MacSend operation.
ProtocolStatus | Called by a NIC driver to notify a transport driver of changes in network interface card status.
ProtocolStatusComplete | Called by a NIC driver to indicate completion of status reporting for a logical adapter.
ProtocolTransferDataComplete | Called by a NIC driver to indicate asynchronous completion of a MacTransferData operation.


A.2.3 NDIS Interface Library

This section lists and briefly describes the functions in the NDIS interface library used by a full NIC driver.


A.2.3.1 General

As Chapter 13 describes, all interactions between transport drivers and a NIC driver use the NDIS interface library. Library functions accept transport driver requests and NIC driver event indications. They also call NIC driver upper-edge functions, transport driver lower-edge functions, or operating system functions to satisfy the driver requests.

For maximum performance, Windows NT provides the NDIS interface library as a set of inline macros instead of actual functions to ensure compatibility and interoperability among NDIS 3.0 drivers. Transport and NIC drivers use simple function calls for requests they cannot implement through macros.

Windows NT furnishes NDIS interface library functions as a DLL. Both transport and NIC drivers link to this DLL. Interface library functions are:

  • Functions called by the NIC driver.

  • Functions called by a transport driver.

  • Buffer, packet, and pool functions.

  • Memory functions.

  • File management functions.

  • Timer functions.

  • Interrupt handling functions.

  • Configuration functions.

  • Send flag functions.

  • Spin lock functions.

  • Interlocked functions.

  • Noninterlocked list manipulation functions.

  • DMA-related functions.

The following sections detail the NDIS interface library functions. These functions come directly from the NDIS interface library and not the NIC driver. Windows NT provides and supports all NDIS interface library functions.


A.2.3.2 Functions Called by the Full NIC Driver

The NIC driver calls a number of NDIS interface library functions for assistance in processing. Table A.2.4 defines the main library functions.

Table A.2.4 NIC Driver Functions
Function | Definition
NdisCompleteCloseAdapter | Forwards NIC close data to ProtocolCloseAdapterComplete.
NdisCompleteOpenAdapter | Forwards NIC open data to ProtocolOpenAdapterComplete.
NdisCompleteQueryStatistics | Performs postprocessing for asynchronous completion of a MacQueryGlobalStatistics operation.
NdisCompleteRequest | Forwards request information to ProtocolRequestComplete.
NdisCompleteReset | Forwards reset information to ProtocolResetComplete.
NdisCompleteSend | Forwards send data to ProtocolSendComplete.
NdisCompleteTransferData | Forwards information about a data transfer to ProtocolTransferDataComplete.
NdisDeregisterAdapter | Terminates the registration of a network interface card with the NDIS interface library.
NdisDeregisterAdapter ShutdownHandler | Removes a registered MacShutdown function.
NdisDeregisterMac | Removes the NIC driver.
NdisIndicateReceive | Forwards a receive indication to ProtocolReceive.
NdisIndicateReceiveComplete | Forwards a receive complete indication to ProtocolReceiveComplete.
NdisIndicateStatus | Forwards an indication of changes in network card status to ProtocolStatus.
NdisIndicateStatusComplete | Forwards a status complete indication to ProtocolStatusComplete.
NdisInitializeWrapper | Initializes data structures that the wrapper associates with the NIC driver.
NdisMapFile | Associates a virtual address with an opened file so the NIC driver can access file contents.
NdisOpenFile | Opens a disk file, typically one the NIC driver will download to an intelligent network card.
NdisRegisterAdapter | Registers each network interface card that the NIC driver supports with the wrapper.
NdisRegisterAdapterShutdown Handler | Registers MacShutdown with the operating system so the system can call the function later when it shuts down.
NdisRegisterMac | Provides the wrapper with NIC driver information.
NdisTerminateWrapper | Completes NIC driver removal from the wrapper connection.
NdisUnmapFile | Disassociates a virtual address from a file previously mapped by NdisMapFile.
NdisWriteErrorLogEntry | Writes to an I/O error log.


A.2.3.3 Functions Called by a Transport Driver

When communicating with a NIC driver, a transport driver must make a number of calls to the NDIS interface library. This table shows the main library functions the transport driver calls.

Table A.2.5 Transport Driver Functions
Function | Definition
NdisCloseAdapter | Forwards a network interface card close request to MacCloseAdapter.
NdisDeregisterProtocol | Deregisters the transport driver from the wrapper connection after the NIC driver has unloaded itself.
NdisOpenAdapter | Forwards a network interface card open request to MacOpenAdapter.
NdisRegisterProtocol | At initialization, provides the wrapper with information about the transport driver.
NdisRequest | Forwards a request to query or set NIC driver information to MacRequest.
NdisReset | Forwards a reset request to MacReset.
NdisSend | Forwards a packet send request to MacSend.
NdisTransferData | Forwards a transfer data request to MacTransferData.


A.2.3.4 Buffer, Packet, and Pool Functions

This section lists buffer, packet, and pool functions.


A.2.3.4.1 Buffers

Both NIC and transport drivers allocate and manage virtual memory buffers in the host. The NDIS interface library provides functions that allow the drivers to allocate, construct, and inspect such buffers. These buffers are listed in Table A.2.6.

NDIS 3.0 (ndis.h) uses an NDIS_BUFFER structure to define a variable-length buffer descriptor for a buffer. A buffer descriptor is an MDL that contains virtual and physical addressing information for the buffer, the buffer length, and a pointer to the next buffer descriptor in the chain of buffer descriptors that make up a packet descriptor.

For bus-mastering transfers, a NIC driver needs to send the physical addresses of virtual memory buffers to its network interface card. Under NDIS 3.0, the driver can obtain these addresses by calling NdisStartBufferPhysicalMapping. A portable NIC driver has limited use of physical addresses and sometimes only has knowledge of a virtual address when the network interface card requires a physical address. The operating system must then map the virtual address to an appropriate physical address. The resultant mapping is temporary and the NIC driver must manage it using map registers.

Table A.2.6 Buffer Functions
Function | Definition
NdisAdjustBufferLength | Modifies the length field in an allocated buffer descriptor.
NdisAllocateBuffer | Allocates and initializes a buffer descriptor for virtual memory.
NdisAllocateBufferPool | Allocates and initializes a block of storage for a pool of buffer descriptors.
NDIS_BUFFER_LINKAGE | Retrieves the internal buffer linkage from a buffer descriptor for use by a transport driver.
NdisChainBufferAtBack | Appends a buffer descriptor to a chain of buffer descriptors for a packet descriptor.
NdisChainBufferAtFront | Adds a buffer descriptor at the front of a chain of buffer descriptors for a packet descriptor.
NdisCompleteBufferPhysicalMapping | Indicates to the operating system that a physical address mapping is no longer needed.
NdisCopyBuffer | Creates a buffer descriptor for a specific memory descriptor that a higher network layer passes to the transport driver.
NdisCreateLookaheadBufferFrom SharedMemory | Provides a lookahead buffer, which is a virtual memory copy of shared memory on the network interface card.
NdisDestroyLookaheadBufferFrom SharedMemory | Disassociates a lookahead buffer from shared memory on the network card.
NdisFreeBuffer | Frees a buffer descriptor.
NdisFreeBufferPool | Terminates the use of a buffer descriptor pool.
NdisGetBufferPhysicalAddress | Obtains the physical segments that make up a single buffer. This function is obsolete, and NdisStartBufferPhysicalMapping replaces it.
NdisGetBufferPhysicalArraySize | Determines the maximum number of physical segments in a buffer.
NdisGetNextBuffer | Finds the next buffer descriptor in a buffer descriptor chain.
NdisGetPhysicalAddressHigh | Queries the high 32 bits of a physical address.
NdisGetPhysicalAddressLow | Returns the low 32 bits of a physical address.
NdisQueryBuffer | Queries a specified buffer descriptor.
NdisSetPhysicalAddressHigh | Sets the high 32 bits of a physical address.
NdisSetPhysicalAddressLow | Sets the low 32 bits of a physical address.
NdisStartBufferPhysicalMapping | Starts a mapping that the NDIS interface library uses to associate the virtual address of a buffer with the addresses of its physical segments. Replaces function NdisGetBufferPhysicalAddress that is no longer supported.
NdisUnchainBufferAtBack | Removes the last buffer descriptor from a buffer descriptor chain.
NdisUnchainBufferAtFront | Removes the first buffer descriptor from a buffer descriptor chain.


A.2.3.4.2 Packets

NDIS 3.0 defines a packet as a chain of one or more buffers that composes a network message. The NIC driver or a transport driver can locate the source and destination start locations for various packet copy operations at any offset in any buffer of the associated packet. The NDIS interface library includes functions that NDIS 3.0 drivers can call to allocate, construct, and inspect packets.

A driver forms a packet descriptor by building a chain of buffer descriptors. NDIS 3.0 (ndis.h) describes a packet descriptor by an NDIS_PACKET structure. The descriptor consists of two parts: a public component that a driver can see and a private component that is invisible to the driver. The public component allows a NIC or transport driver to maintain context information about the packet. A driver can use this component to link packets into data transfer queues.

Table A.2.7 Packet Functions
Function | Definition
NdisAllocatePacket | Allocates and initializes a packet descriptor.
NdisAllocatePacketPool | Allocates and initializes storage for a pool of packet descriptors.
NdisCopyFromPacketToPacket | Copies data between two packets.
NdisFreePacket | Returns a packet descriptor to the packet pool.
NdisFreePacketPool | Terminates use of the packet pool.
NdisQueryPacket | Retrieves information about a packet.
NdisRecalculatePacketCounts | Recalculates packet counts and lengths.
NdisReinitializePacket | Reinitializes a packet descriptor to its initial state.

In allocating packets to describe data to send over the network, a transport driver assigns a packet descriptor; allocates and maps a buffer descriptor for each virtually contiguous buffer that contains packet data; chains the buffer descriptors into a packet descriptor; and issues an NdisSend request, passing a pointer to the packet descriptor. The NIC driver can then inspect the packet descriptor and its component buffer descriptors using the packet and buffer functions of the NDIS interface library.

A transport driver also builds a packet descriptor to describe the non-contiguous area into which it wants the NIC driver to copy a packet received from the network. The transport driver can then make a call to NdisTransferData to request the NIC driver to copy data into the area the packet descriptor indicates.


A.2.3.4.3 Pools

A pool is storage from which an NDIS 3.0 driver allocates buffer descriptors or packet descriptors. A pool is a block of non-paged system pool the NDIS interface library wrapper allocates and manages. The wrapper serializes access to the pool using a spin lock. Packet allocation and deallocation can occur at the IRQL DISPATCH_LEVEL.


A.2.3.5 Memory and I/O Functions

The NDIS interface library provides a set of memory functions that translate general operating system requests into OS-specific calls. This allows a standard portable interface that supports the various operating environments for NDIS 3.0 drivers. Memory functions of the NDIS interface library are defined in Table A.2.8.

Table A.2.8 Memory and I/O Functions
Function | Definition
NdisAllocateMemory | Allocates memory for use by a driver.
NdisAllocateSharedMemory | Allocates memory the NIC driver shares with its network interface card.
NdisFlushBuffer | Flushes a buffer allocated as shared memory.
NdisFreeMemory | Frees memory that NdisAllocateMemory allocates.
NdisFreeSharedMemory | Frees memory that NdisAllocateSharedMemory allocates.
NdisGetCacheFillSize | Retrieves the host cache fill size.
NdisMapIoSpace | Maps a physical I/O address into a virtual address for a network card that has memory with a physical address in the host I/O space.
NdisMoveFromMappedMemory | Copies to host memory from memory mapped using NdisMapIoSpace.
NdisMoveMappedMemory | Copies memory data to/from a network card physical I/O address that NdisMapIoSpace has mapped to a virtual address.
NdisMoveMemory | Copies memory data from one location to another.
Ndis MoveToMappedMemory | Copies data from host memory to memory mapped using NdisMapIoSpace.
NdisRawReadPort Functions | Read a single piece of data from an I/O port. Specific functions include: NdisRawReadPortUchar NdisRawReadPortUlong NdisRawReadPortUshort
NdisRawReadPortBuffer Functions | Read a buffer of data from an I/O port. Specific functions include: NdisRawReadPortBufferUchar NdisRawReadPortBufferUlong NdisRawReadPortBufferUshort
NdisRawWritePort Functions | Writes a single piece of data to an I/O port. Specific functions include: NdisRawWritePortUchar NdisRawWritePortUlong NdisRawWritePortUshort
NdisRawWritePortBuffer Functions | Writes a buffer of data to I/O port. Specific functions include: NdisRawWritePortBufferUchar NdisRawWritePortBufferUlong NdisRawWritePortBufferUshort
NdisReadPort Functions | Read a single piece of data from an I/O port. Specific functions include: NdisReadPortUchar NdisReadPortUlong NdisReadPortUshort
NdisReadPortBuffer Functions | Read a buffer of data from an I/O port. Specific functions include: NdisReadPortBufferUchar NdisReadPortBufferUlong NdisReadPortBufferUshort
NdisReadRegister Functions | Read data from non-cached memory. Specific functions are: NdisReadRegisterUchar NdisReadRegisterUlong NdisReadRegisterUshort
NdisUnmapIoSpace | Disassociates a physical I/O address from a virtual address that NdisMapIoSpace has mapped to it.
NdisUpdateSharedMemory | Ensures that data that the NIC driver reads from a shared memory region is fully up to date.
NdisWritePort Functions | Write a single piece of data to an I/O port. Specific functions include: NdisWritePortUchar NdisWritePortUlong NdisWritePortUshort
NdisWritePortBuffer Functions | Write a buffer of data to an I/O port. Specific functions include: NdisWritePortBufferUchar NdisWritePortBufferUlong NdisWritePortBufferUshort
NdisWriteRegister Functions | Write a single piece of data to non-cached memory. Specific functions are: NdisWriteRegisterUchar NdisWriteRegisterUlong NdisWriteRegisterUshort.
NdisZeroMappedMemory | Zeroes out memory that NdisMapIoSpace maps.
NdisZeroMemory | Zeroes out a block of memory.


A.2.3.6 File Management Functions

File management functions of the NDIS interface library allow the NIC driver or a transport to access and manipulate system files. These functions are defined in Table A.2.9.

Table A.2.9 File Management Functions
Function | Definition
NdisCloseFile | Closes a file that was opened with a call to NdisOpenFile.
NdisOpenFile | Opens a disk file, typically one the NIC driver downloads to an intelligent network card.
NdisMapFile | Associates a virtual address with an opened file so the NIC driver can access file contents.
NdisUnmapFile | Disassociates a virtual address from a file previously mapped by NdisMapFile.
NdisCloseFile | Closes a file opened by calling NdisOpenFile.


A.2.3.7 Timer Functions

Table A.2.10 defines the timer functions included in the NDIS interface library. These functions allow the NIC driver or a transport driver to suspend execution for a specified period of time.

Table A.2.10 Timer Functions
Function | Definition
NdisCancelTimer | Cancels a timer object that NdisSetTimer has set.
NdisInitializeTimer | Initializes an object that represents a timer that causes MacTimerFunction to execute after a specific time delay.
NdisSetTimer | Sets a timer object to expire after a specified delay.
NdisStallExecution | Delays execution for a specified time, for example when a network interface card requires a specified wait during a reset.


A.2.3.8 Interrupt Handling Functions

The NDIS interface library provides several interrupt handling functions for use in a multiprocessor environment, which are listed in Table A.2.11. These functions relate to network interface card interrupts to a host processor. Windows NT makes it invalid to call NdisAcquireSpinLock or NdisReleaseSpinLock from within an ISR. For more information about spin locks, see Section A.2.3.11.

Consider what happens if an executing NIC driver thread acquires a spin lock, begins manipulating associated data structures, and is then interrupted by the network controller hardware. In handling the interrupt, the driver enters a section of code in the ISR that needs the data structures that the spin lock protects. Therefore, the driver needs to acquire the lock from within the ISR to inspect the data structures. The driver must essentially acquire the spin lock from itself. If it attempts to do this, an infinite loop occurs, since the interrupt has pre-empted the locked region of code.

To alleviate this problem, the NIC driver calls NdisSynchronizeWithInterrupt from most functions (except MacInterruptServiceRoutine and MacSynchronizeFunction) to synchronize access to data that the ISR shares with non-ISR functions. NdisSynchronizeWithInterrupt raises the IRQL of the executing thread and then calls MacSynchronizeFunction to access shared data. Because of the raised IRQL, the ISR cannot execute on any processor while MacSynchronizeFunction is running.

A typical use of interrupt synchronization by the NIC driver is to protect a global variable that counts the number of interrupts received but not processed. The ISR must increment this variable and a non-ISR NIC driver function must decrement it once the ISR processes an interrupt. The decrement operation must be non-interruptible, but this is not ensured because interrupts are enabled. Therefore, the driver must use interrupt synchronization to protect the variable. Access from the ISR requires no special precautions. However, the driver must access the variable from non-ISR functions by calling NdisSynchronizeWithInterrupt.

Table A.2.11 Interrupt Handling Functions
Function | Definition
NdisInitializeInterrupt | Associates the MacInterruptServiceRoutine and MacDeferredProcessingRoutine functions with a network interface card.
NdisRemoveInterrupt | Stops interrupt reception by MacInterruptServiceRoutine during network interface card removal.
NdisSynchronizeWithInterrupt | Synchronizes access to data shared between non-ISR functions and MacInterruptServiceRoutine.


A.2.3.9 Configuration Functions

A NIC driver configures itself using information in the Configuration Registry and the configuration functions provided by the NDIS interface library, as Table A.2.12 shows. The Configuration Registry module name the driver uses should not conflict with the module names for other drivers. Keyword names are not restricted since keywords for one module are independent of those for another module.

Table A.2.12 Configuration Functions
Function | Definition
NdisCloseConfiguration | Closes a Configuration Registry database that NdisOpenConfiguration has opened.
NdisImmediateReadPciSlotInformation | Reads a specified number of bytes from the configuration space of a specified PCI device.
NdisImmediateWritePciSlotInformation | Writes a specified number of bytes to the configuration space of a specified PCI device.
NdisOpenConfiguration | Opens the Configuration Registry database for a network interface card.
NdisPciAssignResources | Returns a list of resources used by a specified PCI device.
NdisReadBindingInformation | Reads the Configuration Registry to retrieve the name of a lower-level driver that an intermediate driver treats as a network interface card.
NdisReadConfiguration | Reads the value associated with an individual keyword from the Configuration Registry database for a network interface card.
NdisReadEisaSlotInformation | Reads slot information for an EISA network interface card.
NdisReadMcaPosInformation | Reads data from the POS register on an MCA network interface card.
NdisReadNetworkAddress | Reads the software-configured network address of a network interface card from a Configuration Registry database. Driver writers should use the keyword NetworkAddress for network addresses that are software-configurable.
NdisReadPciSlotInformation | Reads a specified number of bytes from the configuration space of a specified PCI device.
NdisWritePciSlotInformation | Writes a specified number of bytes to the configuration space of a specified PCI device.


A.2.3.10 Send Flag Functions

Send flag functions provided by the NDIS interface library allow a transport driver to set send packet header flags that a NIC driver can query. Table A.2.13 defines these functions.

Table A.2.13 Send Flag Functions
Function | Definition
NdisQuerySendFlags | Reads the Flags field of a packet header structure for send information not included in the packet data. The NIC driver can optionally call this function after a MacSend operation has begun.
NdisSetSendFlags | Sets the Flags field of a packet header structure with send information not included in the packet data. A transport driver can optionally call this function before calling NdisSend.


A.2.3.11 Spin Lock Functions

NDIS interface library spin lock functions are described in Table A.2.A.2. These functions deal with initialization, acquisition, release, and freeing of spin locks. In a multiprocessor environment, they address both interprocessor and intraprocessor synchronization at the same time. For a uniprocessor environment, the functions are optimized to ignore interprocessor synchronization. Spin lock functions use executive spin locks; therefore the NIC driver cannot call them if it is running above the IRQL DISPATCH_LEVEL within MacInterruptServiceRoutine or NdisSynchronizeWithInterrupt.

Table A.2.14 Spin Lock Functions
Function | Definition
NdisAcquireSpinLock | Acquires an NDIS interface library spin lock to give the driver exclusive use of resources that the spin lock protects.
NdisAllocateSpinLock | Initializes an NDIS interface library spin lock, setting it to the released state.
NdisDprAcquireSpinLock | Called by MacDeferredProcessingRoutine to acquire a spin lock.
NdisDprReleaseSpinLock | Releases a spin lock the DPR has acquired by calling NdisDprAcquireSpinLock.
NdisFreeSpinLock | Frees a structure that NdisAllocateSpinLock has allocated as an NDIS interface library spin lock.
NdisReleaseSpinLock | Releases an acquired spin lock so that other executing threads can use resources that it protects.


A.2.3.12 Interlocked Functions

The NDIS interface library uses certain functions to process interlocked requests as Table A.2.15 shows. Operation for these functions is the same as acquiring a spin lock, performing a request, and then releasing the lock. However, internal implementation is efficient and may not involve acquiring an actual spin lock. Interlocked functions use executive spin locks; therefore an NDIS 3.0 driver cannot call them if it is running above IRQL DISPATCH_LEVEL.

The driver synchronizes code that calls interlocked functions with other operations that explicitly use spin locks. In other words, it can modify a variable using NdisInterlockedAddUlong in one place, and modify the same variable inside a spin lock acquisition in another place, without the possibility that the two threads will run simultaneously.

Table A.2.15 Interlocked Functions
Function | Definition
NdisInterlockedAddUlong | Adds an increment to a value that a spin lock protects.
NdisInterlockedInsertHeadList | Inserts a structure at the head of a linked list that a spin lock protects.
NdisInterlockedInsertTailList | Inserts a structure at the tail of a linked list that a spin lock protects.
NdisInterlockedRemoveHeadList | Removes a structure from the head of a linked list that a spin lock protects.


A.2.3.13 Noninterlocked List Manipulation Functions

Table A.2.16 defines the NDIS interface library non-interlocked list manipulation functions that the driver must protect with a spin lock. These functions are actually macros that use a LIST_ENTRY structure. They do not interlock operations with spin locks.

Table A.2.16 Noninterlocked List Manipulation Functions
Function | Definition
InitializeListHead | Initializes a linked list head structure.
I nsertHeadList | Inserts a structure at the head of a linked list.
InsertTailList | Inserts a structure at the tail of a linked list.
IsListEmpty | Determines if a linked list is empty.
RemoveEntryList | Removes a structure from a linked list.
RemoveHeadList | Removes a structure from the head of a linked list.


A.2.3.14 DMA-Related Functions

A NIC driver calls DMA-related functions of the NDIS interface library for DMA operations between the host and the network interface card. These functions support maximized portability so the driver can run on both x86 and RISC-based architectures. Table A.2.17 defines these functions.

Table A.2.17 DMA-Related Functions
Function | Definition
NdisAllocateDmaChannel | Sets up a DMA channel for future DMA operations.
NdisCompleteDmaTransfer | Indicates that a DMA transfer has completed and resets the host DMA controller for further transfers.
NdisFreeDmaChannel | Frees a DMA channel that NdisAllocateDmaChannel has allocated.
NdisReadDmaCounter | Retrieves the number of bytes remaining in the current DMA transfer.
NdisSetupDmaTransfer | Prepares the host DMA controller for a DMA transfer.


A.2.4 Filter Libraries

Filter libraries are special sets of NDIS interface library wrapper functions that route a packet from the NIC driver to specific bound transport drivers. Each filter library maintains a transport database for the associated network, Ethernet, Token Ring, or FDDI. The database associates each network interface card binding with a list of transport driver addresses. The NIC driver must indicate each packet received through the filter library appropriate to the network. Then the filter library processes the received packet and passes it on to each transport driver that has an address entry matching the packet destination address. For more information about NIC driver action functions, see Section A.2.1.


A.2.4.1 Ethernet Filter Library Functions

Table A.2.18 defines the functions included in the Ethernet filter library (efilter.c). In the Network Driver Reference, Chapter 2, Section 2.2 defines several filter library macros. A NIC driver can access these functions and macros by including the efilter.h file.

The driver must contain three action functions the Ethernet filter library functions can call back: MacChangeFilter, MacChangeEthAddresses, and MacCloseAction. For more information about these functions, see Section A.2.1. The NIC driver registers these functions in a call to the Ethernet filter library function EthCreateFilter.

Table A.2.18 Ethernet Filter Library Functions
Function | Definition
EthChangeFilterAddresses | Changes the multicast address list for a binding filter in the Ethernet filter library database.
EthCreateFilter | Creates and initializes the Ethernet filter library database associated with one network interface card.
EthDeleteFilter | Deletes memory associated with the Ethernet filter library database.
EthDeleteFilterOpenAdapter | Deletes binding filter information for a network interface card from the Ethernet filter library database.
EthFilterAdjust | Adjusts the Ethernet filter library database.
EthFilterIndicateReceive | Indicates a received packet to all Ethernet filter library database bindings associated with an Ethernet address.
EthFilterIndicateReceiveComplete | Indicates to all bound transport drivers that EthFilterIndicateReceive has returned.
EthNoteFilterOpenAdapter | Adds a binding filter to the Ethernet filter library database.
EthNumberOfOpenFilterAddresses | Counts the number of Ethernet filter library database multicast addresses in a binding filter.
EthQueryGlobalFilterAddresses | Queries the Ethernet filter library database for a list of multicast addresses associated with a particular network interface card.
EthQueryOpenFilterAddresses | Queries the Ethernet filter library database for a list of multicast addresses associated with a binding.
EthShouldAddressLoopBack | Determines if the NIC driver should place a packet on the network card logical adapter loopback queue to avoid transmitting the packet on the network.


A.2.4.2 Token Ring Filter Library Functions

Token Ring filter library (tfilter.c) functions are defined in Table A.2.19. In the Network Driver Reference, Chapter 2, Section 2.2 defines several filter library macros. The NIC driver can access these functions and macros by including tfilter.h in its code.

The driver must contain four NIC driver action functions for the Token Ring filter library to call back: MacChangeGroup, MacChangeTrAddress, MacChangeFilter, and MacCloseAction. For more information on these functions, see Section A.2.1. The NIC driver registers these functions in a call to the Token Ring filter library function, TrCreateFilter.

Table A.2.19 Token Ring Filter Library Functions
Function | Definition
TrChangeFunctionalAddress | Changes the overall functional address for a network interface card.
TrChangeGroupAddress | Changes the overall network card group address associated with a Token Ring binding filter.
TrCreateFilter | Creates and initializes the Token Ring filter library database associated with a network interface card.
TrDeleteFilter | Deletes memory associated with the Token Ring filter library database.
TrDeleteFilterOpenAdapter | Deletes network card binding filter information from the Token Ring filter library database.
TrFilterAdjust | Adjusts the Token Ring filter library database.
TrFilterIndicateReceive | Indicates a received packet to all Token Ring filter library database bindings associated with a Token Ring address.
TrFilterIndicateReceiveComplete | Indicates to all bound transport drivers that TrFilterIndicateReceive has returned.
TrNoteFilterOpenAdapter | Adds a binding to the Token Ring filter library database.
TrShouldAddressLoopBack | Determines if the NIC driver should place a packet on the network card logical adapter loopback queue to avoid transmitting the packet on the network.


A.2.4.3 FDDI Filter Library Functions

Table A.2.20 defines FDDI filter library (ffilter.c) functions. In the Network Driver Reference, Chapter 2, Section 2.2 defines several filter library macros. The NIC driver can access these functions and macros by including ffilter.h in its code.

The driver must contain three NIC driver action functions that FDDI filter library functions call back: MacChangeFddiAddresses, MacChangeFilter, and MacCloseAction. For more information about these functions, see Section A.2.1. The NIC driver registers these functions in a call to the FDDI filter library function, FddiCreateFilter.

Table A.2.20 FDDI Filter Library Functions
Function | Definition
FddiChangeFilterShortAddresses | Changes the multicast short address list for a binding filter in the FDDI filter library database.
FddiCreateFilter | Creates and initializes the FDDI filter library database associated with a network interface card.
FddiChangeFilterLongAddresses | Changes the multicast long address list for a binding filter in the FDDI filter library database.
FddiDeleteFilter | Deletes memory associated with the FDDI filter library database.
FddiDeleteFilterOpenAdapter | Deletes network card binding filter information from the FDDI filter library database.
FddiFilterAdjust | Adjusts the FDDI filter library database.
FddiFilterIndicateReceive | Indicates a received packet to all FDDI filter library database bindings associated with an FDDI address.
FddiFilterIndicateReceiveComplete | Indicates to all bound transport drivers that FddiFilterIndicateReceive has returned.
FddiNoteFilterOpenAdapter | Adds a binding filter to the FDDI filter library database.
FddiNumberOfOpenFilterLongAddresses | Counts the number of FDDI filter library database multicast long addresses in a binding filter.
FddiNumberOfOpenFilterShortAddresses | Counts the number of FDDI filter library database multicast short addresses in a binding filter.
FddiQueryGlobalFilterLongAddresses | Queries the FDDI filter library database for a list of multicast long addresses associated with a network interface card.
FddiQueryGlobalFilterShortAddresses | Queries the FDDI filter library database for a list of multicast short addresses associated with a network interface card.
FddiQueryOpenFilterLongAddresses | Queries the FDDI filter library database for a list of multicast long addresses associated with a binding.
FddiQueryOpenFilterShortAddresses | Queries the FDDI filter library database for a list of multicast short addresses associated with a binding.
FddiShouldAddressLoopBack | Determines if the NIC driver should place a packet on the network card logical adapter loopback queue to avoid transmitting the packet on the network.


A.3 Full NIC Driver Operations

This section describes full NIC drivers that comply with NDIS 3.0. If you are providing a network driver, it is not necessary for you to write a full NIC driver because the current version of Windows NT supports an NDIS 3.0 miniport NIC driver. It is recommended that you provide a miniport driver instead of the full NIC driver to save time. You can develop fewer routines in your miniport driver because there is more support for common routines in the NDIS interface library.

The NDIS library maintains a descriptor block for each NIC driver or transport driver that registers itself on a network. Depending on the type of driver, the descriptor block contains the addresses of the NIC driver upper-edge functions or the transport driver lower-edge functions. With the exception of interrupt service routines and media access control synchronization functions, NIC driver processing occurs at or below the IRQL DISPATCH_LEVEL. This chapter describes the main operations involved, which are:

  • Initializing wrapper and registering drivers.

  • Registering a network interface card.

  • Opening a network interface card.

  • Sending data.

  • Indicating and transferring received data.

  • Indicating status.

  • Querying and setting NIC driver capabilities (per binding).

  • Querying global statistics (per network interface card).

  • Closing a network interface card.

  • Resetting a network interface card.

  • Removing a network interface card.

  • Unloading and deregistering drivers and terminating wrapper.

For help in following the descriptions, refer to the Network Driver Reference. Chapter 1 of the reference defines the upper-edge NIC driver functions; Chapter 2 defines the NDIS library functions; Part I, Chapter 4 details NDIS 3.0 OIDs; Chapter 5 defines NDIS 3.0 status codes; and Chapter 6 describes structures and enumerations used by NIC drivers.


A.3.1 Initializing Wrapper and Registering Drivers

To initialize the NDIS interface library, Windows NT must ensure the Configuration Registry has a database for each NDIS 3.0 driver, as Figure A.3.1 shows. Using the binding information in the registry database, the operating system creates a driver object and calls the NIC driver at its DriverEntry function. In turn, the full NIC driver calls NdisInitializeWrapper, to which it passes the address of the driver object and the associated Configuration Registry path. The NDIS interface library function can then perform wrapper initialization chores.

After NdisInitializeWrapper returns, the full NIC driver calls NdisRegisterMac to register itself as a driver on the network. This function stores the addresses of driver-supplied dispatch functions in the driver object. Function parameters are the entry points of all the upper-edge functions for the full NIC driver.

When all NIC drivers for the network have registered themselves, the operating system loader calls the DriverEntry function of a transport driver. The transport driver then makes a call to NdisRegisterProtocol in the NDIS interface library to register itself with the NDIS interface library. Each transport driver must be bound to a network interface card so that it can send and receive packets on the network. For details of network binding for Windows NT, refer to the Part 7, Network Component Binding.

At this point, the library allocates a block of non-paged pool memory for driver information, including addresses of event handlers and capability data, and writes the transport driver a handle to this information. The NDIS interface library includes a reference count in the transport driver block. It initializes the count to one and increments it any time the transport driver opens a network interface card. The library decrements the count when the transport driver closes a network interface card or when the driver calls NdisDeregisterProtocol. When the count reaches zero, the library frees the transport driver block.

Figure A.3.1 Initializing Wrapper and Registering Drivers

The operating system repeats this procedure for all transport drivers in the Configuration Registry. When all transport drivers are registered, they are ready to receive packets on the network. First, however, the NDIS interface library must register all network interface cards. For more information about registering network interface cards, see Section A.3.2.


A.3.2 Registering Network Interface Cards

After driver registration, the NDIS interface library must register network interface cards to each NIC driver on the network. As Figure A.3.2 shows, the library calls MacAddAdapter for each network interface card. The NIC driver reads the Configuration Registry database to determine parameters for the network card, using calls to NdisOpenConfiguration, NdisReadConfiguration, and NdisCloseConfiguration. For additional configuration information about specialized network interface cards, EISA-based drivers can call NdisReadEisaSlotInformation, MCA-based drivers can call NdisReadMcaPosInformation, and PCI-based drivers can call NdisReadPciSlotInformation.

When the NIC driver has the appropriate configuration data, it makes a call to NdisRegisterAdapter for the associated network interface card and its logical adapter. This allows the NDIS interface library wrapper to recognize the network card name when a transport driver opens the card later. For more information about this task, see Section A.3.3. Upon entry to NdisRegisterAdapter, the wrapper calls IoCreateDevice to create a device object representing the network interface card, with an object name that matches the network card name.

Figure A.3.2 Registering a network interface card

The device object includes a NIC driver-specific extension that contains a pointer to the NIC driver descriptor block; a list entry that links the network interface card into the NIC driver list of registered network interface cards (anchored in the NIC driver descriptor block); a list head for the list of open bindings to the network interface card; and the context passed to NdisRegisterAdapter. The function gives the NIC driver the address of the device object extension in an NDIS NIC handle.

After NdisRegisterAdapter returns, the NIC driver can further initialize the network interface card in calls to other NDIS interface library functions, such as NdisAllocateMemory, NdisMapIoSpace, and NdisAllocateDmaChannel. When it has completely initialized the network interface card, the NIC driver must initialize interrupts by calling NdisInitializeInterrupt.

When registration for one network interface card is complete, the NDIS interface library completes the preceding procedure for the remainder of the network interface cards on the network. Then each bound transport driver can communicate with its remote node peers using the network interface card to which it is bound, and the associated NIC driver. The first action the transport driver must take is to open its network interface card. For more information about opening a network interface card, see SectionA.3.3.


A.3.3 Opening a Network Interface Card

As Figure A.3.3 shows, a transport driver can open any network interface card to which it is bound by calling MacOpenAdapter through NdisOpenAdapter. The transport driver must furnish a list of the NIC media types that it can use and a network interface card binding context. Typically the driver obtains this information from the Configuration Registry during driver initialization.

A NIC driver for a new network interface card can handle the network type of the card as well as possibly emulate a more popular network type, such as Ethernet. When the transport driver passes a list of NIC media types, NdisOpenAdapter calls MacOpenAdapter to allow the NIC driver to select a medium. NdisOpenAdapter then accepts the chosen name and notifies the operating system that the appropriate network interface card is being opened.

Figure A.3.3 Opening a network interface card

NdisOpenAdapter writes an NDIS binding handle to the transport driver. The transport driver must keep track of this handle, which is its only link to the NIC driver and which the transport driver must use in all future calls to the NDIS interface library.

NdisOpenAdapter calls MacOpenAdapter to inform the NIC driver that a transport driver has been bound to the associated network interface card. In response, the NIC driver activates the network interface card if it is not already physically enabled and then writes a binding context that the NDIS interface library wrapper stores in the NIC binding block. The NIC driver will not disable this network interface card until the reference count of open bindings returns to zero.

MacOpenAdapter completes synchronously if it finishes the NIC open operation before it returns. The function returns a status code other than NDIS_STATUS_PENDING in this case. Alternatively, MacOpenAdapter can return NDIS_STATUS_PENDING, which indicates that the function is asynchronous and cannot complete its operation before returning. To indicate completion, the NIC driver must call ProtocolOpenAdapterComplete through NdisCompleteOpenAdapter. The NDIS interface library links the binding block into the NIC and transport driver block binding lists.

ProtocolOpenAdapterComplete makes appropriate indications to the transport driver bound to the open network interface card. The driver can then request a number of network activities involving the network interface card, such as sending data, copying data received by the network interface card, querying and setting NIC driver capabilities, and closing the network interface card. For more information about these activities, see Sections A.3.4 through A.3.9.


A.3.4 Sending Data

In preparation for sending data to the network interface card, a transport driver must create a packet that consists of data and a header. At this time, the driver can make an optional call to NdisSetSendFlags to set the Flags field in the packet header to pass additional send information to the NIC driver as Figure A.3.4 shows. After this, the transport driver is ready to call NdisSend, which forwards the call to MacSend in the NIC driver.

In its send request to MacSend, the transport driver instructs the NIC driver to transmit the passed packet through the network interface card onto the network. MacSend translates the packet into data frames and carries out the send operation. If necessary, the NIC driver can call NdisQuerySendFlags to retrieve additional send information.

MacSend completes synchronously if it finishes the send operation before it returns. The function returns a status code other than NDIS_STATUS_PENDING for synchronous operation.

Like MacOpenAdapter, MacSend can also be asynchronous if it cannot complete the send operation before it returns. In this case, it returns NDIS_STATUS_PENDING to the transport driver to indicate that the packet has been queued for transmission. The NIC driver eventually calls ProtocolSendComplete through NdisCompleteSend to allow postprocessing. When ProtocolSendComplete returns, the transport driver regains ownership of the packet.

Figure A.3.4 Sending Data


A.3.5 Indicating and Transferring Received Data

When the network interface card receives a data packet over the network, the associated NIC driver issues a call to NdisIndicateReceive as Figure A.3.5 shows. In this call, the driver includes the NDIS binding context for delivery of indications to the transport driver. NdisIndicateReceive passes this context to ProtocolReceive in the transport driver. The transport driver can use any context value that it requires for associating incoming indications with specific network interface cards that it has opened.

The transport driver initiates the transfer of the data packet by calling MacTransferData through NdisTransferData. The NIC driver responds by using MacTransferData to copy the received data frames into the packet structure that the transport driver provides in its call.

MacTransferData is another potentially asynchronous function. If the function completes synchronously, it simply writes the packet to the transport driver and returns a status other than NDIS_STATUS_PENDING. If the function is unable to complete the transfer data operation before it returns, it writes NDIS_STATUS_PENDING. The NIC driver later calls NdisCompleteTransferData to forward the call to ProtocolTransferDataComplete for postprocessing and reception of the copied data frame.

Once all incoming data frames have been received by the network interface card, the NIC driver calls NdisIndicateReceiveComplete. The NDIS interface library then calls ProtocolReceiveComplete to indicate to the transport driver that post-receive processing can be performed.

Figure A.3.5 Indicating and transferring received data


A.3.6 Indicating Status

When the NIC driver needs to indicate a status change to a bound transport driver, it issues a call to NdisIndicateStatus as Figure A.3.6 shows. The NIC driver passes the NDIS binding context; its only link to the transport driver. NdisIndicateStatus forwards the call to ProtocolStatus, which interprets the general status code. Then the transport driver or the Configuration Manager can log the status change and/or take corrective action.

To stop indicating status changes, following one or more NdisIndicateStatus calls, the NIC driver calls NdisIndicateStatusComplete. This library function forwards the call to ProtocolStatusComplete so the transport driver can perform any necessary postprocessing.

Figure A.3.6 Indicating Status


A.3.7 Querying and Setting NIC driver Capabilities

For querying and setting NIC binding information, the transport driver must fill in an NDIS_REQUEST structure and pass it in a call to MacRequest through NdisRequest as Figure A.3.7 shows. The driver places an OID in the structure for an object in the NIC driver MIB that it wants to query or set. Depending on the substructure of NDIS_REQUEST the transport driver has filled in, MacRequest either queries or sets the object that corresponds to the transport driver-provided OID. The function fills in related fields of NDIS_REQUEST and passes the structure back to the transport driver, returning an appropriate status code.

MacRequest is another potentially asynchronous function. If it behaves synchronously, the function returns immediately with a status code other than NDIS_STATUS_PENDING. If it behaves asynchronously, the function returns NDIS_STATUS_PENDING, and the NIC driver later completes the request operation by a call to NdisCompleteRequest. This function forwards the call to ProtocolRequestComplete for postprocessing.


A.3.8 Querying Global Statistics

The NIC driver must be prepared to handle requests for global NIC driver statistics that concern objects in the MIB. The NDIS interface library wrapper itself, representing a network management entity instead of a transport driver bound to a network interface card, calls MacQueryGlobalStatistics to make a statistics request, as Figure A.3.8 shows. This function is similar to MacRequest for an information query, but should not be confused with the other function. MacQueryGlobalStatistics is confined particularly to statistical queries.

MacQueryGlobalStatistics can be asynchronous. If it does require postprocessing, it returns NDIS_STATUS_PENDING. When ready to complete the query operation, the NIC driver calls NdisCompleteQueryStatistics.

Figure A.3.7 Querying and setting NIC driver capabilities (per binding)

Figure A.3.8 Querying global statistics (per network interface card)


A.3.9 Closing Network Interface Cards

As Figure A.3.9 illustrates, a transport driver unbinds itself from a network interface card by calling MacCloseAdapter through NdisCloseAdapter, with the binding block indicated by the NDIS binding handle. The NIC driver uses MacCloseAdapter to close the NIC binding, cancel the ISR, and prevent further requests to the network interface card. Because multiple transport drivers can have open bindings to one network interface card, the NIC driver keeps a reference count of open handles. If any requests remain on a binding when a transport driver calls MacCloseAdapter, the function returns NDIS_STATUS_PENDING, regardless of the state of the network interface card.

If MacCloseAdapter completes synchronously with either a success status code or an error code, the function unlinks the binding block from the NIC and transport driver block binding lists and dereferences these blocks. It then deallocates the binding block and calls the operating system to dereference the file object representing the open network interface card. The operating system ultimately deletes the file object and returns control to the transport driver.

MacCloseAdapter can also be asynchronous, returning NDIS_STATUS_PENDING. In this case, the NIC driver eventually calls NdisCompleteCloseAdapter, which forwards the call to ProtocolCloseAdapterComplete for postprocessing.

After the NIC driver has closed the NIC binding, the transport driver cannot submit further requests on this binding. It cannot call NdisReset even if it concludes the NIC close operation has suspended and hung.

Figure A.3.9 Closing network interface cards

It is possible for a NIC driver to close a binding for a known network interface card malfunction. The driver does this by calling ProtocolStatus through NdisIndicateStatus, passing an NDIS_STATUS_CLOSING general status code. Although this indication does not change the behavior of the transport driver or the NIC driver, the transport driver must call NdisCloseAdapter as soon as possible after receiving it. The NIC driver returns NDIS_STATUS_ADAPTER_NOT_OPEN for any requests made by the transport driver between the NDIS_STATUS_CLOSING indication and the call to NdisCloseAdapter.


A.3.10 Resetting a Network Interface Card

When a transport driver needs to reset a network interface card, as Figure A.3.10 shows, it calls MacReset through NdisReset with network interface card information provided in the NDIS binding handle. MacReset instructs the NIC driver to issue a hardware reset to the network interface card. The driver also sets its own software state.

During the reset operation, the NIC driver calls ProtocolStatus (through NdisIndicateStatus) twice for each bound transport driver, including the original caller. The first call is at the beginning of the reset, and NdisIndicateStatus accordingly delivers the general status code NDIS_STATUS_RESET_START. When the NIC driver again calls the reset functions at the end of the reset operation, NdisIndicateStatus delivers the general status code NDIS_STATUS_RESET_END.

When the NIC driver no longer needs to indicate status changes, it calls ProtocolStatusComplete through NdisIndicateStatusComplete for each bound transport driver. ProtocolStatusComplete allows each transport driver to do postprocessing for the status operation.

MacReset completes synchronously with a success or error status code, or asynchronously with NDIS_STATUS_PENDING. If the function is asynchronous, the NIC driver later calls ProtocolResetComplete through NdisCompleteReset to request postprocessing.

Figure A.3.10 Resetting a network interface card


A.3.11 Removing a Network Interface Card

When all NIC bindings have been closed, the NDIS interface library initiates removal of the associated network interface card, as Figure A.3.11 shows. To accomplish this task, the library calls MacRemoveAdapter in the NIC driver. During the network interface card removal operation, the driver removes associated resources by calling NdisRemoveInterrupt and rejects all incoming requests with NDIS_STATUS_ADAPTER_REMOVED. After resource removal, the driver calls NdisDeregisterAdapter, through which the NDIS interface library informs the operating system that the network interface card has been removed. For more information about closing NIC bindings, see Section A.3.9.

If the NDIS interface library has removed all network interface cards for a particular NIC driver, it can unload and deregister the driver. For more information about this operation, see Section A.3.12.

Figure A.3.11 Removing a Network Interface Card


A.3.12 Unloading and Deregistering Drivers

When the NDIS interface library calls MacUnload, as Figure A.3.12 shows, the function frees allocated resources and decrements the reference count in the NIC driver block to zero. MacUnload then calls NdisDeregisterMac to remove the NIC driver from the NDIS interface library wrapper connection. MacUnload passes a network interface card handle to the NIC driver descriptor block and NdisDeregisterMac sets the driver state to closing to prevent the NIC driver from registering additional network interface cards.

If the NIC driver is the last driver being unloaded and the network is shutting down, MacUnload makes a final call to NdisTerminateWrapper to close the NDIS interface library wrapper. Ndis TerminateWrapper is also called if, during driver initialization, NdisInitializeWrapper is successful but NdisRegisterMac fails to register any network interface cards.

When transports wish to unload, they call NdisDeregisterProtocol to remove themselves from the wrapper connection. For each call, this function decrements the reference count included in the transport driver block. When the count reaches zero, the transport driver block is freed.

Figure A.3.12 Unloading and deregistering drivers and terminating wrapper


A.4 Writing a Full NIC Driver

This chapter describes full NIC drivers that comply with NDIS 3.0. If you are providing a NIC driver, it is not necessary to write a full NIC driver because the current version of Windows NT® supports miniport NIC drivers. It is recommended that you provide a miniport driver instead of the full NIC driver to save time. Fewer routines need to be developed in a miniport driver because there is more support for common routines in the NDIS library.

The current DDK only contains sample miniport NIC drivers. Samples of full NIC drivers are only available as part of the older Windows NT 3.1 DDK.

The Windows NT 3.1 DDK contains several sample full NIC drivers for use in developing your own driver. Although requirements vary depending on the network interface card the NIC driver controls, many basic functions (such as initialization, sending data, receiving data, interrupt handling, and deactivation) are common to all full NIC drivers. Writing a NIC driver from scratch can be very difficult and time-consuming. By modifying an existing driver you can take advantage of the development effort invested in a working product. Most of your programming endeavor can then be spent in code modifications instead of full-scale development.

This chapter examines a full NIC driver for a SONIC Ethernet chip in detail and offers suggestions for adapting the code to your network interface card. It includes some general programming considerations to note before you begin. The 3.1 DDK Release Notes provide building and loading instructions to accompany these details of sample code modification.


A.4.1 Programming Considerations

The following sections discuss programming issues that must be considered by a driver that transmits data from or receives data into memory that is shared between a DMA-capable NIC and the driver that manages it. As well, the section briefly describes synchronization issues.


A.4.1.1 Handling Transmit Data

When writing a NIC driver, ensure that any data the card reads is actually present in physical memory before the read begins. Have your driver flush cached memory for transmit data by calling NdisFlushBuffer. The cache is divided into blocks of a fixed cache fill size, and the driver should flush an entire block at a time. The driver does not need to flush all cached data immediately after writing it, but should flush it before accessing the network interface card. Since cache line tearing is not a problem for transmit data, the driver can flush the data without concern for cache fill size.

For example, the NIC driver usually gives a busmastering network interface card the physical addresses of packet segments. Most of these segments are from the NDIS_PACKET structure that a transport driver passes to MacSend in the NIC driver. The NIC driver describes the segments to the network interface card, and then releases the packet to the card for transmission on the network. The driver must flush the data at some point before it relinquishes the packet, but the actual timing is not important. The NIC driver can flush cached data upon entry to MacSend, or immediately before it releases the packet.

When using noncached memory for transmit data, make sure the NIC driver calls NdisWriteRegisterXxx. This function ensures that the data is in physical memory before it returns. Calling the function allows sequential writes to memory in the order in which they occur and consecutive writes to the same memory location, resulting in two distinct writes to memory. This is especially important when the driver is accessing memory that corresponds to registers on the network interface card. For example, if the reset register requires the NIC driver to write first a zero and then a one before a reset, the driver must not merge these two writes.

Note Generally it is preferable to allocate cached memory for transmit data, since the operating system can have trouble allocating noncached memory. Always assume that memory passed in from a transport driver is cached. Have the NIC driver use cached memory as memory to which it writes only rarely, and flush the memory when writing. To simplify its logic, the driver may use noncached memory as memory to which it writes often.


A.4.1.2 Handling Receive Data

When handling receive data in the NIC driver, ensure that any memory receiving data from the netcard is not present in the cache before a read begins. If the memory is in the cache at the time of the read, the driver obtains the old cache data instead of the new data in physical memory. In addition, if the processor flushes the cache at the wrong time, it overwrites the new data with the old values.

Write your driver to flush cached memory with a call to NdisFlushBuffer. For receive data, set the direction parameter used in the call to FALSE, indicating a read operation. It is critical that you flush cached memory before the network interface card begins writing data into it. Be sure that the driver does not access memory in any way until the network interface card has completed its write. This ensures that memory is not in the cache, and forces subsequent reads to go to physical memory, which contains the current data.

Note For a buffer that does not begin and end on an address that is a multiple of the cache fill size, do not have your driver call NdisFlushBuffer with the write-to-device parameter set to FALSE.

Cache line tearing can occur during the flushing of data that covers one part of a cache block while another process in the operating system owns the rest of the block. Tearing is a problem with receive data in cached memory. It is illegal to make a DMA into memory that is on a cache block that the NIC driver does not completely own. If the driver makes such an access, the owner of the rest of the cache block can flush it while the DMA is in progress, overwriting the new data with stale data from the cache.

You can generally avoid cache line tearing, since the NIC driver allocates memory that the netcard uses to receive data. The driver also ensures that receive buffers do not cross cache lines. Memory the NDIS interface library wrapper allocates with the NDIS_MEMORY_CACHE_ALIGNED flag does not share cache blocks with any other memory. If your driver divides allocated memory, use NdisGetCacheFillSize to ensure that each piece does not share cache blocks. The driver can obtain the cache fill size using this function, and then examine virtual or physical addresses of the memory with the assumption that relevant low bits in memory are the same for virtual and physical addresses.

You must be careful that a netcard performs DMA as a slave device. For receive data, DMA occurs during MacTransferData processing. The NIC driver cannot control the alignment of the buffers that a transport driver passes in. If the NIC driver discovers that the beginning or end of a buffer is not on a cache line, it must DMA-transfer these pieces into a private buffer and then copy them. This is the only way to guarantee that the NIC driver reads data correctly.

When using noncached memory for receive data, make sure that the NIC driver calls the appropriate NdisReadRegisterXxx function for the data type (UCHAR, ULONG, USHORT). These functions ensure the data is in physical memory before they return. They also ensure sequential reads to memory in the order in which they occur and consecutive reads to the same memory, resulting in two distinct reads to memory. This is especially important when the driver accesses memory that corresponds to netcard registers.

Your driver should allocate any receive data that multiple transport drivers can read several times during indications as cached. In general, ownership of receive buffers is clear. The NIC driver can use cached buffers as long as it flushes them just before releasing them. This does not work if releasing the buffers involves writing to them, unless the ownership bits are not on the same cache block as the received data. A driver reads other receive data (such as receive descriptors) only once; so the NIC driver can allocate associated memory as noncached.

The NIC driver must flush receive data in multiples of the cache fill size. Some netcards receive consecutive packets into immediately adjacent memory. The end of one packet may occupy the same cache block as the beginning of the next. Therefore, the driver must allocate such memory as noncached because reading the first packet may cause stale cache data for the start of the second one.


A.4.1.3 Memory Mapping for Physical Addresses

Memory mapping allows the NDIS interface library to associate the virtual address of a buffer with the addresses of the physical segments that make up the buffer. At initialization, your NIC driver must specify the number of map registers needed and, for each mapping, the map register for NdisRegisterAdapter to use. For memory mapping, the driver should first call NdisStartBufferPhysicalMapping. The physical addresses that this function writes are valid for netcard use only until the driver finishes the mapping by calling NdisCompleteBufferPhysicalMapping. Through the virtual address, the driver can only access data the netcard copies into operating system memory after the mapping is complete.

Note An NDIS 3.0 NIC driver cannot allocate memory from its ISR or DPR.


A.4.1.4 Using Shared Memory

Use of cached memory that is shared between a NIC driver and its netcard requires special precautions. Windows NT does not ensure execution of the NDIS interface library with an I/O-coherent cache, since a netcard can access physical memory directly while the NIC driver accesses memory through the cache. This can cause differences in what the driver and the netcard detect in memory, even if they look at the same location.

Use of noncached shared memory avoids most of the problems mentioned for cached shared memory. However, when data written to physical memory must use a small write queue, even noncached data might not appear in physical memory immediately. Additionally, there are cases when using noncached memory is not recommended or is not possible. For example, data the NIC driver reads repeatedly should always be cached to improve performance. The NIC driver should also use cached memory for any data that a transport driver passes in.

To help eliminate shared memory problems, use an NdisAllocateSharedMemory call in your NIC driver to allocate memory for permanent sharing between the netcard and the driver. This function writes a virtual address and a physical address for the shared memory. The physical address is valid until a call to NdisFreeSharedMemory frees the memory. To access the shared memory, your driver must call NdisUpdateSharedMemory.


A.4.1.5 Interrupt Handling

A driver that can run on an SMP machine must be designed such that shared resources are protected when concurrently executing threads may access the same resources simultaneously.

If two driver functions (or the same function executing on two CPUs) execute at the same IRQL and they share data, that data may be protected with a spin lock. The spin lock is acquired by calling NdisAcquireSpinLock and released by calling NdisReleaseSpinLock.

If a driver manages a NIC that interrupts, and any nonISR function of that driver shares data with the ISR of that driver, access to the shared data must be synchronized. Before a nonISR function accesses the shared data, it must call NdisSynchronizeWithInterrupt which synchronizes execution of a caller-supplied routine with execution of the ISR. NdisSynchronizeWithInterrupt temporarily raises the IRQL of the nonISR function supplied routine to that of the ISR so that the access to the shared resources can be serialized. The driver must be designed so that the synchronization routine executes as quickly as possible.


Appendix B STREAMS Support in Windows NT

STREAMS is an environment for network transport protocols that facilitates the porting of existing STREAMS-based stacks from other environments to Windows NT. It is a set of tools for the development of communication services. In the STREAMS environment, one or more drivers or modules are plumbed together to form a network transport provider.

Figure B.1 The STREAMS Environment

STREAMS provides:

  1. A kernel-mode STREAMS environment.

  2. A Transport Provider Interface (TPI)-compatible kernel-level interface for the upper edge of the STREAMS stack and translates this interface into the native NT transport interface, the Transport Driver Interface (TDI).

  3. A Data Link Provider Interface (DLPI)-compatible kernel-level interface for the lower edge of the STREAMS stack and translates this interface into the native NT data link interface, Network Driver Interface Specification version 3.0(NDIS 3.0).

  4. Additional utility functions to ease porting, including UNIX® kernel time and timer functions.
Further Readings on Streams
  1. UNIX System V Release 4 Programmer’s Guide: STREAMS. ISBN 0-13-947003-4. Prentice-Hall. 1990.

  2. Parallel STREAMS: A Multi-Processor Implementation. Arun Garg. Sequent Computer Systems. March 27, 1990.

  3. Dynix/PTX STREAMS Kernel Programming Guide. Part No. 1003-48627-00. Sequent Computer Systems. 1989.

  4. A STREAMS-based Data Link Provider Interface - Version 2. UNIX International. August 20, 1991.

Sections B.1 through B.7 of this appendix provide a functional specification of the Streams environment as supported under Windows NT.


B.1 Stack Definition and Levels of Parallelism

The Windows NT kernel environment differs fundamentally from the traditional UNIX kernel. In Windows NT, a thread executing in the kernel can be preempted, whereas a thread executing within the UNIX kernel can be interrupted, but not preempted. In Windows NT, many threads may also execute concurrently in the kernel on several processors. For Windows NT, a STREAMS driver must be declared in the qi_mplevel member of its qinit structures to be either:
Fully parallel (default) | The service procedure of a queue can execute concurrently with the service procedure of any other queue. The put procedure of a queue can execute concurrently with the service procedure of that queue and the service and put procedures of any other queue. Multiple instances of the put procedure of a queue may also execute concurrently.
Subsystem-parallel | Only one service or put procedure from a subsystem can execute at any given instant. The procedures of other subsystems or of fully parallel stacks may execute concurrently. Execution of these other procedures is serialized with the subsystem if they enter it by way of a put call.


B.1.1 Fully Parallel Stacks

A fully parallel stack runs in a preemptive multiprocessor environment. It uses locks to protect its integrity, and is prepared for:

  1. The service procedures of two or more of its queues running concurrently

  2. Its service and put procedures running concurrently with and/or preempting each other

  3. Multiple instances of an open or close procedure running concurrently on different queues.

  4. All put procedures to be called at DISPATCH_LEVEL processor priority. At this priority level, no page faults may be taken and procedures may not block. All dynamic memory should be allocated from non-paged pool.

STREAMS limits service procedures to one instantiation per queue at any given moment. For optimal throughput, a stack should do as much work as possible in its put procedures.

Service, open, and close procedures are guaranteed.

Additionally, open and close procedures are serialized along a stream. Thus, at any particular time, only one module or driver along that stream is executing an open or close procedure.


B.1.2 Subsystem-Parallel Stacks

A subsystem-parallel stack requires a non preemptive uniprocessor kernel. It has no internal locking, and assumes that:

  1. Its service or put procedures run to completion without being preempted.

  2. Its routines activated by calls to timeout and bufcall do not run concurrently with its service or put procedures.

  3. Only one instance of the open or close procedure of a driver or module is running at any given moment.

When a subsystem-parallel stack is installed in the Windows NT kernel, STREAMS ensures that the assumptions listed above are maintained.


B.2 STREAMS Driver and Module Interface

B.2.1 STREAMS_TDI_INFO Structure

The STREAMS_TDI_INFO structure provides information necessary to the TDI-TPI mapping portion of STREAMS. It is supplied in calls to StrmRegisterDriver described in B.2.2. The structure is defined as follows:

typedef struct _STREAMS_TDI_INFO{
    long SERV_type;
    struct streamtab *Partner;
    long PSERV_type;
    long OPT_length;
    PVOID OPT_ptr;
    BOOLEAN ReuseAddr;
    int AddressInfoCount;
    PSTREAMS_TDI_ADDRESS_INFO AddressInfo;
} STREAMS_TDI_INFO, *PSTREAMS_TDI_INFO;

Members
SERV_type
Specifies the type of service, T_COTS (connection-oriented) or T_CLTS (connectionless).
Partner
Points to NetBIOS partner information.
PSERV_type
Points to type of service of the NetBIOS partner.
OPT_length
Specifies size of option string pointed to by OPT_ptr, in bytes.
OPT_ptr
Points to option string to turn on TO_REUSE_ADDR.
ReuseAddr
Specifies reuse address. This member is set to TRUE if TO_REUSE_ADDR is on by default.
AddressInfoCount
Specifies number of TDI address types supported.
AddressInfo
Points to array of address types supported by provider.

The structure for this array is STREAMS_TDI_ADDRESS_INFO, described below:

typedef struct _STREAMS_TDI_ADDRESS_INFO {
    long ADDR_length;
    USHORT AddressType;
    BOOLEAN DirectedRouted;
    PVOID ADDR_ptr;
    BOOLEAN (*AddressCompare)(char *, int, char *, int, int);
} STREAMS_TDI_ADDRESS_INFO, *PSTREAMS_TDI_ADDRESS_INFO;

Members
ADDR_length
Specifies length of broadcast address.
AddressType
pecifies TDI address type.
DirectedRouted
Specifies message routing. This member is set to TRUE if a directed message travels farther than a broadcast or multicast message.
ADDR_ptr
Points to broadcast address, or NULL if none
AddressCompare
Points to a transport address comparison function or NULL for the default function.

By default, STREAMS performs a simple memory comparison to compare transport addresses for equality. For some transports, this test may not be sufficient. These transports may provide their own transport comparison function in the STREAMS_TDI_ADDRESS_INFO structure. The definition of a function of this type is as follows:

BOOLEAN
AddressCompare(
    IN CHAR * First Address,
    IN int FirstAddressLen,
    IN char * SecondAddress,
    IN int SecondAddressLen,
    IN int CompareType
    );

Parameters
FirstAddress
Points to the first address to be compared.
FirstAddressLen
Length in bytes of the FirstAddress.
SecondAddress
Points to the second address to be compared.
SecondAddressLen
Length in bytes of the SecondAddress.
CompareType
SHTDI_ADDRESS_COMPARE_BIND or
SHTDI_ADDRESS_COMPARE_RECEIVE.

Comparisons are required on two operations: bind and receive. During a bind, the FirstAddress parameter specifies the value passed in the T_BIND_REQ message. This value may contain wild cards. The SecondAddress parameter specifies the value returned in the T_BIND_ACK message, which will have been resolved by the transport to be a specific address devoid of wild cards. The two addresses are equal if a T_BIND_REQ of the FirstAddress should result in a T_BIND_ACK of the SecondAddress.

During a receive, the FirstAddress parameter specifies the address passed down by the transport user as a receive filter. The SecondAddress parameter specifies the address of the network packet being received. The two addresses are equal if a a filter specified by the FirstAddress should result in receiving packets addressed to the SecondAddress.


B.2.2 STREAMS Driver and Module Registration Functions

STREAMS transport drivers and modules are compiled into NT drivers which are not linked into the kernel or the STREAMS binaries. They register their streamtab structure and other information needed by STREAMS by use of the following functions, detailed in the paragraphs below:

  • StrmRegisterDriver

  • StrmRegisterModule
StrmRegisterDriver

The function StrmRegisterDriver registers a STREAMS driver with Windows NT.

NTSTATUS
StrmRegisterDriver(
    IN PDRIVER_OBJECT driverobject,
    IN struct streamtab *ptr,
    IN char *subsystem OPTIONAL,
    IN PSTREAMS_TDI_INFO ptdiinfo OPTIONAL
    );

Parameters
driverobject
Points to Windows NT driver object that contains the STREAMS driver.
ptr
Points to relevant STREAMS information.
subsystem
Optionally points to subsystem name, or NULL if stack is fully parallel.
ptdiinfo
Optionally points to information about driver for TDI.
StrmRegisterModule

The function StrmRegisterModule registers a STREAMS module with Windows NT.

NTSTATUS
StrmRegisterModule(
    IN PDRIVER_OBJECT driverobject,
    IN struct streamtab *ptr,
    IN char *subsystem OPTIONAL
    );

Parameters
driverobject
Points to Windows NT driver object that contains the STREAMS module.
ptr
Points to relevant STREAMS information.
subsystem
Optionally points to subsystem name, or NULL if stack is fully parallel.


B.2.3 STREAMS Interface Functions

To use the STREAMS environment, STREAMS drivers and modules that are part of the transport provider must conform to the interface prototyped in this section. Interface definitions are specified in Reference 1 (Further Readings on Streams). STREAMS drivers/modules must provide the following interface procedures which are called by STREAMS:

  • Xxx Open

  • Xxx Close

  • Xxx Put

  • Xxx Srv

Note The prefix “Xxx” is used as an example only. In real life, a descriptive prefix is used for these procedures. For example, the open procedure of the TCP driver is called TcpOpen; the close procedure of the UDP driver is called UdpClose.

Xxxopen

The procedure Xxx open is called by STREAMS to open a STREAMS driver or module. See Reference 1 for details.

INT
Xxxopen(
    IN STRUCT queue *rq,
    IN OUT dev_t *devp,
    IN INT flag,
    IN INT sflag,
    IN VOID *credp
    );

Parameters
rq
Points to read queue.
devp
Points to assigned device number.
flag
Indicates the Open() flag; 0 for modules.
sflag
Indicates the stream flag, which is CLONEOPEN or MODOPEN.
credp
Unused.

After being registered by StrmRegisterDriver, a STREAMS driver can be opened via the Windows NT path:

\Device\Streams\name

where name is the driver specified in the streamtab structure, as follows:

streamtab.st_rdinit->qi_minfo->mi_idname

Every STREAMS driver for Windows NT must be clonable. The stream head creates a new file object every time a STREAMS driver is opened, and always calls the open procedure of the driver with the CLONEOPEN flag.

The return value from the open procedure of the driver (egopen) must either be 0 or OPENFAIL. If the return value is 0, egopen must have assigned a minor device number to the stream, and returned it via the devp parameter.

Xxxclose

The Xxx close procedure is called by STREAMS to close a STREAMS driver or module. See Further Readings on Streams, Reference 1 for details.

INT
Xxxclose(
    IN STRUCT queue *rq,
    IN INT flag,
    IN VOID *credp
    );

Parameters
rq
Points to the read queue.
flag
Indicates the Open() flag.
credp
Unused.
Xxxput

The Xxx put procedure is an example of a queue routine that receives messages from preceding queues in the stream. See Further Readings on Streams, Reference 1 for details.

INT
Xxxput(
    IN STRUCT queue *q,
    IN STRUCT msgb *mp
    );

Parameters
q
Points to the queue to be used.
mp
Points to the message to be processed.
Xxxsrv

The procedure Xxx srv is an example of a service procedure for a STREAMS driver/module.

INT
Xxxsrv(
    IN STRUCT queue *q
    );
Parameters
q
Points to the queue to be used.


STREAMS_TDI_ACTION Structure

The STREAMS_TDI_ACTION structure allows a kernel-mode client of a streams transport to make special or proprietary extensions available to the client by making a TDI_ACTION request to the transport. For more information about TDI_ACTION requests, see the Network Driver Reference.

The structure is defined as follows:

typedef struct _STREAMS_TDI_ACTION {
    TDI_ACTION_HEADER Header;
    BOOLEAN DatagramOption;
    ULONG BufferLength;
    CHAR Buffer[1];
} STREAMS_TDI_ACTION, *PSTREAMS_TDI_ACTION;

STREAMS_TDI_ACTION defines a client call to the action function of the Windows NT STREAMS wrapper.

Members
Header
Specifies a TDI_ACTION_HEADER structure defining the header for the action parameter block.
DatagramOption
Specifies TRUE if the action request applies to a datagram.
BufferLength
Specifies the length, in bytes, of the buffer defining the action.
Buffer[1]
Specifies a character array buffer defining the requested action.
See Also

TDI_ACTION_HEADER


B.3 Services Available to STREAMS Drivers and Modules

This contains information about the following topics:

  • UNIX SVR4 STREAMS Utility Routines

  • Parallel STREAMS Functions

  • Dynix/PTX Spin Lock Primitives

B.3.1 UNIX SVR4 STREAMS Utility Routines

STREAMS provides the functions and macros listed below, with the functionality specified in Further Readings on Streams, Reference 1. In Windows NT, unbufcall returns 1 if it has successfully canceled a bufcall request, -1 if its argument is invalid, or 0 if the bufcall request cannot be canceled.

Note In Windows NT, it is possible for unbufcall to fail even thought the bufcall handler has not yet executed. In this case, the execution of the bufcall handler will be pending. Alternately, the handler may already be executing and spinning on a lock currently held by the transport. Often, a bufcall handler will be passed a pointer to a transport structure. If that structure is freed and the bufcall cannot be canceled, the handler will execute and possibly modify memory no longer owned by the transport. A transport must be prepared to handle these circumstances in a safe manner, or system crashes will result.

Priority bands are not supported by STREAMS.
Buffer Management:

adjmsg
dupmsg
pullupmsg
allocb
esballoc (1)
qsize
bufcall
flushq
rmvb
copyb
freeb
rmvq
copymsg
freemsg
testb
datamsg
linkb
unbufcall (1)
dupb
msgdsize
unlinkb

Message Passing:
putnext
putctl
putctl1
qreply
Flow Control and Scheduling:
canput
insq
putq
enableok
noenable
qenable
getq
putbq
Queue Management:
backq
OTHERQ
strqget (1)
getadmin (1)
RD
strqset (1)
getmid (1)
SAMESTR
WR
Not Supported:
bcanput (1)
bcanputnext
flushband (1

)

(1) New in UNIX SVR4cd ddk_br


B.3.2 Parallel STREAMS Functions

STREAMS provides the Sequent Parallel STREAMS functions listed below. These functions are defined in Further Readings on Streams, Reference 2.

  • canputnext

  • disable_procs

  • enable_procs

  • putnextctl

  • putnextctl1


B.3.3 Dynix/PTX Spin Lock Primitives

In Windows NT, the thread that acquires a spin lock must release the lock. Another thread cannot release the spin lock on behalf of the first thread. Variables of type lock_t should only be accessed by the following Dynix/PTX spin lock primitives (Further Readings on Streams, Reference 3):

  • init_lock

  • p_lock

  • v_lock

These primitives are implemented as macros that call the kernel-mode spin lock functions.

init_lock

The init_lock function initializes a spin lock.

VOID
init_lock(
    IN lock_t *lockp
    );

Parameters
lockp
Points to the spin lock to be initialized.
p_lock

The p_lock function acquires a spin lock and returns the previous IRQL.

INT
p_lock(
    IN lock_t *lockp,
    IN INT ignored
    );

Parameters
lockp
Points to the spin lock to be acquired.
ignored
Ignored for Windows NT.
v_lock

The v_lock function releases a spin lock.

VOID
v_lock(
    IN lock_t *lockp,
    IN USHORT level
    );

Parameters
lockp
Points to the spin lock to be released.
level
Specifies the IRQL to restore.


B.3.4 Miscellaneous UNIX Kernel Functions, Macros, and Variables

Although the functions, macros, and variables listed below are not part of the STREAMS definition, they are often used by STREAMS stacks. Therefore they are provided as part of the STREAMS environment for Windows NT.
Time Variables:

lbolt
time

Timer Functions:
timeout
untimeout
Miscellaneous Routines:
bcopy
min
suser
bzero
minor
max
major

The global kernel variables lbolt and time are frequently used by STREAMS stacks to calculate relative time. In Windows NT, these variables are defined as macros that call Windows NT kernel services to calculate their value. As such they incur overhead to convert from the Windows NT time format. Performance conscious transports should replace references to these variables with calls to the native Windows NT equivalents: KeQueryTickCount and KeQuerySystemTime.

The suser function currently always returns a value of 1.

The timer function timeout schedules a procedure to be executed at some time in the future. It is implemented using KeSetTimer to schedule the target procedure as a DPC, via a timer object. The function untimeout cancels an earlier call to timeout. It returns 1 if it has successfully canceled the timer, -1 if its argument is invalid, or 0 if the target procedure cannot be canceled.

Note In Windows NT, it is possible for untimeout to fail even thought the timeout handler has not yet executed. In this case, the execution of the timeout handler will be pending. Alternately, the handler may already be executing and spinning on a lock currently held by the transport. Often, a timeout handler will be passed a pointer to a transport structure. If that structure is freed and the timeout cannot be canceled, the handler will execute and possibly modify memory no longer owned by the transport. A transport must be prepared to handle these circumstances in a safe manner, or system crashes will result.

The timeout and untimeout functions incur substantial overhead compared to the native Windows NT services. Performance conscious transports should replace calls to these functions with calls to the native services.


B.3.4.1 splstr and splx (Not Supported)

In the UNIX kernel, STREAMS drivers and modules use the spl* functions (splstr and splx) to implement critical sections, so that put and service procedures are not preempted by interrupt routines and procedures triggered by timeout. In the Windows NT kernel, it is not sufficient for a STREAMS stack to protect its critical sections by raising its interrupt level. Hence, STREAMS does not provide the spl* functions. STREAMS stacks must be rewritten to use spin locks.


B.3.4.2 The u Structure (Not Supported)

For UNIX, open and close procedures may set the u.u_error member of the u structure to return errors to the stream head. For Windows NT, a process does not have a u structure. Instead, the function StrmSetError (Section B.3.5) is provided to perform the equivalent of an assignment to u.u_error. No other u structure members are emulated.


B.3.4.3 sleep and wakeup (Not Supported)

Open and close procedures may block when resources are not available. In the UNIX kernel, process synchronization is accomplished using the sleep and wakeup functions. These functions are not provided by STREAMS. STREAMS stacks must be rewritten to use StrmWaitForSingleObject or StrmWaitForMultipleObjects (Section B.3.6), in conjunction with any of the following Windows NT kernel synchronization facilities:

  1. Synchronization events

  2. Notification events

  3. Semaphores

See Section B.8 for an example of this conversion.


B.3.5 StrmSetError

STREAMS includes the function StrmSetError for setting errors. It performs the equivalent of an assignment to u.u_error (Section 31.3.4.2) by storing an error code in a per-thread location for the stream head to retrieve after the return of the open or close procedure of the driver/module.

VOID
StrmSetError(
    IN CHAR errno
    );

Parameters
errno
Specifies the error code associated with an open or close operation.


B.3.6 Blocking Primitives for Open and Close Procedures

Instead of calling KeWaitForMutexObject, KeWaitForMultipleObjects, and KeWaitForSingleObject, a STREAMS stack must use the following corresponding functions, each of which has a queue pointer as the first parameter:

  • StrmWaitForMutexObject

  • StrmWaitForMultipleObjects

  • StrmWaitForSingleObject
StrmWaitForMutexObject

The function StrmWaitForMutexObject allows the calling routine to wait until the specified mutex object attains a signaled state.

NTSTATUS
StrmWaitForMutexObject(
    IN queue_t *Queue,
    IN PKMUTEX Mutex,
    IN KWAIT_REASON WaitReason,
    IN KPROCESSOR_MODE WaitMode,
    IN BOOLEAN Alertable,
    IN PLARGE_INTEGER Timeout OPTIONAL
    );

Parameters
Queue
Points to the queue of the calling open or close routine.
Mutex
Points to the mutex object for which to wait.
WaitReason
Specifies the reason for waiting.
WaitMode
Specifies the processor wait mode to be used.
Alertable
Specifies if the wait is alertable.
Timeout
Optionally points to the timeout period.
StrmWaitForMultipleObjects

The function StrmWaitForMultipleObjects allows the calling routine to wait until the specified objects attain a signaled state.

NTSTATUS
StrmWaitForMultipleObjects(
    IN queue_t *Queue,
    IN CCHAR Count,
    IN PVOID Object[],
    IN WAIT_TYPE WaitType,
    IN KWAIT_REASON WaitReason,
    IN KPROCESSOR_MODE WaitMode,
    IN BOOLEAN Alertable,
    IN PLARGE_INTEGER Timeout OPTIONAL,
    IN PKWAIT_BLOCK WaitBlockArray OPTIONAL
    );

Parameters
Queue
Points to the queue of the calling open or close routine.
Count
Specifies the number of objects for which to wait.
Object[]
Points to an array of pointers to dispatcher objects.
WaitType
Specifies the wait type (WaitAll or WaitAny).
WaitReason
Specifies the reason for waiting.
WaitMode
Specifies the processor wait mode to be used.
Alertable
Specifies if the wait is alertable.
Timeout
Optionally points to the timeout period.
WaitBlockArray
Optionally points to an array of wait blocks.
StrmWaitForSingleObject

The function StrmWaitForSingleObject allows the calling routine to wait until the specified object attains a Signaled state.

NTSTATUS
StrmWaitForSingleObject(
    IN queue_t *Queue,
    IN PVOID Object,
    IN KWAIT_REASON WaitReason,
    IN KPROCESSOR_MODE WaitMode,
    IN BOOLEAN Alertable,
    IN PLARGE_INTEGER Timeout OPTIONAL
    );

Parameters
Queue
Points to the queue of the calling open or close routine.
Object
Points to the dispatcher object.
WaitReason
Specifies the reason for waiting.
WaitMode
Specifies the processor wait mode to be used.
Alertable
Specifies if the wait is alertable.
Timeout
Optionally points to the timeout period.


B.3.7 Time Unit Conversion Function

StrmConvertCentisecondsToRelativeTimeout

STREAMS provides the StrmConvertCentisecondsToRelativeTimeout function to conveniently convert timeouts expressed in UNIX clock ticks to Windows NT relative timeouts. Such conversion is useful when timeout calls (Section B.3.4) are being converted into StrmWaitFor* calls (Section B.3.6).

LARGE_INTEGER
StrmConvertCentisecondsToRelativeTimeout(
    IN ULONG Centiseconds
    );

Parameter
Centiseconds
Specifies the timeout period, in centiseconds.


B.3.8 Registry Functions

To query the Registry, a STREAMS stack can use the following functions:

  • StrmOpenRegKey

  • StrmGetRegValue
StrmOpenRegKey

The function StrmOpenRegKey uses a registry key name to open the registry key.

NTSTATUS
StrmOpenRegKey(
    OUT PHANDLE HandlePtr,
    IN PUCHAR Keyname
    );

Parameters
HandlePtr
Points to a calling routine location into which StrmOpenRegKey writes a handle for the registry key.
Keyname
Points to the name of the key to be opened.
StrmGetRegValue

The function StrmGetRegValue uses a registry key handle and item name to obtain a keyed value from the Registry.

NTSTATUS
StrmGetRegValue(
    IN HANDLE KeyHandle,
    IN PUCHAR ValueName,
    OUT PUCHAR ValueData,
    OUT ULONG ValueLength,
    OUT PULONG ValueType
    );

Parameters
KeyHandle
Specifies the registry key handle returned from a previous call to StrmOpenRegKey.
ValueName
Points to the name of the registry key value to be retrieved.
ValueData
Points to a calling routine character array into which StrmGetRegValue writes the retrieved value.
ValueLength
Indicates the length in bytes of the retrieved value.
ValueType
Points to a calling routine location into which StrmGetRegValue writes the type of the retrieved value. The type can be any valid registry type, such as: REG_SZ, REG_MULTI_SZ, REG_DWORD, REG_EXPAND_S2.


B.3.9 Event Logging Function

StrmLogEvent

Windows NT provides the strlog function as a convenience in porting. The output of strlog will be sent to the Win32 event log. To fully exploit the capability of the event logging facility, STREAMS stacks should be rewritten to use the function StrmLogEvent. This function writes an entry into the Windows NT I/O error log. The Windows NT event logging facility permits the use of localizable message strings with run time insertion strings, and binary dump data.

NTSTATUS
StrmLogEvent(
    IN PDRIVER_OBJECT DriverObject OPTIONAL,
    IN NTSTATUS EventCode,
    IN ULONG UniqueEventValue,
    IN USHORT NumStrings,
    IN PCHAR *Strings OPTIONAL,
    IN ULONG DataSize,
    IN PVOID Data OPTIONAL
    );

Parameters
DriverObject
Optionally points to a driver object.
EventCode
Identifies the error message.
UniqueEventValue
Identifies this instance of the error message.
NumStrings
Specifies the number of ANSI strings in the Strings array.
Strings
Optionally points to an array of pointers to ANSI strings.
DataSize
Specifies the number of bytes contained in the Data parameter.
Data
Optionally points to the binary data associated with the error log entry.

The interface to the I/O event logging facility requires Unicode insertion strings. StrmLogEvent performs the conversion from ANSI to Unicode.

There is a limit on the amount of binary dump data and insertion string data that can be logged in a single call to the event logging facility. The manifest constant STRM_MAX_ERROR_LOG_DATA_SIZE defines this value for STREAMS transports. This definition accounts for the ANSI to Unicode conversion. Note that strlog is implemented as a call to StrmLogEvent with a single insertion string containing the strlog data. Thus, the length of the strlog entry is severely limited.


B.4 Streams Message Types

STREAMS supports the message types listed below. These message types are documented in Further Readings on Streams, Reference 1, Appendix B.
Regular Priority:

M_BREAK (1,2)
M_DELAY (1,2)
M_SETOPTS
M_CTL (1,2)
M_IOCTL
M_DATA
M_PROTO

High Priority:
M_ERROR
M_IOCACK
M_PCPROTO
M_FLUSH
M_IOCNAK

(1) Never generated at the write queue of the stream head

(2) Discarded by the read queue of the stream head

STREAMS does not support the message types listed below. Neither the stream head nor the dndis driver generates or reacts to these message types.
M_COPYIN
M_PCRSE
M_START
M_COPYOUT
M_PCSIG
M_STARTI
M_HANGUP
M_READ
M_STOP
M_IOCDATA
M_RSE
M_STOPI
M_PASSFP
M_SIG


B.5 Streams User-Level Interface

B.5.1 Stream Head Functions

STREAMS does not provide a Transport Layer Interface (TLI) library. User-mode programs, such as routing or plumbing daemons, can access STREAMS drivers and modules directly with the following functions listed below, which block synchronously.

  • getmsg

  • putmsg

  • poll

  • s_ioctl

  • s_open

These functions operate only on handles to STREAMS drivers. To close a handle, call the Win32 CloseHandle API. These functions are defined in Further Readings on Streams, Reference 1. Windows NT adds the s_ prefix to the last two due to a name conflict with the C runtime functions of the same name.

These functions are not recommended for general application access to transports. The Windows Sockets interface is more appropriate for general use.

INT
getmsg(
    IN HANDLE fd,
    IN OUT struct strbuf *ctrlptr OPTIONAL,
    IN OUT struct strbuf *dataptr OPTIONAL,
    IN OUT int *flagsp
    );

Parameters
fd
The stream handle.
ctrlptr
Pointer to the control portion of the STREAMS message.
dataptr
Pointer to the data portion of the STREAMS message.
flagsp
Pointer to the returned flags argument, which may be RS_HIPRI.
INT
putmsg(
    IN HANDLE fd,
    IN struct strbuf *ctrlptr OPTIONAL,
    IN struct strbuf *dataptr OPTIONAL,
    IN int flags
    );

Parameters
fd
The stream handle.
ctrlptr
Pointer to the control portion of the STREAMS message.
dataptr
Pointer to the data portion of the STREAMS message.
flags
0 or RS_HIPRI.
INT
poll(
    IN OUT struct pollfd *fds,
    IN unsigned int nfds,
    IN int timeout
    );

Parameters
fds
Pointer to an array of poll structures
nfds
The number of poll strucutures contained in the array.
timeout
0, INFTIM (-1), or timeout value in milliseconds.
struct pollfd {
    HANDLE fd;                // file handle to poll
    short events;             // events of interest on fd
    short revents;            // events that occurred on fd
};

INT
s_ioctl(
    IN HANDLE fd,
    IN int cmd,
    IN OUT void *arg OPTIONAL
    );

Parameters
fd
The stream handle.
cmd
The IOCTL_xxx
arg
Command-dependant argument.
HANDLE
s_open(
    IN CHAR *path,
    IN INT oflag,
    IN INT ignored
    );

Parameters
path
Defines the path \Device\Streams\name, where name specifies the name of the driver.
oflag
Defines the open flag; must be 0.
ignored
Ignored.
STREAMS does not provide the stream head API functions listed below. These functions apply predominantly to named streams and pseudo-TTYs.
fattach
getpmsg
ptsname
fcntl
grantpt
putpmsg
fdetach
isastream
unlockpt

The s_poll function allows the caller to query several streams for information. For STREAMS, the flags listed below can be used to specify the events of interest to poll:

  • POLLIN

  • POLLOUT

  • POLLMSG (1)

  • POLLPRI

  • POLLERR

  • POLLNVAL

(1) New in UNIX SVR4

STREAMS does not support the following flags:

  • POLLHUP

  • POLLRDNORM

  • POLLRDBAND

  • POLLWRNORM

  • POLLWRBAND


B.5.2 Streams IOCTL Commands

STREAMS uses the STREAMS IOCTL commands listed below. The command functions are documented in the streamio section of Further Readings on Streams, Reference 1.

Plumbing Commands:

  • I_LINK

  • I_PUSH

  • I_UNLINK

Specialized Commands:

  • I_FDINSERT

  • I_STR

A special IOCTL command, DATAL_IBIND, is described in Section 31.7 of this specification.

STREAMS does not implement the stream head IOCTL commands listed below.
I_ATMARK (1)
I_GRDOPT
I_POP
I_CANPUT (1)
I_GETSIG
I_PUNLINK (1)
I_CKBAND (1)
I_GWROPT (1)
I_RECVFD
I_FIND
I_LIST (1)
I_SENDFD
I_FLUSH
I_LOOK
I_SETCLTIME (1)
I_FLUSHBAND (1)
I_NREAD
I_SETSIG
I_GETBAND (1)
I_PEEK
I_SRDOPT
I_GETCLTIME (1)
I_PLINK (1)
I_SWROPT (1

)

(1) New in UNIX SVR4


B.5.3 Unsupported UNIX SVR4 Plumbing Features

Streams Persistent Links

In UNIX SVR4, the plumbing between STREAMS drivers is maintained by either ordinary or persistent links. A persistent link is not dependent on its controlling stream remaining open. A user process plumbs persistent links with the I_PLINK and I_PUNLINK stream head IOCTL commands.

STREAMS does not support persistent links. When the last stream to the topmost STREAMS driver is closed, the plumbing underneath that driver is dismantled. Thus, a persistent plumbing daemon is required to maintain a transport stack under Windows NT.

Streams Autopush Facility

STREAMS on Windows NTdoes not provide an autopush facility.


B.6 Streams Transport Provider Upper-edge Translation

The system interface between transport user and transport provider is the TDI. The LAN Manager redirector and server, Windows Sockets, and the NetBIOS interface libraries use TDI. However, most STREAMS stacks use the TPI at their upper edges. STREAMS includes a TDI-TPI mapper so that a STREAMS stack that uses TPI can be accessed by most Windows NT transport users.

The following TPI message structures have been extended in STREAMS. The original definitions are detailed in Further Readings on Streams, References 1 and 4.

struct T_info_ack {
    LONG PRIM_type;
    LONG TSDU_size;
    LONG ETSDU_size;
    LONG CDATA_size;
    LONG DDATA_size;
    LONG ADDR_size;
    LONG OPT_size;
    LONG TIDU_size;
    LONG SERV_type;
    LONG CURRENT_state;
                             // Windows NT Extensions:
    LONG ADDR_length;        // current transport address of this stream
    UCHAR UNIQUE_addr[6];    // unique address for this host
};

struct T_discon_ind {
    LONG PRIM_type;
    LONG DISCON_reason;      // may be an NTSTATUS code
    LONG SEQ_number;
};

struct T_error_ack {
    LONG PRIM_type;
    LONG ERROR_prim;
    LONG TLI_error;          // if TLI_error == TNTERROR,
    LONG UNIX_error;         // then UNIX_error is an NTSTATUS value
};


B.7 Streams Transport Provider Lower Edge Translation

All Windows NT network adapter drivers use NDIS 3.0. However, most STREAMS stacks use the DLPI at their lower edges. While DLPI is a STREAMS message-based interface, NDIS 3.0 is a function-based interface. STREAMS includes a DLPI-NDIS 3.0 translator so that a STREAMS stack that uses DLPI can be configured to use any network adapter device recognized by Windows NT. The STREAMS dndis driver (\Device\Streams\dndis) translates connectionless-mode DLPI primitives into NDIS 3.0 primitives, and vice-versa.

To associate a stream opened to dndis with a network interface card, the DATAL_IBIND IOCTL command must be used. This is most easily done by using a plumbing daemon, as described in Section B.8.

The STREAMS PC_PROTO message structures described next are defined for DLPI. They are detailed in Further Readings on Streams, Reference1.


B.7.1 Ethernet and 802.3 Medium (DL_CSMACD)

dl_bind_req_t
typedef struct {
    ULONG        dl_primitive;
    ULONG        dl_sap;
    ULONG        dl_max_conind;
    ushort       dl_service_mode;
    ushort       dl_conn_mgmt;
    ULONG        dl_xidtest_flg;
} dl_bind_req_t;

Members
dl_primitive
Must be the manifest constant DL_BIND_REQ_T
dl_sap
Specifies data link service access point (SAP), set to the 2-byte Ethernet packet type. Type 0xFFFE signifies all 802.3 packets. Type 0xFFFF signifies all Novell® IPX 802.3 packets. All other types are interpreted as Ethernet packets.
dl_max_conind
Ignored.
dl_service_mode
Specifies service mode; only DL_CLDLS supported.
dl_conn_mgmt
Ignored.
dl_xidtest_flg
Ignored.
DL_BIND_ACK_T
typedef struct {
    ULONG dl_primitive;
    ULONG dl_sap;
    ULONG dl_addr_length;
    ULONG dl_addr_offset;
    ULONG dl_max_connid;
    ULONG dl_xidtest_flg;
} dl_bind_ack_t;

Members
dl_primitive
Must be the manifest constant DL_BIND_ACK_T.
dl_sap
Specifies the SAP from the previous dl_bind_req_t structure.
dl_addr_length
Specifies length of address; always 6.
dl_addr_offset
Specifies offset to address.
dl_max_connid
Specifies maximum connection ID; always 0.
dl_xidtest_flg
Specifies flag for exchange ID test; always 0.
DL_UNITDATA_REQ_T
typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_dest_addr_length;
    ULONG     dl_dest_addr_offset;
    ULONG     dl_priority;
} dl_unitdata_req_t;

Members
dl_primitive
Must be the manifest constant DL_UNITDATA_REQ_T
dl_dest_addr_length
Specifies length of destination address; must be 8.
dl_dest_addr_offset
Specifies offset to byte sequence below. The offset must point to a byte sequence composed of the destination media-access control address followed by the Ethernet type.
media-access control address
6 bytes
type
2 bytes
dl_priority
Specifies data link priority.
DL_UNITDATA_IND_T
typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_dest_addr_length;
    ULONG     dl_dest_addr_offset;
    ULONG     dl_src_addr_length;
    ULONG     dl_src_addr_offset;
    ULONG     dl_group_address;
} dl_unitdata_ind_t;

Members
dl_primitive
Must be the manifest constant DL_UNITDATA_IND_T.
dl_dest_addr_length
Specifies length of destination address; always 6.
dl_dest_addr_offset
Specifies offset to destination address.
dl_src_addr_length
Specifies length of source address; always 8.
dl_src_addr_offset
Specifies offset to byte sequence below. The offset points to the byte sequence composed of the source media-access control address followed by the Ethernet type.
media-access control address
6 bytes
type
2 bytes
dl_group_address
Specifies group address; 1 if broadcast or multicast.


B.7.2 Token Ring and SNAP (DL_TPR)

B.7.2.1 DL_BIND_REQ_T

typedef struct {
    ULONG         dl_primitive;
    ULONG         dl_sap;
    ULONG         dl_max_conind;
    ushort    dl_service_mode;
    ushort    dl_conn_mgmt;
    ULONG         dl_xidtest_flg;
} dl_bind_req_t;

Members
dl_primitive
Must be the manifest constant DL_BIND_REQ_T.
dl_sap
Specifies destination SAP; 0xAA is the SAP for SNAP.
dl_max_conind
Ignored.
dl_service_mode
Specifies service mode; only DL_CLDLS supported.
dl_conn_mgmt
Ignored.
dl_xidtest_flg
Ignored.


B.7.2.2 DL_SUBS_BIND_REQ_T

The DL_SUBS_BIND_REQ DLPI request message is only valid if the original bind was to type 0xAA (SNAP). The subsequent bind will filter on the SNAP information.

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_subs_sap_offset;
    ULONG     dl_subs_sap_length;
    ULONG     dl_subs_bind_class;
} dl_subs_bind_req_t;

Members
dl_primitive
Must be the manifest constant DL_SUBS_BIND_REQ_T
dl_subs_sap_offset
Specifies offset to byte sequence below. The offset points to the byte sequence composed of the SNAP header followed by the Ethernet type.
SNAP header
3 bytes
Ethernet type
2 bytes
dl_subs_sap_length
Specifies length of SAP; must be 5.
dl_subs_bind_class
Specifies subsequent bind class; must be DL_HIERARCHICAL_BIND.


B.7.2.3 DL_UNITDATA_REQ_T

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_dest_addr_length;
    ULONG     dl_dest_addr_offset;
    ULONG     dl_priority;
} dl_unitdata_req_t;

Members
dl_primitive
Must be the manifest constant DL_UNITDATA_REQ_T.
dl_dest_addr_length
Specifies length of byte sequence below:
media-access control address
6 bytes
type
2 bytes
routing information
variable
dl_dest_addr_offset
Specifies offset to byte sequence above. The offset must point to a byte sequence composed of the destination media-access control address, the Ethernet type, and routing information.
dl_priority
Specifies data link priority.


B.7.2.4 DL_UNITDATA_IND_T

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_dest_addr_length;
    ULONG     dl_dest_addr_offset;
    ULONG     dl_src_addr_length;
    ULONG     dl_src_addr_offset;
    ULONG     dl_group_address;
} dl_unitdata_ind_t;

Members
dl_primitive
Must be the manifest constant DL_UNITDATA_IND_T.
dl_dest_addr_length
Specifies length of destination address; always 6.
dl_dest_addr_offset
Specifies offset to destination address.
dl_src_addr_length
Specifies length of byte sequence below:
media-access control address
6 bytes
type
2 bytes
routing information
variable
dl_src_addr_offset
Specifies offset to byte sequence above. The offset must point to a byte sequence composed of the destination media-access control address, the Ethernet type, and routing information, if any.
dl_group_address
Specifies group address; 1 if broadcast or multicast.


B.7.3 Fiber Distributed Data Interface (DL_FDDI)

B.7.3.1 DL_BIND_REQ_T

typedef struct {
    ULONG         dl_primitive;
    ULONG         dl_sap;
    ULONG         dl_max_conind;
    ushort    dl_service_mode;
    ushort    dl_conn_mgmt;
    ULONG         dl_xidtest_flg;
} dl_bind_req_t;

Members
dl_primitive
Must be the manifest constant DL_BIND_REQ_T.
dl_sap
Specifies destination SAP; 0xAA is the SAP for SNAP.
dl_max_conind
Ignored.
dl_service_mode
Specifies service mode; only DL_CLDLS supported.
dl_conn_mgmt
Ignored.
dl_xidtest_flg
Ignored.


B.7.3.2 DL_BIND_ACK_T

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_sap;
    ULONG     dl_addr_length;
    ULONG     dl_addr_offset;
    ULONG     dl_max_connid;
    ULONG     dl_xidtest_flg;
} dl_bind_ack_t;

Members
dl_primitive
Must be the manifest constant DL_BIND_ACK_T.
dl_sap
Specifies the SAP from dl_bind_req_t structure.
dl_addr_length
Specifies length of address; always 6.
dl_addr_offset
Specifies offset to address.
dl_max_connid
Specifies maximum connect indications; always 0.
dl_xidtest_flg
Specifies flag for exchange ID test; always 0.


B.7.3.3 DL_SUBS_BIND_REQ_T

This DLPI request message is only valid if the original bind was to type 0xAA (SNAP). The subsequent bind will filter on the SNAP information.

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_subs_sap_offset;
    ULONG     dl_subs_sap_length;
    ULONG     dl_subs_bind_class;
} dl_subs_bind_req_t;

Members
dl_primitive
Must be the manifest constant DL_SUBS_BIND_REQ_T.
dl_subs_sap_offset
Specifies offset to byte sequence below. The offset points to the byte sequence composed of the SNAP header followed by the Ethernet type. The SNAP header given must match the SNAP header received off the wire.
SNAP header
3 bytes
Ethernet type
2 bytes
dl_subs_sap_length
Specifies length of subsequent SAP; must be 5.
dl_subs_bind_class
Specifies subsequent SAP bind class; must be DL_HIERARCHICAL_BIND.


B.7.3.4 DL_UNITDATA_REQ_T

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_dest_addr_length;
    ULONG     dl_dest_addr_offset;
    ULONG     dl_priority;
} dl_unitdata_req_t;

Members:
dl_primitive
Must be the manifest constant DL_UNITDATA_REQ_T.
dl_dest_addr_length
Specifies length of address; must be 8.
dl_dest_addr_offset
Specifies offset to byte sequence below. The offset must point to a byte sequence composed of the destination media-access control address followed by the Ethernet type.
media-access control address
6 bytes
type
2 bytes
dl_priority
Specifies data link priority.


B.7.3.5 DL_UNITDATA_IND_T

typedef struct {
    ULONG     dl_primitive;
    ULONG     dl_dest_addr_length;
    ULONG     dl_dest_addr_offset;
    ULONG     dl_src_addr_length;
    ULONG     dl_src_addr_offset;
    ULONG     dl_group_address;
} dl_unitdata_ind_t;

Members:
dl_primitive
Must be the manifest constant DL_UNITDATA_IND_T.
dl_dest_addr_length
Specifies length of destination address; always 6.
dl_dest_addr_offset
Specifies offset to destination address.
dl_src_addr_length
Specifies length of source address; always 8.
dl_src_addr_offset
Specifies offset to byte sequence below. The offset points to the byte sequence composed of the source media-access control address followed by the Ethernet type.
media-access control address
6 bytes
type
2 bytes
dl_group_address
Specifies group address; 1 if broadcast or multicast.


B.8 Porting a Fully Parallel STREAMS Stack

Porting a fully parallel STREAMS stack to Windows NT, you will need to:

  1. Build the stack into Windows NT driver(s).

  2. Set the registry to load Windows NT driver(s).

  3. Port the plumbing daemon over to Windows NT.


B.8.1 Building the Stack into a Windows NT Driver

A STREAMS stack can be built into one Windows NT driver, or partitioned into several. For example, tcpip.sys under Windows NT 3.1 contained the tcp, udp, and ip STREAMS drivers, as well as the arp module.(In Windows NT 3.5, TCP/IP is now implemented as a native transport driver.)


B.8.1.1 DriverEntry Routine

When a Windows NT driver is loaded, its DriverEntry routine is called. For each Windows NT driver containing a STREAMS stack, its DriverEntry routine must:

  1. Register each STREAMS component contained in the driver.

  2. Call the init routine, if any, of each STREAMS component in the driver.

STREAMS stacks that implement NetBIOS providers require more work, as described in Section B.8.4.

Example: DriverEntry Routine for tcpip.sys from Windows NT 3.1

#include <ntddk.h>
#include <stream.h>

extern struct streamtab arpinfo;
extern struct streamtab tcpinfo;

extern int arpinit(void);
extern int tcpinit(void);

NTSTATUS
DriverEntry(
        IN PDRIVER_OBJECT driver,
        IN PUNICODE_STRING device
)
{
    NTSTATUS status;

    status = StrmRegisterModule(driver, &arpinfo, NULL);

    if (status != STATUS_SUCCESS) {
        return(status);
    }

    status = StrmRegisterDriver(driver, &tcpinfo, NULL, NULL);

    if (status != STATUS_SUCCESS) {
        return(status);
    }

    arpinit();
    tcpinit();

    return(STATUS_SUCCESS);
}


B.8.2 Loading Windows NT Drivers

Windows NT drivers are loaded either:

  1. At system boot

  2. Dynamically by the Win32 Service Controller

The second method is recommended. Either method requires setting up the Registry.

The streams.sys driver must be loaded prior to any driver that calls a StrmRegister* function. The path for this driver is:

%SystemRoot%\system\drivers\streams.sys

The dynamic link library that provides the user-level stream head API functions is winstrm.dll, accessed by the following path:

%SystemRoot%\system\winstrm.dll

In the following example, the Tcpipsys section of the Registry is for the driver containing the STREAMS stack. The Tcpip section is for the user-level plumbing daemon.

Example: Registry Parameters for TCP/IP Configuration

\Registry\Machine
    System
        CurrentControlSet
            Services
                Tcpipsys
                    Type = KernelDriver
                    Start = DemandLoad
                    ErrorControl = ErrorNormal
                    DependOnService = REG_MULTI_SZ "Streams"
                    ImagePath = REG_EXPAND_SZ
                                 \SystemRoot\system32\drivers\tcpip.sys

                Tcpip
                    Type = Win32ServiceShareProcess
                    Start = DemandLoad
                    ErrorControl = ErrorNormal
                    ImagePath = REG_EXPAND_SZ
                                %SystemRoot%\system32\LmSvcs.exe
                    DependOnService = REG_MULTI_SZ "Tcpipsys"
                    ObjectName = REG_SZ LocalSystem
                    Parameters
                        DefaultGateway = 11.1.0.1
                        DatabasePath = REG_EXPAND_SZ
                                "%SystemRoot%\system\drivers\etc"


B.8.3 Customizing the STREAMS Stack for Windows NT

In the UNIX kernel, process synchronization uses the sleep and wakeup functions, in conjunction with flag variables and timers. Since the Kernel is preemptive and a driver cannot wait while holding a spin lock, sleep is difficult to emulate without introducing a timing window between the timeout call and the sleep call.

Since common use of sleep and wakeup within transport stacks effectively emulates Kernel event objects, these functions are not provided for STREAMS. STREAMS stacks must use the appropriate synchronization facility instead. Usually, this facility consists of event objects.

Example: Orderly Release Within a Close Procedure

Consider a STREAMS driver implementing a connection-oriented protocol with orderly release. Suppose the original pseudocode for its close procedure is:

int thing;
egclose(rq, ...)
{
    flag = CLOSING;
    send release indication;

    id = timeout(timeout_handler, ...);
    sleep(&thing, ...);

    untimeout(id);
    if (flag != CLOSED || sleep was interrupted) {
            recover;
    }
    else {
        finish closing;
    }
}

handle_orderly_response()
{
        flag = CLOSED;
        wakeup(&thing);
}

timeout_handler()
{
        wakeup(&thing);
}

For Windows NT, the STREAMS stack must use a Kernel synchronization event to indicate that the response to the orderly release request has arrived. This object has the desirable property that it remains set until a thread waits on it and then automatically resets itself when the wait has been satisfied.

If several threads must wait on an object, then a Kernel notification event provides nearly the same semantics as sleep and wakeup. All waits for the event are satisfied when it is set to signaled, but it remains in the Signaled state until explicitly reset. The converted pseudocode is:

#include <ntddk.h>
#include <stream.h>

KEVENT CloseEvent;

egopen(...)
{
    KeInitializeEvent(&CloseEvent, SynchronizationEvent, FALSE);
}

egclose(
    IN queue_t *rq,
    ...
)
{
    NTSTATUS status;
    LARGE_INTEGER timeout_value;

    timeout_value = StrmConvertCentisecondsToRelativeTimeout(1000);
    send release indication;

    status = StrmWaitForSingleObject(
                rq,                 // current queue
                &CloseEvent,        // pointer to the dispatcher object
                UserRequest,        // reason for the wait
                KernelMode,         // processor mode to wait in
                FALSE,              // is this wait alertable?
                &timeout_value);    // timeout period

if (status == STATUS_TIMEOUT) {
    recover;
}
else if (!NT_SUCCESS(status)) {
    recover;
}
else {
    finish closing;
}

handle_orderly_release_response()
{
    KeSetEvent(&CloseEvent, 0, FALSE);
}

The test for STATUS_TIMEOUT after the wait returns is necessary because the NT_SUCCESS macro does not consider STATUS_TIMEOUT an error and will return TRUE. Once again, please remember that a driver absolutely must not wait while holding a spin lock. However, issuing a wait call with a timeout of 0 guarantees that the call will not block and can be made while a spin lock is being held. Under some circumstances while a spin lock is being held, it can be more efficient for the driver to make a 0 timeout call first. Only if this wait fails with a STATUS_TIMEOUT is the driver required to relinquish the spin lock and issue a real wait call.


B.8.4 Using Paired NetBIOS Providers

A NetBIOS provider must closely couple its connection-oriented services with its connectionless services. However, through TPI, a STREAMS stack must declare itself as either a connection-oriented (T_COTS) or a connectionless (T_CLTS) provider. Therefore a STREAMS NetBIOS provider has to be implemented via two drivers: a T_COTS driver and a T_CTLS driver. For example, NetBIOS over TCP/IP is provided by the following STREAMS drivers:

\Device\Streams\nbt

\Device\Streams\nbdg

Example: DriverEntry Routine for nbt.sys

#include <ntddk.h>
#include <tdi.h>
#include <stream.h>
#include <xti.h>

**********************
extern struct streamtab nbtinfo, nbdginfo;

static struct nbaddr BroadcastName = {
    NBBROADCAST | NBGROUPNM,
    "*"
};

static STREAMS_TDI_ADDRESS_INFO AddressInfo = {
    sizeof(BroadcastName),                  // Broadcast address length
    TDI_ADDRESS_TYPE_NETBIOS,               // TDI address type
    TRUE,                                   // Directed message goes farther
                                            // than broadcast or multicast
    &BroadcastName                          // Broadcast address
};

STREAMS_TDI_INFO NbtTdiInfo = {
    T_COTS_ORD,                             // SERV_type
    &nbdginfo,                              // Partner streamtab
    T_CLTS,                                 // Partner SERV_type
    0,                                      // Option length
    NULL,                                   // Option pointer
    TRUE,                                   // Reuseaddr set by default
    1,                                      // One address info structure
    &AddressInfo                            // AddressInfo
};

STREAMS_TDI_INFO NbdgTdiInfo = {
    T_CLTS,                                 // SERV_type
    &nbtinfo,                               // Partner streamtab
    T_COTS_ORD,                             // Partner SERV_type
    0,                                      // Option length
    NULL,                                   // Option pointer
    TRUE,                                   // Reuseaddr set by default
    1,                                      // One address info structure
    &AddressInfo                            // AddressInfo
};

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT driver,
    PUNICODE_STRING description
    )
{
    NTSTATUS status;

    status = ExtractRegistry();

    if (status != STATUS_SUCCESS) {
        return(status);
    }

    if (nbtinit()) {
        return(STATUS_NO_MEMORY);
    }

    status = StrmRegisterDriver(driver, &nbtinfo, NULL, &NbtTdiInfo);

    if (status != STATUS_SUCCESS) {
        return(status);
    }
    status = StrmRegisterDriver(driver, &nbdginfo, NULL, &NbdgTdiInfo);

    if (status != STATUS_SUCCESS) {
        return(status);
    }

    return(status);

}


B.8.5 Plumbing a Stack

Since STREAMS does not support persistent links (Section B.5.3.1), a user-level daemon must maintain the plumbing for the life of the STREAMS stack.


B.8.5.1 Opening a STREAMS Driver

Use the s_open function exported by WINSTRM.DLL to obtain a handle to a stream to an STREAMS transport driver. The path passed into the s_open call should be of the form \Driver\Streams\tname where tname is the name of the transport driver as defined in the streamtab structure. Note that a double \\ is necessary in a quoted C string.


B.8.5.2 Binding a Stream to a Network Adapter Device

Usually, the lower edge of a STREAMS stack must be bound to one or more network adapter devices. This is best done by having the plumbing daemon for the stack issue a DATAL_IBIND IOCTL command to the dndis driver (open \Device\Streams\dndis).

Example: Associating a Stream with the Lance Network Adapter

#include <dl_ctrl.h>

cf_ndisdrv()
{
    int result;
    HANDLE fd;
    struct strioctl StrIoctl;
    struct datal_ibind DatalIbind;

    fd = s_open("\Device\Streams\dndis", 0, 0);

    strncpy(
        DatalIbind.buffer,
        "\Device\Lance01",
        sizeof(DatalIbind.buffer) - 1
        );

    RtlInitString(&DatalIbind.adapter_name, DatalIbind.buffer);

    Strioctl.ic_cmd = DATAL_IBIND;
    StrIoctl.ic_len = sizeof(DatalIbind);
    StriIoctl.ic_dp = (char *) &DatalIbind;

    result = s_ioctl(fd, I_STR, &StrIoctl);

    // now I_LINK fd under the ip driver
    ...
}


B.9 Porting a Streams Subsystem-Parallel Stack

Porting a subsystem-parallel STREAMS stack to Windows NT, requires the following:

  1. Build the stack into Windows NT driver(s).

  2. Set up the Registry to load the Windows NT driver(s).

  3. Port the plumbing daemon to a Windows NT service.

  4. Protect the stack from concurrency by insulating its boundaries.

For steps 1-3, Section B.8.3 discussions for fully parallel STREAMS stacks also apply to subsystem-parallel stacks. The details of step 4 are discussed next.


B.9.1 Registering the Components of a Subsystem-Parallel Stack

Every STREAMS driver and module in the subsystem-parallel stack must identify itself as belonging to that stack. This is done by specifying the same nonNULL third argument to StrmRegisterDriver and StrmRegisterModule.

Example: DriverEntry Routine for a Subsystem-Parallel tcpip.sys

#include <ntddk.h>
#include <stream.h>

#define    SUBSYSTEM_NAME        "tcpip_up"

extern int arpinit(void);
extern int tcpinit(void);

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT driver,
    IN PUNICODE_STRING device
    )
{
    NTSTATUS status;

    status = StrmRegisterModule(driver, &arpinfo, SUBSYSTEM_NAME);

    if (status != STATUS_SUCCESS) {
        return(status);
    }

    status = StrmRegisterDriver(
                  driver,
                  &tcpinfo,
                  SUBSYSTEM_NAME,
                  NULL
                  );

    if (status != STATUS_SUCCESS) {
        return(status);
    }

    arpinit();
    tcpinit();

    return(STATUS_SUCCESS);
}


B.9.2 Protecting a Subsystem-Parallel Stack from Concurrency

In protecting a subsystem-parallel stack from concurrency, the fundamental issue is to ensure that only one put or service procedure within the stack is executing at any given instance. This procedure must execute to completion.

Figure B.9.1 Protecting a subsystem-parallel stack from concurrency


B.9.2.1 Entering the Subsystem-Parallel Stack

The STREAMS environment guarantees that only one open, close, or service procedure from the subsystem-parallel stack is executing at any given time. It also ensures that the service procedure completes before another service procedure is run. However, this protection is insufficient.

In STREAMS, both the stream head and the dndis driver are fully parallel. At a given moment, many instances of the read put procedure of dndis and the write put procedure of the stream head can be executing. These instances must be prevented from entering the sybsystem concurrently.

The solution to this problem is to serialize the concurrent putnext (Section B.3.1) calls, which can be done in either of two ways:

  1. Make the subsystem boundary put procedures queue every message with the function putq (Section B.3.1) and defer all processing to the service procedure for its queue.

  2. Plumb an insulating module between the subsystem-parallel stack and all other drivers. This module declares itself as part of the subsystem-parallel stack. Its put procedure queues every message with putq, and its service procedure handles every message using the putnext function.

The key to these two methods is that the put procedure at the boundary of a stack must defer all processing to its service procedure. The service procedures of a stack are serialized by the STREAMS environment. An insulating module is required at both the lower read and upper write queues of a subsystem stack.


B.9.2.2 Using Insulating Modules

Plumbing an insulating module at the boundary of a subsystem-parallel stack can be easier than modifying the put procedures at the boundary. However, pushing a module onto a stream can disrupt flow control. The high- and low-water marks of queues for an insulating module must be chosen carefully. Source code for an example insulating module is included in the Windows NT DDK.


B.9.3 Leaving a Subsystem-Parallel Stack (Exit Module)

In STREAMS, a thread executing a service procedure remembers if it is executing within a subsystem-parallel stack, which is necessary if the thread calls bufcall or timeout while it is within the stack. This feature preserves the subsystem-parallel stack requirement of a nonpreemptive uniprocessor environment.

If the thread leaves the stack by calling the put procedure of the next queue, STREAMS must be notified. The mechanism for this is the exit module. The stack includes an upper exit module and a lower exit module, similar to insulating modules, one for each boundary of a subsystem. The upper exit module queues all puts on its read queue to its service procedure.

The lower exit module queues all puts on its write queue to its service procedure.

The plumbing daemon of a subsystem-parallel stack must ensure that the appropriate exit modules are pushed above and below the stack. Sample source code for the exit modules is included in the Windows NT DDK. The insulating and exit modules may be combined into a single module if desired.


B.9.4 Blocking Within a Subsystem-Parallel Stack

Within a subsystem-parallel stack, it is very important that open and close procedures call the STREAMS functions StrmWaitForMutexObject, StrmWaitForMultipleObjects, and StrmWaitForSingleObject, instead of the functions KeWaitForMultipleObjects and KeWaitForSingleObject. This functional distinction is required because the STREAMS environment enforces serialization within a subsystem-parallel stack by the use of a per-subsystem Kernel mutex object. When a close procedure blocks, it expects other put or service procedures to run while it is blocked. The StrmWaitFor* functions release the per-subsystem mutex object before blocking, and reacquire the mutex object after being unblocked.


B.10 Debugging a Streams Driver

The strmdbg utility provides some useful facilities for debugging STREAMS drivers. It provides services to track the allocation/deallocation of STREAMS messages and the allocation/deallocation of kernel pool memory. It also provides spin lock usage tracing and queue status information.

Note The strmdbg command only operates on a checked version of STREAMS.SYS. Supporting this tool required modifying the definitions of some of the functions exported by STREAMS.SYS. Macros in the header file debugapi.h transparently redefine the necessary functions to implement the debugging features. Thus, a free build of streams.sys and a checked version of a driver will not interoperate, and vice versa. The transport driver will fail to load if they are mixed because the loader will be unable to resolve some of the driver’s imports. The Service Controller will log an event if this situation occurs with drivers loaded under its control.

The output of the program is to the kernel debugger. The basic command is:

    strmdbg


B.10.1 Status of Active Queues

With the strmdbg command, a snapshot of the state of all active queues can be output on the kernel debugger, which is useful for finding clogged queues and stuck messages. The format of the command for status of active queues is:

    strmdbg -q


B.10.2 STREAMS Message Tracing

The STREAMS message-tracing facility can be used to track down any unfreed messages. When enabled, STREAMS tracks all allocated messages in an internal table. The file name and line number of the allocation are tracked. The following command formats are available:

    strmdbg -m flush            // flushes the tracing table
    strmdbg -m on               // enables message tracing
    strmdbg -m off              // disables message tracing
    strmdbg -m print            // dumps the table to the debugger


B.10.3 STREAMS Pool Tracing

The STREAMS pool-tracing facility can be used to trace allocations from pool memory. This facility operates similar to the message-tracing feature. The following command formats are available:

    strmdbg -p flush            // flushes the tracing table
    strmdbg -p on               // enables pool tracing
    strmdbg -p off              // disables pool tracing
    strmdbg -p print            // dumps the table too the debugger

Code that modifies pool memory after deallocating it can be tracked by tracing recent allocations. The following command provides this facility:

    strmdbg -n on               // enables pool history tracing
    strmdbg -n off              // disables pool history tracing

STREAMS maintains a circular log in memory of recent pool operations by address, size, file, and line number. An individual log is kept for each processor. The log can be dumped by issuing the following commands at the kernel debugger prompt:

    da streams!StrmPoolLogList
    da
    da
    ...

The next entry in the circular buffer will be marked as will the end of the log for each processor.


B.10.4 Spin Lock Usage

Spin lock usage can be traced using a method similar to the pool-allocation logging. This facility is useful for finding code that returns without releasing a lock. A log of recent p_lock and v_lock activity is kept in the kernel data location StrmLockLogList, a circular buffer. The log can be dumped using the Windows NT kernel debugger commands:

    da streams!StrmLockLogList
    da
    da
    ...

Spin-lock tracing is not actually part of the strmdbg services. The “-l” switch displayed by the command line help of strmdbg turns on lock tracing only for streams.sys and has no effect on transport drivers. To make use of the spin lock tracing facility, you must include macros similar to the following in your code:

#if DBG

#define KeAcquireSpinLock(PSpinLock, POldIrql)                 \
    ( (TcpDebugBits & DBG_TCP_LOCKTRACE) ?                     \
           StrmAcquireSpinLock(PSpinLock, POldIrql, __LINE__, __FILE__) \
                                                               \
           StrmAcquireSpinLock(PSpinLock, POldIrql, 0, NULL)   \
    )

#define KeReleaseSpinLock(PSpinLock, OldIrql)                  \
    ( (TcpDebugBits & DBG_TCP_LOCKTRACE) ?                     \
           StrmReleaseSpinLock(PSpinLock, OldIrql, __LINE__, __FILE__)  \
                                                               \
           StrmReleaseSpinLock(PSpinLock, OldIrql, 0, NULL)    \
    )

#endif

The Strm..SpinLock functions export by STREAMS.SYS will log an operation if their fourth argument is nonNULL.


Network Glossary

A

A/C (access control)

Refers to token ring frames.

ACE

1. access control entry

An ACE is an individual entry in an ACL. An ACE contains a SID and describes the access rights a particular user or group of users has to a system resource. The set of all ACEs on an object are used to determine whether an access request to the object is granted. See also security descriptor.

2. advanced computing environment

A consortium of hardware and software companies formed to promote an open computing environment. Windows NT is one of the original ACE operating systems.

ACL (access control list)

An ordered list of ACEs.

address object

The context collection associated with a local transport driver address. An address object is associated with a file object so several clients can open the same address object. The address object implicitly provides the local addressing information for a connection endpoint.

APC (asynchronous procedure call)

An APC is a Kernel-defined control object representing a procedure that is called asynchronously. APCs are thread-context dependent; that is, they are queued to a particular thread for execution.
There are three different kinds of APCs in Windows NT:

1. User APCs ¾ These APCs are used by certain asynchronous Windows NT system services to allow user-mode applications or protected subsystems to synchronize the execution of a thread with the completion of an operation or the occurrence of an event such as a timer’s expiration. User APCs are, by default, disabled. That is, they are queued to the user-mode thread, but they are not executed except at well-defined points in the program. Specifically, they can only be executed when an application or protected subsystem has called a wait service and has enabled alerts to occur, or if it has called the test-alert service.

2. Kernel APCs ¾ These APCs are normal kernel-mode APCs. They are much like a normal user APC except they are executable by default. That is, they are enabled except when the thread is already executing a kernel APC. (Note that a special always kernel preempts these.)

3. Special Kernel APCs ¾ These APCs cannot be blocked except by running at a raised IRQL. They are executed at APC_LEVEL IRQL (see IDT), in kernel mode. These types of APCs are used by the system to force a thread to execute a procedure in the thread’s context. An example of this is I/O completion: the I/O Manager needs to get back into the context of the original requestor of the I/O operation so it can copy buffers, and so forth. In order to do this, the I/O Manager must be able to access the virtual address space of the thread/process, and the most efficient way to complete the operation is to be in the calling thread’s context.


B

binding

A process during which a transport driver and a netcard driver exchange information, using the NDIS interface library wrapper, about identities, capabilities, function addresses, and binding context.

Also, a process by which the Windows NT configuration registry installs and associates the components of a network.

broadcast address

A 48-bit identifier transmitted over a network with all bits set to 1. All networks receive broadcast data packets.

buffer

A virtually contiguous block of storage used to hold a portion of a data packet. A buffer descriptor structure describes a buffer.

buffer descriptor

A data structure containing information about a buffer. This information includes, for example, a pointer to buffer physical location, number of bytes, and size. It does not include the actual data in the buffer.

busmaster

An adapter card that does direct memory access and that can take control of the bus whenever necessary.


C

CAM (content-addressable memory)

A netcard driver accesses CAM using a data field rather than a data address.

connection

A long-term communication association between two clients on a network. Communication through a connection is expected to be reliable and error-free.

connection endpoint

The context collection associated with one end of a connection. There is exactly one connection between two connection endpoints. Each connection endpoint is associated with exactly one address object, but an address object can be associated with several connection endpoints. A connection endpoint is identified by a connection endpoint identifier.

context

The execution state of a thread at any given moment:

· For a user-mode thread, the platform-dependent register state, kernel stack, TEB, and user stack in the address space of the process to which the thread belongs.

· For a kernel-mode thread, the platform-dependent register state and kernel stack.

Kernel-mode threads have neither a TEB nor a user-mode context, but they must have an associated process. See also process object.

Most Windows NT device drivers do not have a context in this sense. Unless a driver (such as an FSD) creates its own process and/or thread(s), it does not have its own stack space or register state. For each driver, the set of objects it owns and the IRPs that it can access in the device queue associated with its device object or any driver-created internal queue(s) can be considered all or part of its context.

control channel

A TDI client binding to a TDI driver. The client uses the control channel to query and set global information.

CRC (circular redundancy check)

A method of ensuring data integrity for transmissions over a network. The receiving node uses the CRC value in a packet for comparison with a value calculated by using the same algorithm the sender uses to calculate the original CRC value.

CTS (clear to send)

A type of signal used on a LocalTalk network.


D

device object

A kernel-mode-only, I/O Manager-defined object type, used to represent a physical, logical, or virtual device whose driver has been loaded into the system. Each driver calls IoCreateDevice to initialize a device object for each device that driver services. See also driver object.

Devices are “visible” to end users as named file objects, stored in a directory protected against careless or malicious user-mode access. User-mode code (protected subsystems) must access any device through the opaque handle returned by the system service that opens the file object that represents the device.

DIRQL (device interrupt request level)

See also IRQL and IDT ¾ This particular IRQL refers to the IRQL at which a given device interrupts.

DIX

DEC/Intel/Xerox. DIX designates a type of network.

DLL (dynamic link library)

A software library that resolves relocatable entry points at run time.

DLPI (datalink provider interface)

DMA (direct memory access)

No different from any other OS.

DPC (deferred procedure call)

A DPC is a kernel-defined control object type, representing a procedure that is to be called later. DPCs are executed in kernel mode at IRQL DISPATCH_LEVEL (see IDT and IRQL).

A DPC is primarily used when an ISR, such as device driver’s or clock interrupt service routine, needs to perform more work but should do so at a lower IRQL than the one at which an ISR executes. Getting the IRQL lowered back down quickly is important for overall system response time. The ISR, then, can request that a procedure be executed at a lower IRQL at a later time.

The kernel queues DPC objects in a single, system-wide DPC queue, and other executive components and drivers insert DPCs into the queue by calling kernel-supplied support routines. The DPC queue is monitored by each of the processors in the system whenever a DISPATCH_LEVEL interrupt occurs on the individual processor or when the processor is idle. When a DPC queue entry is dequeued, the kernel calls the procedure.

DPR (deferred processing routine)

This is a netcard driver function that completes interrupt processing for an ISR.


E

EISA (extended industry standard architecture)

A standard that defines the architecture of (and interface to) the extended PC bus. See also ISA.

ENDEC (encoder/decoder)

event object

A user-mode object upon which one or more threads can wait until the event is set to the signaled state. Every user-mode event object is implemented through the use of a kernel-mode event object.

A kernel-mode event object is an instance of a kernel-defined dispatcher object type. Each kernel-mode event can be classified as either of the following:

1. When a synchronization event (a.k.a. “autoclearing event”) is set to the signaled state, a single thread waiting on the event is released (its dispatch state transitions from waiting to ready, standby, or running), and an autoreset to not-signaled occurs.

2. When a notification event is set to the signaled state, all threads waiting on the event are released, and the event remains in the Signaled state until an explicit reset to not-signaled occurs.


F

FDDI (fiber distributed data interface)

FCS (frame check sequence)

An FCS is a data validation method similar to a CRC but is used only for token ring networks.

FIFO (first in, first; out)

FSD (file system driver)

The FSD refers to the portion of a driver running in the context of the caller, to which an I/O request is dispatched when the request is originally made. The FSD has no process virtual address space and no particular thread context because it operates in the context of whoever happens to call it.

An FSD also executes in the context of the lower-level (intermediate or mass-storage device) driver’s DPC routine when the I/O operation is being completed if the FSD indicated that it should be called upon completion of the IRP.

The term FSD normally refers to the DD (device driver) part of a file system driver only when the driver also has an FSP associated with it; otherwise, it’s called simply a device driver.

FSP (file system process)

The FSP usually refers to a set of device-dedicated or system worker threads that aid a file system driver (FSD) in getting work done, but can also refer to a kernel-mode process set up by an FSD. FSP threads are generally used when a file system driver needs a thread context in which to work. An example is when the driver needs to be able to wait without tying up the calling thread. An FSP kernel-mode process is used only when an FSD must have a driver-specific process address space in which its device-dedicated threads execute. Most Windows NT file system drivers conserve system memory and improve performance by not creating their own kernel-mode processes.

Communication between the FSD and the FSP threads is performed through the use of a communication region. This region is generally allocated in a device object’s device extension and consists of an interlocked queue and a synchronization event. See also device object, device extension, and event object.

Any Windows NT driver (not just file systems) can create one or more device-dedicated threads or use system worker threads. In particular, an NT driver should create a thread for I/O operations that use synchronization mechanisms (such as events, timers, semaphores, mutexes, etc.) other than spin locks. Such a driver’s thread can wait for an indefinite, nonzero interval on a kernel-defined dispatcher object, but context switches to the thread slow down the driver’s I/O operations.

Functional Address

A 48-bit identifier (as transmitted over a token ring network) describing the type of machine that should receive a packet. Most of the functional address is a bitmask in which each bit indicates a type of receiver. A client or driver can combine bits to send a packet to several receivers.


H

HAL (hardware abstraction layer)

A Windows NT executive component providing platform-specific support for the Windows NT Kernel, I/O Manager, kernel-mode debuggers, and lowest-level device drivers. The HAL exports routines that abstract platform-specific hardware details about caches, I/O buses, interrupts, etc. and provides an interface between the platform’s hardware and the system software. For example, the HAL implements a routine to map each device driver’s bus-relative device interrupt vector to a system-assigned vector with a corresponding platform-specific hardware priority (DIRQL), as well as several routines that provide DMA-transfer support.


I

IC (integrated circuit)

Intermediate driver

An NDIS driver that exports a set of MiniportXxx functions at its upper edge and a set of ProtocolXxx functions at its lower edge. This driver is binds to an underlying NDIS NIC driver and is bound to by an overlying transport driver.

The other type of intermediate driver exports a set of MiniportXxx functions at its upper edge, has a nonNDIS private interface at its lower edge, and binds to an underlying nonNDIS NIC driver.

IDT (interrupt dispatch table)

A kernel-defined call table with a platform-dependent number of first-level entries (some for DIRQL ISRs) and second-level entries for interrupt transfer routines (dependent on a first-level ISR). For symmetric multiprocessor platforms, the kernel sets up an IDT for each processor. See also multiprocessor machine.

The Windows NT Kernel reserves eight first-level entries per IDT for its own use; the remaining first-level entries can be connected to a platform-specific bus interrupt by the HAL or to a device interrupt. The kernel’s reserved entries (in low-to-high IRQL priority) are defined by the following constants:

· PASSIVE_LEVEL ¾ execute thread

· APC_LEVEL ¾ execute special Kernel APC

· DISPATCH_LEVEL ¾ dispatch (execute DPC)

· WAKE_LEVEL ¾ debugger execution

· CLOCK2_LEVEL ¾ interval-timer execution

· REQUEST_LEVEL ¾ interprocessor request

· POWER_LEVEL ¾ power failure notification

· HIGH_LEVEL ¾ machine checks or bus errors

The set of software and hardware interrupt vectors mapped to these constants is platform-dependent, but none can be connected to a driver’s interrupt object. The constants PASSIVE_LEVEL through DISPATCH_LEVEL and WAKE_LEVEL correspond to software interrupt vectors. DIRQLs for device drivers usually have hardware priority higher than DISPATCH_LEVEL but lower than CLOCK2_LEVEL.

IP (internet protocol)

interrupt

An asynchronous hardware signal, usually indicating a peripheral device needs service, that is detected by the processor. An interrupt causes the processor to save state, to branch to a fixed location, and to resume execution at that location. See also IDT.

interrupt object

A kernel-mode-only, kernel-defined control object type, used to connect a hardware interrupt source (see DIRQL) and an ISR to an IDT entry, or to connect an ISR and an interrupt transfer routine (dependent on the ISR at its IRQL) to a second-level IDT entry.

Each interrupt object:

· Can be associated with a single IDT entry (and, therefore, with a single processor in a multiprocessor machine)

· Is either LevelSensitive or Latched, depending on the platform or device

· Can be one of several interrupt objects associated with a given IDT entry if and only if every interrupt object for that entry is of a single type (LevelSensitive or Latched)

For a second-level IDT entry, an ISR, such as an ISR for a bus adapter that is associated with a first-level IRQL routes the interrupt to the device-specific interrupt transfer routine for service via its entry in the IDT.

When more than one set of interrupt objects are connected to the same first-level IDT entry, the corresponding ISRs are called in the same order in which they were connected.

IPX (internet packet exchange)

IRP (I/O request packet)

An IRP is the basic I/O Manager structure used to communicate with drivers and to allow drivers to communicate with each other. A packet consists of two different parts:

1. Header, or fixed part of the packet ¾ Used by the I/O Manager to store information about the original request, such as the caller’s parameters, the address of the device object upon which the file is open, etc. It is also used by drivers the request. See also I/O status block and device object.

2. I/O stack locations ¾ Following the header is a set of I/O stack locations, one per driver in the chain of layered drivers for which the request is bound. Each stack location contains the parameters, function codes, and context used by the corresponding driver to determine what it is supposed to be doing.

IRQ (interrupt request lines)

A hardware line over which a peripheral device, bus controller, other processor, or the kernel signals a request for service to the microprocessor. See also interrupt.

IRQL (interrupt request level)

The hardware priority level at which a given kernel-mode routine runs, thereby “masking off” interrupts with equivalent and lower IRQL on the processor. Such a routine can be preempted by any interrupt with a higher IRQL. Also, running at IRQL DISPATCH_LEVEL or higher prevents threads (even those with the highest real-time priority level) from running on the same processor until the current kernel-mode routine lowers IRQL. However, running at raised IRQL on a given processor has no effect on the IRQL of any other processor in a symmetric multiprocessor machine. See also IDT, interrupt object, IRQ, ISR, multiprocessor machine, and priority.

ISA (industry standard architecture)

Also, a standard defining the architecture of the PC bus (a.k.a. “AT bus standard”).

ISR (interrupt service routine)

A routine whose function is to service a device when it generates an interrupt.

An Windows NT driver’s ISR executes at raised IRQL, usually at the DIRQL of the interrupt object(s) set up for its device (i.e., drivers connected to second-level IDT entries execute at the IRQL of the first-level entry they connect to). Every Windows NT driver’s ISR should execute as quickly as possible, doing only what is necessary to save sufficient state, to make the device stop generating interrupts, and to queue a DPC that completes interrupt processing at a lower IRQL. See also DPC object.


L

LAN (local area network)

LAN types include: Ethernet (802.3), token ring (802.5), FDDI, LocalTalk, DIX.

logical adapter

A software object representing a single netcard.


M

MAC or Mac (media access controller)

Refers to a specific type of netcard driver.

MCA

Microchannel architecture, a type of backplane architecture that only accepts a compatible netcard.

MDL (memory descriptor list)

A memory descriptor list uses an array of physical page frame numbers (PFNs) to describe the pages that back a virtual memory range.

MIB (managed information block)

The MIB is the set of netcard driver objects that represent information about driver capabilities and current status. The MIB identifies a managed object by a 32-bit OID.

monolithic

A type of driver that includes the functions of both a transport driver and a netcard driver.

multicast address

A 48-bit identifier (as transmitted over an Ethernet or FDDI network) naming the group of stations that should receive a data packet. Instead of being a bitmask (see functional address), a multicast address simply contains a unique group number by which receiving stations can request packets.

multiprocessor machine

A platform with more than one CPU. Windows NT is designed to run on multiprocessor machines, usually configured to be:

· Homogenous ¾ all CPUs are identical, and either all have identical coprocessors or none has a coprocessor.

· Closely coupled ¾ all CPUs share memory and have uniform access to memory.

· Symmetric ¾ every CPU can access memory, handle any interrupt, and access I/O control registers just like every other CPU in the system. Note that any version of Windows NT that runs on a symmetric multiprocessor machine also runs unchanged on a uniprocessor machine based on the same CPU.

While Windows NT can run on asymmetric platforms, configured with a master CPU that handles all IRQs and one or more slave CPUs as long as the platform is both homogenous and closely coupled, the system has been designed to run primarily on symmetric multiprocessor platforms because SMP machines have better performance characteristics, particularly for I/O.


N

NCPA (network control panel applet)

The configuration tool for Windows NT.

NLPID (network-level protocol identifier)

NDIS (network driver interface specification)

Version 4.0 unless otherwise specified.

NDIS library

A DLL containing a set of abstract functions that interface between transport driver functions, intermediate driver functions and netcard driver functions, submit a request to the operating system, or cause a local action that does not require communication with other software functions. The main purpose of the NDIS library is to form a wrapper that allows network drivers to send and receive packets on a LAN or WAN in an operating system-independent manner.

netcard

A network adapter card residing in a computer and controlled in its transmission and reception of network information by a particular netcard driver.

netcard driver

A driver that provides datalink layer functions to higher-level network drivers through the NDIS interface library. A single netcard driver can support numerous netcards of a particular type.

NIC (network interface card)

A netcard.

NVRAM (nonvolatile RAM)


O

octet

A set of eight bits transmitted over a network.

OID (object identifier)

An OID is a 32-bit identifier a netcard driver uses to denote an object in its MIB.

OOB (Out of Band)

Out of band data, if supported by and supplied by a network driver, is associated with a packet descriptor. OOB data can contain one or more of the following: media-specific information, priority and timestamps. NDIS drivers access OOB data using NDIS-supplied macros.

OSI (open systems interconnection)


P

Packet

A chain of one or more buffers that composes a network message. A packet descriptor describes a packet.

packet descriptor

A data structure with both a public component and a private component. The visible public component contains, for instance, a pointer to a linked list of buffers, the number of physical buffers, and the length of the packet. If supported by the driver originating the packet descriptor, each packet descriptor has associated OOB data containing media-specific information, priority information and timestamps. A packet descriptor contains a network driver-reserved field that an NDIS driver uses to link packets into transmit and receive queues.

PCI (peripheral component interconnect)

A high speed bus.

PFN (page frame number)

Also, the database in which the Windows NT Memory Manager maintains information about every physical page in the system. Each PFN is an index for a physical page in the array of records that makes up the PFN database. See also PTE.

pool

A block of storage from which an NDIS 3.0 driver allocates buffer descriptors or packet descriptors.

POS (programmable option select)

POS indicates a type of register on an MCA netcard.

priority

An attribute of a thread that determines when and how often it is scheduled to run. For a running thread, its priority falls into either of two classes, each class with 16 levels:

1. Variable priority class has values in the range 0 to 15. This class is used by most threads.

Threads with variable priority are always preemptible; that is, they are scheduled to run round-robin with other threads at the same level. In general, the kernel manages a variable-priority thread as follows: when the thread is interactive with a user, its priority is high (given a boost); otherwise, its priority decays by one level per quantum the thread runs until it reaches its original programmer-defined base level.

2. Real-time priority class has values in the range 16 to 31. This class is used by time-critical threads, making such a thread preemptible only by a thread with higher priority. A thread running at the highest real-time priority level can be preempted by a special kernel APC if an APC_LEVEL interrupt occurs.

process object

A kernel-defined control object type, representing the virtual address space and control information necessary for the execution of a set of thread objects. A user-mode process object defines the base priority, default affinity, and directory table base value for its threads and for any child processes it creates. Every user-visible process object is implemented through the use of an embedded kernel-mode process object.

A kernel-mode-only process object must be initialized before any thread objects that belong to the process.

At any given moment, a process is either part of the balance set (Included) or not (Excluded). A kernel-mode process must remain in the balance set as long as any of its threads owns a mutex object. Each thread owning one or more mutexes continues to run until it releases its last mutex, when the thread is suspended. The process is not removed from the balance set until every thread has released all mutexes.

PTE (page table entry)

The Memory Manager uses a PTE to represent the state of a virtual page.


Q

QOS (Quality of service)


S

SACL (system access control list)

A part of the Windows NT security descriptor for an object, used to maintain per object auditing information. See also security descriptor, ACE, and ACL.

SAP (service access point)

security descriptor

A data structure used to hold per-object security information, including the object’s owner, group, protection attributes, and audit information. See also ACE, ACL, SACL, and SID.

SID (security identifier)

A value, unique across time and space, that identifies a process in the Security system. SIDs can either identify an individual process, usually containing a user’s logon identifier, or a group of processes.

SMP machine (symmetric multiprocessor machine)

See multiprocessor machine.

SNAP (sub-network access protocol)

SONIC (systems-oriented network interface controller)

SONIC or Sonic

spin lock

A spin lock is a low-level, synchronization mechanism defined by the Windows NT Kernel. A driver or any other kernel-mode component that runs at raised IRQL can use a spin lock to synchronize access to a shared resource, particularly in a multiprocessor machine.

When one routine holds a spin lock, a second routine running on another processor cannot access the resource protected by that spin lock until it acquires the lock. While a routine attempts to acquire a spin lock, it cannot carry out other operations (it spins) until the lock has been acquired.

Operations involving system-defined spin locks are IRQL-specific to help prevent deadlocks: an interrupt spin lock can only be acquired while executing at a DIRQL of the associated interrupt object(s); an executive spin lock can only be acquired while executing at the highest IRQL (usually DISPATCH_LEVEL) of any routine that can acquire that executive spin lock.

STREAMS

A network communication environment based on UNIX STREAMS. Windows NT STREAMS facilitates the porting of existing stream transport drivers to Windows NT.

SVR4

System V, Release 4 (UNIX).


T

TCP (transmission control protocol)

TDI (transport driver interface)

TDI is a native Windows NT interface between a client and a transport driver. The LAN Manager redirector and server, Windows Sockets, and other NetBIOS interface libraries use TDI.

TDI driver

A driver providing transport layer services to clients with connection-oriented, error-free, end-to-end delivery of data. Optionally, a TDI driver can provide a connectionless mode of data transfer, using a special TSDU called a datagram.

TDI memory descriptor

Memory descriptor associated with a TDI client or driver.

TEB (thread environment block)

TLI (transport layer interface)

TPCTL (test protocol control program)

This NDIS 3.0 netcard driver tester program runs in the Win32 user mode to provide a visual and control interface.

TPDIFF (test protocol differences program)

This NDIS 3.0 netcard driver program runs in Win32 user mode to compare log files for a test run with validation logs prepared at installation.

TPDRVR (test protocol driver program)

This NDIS 3.0 netcard driver tester program runs in the kernel mode to simulate an NDIS 3.0 transport driver for testing.

TPI (transport provider interface)

transport address

A variable-length string identifying a local or remote node address object. A transport address includes any necessary network layer addressing information. The syntax and semantics of a transport address are transport driver-specific.

transport driver

A driver generating and responding to higher protocols encapsulated by the netcard driver. A transport driver provides a variety of functions, including bridging. The TDI driver and Windows NT STREAMS transports are examples of transport drivers that execute under Windows NT.

TSAP (transport service access point)

TSDU (transport service data unit)

A TSDU is a unit of data transfer at the TDI level, either a message or a segment of a stream. The TDI client issues send or send datagram requests to send a TSDU. Some transport drivers cannot support message-oriented TSDUs and must use a stream transmission mode instead.


U

UDP (user datagram protocol)


W

WAN (wide area network)

A WAN uses various types of point-to-point and WAN netcards. This network type has a subtype for each netcard, as well as an address and header format negotiated between the transport driver and the netcard.


Chapter 1 NDIS Driver Upper-Edge Functions

This describes, in alphabetic order, the system-defined functions implemented in NIC drivers and in intermediate drivers that export a set of NDIS upper-edge functions.

Two types of NDIS drivers have some or all of the functions described here:

  1. NDIS miniports, whether for LAN or WAN NICs, export the DriverEntry and MiniportXxx functions described here.

    NIC drivers for WAN media export the same functions as NIC drivers for LAN media with one exception: a MiniportWanSend function replaces the MiniportSendPackets or MiniportSend function of LAN NIC miniports. Each of these MiniportXxx functions is described later.

  2. Intermediate drivers export a subset of the MiniportXxx functions described here and a DriverEntry function that fulfills the initialization-time requirements of the NDIS library.

    Intermediate drivers also can export a set of the NDIS Driver Lower-Edge Functions, described later, or they can use another interface to communicate with underlying drivers of devices other than a NIC.

    NDIS intermediate drivers export both ProtocolXxx and MiniportXxx functions, but they need not have certain functions, such as MiniportISR or MiniportTimer, that any NIC driver must have to communicate with and control its NIC.

Driver Function Names

All NDIS driver functions with the prefix Miniport have been given metanames describing their respective basic functionality in the alphabetical references that follow.

Except for the DriverEntry function, NDIS driver functions can have any name the driver writer chooses. For easy debugging, most system-supplied miniports simply substitute a driver-specific prefix for the Miniport prefix used in this documentation.

The initial entry point of any Windows NT kernel-mode driver has the explicit name DriverEntry in order to be loaded. For more information about the general requirements for and functionality of Windows NT DriverEntry routines, see also the Kernel-Mode Driver Design Guide.

Calling NdisXxx Functions at Appropriate IRQLs

NDIS drivers’ functions run, by default, at one of the following IRQLs, shown in order of increasing priority:
PASSIVE_LEVEL < DISPATCH_LEVEL < DIRQL.

NDIS intermediate drivers’ functions run, by default, at either of the preceding lower priority IRQLs. Only drivers of NICs that generate interrupts have functions that run at device IRQL.

Any driver function running at IRQL PASSIVE_LEVEL can be pre-empted by a thread of execution running at a higher priority IRQL. Any driver function running at PASSIVE_LEVEL or DISPATCH_LEVEL can be pre-empted by an interrrupt, that is, by an interrupt service routine or by a synchronization routine that also runs at DIRQL.

Each of the following DriverEntry and MiniportXxx descriptions specifies the default IRQL at which that function runs. The default IRQLs of certain MiniportXxx effectively restrict the set of Ndis Xxx functions that these MiniportXxx can call. For example, it is a fatal error for a MiniportISR function, which runs at DIRQL, to call any Ndis Xxx function that requires calls from IRQL <= DISPATCH_LEVEL.

For specific information about the range of valid IRQLs for calls to each Ndis Xxx function, see Chapter 3.

Initialization-Only and Pageable Driver Code

NDIS miniport and intermediate drivers mark their DriverEntry functions as initialization-only code, using the NDIS_INIT_FUNCTION macro. This causes DriverEntry to be discarded after it returns control.

Other driver functions can be marked as pageable code, using the NDIS_PAGABLE_FUNCTION macro. However, a driver function can be pageable code if and only if it never runs at IRQL >= DISPATCH_LEVEL. This restriction implies the following:

  • A function marked pageable cannot acquire any spin lock and should not call other functions that do.

  • A function marked pageable cannot be called by any function that is holding a spin lock when the call occurs.

  • A function marked pageable must run always at IRQL < DISPATCH_LEVEL.


DriverEntry of NDIS Miniport Drivers

NDIS_STATUS
DriverEntry(
IN PVOID Argument1,
IN PVOID Argument2
);

DriverEntry is a required function, which the system calls first in any NDIS driver.

Parameters

Argument1
Points to a system-supplied parameter. For Windows NT drivers, this points to a driver object, which is opaque to any NDIS driver that exports only MiniportXxx functions.
Argument2
Points to a second system-supplied parameter. For Windows NT drivers, this points to a registry path, which is opaque to any NDIS driver that exports only MiniportXxx functions.

Return Value

DriverEntry returns the value returned by NdisMRegisterMiniport or NdisIMRegisterLayeredMiniport.

Comments

NDIS miniports and intermediate drivers carry out two basic tasks in their DriverEntry functions:

  1. Call NdisMInitializeWrapper to notify the NDIS library that the driver is about to register itself as a miniport.

    NDIS sets up the state it needs to track the driver and returns an NdisWrapperHandle, which the driver saves for subsequent calls to Ndis Xxx configuration and initialization functions.

  2. Fill in an NDISXX_MINIPORT_CHARCTERISTICS structure with the appropriate version numbers and the entry points for driver-supplied MiniportXxx functions and, then, call NdisMRegisterMiniport or NdisIMRegisterLayeredMiniport.

    Usually, NIC drivers call NdisMRegisterMiniport, as do intermediate drivers that export only a set of MiniportXxx functions. Usually, NDIS intermediate drivers call NdisIMRegisterLayeredMiniport, which effectively defers the initialization of such a driver’s virtual NIC until the driver calls NdisIMInitializeDeviceInstance from its ProtocolBindAdapter function.

DriverEntry can allocate the NDISXX_MINIPORT_CHARACTERISTICS structure on the stack since the NDIS library copies the relevant information to its own storage. DriverEntry should clear the memory for this structure with NdisZeroMemory before setting any driver-supplied values in its members. The current MajorNdisVersion is 0x04, and the current MinorNdisVersion is 0x00. In each Xxx Handler member of the characteristics structure, DriverEntry must set the name of a driver-supplied MiniportXxx function, or the member must be NULL.

Calling NdisMRegisterMiniport causes the driver's MiniportInitialize function to run in the context of NdisMRegisterMiniport. Calling NdisIMRegisterLayeredMiniport defers the call to MiniportInitialize until the driver calls NdisIMInitializeDeviceInstance.

Drivers that call NdisMRegisterMiniport must be prepared for an immediate call to their MiniportInitialize functions. Such a driver must have sufficient installation and configuration information stored in the registry or available from calls to an Ndis Xxx bus-type-specific configuration function to set up any NIC-specific resources the driver will need to carry out network I/O operations.

Drivers that call NdisIMRegisterLayeredMiniport defer the call to their MiniportInitialize functions to another driver-supplied function that makes a call to NdisIMInitializeDeviceInstance. NDIS intermediate drivers usually register a ProtocolBindAdapter function and call NdisIMRegisterLayeredMiniport so that NDIS will call the ProtocolBindAdapter function after all underlying NIC drivers have initialized. This strategy gives such an NDIS intermediate driver, which makes the call to NdisIMInitializeDeviceInstance from ProtocolBindAdapter, the advantage of having its MiniportInitialize function configure driver-allocated resources for the intermediate's virtual NIC to the features of the underlying NIC driver to which the intermediate has already bound itself.

If NdisMRegisterMiniport or NdisIMRegisterLayeredMiniport does not return NDIS_STATUS_SUCCESS, DriverEntry must release any resources it allocated, such as memory to hold the NdisWrapperHandle, and must call NdisTerminateWrapper before it returns control. The driver will not be loaded if this occurs.

By default, DriverEntry runs at IRQL PASSIVE_LEVEL in a system-thread context.

See Also

DriverEntry of NDIS Protocol Drivers, NdisIMInitializeDeviceInstance, MiniportInitialize, NdisIMRegisterLayeredMiniport, NdisMInitializeWrapper, NdisMRegisterMiniport, NdisTerminateWrapper, NdisZeroMemory, ProtocolBindAdapter


MiniportAllocateComplete

VOID
MiniportAllocateComplete(
IN NDIS_HANDLE MiniportAdapterContext,
IN PVOID VirtualAddress,
IN PNDIS_PHYSICAL_ADDRESS PhysicalAddress,
 IN ULONG Length,
 IN PVOID Context
);

MiniportAllocateComplete is an optional function, supplied by drivers of busmaster DMA NICs that call NdisMAllocateSharedMemoryAsync.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
VirtualAddress
Specifies the base virtual address of the shared memory allocated by a preceding call to NdisMAllocateSharedMemoryAsync or NULL if the allocation attempt failed.
PhysicalAddress
Specifies the base physical address, suitable for use by the NIC, mapped to VirtualAddress or NULL if the allocation attempt failed.
Length
Specifies the number of bytes allocated by the preceding call to NdisMAllocateSharedMemoryAsync, or zero if the allocation attempt failed.
Context
Points to a driver-determined context passed in the preceding call to NdisMAllocateSharedMemoryAsync.

Comments

Drivers of busmaster DMA NICs are most likely to achieve significant performance gains by supporting multipacket receive indications. Such a driver supplies a MiniportAllocateComplete function so it can call NdisMAllocateSharedMemoryAsync when it is running low on shared memory for receive buffers due to high network traffic.

NdisMAllocateSharedMemoryAsync allocates memory that is shareable between the driver, which uses virtual addresses, and its busmaster DMA NIC, which uses the corresponding physical addresses. NdisMAllocateSharedMemoryAsync calls MiniportAllocateComplete whether its allocation attempt succeeds or fails.

A call to NdisMAllocateSharedMemoryAsync can occur at IRQL <= DISPATCH_LEVEL, possibly from the driver’s MiniportHandleInterrupt or MiniportTimer function as it indicates incoming receives to bound protocols with NdisMIndicateReceivePacket. Such a driver also must have a MiniportReturnPacket function.

If the call to NdisMAllocateSharedMemoryAsync succeeds, MiniportAllocateComplete can set up additional receive buffers in the newly allocated shared memory block. If this call allocated cached memory, MiniportAllocateComplete should align each new receive buffer on a cache-line boundary. MiniportAllocateComplete must allocate the buffer descriptors that map these new receive buffers from buffer pool.

Such a miniport can continue making multipacket receive indications, which NDIS forwards to the ProtocolReceivePacket function(s) of bound protocol(s) that export this function or as individual packets to the ProtocolReceive functions of other bound protocols. If the call to NdisMAllocateSharedMemoryAsync fails, MiniportAllocateComplete should set a state variable in the MiniportAdapterContext area to indicate that the driver must set NDIS_STATUS_RESOURCES in the Status member of the out-of-band block for the packets it subsequently indicates. This status causes NDIS to call the ProtocolReceive function(s) of bound protocol(s) with each packet for which the miniport set NDIS_STATUS_RESOURCES, so protocol driver(s) release the miniport-allocated packets with chained receive buffers individually as they are indicated, rather than holding on to packets until the protocol(s) have processed all indicated data. When the receive buffer space in the miniport’s existing shared memory area is replenished, the driver can reset its state variable and resume indicating packet arrays with NDIS_STATUS_SUCCESS.

By default, MiniportAllocateComplete runs at IRQL DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportReturnPacket, MiniportTimer, NdisAllocateBuffer, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync, NdisMIndicateReceivePacket, NDIS_PACKET_OOB_DATA, ProtocolReceive, ProtocolReceivePacket


MiniportCheckForHang

BOOLEAN
MiniportCheckForHang(
IN NDIS_HANDLE MiniportAdapterContext
 );

MiniportCheckForHang is an optional function that reports the state of the NIC or monitors the responsiveness of an underlying device driver.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Return Value

MiniportCheckForHang returns TRUE if the driver determines that its NIC is not operating or if an intermediate driver determines that the underlying device driver is unresponsive.

Comments

In NIC drivers, MiniportCheckForHang does nothing more than check the internal state of the NIC and return TRUE if it detects that the NIC is not operating correctly.

In intermediate drivers, MiniportCheckForHang can periodically check the state of the driver’s virtual NIC to determine whether the underlying device driver appears to be hung.

By default, the NDIS library calls MiniportCheckForHang approximately every two seconds.

If MiniportCheckForHang returns TRUE, NDIS then calls the driver’s MiniportReset function.

If a NIC driver has no MiniportCheckForHang function and NDIS judges the driver unresponsive as, for example, when NDIS holds many pending sends and requests queued to the miniport for a time-out interval, NDIS calls the driver’s MiniportReset function. The NDIS library’s default time-out interval for queued sends and requests is around four seconds. However, a NIC driver’s MiniportInitialize function can extend NDIS’s time-out interval by calling NdisMSetAttributesEx from MiniportInitialize to avoid unnecessary resets.

The MiniportInitialize function of an intermediate driver should disable NDIS’s time-out interval with NdisMSetAttributesEx because such a driver can neither control nor estimate a reasonable completion interval for the underlying device driver.

MiniportCheckForHang can be pre-empted by an interrupt.

By default, MiniportCheckForHang runs at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportReset, NdisMSetAttributesEx


MiniportDisableInterrupt

VOID
MiniportDisableInterrupt(
IN NDIS_HANDLE MiniportAdapterContext
);

MiniportDisableInterrupt is an optional function, supplied by drivers of NICs that support dynamic enabling and disabling of interrupts but do not share an IRQ.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Comments

MiniportDisableInterrupt typically disables interrupts by writing a mask to the NIC. If a driver does not have this function, typically its MiniportISR disables interrupts on the NIC.

If its NIC does not support dynamic enabling and disabling of interrupts or if it shares an IRQ, the NIC driver must register a MiniportISR function and set RequestIsr to TRUE when it calls NdisMRegisterMiniport. Such a driver’s MiniportISR function must acknowledge each interrupt generated by the NIC and save any necessary interrupt information for the driver’s MiniportHandleInterrupt function.

By default, MiniportDisableInterrupt runs at DIRQL, in particular at the DIRQL assigned when the NIC driver’s MiniportInitialize function called NdisMRegisterInterrupt. Therefore, MiniportDisableInterrupt can call only a subset of the NDIS library functions, such as the NdisRaw Xxx functions that are safe to call at any IRQL.

If MiniportDisableInterrupt shares resources, such as NIC registers, with another MiniportXxx that runs at a lower IRQL, that MiniportXxx must call NdisMSychronizeWithInterrupt so the driver’s MiniportSynchronizeISR function will access those shared resources in a synchronized and multiprocessor-safe manner. Otherwise, while it is accessing the shared resources, that MiniportXxx function can be pre-empted by MiniportDisableInterrupt, possibly undoing the work just done by MiniportXxx.

See Also

MiniportEnableInterrupt, MiniportHandleInterrupt, MiniportInitialize, MiniportISR, MiniportSynchronizeISR, NdisMRegisterInterrupt, NdisMSynchronizeWithInterrupt


MiniportEnableInterrupt

VOID
MiniportEnableInterrupt(
IN NDIS_HANDLE MiniportAdapterContext
);

MiniportEnableInterrupt is an optional function, supplied by some drivers of NICs that support dynamic enabling and disabling of interrupts but do not share an IRQ.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Comments

MiniportEnableInterrupt typically enables interrupts by writing a mask to the NIC.

A NIC driver that exports a MiniportDisableInterrupt function need not have a reciprocal MiniportEnableInterrupt function. Such a driver’s MiniportHandleInterrupt function is responsible for re-enabling interrupts on the NIC.

If its NIC does not support dynamic enabling and disabling of interrupts or if it shares an IRQ, the NIC driver must register a MiniportISR function and set RequestIsr to TRUE when it calls NdisMRegisterMiniport. Such a driver’s MiniportISR function must acknowledge each interrupt generated by the NIC and save any necessary interrupt information for the driver’s MiniportHandleInterrupt function.

MiniportEnableInterrupt can be pre-empted by an interrupt.

By default, MiniportEnableInterrupt runs at IRQL DISPATCH_LEVEL.

See Also

MiniportDisableInterrupt, MiniportHandleInterrupt, MiniportInitialize, MiniportISR, NdisMRegisterInterrupt


MiniportHalt

VOID
MiniportHalt(
IN NDIS_HANDLE MiniportAdapterContext
);

MiniportHalt is a required function that deallocates resources when the NIC is removed and halts the NIC.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Comments

MiniportHalt should stop the NIC, if it controls a physical NIC, and must free all resources that the driver allocated for its NIC before MiniportHalt returns control. In effect, MiniportHalt undoes everything that was done by MiniportInitialize for a particular NIC.

If the NIC driver allocated memory, claimed an I/O port range, mapped on-board device memory to host memory, initialized timer(s) and/or spin lock(s), allocated map registers or claimed a DMA channel, and registered an interrupt, that driver must call the reciprocals of the Ndis Xxx functions with which it originally allocated these resources.

As a general rule, a MiniportHalt function should call reciprocal Ndis Xxx functions in inverse order to the calls the driver made from MiniportInitialize. That is, if a NIC driver’s MiniportInitialize function called NdisMRegisterAdapterShutdownHandler just before it returned control, its MiniportHalt function would call NdisMDeregisterAdapterShutdownHandler first.

If its NIC generates interrupts or shares an IRQ, a NIC driver’s MiniportHalt function can be pre-empted by its MiniportISR or MiniportDisableInterrupt function until MiniportHalt calls NdisMDeregisterInterrupt. Such a driver’s MiniportHalt function usually disables interrupts on the NIC, if possible, and calls NdisMDeregisterInterrupt as soon as it can.

If the driver has a MiniportTimer function associated with any timer object that might be in the system timer queue, MiniportHalt should call NdisMCancelTimer.

Otherwise, it is unnecessary for the miniport to complete outstanding requests to its NIC before MiniportHalt begins releasing allocated resources. NDIS submits no further requests to the miniport for the NIC designated by the MiniportAdapterContext handle when NDIS has called MiniportHalt. On return from MiniportHalt, NDIS cleans up any state it was maintaining about this NIC and about its driver if this miniport supports no other NICs in the current machine.

An NDIS intermediate driver's call to NdisIMDeinitializeDeviceInstance causes a call to its MiniportHalt function.

By default, MiniportHalt runs at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, MiniportShutdown, NdisFreeMemory, NdisFreeSpinLock, NdisIMDeinitializeDeviceInstance, NdisMCancelTimer, NdisMDeregisterAdapterShutdownHandler, NdisMDeregisterDmaChannel, NdisMDeregisterInterrupt, NdisMDeregisterIoPortRange, NdisMFreeMapRegisters, NdisMFreeSharedMemory, NdisMUnmapIoSpace, ProtocolUnbindAdapter


MiniportHandleInterrupt

VOID
MiniportHandleInterrupt(
IN NDIS_HANDLE MiniportAdapterContext
);

MiniportHandleInterrupt is a required function if a driver’s NIC generates interrupts.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Comments

MiniportHandleInterrupt does the deferred processing of all outstanding interrupt operations and starts any new operations. That is, the driver’s MiniportISR or MiniportDisableInterrupt function dismisses the interrupt on the NIC, saves any necessary state about the operation, and returns control as quickly as possible, thereby deferring most interrupt-driven I/O operations to MiniportHandleInterrupt.

MiniportHandleInterrupt carries out most operations to indicate receives on NICs that generate interrupts, including but not limited to the following:

  • Adjusting the size of the buffer descriptor(s) to match the size of the received data and chaining the buffer descriptor(s) to the packet descriptor for the indication

  • Setting up an array of packet descriptors and setting up any out-of-band information for each packet in the array for the indication or, if the miniport does not support multipacket receive indications, setting up a lookahead buffer

    If the driver supports multipacket receives, it must indicate packet arrays in which the packet descriptors were allocated from packet pool and the buffer descriptors chained to those packets were allocated from buffer pool.

  • Calling the appropriate Ndis..IndicateReceive function for the received data

MiniportHandleInterrupt also can call NdisSendComplete on packets for which the MiniportSendPackets or Miniport(Wan)Send function returned NDIS_STATUS_PENDING.

If the NIC shares an IRQ, MiniportHandleInterrupt is called only if the MiniportISR function returned InterruptRecognized set to TRUE, thereby indicating that the NIC generated a particular interrupt.

When MiniportHandleInterrupt is called, interrupts are disabled on the NIC, either by the MiniportISR or MiniportDisableInterrupt function. Before it returns control, MiniportHandleInterrupt can re-enable interrupts on the NIC. Otherwise, NDIS calls a driver-supplied MiniportEnableInterrupt function to do so when MiniportHandleInterrupt returns control.

By default, MiniportHandleInterrupt runs at IRQL DISPATCH_LEVEL.

See Also

MiniportDisableInterrupt, MiniportEnableInterrupt, MiniportInitialize, MiniportISR, MiniportSend, MiniportSendPackets, MiniportWanSend, NdisAdjustBufferLength, NdisAllocateBuffer, NdisAllocateMemory, NdisAllocatePacket, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMRegisterInterrupt, NdisMSynchronizeWithInterrupt, NdisMTrIndicateReceive, NdisMWanIndicateReceive, NDIS_PACKET, NDIS_PACKET_OOB_DATA


MiniportInitialize

NDIS_STATUS
MiniportInitialize(
OUT PNDIS_STATUS OpenErrorStatus,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE WrapperConfigurationContext
 );

MiniportInitialize is a required function that sets up a NIC (or virtual NIC) for network I/O operations, claims all hardware resources necessary to the NIC in the registry, and allocates resources the driver needs to carry out network I/O operations.

Parameters

OpenErrorStatus
Points to a variable that MiniportInitialize sets to an NDIS_STATUS_XXX code specifying additional information about the error if MiniportInitialize will return NDIS_STATUS_OPEN_ERROR.
SelectedMediumIndex
Points to a variable in which MiniportInitialize sets the index of the MediumArray element that specifies the medium type the driver or its NIC uses.
MediumArray
Specifies an array of NdisMedium Xxx values from which MiniportInitialize selects one that its NIC supports or that the driver supports as an interface to higher-level drivers.
MediumArraySize
Specifies the number of elements at MediumArray.
MiniportAdapterHandle
Specifies a handle identifying the miniport’s NIC, which is assigned by the NDIS library. MiniportInitialize should save this handle; it is a required parameter in subsequent calls to Ndis Xxx functions.
WrapperConfigurationContext
Specifies a handle used only during initialization for calls to Ndis Xxx configuration and initialization functions. For example, this handle is a required parameter to NdisOpenConfiguration and the NdisImmediateRead Xxx and NdisImmediateWrite Xxx functions.

Return Value

MiniportInitialize can return either of the following:

NDIS_STATUS_SUCCESS
MiniportInitialize configured and set up the NIC, and it allocated all the resources the driver needs to carry out network I/O operations.
NDIS_STATUS_FAILURE
MiniportInitialize could not set up the NIC to an operational state or could not allocate needed resources. MiniportInitialize called NdisWriteErrorLogEntry with parameters specifying the configuration or resource allocation failure.

As alternatives to NDIS_STATUS_FAILURE, MiniportInitialize can return one of the following values, as appropriate, when it fails an initialization:

NDIS_STATUS_UNSUPPORTED_MEDIA
The values at MediumArray did not include a medium the driver (or its NIC) can support.
NDIS_STATUS_ADAPTER_NOT_FOUND
MiniportInitialize did not recognize the NIC either from its description in the registry, using NdisOpenConfiguration and NdisReadConfiguration, or by probing the NIC on a particular I/O bus, using one of the NdisImmediate Xxx or bus-type-specific Ndis Xxx configuration functions. This return can be propagated from the miniport’s call to certain Ndis Xxx functions, such as NdisOpenConfiguration.
NDIS_STATUS_OPEN_ERROR
MiniportInitialize attempted to set up a NIC but was unsuccessful.
NDIS_STATUS_NOT_ACCEPTED
MiniportInitialize could not get its NIC to accept the configuration parameters that it got from the registry or from a bus-type-specific Ndis Xxx configuration function.
NDIS_STATUS_RESOURCES
Either MiniportInitialize could not allocate sufficient resources to carry out network I/O operations or an attempt to claim bus-relative hardware resources in the registry for the NIC failed. This return can be propagated from the miniport’s call to an Ndis Xxx function.

If another device has already claimed a resource in the registry that its NIC needs, MiniportInitialize also should call NdisWriteErrorLogEntry to record the particular resource conflict (I/O port range, interrupt vector, device memory range, as appropriate). Supplying an error log record gives the user or system administrator information that can be used to reconfigure the machine to avoid such hardware resource conflicts.

Comments

NDIS submits no requests to a driver until its initialization is completed.

In NIC and intermediate drivers that call NdisMRegisterMiniport from their DriverEntry functions, NDIS calls MiniportInitialize in the context of NdisMRegisterMiniport. The underlying device driver must initialize before an intermediate driver that depends on that device calls NdisMRegisterMiniport.

For NDIS intermediate drivers that export both ProtocolXxx and MiniportXxx functions and that call NdisIMRegisterLayeredMiniport from their DriverEntry functions, NDIS calls MiniportInitialize in the context of NdisIMInitializeDeviceInstance. Such a driver’s ProtocolBindAdapter function usually makes the call to NdisIMInitializeDeviceInstance.

For NIC drivers, NDIS must find at least the NIC’s I/O bus interface type and, if it is not an ISA bus, the bus number already installed in the registry by the driver’s installation script. For more information about installing Windows NT drivers, see the Programmer’s Guide.

In general, drivers of ISA NICs call NdisOpenConfiguration and NdisReadConfiguration to get the bus-relative configuration parameters for their NICs from the registry. Drivers for NICs on other types of I/O buses usually call the appropriate bus-type-specific NdisRead Xxx function. Drivers of PCMCIA NICs should call NdisMQueryAdapterResources to maintain their binary compatibilty across Microsoft operating systems that support Win32.

When it calls MiniportInitialize, the NDIS library supplies an array of supported media types, specified as system-defined NdisMedium Xxx values. MiniportInitialize reads the array elements and provides the index of the medium type that NDIS should use with this driver for its NIC. If the miniport is emulating a medium type, its emulation must be transparent to NDIS.

The MiniportInitialize function of a NIC driver must call NdisMSetAttributes or NdisMSetAttributesEx before it calls any Ndis Xxx function, such as NdisRegisterIoPortRange or NdisMMapIoSpace, that claims hardware resources in the registry for the NIC. MiniportInitialize must call NdisMSetAttributes(Ex) before it attempts to allocate resources for DMA operations as well. If the NIC is a busmaster, MiniportInitialize must call NdisMAllocateMapRegisters following its call to NdisMSetAttributes(Ex) and before it calls NdisMAllocateSharedMemory. If the NIC is a slave, MiniportInitialize must call NdisMSetAttributes(Ex) before it calls NdisMRegisterDmaChannel.

Intermediate driver MiniportInitialize functions must call NdisMSetAttributesEx with NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER set in the AttributeFlags argument. Setting this flag causes NDIS to treat every intermediate driver as a full-duplex miniport, thereby preventing rare but intermittant deadlocks when concurrent send and receive events occur. Consequently, every intermediate driver must be written as a full-duplex driver capable of handling concurrent sends and indications.

If the NDIS library’s default four-second time-out interval on outstanding sends and requests is too short for the driver’s NIC, MiniportInitialize can call NdisMSetAttributesEx to extend the interval. Every intermediate driver also should call NdisMSetAttributesEx with NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT and NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT set in the AttributeFlags so that NDIS will not attempt to time out sends and requests that NDIS holds queued to the intermediate driver.

The call to NdisMSetAttributes or NdisMSetAttributesEx includes a MiniportAdapterContext handle to a driver-allocated context area, in which the miniport maintains runtime state information. NDIS subsequently passes the supplied MiniportAdapterContext handle as an input parameter to other MiniportXxx functions.

Consequently, the MiniportInitialize function of an intermediate driver must call NdisMSetAttributesEx to set up the MiniportAdapterContext handle for a driver-allocated per-virtual-NIC context area. Otherwise, NDIS would pass a NULL MiniportAdapterContext handle in its subsequent calls to the intermediate driver’s other MiniportXxx functions.

After a call to NdisMRegisterIoPortRange, a miniport must call the NdisRaw Xxx functions with the PortOffset value returned by NdisMRegisterIoPortRange to communicate with its NIC. The NIC driver can no longer call the NdisImmediateRead/WritePort Xxx functions. Similarly, after a call to NdisMMapIoSpace, a NIC driver can no longer call NdisImmediateRead/WriteSharedMemory.

After it has claimed any bus-relative hardware resources for its NIC in the registry, a miniport should no longer call any bus-type-specific NdisRead Xxx function.

After MiniportInitialize calls NdisMRegisterInterrupt, the driver’s MiniportISR function is called if the driver’s NIC generates an interrupt or if any other device with which the NIC shares an IRQ interrupts. NDIS does not call the MiniportDisableInterrupt and MiniportEnableInterrupt functions, if the driver supplied them, during initialization, so it is such a miniport’s responsibility to acknowledge and clear any interrupts its NIC generates. If the NIC shares an IRQ, the driver must first determine whether its NIC generated the interrupt; if not, the miniport must return FALSE as soon as possible.

If the NIC does not generate interrupts, MiniportInitialize should call NdisMInitializeTimer with a driver-supplied polling MiniportTimer function and a pointer to driver-allocated memory for a timer object. Drivers of NICs that generate interrupts and intermediate drivers also can set up one or more MiniportTimer functions, each with its own timer object. MiniportInitialize usually calls NdisMSetPeriodicTimer to enable a polling MiniportTimer function; a driver calls NdisMSetTimer subsequently when conditions occur such that the driver’s nonpolling MiniportTimer function should be run.

If the driver subsequently indicates receives with NdisMIndicateReceivePacket, the MiniportInitialize function should call NdisAllocatePacketPool and NdisAllocateBufferPool and save the handles returned by these NDIS functions. The packets that the driver subsequently indicates with NdisMIndicateReceivePacket must reference descriptors that were allocated with NdisAllocatePacket and NdisAllocateBuffer.

If driver functions other than MiniportISR or MiniportDisableInterrupt share resources, MiniportInitialize should call NdisAllocateSpinLock to set up any spin lock necessary to synchronize access to such a set of shared resources, particularly in a full-duplex driver or in a driver with a polling MiniportTimer function rather than an ISR. Resources shared by other driver functions with MiniportISR or MiniportDisableInterrupt, such as NIC registers, are protected by the interrupt object set up when MiniportInitialize calls NdisMRegisterInterrupt and accessed subsequently by calling NdisMSynchronizeWithInterrupt.

Any NIC driver’s MiniportInitialize function should test the NIC to be sure the hardware is configured correctly to carry out subsequent network I/O operations. If it must wait for state changes to occur in the hardware, MiniportInitialize either can call NdisWaitEvent with the pointer to a driver-initialized event object, or it can call NdisMSleep.

Unless the MiniportInitialize function of a NIC driver will return an error status, it should call NdisMRegisterAdapterShutdownHandler with a driver-supplied MiniportShutdown function.

If MiniportInitialize will fail the initialization, it must release all resources it has already allocated before it returns control.

If MiniportInitialize returns NDIS_STATUS_OPEN_ERROR, NDIS can examine the value returned at OpenErrorStatus to obtain more information about the error.

When MiniportInitialize returns NDIS_STATUS_SUCCESS, the NDIS library calls the driver’s MiniportQueryInformation function next.

By default, MiniportInitialize runs at IRQL PASSIVE_LEVEL and in the context of a system thread.

See Also

DriverEntry of NDIS Miniport Drivers, DriverEntry of NDIS Protocol Drivers, MiniportDisableInterrupt, MiniportEnableInterrupt, MiniportISR, MiniportQueryInformation, MiniportShutdown, MiniportTimer, NdisAllocateBuffer, NdisAllocateBufferPool, NdisAllocateMemory, NdisAllocatePacket, NdisAllocatePacketPool, NdisAllocateSpinLock, NdisAnsiStringToUnicodeString, NdisIMInitializeDeviceInstance, NdisImmediateReadPciSlotInformation, NdisImmediateReadPortUchar, NdisImmediateReadSharedMemory, NdisIMRegisterLayeredMiniport, NdisInitializeEvent, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMCreateLog, NdisMIndicateReceivePacket, NdisMInitializeTimer, NdisMMapIoSpace, NdisMQueryAdapterResources, NdisMRegisterAdapterShutdownHandler, NdisMRegisterDmaChannel, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMRegisterMiniport, NdisMSetAttributes, NdisMSetAttributesEx, NdisMSetPeriodicTimer, NdisMSetTimer, NdisMSleep, NdisOpenConfiguration, NdisPciAssignResources, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawReadPortUlong, NdisRawReadPortUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisRawWritePortUchar, NdisRawWritePortUlong, NdisRawWritePortUshort, NdisReadConfiguration, NdisReadEisaSlotInformation, NdisReadMcaPosInformation, NdisReadNetworkAddress, NdisReadPciSlotInformation, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisUnicodeStringToAnsiString, NdisWaitEvent, NdisWriteErrorLogEntry, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort, ProtocolBindAdapter


MiniportISR

VOID
MiniportISR(
OUT PBOOLEAN InterruptRecognized,
OUT PBOOLEAN QueueMiniportHandleInterrupt,
IN NDIS_HANDLE MiniportAdapterContext
 );

MiniportISR is a required function if the driver’s NIC generates interrupts.

Parameters

InterruptRecognized
Points to a variable in which MiniportISR returns whether the NIC actually generated the interrupt. MiniportISR sets this to TRUE if it detects that the interrupt came from the NIC designated at MiniportAdapterContext.

If its NIC shares an IRQ with other devices on the same bus, MiniportISR should return FALSE as quickly as possible whenever it determines that the NIC did not interrupt.

QueueMiniportHandleInterrupt
Points to a variable that MiniportISR sets to TRUE if the MiniportHandleInterrupt function should be called to complete the interrupt-driven I/O operation.

If the variable at InterruptRecognized is set to FALSE, the value of this variable is irrelevant because MiniportHandleInterrupt will not be called.

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Comments

Any NIC driver should do as little work as possible in its MiniportISR function, deferring I/O operations for each interrupt the NIC generates to the MiniportHandleInterrupt function. A NIC driver’s ISR is not re-entrant, although two instantiations of a MiniportISR function can execute concurrently in SMP machines, particularly if the miniport supports full-duplex sends and receives.

Miniport ISR is called under the following conditions:

  • An interrupt occurs on the NIC while the driver’s MiniportInitialize or MiniportHalt function is running.

  • An interrupt occurs on the I/O bus and the NIC shares an IRQ with other devices on that bus.

    If the NIC shares an IRQ with other devices, that miniport’s ISR must be called on every interrupt to determine whether its NIC actually generated the interrupt. If not, MiniportISR should return FALSE immediately so the driver of the device that actually generated the interrupt is called quickly. This strategy maximizes I/O throughput for every device on the same bus.

  • An interrupt occurs and the NIC driver specified that its ISR should be called to handle every interrupt when its MiniportInitialize function called NdisMRegisterInterrupt.

    Miniports that do not provide MiniportDisable/EnableInterrupt functionality must have their ISRs called on every interrupt.

MiniportISR dismisses the interrupt on the NIC, saves whatever state it must about the interrupt, and defers as much of the I/O processing for each interrupt as possible to the MiniportHandleInterrupt function.

After MiniportISR returns control with the variables at InterruptRecognized and QueueMiniportHandleInterrupt set to TRUE, the corresponding MiniportHandleInterrupt function runs at a lower hardware priority (IRQL DISPATCH_LEVEL) than that of the ISR (DIRQL). As a general rule, MiniportHandleInterrupt should do all the work for interrupt-driven I/O operations except for determining whether the NIC actually generated the interrupt, and, if necessary, preserving the type (receive, send, reset...) of interrupt.

However, a driver writer should not rely on a one-to-one correspondence between the execution of MiniportISR and MiniportHandleInterrupt. A MiniportHandleInterrupt function should be written to handle the I/O processing for more than one NIC interrupt. Its MiniportISR and MiniportHandleInterrupt functions can run concurrently in SMP machines. Moreover, as soon as MiniportISR acknowledges a NIC interrupt, the NIC can generate another interrupt, while the MiniportHandleInterrupt DPC can be queued for execution once for such a sequence of interrupts.

The MiniportHandleInterrupt function is not queued if the driver’s MiniportHalt or MiniportInitialize function is currently executing.

If MiniportISR shares resources, such as NIC registers or state variables, with another MiniportXxx that runs at lower IRQL, that MiniportXxx must call NdisMSychronizeWithInterrupt so the driver’s MiniportSynchronizeISR function will access those shared resources in a synchronized and multiprocessor-safe manner. Otherwise, while it is accessing the shared resources, that MiniportXxx function can be pre-empted by MiniportISR, possibly undoing the work just done by MiniportXxx.

By default, MiniportISR runs at DIRQL, in particular at the DIRQL assigned when the driver initialized the interrupt object with NdisMRegisterInterrupt. Therefore, MiniportIsr can call only a subset of the NDIS library functions, such as the NdisRaw Xxx or NdisRead/WriteRegister Xxx functions that are safe to call at any IRQL.

See Also

MiniportDisableInterrupt, MiniportEnableInterrupt, MiniportHalt, MiniportHandleInterrupt, MiniportInitialize, MiniportSynchronizeISR, NdisMRegisterInterrupt, NdisMSynchronizeWithInterrupt, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawReadPortUlong, NdisRawReadPortUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisRawWritePortUchar, NdisRawWritePortUlong, NdisRawWritePortUshort, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


MiniportQueryInformation

NDIS_STATUS
MiniportQueryInformation(
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG BytesWritten,
OUT PULONG BytesNeeded
 );

MiniportQueryInformation is a required function that returns information about the capabilities and status of the driver and/or its NIC.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
Oid
Specifies the system-defined OID_XXX code designating the global query operation the driver should carry out.
InformationBuffer
Points to a buffer in which MiniportQueryInformation should return the OID-specific information.
InformationBufferLength
Specifies the number of bytes at InformationBuffer.
BytesWritten
Points to a variable that MiniportQueryInformation sets to the number of bytes it is returning at InformationBuffer.
BytesNeeded
Points to a variable that MiniportQueryInformation sets to the number of additional bytes it needs to satisfy the request if InformationBufferLength is less than Oid requires.

Return Value

MiniportQueryInformation can return one of the following:

NDIS_STATUS_SUCCESS
MiniportQueryInformation returned the requested information at InformationBuffer and set the variable at BytesWritten to the amount of information it returned.
NDIS_STATUS_PENDING
The driver will complete the request asynchronously with a call to NdisMQueryInformationComplete when it has gathered the requested information.
NDIS_STATUS_INVALID_OID
MiniportQueryInformation does not recognize the Oid.
NDIS_STATUS_INVALID_LENGTH
The InformationBufferLength does not match the length required by the given Oid. MiniportQueryInformation returned how many bytes the buffer should be at BytesNeeded.
NDIS_STATUS_NOT_ACCEPTED
MiniportQueryInformation attempted to gather the requested information from the NIC but was unsuccessful.
NDIS_STATUS_NOT_SUPPORTED
MiniportQueryInformation does not support the Oid, which is optional.
NDIS_STATUS_RESOURCES
MiniportQueryInformation could not allocate sufficient resources to return the requested information. This return value does not necessarily mean that the same request, submitted at a later time, will be failed for the same reason.

Comments

NDIS calls the MiniportQueryInformation function either on its own behalf, such as to determine which options the driver supports or to manage binding-specific information for the miniport, or when a bound protocol driver calls NdisRequest.

NDIS makes one or more calls to MiniportQueryInformation just after a driver’s MiniportInitialize function returns NDIS_STATUS_SUCCESS. NDIS supplies the following OIDs in its initialization-time calls to the driver’s MiniportQueryInformation function:

OID_GEN_MAXIMUM_LOOKAHEAD
MiniportQueryInformation must return how many bytes of lookahead data the NIC can provide, that is, the initial transfer capacity of the NIC.

Even if a driver supports multipacket receives and, therefore, will indicate an array of pointers to fully set up packets, MiniportQueryInformation must supply this information. Such a driver should return the maximum packet size it can indicate.

OID_GEN_MAC_OPTIONS
MiniportQueryInformation must return a bitmask set with the appropriate NDIS_MAC_OPTION_XXX flags indicating which options it (or its NIC) supports, or it can return zero at InformationBuffer if the driver supports none of the options designated by these flags.

For example, a NIC driver always sets the NDIS_MAC_OPTION_NO_LOOPBACK flag if its NIC has no internal hardware support for loopback. This tells NDIS to manage loopback for the driver, which cannot provide software loopback code as efficient as the NDIS library’s because NDIS manages all binding-specific information for miniports. Any miniport that tries to provide software loopback must check the destination address of every send packet against the currently set filter addresses to determine whether to loop back each packet. WAN NIC drivers must set this flag.

If the NIC driver sets the NDIS_MAC_OPTION_FULL_DUPLEX flag, the NDIS library serializes calls to the MiniportSendPackets or Miniport(Wan)Send function separately from its serialized calls to other MiniportXxx functions in SMP machines. However, NDIS returns incoming send packets to protocols while such a driver’s MiniportReset function is executing: that is, NDIS never calls a full-duplex miniport to transmit a packet until its reset operation is completed. The designer of any full-duplex driver can expect that driver to achieve significantly higher performance in SMP machines, but the driver must synchronize its accesses to shared resources carefully to prevent race conditions or deadlocks from occurring. NDIS assumes that all intermediate drivers are full-duplex drivers.

Depending on the NdisMedium Xxx that MiniportInitialize selected, NDIS submits additional intialization-time requests to MiniportQueryInformation, such as the following:

OID_XXX_CURRENT_ADDRESS
If the driver’s MiniportInitialize function selected an NdisMedium Xxx for which the system supplies a filter, NDIS calls MiniportQueryInformation to return the NIC’s current address in medium-specific format. For FDDI drivers, NDIS requests both long and short current addresses.
OID_802_3_MAXIMUM_LIST_SIZE
For Ethernet drivers, NDIS requests the multicast list size.
OID_FDDI_LONG/SHORT_MAX_LIST_SIZE
For FDDI drivers, NDIS requests the multicast list sizes.

If possible, MiniportQueryInformation should not return NDIS_STATUS_PENDING for initialization-time requests. Until NDIS has sufficient information to set up bindings to the miniport, such requests should be handled synchronously.

Subsequently, the NDIS library intercepts all protocol-initiated queries on certain OIDs, such as the following:

OID_GEN_CURRENT_PACKET_FILTER
OID_GEN_PROTOCOL_OPTIONS
OID_802_5_CURRENT_FUNCTIONAL
OID_802_3_MULTICAST_LIST
OID_FDDI_LONG_MULTICAST_LIST
OID_FDDI_SHORT_MULTICAST_LIST

For more information about the system-defined OIDs, see Chapter 5.

If MiniportQueryInformation does not complete a request synchronously and returns NDIS_STATUS_PENDING, the driver must complete the request later with a call to NdisMQueryInformationComplete. Until it completes any such request, the miniport can safely access the memory at InformationBuffer, BytesWritten, and BytesNeeded. After the miniport completes any query, ownership of these variables and the buffer reverts to NDIS or the caller of NdisRequest, whichever allocated the memory.

After a call to MiniportQueryInformation, NDIS submits no other requests to the driver until it has completed the current request, either synchronously or asynchronously. Instead, NDIS holds requests queued until the current query is completed.

MiniportQueryInformation can be pre-empted by an interrupt.

By default, MiniportQueryInformation runs at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportSetInformation, NdisMQueryInformationComplete, NdisRequest, NDIS_REQUEST


MiniportReconfigure

NDIS_STATUS
MiniportReconfigure(
OUT PNDIS_STATUS OpenErrorStatus,
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_HANDLE WrapperConfigurationContext
 );

MiniportReconfigure is an function that the NDIS library currently never calls but that a MiniportInitialize function can call as an internal driver function.

Parameters

OpenErrorStatus
Points to a variable that MiniportReconfigure sets to an NDIS_STATUS_XXX code specifying additional information about the error if MiniportReconfigure will return NDIS_STATUS_OPEN_ERROR.
MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize with NdisMSetAttributes or NdisMSetAttributesEx.
WrapperConfigurationContext
Specifies a handle used only during initialization for calls to Ndis Xxx configuration and initialization functions. For example, this handle is a required parameter to NdisOpenConfiguration and the NdisImmediateRead Xxx and NdisImmediateWrite Xxx functions.

Return Value

MiniportReconfigure either returns NDIS_STATUS_SUCCESS or it can return any driver-determined value, such as one of the following:

NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_OPEN_ERROR
NDIS_STATUS_FAILURE

Comments

As an internal driver function, MiniportReconfigure might be called from MiniportInitialize to reconfigure a NIC to new parameters returned by one of the Ndis Xxx bus-relative configuration functions. For example, a MiniportReconfigure function might be used to support plug-and-play NICs or software-configurable NICs.

Because an internal MiniportReconfigure function is called from MiniportInitialize, no other driver request can be outstanding when MiniportReconfigure is called, and NDIS will never submit another request to the miniport until its caller, MiniportInitialize, returns control.

Like MiniportInitialize, MiniportReconfigure can be pre-empted by an interrupt if the driver has already called NdisMRegisterInterrupt or if the NIC shares an IRQ.

As an internal driver function called from MiniportInitialize, MiniportReconfigure can be pageable code.

By default, MiniportReconfigure runs at the same IRQL as MiniportIntialize.

See Also

MiniportInitialize


MiniportReset

NDIS_STATUS
MiniportReset(
OUT PBOOLEAN AddressingReset,
 IN NDIS_HANDLE MiniportAdapterContext
);

MiniportReset is a required function that issues a hardware reset to the NIC and/or resets the driver’s software state.

Parameters

AddressingReset
Points to a variable that MiniportReset sets to TRUE if the NDIS library should call MiniportSetInformation to restore addressing information to the current values.
MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.

Return Value

MiniportReset can return one of the following:

NDIS_STATUS_SUCCESS
MiniportReset successfully reset the NIC to an operational state.
NDIS_STATUS_PENDING
The driver will complete the reset operation asynchronously by calling NdisMResetComplete when the operation is done.
NDIS_STATUS_NOT_RESETTABLE
The NIC does not provide reset capabilities under software control.
NDIS_STATUS_RESET_IN_PROGRESS
MiniportReset determined that the NIC was currently being reset, so this call is superfluous.
NDIS_STATUS_SOFT_ERRORS
MiniportReset successfully reset the NIC but a recoverable error occurred during the operation. MiniportReset has called NdisWriteErrorLogEntry with more information about the error.
NDIS_STATUS_HARD_ERRORS
MiniportReset attempted to reset the NIC but an unrecoverable error occurred during the operation. MiniportReset has called NdisWriteErrorLogEntry with more information about the error.

Comments

MiniportReset can reset the parameters of its NIC. If a reset causes a change in the NIC’s station address, the miniport automatically restores the station address following the reset to its prior value. Any multicast or functional addressing masks reset by the hardware do not have to be reset in this function.

If other information, such as multicast or functional addressing information or the lookahead size, is changed by a reset, MiniportReset must set the variable at AddressingReset to TRUE before it returns control. This causes NDIS to call the MiniportSetInformation function to restore the information.

As a general rule, the MiniportReset function of an NDIS intermediate driver should always set AddressingReset to TRUE. Until the underlying NIC driver resets its NIC, such an intermediate driver cannot determine whether it must restore addressing information for its virtual NIC. Because an intermediate driver disables the NDIS library’s timing out of queued sends and requests to itself with an initialization-time call to NdisMSetAttributesEx, such a driver’s MiniportReset function is called only when a reset request is directed to the underlying NIC driver.

Intermediate drivers that layer themselves above other types of device drivers also must have a MiniportReset function. Such a MiniportReset function must handle reset requests initiated by protocol drivers’ calls to NdisReset. If the intermediate driver also has a MiniportCheckForHang function, its MiniportReset function will be called whenever MiniportCheckForHang returns TRUE.

It is unnecessary for a driver to complete outstanding requests before MiniportReset begins resetting the NIC or updating its software state. NDIS submits no further requests to the miniport for the NIC designated by the MiniportAdapterContext handle when NDIS has called MiniportReset until the reset operation is completed. A miniport need not call NdisMIndicateStatus to signal the start and finish of each reset operation because NDIS notifies bound protocols when a reset begins and ends.

If MiniportReset must wait for state changes in the NIC during reset operations, it can call NdisStallExecution. However, a MiniportReset function should never call NdisStallExecution with an interval greater than 50 microseconds.

If MiniportReset returns NDIS_STATUS_PENDING, the driver must complete the original request subsequently with a call to NdisMResetComplete.

MiniportReset can be pre-empted by an interrupt.

If a NIC driver supplies a MiniportCheckForHang function, the NDIS library calls it periodically to determine whether to call the driver’s MiniportReset function. Otherwise, the NDIS library calls a NIC driver’s MiniportReset function whenever requests NDIS submitted to the MiniportQueryInformation, MiniportSetInformation, MiniportSendPackets, MiniportSend, or MiniportWanSend function seem to have timed out. By default, the NDIS-determined time-out interval for outstanding sends and requests is around four seconds. If this default is too short, a NIC driver can make an initialization-time call to NdisMSetAttributesEx, rather than NdisMSetAttributes, to lengthen the time-out interval to suit its NIC.

Every NDIS intermediate driver should call NdisMSetAttributesEx from MiniportInitialize and disable NDIS’s attempts to time out requests and sends in the intermediate driver. NDIS runs an intermediate driver’s MiniportCheckForHang function, if any, approximately every two seconds.

NDIS cannot determine whether a NIC might be hung on receives, so supplying a MiniportCheckForHang function allows a driver to monitor its NIC for this condition and to force a reset if it occurs.

By default, MiniportReset runs at IRQL DISPATCH_LEVEL.

See Also

MiniportCheckForHang, MiniportInitialize, MiniportQueryInformation, MiniportSend, MiniportSendPackets, MiniportSetInformation, MiniportWanSend, NdisMIndicateStatus, NdisMResetComplete, NdisMSetAttributesEx, NdisWriteErrorLogEntry


MiniportReturnPacket

VOID
MiniportReturnPacket(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET Packet
);

MiniportReturnPacket is a required function in drivers that indicate receives with NdisMIndicateReceivePacket.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
Packet
Points to a packet descriptor being returned to the miniport, which previously indicated a packet array that contained this pointer.

Comments

Drivers of busmaster DMA NICs that support multipacket receives and NIC drivers that supply media-specific information, such as packet priorities, with their indications must have MiniportReturnPacket functions. NDIS intermediate drivers that bind themselves to such a NIC driver also must have MiniportReturnPacket functions.

Any packet with associated NDIS_PACKET_OOB_DATA in which the Status is set to NDIS_STATUS_PENDING on return from NdisMIndicateReceivePacket will be returned to MiniportReturnPacket. When all bound protocols have called NdisReturnPackets as many times as necessary to release their references to the originally indicated packet array, NDIS returns pended packets from the array to the MiniportReturnPacket function of the driver that originally allocated the packet array.

Usually, MiniportReturnPacket prepares such a returned packet to be used in a subsequent receive indication. Although MiniportReturnPacket could return the buffer descriptors chained to the packet to buffer pool and the packet descriptor itself to packet pool, it is far more efficient to reuse returned descriptors.

MiniportReturnPacket must call NdisUnchainBufferAt Xxx as many times as necessary to save the pointers to all chained buffer descriptors before it calls NdisReinitializePacket. Otherwise, MiniportReturnPacket cannot recover the buffer descriptors the driver originally chained to the packet for the indication.

MiniportReturnPacket also can call NdisZeroMemory with the pointer returned by NDIS_OOB_DATA_FROM_PACKET to prepare the packet’s associated out-of-band block for reuse.

If a particular buffer descriptor was shortened to match the size of an indicated range of data, MiniportReturnPacket should call NdisAdjustBufferLength with that buffer descriptor to restore its mapping of the NIC’s receive buffer range.

By default, MiniportReturnPacket runs at IRQL DISPATCH_LEVEL.

See Also

NdisAdjustBufferLength, NdisAllocateBuffer, NdisAllocatePacket, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisReinitializePacket, NdisReturnPackets, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, NdisZeroMemory


MiniportSend

NDIS_STATUS
MiniportSend(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET Packet,
IN UINT Flags
 );

MiniportSend is a required function if the driver has neither a MiniportSendPackets nor MiniportWanSend function. MiniportSend transfers a protocol-supplied packet over the network.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
Packet
Points to a packet descriptor specifying the data to be transmitted.
Flags
Specifies the packet flags, if any, set by the protocol.

Return Value

MiniportSend can return one of the following values:

NDIS_STATUS_SUCCESS
The driver (or its NIC) has accepted the packet data for transmission, so MiniportSend is returning the packet, which NDIS will return to the protocol.
NDIS_STATUS_PENDING
The driver will complete the packet asynchronously with a call to NdisMSendComplete.
NDIS_STATUS_RESOURCES
The driver (or NIC) currently has insufficient resources available to process the given packet so NDIS should queue the send packet for a resubmission when the driver next calls NdisMSendResourcesAvailable or NdisMSendComplete.
NDIS_STATUS_FAILURE
The given packet was invalid or unacceptable to the NIC.

Comments

If a driver registers both MiniportSend and MiniportSendPackets functions when it initializes, its MiniportSend function is never called by NDIS.

MiniportSend can safely access the packet and all buffer descriptors chained to the packet until the given packet is complete. If MiniportSend returns a status other than NDIS_STATUS_PENDING or NDIS_STATUS_RESOURCES, the request is considered complete and ownership of the packet descriptor and all memory associated with the packet reverts to the allocating protocol.

If MiniportSend returns NDIS_STATUS_PENDING, the driver subsequently must signal completion of the request by calling NdisMSendComplete. When MiniportSend returns NDIS_STATUS_RESOURCES, the NDIS library reflects this status to the protocol as NDIS_STATUS_PENDING.

When this function returns a packet with NDIS_STATUS_RESOURCES, the next packet submitted to MiniportSend is the same packet it just returned to NDIS. Consequently, MiniportSend can optimize by retaining information about such a returned packet if the driver currently has sufficient resources to store the information. NDIS assumes MiniportSend is ready to accept that packet as soon as the driver calls NdisMSendResourcesAvailable or NdisMSendComplete, whichever occurs first.

Each protocol driver must pass packet descriptors to NdisSend that are fully set up to be passed by the underlying driver’s MiniportSend function to its NIC. That is, the protocol is responsible for determining what is required, based on the medium type selected by the miniport to which the protocol bound itself. However, a protocol can supply net packets mapped by the chained buffer descriptors that are shorter than the minimum for the selected medium, which MiniportSend must pad if its medium imposes a minimum-length requirement on transmits.

Any NDIS intermediate driver that layers itself betwen a higher-level protocol and an underlying NIC driver has the same responsibility as any protocol driver to set up packets according to the requirements of the underlying miniport and its selected medium. Such an intermediate driver must repackage each incoming send packet in a fresh packet descriptor that was allocated by the intermediate driver.

MiniportSend can use only the eight-byte area at MiniportReserved within the NDIS_PACKET structure for its own purposes. Consequently, an NDIS intermediate driver that forwards send requests to an underlying NIC driver must repackage the packets input to its MiniportSend function in fresh packet descriptors, which the intermediate driver allocates from packet pool, so that the underlying miniport has a MiniportReserved area it can use.

MiniportSend can call NdisQueryPacket to extract information, such as the number of buffer descriptors chained to the packet and the total size in bytes of the requested transfer. It can call NdisGetFirstBufferFromPacket, NdisQueryBuffer, or NdisQueryBufferOffset to extract information about individual buffers containing the data to be transmitted.

The Flags parameter can provide information about a send that is not contained in the packet data itself. Currently, there are no system-defined flags, but a pair of closely coupled protocol and miniport drivers can pass information in this parameter, which MiniportSend can retrieve with NdisGetPacketFlags. However, such a pair of drivers can communicate more information, such as timestamps and packet priority, in the NDIS_PACKET_OOB_DATA block associated with each packet descriptor.

If the underlying driver’s MiniportQueryInformation function set the NDIS_MAC_OPTION_NO_LOOPBACK flag when the NDIS library queried the OID_GEN_MAC_OPTIONS, the miniport must not attempt to loop back any packets. The NDIS library provides software loopback support for such a driver.

MiniportSend can be pre-empted by an interrupt.

By default, MiniportSend runs at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportQueryInformation, MiniportSendPackets, MiniportWanSend, NdisAllocatePacket, NdisGetBufferPhysicalArraySize, NdisGetFirstBufferFromPacket, NdisGetNextBuffer, NdisGetPacketFlags, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_TIME_TO_SEND, NdisMoveMemory, NdisMoveToMappedMemory, NdisMSendComplete, NdisMSendResourcesAvailable, NdisMSetupDmaTransfer, NdisMStartBufferPhysicalMapping, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryBuffer, NdisQueryBufferOffset, NdisQueryPacket, NdisSend, NdisZeroMemory


MiniportSendPackets

VOID
MiniportSendPackets(
IN NDIS_HANDLE MiniportAdapterContext,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
);

MiniportSendPackets is a required function if the driver has neither a MiniportSend nor MiniportWanSend function. MiniportSendPackets transfers some number of packets, specified as an array of packet pointers, over the network.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
PacketArray
Points to the initial element in a packet array, with each element specifying the address of a packet descriptor for a packet to be transmitted, along with an associated out-of-band data block containing information such as the packet priority, an optional timestamp, and the per-packet status to be set by MiniportSendPackets.
NumberOfPackets
Specifies the number of pointers to packet descriptors at PacketArray.

Comments

If a driver registers both MiniportSendPackets and MiniportSend functions when it initializes, NDIS always calls its MiniportSendPackets function.

The input packet packet descriptor pointers have been ordered according to the order in which the packets should be sent over the network by the protocol driver that set up the packet array. The NDIS library preserves the protocol-determined ordering when it submits each packet array to MiniportSendPackets.

Consequently, MiniportSendPackets should transmit each packet in any given array sequentially. MiniportSendPackets can call NdisQueryPacket to extract information, such as the number of buffer descriptors chained to the packet and the total size in bytes of the requested transfer. It can call NdisGetFirstBufferFromPacket, NdisQueryBuffer, or NdisQueryBufferOffset to extract information about individual buffers containing the data to be transmitted.

MiniportSendPackets can retrieve any protocol-supplied out-of-band information associated with each packet by using the relevant NDIS_GET_PACKET_TIME_TO_SEND and NDIS_GET_MEDIA_SPECIFIC_INFO macros.

Each protocol driver must set up packet arrays with packet descriptors that are fully set up to be passed by the underlying driver’s MiniportSendPackets function to its NIC. That is, the protocol is responsible for determining what is required, based on the medium type selected by the miniport to which the protocol bound itself. However, a protocol can supply packets shorter than the minimum for the selected medium, which MiniportSendPackets must pad if its medium imposes a minimum-length requirement on transmits.

Any NDIS intermediate driver that layers itself between a higher-level protocol and an underlying NIC driver has the same responsibility as any protocol driver to set up packets according to the requirements of the underlying miniport and its selected medium. Such an intermediate driver must repackage each incoming send packet in a fresh packet descriptor that was allocated by the intermediate driver.

MiniportSendPackets can use only the eight-byte area at MiniportReserved within the NDIS_PACKET structure for its own purposes. Consequently, an NDIS intermediate driver that forwards send requests to an underlying NIC driver must repackage the packets given to its MiniportSendPackets function in fresh packet descriptors, which the intermediate driver allocates from packet pool, so that the underlying miniport has a MiniportReserved area it can use.

MiniportSendPackets sets one of the following values in the Status member of the NDIS_PACKET_OOB_DATA block associated with an input packet descriptor:

NDIS_STATUS_SUCCESS
The driver (or its NIC) has accepted the packet data for transmission, so MiniportSendPackets is returning the packet and the associated out-of-band data, which NDIS will return to the protocol that made this request.
NDIS_STATUS_PENDING
The driver will complete the transmit operation asynchronously with NdisMSendComplete.
NDIS_STATUS_RESOURCES
The miniport currently cannot set up the transmit operation due to resource constraints, so NDIS should queue this packet and all remaining packets in the given array for resubmission to MiniportSendPackets when the driver next calls NdisMSendResourcesAvailable or NdisMSendComplete.

When MiniportSendPackets sets this value for a packet in the input array, the NDIS library assumes all remaining packets in the array have the same status set, so NDIS requeues the associated packets in the same order, which preserves the protocol-determined ordering of the packet array. NDIS reflects this status to the protocol as NDIS_STATUS_PENDING for this packet and for all remaining packets in the given array.

NDIS_STATUS_FAILURE
The given packet was invalid or unacceptable to the NIC. Setting this status or any other driver-determined NDIS_STATUS_XXX error value for a packet effectively completes the packet, which NDIS returns to the allocating protocol as a failed send request.

When MiniportSendPackets returns control, the driver can no longer access anything in the given packet array with the following exceptions:

  • If MiniportSendPackets set the Status for a packet to NDIS_STATUS_PENDING, the miniport can access the packet descriptor and any media-specific information in the associated out-of-band data block. MiniportSendPackets can use the buffer descriptors chained to that packet descriptor, as well.

    After the driver calls NdisMSendComplete with the final status of the transmit operation, ownership of the given packet, out-of-band data block, and associated buffers reverts to the allocating protocol. A miniport should never return NDIS_STATUS_RESOURCES for a packet it passes to NdisMSendComplete.

  • If MiniportSendPackets set the Status for a packet to NDIS_STATUS_RESOURCES, the miniport can access the packet, its associated out-of-band data block, and the buffer descriptors chained to that packet when NDIS resubmits the send request, along with subsequent elements of the packet array, to MiniportSendPackets.

Setting NDIS_STATUS_RESOURCES for an incoming packet causes NDIS to requeue the associated packet and those specified by all remaining array elements for subsequent resubmission to MiniportSendPackets. NDIS assumes that a subsequent call to NdisMSendResourcesAvailable or NdisMSendComplete, whichever occurs first, indicates that MiniportSendPackets is ready to accept more packets for transmission. NDIS preserves the original ordering of returned array elements when it resubmits them as a packet array to MiniportSendPackets.

If the underlying driver’s MiniportQueryInformation function set the NDIS_MAC_OPTION_NO_LOOPBACK flag when the NDIS library queried the OID_GEN_MAC_OPTIONS, the miniport must not attempt to loop back any packets. The NDIS library provides software loopback support for such a driver.

By default, MiniportSendPackets runs at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportQueryInformation, NdisAllocatePacket, NdisGetBufferPhysicalArraySize, NdisGetFirstBufferFromPacket, NdisGetNextBuffer, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_TIME_TO_SEND, NdisMoveMemory, NdisMoveToMappedMemory, NdisMSendComplete, NdisMSendResourcesAvailable, NdisMSetupDmaTransfer, NdisMStartBufferPhysicalMapping, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryBuffer, NdisQueryBufferOffset, NdisQueryPacket, NdisSendPackets, NdisZeroMemory


MiniportSetInformation

NDIS_STATUS
MiniportSetInformation(
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG BytesRead,
OUT PULONG BytesNeeded
 );

MiniportSetInformation is a required function that allows bound protocol drivers (or NDIS) to request changes in the state information that the miniport maintains for particular OIDs, such as changes in multicast addresses.

Parameters

MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
Oid
Specifies the system-defined OID_XXX code designating the set operation the driver should carry out.
InformationBuffer
Points to a buffer containing the OID-specific data used by MiniportSetInformation for the set.
InformationBufferLength
Specifies the number of bytes at InformationBuffer.
BytesRead
Points to a variable that MiniportSetInformation sets to the number of bytes it read from the buffer at InformationBuffer.
BytesNeeded
Points to a variable that MiniportSetInformation sets to the number of additional bytes it needs to satisfy the request if InformationBufferLength is less than Oid requires.

Return Value

MiniportSetInformation can return one of the following:

NDIS_STATUS_SUCCESS
MiniportSetInformation used the data at InformationBuffer to set itself or its NIC to the state required by the given Oid, and it set the variable at BytesRead to the amount of supplied data it used.
NDIS_STATUS_PENDING
The driver will complete the request asynchronously with a call to NdisMSetInformationComplete when it has set itself or its NIC to the state requested.
NDIS_STATUS_INVALID_OID
MiniportSetInformation did not recognize the Oid.
NDIS_STATUS_INVALID_LENGTH
The InformationBufferLength does not match the length required by the given Oid. MiniportSetInformation returned how many bytes the buffer should be at BytesNeeded.
NDIS_STATUS_INVALID_DATA
The data supplied at InformationBuffer was invalid for the given Oid.
NDIS_STATUS_NOT_ACCEPTED
MiniportSetInformation attempted the requested set operation on the NIC but was unsuccessful.
NDIS_STATUS_NOT_SUPPORTED
MiniportSetInformation does not support the Oid, which is optional.
NDIS_STATUS_RESOURCES
MiniportSetInformation could not carry out the requested operation due to resource constraints. This return value does not necessarily mean that the same request, submitted at a later time, will be failed for the same reason.

Comments

NDIS calls MiniportSetInformation either on its own behalf, such as to manage bindings to the miniport, or when a bound protocol driver calls NdisRequest.

If MiniportSetInformation returns NDIS_STATUS_PENDING, the driver must complete the request later by calling NdisMSetInformationComplete. Until it completes any request, the miniport can safely access the memory at InformationBuffer, BytesRead, and BytesNeeded. After the miniport completes any set request, ownership of these variables and the buffer reverts to NDIS or the caller of NdisRequest, whichever allocated the memory.

After a call to MiniportSetInformation, NDIS submits no other requests to the driver until it has completed the operation, either synchronously or asynchronously. Instead, NDIS holds requests queued until the current set operation is completed.

For more information about the system-defined OIDs, see Chapter 5.

MiniportSetInformation can be pre-empted by an interrupt.

By default, MiniportSetInformation runs at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportQueryInformation, NdisMSetInformationComplete, NdisRequest, NDIS_REQUEST


MiniportShutdown

VOID
MiniportShutdown(
IN PVOID ShutdownContext
);

MiniportShutdown is an optional function that restores a NIC to its initial state when the system is shut down, whether by the user or because an unrecoverable system error occurred.

Parameters

ShutdownContext
Points to a context area supplied when the NIC driver’s MiniportInitialize function called NdisMRegisterAdapterShutdownHandler. Usually, this input parameter is the NIC-specific MiniportAdapterContext pointer passed to other MiniportXxx functions.

Comments

Every NIC driver should have a MiniportShutdown function. MiniportShutdown does nothing more than restore the NIC to its initial state (before the miniport’s DriverEntry function runs). However, this ensures that the NIC is in a known state and ready to be reinitialized when the machine is rebooted after a system shutdown occurs for any reason, including a crash dump.

A NIC driver’s MiniportInitialize function must call NdisMRegisterAdapterShutdownHandler to set up a MiniportShutdown function. The driver’s MiniportHalt function must make a reciprocal call to NdisMDeregisterAdapterShutdownHandler.

If MiniportShutdown is called due to a user-initiated system shutdown, it runs at IRQL PASSIVE_LEVEL in a system-thread context. If it is called due to an unrecoverable error, MiniportShutdown runs at an arbitrary IRQL and in the context of whatever component raised the error. For example, MiniportShutdown might be run at high DIRQL in the context of an ISR for a device essential to continued execution of the system.

Because MiniportShutdown runs at an arbitrary IRQL, it can call only the set of Ndis Xxx functions that are safe to call at any IRQL.

See Also

MiniportHalt, MiniportInitialize, NdisMDeregisterAdapterShutdownHandler, NdisMRegisterAdapterShutdownHandler, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawReadPortUlong, NdisRawReadPortUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisRawWritePortUchar, NdisRawWritePortUlong, NdisRawWritePortUshort, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


MiniportSynchronizeISR

BOOLEAN
MiniportSynchronizeISR(
IN PVOID SynchronizeContext
);

MiniportSynchronizeISR is a required function if any NIC driver function that runs at less than DIRQL shares resources with that driver’s MiniportISR or MiniportDisableInterrupt function.

Parameters

SynchronizeContext
Points to a context area supplied when the NIC driver’s MiniportXxx or internal function called NdisMSynchronizeWithInterrupt. Usually, this input parameter is a pointer to a set of shared resources at an offset within the NIC-specific MiniportAdapterContext area passed to other MiniportXxx functions.

Return Value

MiniportSynchronizeISR returns a Boolean with a driver-determined meaning that is propagated to the caller of NdisMSynchronizeWithInterrupt.

Comments

If any NIC driver function shares resources, such as device registers, with the driver’s MiniportISR or MiniportDisableInterrupt function, the driver function that runs, by default, at less than DIRQL cannot access those resources directly. If it attempted to access such shared resources directly, the lower priority function might be pre-empted by execution of MiniportISR or MiniportDisableInterrupt, which could change state out from under the lower priority driver function.

To synchronize access to such shared resources with MiniportISR or MiniportDisableInterrupt, lower priority driver functions must call NdisMSynchronizeWithInterrupt to have the driver’s MiniportSynchronizeISR function access those shared resources at DIRQL. Calling NdisMSynchronizeWithInterrupt prevents race conditions and deadlocks in such a NIC driver, particularly in SMP machines.

Any lower priority driver functions that share resources among themselves (but not with any function that runs at DIRQL) should use a spin lock to protect those shared resources.

MiniportSynchronizeISR runs at the DIRQL assigned when the driver’s MiniportInitialize function called NdisMRegisterInterrupt. Like any driver function that runs at DIRQL, MiniportSynchronizeISR should return control as quickly as possible, and it can call only those Ndis Xxx functions that are safe to call at any IRQL.

See Also

MiniportDisableInterrupt, MiniportInitialize, MiniportISR, NdisAllocateSpinLock, NdisMRegisterInterrupt, NdisMSynchronizeWithInterrupt, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawReadPortUlong, NdisRawReadPortUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisRawWritePortUchar, NdisRawWritePortUlong, NdisRawWritePortUshort, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


MiniportTimer

VOID
MiniportTimer(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
 );

MiniportTimer is a required function if a driver’s NIC does not generate interrupts. Otherwise, one or more MiniportTimer functions are optional.

Parameters

SystemSpecific1
Points to a system-specific variable, which is opaque to MiniportTimer and reserved for system use.
FunctionContext
Specifies the context pointer passed to NdisMInitializeTimer when MiniportInitialize set up the associated timer object.
SystemSpecific2
Points to a system-specific value that is reserved for system use.
SystemSpecific3
Points to a system-specific value that is reserved for system use.

Comments

The driver of a NIC that does not generate interrupts must have a MiniportTimer function to poll the state of the NIC. After such a driver’s MiniportInitialize function sets up the driver-allocated timer object with NdisMInitializeTimer, a call to NdisMSetPeriodicTimer causes the MiniportTimer function associated with the timer object to be run repeatedly and automatically at the interval specified by MillisecondsPeriod. Such a polling MiniportTimer function monitors the state of the NIC to determine when to make indications, when to complete pending sends, and so forth. In effect, such a polling MiniportTimer function has the same functionality as the MiniportHandleInterrupt function in the driver of a NIC that does generate interrupts.

By contrast, calling NdisMSetTimer causes the MiniportTimer function associated with the timer object to be run once when the given MillisecondsToDelay expires. Such a MiniportTimer function usually performs some driver-determined action if a particular operation times out.

If either type of MiniportTimer function shares resources with other driver functions, the driver should synchronize access to those resources with a spin lock.

Any NIC driver or intermediate driver can have more than one MiniportTimer function at the discretion of the driver writer. Each such MiniportTimer function must be associated with a driver-allocated and initialized timer object.

A call to NdisMCancelTimer cancels execution of a nonpolling MiniportTimer function, provided that the interval passed in the immediately preceding call to NdisMSetTimer has not yet expired. After a call to NdisMSetPeriodicTimer, a call to NdisMSetTimer or NdisMCancelTimer with the same timer object disables a polling MiniportTimer function: either the MiniportTimer function runs once, or it is canceled.

The MiniportHalt function of any driver with a MiniportTimer function should call NdisMCancelTimer to ensure that the MiniportTimer function does not attempt to access resources that MiniportHalt has already released.

By default, MiniportTimer runs at IRQL DISPATCH_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NdisAcquireSpinLock, NdisAllocateSpinLock, NdisDprAcquireSpinLock, NdisMCancelTimer, NdisMInitializeTimer, NdisMSetPeriodicTimer, NdisMSetTimer


MiniportTransferData

NDIS_STATUS
MiniportTransferData(
OUT PNDIS_PACKET Packet,
OUT PUINT BytesTransferred,
 IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_HANDLE MiniportReceiveContext,
IN UINT ByteOffset,
IN UINT BytesToTransfer
);

MiniportTransferData is a required function in NIC drivers that do not indicate multipacket receives and/or media-specific information with NdisMIndicateReceivePacket and in those that do not support WAN media.

Parameters

Packet
Points to a packet descriptor with chained buffers into which MiniportTransferData should copy the received data.
BytesTransferred
Points to a variable that MiniportTransferData sets to the number of bytes it copied into the packet. This value is meaningless if MiniportTransferData returns NDIS_STATUS_PENDING.
MiniportAdapterContext
Specifies the handle to a miniport-allocated context area in which the driver maintains per-NIC state, set up by MiniportInitialize.
MiniportReceiveContext
Specifies the context handle previously passed to NdisM..IndicateReceive. The miniport can examine this value to determine which receive to copy.
ByteOffset
Specifies the offset within the received packet at which MiniportTransferData should begin the copy. If the entire packet is be copied, ByteOffset is zero.
BytesToTransfer
Specifies how many bytes to copy. The sum of ByteOffset and BytesToTransfer should be less than or equal to the packet size that was specified in the miniport’s receive indication. This value can be zero.

Return Value

MiniportTransferData can return one of the the following:

NDIS_STATUS_SUCCESS
MiniportTransferData copied the requested data into the protocol-supplied packet, and it set BytesTransferred to the number of bytes copied.
NDIS_STATUS_PENDING
The driver will complete the transfer asynchronously with a call to NdisMTransferDataComplete.
NDIS_STATUS_FAILURE
Either the data could not be copied or the input transfer range was invalid.

Comments

MiniportTransferData copies the contents of the received packet to a given protocol-allocated packet.

NDIS calls MiniportTransferData when a ProtocolReceive function calls NdisTransferData. The media header associated with a packet cannot be copied; only the data portion of a packet can be copied. The range passed to NdisTransferData never includes the media header. Usually, the ByteOffset input to MiniportTransferData excludes data that the miniport already indicated in the lookahead buffer.

A miniport must be prepared to copy a given packet more than once. If its NIC supports reading a given packet only once, the miniport must copy each received network packet to a staging buffer.

MiniportTransferData can call NdisQueryPacket to determine how many buffer descriptors the allocating protocol has provided. The protocol is responsible for supplying a packet descriptor that accommodates the transfer it requested. MiniportTransferData can call NdisQueryBuffer to determine how much data to copy into each protocol-supplied buffer.

MiniportTransferData can fail a request if the given ByteOffset and BytesToTransfer exceed the packet size originally indicated. Alternatively, MiniportTransferData can simply transfer all available data starting at any valid ByteOffset.

Because MiniportTransferData is asynchronous, a miniport that has this function cannot deliver receive indications while a transfer is pending. If MiniportTransferData returns NDIS_STATUS_PENDING, the miniport must call NdisMTransferDataComplete when the transfer is complete.

A driver that indicates packets with NdisMIndicateReceivePacket does not need a MiniportTransferData function because such a driver always indicates full packets to bound protocols.

Interrupts are still disabled when MiniportTransferData is called.

By default, MiniportTransferData runs at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisGetFirstBufferFromPacket, NdisGetNextBuffer, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMoveMemory, NdisMTransferDataComplete, NdisMTrIndicateReceive, NdisQueryBuffer, NdisQueryPacket, NdisTransferData, ProtocolReceive


MiniportWanSend

NDIS_STATUS
MiniportWanSend(
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_HANDLE NdisLinkHandle,
IN PNDIS_WAN_PACKET WanPacket
);

MiniportWanSend is a required function if the driver controls a WAN NIC. Such a driver does not have a MiniportSendPackets or MiniportSend function.

Parameters

MiniportAdapterContext
Specifies the handle, originally input to MiniportInitialize, identifying the miniport and WAN NIC it controls.
NdisLinkHandle
Specifies the handle to a miniport-allocated context area for this link. The driver previously returned this handle in an NDIS_MAC_LINE_UP indication when the link was established.
WanPacket
Points to an NDIS_WAN_PACKET structure, specifying the data to be transmitted. This structure specifies the virtual range for a buffer with guaranteed padding at the beginning and end. The driver can manipulate the data in this buffer in any way.

Return Value

MiniportWanSend can return one of the following:

NDIS_STATUS_SUCCESS
The driver (or its NIC) has accepted the packet data for transmission, so MiniportWanSend is returning the packet.
NDIS_STATUS_PENDING
The driver will complete the packet asynchronously with a call to NdisMWanSendComplete.
NDIS_STATUS_FAILURE or NDIS_STATUS_XXX
The given packet was invalid or unacceptable to the NIC.

Comments

When MiniportWanSend is called, ownership of both the packet descriptor and the packet data is transferred to the driver until it completes the given packet, either synchronously or asynchronously. If MiniportWanSend returns a status other than NDIS_STATUS_PENDING, the request is considered complete and ownership of the packet immediately reverts to the initiator of the send request. If MiniportWanSend returns NDIS_STATUS_PENDING, the miniport subsequently must call NdisMWanSendComplete with the packet to indicate completion of the transmit request.

MiniportWanSend can use both the MacReserved x and WanPacketQueue areas within the NDIS_WAN_PACKET structure. However, the miniport cannot access the ProtocolReserved x members.

Any NDIS intermediate driver that binds itself to an underlying WAN miniport is responsible for providing a fresh NDIS_WAN_PACKET structure to the underlying driver’s MiniportWanSend function. Before such an intermediate driver calls NdisSend, it must repackage the send packet given to its MiniportWanSend function so the underlying driver will have MacReserved x and WanPacketQueue areas of its own to use.

The available header padding within a given packet can be calculated as (CurrentBuffer - StartBuffer), the available tail padding as (EndBuffer - (CurrentBuffer + CurrentLength)). The header and tail padding is guaranteed to be at least the length that the miniport requested in response to a preceding OID_WAN_GET_INFO query. The header and/or tail padding for any packet given to MiniportWanSend can be more than the length that was requested.

MiniportWanSend can neither return NDIS_STATUS_RESOURCES for an input packet nor call NdisMSendResourcesAvailable. Instead, the miniport must queue incoming send packets internally for subsequent transmission. The miniport controls how many packets NDIS will submit to MiniportWanSend when the NIC driver sets the SendWindow value in the driver’s NDIS_MAC_LINE_UP indication to establish the given link. NDISWAN uses this value as an upper bound on uncompleted sends submitted to MiniportWanSend, so the miniport’s internal queue cannot be overrun, and the miniport can adjust the SendWindow dynamically with subsequent line-up indications for established links. If the miniport set the SendWindow to zero when it called NdisMIndicateStatus with a particular line-up indication, NDISWAN uses the MaxTransmit value that the driver originally set in response to the OID_WAN_GET_INFO query as its limit on submitted but uncompleted send packets.

Each packet passed to MiniportWanSend is set up according to one of the flags that the miniport set in the FramingBits member in response to the OID_WAN_GET_INFO query. It will contain simple HDLC PPP framing if the driver claimed to support PPP. For SLIP or RAS framing, such a packet contains only the data portion with no framing whatsoever.

For more information about system-defined WAN and TAPI OIDs, see Chapter 5.

A WAN driver cannot manage software loopback or promiscuous mode loopback internally. NDISWAN supplies this loopback support for WAN drivers.

MiniportWanSend can be pre-empted by an interrupt.

By default, MiniportWanSend runs at IRQL DISPATCH_LEVEL.

See Also

MiniportQueryInformation, NDIS_MAC_LINE_UP, NdisMIndicateStatus, NdisMWanSendComplete, NDIS_WAN_PACKET


Chapter 2 NDIS Library Functions

This summarizes the set of NDIS-supplied functions and macros that NDIS drivers use to communicate with the system and with other NDIS drivers.

NDIS miniport drivers can call the functions described in the following:
2.1 NDIS Library Functions Used by Miniport Drivers
2.2 NDIS Library Functions Used by WAN NIC Miniport Drivers

NDIS protocol drivers can call the functions described in the following:
2.3 NDIS Library Functions Used by Protocol Drivers

NDIS intermediate drivers can call functions described in the following:
2.1 NDIS Library Functions Used by Miniport Drivers
2.2 NDIS Library Functions Used by WAN NIC Miniport Drivers
2.3 NDIS Library Functions Used by Protocol Drivers

However, NDIS intermediate drivers have no use for certain Ndis Xxx functions, such as DMA functions, that support communication with a physical NIC.


2.1 NDIS Library Functions Used by Miniport Drivers

ETH_COPY_NETWORK_ADDRESS
Copies a given Ethernet address to a given location.
FDDI_COMPARE_NETWORK_ADDRESSES
Sets a caller-supplied variable to a value indicating whether a given FDDI address is greater than, less than, or equal to another given FDDI address.
FDDI_IS_BROADCAST
Sets a caller-supplied variable to a Boolean value indicating whether a given FDDI address is a broadcast address.
FDDI_IS_MULTICAST
Sets a caller-supplied variable to a Boolean value indicating whether a given FDDI address is a multicast address.
FDDI_IS_SMT
Sets a caller-supplied variable to a Boolean value indicating whether a given FDDI frame is SMT (systems management utility).
NdisAcquireSpinLock
Acquires a spin lock so the caller can synchronize access to resources shared among nonISR driver routines in a multiprocessor-safe way.
NdisAdjustBufferLength
Modifies the length specified in a given buffer descriptor that was allocated or copied by the caller.
NdisAllocateBuffer
Creates a buffer descriptor describing a specified virtual (sub)range within an already allocated, nonpaged memory block, given a handle returned by NdisAllocateBufferPool.
NdisAllocateBufferPool
Returns a handle with which the caller can allocate buffer descriptors with NdisAllocateBuffer.
NdisAllocateMemory
Allocates resident (nonpaged) system-space memory, optionally within a specified address limit, as a physically contiguous allocation, and/or as a noncached allocation.
NdisAllocatePacket
Allocates a packet descriptor, given a handle returned by a preceding call to NdisAllocatePacketPool.
NdisAllocatePacketPool
Allocates and initializes a block of nonpaged packet pool, given the number of packet descriptors and how many bytes each fixed-size packet descriptor should contain.
NdisAllocateSpinLock
Initializes a variable of type NDIS_SPIN_LOCK, used to synchronize access to resources shared among nonISR driver functions. An initialized spin lock, for which the caller supplies resident storage, is a required parameter to the Ndis..SpinLock and NdisInterlocked Xxx functions.
NdisAnsiStringToUnicodeString
Converts a given counted ANSI string into a counted Unicode string.
NDIS_BUFFER_LINKAGE
Returns a pointer to a linked buffer, given a pointer to an NDIS buffer descriptor.
NDIS_BUFFER_TO_SPAN_PAGES
Determines how many pages of host memory are being used to back a given buffer.
NdisChainBufferAtBack
Links a given buffer descriptor at the end of the chain of buffer descriptors for a given packet descriptor.
NdisChainBufferAtFront
Links a given buffer descriptor at the start of the chain of buffer descriptors for a given packet descriptor.
NdisCloseConfiguration
Releases the handle returned by NdisOpenConfiguration after the caller has retrieved configuration information for a NIC or itself and/or, possibly, stored additional information about its NIC in the registry.
NdisCloseFile
Releases a handle returned by NdisOpenFile and frees the memory allocated to hold the file contents when it was opened.
NdisCopyBuffer
Creates a buffer descriptor for a specified virtual (sub)range, given a handle for a block of already allocated memory and a pointer to the buffer descriptor for the block.
NdisCopyFromPacketToPacket
Copies a specified number of bytes of data from one packet to another, starting at a specified offset in a given source packet’s data and copying the data to a specified offset in the target packet. Returns the number of bytes actually copied, which can be less than the requested number, depending on the source packet and specified starting offset for the copy operation.
NdisCreateLookaheadBufferFromSharedMemory
Returns the virtual address of a buffer within a block of memory shared with a busmaster DMA NIC so the caller can set up a read-only lookahead buffer for a receive indication to higher-level drivers.
NdisDestroyLookaheadBufferFromSharedMemory
Releases access to the buffer obtained in the preceding call to NdisCreateLookaheadBufferFromSharedMemory.
NdisDprAcquireSpinLock
Acquires a spin lock so the caller can synchronize access to resources shared among nonISR driver functions in a multiprocessor-safe way, provided that the caller is running at IRQL DISPATCH_LEVEL.
NdisDprAllocatePacket
Allocates and initializes a packet descriptor when the caller is running at IRQL DISPATCH_LEVEL, given a handle returned by a preceding call to NdisAllocatePacketPool.
NdisDprFreePacket
Releases a driver-allocated packet descriptor and returns it to the free list when the caller is running at IRQL DISPATCH_LEVEL.
NdisDprReleaseSpinLock
Releases a spin lock that was acquired in a preceding call to NdisDprAcquireSpinLock.
NdisEqualString
Compares two strings to determine whether they are equal.
NdisFlushBuffer
Flushes the memory region described by a given buffer descriptor from all processor caches.
NdisFreeBuffer
Releases a buffer descriptor obtained in a preceding call to NdisAllocateBuffer or NdisCopyBuffer.
NdisFreeBufferPool
Releases a handle obtained in a preceding call to NdisAllocateBufferPool.
NdisFreeMemory
Releases a handle, usually when the driver is being unloaded, that was obtained in a preceding call to NdisAllocateMemory.
NdisFreePacket
Releases a packet descriptor that was allocated from packet pool with NdisAllocatePacket and returns it to the free list.
NdisFreePacketPool
Releases a handle to a block of packet pool that was allocated with NdisAllocatePacketPool.
NdisFreeSpinLock
Releases a spin lock initialized in a preceding call to NdisAllocateSpinLock, usually when a driver is unloading.
NdisFreeString
Frees the storage for a buffered string.
NdisGetBufferPhysicalArraySize
Returns the number of discontiguous physical blocks in a buffer, given a buffer descriptor.
NdisGetCacheFillSize
Returns the size of the microprocessor cache boundary in bytes. (This information is useful primarily to drivers of busmaster DMA NICs.)
NdisGetCurrentProcessorCpuUsage
Returns how busy the current processor is as a percentage.
NdisGetCurrentSystemTime
Returns the current system time, suitable for use as a timestamp.
NdisGetFirstBufferFromPacket
Returns pointers to the buffer descriptor and base virtual address for the initial buffer chained to a given packet, along with the sizes of the initial buffer and full buffer, in case the buffer is fragmented.
NdisGetNextBuffer
Returns the next buffer descriptor in a chain, given the current buffer descriptor.
NdisGetPacketFlags
Returns the flags, if any, set by a protocol driver in a given packet descriptor.
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO
Returns the address (MediaSpecificInformation) and length (SizeMediaSpecificInfo) from the NDIS_PACKET_OOB_DATA block associated with the packet descriptor of a buffer containing out-of-band information, such as packet priority, about a given send packet set up by a protocol.
NDIS_GET_PACKET_STATUS
Returns the Status of an indicated receive packet on return from NdisMIndicateReceivePacket so the driver can determine whether its miniport-allocated packet descriptor (and chained buffer descriptors) can be prepared for reuse immediately.
NDIS_GET_PACKET_TIME_TO_SEND
Returns the protocol-set TimeToSend timestamp, directing the miniport when to send the given packet, from the NDIS_PACKET_OOB_DATA block associated with the incoming packet descriptor.
NdisGetPhysicalAddressHigh
Returns the high-order part of a given physical address.
NdisGetPhysicalAddressLow
Returns the low-order part of a given physical address.
NdisIMDeInitializeDeviceInstance
Calls an NDIS intermediate driver's MiniportHalt function to tear down the driver's virtual NIC.
NdisIMInitializeDeviceInstance
Calls an NDIS intermediate driver’s MiniportInitialize function to set up the driver’s virtual NIC for I/O operations bound for an underlying NIC driver to which the intermediate driver is bound.
NdisImmediateReadPciSlotInformation
Returns a specified range of PCI configuration information to a caller-supplied buffer, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisImmediateReadPortUchar
Reads a byte from a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to read the data.
NdisImmediateReadPortUlong
Reads a ULONG from a specified port on an I/O bus during driver initialization, given a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to read the data.
NdisImmediateReadPortUshort
Reads a USHORT from a specified port on an I/O bus during driver initialization, given a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to read the data.
NdisImmediateReadSharedMemory
Reads a specified number of bytes into a caller-supplied buffer from shared device RAM, given the base physical address for the shared memory and a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative memory range in order to read the data.
NdisImmediateWritePciSlotInformation
Writes data from a caller-supplied buffer to a specified range of the PCI configuration space, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisImmediateWritePortUchar
Writes a byte to a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to write the data.
NdisImmediateWritePortUlong
Writes a ULONG to a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to write the data.
NdisImmediateWritePortUshort
Writes a USHORT to a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to write the data.
NdisImmediateWriteSharedMemory
Writes a specified number of bytes from a caller-supplied buffer to shared device RAM, given the base physical address for the shared memory and a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative memory range in order to write the data.
NdisIMQueueMiniportCallback
Calls an intermediate driver’s callback function to when it can execute in a MiniportXxx context, as, for example, to forward an indication to higher-level protocols.
NdisIMRegisterLayeredMiniport
Registers an NDIS intermediate driver’s MiniportXxx entry points and name with the NDIS library when the driver initializes.
NdisIMRevertBack
Returns the function of an intermediate driver to the context in which it was executing before it called NdisIMSwitchToMiniport.
NdisIMSwitchToMiniport
Allows an intermediate driver function to call NdisM Xxx normally called only from MiniportXxx functions, as, for example, to make indications up to higher-level protocols.
NdisInitAnsiString
Initializes a counted ANSI string.
NDIS_INIT_FUNCTION
Marks a given driver function as initialization-only code to be discarded after the function has run once.
NdisInitializeEvent
Sets up an event object during driver initialization to be used subsequently as a synchronization mechanism.
NdisInitializeListHead
Sets up a queue header for a driver’s internal queue, given a pointer to driver-supplied resident storage for the queue header and queue. Then, entries (usually packets) can be added to and removed from the doubly-linked queue with the NdisInterlocked..List functions.
NdisInitializeString
Allocates a buffer for and initializes a counted string in the system-default character set, given a source string and a NULL pointer.
NdisInitUnicodeString
Initializes a counted Unicode string.
NdisInterlockedAddUlong
Adds an unsigned long value to a given unsigned integer as an atomic operation, using a caller-supplied and initialized spin lock.
NdisInterlockedInsertHeadList
Inserts an entry, usually a packet, at the head of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among nonISR driver functions.
NdisInterlockedInsertTailList
Inserts an entry, usually a packet, at the end of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among nonISR driver functions.
NdisInterlockedRemoveHeadList
Removes an entry, usually a packet, from the head of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among nonISR driver functions.
NdisMAllocateMapRegisters
Reserves system resources during NIC driver initialization for subsequent busmaster DMA operations.
NdisMAllocateSharedMemory
Allocates and maps a memory range so it is simultaneously accessible from both the system and a busmaster DMA device, given the MiniportAdapterHandle for the NIC input to MiniportInitialize, the number of bytes to be allocated, and whether the memory should be cached. Returns the base (system) virtual address of the allocation for use by the miniport and a mapped base logical address for use by the NIC.
NdisMAllocateSharedMemoryAsync
Allocates additional memory shared between a miniport and its busmaster DMA NIC, usually when the miniport is running low on available receive buffers.
NdisMapFile
Maps the contents of an already open file to a caller-accessible buffer if the file currently is unmapped, given a handle returned by NdisOpenFile.
NdisMArcIndicateReceive
Notifies NDIS that an ARCNET packet is being received on a particular NIC, identified by the adapter handle, and that the packet or some initial lookahead portion of the packet, along with the packet header, is available to be forwarded to the appropriate bound protocol driver(s).
NdisMArcIndicateReceiveComplete
Notifies NDIS that an ARCNET receive packet, identified in a preceding call to NdisMArcIndicateReceive, has been fully transferred through a particular NIC, identified by the given adapter handle, so that NDIS can notify the appropriate bound protocol driver(s).
NdisMCancelTimer
Cancels a preceding call to NdisMSetPeriodicTimer or cancels the immediately preceding call to NdisMSetTimer if the interval given to NdisMSetTimer has not yet expired.
NdisMCloseLog
Releases resources allocated when the driver called NdisMCreateLog.
NdisMCompleteBufferPhysicalMapping
Flushes any data remaining in a busmaster NIC’s internal cache at the end of a transfer operation that was set up with NdisMStartBufferPhysicalMapping.
NdisMCompleteDmaTransfer
Completes a system DMA transfer operation begun with a preceding call to NdisMSetupDmaTransfer, ensuring that the data is flushed to system memory or to the NIC, depending on the transfer direction.
NdisMCreateLog
Allocates and opens a log file in which a NIC miniport can write data to be displayed by a driver-dedicated Win32 application.
NdisMDeregisterAdapterShutdownHandler
Removes a driver-supplied MiniportShutdown function when the driver is unloading from the set of shutdown handlers that were registered with NdisMRegisterAdapterShutdownHandler.
NdisMDeregisterDmaChannel
Releases DMA system resources, including claimed hardware resources in the registry, that were acquired with NdisMRegisterDmaChannel. Drivers call NdisMDeregisterDmaChannel when they are unloading.
NdisMDeregisterInterrupt
Releases the mapping created with an initialization-time call to NdisMRegisterInterrupt. Drivers call NdisMDeregisterInterrupt when they are unloading.
NdisMDeregisterIoPortRange
Releases a mapping that was set up with NdisMRegisterIoPortRange during driver initialization.
NdisMEthIndicateReceive
Notifies NDIS that an Ethernet packet is being received on a particular NIC, identified by the adapter handle, and that the packet or some initial lookahead portion of the packet, along with the packet header, is available to be forwarded to the appropriate bound protocol driver(s).
NdisMEthIndicateReceiveComplete
Notifies NDIS that an Ethernet receive packet, identified in a preceding call to NdisMEthIndicateReceive, has been fully transferred through a particular NIC, identified by the given adapter handle, so that NDIS can notify the appropriate bound protocol driver(s).
NdisMFddiIndicateReceive
Notifies NDIS that an FDDI packet is being received on a particular NIC, identified by the adapter handle, and that the packet or some initial lookahead portion of the packet, along with the packet header, is available to be forwarded to the appropriate bound protocol driver(s).
NdisMFddiIndicateReceiveComplete
Notifies NDIS that an FDDI receive packet, identified in a preceding call to NdisMFddiIndicateReceive, has been fully transferred through a particular NIC, identified by the given adapter handle, so that NDIS can notify the appropriate bound protocol driver(s).
NdisMFlushLog
Clears a miniport’s log file that was allocated with NdisMCreateLog.
NdisMFreeMapRegisters
Releases system resources for busmaster DMA that were reserved with NdisMAllocateMapRegisters. Drivers call NdisMFreeMapRegisters when they are unloading or during initialization if they fail to successfully initialize a NIC.
NdisMFreeSharedMemory
Releases a block of memory that was allocated with NdisMAllocateSharedMemory. Drivers call NdisMFreeSharedMemory when they are unloading or during initialization if they fail to successfully initialize a NIC.
NdisMIndicateReceivePacket
Notifies NDIS that an array of received packets is available to be forwarded to the appropriate bound protocol driver(s).
NdisMIndicateStatus
Notifies NDIS of a change in the runtime status of a NIC that might be corrected with a reset operation or possibly even require the NIC to be taken out of service, depending on the given NDIS_STATUS_XXX and additional driver-supplied information.
NdisMIndicateStatusComplete
Notifies NDIS that the driver, which has previously called NdisMIndicateStatus one or more times to indicate hardware anomalies or runtime state changes in the NIC, is ready to resume normal device I/O operations.
NdisMInitializeTimer
Sets up a caller-supplied MiniportTimer function to be associated with a timer object during driver initialization; returns a handle at the caller-supplied resident storage for the timer object. This handle is a required parameter to all other NdisM..Timer functions.
NdisMInitializeWrapper
Notifies NDIS that a new NIC driver is initializing and returns a WrapperConfigurationContext handle to be passed in subsequent calls to Ndis Xxx initialization and configuration functions.
NdisMMapIoSpace
Maps a given bus-relative “physical” range of device memory, such as on-board device RAM or a set of NIC registers, onto system-space nonpaged pool and returns the mapped virtual base address for subsequent use in PIO operations.
NdisMoveFromMappedMemory
Copies data from device memory that was mapped with NdisMMapIoSpace to a system-space buffer.
NdisMoveMappedMemory
Copies data from one location to another in a memory block that was mapped with NdisMMapIoSpace.
NdisMoveMemory
Copies a specified number of bytes from one caller-supplied location to another.
NdisMoveToMappedMemory
Copies data from a system-space buffer to device memory that was mapped with NdisMMapIoSpace.
NdisMPciAssignResources
Claims resources in the registry for the caller and returns the hardware configuration information for a PCI NIC, given a handle passed in to the MiniportInitialize function and the slot number of its NIC on the PCI bus.
NdisMQueryAdapterResources
Either returns a list of PCMCIA resources used by a NIC or returns NDIS_STATUS_NOT_SUPPORTED.
NdisMQueryInformationComplete
Returns the final status of a query-information request for which the driver previously returned NDIS_STATUS_PENDING.
NdisMReadDmaCounter
Returns the number of bytes remaining to be transferred during a DMA operation if the NIC uses the host DMA controller’s autoinitialize mode, given a handle returned by NdisMRegisterDmaChannel.
NdisMRegisterAdapterShutdownHandler
Registers a driver-supplied MiniportShutdown function to be called with a pointer to a caller-supplied context buffer when the system is about to be shut down, given a handle passed in to the MiniportInitialize function. Such a MiniportShutdown function restores the NIC to its initial state.
NdisMRegisterDmaChannel
Reserves system resources during driver initialization for subsequent slave DMA operations or ISA busmaster DMA operations and claims a DMA channel in the registry, returning a handle to be passed in subsequent calls to the NdisM Xxx DMA functions.
NdisMRegisterInterrupt
Sets up a mapping between the miniport’s ISRHandler and HandleInterruptHandler (DPC) functions, already registered with NdisMRegisterMiniport, and the bus-relative vector (or level) on which its NIC interrupts, given a pointer to caller-allocated storage for opaque interrupt state and the adapter handle for the NIC. The interrupt handle initialized in this call is a required parameter to NdisMSynchronizeWithInterrupt.
NdisMRegisterIoPortRange
Claims a range of device memory in the registry and maps a given bus-relative range of I/O ports to a range of system logical addresses during initialization, which the driver can pass in subsequent calls to the NdisRaw Xxx to communicate with its NIC.
NdisMRegisterMiniport
Registers a miniport’s entry points and name with the NDIS library, given a handle obtained from NdisMInitializeWrapper and a pointer to the NDIS_MINIPORT_CHARACTERISTICS structure set up by the caller.
NdisMResetComplete
Returns the final status of a reset request for which the driver previously returned NDIS_STATUS_PENDING and indicates whether the reset operation on the NIC affected its network address information.
NdisMSendComplete
Returns the packet and final status of a completed send request for which the driver previously returned NDIS_STATUS_PENDING, so that NDIS can return the completed packet to the appropriate bound protocol driver(s).
NdisMSendResourcesAvailable
Notifies NDIS that the driver has internal resources available to process another send request when the driver has an outstanding send request for which it returned NDIS_STATUS_PENDING but has not yet called NdisMSendComplete.
NdisMSetAttributes
Informs the NDIS library about significant features of the caller’s NIC, such as whether it is a busmaster and the type of I/O bus on which the NIC can be found, so the miniport can call other Ndis Xxx initialization and configuration routines, particularly the NdisRegister Xxx. NIC drivers can call this function or NdisMSetAttributesEx. Intermediate drivers must call NdisMSetAttributesEx.
NdisMSetAttributesEx
Informs the NDIS library about significant features of the caller’s NIC, such as whether it is a busmaster and the type of I/O bus on which the NIC can be found, so the miniport can call other Ndis Xxx initialization and configuration routines, particularly the NdisRegister Xxx. In addition, this function prevents NDIS from timing out sends and requests in an intermediate driver or optionally resets the interval at which a NIC driver’s MiniportCheckForHang function is called.
NdisMSetInformationComplete
Returns the final status of a set-information request for which the driver previously returned NDIS_STATUS_PENDING.
NdisMSetPeriodicTimer
Sets a timer to fire periodically, thereby running an associated MiniportTimer function repeatedly at a specified fixed interval.
NdisMSetTimer
Sets a caller-initialized timer object to a specified interval, thereby running an associated MiniportTimer function once when the given interval expires.
NdisMSetupDmaTransfer
Sets up the host DMA controller to transfer a specified range of data from a given buffer, given the handle returned by a preceding call to NdisMRegisterDmaChannel and the transfer direction.
NdisMSleep
Delays execution of the caller for a given interval in microseconds.
NdisMStartBufferPhysicalMapping
Sets up a busmaster NIC for a DMA transfer operation, given the adapter handle for the NIC, the direction of the transfer, a pointer to the buffer descriptor, an index to the initial map register, a pointer to caller-allocated memory for a variable-size array of NDIS_PHYSICAL_UNIT structures, and the number of elements in the array.
NdisMSynchronizeWithInterrupt
Synchronizes the execution of a caller-supplied MiniportSynchronizeISR function with that of the ISR associated with the given initialized interrupt handle. When it runs, the supplied function can access data shared with the ISR in a multiprocessor-safe way.
NdisMTrIndicateReceive
Notifies NDIS that a Token Ring packet is being received on a particular NIC, identified by the adapter handle, and that the packet or some initial lookahead portion of the packet, along with the packet header, is available to be forwarded to the appropriate bound protocol driver(s).
NdisMTrIndicateReceiveComplete
Notifies NDIS that a Token Ring packet, identified in a preceding call to NdisMTrIndicateReceive, has been fully transferred through a particular NIC, identified by the given adapter handle, so that NDIS can notify the appropriate bound protocol driver(s).
NdisMTransferDataComplete
Returns the packet and final status of a completed transfer-data request for which the driver previously returned NDIS_STATUS_PENDING and returns the number of bytes actually transferred.
NdisMUnmapIoSpace
Releases a mapping obtained with NdisMMapIoSpace, usually when a driver is unloading.
NdisMUpdateSharedMemory
Ensures that data read from shared memory is current when the busmaster NIC has just written data to shared memory on platforms that require explicit coherency support from the system.
NdisMWriteLogData
Transfers miniport-supplied information into its log file for consumption and display by a driver-dedicated Win32 application.
NDIS_OOB_DATA_FROM_PACKET
Returns a pointer to the NDIS_PACKET_OOB_DATA block associated with a given packet descriptor.
NdisOpenConfiguration
Returns a handle for a miniport’s HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
\DriverName\Parameters registry key. This handle is a required parameter to other Ndis Xxx configuration functions.
NdisOpenFile
Returns a handle for an opened file, given its name and the highest acceptable address at which to allocate memory for the file data. The returned handle is a required parameter to NdisMapFile, which gives the caller exclusive access to the file data until it calls NdisUnmapFile.
NDIS_PAGABLE_FUNCTION
Marks a driver function as pageable code.
NDIS_PHYSICAL_ADDRESS_CONST
Initializes a constant of type NDIS_PHYSICAL_ADDRESS.
NdisPrintString
Displays a given string in the debugging window.
NdisQueryBuffer
Retrieves the size of the range, and optionally its base virtual address, specified in a given buffer descriptor.
NdisQueryBufferOffset
Retrieves the base virtual address of the range specified in a given buffer descriptor.
NdisQueryMapRegisterCount
Returns the maximum number of available map registers in the current platform so the driver of a busmaster DMA NIC can call NdisMAllocateMapRegisters with an appropriate value during initialization.
NdisQueryPacket
Returns any or all of the following information about a given packet descriptor: a count of buffer descriptors chained to the packet descriptor, the total number of bytes in all buffers mapped by the buffer descriptor chained to the packet descriptor, the total number of physically discontiguous blocks for all such buffers, and a pointer to the initial buffer descriptors in the chain.
NdisQuerySendFlags
See NdisGetPacketFlags.
NdisRawReadPortBufferUchar
Reads a specified number of bytes into a caller-supplied buffer, given an I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawReadPortBufferUlong
Reads a specified number of ULONGs into a caller-supplied buffer, given an I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawReadPortBufferUshort
Reads a specified number of USHORTs into a caller-supplied buffer, given an I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawReadPortUchar
Reads a byte from a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawReadPortUlong
Reads a ULONG from a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawReadPortUshort
Reads a USHORT from a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawWritePortBufferUchar
Writes a specified number of bytes from a caller-supplied buffer to a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawWritePortBufferUlong
Writes a specified number of ULONGs from a caller-supplied buffer to a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawWritePortBufferUshort
Writes a specified number of USHORTs from a caller-supplied buffer to a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawWritePortUchar
Writes a given byte to a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawWritePortUlong
Writes a given ULONG to a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisRawWritePortUshort
Writes a given USHORT to a given I/O port mapped during initialization with NdisMRegisterIoPortRange.
NdisReadConfiguration
Reads the value of a named entry of the specified type, given the registry handle for the caller’s Parameters key returned by NdisOpenConfiguration.
NdisReadEisaSlotInformation
Returns EISA configuration information to a caller-supplied buffer during initialization, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisReadEisaSlotInformationEx
Returns EISA configuration information, including all available EISA function information, to a caller-supplied buffer during initialization, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisReadMcaPosInformation
Returns POS configuration information to a caller-supplied buffer during initialization, given a handle to the wrapper configuration context and the channel number for the NIC on the I/O bus.
NdisReadNetworkAddress
Returns the size and value, in binary, of the NetworkAddress entry, specifying the setup-configured network address of the NIC, from the caller’s Parameters key in the registry, given the handle obtained from NdisOpenConfiguration.
NdisReadPciSlotInformation
Returns a specified range of PCI configuration information to a caller-supplied buffer during initialization, given an adapter handle and the slot number for the NIC on the I/O bus.
NdisReadRegisterUchar
Reads a byte from a device register, given its mapped memory-space address.
NdisReadRegisterUlong
Reads a ULONG from a device register, given its mapped memory-space address.
NdisReadRegisterUshort
Reads a USHORT from a device register, given its mapped memory-space address.
NdisRecalculatePacketCounts
Resets the valid counts of chained buffer descriptors for a given packet descriptor.
NdisReinitializePacket
Sets to NULL the queue head for any chained buffers in a given packet descriptor and reinitializes it for reuse.
NdisReleaseSpinLock
Releases a spin lock that was acquired in a preceding call to NdisAcquireSpinLock.
NdisResetEvent
Clears the Signaled state of a given event.
NdisRetrieveUlong
Retrieves a ULONG value from the source address, avoiding alignment faults.
NdisSetEvent
Sets a given event to the Signaled state if it was not already Signaled, thereby causing waits on the event to be satisfied.
NDIS_SET_PACKET_HEADER_SIZE
Sets the HeaderSize in the NDIS_PACKET_OOB_DATA block associated with a miniport-allocated packet descriptor for received net packet(s) to be indicated.
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO
Sets the MediaSpecificInformation and SizeMediaSpecificInfo in the NDIS_PACKET_OOB_DATA block associated with a miniport-allocated packet descriptor for a received net packet about to be indicated.
NDIS_SET_PACKET_STATUS
Sets the Status in the NDIS_PACKET_OOB_DATA block associated with packet descriptor for a received net packet about to be indicated or for a given send packet completed in the MiniportSend(Packets) function.
NDIS_SET_PACKET_TIME_RECEIVED
Sets the TimeReceived in the NDIS_PACKET_OOB_DATA block associated with a miniport-allocated packet descriptor for a received net packet about to be indicated with NdisMIndicateReceivePacket.
NDIS_SET_PACKET_TIME_SENT
Sets the TimeSent in the NDIS_PACKET_OOB_DATA associated with a miniport-allocated packet descriptor for a received net packet about to be indicated with NdisMIndicateReceivePacket.
NdisSetPhysicalAddressHigh
Sets the high-order part of a given physical address to a given value.
NdisSetPhysicalAddressLow
Sets the low-order part of a given physical address to a given value.
NdisStallExecution
Stalls the caller on the current processor for a given interval in microseconds.
NdisStoreUlong
Stores a ULONG value at a particular address, avoiding alignment faults.
NdisSystemProcessorCount
Determines whether its caller is running on a uniprocessor or multiprocessor machine, which is useful information for allocating resources during driver initialization.
NdisTerminateWrapper
Releases system resources allocated when the driver called NdisInitializeWrapper. Drivers call NdisTerminateWrapper during initialization if they cannot find and successfully initialize at least one NIC or when they are unloading.
NdisUnchainBufferAtBack
Removes a given buffer descriptor at the end of the chain of buffer descriptors for a given packet descriptor and returns a pointer to the buffer descriptor.
NdisUnchainBufferAtFront
Removes a given buffer descriptor at the start of the chain of buffer descriptors for a given packet descriptor and returns a pointer to the buffer descriptor.
NdisUnicodeStringToAnsiString
Converts a given counted Unicode string into a counted ANSI string.
NdisUnmapFile
Releases the file mapping set up by the preceding call to NdisMapFile.
NdisWaitEvent
Puts the caller into a wait state until the given event is set to the Signaled state or the wait times out.
NdisWriteConfiguration
Writes a caller-supplied value entry in the registry, given the handle for the caller’s Parameters key returned by NdisOpenConfiguration
NdisWriteErrorLogEntry
Allocates an error-log record, fills it in, and writes it to the I/O error log, given the adapter handle for the NIC, an NDIS_XXX error code, a count of caller-supplied error values, and a corresponding number of ULONGs specifying additional information about the I/O error or configuration error.
NdisWritePciSlotInformation
Writes data from a caller-supplied buffer to a specified range of the PCI configuration space, given a handle to the adapter and the slot number for the NIC on the I/O bus.
NdisWriteRegisterUchar
Writes a given byte to a device register, given its mapped memory-space address.
NdisWriteRegisterUlong
Writes a given ULONG to a device register, given its mapped memory-space address.
NdisWriteRegisterUshort
Writes a given USHORT to a device register, given its mapped memory-space address.
NdisZeroMappedMemory
Fills a block of memory that was mapped in a preceding call to NdisMapIoSpace with zeros, given a pointer to the block and the length in bytes to be filled.
NdisZeroMemory
Fills a block of memory with zeros, given a pointer to the block and the length in bytes to be filled.
TR_COMPARE_NETWORK_ADDRESSES
Sets a caller-supplied variable to a value indicating whether a given Token Ring address is greater than, less than, or equal to another given Token Ring address.
TR_COPY_NETWORK_ADDRESS
Copies a given Token Ring address to a given location.
TR_IS_BROADCAST
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a broadcast address.
TR_IS_FUNCTIONAL
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a functional address.
TR_IS_GROUP
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a group address.
TR_IS_NOT_DIRECTED
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is neither a functional nor a group address.
TR_IS_SOURCE_ROUTING
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a source-routing address.

To ensure their portability across Microsoft operating systems that support Win32, most NDIS miniport drivers call only functions exported by the NDIS library and use only macros explicitly declared in the master header file, ndis.h, or in the appropriate efilter.h, ffilter.h, tfilter.h, or afilter.h, if the NDIS uses the system-supplied Ethernet, FDDI, Token Ring, or ARCNET filter-library support to manage bindings for the miniport.

However, in addition to the NDIS library functions listed here, a miniport driver might use some of the macros and support routines exported by other Windows NT kernel-mode components, such as the following:
CONTAINING_RECORD
InitializeListHead
InsertHeadList
InsertTailList
IsListEmpty
RemoveEntryList
RemoveHeadList

For more information about the preceding Windows NT macro and functions, see the Kernel-Mode Driver Reference.


2.2 NDIS Library Functions Used by WAN NIC Miniport Drivers

NDIS miniport drivers of WAN NICs call the same NDIS library functions that other NIC miniport drivers call with very few exceptions. See the preceding summary of NDIS Library Functions Used by Miniport Drivers for a list of the common functions that WAN and LAN miniports call.

However, WAN miniport drivers cannot call the following Ndis Xxx functions:
NdisMIndicateReceive
NdisMIndicateReceiveComplete
NdisMSendComplete

Instead, WAN miniport drivers call these functions:

NdisMWanIndicateReceive
Notifies NDIS that a packet has been received on a particular link, identified by the given link-context handle, and on a particular NIC, identified by the adapter handle, and that the full packet is available to be forwarded to the appropriate bound protocol driver(s).
NdisMWanIndicateReceiveComplete
Notifies NDIS that one or more receives on a particular link, identified by the given link-context handle, and on a particular NIC, identified by the adapter handle, are complete (that is, the preceding call to NdisMWanIndicateReceive has returned) so that NDIS can notify the appropriate bound protocol driver(s).
NdisMWanSendComplete
Returns the packet and final status of a completed send request for which the driver previously returned NDIS_STATUS_PENDING so that NDIS can return the completed packet to the appropriate bound protocol driver.


2.3 NDIS Library Functions Used by Protocol Drivers

In addition to the Ndis Xxx listed here, highest-level NDIS protocol drivers that are Windows NT transport protocols call the Tdi Xxx functions described later in Part II.

NdisAcquireSpinLock
Acquires a spin lock so the caller can synchronize access to resources shared among nonISR driver routines in a multiprocessor-safe way.
NdisAdjustBufferLength
Modifies the length specified in a given buffer descriptor that was allocated or copied by the caller.
NdisAllocateBuffer
Creates a buffer descriptor describing a specified virtual (sub)range within an already allocated, nonpaged memory block, given a handle returned by NdisAllocateBufferPool or NdisAllocateMemory.
NdisAllocateBufferPool
Returns a handle with which the caller can allocate buffer descriptors with NdisAllocateBuffer.
NdisAllocateMemory
Allocates resident (nonpaged) system-space memory, optionally within a specified address limit, as a physically contiguous allocation, and/or as a noncached allocation.
NdisAllocatePacket
Allocates a packet descriptor, given a handle returned by a preceding call to NdisAllocatePacketPool.
NdisAllocatePacketPool
Allocates and initializes a block of nonpaged packet pool, given the number of packet descriptors and how many bytes each fixed-size packet descriptor should contain.
NdisAllocateSpinLock
Initializes a variable of type NDIS_SPIN_LOCK, used to synchronize access to data shared among driver routines. An initialized spin lock, for which the caller supplies resident storage, is a required parameter to the Ndis..SpinLock and NdisInterlocked Xxx functions.
NdisAnsiStringToUnicodeString
Converts a given counted ANSI string into a counted Unicode string.
NDIS_BUFFER_LINKAGE
Returns a pointer to a linked buffer, given a pointer to an NDIS buffer descriptor.
NdisChainBufferAtBack
Links a given buffer descriptor at the end of the chain of buffer descriptors for a given packet descriptor.
NdisChainBufferAtFront
Links a given buffer descriptor at the start of the chain of buffer descriptors for a given packet descriptor.
NdisCloseAdapter
Releases the binding set up with NdisOpenAdapter, given the binding handle that it previously returned.
NdisCloseConfiguration
Releases the handle returned by NdisOpenProtocolConfiguration after the caller has retrieved configuration information and/or, possibly, stored additional information about itself in the registry.
NdisCompleteBindAdapter
Completes a binding operation when the driver returned NDIS_STATUS_PENDING from its ProtocolBindAdapter function.
NdisCompleteUnbindAdapter
Completes a close-adapter operation when the driver returned NDIS_STATUS_PENDING from its ProtocolUnbindAdapter function.
NdisCopyBuffer
Creates a buffer descriptor for a specified virtual (sub)range, given a handle for a block of already allocated memory and a pointer to the memory descriptor for the block.
NdisCopyFromPacketToPacket
Copies a specified number of bytes of data from one packet to another, starting at a specified offset in a given source packet’s data and copying the data to a specified offset in the target packet. Returns the number of bytes actually copied, which can be less than the requested number, depending on the source packet and specified starting offset for the copy operation.
NdisDeregisterProtocol
Releases the resources allocated when the driver called NdisRegisterProtocol, given the protocol handle it returned at initialization.
NdisDprAllocatePacket
Allocates and initializes a packet descriptor when the caller is running at IRQL DISPATCH_LEVEL, given a handle returned by a preceding call to NdisAllocatePacketPool.
NdisDprAllocatePacketNonInterlocked
Allocates a packet descriptor when the caller is running at IRQL DISPATCH_LEVEL and that caller provides internal synchronization for accessing packet pool, given a handle returned by a preceding call to NdisAllocatePacketPool.
NdisDprFreePacket
Releases a driver-allocated packet descriptor and returns it to the free list when the caller is running at IRQL DISPATCH_LEVEL.
NdisDprFreePacketNonInterlocked
Releases a packet descriptor that was allocated with NdisDprAllocatePacketNonInterlocked and returns it to the free list.
NdisEqualString
Compares two strings to determine whether they are equal.
NdisFreeBuffer
Releases a buffer descriptor obtained in a preceding call to NdisAllocateBuffer or NdisCopyBuffer.
NdisFreeBufferPool
Releases a handle obtained in a preceding call to NdisAllocateBufferPool.
NdisFreeMemory
Releases a handle that was obtained in a preceding call to NdisAllocateMemory.
NdisFreePacket
Releases a packet descriptor that was allocated from packet pool with NdisAllocatePacket and returns it to the free list.
NdisFreePacketPool
Releases a handle to a block of packet pool that was allocated with NdisAllocatePacketPool.
NdisFreeSpinLock
Releases a spin lock initialized in a preceding call to NdisAllocateSpinLock, usually when a driver is unloading.
NdisFreeString
Frees the storage for a buffered string.
NdisGetBufferPhysicalArraySize
Returns the number of physical discontiguities in a buffer, given the buffer descriptor.
NdisGetCurrentProcessorCpuUsage
Returns how busy the current processor is as a percentage.
NdisGetCurrentSystemTime
Returns the current system time, suitable for use as a timestamp.
NdisGetFirstBufferFromPacket
Returns pointers to the buffer descriptor and base virtual address for the initial buffer chained to a given packet, along with the sizes of the initial buffer and full buffer, in case the buffer is fragmented.
NdisGetNextBuffer
Returns the next buffer descriptor in a chain, given the current buffer descriptor.
NdisGetPacketFlags
Returns the flags, if any, set by a higher-level protocol driver in a given packet.
NDIS_GET_PACKET_HEADER_SIZE
Returns the number of bytes in a received net packet header from the HeaderSize set in the NDIS_PACKET_OOB_DATA block associated with a given packet descriptor by the indicating miniport.
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO
Returns a pointer to buffered out-of-band information, such as packet priority, and the number of bytes in the buffer from the MediaSpecificInformation and SizeMediaSpecificInfo set in the NDIS_PACKET_OOB_DATA block associated with a given packet descriptor by the indicating miniport.
NDIS_GET_PACKET_TIME_RECEIVED
Returns the TimeReceived timestamp from the NDIS_PACKET_OOB_DATA block associated with an indicated receive packet.
NDIS_GET_PACKET_TIME_SENT
Returns the TimeSent timestamp from the associated NDIS_PACKET_OOB_DATA block associated with an indicated receive packet.
NdisIMDeInitializeDeviceInstance
Calls an NDIS intermediate driver's MiniportHalt function to tear down the driver's virtual NIC.
NdisIMInitializeDeviceInstance
Calls an NDIS intermediate driver’s MiniportInitialize function to set up the driver’s virtual NIC for I/O operations bound for an underlying NIC driver to which the intermediate driver is bound.
NdisIMQueueMiniportCallback
Calls an intermediate driver’s callback function to when it can execute in a MiniportXxx context, as, for example, to forward an indication to higher-level protocols.
NdisIMRegisterLayeredMiniport
Registers an NDIS intermediate driver’s MiniportXxx entry points and name with the NDIS library when the driver initializes.
NdisIMRevertBack
Returns the function of an intermediate driver to the context in which it was executing before it called NdisIMSwitchToMiniport.
NdisIMSwitchToMiniport
Allows an intermediate driver function to call NdisM Xxx normally called only from MiniportXxx functions, as, for example, to make indications up to higher-level protocols.
NdisInitAnsiString
Initializes a counted ANSI string.
NDIS_INIT_FUNCTION
Marks a given driver function as initialization-only code to be discarded after the function has run once.
NdisInitializeEvent
Sets up an event object during driver initialization to be used subsequently as a synchronization mechanism.
NdisInitializeListHead
Sets up a queue header for a driver’s internal queue, given a pointer to driver-supplied resident storage for the queue header and queue. Then, entries (usually packets) can be added to and removed from the doubly-linked queue with the NdisInterlocked..List functions.
NdisInitializeString
Allocates a buffer for and initializes a counted string in the system-default character set, given a source string and a NULL pointer.
NdisInitUnicodeString
Initializes a counted Unicode string.
NdisInterlockedAddUlong
Adds an unsigned long value to a given unsigned integer as an atomic operation, using a caller-supplied and initialized spin lock.
NdisInterlockedInsertHeadList
Inserts an entry, usually a packet, at the head of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among driver functions.
NdisInterlockedInsertTailList
Inserts an entry, usually a packet, at the end of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among driver functions.
NdisInterlockedRemoveHeadList
Removes an entry, usually a packet, from the head of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among driver functions.
NdisMoveMemory
Copies a specified number of bytes from one caller-supplied location to another.
NdisMSetAttributesEx
Informs the NDIS library about significant features of an intermediate driver’s virtual NIC and prevents NDIS from timing out sends and requests in the intermediate driver.
NDIS_OOB_DATA_FROM_PACKET
Returns a pointer to the NDIS_PACKET_OOB_DATA block associated with a given packet descriptor.
NdisOpenAdapter
Sets up a binding between the caller and an underlying NIC driver, given an array of medium types that the protocol driver supports, the number of elements in the supplied array, the handle already returned by NdisRegisterProtocol, a handle to a protocol-supplied context area to be passed back to the protocol in subsequent receive indications, the name of the NIC to be opened, a mask set with the caller’s preferred open options, and, possibly, a pointer to some caller-supplied addressing information, as well as four caller-specified locations at which NdisOpenAdapter returns values. If the call succeeds, NdisOpenAdapter returns a binding handle for the protocol to use in subsequent Ndis Xxx calls that forward device I/O requests to the NIC driver, an index specifying the NIC driver’s preferred medium type, and NDIS_STATUS_SUCCESS; otherwise, it returns both a general failure status and an informational error status.
NdisOpenProtocolConfiguration
Returns a handle for a driver’s HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
\NICDriverInstance\Parameters\ProtocolName registry key. This handle is a required parameter to other Ndis Xxx configuration functions.
NDIS_PAGABLE_FUNCTION
Marks a driver function as pageable code.
NdisPrintString
Displays a given string in the debugging window.
NdisQueryBuffer
Retrieves the size of the range, and optionally its base virtual address, specified in a given buffer descriptor.
NdisQueryBufferOffset
Retrieves the base virtual address of the range specified in a given buffer descriptor.
NdisQueryPacket
Returns any or all of the following information about a given packet descriptor: a count of buffer descriptors chained to the packet descriptor, the total number of bytes in all buffers mapped by the buffer descriptor chained to the packet descriptor, the total number of physically discontiguous blocks for all such buffers, and a pointer to the initial buffer descriptor in the chain.
NdisQueryReceiveInformation
Returns information from the out-of-band data block associated with a given packet descriptor set up by the underlying driver for a receive indication.
NdisReadConfiguration
Reads the value of a named entry of the specified type, given the registry handle for the ..\Parameters\ProtocolName key returned by NdisOpenProtocolConfiguration.
NdisRecalculatePacketCounts
Resets the valid counts of chained buffer descriptors for a given packet descriptor.
NdisRegisterProtocol
Registers a protocol driver’s upper-edge entry points (ProtocolXxx) and name with the NDIS library, given a pointer to the NDIS_PROTOCOL_CHARACTERISTICS structure set up by the caller and the size in bytes of this structure. NDIS returns the status of the registration attempt and, if the call is successful, a protocol handle representing the driver, which can be passed in a subsequent call to NdisOpenAdapter.
NdisReinitializePacket
Sets to NULL the queue head for any chained buffers in a given packet descriptor and reinitializes it for reuse.
NdisReleaseSpinLock
Releases a spin lock that was acquired in a preceding call to NdisAcquireSpinLock.
NdisRequest
Forwards a caller-initialized NDIS_REQUEST-type query or set request to the underlying NIC driver, given the binding handle previously returned by NdisOpenAdapter.
NdisReset
Forwards a reset request to the underlying NIC driver, given the binding handle and a pointer to a variable in which the status of the reset operation is returned.
NdisResetEvent
Clears the Signaled state of a given event.
NdisRetrieveUlong
Retrieves a ULONG value from the source address, avoiding alignment faults.
NdisReturnPackets
Releases ownership of one or more receive packets after a protocol driver has consumed the receive data.
NdisSend
Forwards a packet to the NIC driver for transmission over the wire, given the binding handle, a pointer to the packet to be sent, and a pointer to a variable in which the status of the send is returned.
NdisSendPackets
Forwards a multipacket send request, possibly specifying out-of-band information for each packet in the array, such as packet priority, to the underlying driver.
NdisSetEvent
Sets a given event to the Signaled state if it was not already Signaled, thereby causing waits on the event to be satisfied.
NdisSetPacketFlags
Sets the Flags member in a given packet descriptor to the given value, before the protocol calls NdisSendPackets or NdisSend.
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO
Sets the MediaSpecificInformation and SizeMediaSpecificInfo in the NDIS_PACKET_OOB_DATA block, specifying out-of-band information associated with a protocol-allocated packet descriptor for a send request.
NDIS_SET_PACKET_TIME_TO_SEND
Sets the TimeToSend timestamp in the NDIS_PACKET_OOB_DATA block associated with a protocol-allocated packet descriptor, specifying when the underlying driver should transmit the packet data over the wire.
NdisSetSendFlags
See NdisSetPacketFlags.
NdisStoreUlong
Stores a ULONG value at a particular address, avoiding alignment faults.
NdisTransferData
Forwards a request that the underlying NIC driver transfer (copy) received data to a supplied packet, given the binding handle, the receive-context handle passed to the protocol when the receive was indicated, the byte offset in the indication at which to start the transfer and the number of bytes to transfer, and pointers to variables in which the status of the completed transfer operation and the number of bytes actually transferred are returned.
NdisUnchainBufferAtBack
Removes a given buffer descriptor at the end of the chain of buffer descriptors for a given packet descriptor and returns a pointer to the buffer descriptor.
NdisUnchainBufferAtFront
Removes a given buffer descriptor at the start of the chain of buffer descriptors for a given packet descriptor and returns a pointer to the buffer descriptor.
NdisUnicodeStringToAnsiString
Converts a given counted Unicode string into a counted ANSI string.
NdisWaitEvent
Puts the caller into a wait state until the given event is set to the Signaled state or the wait times out.
NdisWriteConfiguration
Writes a caller-supplied value entry in the registry, given the handle for the ..\Parameters\ProtocolName key returned by NdisOpenProtocolConfiguration.
NdisWriteErrorLogEntry
Allocates an error-log record, fills it in, and writes it to the I/O error log, given a binding handle for the underlying NIC, an NDIS_XXX error code, a count of caller-supplied error values, and a corresponding number of ULONGs specifying additional information about the I/O error or configuration error.
NdisZeroMemory
Fills a block of memory with zeros, given a pointer to the block and the length in bytes to be filled.

Highest-level NDIS drivers that export only the lower-edge ProtocolXxx functions also can call support routines exported by other Windows NT kernel-mode components. For more information about these support routines, see the Kernel-Mode Driver Reference.


Chapter 3 NDIS Library Function References

Functions and macros described here are in alphabetical order. They include all macros and functions defined in the master header, ndis.h, or in a system-supplied filter-library header, _filter.h, that NDIS protocol, miniport, WAN miniport, and intermediate drivers call.

To ensure their portability across Windows NT platforms and binary compatibility across Microsoft operating systems that support Win32®, NDIS miniport drivers should call only the functions described here. Such a miniport might require recompilation for different Windows NT platforms and separate installation scripts to install its NIC on all Microsoft Win32 platforms but such a device driver is otherwise OS-independent.

On the other hand, NDIS intermediate drivers that have both NDIS-defined upper-edge (MiniportXxx) and lower-edge (ProtocolXxx) functions and highest-level NDIS transport protocol drivers usually call additional driver support routines provided by the operating system. For more information about Windows NT kernel-mode support routines that can be called by NDIS intermediate and highest-level protocol drivers, see the Kernel-Mode Driver Reference.

For a summary of the NDIS functions and macros used by protocol, by miniport, and/or by WAN miniport drivers, respectively, see Chapter 2.

For more information about the NDIS functions and macros used only by legacy full-NIC drivers, see Appendix A.


ETH_COPY_NETWORK_ADDRESS

VOID
ETH_COPY_NETWORK_ADDRESS(
OUT PCHAR D,
IN PCHAR S
 );

ETH_COPY_NETWORK_ADDRESS copies a given Ethernet address to a given location.

Parameters

D
Points to a caller-supplied varible to which this function copies the source address.
S
Points to the source address.

Comments

Callers of this macro can be running at any IRQL as long as the source and destination address pointers are resident.


FDDI_COMPARE_NETWORK_ADDRESSES

VOID
FDDI_COMPARE_NETWORK_ADDRESSES(
IN PUCHAR A,
IN PUCHAR B,
IN ULONG AddressLength,
OUT PINT Result
);

FDDI_COMPARE_NETWORK_ADDRESSES sets a caller-supplied variable to a value indicating whether a given FDDI address is greater than, less than, or equal to another given FDDI address.

Parameters

A
Points to the first network address.
B
Points to the second network address.
AddressLength
Specifies the length in bytes of the addresses (either 2 or 6).
Result
Points to a caller-supplied variable in which this macro returns the result of its comparison as one of the following values:
Result < zero
B is greater than A.
Result > zero
A is greater than B.
Zero
A equals B.

Comments

A caller can use the FDDI_LENGTH_OF_SHORT_ADDRESS and FDDI_LENGTH_OF_LONG_ADDRESS constants, corresponding to two and six bytes, respectively, as the value for AddressLength.

FDDI network addresses do not have a defined ordering relationship. Consequently, the results of this macro for unequal addresses depend on the argument ordering.

Callers of this macro can be running at any IRQL if all pointer parameters access resident memory.


FDDI_IS_BROADCAST

VOID
FDDI_IS_BROADCAST(
IN PUCHAR Address,
IN ULONG AddressLength,
OUT PBOOLEAN Result
);

FDDI_IS_BROADCAST sets a caller-supplied variable to a Boolean value indicating whether a given FDDI address is a broadcast address.

Parameters

Address
Points to a network address.
AddressLength
Specifies the size in bytes of the address (either 2 or 6).
Result
Points to a caller-supplied variable in which this macro returns TRUE if the given address is broadcast.

Comments

A caller can use the FDDI_LENGTH_OF_SHORT_ADDRESS and FDDI_LENGTH_OF_LONG_ADDRESS constants, corresponding to two and six bytes, respectively, as the value for AddressLength.

A broadcast address applies to all stations in the extended network.

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

FDDI_IS_MULTICAST


FDDI_IS_MULTICAST

VOID
FDDI_IS_MULTICAST(
IN PUCHAR Address,
IN ULONG AddressLength,
OUT PBOOLEAN Result
);

FDDI_IS_MULTICAST sets a caller-supplied variable to a Boolean value indicating whether a given FDDI address is a multicast address.

Parameters

Address
Points to a network address.
AddressLength
Specifies the size in bytes of the address (either 2 or 6).
Result
Points to a caller-supplied variable in which this macro returns TRUE if the given address is multicast.

Comments

A caller can use the FDDI_LENGTH_OF_SHORT_ADDRESS and FDDI_LENGTH_OF_LONG_ADDRESS constants, corresponding to two and six bytes, respectively, as the value for AddressLength.

A multicast address applies to zero or more stations in a network and can be bridged to the extended network.

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

FDDI_IS_BROADCAST


FDDI_IS_SMT

VOID
FDDI_IS_SMT(
IN UCHAR FcByte,
OUT PBOOLEAN Result
);

FDDI_IS_SMT sets a caller-supplied variable to a Boolean value indicating whether a given FDDI frame is SMT.

Parameters

FcByte
Specifies the FC code retrieved from the packet.
Result
Points to a caller-supplied variable in which this macro returns TRUE if the given FC byte indicates an SMT frame.

Comments

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

FDDI_IS_BROADCAST, FDDI_IS_MULTICAST


NdisAcquireSpinLock

VOID
NdisAcquireSpinLock(
IN PNDIS_SPIN_LOCK SpinLock
);

NdisAcquireSpinLock acquires a spin lock so the caller gains exclusive access to the resources, shared among driver functions, that the spin lock protects.

Parameters

SpinLock
Points to an opaque spin lock, already initialized by the caller.

Comments

The driver must initialize a variable of type NDIS_SPIN_LOCK with NdisAllocateSpinLock before it calls any other Ndis..SpinLock function. The driver must provide resident storage for the spin lock(s) it uses.

After acquiring a spin lock with NdisAcquireSpinLock, the caller must release that lock with a call to NdisReleaseSpinLock. A driver must call NdisReleaseSpinLock following each call to NdisAcquireSpinLock. Otherwise, a deadlock occurs, hanging the driver.

A spin lock acquired with NdisAcquireSpinLock must be released with NdisReleaseSpinLock. A spin lock acquired with NdisDprAcquireSpinLock must be released with NdisDprReleaseSpinLock.

A driver should never hold a spin lock for an extended period (more than a few instructions). Holding a spin lock for longer than 25 microseconds degrades both system and driver performance.

A miniport driver cannot use a spin lock to protect resources that its other functions share with the MiniportISR and/or MiniportDisableInterrupt functions. Instead, a miniport must call NdisMSynchronizeWithInterrupt so that its MiniportSynchronizeISR function accesses such shared resources at the same DIRQL at which its MiniportISR and/or MiniportDisableInterrupt functions do.

Callers of NdisAcquireSpinLock run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportDisableInterrupt, MiniportHalt, MiniportInitialize, MiniportISR, MiniportSynchronizeISR, NdisAllocateSpinLock, NdisDprAcquireSpinLock, NdisMSynchronizeWithInterrupt, NdisReleaseSpinLock


NdisAdjustBufferLength

VOID
NdisAdjustBufferLength(
IN PNDIS_BUFFER Buffer,
IN UINT Length
);

NdisAdjustBufferLength modifies the length specified in a buffer descriptor allocated or copied by the caller.

Parameters

Buffer
Points to the buffer descriptor, previously allocated with NdisAllocateBuffer or NdisCopyBuffer.
Length
Specifies the new length in bytes to be set in the buffer descriptor.

Comments

The caller of NdisAdjustBufferLength can pass only a buffer descriptor that was allocated by the caller. It cannot pass a buffer descriptor that was allocated by another driver.

For example, the driver of a busmaster DMA NIC calls this function with buffer descriptors that it allocated to specify receive buffers in its shared memory block. Before such a driver indicates received data, it calls NdisAdjustBufferLength to make the buffer descriptor that it will include in the receive indication match the size of the received data if it is less than the size of the NIC’s receive buffer. When a receive packet with such an adjusted buffer descriptor has been consumed and returned to the miniport, the driver again calls NdisAdjustBufferLength to make the buffer descriptor match the actual size of its receive buffer again.

The Length passed to NdisAdjustBufferLength cannot be larger than the Length that was passed to NdisAllocateBuffer or NdisCopyBuffer when the buffer descriptor was allocated.

The caller of NdisAdjustBufferLength must restore the length to its original value before it frees the buffer descriptor with NdisFreeBuffer.

The driver must release any spin lock it is currently holding before calling NdisAdjustBufferLength.

Callers of NdisAdjustBufferLength can run at any IRQL, but usually run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBuffer, NdisCopyBuffer, NdisFreeBuffer, NdisReleaseSpinLock


NdisAllocateBuffer

VOID
NdisAllocateBuffer(
OUT PNDIS_STATUS Status,
OUT PNDIS_BUFFER *Buffer,
IN NDIS_HANDLE PoolHandle,
IN PVOID VirtualAddress,
IN UINT Length
);

NdisAllocateBuffer creates a buffer descriptor mapping a specified virtual (sub)range within an already allocated, nonpaged memory block.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the buffer allocation, which is NDIS_STATUS_SUCCESS if it returned a valid buffer descriptor pointer at Buffer and otherwise NDIS_STATUS_FAILURE.
Buffer
Points to a caller-supplied variable in which this function returns a pointer to the allocated buffer descriptor.
PoolHandle
Specifies the handle returned by a preceding call to NdisAllocateBufferPool.
VirtualAddress
Points to the base virtual address of previously allocated system-space memory to be mapped in the returned buffer descriptor.
Length
Specifies the number of bytes to be mapped.

Comments

NdisAllocateBuffer allocates storage for and initializes a buffer descriptor to map a specified range of previously allocated memory, such as memory used for a packet header or a NIC receive buffer.

Buffer descriptor allocation draws on the buffer pool that NdisAllocateBufferPool creates. A driver can call NdisAllocateBuffer as many times as necessary to allocate the buffer descriptors that it requires if it called NdisAllocateBufferPool during initialization.

The driver must release any spin lock it is currently holding before calling NdisAllocateBuffer.

All lower-level NDIS drivers must allocate all buffer descriptors that they chain to packets from buffer pool. Only highest-level protocol drivers can be given OS-dependent descriptors mapping virtual memory ranges, and, if these memory descriptors are equivalent to NDIS buffer descriptors, a highest-level protocol can pass such descriptors as parameters to Ndis Xxx functions.

Callers of NdisAllocateBuffer run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBufferPool, NdisAllocateMemory, NdisFreeBuffer


NdisAllocateBufferPool

VOID
NdisAllocateBufferPool(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE PoolHandle,
IN UINT NumberOfDescriptors
);

NdisAllocateBufferPool returns a handle with which the caller can allocate buffer descriptors by calling NdisAllocateBuffer.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the buffer pool allocation, which is always NDIS_STATUS_SUCCESS.
PoolHandle
Points to a caller-supplied variable in which this function returns a handle to the buffer pool. This handle is a required parameter to the Ndis..Buffer functions that the driver calls subsequently.
NumberOfDescriptors
Specifies the number of buffer descriptors the caller expects to allocate from the buffer pool without releasing any allocated buffer descriptors back to the pool.

Comments

After NdisAllocateBufferPool returns, the driver calls NdisAllocateBuffer one or more times to allocate the buffer descriptors that it requires. A driver should call NdisAllocateBufferPool during initialization.

The NumberOfDescriptors specified by a NIC driver usually depends on the features of the NIC. For example, the driver of a busmaster DMA NIC with ring buffers would specify at least a large enough NumberOfDescriptors to map the full ring.

The NumberOfDescriptors specified in the call to NdisAllocateBufferPool is the effective limit on how many times the driver can call NdisAllocateBuffer before it must call NdisFreeBuffer to return a buffer descriptor to the free list for the buffer pool.

When a driver no longer needs the buffer pool it allocated, it calls NdisFreeBufferPool to release the buffer pool handle.

The driver must release any spin lock it is holding before calling NdisAllocateBufferPool.

All lower-level NDIS drivers must allocate all buffer descriptors that they chain to packets from buffer pool. Only highest-level protocol drivers can be given OS-dependent descriptors mapping virtual memory ranges, and, if these memory descriptors are equivalent to NDIS buffer descriptors, a highest-level protocol can pass such descriptors as parameters to Ndis Xxx functions.

Callers of NdisAllocateBufferPool run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAdjustBufferLength, NdisAllocateBuffer, NdisChainBufferAtBack, NdisChainBufferAtFront, NdisCopyBuffer, NdisCreateLookaheadBufferFromSharedMemory, NdisFreeBuffer, NdisFreeBufferPool, NdisQueryBuffer, NdisReleaseSpinLock


NdisAllocateMemory

NDIS_STATUS
NdisAllocateMemory(
OUT PVOID *VirtualAddress,
IN UINT Length,
IN UINT MemoryFlags,
IN NDIS_PHYSICAL_ADDRESS HighestAcceptableAddress
);

NdisAllocateMemory allocates resident (nonpaged) system-space memory, optionally within a specified address limit, as a physically contiguous allocation, and/or as a noncached allocation.

Parameters

VirtualAddress
Points to a caller-supplied variable in which this function returns the base virtual address of the allocated memory or NULL if memory of the specified type is unavailable.
Length
Specifies the size in bytes to be allocated.
MemoryFlags
Specifies zero or a bitmask designating the type(s) of memory to allocate, as follows:

Value

Meaning

zero

Allocate nonpaged system-space memory.

NDIS_MEMORY_CONTIGUOUS

Allocate physically contiguous memory.

NDIS_MEMORY_NONCACHED

Allocate noncached memory.

Only NIC drivers specify the allocation of contiguous and/or noncached memory during driver initialization. These two flags can be ORed into MemoryFlags before NdisAllocateMemory is called.

HighestAcceptableAddress
Specifies -1.

Return Value

NdisAllocateMemory can return either of the following:

NDIS_STATUS_SUCCESS
The caller can use the allocated memory range starting at the value returned at VirtualAddress.
NDIS_STATUS_FAILURE
An attempt to allocate the requested memory failed. This return does not necessarily mean that a subsequent call will fail.

Comments

Any NDIS driver might call NdisAllocateMemory with the MemoryFlags set to zero. For example, NDIS drivers that export a set of NDIS upper-edge (MiniportXxx) functions can call NdisAllocateMemory to allocate a context area in which the driver will maintain per-NIC (or per-virtual-NIC) runtime state information and pass the pointer returned at VirtualAddress to NdisMSetAttributes(Ex). NDIS drivers that export a set of NDIS lower-edge (ProtocolXxx) functions also can call NdisAllocateMemory whenever such a driver needs to allocate buffer space.

Only NIC miniports can set the MemoryFlags with either or both of the NDIS_MEMORY_XXX when they make initialization-time calls to NdisAllocateMemory. During initialization, miniport drivers allocate these types of memory to be shared with their NICs.

If such a miniport specifies physically contiguous memory in a call to NdisAllocateMemory, the virtual memory allocation maps to a single physically contiguous region. If a miniport specifies noncached memory, the allocated virtual range is not cached. A NIC driver can access noncached memory without flushing cache buffers during DMA operations.

Whatever the value of the input MemoryFlags, a successful caller of NdisAllocateMemory uses a range of virtual addresses to access the allocated memory. Depending on the size of the allocation and on the type of memory requested, one or more physical memory ranges (pages) back this single virtual range.

While the NDIS_MEMORY_XXX can be ORed in MemoryFlags to request a contiguous and noncached allocation by a NIC driver, both contiguous and noncached memory are very limited system resources. A NIC driver should never request more contiguous memory and/or more noncached memory than it needs. Attempts to allocate either of these types of memory except during driver initialization are a programming error. Such an attempt is unlikely to be successful because system-space memory becomes fragmented due to allocations by other kernel-mode components, including other drivers, and due to runtime paging.

If such an initialization-time call fails, a NIC driver can attempt to allocate one or more smaller blocks of contiguous and/or noncached memory, rather than failing to initialize if it cannot allocate a large block.

Callers of NdisAllocateMemory can run at IRQL <= DISPATCH_LEVEL to allocate memory from nonpaged pool. NIC drivers that allocate contiguous and/or noncached memory must be running at IRQL PASSIVE_LEVEL during initialization.

See Also

MiniportInitialize, NdisFreeMemory, NdisMAllocateSharedMemory, NdisMSetAttributes, NdisMSetAttributesEx


NdisAllocatePacket

VOID
NdisAllocatePacket(
OUT PNDIS_STATUS Status,
OUT PNDIS_PACKET *Packet,
IN NDIS_HANDLE PoolHandle
);

NdisAllocatePacket allocates and initializes a packet descriptor.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the request, which can be one of the following:
NDIS_STATUS_SUCCESS
The caller can use the packet descriptor returned at Packet.
NDIS_STATUS_RESOURCES
The free list for the packet pool currently has no available entries. A subsequent call to NdisFreePacket will return the given entry to the free list.
Packet
Points to a caller-supplied variable in which this function returns a pointer to the allocated packet descriptor.
PoolHandle
Specifies the handle returned by a preceding call to NdisAllocatePacketPool.

Comments

NDIS drivers must allocate packet descriptors from packet pool. Protocol drivers call NdisAllocatePacket one or more times for their subsequent calls to NdisSend or NdisSendPackets. Miniports call NdisAllocatePacket one or more times for subsequent calls to NdisMIndicateReceivePacket. NDIS intermediate drivers must call NdisAllocatePacket to repackage sends and receive indications in fresh packet descriptors before passing incoming transfer requests on to other NDIS drivers.

Each packet descriptor will eventually have one or more buffer descriptors chained to it. NdisAllocatePacket zero-initializes the packet descriptor and sets its buffer chain pointer to NULL. The caller must chain the buffer descriptor(s) mapping the packet data to the packet descriptor before it is sent or indicated. Most NDIS drivers allocate buffer descriptors with NdisAllocateBuffer.

Drivers must use the NDIS_GET/SET_PACKET_XXX macros or NDIS_OOB_DATA_FROM_PACKET to access or set up any out-of-band information associated with the packet descriptor.

Callers of NdisAllocatePacket run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBuffer, NdisAllocatePacketPool, NdisDprAllocatePacket, NdisDprFreePacket, NdisFreePacket, NDIS_GET_PACKET_HEADER_SIZE, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_STATUS, NDIS_GET_PACKET_TIME_RECEIVED, NDIS_GET_PACKET_TIME_SENT, NDIS_GET_PACKET_TIME_TO_SEND, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMTrIndicateReceive, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_HEADER_SIZE, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_STATUS, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_SENT, NDIS_SET_PACKET_TIME_TO_SEND


NdisAllocatePacketPool

VOID
NdisAllocatePacketPool(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE PoolHandle,
IN UINT NumberOfDescriptors,
IN UINT ProtocolReservedLength
);

NdisAllocatePacketPool allocates and initializes a block of storage for a pool of packet descriptors.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the packet pool allocation, which can be one of the following:
NDIS_STATUS_SUCCESS
The driver can make calls to NdisAllocatePacket when it needs packet descriptors to make indications up to higher-level drivers or to send packets down to lower-level drivers.
NDIS_STATUS_RESOURCES
The attempt to allocate packet pool failed, possibly due to a shortage of memory. This return does not necessarily mean that a subsequent call will fail. However, the caller might attempt to decrease the NumberOfDescriptors and call again immediately.
PoolHandle
Points to a caller-supplied variable in which this function returns a handle to the packet pool. This handle is a required parameter to the Ndis..Packet functions that the driver calls subsequently.
NumberOfDescriptors
Specifies the number of packet descriptors the pool should contain.
ProtocolReservedLength
Specifies the number of bytes to be allocated for the ProtocolReserved array of each packet descriptor. For packet descriptors to be used in receive indications, this parameter can be at most 16 (bytes).

Comments

A successful call to NdisAllocatePacketPool returns a handle to the packet pool, which the caller should save. Usually, the driver next calls NdisAllocatePacket one or more times with this handle to allocate a set of packet descriptors. The NumberOfDescriptors specified in the call to NdisAllocatePacketPool is the effective limit on how many times the driver can call NdisAllocatePacket before it must call NdisFreePacket to return a packet descriptor to the free list for the packet pool.

As packets sent or indicated are returned to the allocating driver, it can reuse each packet descriptor after calling NdisReinitializePacket and setting it up again with a chain of buffer descriptors, or the driver can call NdisFreePacket to return that packet to the free list. Reinitializing and reusing packets allocated from packet pool is far faster than freeing and, then, having to reallocate them again. Before a driver calls NdisReinitializePacket with a packet descriptor, it must save the pointers to buffer descriptors that are chained to the packet descriptor since NdisReinitializePacket sets the head of the buffer chain to NULL.

When the driver no longer needs the packet pool, it calls NdisFreePacketPool to release the packet pool storage.

The driver must release any spin lock it is holding before calling NdisAllocatePacketPool.

Callers of NdisAllocatePacketPool run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocatePacket, NdisDprAllocatePacket, NdisDprAllocatePacketNonInterlocked, NdisFreePacketPool, NdisMIndicateReceivePacket, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisReinitializePacket, NdisSendPackets, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, ProtocolSendComplete


NdisAllocateSpinLock

VOID
NdisAllocateSpinLock(
IN PNDIS_SPIN_LOCK SpinLock
);

NdisAllocateSpinLock initializes a variable of type NDIS_SPIN_LOCK, used to synchronize access to resources shared among nonISR driver functions.

Parameters

SpinLock
Points to an opaque variable that represents a spin lock.

Comments

Before a driver calls NdisAcquireSpinLock, NdisDprAcquireSpinLock, or any of the NdisInterlocked Xxx functions, it must call NdisAllocateSpinLock to initialize the spin lock passed as a required parameter to these Ndis Xxx functions. The caller must provide nonpaged storage for the variable at SpinLock.

After calling NdisAllocateSpinLock, the driver can call NdisAcquireSpinLock to obtain exclusive use of the resource(s) the spin lock protects. When resource access is complete, the driver calls NdisReleaseSpinLock so that other driver functions can access the resource(s) protected by that spin lock.

As a general rule, a driver should not pass the same spin lock to every Ndis..SpinLock and NdisInterlocked Xxx it calls subsequently. Thus, any driver might initialize more than one spin lock with NdisAllocateSpinLock.

Each spin lock that a driver allocates protects a discrete set of shared resources from simultanous access by driver functions that run at IRQL <= DISPATCH_LEVEL. For example, a driver that maintains an internal queue of packets might initialize one spin lock to protect its queue and another to protect a set of state variables that several driver functions, not including the MiniportISR or MiniportDisableInterrupt function, access while the driver is processing packets.

A driver should never use two spin locks to protect the same (sub)set of resources because nested spin lock acquisitions so frequently cause deadlocks. Even if a driver could be designed to prevent deadlocks, nested spin lock acquisitions have an adverse effect on driver performance and I/O throughput.

A miniport driver cannot use a spin lock to protect resources that its nonISR functions share with its MiniportISR or MiniportDisableInterrupt function. To access resources shared with a MiniportISR or MiniportDisableInterrupt function, a miniport must call NdisMSynchronizeWithInterrupt to have its MiniportSynchronizeISR function access those resources at DIRQL.

When a driver no longer requires resource protection, for example, when a NIC is being removed and the driver is releasing the resources it allocated for that NIC, the driver calls NdisFreeSpinLock.

Freeing a spin lock and releasing a spin lock are potentially confusing. NdisFreeSpinLock clears the memory at SpinLock so it no longer represents a spin lock. Releasing an acquired spin lock with NdisReleaseSpinLock simply allows another thread of execution to acquire that spin lock.

Callers of NdisAllocateSpinLock can run at any IRQL. Usually a caller is running at IRQL PASSIVE_LEVEL during initialization.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportDisableInterrupt, MiniportHalt, MiniportInitialize, MiniportISR, MiniportTimer, NdisAcquireSpinLock, NdisDprAcquireSpinLock, NdisDprReleaseSpinLock, NdisFreeSpinLock, NdisInterlockedAddUlong, NdisInterlockedInsertHeadList, NdisInterlockedInsertTailList, NdisInterlockedRemoveHeadList, NdisMSynchronizeWithInterrupt, NdisReleaseSpinLock


NdisAnsiStringToUnicodeString

NDIS_STATUS
NdisAnsiStringToUnicodeString(
IN OUT PNDIS_STRING DestinationString,
IN PNDIS_ANSI_STRING SourceString
);

NdisAnsiStringToUnicodeString converts a given counted ANSI string into a counted Unicode string. The translation conforms to the current system locale information.

Parameters

DestinationString
Points to a caller-allocated buffer in which this function should return the converted Unicode string.
SourceString
Points to the ANSI string to be converted.

Return Value

NdisAnsiStringToUnicodeString returns NDIS_STATUS_SUCCESS if the conversion succeeds. Otherwise, the contents of the buffer at DestinationString are unaffected.

Comments

The caller must allocate storage for both the source and destination strings and release these buffers as soon as the strings are no longer needed. The buffer at DestinationString must be at least twice the size of that at SourceString.

Callers of NdisAnsiStringToUnicodeString run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisFreeString, NdisInitAnsiString, NdisInitializeString, NdisInitUnicodeString, NdisUnicodeStringToAnsiString, ProtocolBindAdapter


NDIS_BUFFER_LINKAGE

PVOID
NDIS_BUFFER_LINKAGE(
IN PNDIS_BUFFER Buffer
);

NDIS_BUFFER_LINKAGE returns a pointer to a linked buffer, given a pointer to a buffer descriptor.

Parameters

Buffer
Points to a buffer descriptor. This buffer descriptor must not be chained to a packet descriptor.

Return Value

NDIS_BUFFER_LINKAGE returns a pointer to the structure member containing the linkage, or NULL if it fails.

Comments

This macro allows a driver to queue allocated buffer descriptors without having to supply its own buffer descriptor linkage.

Callers of this macro can be running at any IRQL.

See Also

NdisAllocateBuffer


NDIS_BUFFER_TO_SPAN_PAGES

ULONG
NDIS_BUFFER_TO_SPAN_PAGES(
IN PNDIS_BUFFER Buffer
);

NDIS_BUFFER_TO_SPAN_PAGES determines how many physical pages of memory are being used to back a given buffer.

Parameters

Buffer
Specifies the buffer descriptor mapping the buffer.

Return Value

NDIS_BUFFER_TO_SPAN_PAGES returns the number of pages backing the virtual range for the buffer.

Comments

This macro can be used by DMA drivers to determine how many physical discontiguities exist in a given virtual buffer.

Callers of NDIS_BUFFER_TO_SPAN_PAGES can be running at any IRQL.

See Also

NdisMSetupDmaTransfer, NdisMStartBufferPhysicalMapping


NdisChainBufferAtBack

VOID
NdisChainBufferAtBack(
IN OUT PNDIS_PACKET Packet,
IN OUT PNDIS_BUFFER Buffer
);

NdisChainBufferAtBack links a given buffer descriptor to the tail of the buffer-descriptor chain attached to a packet descriptor.

Parameters

Packet
Points to the packet descriptor.
Buffer
Points to the caller-supplied buffer descriptor to be added to the chain.

Comments

NdisChainBufferAtBack either links the given buffer descriptor to the end of an existing chain or, if the given packet has no associated buffer descriptors already, at the head of the chain.

It also resets the valid counts for the packet to FALSE, thereby forcing NdisQueryPacket to recalculate information about the given packet if it is called subsequently with that packet.

Callers of NdisChainBufferAtBack can run at any IRQL but usually run at IRQL <= DISPATCH_LEVEL.

See Also

NdisChainBufferAtFront, NdisQueryPacket, NdisUnchainBufferAtBack


NdisChainBufferAtFront

VOID
NdisChainBufferAtFront(
IN OUT PNDIS_PACKET Packet,
IN OUT PNDIS_BUFFER Buffer
);

NdisChainBufferAtFront links a given buffer descriptor to the head of the buffer-descriptor chain attached to a packet descriptor.

Parameters

Packet
Points to the packet descriptor.
Buffer
Points to the caller-supplied buffer descriptor to be added to the chain.

Comments

NdisChainBufferAtFront links the given buffer descriptor at the head of the chain for the given packet.

It also resets the valid counts for the packet to FALSE, thereby forcing NdisQueryPacket to recalculate information about the given packet if it is called subsequently with that packet.

Callers of NdisChainBufferAtFront can run at any IRQL but usually run at IRQL <= DISPATCH_LEVEL.

See Also

NdisChainBufferAtBack, NdisQueryPacket, NdisUnchainBufferAtFront


NdisCloseAdapter

VOID
NdisCloseAdapter(
OUT PNDIS_STATUS Status,
 IN NDIS_HANDLE NdisBindingHandle
 );

NdisCloseAdapter releases the binding established and the resources allocated when the protocol called NdisOpenAdapter.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the close operation, which can be one of the following values:
NDIS_STATUS_SUCCESS
The binding was closed and all resources pertaining to that binding have been released.
NDIS_STATUS_PENDING
A close-adapter request is being handled asynchronously, and the caller’s ProtocolCloseAdapterComplete function will be called when the close operation is done.
NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the virtual adapter or NIC to be closed.

Comments

As soon as a protocol calls NdisCloseAdapter, the handle at NdisBindingHandle should be considered invalid by the caller. It is a programming error to pass this handle in any subsequent call to an Ndis Xxx function.

Callers of NdisCloseAdapter run at IRQL PASSIVE_LEVEL.

See Also

NdisCompleteUnbindAdapter, NdisOpenAdapter, ProtocolCloseAdapterComplete, ProtocolUnbindAdapter


NdisCloseConfiguration

VOID
NdisCloseConfiguration(
IN NDIS_HANDLE ConfigurationHandle
);

NdisCloseConfiguration releases the handle to the registry Parameters key returned by NdisOpenConfiguration or NdisOpenProtocolConfiguration.

Parameters

ConfigurationHandle
Specifies the handle returned by NdisOpenConfiguration or NdisOpenProtocolConfiguration.

Comments

This function frees any temporary storage allocated in calls to other Ndis Xxx functions that required the returned ConfigurationHandle as a parameter.

Callers of NdisCloseConfiguration run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisOpenConfiguration, NdisOpenProtocolConfiguration, NdisReadBindingInformation, NdisReadConfiguration, NdisReadNetworkAddress, NdisWriteConfiguration, ProtocolBindAdapter


NdisCloseFile

VOID
NdisCloseFile(
IN NDIS_HANDLE FileHandle
);

NdisCloseFile releases a handle returned by NdisOpenFile and frees the memory allocated to hold the file contents when it was opened.

Parameters

FileHandle
Specifies the handle returned in a preceding call to NdisOpenFile.

Comments

For miniports, calls to this function are valid only during initialization. If the MiniportInitialize function calls NdisOpenFile, it must call NdisCloseFile before it returns control.

Callers of NdisCloseFile run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMapFile, NdisOpenFile, NdisUnmapFile


NdisCompleteBindAdapter

VOID
NdisCompleteBindAdapter(
IN NDIS_HANDLE BindAdapterContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenStatus
);

NdisCompleteBindAdapter completes a binding operation for which the caller’s ProtocolBindAdapter function previously returned NDIS_STATUS_PENDING.

Parameters

BindAdapterContext
Specifies the BindContext handle passed in to ProtocolBindAdapter.
Status
Specifies the final status of the completed bind operation.
OpenStatus
Specifies the status returned by the preceding call to NdisOpenAdapter for this binding attempt.

Comments

When a protocol returns NDIS_STATUS_PENDING from its ProtocolBindAdapter function, that driver must eventually call NdisCompleteBindAdapter when the binding operation is completed.

If the binding operation was successful, the protocol is ready to accept receive indications from underlying drivers and to send transmit, query, and set requests down to the underlying driver(s). If NdisCompleteBindAdapter is called with an input error Status, the binding attempt failed and the protocol has released any resources it allocated to establish the binding.

In either case, the protocol calls NdisCompleteBindAdapter to notify the NDIS library of the completion of the binding operation that this driver initiated when its ProtocolBindAdapter function called NdisOpenAdapter.

Callers of NdisCompleteBindAdapter run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisIMInitializeDeviceInstance, NdisOpenAdapter, NdisRequest, ProtocolBindAdapter, ProtocolUnbindAdapter


NdisCompleteUnbindAdapter

VOID
NdisCompleteUnbindAdapter(
IN NDIS_HANDLE UnbindAdapterContext,
IN NDIS_STATUS Status
);

NdisCompleteUnbindAdapter completes an unbinding operation for which the caller’s ProtocolUnbindAdapter function previously returned NDIS_STATUS_PENDING.

Parameters

UnbindAdapterContext
Specifies the ProtocolBindingContext handle passed in to ProtocolUnbindAdapter.
Status
Specifies the Status of the completed unbind operation, always NDIS_STATUS_SUCCESS.

Comments

When a protocol returns NDIS_STATUS_PENDING from its ProtocolUnbindAdapter function, that driver must eventually call NdisCompleteUnbindAdapter when the unbinding operation is completed.

When NdisCompleteUnbindAdapter is called, the protocol has finished cleaning up any state the driver maintains about the binding and released any resources it allocated to establish the binding. This call notifies the NDIS library of the completion of the unbinding operation that this driver initiated when it called NdisOpenAdapter from ProtocolBindAdapter.

On return from NdisCompleteUnbindAdapter the UnbindAdapterContext handle should be considered invalid. That is, the protocol should not pass this handle in calls to any Ndis Xxx function.

Callers of NdisCompleteUnbindAdapter run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, NdisCloseAdapter, NdisIMDeInitializeDeviceInstance, ProtocolBindAdapter, ProtocolUnbindAdapter


NdisCopyBuffer

VOID
NdisCopyBuffer(
OUT PNDIS_STATUS Status,
OUT PNDIS_BUFFER *Buffer,
IN NDIS_HANDLE PoolHandle,
IN PVOID MemoryDescriptor,
IN UINT Offset,
IN UINT Length
);

NdisCopyBuffer creates a buffer descriptor for a specified virtual (sub)range, given a handle for a block of already allocated memory and a pointer to the memory descriptor for the block.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the buffer descriptor copy operation, which can be one of the following:
NDIS_STATUS_SUCCESS
The driver can use the buffer descriptor or buffer descriptor chain returned at Buffer.
NDIS_STATUS_FAILURE
An attempt to map the given range failed, possibly because of an out-of-range Offset and Length specification.
Buffer
Points to a caller-supplied variable in which this function returns a pointer to the new buffer descriptor if the call is successful.
PoolHandle
Specifies the handle returned by a preceding call to NdisAllocateBufferPool.
MemoryDescriptor
Points to the memory descriptor, which can be of type NDIS_BUFFER.
Offset
Specifies the byte offset within the memory block at which to start the copy.
Length
Specifies the number of bytes to copy.

Comments

NdisCopyBuffer creates a buffer descriptor mapping a (sub)range within a block of memory, described by a given memory descriptor. This function allocates storage for the new buffer descriptor and then copies information from the given MemoryDescriptor into the new buffer descriptor.

If the given memory descriptor contains chained elements, the buffer descriptor that NdisCopyBuffer creates can be the head of a chain of buffer descriptors. A driver can call NdisGetNextBuffer and then NdisQueryBuffer to query each chained buffer descriptor.

The driver must release any spin lock it is holding before calling NdisCopyBuffer. The driver must call NdisFreeBuffer to release the buffer descriptor when it is no longer needed.

Callers of NdisCopyBuffer run at IRQL PASSIVE_LEVEL.

See Also

NdisAllocateBufferPool, NdisFreeBuffer, NdisGetNextBuffer, NdisQueryBuffer, NdisReleaseSpinLock


NdisCopyFromPacketToPacket

VOID
NdisCopyFromPacketToPacket(
IN PNDIS_PACKET Destination,
IN UINT DestinationOffset,
IN UINT BytesToCopy,
IN PNDIS_PACKET Source,
IN UINT SourceOffset,
OUT PUINT BytesCopied
);

NdisCopyFromPacketToPacket copies a specified range of data from one packet to another.

Parameters

Destination
Points to the destination packet descriptor.
DestinationOffset
Specifies the byte offset within the destination packet at which to begin writing the copied data.
BytesToCopy
Specifies the number of bytes to copy.
Source
Points to the source packet descriptor.
SourceOffset
Specifies the byte offset within the source packet at which to begin copying the data.
BytesCopied
Points to the caller-supplied variable in which this function returns the number of bytes actually copied. This number can be less than the value of BytesToCopy if the source packet runs out of data or the destination packet runs out of space.

Comments

The caller of NdisCopyFromPacketToPacket allocates the destination packet, if not the source packet as well. The packet descriptor of the destination packet should have enough chained buffer descriptors to receive the data.

If the source packet runs out of data or the destination packet out of space before the specified number of bytes has been copied, the copy operation stops. In either case, NdisCopyFromPacketToPacket returns the number of bytes successfully copied from the source to the destination packet.

NdisCopyFromPacketToPacket does not copy any out-of-band information associated with the given Source to the Destination. To copy this information, the driver can use pointers returned by NDIS_OOB_DATA_FROM_PACKET and then call NdisMoveMemory.

The driver must release any spin lock it is holding before calling NdisCopyFromPacketToPacket.

Callers of NdisCopyFromPacketToPacket run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBuffer, NdisAllocatePacket, NdisMoveMemory, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA


NdisCreateLookaheadBufferFromSharedMemory

VOID
NdisCreateLookaheadBufferFromSharedMemory(
IN PVOID pSharedMemory,
IN UINT LookaheadLength,
OUT PVOID *pLookaheadBuffer
);

NdisCreateLookaheadBufferFromSharedMemory returns the virtual address of a buffer within a block of memory shared with a busmaster DMA NIC.

Parameters

pSharedMemory
Specifies the base address of the shared memory range containing the data for the lookahead buffer.
LookaheadLength
Specifies the length in bytes for the lookahead buffer.
pLookaheadBuffer
Points to a caller-supplied variable in which this function returns the virtual address of the lookahead buffer.

Comments

NdisCreateLookaheadBufferFromSharedMemory provides a lookahead buffer that is a virtual memory copy of shared memory for miniport receive indications. To bound protocol drivers, such a lookahead buffer represents a read-only version of the shared memory.

This function accommodates system architectures that do not allow the NIC driver to read shared memory on a network interface card directly.

Callers of NdisCreateLookaheadBufferFromSharedMemory run at any IRQL.

See Also

MiniportHandleInterrupt, MiniportTimer, NdisDestroyLookaheadBufferFromSharedMemory, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMTrIndicateReceive, NdisMWanIndicateReceive, ProtocolReceive


NdisDeregisterProtocol

VOID
NdisDeregisterProtocol(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE NdisProtocolHandle
);

NdisDeregisterProtocol releases the resources allocated when the driver called NdisRegisterProtocol.

Parameters

Status
Points to a caller-supplied variable that is set to NDIS_STATUS_SUCCESS on return from this function.
NdisProtocolHandle
Specifies the handle returned by NdisRegisterProtocol when the driver initialized.

Comments

Registered protocol drivers call NdisDeregisterProtocol in any of the following circumstances:

  • The driver already called NdisRegisterProtocol successfully but it cannot bind to any underlying NDIS driver in the machine.

  • The driver is being unloaded. That is, the Unload routine set up in its driver object during initialization has been called or the ProtocolUnbindAdapter function has been called to release the driver’s last remaining binding.

    For more information about the Unload routine and other standard Windows NT kernel-mode driver routines, see the Kernel-Mode Driver Design Guide.

  • The system is being shut down. Because the driver, which is a highest-level protocol, called IoRegisterShutdownNotification when it initialized, its Shutdown routine has been called.

    For more information about IoRegisterShutdownNotification, see the Kernel-Mode Driver Reference.

Before calling NdisDeregisterProtocol, the driver should call NdisCloseAdapter with each binding handle that it obtained from NdisOpenAdapter but has not yet closed.

NdisDeregisterProtocol calls the driver’s ProtocolStatus function with NDIS_STATUS_CLOSING to allow its caller to release any resources the protocol driver has allocated for open bindings. If the protocol driver has any outstanding bindings, the NDIS library closes them and NdisDeregisterProtocol forwards close-adapter requests to any underlying driver(s) that registered a close-adapter handler. If such an underlying driver returns NDIS_STATUS_PENDING for a close-adapter request, the caller’s ProtocolCloseAdapterComplete function is called before NdisDeregisterProtocol returns control.

When outstanding opens, if any, have been closed, NdisDeregisterProtocol releases all filters its caller has set up and frees the memory NDIS allocated to track bindings and filters for the protocol driver.

Callers of NdisDeregisterProtocol run at IRQL <= DISPATCH_LEVEL.

See Also

NdisCloseAdapter, NdisIMDeInitializeDeviceInstance, NdisOpenAdapter, NdisRegisterProtocol, ProtocolCloseAdapterComplete, ProtocolStatus, ProtocolUnbindAdapter


NdisDestroyLookaheadBufferFromSharedMemory

VOID
NdisDestroyLookaheadBufferFromSharedMemory(
IN PVOID pLookaheadBuffer
);

NdisDestroyLookaheadBufferFromSharedMemory releases the buffer mapping obtained in a preceding call to NdisCreateLookaheadBufferFromSharedMemory.

Parameters

pLookaheadBuffer
Specifies the lookahead buffer pointer returned in a preceding call to NdisCreateLookaheadBufferFromSharedMemory.

Comments

Callers of NdisDestroyLookaheadBufferFromSharedMemory run at any IRQL.

See Also

NdisCreateLookaheadBufferFromSharedMemory


NdisDprAcquireSpinLock

VOID
NdisDprAcquireSpinLock(
IN PNDIS_SPIN_LOCK SpinLock
);

NdisDprAcquireSpinLock acquires a spin lock so the caller can synchronize access to resources shared among nonISR driver functions in a multiprocessor-safe way.

Parameters

SpinLock
Points to an opaque spin lock, already initialized by the caller.

Comments

The NIC driver must initialize a variable of type NDIS_SPIN_LOCK with NdisAllocateSpinLock before it calls any other Ndis..SpinLock function. The driver must provide resident storage for the spin lock(s) it uses.

NdisDprAcquireSpinLock is an optimized version of NdisAcquireSpinLock that a miniport can call only while running at IRQL DISPATCH_LEVEL.

After acquiring a spin lock with NdisDprAcquireSpinLock, the caller must release that lock with a call to NdisDprReleaseSpinLock. A NIC driver must call NdisDprReleaseSpinLock following each call to NdisDprAcquireSpinLock. Otherwise, a deadlock occurs, hanging the driver.

A spin lock acquired with NdisDprAcquireSpinLock must be released with NdisDprReleaseSpinLock. A spin lock acquired with NdisAcquireSpinLock must be released with NdisReleaseSpinLock.

A driver should never hold a spin lock for an extended period (more than a few instructions). Holding a spin lock for longer than 25 microseconds degrades both system and driver performance.

A miniport driver cannot use a spin lock to protect resources that its other functions share with the MiniportISR and/or MiniportDisableInterrupt functions. Instead, a miniport must call NdisMSynchronizeWithInterrupt so that its MiniportSynchronizeISR function accesses such shared resources at the same DIRQL at which its MiniportISR and/or MiniportDisableInterrupt functions do.

Callers of NdisDprAcquireSpinLock must be running at IRQL DISPATCH_LEVEL.

See Also

MiniportDisableInterrupt, MiniportISR, MiniportSynchronizeISR, MiniportTimer, NdisAcquireSpinLock, NdisAllocateSpinLock, NdisDprReleaseSpinLock, NdisMSynchronizeWithInterrupt


NdisDprAllocatePacket

VOID
NdisDprAllocatePacket(
OUT PNDIS_STATUS Status,
OUT PNDIS_PACKET *Packet,
IN NDIS_HANDLE PoolHandle
);

NdisDprAllocatePacket allocates and initializes a packet descriptor when the caller is running at IRQL DISPATCH_LEVEL.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the request, which can be one of the following:
NDIS_STATUS_SUCCESS
The caller can use the packet descriptor returned at Packet.
NDIS_STATUS_RESOURCES
The free list for the packet pool currently has no available entries. A subsequent call to NdisDprFreePacket or NdisFreePacket will return the given entry to the free list. The variable at Packet is set to NULL.
Packet
Points to a caller-supplied variable in which this function returns a pointer to the allocated packet descriptor.
PoolHandle
Specifies the handle returned by a preceding call to NdisAllocatePacketPool.

Comments

NdisDprAllocatePacket runs slightly faster than NdisAllocatePacket because NDIS can acquire the spin lock it uses to synchronize access to the packet pool without having to raise IRQL.

A caller of NdisDprAllocatePacket must be running at IRQL DISPATCH_LEVEL. Otherwise, the driver should call NdisAllocatePacket.

See Also

NdisAllocatePacket, NdisAllocatePacketPool, NdisDprAllocatePacketNonInterlocked, NdisDprFreePacket, NdisFreePacket, NdisReleaseSpinLock


NdisDprAllocatePacketNonInterlocked

VOID
NdisDprAllocatePacketNonInterlocked(
OUT PNDIS_STATUS Status,
OUT PNDIS_PACKET *Packet,
IN NDIS_HANDLE PoolHandle
);

NdisDprAllocatePacketNonInterlocked allocates and initializes a packet descriptor when the caller is running at IRQL DISPATCH_LEVEL and that caller provides internal synchronization for accessing packet pool.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the request, which can be one of the following:
NDIS_STATUS_SUCCESS
The caller can use the packet descriptor returned at Packet.
NDIS_STATUS_RESOURCES
The free list for the packet pool currently has no available entries. A subsequent call to NdisDprFreePacketNonInterlocked will return the given entry to the free list. The variable at Packet is set to NULL.
Packet
Points to a caller-supplied variable in which this function returns a pointer to the allocated packet descriptor.
PoolHandle
Specifies the handle returned by a preceding call to NdisAllocatePacketPool.

Comments

A caller of NdisDprAllocatePacketNonInterlocked and its reciprocal NdisDprFreePacketNonInterlocked is responsible for synchronizing all accesses to the packet pool that driver allocated with NdisAllocatePacketPool. Otherwise, the driver should let NDIS manage this synchronization on its behalf by calling NdisAllocatePacket or NdisDprAllocatePacket.

NdisDprAllocatePacketNonInterlocked runs slightly faster than NdisAllocatePacket because NDIS need not acquire a spin lock, which NDIS typically uses to synchronize access to packet pools that drivers allocate.

Instead, a caller of NdisDprAllocatePacketNonInterlocked and NdisDprFreePacketNonInterlocked manages the synchronization of its accesses to packet pool internally.

Usually, such a driver protects its packet pool with a driver-allocated spin lock. Before calling NdisDprAllocatePacketNonInterlocked, the driver calls NdisAcquireSpinLock, which raises IRQL to DISPATCH_LEVEL. Only when the driver resumes execution holding its spin lock does it call NdisDprAllocatePacketNonInterlocked. Such a driver releases its spin lock with NdisReleaseSpinLock when NdisDprAllocatePacketNonInterlocked returns control.

In a similar manner, such a driver calls NdisAcquireSpinLock before it releases a packet descriptor back to pool with NdisDprFreePacketNonInterlocked and, then, it calls NdisReleaseSpinLock.

Any packet descriptor allocated with NdisDprAllocatePacketNonInterlocked must be freed with NdisDprFreePacketNonInterlocked except, possibly, when a driver is releasing its allocated resources just before being unloaded. Otherwise, it is a runtime synchronization error that can cause race conditions to release any packet descriptor with NdisDprFreePacket or NdisFreePacket if that packet descriptor was allocated with NdisDprAllocatePacketNonInterlocked.

The caller of NdisDprAllocatePacketNonInterlocked must be running at IRQL DISPATCH_LEVEL.

See Also

NdisAcquireSpinLock, NdisAllocatePacket, NdisAllocatePacketPool, NdisAllocateSpinLock, NdisDprAllocatePacket, NdisDprFreePacket, NdisDprFreePacketNonInterlocked, NdisFreePacket, NdisReleaseSpinLock


NdisDprFreePacket

VOID
NdisDprFreePacket(
IN PNDIS_PACKET Packet
);

NdisDprFreePacket releases a driver-allocated packet descriptor and returns it to the free list when the caller is running at IRQL DISPATCH_LEVEL.

Parameters

Packet
Points to the packet descriptor returned by NdisDprAllocatePacket or NdisAllocatePacket.

Comments

Before calling NdisDprFreePacket, the driver either must call NdisFreeBuffer as many times as necessary to release all buffer descriptors chained to the packet, or it must call an NdisUnchainBufferAt Xxx function as many times as necessary to save all pointers to buffer descriptors. Otherwise, memory associated with buffer descriptors in the chain remains unusable by the driver and by NDIS until the system is rebooted.

Callers of NdisDprFreePacket must be running at IRQL DISPATCH_LEVEL. Otherwise, drivers should call NdisFreePacket.

See Also

NdisAllocatePacket, NdisAllocatePacketPool, NdisDprAllocatePacket, NdisFreeBuffer, NdisFreePacket, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront


NdisDprFreePacketNonInterlocked

VOID
NdisDprFreePacketNonInterlocked(
IN PNDIS_PACKET Packet
);

NdisDprFreePacketNonInterlocked releases a driver-allocated packet descriptor and returns it to the free list when the caller is running at IRQL DISPATCH_LEVEL and that caller provides internal synchronization for accessing packet pool.

Parameters

Packet
Points to a packet descriptor returned by NdisDprAllocatePacketNonInterlocked.

Comments

Before calling NdisDprFreePacketNonInterlocked, the driver either must call NdisFreeBuffer as many times as necessary to release all buffer descriptors chained to the packet, or it must call an NdisUnchainBufferAt Xxx function as many times as necessary to save all pointers to buffer descriptors. Otherwise, memory associated with buffer descriptors in the chain remains unusable by the driver

A caller of NdisDprFreePacketNonInterlocked and its reciprocal NdisDprAllocatePacketNonInterlocked is responsible for synchronizing all accesses to the packet pool that driver allocated with NdisAllocatePacketPool. Otherwise, the driver should let NDIS manage this synchronization on its behalf by calling NdisAllocate/FreePacket or NdisDprAllocate/FreePacket.

Usually, such a driver protects its packet pool with a driver-allocated spin lock. Before calling NdisDprFreePacketNonInterlocked, the driver calls NdisAcquireSpinLock, which raises IRQL to DISPATCH_LEVEL. Only when the driver resumes execution holding its spin lock does it call NdisDprFreePacketNonInterlocked. Such a driver releases its spin lock with NdisReleaseSpinLock when NdisDprFreePacketNonInterlocked returns control.

Callers of NdisDprFreePacketNonInterlocked must be running at IRQL DISPATCH_LEVEL.

See Also

NdisAcquireSpinLock, NdisAllocatePacketPool, NdisDprAllocatePacketNonInterlocked, NdisDprFreePacket, NdisFreeBuffer, NdisFreePacket, NdisReleaseSpinLock, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront


NdisDprReleaseSpinLock

VOID
NdisDprReleaseSpinLock(
IN PNDIS_SPIN_LOCK SpinLock
);

NdisDprReleaseSpinLock releases a spin lock acquired in the immediately preceding call to NdisDprAcquireSpinLock.

Parameters

SpinLock
Points to the acquired spin lock to be released.

Comments

Release of the spin lock allows another driver function to use the resources the lock protects after that function acquires the spin lock.

A spin lock acquired with NdisDprAcquireSpinLock must be released with NdisDprReleaseSpinLock. A spin lock acquired with NdisAcquireSpinLock must be released with NdisReleaseSpinLock.

Callers of NdisDprReleaseSpinLock are running at IRQL DISPATCH_LEVEL.

See Also

NdisDprAcquireSpinLock


NdisEqualString

BOOLEAN
NdisEqualString(
IN PNDIS_STRING String1,
IN PNDIS_STRING String2,
IN BOOLEAN CaseInsensitive
);

NdisEqualString compares two strings, in the OS-default character set, to determine whether they are equal.

Parameters

String1
Points to the first string.
String2
Points to the second string.
CaseInsensitive
Specifies TRUE if case should be ignored when doing the comparison.

Return Value

NdisEqualString returns TRUE if the two strings are equal.

Comments

For Windows NT drivers, a string of type NDIS_STRING is a counted, zero-terminated Unicode string.

Callers of NdisEqualString run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisInitializeString, ProtocolBindAdapter


NdisFlushBuffer

VOID
NdisFlushBuffer(
IN PNDIS_BUFFER Buffer,
IN BOOLEAN WriteToDevice
);

NdisFlushBuffer flushes the memory region described by a given buffer descriptor from all processor caches.

Parameters

Buffer
Points to a buffer description mapping a buffer from which or into which data is transferred as a busmaster DMA operation.
WriteToDevice
Specifies TRUE if the NIC driver calls this function to flush a buffer before making a transfer from host memory to the NIC.

If the NIC driver specifies FALSE, it must ensure the buffer begins and ends on a cache-line boundary.

Comments

Drivers of busmaster DMA NICs call NdisFlushBuffer to maintain data integrity in shared memory. Flushing the buffer ensures coherency between the host memory cache and processor physical memory before a DMA transfer to or from the network interface card.

The driver flushes such a buffer when data that it shares with its NIC might be cached. For any transfer between host memory and a busmaster DMA NIC, the miniport must flush the buffer before the transfer starts. Such a driver also must ensure that host memory is not accessed until the transfer completes.

Callers of NdisFlushBuffer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, NdisMAllocateSharedMemory, NdisUpdateSharedMemory


NdisFreeBuffer

VOID
NdisFreeBuffer(
IN PNDIS_BUFFER Buffer
);

NdisFreeBuffer releases a buffer descriptor obtained in a preceding call to NdisAllocateBuffer or NdisCopyBuffer.

Parameters

Buffer
Points to a buffer descriptor.

Comments

NdisFreeBuffer returns the given buffer descriptor to the free list.

A driver must not call NdisFreePacket with a packet to which buffers are chained, because doing this causes a memory leak. Before it releases such a packet, either the driver should call NdisFreeBuffer as many times as necessary to release all buffer descriptors chained to the packet, or it should call either NdisUnchainBufferAt Xxx function as many times as necessary to save the pointers to all chained buffer descriptors.

The driver must release any spin lock it is holding before calling NdisFreeBuffer.

Callers of NdisFreeBuffer run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBuffer, NdisCopyBuffer, NdisFreePacket, NdisReleaseSpinLock, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront


NdisFreeBufferPool

VOID
NdisFreeBufferPool(
IN NDIS_HANDLE PoolHandle
);

NdisFreeBufferPool releases a handle obtained in a preceding call to NdisAllocateBufferPool.

Parameters

PoolHandle
Specifies the handle returned when the driver called NdisAllocateBufferPool. The pool handle is no longer valid after this function returns.

Comments

Before calling NdisFreeBufferPool, the driver must call NdisFreeBuffer as many times as necessary to release all buffer descriptors that were allocated with NdisAllocateBuffer but not yet released. Otherwise, the call to NdisFreeBufferPool causes a memory leak.

The driver must release any spin lock it is holding before calling NdisFreeBufferPool.

Callers of NdisFreeBufferPool run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBuffer, NdisAllocateBufferPool, NdisFreeBuffer, NdisReleaseSpinLock


NdisFreeMemory

VOID
NdisFreeMemory(
IN PVOID VirtualAddress,
IN UINT Length,
IN UINT MemoryFlags
);

NdisFreeMemory releases a block of memory previously allocated with NdisAllocateMemory.

Parameters

VirtualAddress
Points to the base virtual address of the allocated memory. This address was returned by NdisAllocateMemory.
Length
Specifies the size in bytes of the memory block to be released. This parameter must be identical to the Length that was passed to NdisAllocateMemory.
MemoryFlags
Specifies zero or the bitmask originally passed to NdisAllocateMemory for the preceding range, designating the following:

Value

Meaning

zero

Nonpaged system-space memory

NDIS_MEMORY_CONTIGUOUS

Physically contiguous memory

NDIS_MEMORY_NONCACHED

Noncached memory

This value must be identical to the MemoryFlags that was passed to NdisAllocateMemory.

Comments

The parameters passed to NdisFreeMemory must be identical to those passed to NdisAllocateMemory when the block of memory was allocated. That is, a caller of NdisFreeMemory cannot release a subrange of the block that was allocated.

Because noncached memory and contiguous memory are seldom released until the allocating NIC driver is unloading, callers of NdisFreeMemory usually are running at IRQL PASSIVE_LEVEL for these types of deallocations.

Callers of NdisFreeMemory can run at IRQL <= DISPATCH_LEVEL when releasing memory that was allocated from nonpaged system space or that was allocated as noncached memory. Callers must be running at IRQL PASSIVE_LEVEL to release contiguous allocations.

See Also

MiniportHalt, NdisAllocateMemory


NdisFreePacket

VOID
NdisFreePacket(
IN PNDIS_PACKET Packet
);

NdisFreePacket releases a packet that was allocated from packet pool with NdisAllocatePacket and returns it to the free list.

Parameters

Packet
Points to the packet descriptor returned by NdisAllocatePacket or NdisDprAllocatePacket.

Comments

Before calling NdisFreePacket, the driver either must call NdisFreeBuffer as many times as necessary to release all buffer descriptors chained to the packet, or it must call an NdisUnchainBufferAt Xxx function as many times as necessary to save all pointers to buffer descriptors. Otherwise, memory associated with buffer descriptors in the chain remains unusable by the driver and by NDIS until the system is rebooted.

The driver must release any spin lock it is holding before calling NdisFreePacket.

Callers of NdisFreePacket run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocatePacket, NdisDprAllocatePacket, NdisFreeBuffer, NdisReleaseSpinLock, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront


NdisFreePacketPool

VOID
NdisFreePacketPool(
IN NDIS_HANDLE PoolHandle
);

NdisFreePacketPool releases a handle to a block of packet pool that was allocated with NdisAllocatePacketPool.

Parameters

PoolHandle
Specifies the handle returned when the driver called NdisAllocatePacketPool. The pool handle is no longer valid after this function returns.

Comments

NdisFreePacketPool frees the storage for the packet pool. Before calling this function, the driver must call NdisFreePacket as many times as necessary to release all packet descriptors that it has allocated with NdisAllocatePacket but not yet freed.

The driver also must release any spin lock it is holding before calling NdisFreePacketPool.

Callers of NdisFreePacketPool run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocatePacket, NdisAllocatePacketPool, NdisFreePacket, NdisReleaseSpinLock


NdisFreeSpinLock

VOID
NdisFreeSpinLock(
IN PNDIS_SPIN_LOCK SpinLock
);

NdisFreeSpinLock releases a spin lock initialized in a preceding call to NdisAllocateSpinLock.

Parameters

SpinLock
Points to the spin lock to be deinitialized.

Comments

If the caller of NdisFreeSpinLock needs to use the spin lock again, it must call NdisAllocateSpinLock before passing that spin lock pointer to any of the Ndis..SpinLock or NdisInterlocked Xxx functions.

Callers of NdisFreeSpinLock run at IRQL PASSIVE_LEVEL. Usually, this function is not called until a driver is unloading.

See Also

MiniportHalt, NdisAcquireSpinLock, NdisAllocateSpinLock, NdisDprAcquireSpinLock, NdisDprReleaseSpinLock, NdisInterlockedAddUlong, NdisInterlockedInsertHeadList, NdisInterlockedInsertTailList, NdisInterlockedRemoveHeadList, NdisReleaseSpinLock


NdisFreeString

VOID
NdisFreeString(
IN NDIS_STRING String
);

NdisFreeString releases storage that was allocated for a buffered string.

Parameters

String
Points to the buffer containing the string.

Comments

Drivers usually call NdisInitializeString, NdisIntiAnsiString, and/or NdisInitUnicodeString when they are initializing, for example, to set up names passed to the Ndis..Configuration functions. For each call it makes to an Ndis..String function, the driver should make a corresponding call to NdisFreeString before its initialization function returns control.

NDIS drivers also can call NdisFreeString to release buffers they allocate for calls to NdisAnsiStringToUnicodeString and NdisUnicodeStringToAnsiString.

Callers of NdisFreeString run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisAnsiStringToUnicodeString, NdisFreeMemory, NdisInitAnsiString, NdisInitializeString, NdisInitUnicodeString, NdisOpenAdapter, NdisRegisterProtocol, NdisUnicodeStringToAnsiString, ProtocolBindAdapter


NdisGetBufferPhysicalArraySize

VOID
NdisGetBufferPhysicalArraySize(
IN PNDIS_BUFFER Buffer,
OUT PUINT ArraySize
);

NdisGetBufferPhysicalArraySize returns the number of discontiguous physical blocks backing a buffer, given the buffer descriptor.

Parameters

Buffer
Points to the buffer descriptor.
ArraySize
Points to a caller-supplied variable in which this function returns the number of discrete physical ranges backing the buffer.

Comments

The driver of a busmaster DMA NIC might call NdisGetBufferPhysicalArraySize to determine how many NDIS_PHYSICAL_ADDRESS structures to allocate and fill in before calling NdisStartBufferPhysicalMapping.

A protocol driver might call NdisGetBufferPhysicalArraySize to build a scatter/gather list for an underlying NIC driver. However, a protocol driver with such NIC-specific functionality is unlikely to be bindable to any other NIC driver.

The maximum number of physical blocks in the buffer can be one more than the number this function writes, depending on the arrangement of the buffer in paged memory. NdisGetBufferPhysicalArraySize returns the number of pages required to back a buffer of the given length, so if the buffer descriptor actually maps a buffer that begins at an offset within a page, the value returned by this call is one less than the number of page breaks.

A driver must release any spin lock it is holding before calling NdisGetBufferPhysicalArraySize.

Callers of NdisGetBufferPhysicalArraySize run at IRQL <= DISPATCH_LEVEL.

See Also

NDIS_PHYSICAL_ADDRESS, NdisStartBufferPhysicalMapping


NdisGetCacheFillSize

ULONG
NdisGetCacheFillSize(
);

NdisGetCacheFillSize returns the microprocessor’s data cache-line boundary in bytes.

Return Value

If NdisGetCacheFillSize succeeds, it returns the host cache fill size.

Comments

Drivers of DMA NICs can use the information returned by this function to avoid cache-line tearing during DMA transfers.

For example, a NIC driver might use the value returned by NdisGetCacheFillSize in any (or all) of the following ways:

  • Allocate cache-aligned buffers for DMA operations.

  • Transfer data from aligned buffers that are sized as an integral of the value returned by this function.

  • Subtract one from the returned value and use the result as a mask to determine whether a “physical” address in shared memory is aligned on a cache-line boundary.

Callers of NdisGetCacheFillSize run at IRQL PASSIVE_LEVEL. Usually, miniport drivers call this function during initialization and save the value it returns.

See Also

MiniportInitialize, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync


NdisGetCurrentProcessorCpuUsage

VOID
NdisGetCurrentProcessorCpuUsage(
PULONG pCpuUsage
);

NdisGetCurrentProcessorCpuUsage returns how busy the current processor is as a percentage.

Parameters

pCpuUsage
Points to a caller-supplied variable in which this function returns the percentage of CPU, on which the caller is running, that is currently in use.

Comments

NDIS drivers can call NdisGetCurrentProcessorCpuUsage to determine how loaded the CPU on which they are running is so they can change how they handle certain operations to improve driver performance.

For example, a NIC driver might call this function periodically and, as its percentage of CPU usage trends higher, disable interrupts on the NIC and switch to polling the state of the NIC. This strategy yields higher performance during periods of high network traffic by eliminating interrupt-processing overhead while the NIC is generating many interrupts. Instead, the driver’s polling MiniportTimer function makes indications and completes sends until network traffic declines again. When its periodic calls to NdisGetCurrentProcessorCpuUsage indicate CPU usage is trending lower again, such a driver would re-enable interrupts on the NIC and cancel its polling MiniportTimer function.

Callers of NdisGetCurrentProcessorCpuUsage can run at any IRQL.

See Also

MiniportDisableInterrupt, MiniportHandleInterrupt, MiniportISR, MiniportTimer, NdisMCancelTimer, NdisMSetPeriodicTimer


NdisGetCurrentSystemTime

VOID
NdisGetCurrentSystemTime(
IN PLONGLONG pSystemTime
);

NdisGetCurrentSystemTime returns the current system time, suitable for setting timestamps.

Parameters

pSystemTime
Points to a caller-supplied variable in which this function returns a count of 100-nanosecond intervals since January 1, 1601.

Comments

An NDIS driver can call NdisGetCurrentSystemTime to set timestamps in the out-of-band blocks associated with packets it indicates or sends.

An NDIS driver also might call NdisGetCurrentSystemTime if it maintains a count of how many packets it receives within any particular interval to tune its performance dynamically. For example, a NIC driver could call NdisGetCurrentSystemTime for each receive interrupt to determine periods of particularly high network traffic, when the driver might disable one or more types of interrupts on the NIC and enable a polling MiniportTimer function to process receives and/or sends.

Callers of NdisGetCurrentSystemTime can be running at any IRQL.

See Also

MiniportHandleInterrupt, MiniportTimer, NdisGetCurrentProcessorCpuUsage, NdisMSetPeriodicTimer, NDIS_PACKET_OOB_DATA, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_SENT, NDIS_SET_PACKET_TIME_TO_SEND


NdisGetFirstBufferFromPacket

VOID
NdisGetFirstBufferFromPacket(
IN PNDIS_PACKET _Packet,
OUT PNDIS_BUFFER *_FirstBuffer,
OUT PVOID *_FirstBufferVA,
OUT PUINT _FirstBufferLength,
 OUT PUINT _TotalBufferLength
);

NdisGetFirstBufferFromPacket returns pointers to the buffer descriptor and base virtual address for the initial buffer chained to a given packet descriptor, along with the sizes of the initial buffer and total buffer in case the buffer is fragmented.

Parameters

Packet
Points to the packet descriptor from which this macro extracts information about the initial buffer.
FirstBuffer
Points to a caller-supplied variable in which this macro returns the address of the initial buffer descriptor chained to the given packet descriptor.
FirstBufferVA
Points to a caller-supplied variable in which this macro returns the base virtual address of the initial buffer associated with the packet.
FirstBufferLength
Points to a caller-supplied variable in which this macro returns the number of bytes mapped by the initial buffer descriptor chained to the packet descriptor.
TotalBufferLength
Points to a caller-supplied variable in which this macro returns the total number of bytes mapped by all buffer descriptors chained to the packet descriptor.

Comments

Drivers use this macro to get information about the initial buffer and amount of data associated with a given packet faster than they can by making calls to NdisQueryPacket and, then, NdisQueryBuffer.

Callers of NdisGetFirstBufferFromPacket run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportSend, MiniportSendPackets, NdisGetNextBuffer, NDIS_PACKET, NdisQueryBuffer, NdisQueryPacket, ProtocolReceivePacket


NdisGetNextBuffer

VOID
NdisGetNextBuffer(
IN PNDIS_BUFFER CurrentBuffer,
OUT PNDIS_BUFFER *NextBuffer
);

NdisGetNextBuffer returns the next buffer descriptor in a chain, given a pointer to the current buffer descriptor.

Parameters

CurrentBuffer
Points to the current buffer descriptor.
NextBuffer
Points to a caller-supplied variable in which this function returns a pointer to the buffer descriptor, if any, that follows CurrentBuffer in the buffer descriptor chain.

Comments

NdisGetNextBuffer returns NULL at NextBuffer if CurrentBuffer points to the last buffer descriptor in the chain.

A driver usually calls this function after it calls NdisQueryPacket or NdisGetFirstBufferFromPacket.

A driver also can call NdisQueryBuffer to get details about a buffer descriptor that NdisGetNextBuffer finds.

NdisGetNextBuffer does not perform any synchronization. Therefore, a caller must handle any synchronization issues internally if more than one driver function can access buffer descriptors chained to a packet simultaneously.

Callers of NdisGetNextBuffer can run at any IRQL.

See Also

NdisGetFirstBufferFromPacket, NdisQueryBuffer, NdisQueryPacket


NdisGetPacketFlags

UINT
NdisGetPacketFlags(
IN PNDIS_PACKET Packet
);

NdisGetPacketFlags returns the flags, if any, set by a protocol driver in a given packet descriptor.

Parameters

Packet
Points to the packet.

Return Value

NdisGetPacketFlags returns the value of the flags in the given packet descriptor. This function returns zero if the packet has no explictly set flags information.

Comments

NdisGetPacketFlags returns information set in the flags packet descriptor that a protocol driver passes down to the underlying NDIS driver, usually with NdisSend or NdisSendPackets. These flags can contain protocol-determined information about the send that is not included in the packet data nor in the out-of-band block associated with the packet descriptor.

Protocol drivers can set the flags in packets to be sent on any medium that transmits data in an expedited manner. The MiniportSendPackets function of an underlying NIC driver calls NdisGetPacketFlags to retrieve this information. For underlying NIC drivers that do not support multipacket sends, calls to NdisGetPacketFlags are unnecessary because the packet flags value is passed as an input parameter to the MiniportSend function.

Callers of NdisGetPacketFlags can run at any IRQL.

See Also

MiniportSend, MiniportSendPackets, MiniportWanSend, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisSetPacketFlags


NDIS_GET_PACKET_HEADER_SIZE

UINT
NDIS_GET_PACKET_HEADER_SIZE(
IN PNDIS_PACKET Packet
);

NDIS_GET_PACKET_HEADER_SIZE returns the HeaderSize value from the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.

Return Value

NDIS_GET_PACKET_HEADER_SIZE returns the value set in the HeaderSize member of the NDIS_PACKET_OOB_DATA block associated with the given packet descriptor.

Comments

The HeaderSize specifies the number of bytes of medium-specific header mapped by the initial buffer descriptor chained to the packet descriptor. A NIC driver sets this value with NDIS_SET_PACKET_HEADER_SIZE in the out-of-band data block for the receive packets it will indicate with NdisMIndicateReceivePacket. Typically, a NIC miniport sets the HeaderSize once in each packet descriptor it allocates with NdisAllocatePacket for subsequent receive indications, and the value it sets depends on the medium it selected during initialization.

An NDIS intermediate driver that translates indicated packets to another medium for still higher level protocols would use NDIS_SET_PACKET_HEADER to set the header size to a value appropriate to its own medium as it translates incoming receives.

Callers of NDIS_GET_PACKET_HEADER_SIZE run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisAllocatePacket, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_HEADER_SIZE


NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO

VOID
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(
IN PNDIS_PACKET Packet,
IN PPVOID pMediaSpecificInfo,
IN PUINT pSizeMediaSpecificInfo
);

NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO returns the MediaSpecificInformation pointer and the SizeMediaSpecificInfo value from the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.
pMediaSpecificInfo
Points to a caller supplied variable that this macro sets to the MediaSpecificInformation pointer. The returned address gives the caller access to any buffered out-of-band information, such as packet priority, associated with the specified net packet indicated or to be transmitted.
pSizeMediaSpecificInfo
Points to a caller-supplied variable that this macro sets to the SizeMediaSpecificInfo value, which specifies the size in bytes of the buffer at MediaSpecificInformation.

Comments

NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO returns a NULL pointer at pMediaSpecificInfo and zero at pSizeMediaSpecificInfo if there is no media-specific information in the NDIS_PACKET_OOB_DATA block associated with the given packet descriptor.

Depending on the medium, a NIC miniport might set these members with NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO in the out-of-band data blocks associated with the packet descriptors it allocates for receive indications.

Depending on the medium of the underlying driver to which it is bound, a protocol might set MediaSpecificInformation and SizeMediaSpecificInfo in the out-of-band blocks associated with the packet descriptors it allocates for sends.

In either case, while a higher-level protocol is consuming a receive indication or a lower-level miniport is processing a send, that driver must have exclusive access to the buffer at MediaSpecificInformation. This restriction implies the following:

  • A miniport cannot access the buffer it set up with media-specific information when NdisMIndicateReceivePacket returns control if NDIS_STATUS_PENDING is set in the Status member of the associated out-of-band block. The driver can use NDIS_GET_PACKET_STATUS to determine whether each packet in the packet array it just indicated has this status set or has been returned with NDIS_STATUS_SUCCESS.

  • A protocol driver cannot access the media-specific information buffer it set up for a send, nor anything else in the out-of-band data block associated with a packet descriptor it passes to NdisSendPackets or NdisSend, until the packet descriptor is returned to its ProtocolSendComplete function.

    Because NDIS queues send packets and resubmits them if the MiniportSend function returns NDIS_STATUS_RESOURCES or if the MiniportSendPackets function sets NDIS_STATUS_RESOURCES for the Status of a packet in a submitted array, the final status of each packet in a send (array) is volatile until that packet has been returned to ProtocolSendComplete.

A protocol driver can call NdisQueryReceiveInformation to obtain this information from the NDIS_PACKET_OOB_DATA block associated with a packet descriptor when its ProtocolReceive function is given an indication. However, this call is superfluous if the protocol is bound to an underlying driver that does not supply out-of-band information with its indications.

Callers of NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportSend, MiniportSendPackets, MiniportTimer, NdisAllocatePacket, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryReceiveInformation, NdisSend, NdisSendPackets, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, ProtocolReceive, ProtocolReceivePacket, ProtocolSendComplete


NDIS_GET_PACKET_STATUS

NDIS_STATUS
NDIS_GET_PACKET_STATUS(
IN PNDIS_PACKET Packet
);

NDIS_GET_PACKET_STATUS returns the Status value from the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.

Return Value

NDIS_GET_PACKET_STATUS returns the Status value currently set in the NDIS_PACKET_OOB_DATA block associated with the given packet descriptor, usually one of the following:

NDIS_STATUS_SUCCESS
If this is set on return from a miniport’s call to NdisMIndicateReceivePacket, the miniport regains ownership of the following:
  • The packet descriptor

  • The associated out-of-band data block and of any media-specific information buffer specified in this block

  • All buffers mapped by buffer descriptors chained to the packet the miniport just indicated

The driver can prepare these descriptors, the out-of-band block, and media-specific information buffer, if any, for reuse in subsequent receive indications, and, if it is a NIC driver, can receive new data in the NIC’s receive buffers mapped by the returned buffer descriptors.

NDIS_STATUS_PENDING
If this is set on return from NdisMIndicateReceivePacket, protocols retain ownership of the following:
  • The packet descriptor

  • The associated out-of-band data block and of any media-specific information buffer specified in this block

  • All buffers mapped by buffer descriptors chained to the packet the miniport just indicated

Until the packet descriptor is returned to the MiniportReturnPacket function of the indicating driver, it should not access any of the buffers specified with the packet descriptor.

Comments

Only drivers that indicate receives up to higher-level drivers use this macro.

Protocol drivers should never test the Status value in an associated out-of-band data block on return from NdisSendPackets or NdisSend. NDIS or the underlying driver retains ownership of the packet descriptor, of the associated out-of-band block and any buffers specified in this block, and of all buffers mapped by buffer descriptors chained to the packet descriptor until the ProtocolSendComplete function is called with the packet descriptor.

Callers of NDIS_GET_PACKET_STATUS run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportReturnPacket, MiniportTimer, NdisAllocatePacket, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_STATUS, ProtocolSendComplete


NDIS_GET_PACKET_TIME_RECEIVED

ULONGLONG
NDIS_GET_PACKET_TIME_RECEIVED(
IN PNDIS_PACKET Packet
);

NDIS_GET_PACKET_TIME_RECEIVED returns the TimeReceived value from the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.

Return Value

NDIS_GET_PACKET_TIME_RECEIVED returns the timestamp, if any, set by the miniport when the net packet was received on the underlying NIC.

Comments

When a protocol driver’s ProtocolReceivePacket function is called with an indication, it can use this macro to retrieve the TimeReceived timestamp using the Packet pointer it is given on entry.

A protocol driver can call NdisQueryReceiveInformation to obtain this timestamp from the NDIS_PACKET_OOB_DATA block associated with a packet descriptor when its ProtocolReceive function is given an indication. However, this call is superfluous if the protocol is bound to an underlying driver that does not supply out-of-band information with its indications.

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packet descriptors are expressed in system time units as the number of 100-nanosecond intervals since January 1, 1601.

A highest-level protocol driver might use this macro before it forwards a received net packet to clients if its clients expect timestamps. Still higher-level drivers or system components can convert this timestamp into locale-specific values more meaningful to the end user.

Callers of NDIS_GET_PACKET_TIME_RECEIVED run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportTimer, NdisGetCurrentSystemTime, NDIS_GET_PACKET_TIME_SENT, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryReceiveInformation, NDIS_SET_PACKET_TIME_RECEIVED, ProtocolReceive, ProtocolReceivePacket


NDIS_GET_PACKET_TIME_SENT

ULONGLONG
NDIS_GET_PACKET_TIME_SENT(
IN PNDIS_PACKET Packet
);

NDIS_GET_PACKET_TIME_SENT returns the TimeSent value from the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.

Return Value

NDIS_GET_PACKET_TIME_SENT returns the timestamp, if any, set by the miniport when the net packet was received on the underlying NIC.

Comments

When a protocol driver’s ProtocolReceivePacket function is called with an indication, it can use this macro to retrieve the TimeSent timestamp using the Packet pointer it is given on entry. This value was set originally by a protocol on the remote node that transmitted the packet as the TimeToSend timestamp.

A protocol driver can call NdisQueryReceiveInformation to obtain this timestamp from the NDIS_PACKET_OOB_DATA block associated with a packet descriptor when its ProtocolReceive function is given an indication. However, this call is superfluous if the protocol is bound to an underlying driver that does not supply out-of-band information with its indications.

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packets are expressed in system time units as the number of 100-nanosecond intervals since January 1, 1601.

A highest-level protocol driver might use this macro before it forwards a received net packet to clients if its clients expect timestamps. Still higher-level drivers or system components can convert this timestamp into locale-specific values more meaningful to the end user.

Callers of NDIS_GET_PACKET_TIME_SENT run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportTimer, NdisGetCurrentSystemTime, NDIS_GET_PACKET_TIME_RECEIVED, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryReceiveInformation, NDIS_SET_PACKET_TIME_SENT, ProtocolReceive, ProtocolReceivePacket


NDIS_GET_PACKET_TIME_TO_SEND

ULONGLONG
NDIS_GET_PACKET_TIME_TO_SEND(
IN PNDIS_PACKET Packet
);

NDIS_GET_PACKET_TIME_TO_SEND returns the TimeToSend value from the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.

Return Value

NDIS_GET_PACKET_TIME_TO_SEND returns the timestamp, if any, set by the protocol driver before passing the Packet in an array to NdisSendPackets or as a parameter to NdisSend.

Comments

The MiniportSendPackets or MiniportSend function of an underlying NIC driver might use NDIS_GET_PACKET_TIME_TO_SEND to determine when a given packet should be transmitted over the network.

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packets are expressed in system time units as the number of 100-nanosecond intervals since January 1, 1601.

Callers of NDIS_GET_PACKET_TIME_TO_SEND run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportSend, MiniportSendPackets, NdisGetCurrentSystemTime, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_TIME_TO_SEND, ProtocolSendComplete


NdisGetPhysicalAddressHigh

ULONG
NdisGetPhysicalAddressHigh(
IN NDIS_PHYSICAL_ADDRESS PhysicalAddress
);

NdisGetPhysicalAddressHigh returns the high-order part of a given physical address.

Parameters

PhysicalAddress
Specifies a physical address of an OS-dependent size.

Return Value

NdisGetPhysicalAddressHigh returns the value of the high-order part of the physical address.

Comments

Callers of NdisGetPhysicalAddressHigh can be running at any IRQL.

See Also

NdisGetPhysicalAddressLow, NDIS_PHYSICAL_ADDRESS, NdisSetPhysicalAddressHigh


NdisGetPhysicalAddressLow

ULONG
NdisGetPhysicalAddressLow(
IN NDIS_PHYSICAL_ADDRESS PhysicalAddress
);

NdisGetPhysicalAddressLow returns the low-order part of a given physical address.

Parameters

PhysicalAddress
Specifies a physical address of an OS-dependent size.

Return Value

NdisGetPhysicalAddressLow returns the value of the low-order part of the physical address.

Comments

Callers of NdisGetPhysicalAddressLow can run at any IRQL.

See Also

NdisGetPhysicalAddressHigh, NDIS_PHYSICAL_ADDRESS, NdisSetPhysicalAddressLow


NdisIMDeInitializeDeviceInstance

NDIS_STATUS
NdisIMDeInitializeDeviceInstance(
IN NDIS_HANDLE NdisMiniportHandle
);

NdisIMDeInitializeDeviceInstance calls an NDIS intermediate driver’s MiniportHalt function to tear down the driver’s virtual NIC.

Parameters

NdisMiniportHandle
Specifies the handle originally input to MiniportInitialize.

Return Value

NdisIMDeInitializeDeviceInstance returns NDIS_STATUS_SUCCESS if the NIC has been torn down. Otherwise, it can return NDIS_STATUS_FAILURE if the given NdisMiniportHandle is invalid.

Comments

For NDIS intermediate drivers, NdisIMDeInitializeDeviceInstance is the reciprocal of NdisIMInitializeDeviceInstance. Such a driver usually calls NdisIMDeInitializeDeviceInstance from its ProtocolUnbindAdapter function, when the underlying NIC to which it was bound is being removed from the system, possibly because it is being reconfigured.

The call to NdisIMDeInitializeDeviceInstance causes a call to the intermediate driver's MiniportHalt function after NDIS has told all higher level protocols that had bound themselves to the intermediate's virtual NIC that they must unbind.

Callers of NdisIMDeInitializeDevice instance run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, NdisIMInitializeDeviceInstance, ProtocolUnbindAdapter


NdisIMInitializeDeviceInstance

NDIS_STATUS
NdisIMInitializeDeviceInstance(
IN NDIS_HANDLE DriverHandle,
IN PNDIS_STRING DeviceInstance
);

NdisIMInitializeDeviceInstance calls an NDIS intermediate driver’s MiniportInitialize function to set up the driver’s virtual NIC for I/O operations on an underlying NIC driver to which the intermediate driver is bound.

Parameters

DriverHandle
Specifies the handle returned by NdisIMRegisterLayeredMiniport.
DeviceInstance
Points to a buffered, caller-initialized counted string, in the system-default character set, naming the registry key in which the driver stores information about its virtual NIC and, possibly, binding-specific information. For Windows NT drivers, this string contains Unicode characters.

Return Value

NdisIMInitializeDeviceInstance propagates the status returned by the MiniportInitialize function.

Comments

An NDIS intermediate driver usually calls NdisIMInitializeDeviceInstance from its ProtocolBindAdapter function.

The driver’s installation script created the key designated by DeviceInstance in the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services registry tree.

Before it calls NdisIMInitializeDeviceInstance, ProtocolBindAdapter must make a successful call to NdisOpenAdapter, thereby binding the intermediate driver to the underlying NIC driver. Then, its subsequent call to NdisIMInitializeDeviceInstance allows the driver’s MiniportInitialize function to allocate any resources the driver needs to carry out network I/O operations and to initialize the driver’s virtual NIC, to which higher-level protocol drivers can bind themselves when the NDIS intermediate driver’s initialization is complete.

Callers of NdisIMInitializeDeviceInstance run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisIMDeInitializeDeviceInstance, NdisIMRegisterLayeredMiniport, NdisInitializeString, NdisOpenAdapter, ProtocolBindAdapter


NdisImmediateReadPciSlotInformation

ULONG
NdisImmediateReadPciSlotInformation(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG Length
 );

NdisImmediateReadPciSlotInformation returns a specified range of PCI configuration information in a caller-supplied buffer.

Parameters

WrapperConfigurationContext
Specifies the handle passed to the caller’s MiniportInitialize function.
SlotNumber
Specifies the slot number of the NIC on the I/O bus.
Offset
Specifies the byte offset within the PCI configuration space at which to begin transferring the configuration information.
Buffer
Points to a caller-allocated buffer in which to return the requested configuration information.
Length
Specifies the size in bytes of the buffer at Buffer and, therefore, how many bytes of configuration information to return.

Return Value

NdisImmediateReadPciSlotInformation returns the number of bytes it wrote at Buffer.

Comments

NdisImmediateReadPciSlotInformation helps a miniport driver find a NIC that it supports on a PCI bus, possibly before MiniportInitialize calls the Ndis..Configuration functions to get configuration information stored in the registry. This function cannot be called after the driver has claimed hardware resources in the registry for its NIC with NdisMPciAssignResources.

NdisImmediateReadPciSlotInformation returns the same bus-relative PCI configuration information as NdisReadPciSlotInformation. However, callers of NdisReadPciSlotInformation pass the adapter handle, rather than the WrapperConfigurationContext handle that is valid only during NIC driver initialization.

Callers of NdisImmediateReadPciSlotInformation can run at IRQL <= DISPATCH_LEVEL. Usually, callers are running at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMPciAssignResources, NdisOpenConfiguration, NdisReadPciSlotInformation


NdisImmediateReadPortUchar

VOID
NdisImmediateReadPortUchar(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG Port,
OUT PUCHAR Data
);

NdisImmediateReadPortUchar reads a byte from a given bus-relative I/O port.

Parameters

WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
Port
Specifies the bus-relative I/O port address.
Data
Points to a caller-supplied variable in which this function returns the value read from the I/O port.

Comments

NdisImmediateReadPortUchar determines how the host accesses the given I/O port, maps the bus-relative port address, and then reads a byte from the device.

A miniport driver can call NdisImmediateReadPortUchar during initialization to read data from a device I/O port when that driver has not yet called NdisMRegisterIoPortRange to obtain mapped device memory ranges. After its call to NdisMRegisterIoPortRange, the driver uses the faster NdisRaw Xxx functions to communicate with its NIC.

Callers of NdisImmediateReadPortUchar run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPortUlong, NdisImmediateReadPortUshort, NdisImmediateWritePortUchar, NdisMRegisterIoPortRange, NdisRawReadPortUchar


NdisImmediateReadPortUlong

VOID
NdisImmediateReadPortUlong(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG Port,
OUT PULONG Data
);

NdisImmediateReadPortUlong reads a ULONG from a given bus-relative I/O port.

Parameters

WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
Port
Specifies the bus-relative I/O port address.
Data
Points to a caller-supplied variable in which this function returns the value read from the I/O port.

Comments

NdisImmediateReadPortUlong determines how the host accesses the given I/O port, maps the bus-relative port address, and then reads a ULONG-type value from the device.

A miniport driver can call NdisImmediateReadPortUlong during initialization to read data from a device I/O port when that driver has not yet called NdisMRegisterIoPortRange to obtain mapped device memory ranges. After its call to NdisMRegisterIoPortRange, the driver uses the faster NdisRaw Xxx functions to communicate with its NIC.

Callers of NdisImmediateReadPortUlong run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPortUchar, NdisImmediateReadPortUshort, NdisImmediateWritePortUlong, NdisMRegisterIoPortRange, NdisRawReadPortUlong


NdisImmediateReadPortUshort

VOID
NdisImmediateReadPortUshort(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG Port,
OUT PUSHORT Data
);

NdisImmediateReadPortUshort reads a USHORT from a given bus-relative I/O port.

Parameters

WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
Port
Specifies the bus-relative I/O port address.
Data
Points to a caller-supplied variable in which this function returns the value read from the I/O port.

Comments

NdisImmediateReadPortUshort determines how the host accesses the given I/O port, maps the bus-relative port address, and then reads a USHORT-type value from the device.

A miniport driver can call NdisImmediateReadPortUshort during initialization to read data from a device I/O port when that driver has not yet called NdisMRegisterIoPortRange to obtain mapped device memory ranges. After its call to NdisMRegisterIoPortRange, the driver uses the faster NdisRaw Xxx functions to communicate with its NIC.

Callers of NdisImmediateReadPortUshort run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPortUchar, NdisImmediateReadPortUlong, NdisImmediateWritePortUshort, NdisMRegisterIoPortRange, NdisRawReadPortUshort


NdisImmediateReadSharedMemory

VOID
NdisImmediateReadSharedMemory(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG SharedMemoryAddress,
OUT PUCHAR Buffer,
IN ULONG Length
);

NdisImmediateReadSharedMemory reads a specified number of bytes from shared device RAM.

Parameters

WrapperConfigurationContext
Specifies the handle passed to MiniportInitialize.
SharedMemoryAddress
Specifies the bus-relative base address of the shared device memory range.
Buffer
Points to a caller-allocated buffer in which this function returns the data read from the device memory range starting at SharedMemoryAddress.
Length
Specifies the size in bytes of the buffer at Buffer and, therefore, how many bytes of data to return.

Comments

NdisImmediateReadShared Memory determines how the host accesses shared device memory, maps the bus-relative base address, and then copies Length bytes of data from the device memory area into the given buffer.

A miniport driver can call NdisImmediateReadSharedMemory during initialization to read data from a range of device memory when that driver has not yet called NdisMMapIoSpace to obtain mapped virtual addresses for the bus-relative device memory range. After its call to NdisMMapIoSpace, the driver uses the returned mapped virtual addresses in PIO operations.

Callers of NdisImmediateReadSharedMemory run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateWriteSharedMemory, NdisMMapIoSpace


NdisImmediateWritePciSlotInformation

ULONG
NdisImmediateWritePciSlotInformation(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG Length
 );

NdisImmediateWritePciSlotInformation writes PCI configuration information from a caller-supplied buffer to a specified range in the PCI configuration space for the NIC.

Parameters

WrapperConfigurationContext
Specifies the handle passed to the caller’s MiniportInitialize function.
SlotNumber
Specifies the slot number of the NIC on the I/O bus.
Offset
Specifies the byte offset within the PCI configuration space at which to begin transferring the caller-supplied configuration information.
Buffer
Points to a caller-allocated buffer containing the PCI configuration information to be written.
Length
Specifies the size in bytes of the buffer at Buffer and, therefore, how many bytes of PCI configuration information to write.

Return Value

NdisImmediateWritePciSlotInformation returns the number of bytes written.

Comments

NdisImmediateWritePciSlotInformation writes PCI configuration information for a NIC. However, drivers of NICs on PCI buses call NdisImmediateReadPciSlotInformation, NdisReadPciSlotInformation, or NdisMPciAssignResources far more frequently than this function. NdisImmediateWritePciSlotInformation cannot be called after the driver has claimed hardware resources in the registery for its NIC with NdisMPciAssignResources.

NdisImmediateWritePciSlotInformation transfers the same type of bus-relative PCI configuration information as NdisWritePciSlotInformation. However, callers of NdisWritePciSlotInformation pass the adapter handle, rather than the WrapperConfigurationContext handle that is valid only during NIC driver initialization.

Callers of NdisImmediateWritePciSlotInformation can run at IRQL <= DISPATCH_LEVEL. Usually, callers are running at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPciSlotInformation, NdisMPciAssignResources, NdisWritePciSlotInformation


NdisImmediateWritePortUchar

VOID
NdisImmediateWritePortUchar(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG Port,
IN UCHAR Data
);

NdisImmediateWritePortUchar writes a byte to a given bus-relative I/O port.

Parameters

WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
Port
Specifies the bus-relative I/O port address.
Data
Specifies the caller-supplied byte value to write to the I/O port.

Comments

NdisImmediateWritePortUchar determines how the host accesses the given I/O port, maps the bus-relative port address, and then writes the given byte to the device.

A miniport driver can call NdisImmediateWritePortUchar during initialization to write data to a device I/O port when that driver has not yet called NdisMRegisterIoPortRange to obtain mapped device memory ranges. After its call to NdisMRegisterIoPortRange, the driver uses the faster NdisRaw Xxx functions to communicate with its NIC.

Callers of NdisImmediateWritePortUchar run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPortUchar, NdisImmediateWritePortUlong, NdisImmediateWritePortUshort, NdisMRegisterIoPortRange, NdisRawWritePortUchar


NdisImmediateWritePortUlong

VOID
NdisImmediateWritePortUlong(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG Port,
IN ULONG Data
);

NdisImmediateWritePortUlong writes a ULONG to a given bus-relative I/O port.

Parameters

WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
Port
Specifies the bus-relative I/O port address.
Data
Specifies the caller-supplied ULONG value to write to the I/O port.

Comments

NdisImmediateWritePortUlong determines how the host accesses the given I/O port, maps the bus-relative port address, and then writes the given ULONG to the device.

A miniport driver can call NdisImmediateWritePortUlong during initialization to write data to a device I/O port when that driver has not yet called NdisMRegisterIoPortRange to obtain mapped device memory ranges. After its call to NdisMRegisterIoPortRange, the driver uses the faster NdisRaw Xxx functions to communicate with its NIC.

Callers of NdisImmediateWritePortUlong run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPortUlong, NdisImmediateWritePortUchar, NdisImmediateWritePortUshort, NdisMRegisterIoPortRange, NdisRawWritePortUlong


NdisImmediateWritePortUshort

VOID
NdisImmediateWritePortUshort(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG Port,
IN USHORT Data
);

NdisImmediateWritePortUshort writes a USHORT to a given bus-relative I/O port.

Parameters

WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
Port
Specifies the bus-relative I/O port address.
Data
Specifies the caller-supplied USHORT value to write to the I/O port.

Comments

NdisImmediateWritePortUshort determines how the host accesses the given I/O port, maps the bus-relative port address, and then writes the given USHORT to the device.

A miniport driver can call NdisImmediateWritePortUshort during initialization to write data to a device I/O port when that driver has not yet called NdisMRegisterIoPortRange to obtain mapped device memory ranges. After its call to NdisMRegisterIoPortRange, the driver uses the faster NdisRaw Xxx functions to communicate with its NIC.

Callers of NdisImmediateWritePortUshort run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPortUshort, NdisImmediateWritePortUchar, NdisImmediateWritePortUlong, NdisMRegisterIoPortRange, NdisRawWritePortUshort


NdisImmediateWriteSharedMemory

VOID
NdisImmediateWriteSharedMemory(
IN NDIS_HANDLE WrapperConfigurationContext,
IN ULONG SharedMemoryAddress,
IN PUCHAR Buffer,
IN ULONG Length
);

NdisImmediateWriteSharedMemory writes a specified number of bytes from a caller-supplied buffer to shared device RAM.

Parameters

WrapperConfigurationContext
Specifies the handle passed to MiniportInitialize.
SharedMemoryAddress
Specifies the bus-relative base address of the shared device memory range.
Buffer
Points to a caller-allocated buffer containing the data to be written to the device memory range starting at SharedMemoryAddress.
Length
Specifies the size in bytes of the buffer at Buffer and, therefore, how many bytes of data to transfer to device RAM.

Comments

NdisImmediateWriteSharedMemory determines how the host accesses shared device memory, maps the bus-relative base address, and then copies Length bytes of data from the given buffer to the device memory area.

A miniport driver can call NdisImmediateWriteSharedMemory during initialization to transfer data to a range of device memory when that driver has not yet called NdisMMapIoSpace to obtain mapped virtual addresses for the bus-relative device memory range. After its call to NdisMMapIoSpace, the driver uses the returned mapped virtual addresses in PIO operations.

Callers of NdisImmediateWriteSharedMemory run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadSharedMemory, NdisMMapIoSpace


NdisIMQueueMiniportCallback

NDIS_STATUS
NdisIMQueueMiniportCallback(
IN NDIS_HANDLE MiniportAdapterHandle,
IN W_MINIPORT_CALLBACK CallbackRoutine,
IN PVOID CallbackContext
);

NdisIMQueueMiniportCallback calls an intermediate driver’s callback function when it can execute in a MiniportXxx context, as, for example, to forward an indication to higher level protocols.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
CallbackRoutine
Specifies the entry point of a driver-supplied function, declared as follows:

VOID
MiniportCallback(
IN NDIS_HANDLE MiniportAdapterContext,
IN PVOID CallbackContext
);

Parameters to the MiniportCallback function include the following:

MiniportAdapterContext
Specifies the handle to a miniport-allocated per-NIC context area, set up by MiniportInitialize.
CallbackContext
Specifies the driver-supplied pointer passed to NdisIMQueueMiniportCallback.
CallbackContext
Points to a caller-determined context. This pointer will be passed as an input parameter to the driver’s callback function when it is run.

For example, this parameter might point to a caller-allocated area containing an array of pointers to packets to be indicated to higher-level protocols.

Return Value

NdisIMQueueMiniportCallback can return one of the following:

NDIS_STATUS_SUCCESS
The function at CallbackRoutine has been run.
NDIS_STATUS_PENDING
The function at CallbackRoutine will be run asynchronously.
NDIS_STATUS_FAILURE
NDIS could not run the function at CallbackRoutine at this time, possibly due to resource constraints. This return does not necessarily mean that a subsequent call to NdisIMQueueMiniportCallback will fail.

Comments

An NDIS intermediate driver must be executing as if it were a NIC miniport when it indicates up to bound protocols.

For example, packet arrays indicated up to an NDIS intermediate driver’s ProtocolReceivePackets function cannot be forwarded directly to higher-level bound protocols with a call to NdisMIndicateReceivePacket.

When an intermediate driver’s call to NdisIMSwitchToMiniport returns FALSE, the driver calls NdisIMQueueMiniportCallback.

First, such a driver’s ProtocolXxx function should call NdisIMSwitchToMiniport. If this call succeeds, it is safe for the ProtocolXxx function to call miniport-only NdisM Xxx functions and to call the reciprocal NdisIMRevertBack as soon as ProtocolXxx no longer has any more miniport-only processing to be done. If its call to NdisIMSwitchToMiniport returns FALSE, ProtocolXxx must call NdisIMQueueMiniportCallback with a driver function that essentially does exactly what ProtocolXxx would have done if its call to NdisIMSwitchToMiniport succeeded. However, the supplied MiniportCallback function also might return status to the caller in the CallbackContext or MiniportAdapterContext area.

If NdisIMQueueMiniportCallback returns NDIS_STATUS_SUCCESS, the ProtocolXxx function resumes execution with all necessary miniport-only work already done by the supplied function at CallbackRoutine.

The MiniportCallback function passed to NdisIMQueueMiniportCallback runs, by default, at IRQL DISPATCH_LEVEL.

Callers of NdisIMQueueMiniportCallback run at IRQL DISPATCH_LEVEL.

See Also

NdisIMRevertBack, NdisIMSwitchToMiniport, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMIndicateStatus, NdisMTrIndicateReceive, NdisMWanIndicateReceive, ProtocolReceive, ProtocolReceivePacket, ProtocolStatus


NdisIMRegisterLayeredMiniport

NDIS_STATUS
NdisIMRegisterLayeredMiniport(
IN NDIS_HANDLE NdisWrapperHandle,
IN PNDIS_MINIPORT_CHARACTERISTICS MiniportCharacteristics,
IN UINT CharacteristicsLength,
 OUT PNDIS_HANDLE DriverHandle
 );

NdisIMRegisterLayeredMiniport registers an intermediate driver’s MiniportXxx entry points and name with the NDIS library when the driver initializes.

Parameters

NdisWrapperHandle
Specifies the handle returned by NdisMInitializeWrapper.
MiniportCharacteristics
Points to an NDISXX_MINIPORT_CHARACTERISTICS structure set up by the caller. The structure at MiniportCharacteristics is defined as follows:
typedef struct _NDIS_MINIPORT_CHARACTERISTICS {
    UCHAR MajorNdisVersion;
    UCHAR MinorNdisVersion;
    UINT Reserved;
    W_CHECK_FOR_HANG_HANDLER CheckForHangHandler;
    W_DISABLE_INTERRUPT_HANDLER DisableInterruptHandler;
    W_ENABLE_INTERRUPT_HANDLER  EnableInterruptHandler;
    W_HALT_HANDLER HaltHandler;
    W_HANDLE_INTERRUPT_HANDLER  HandleInterruptHandler;
    W_INITIALIZE_HANDLER InitializeHandler;
    W_ISR_HANDLER ISRHandler;
    W_QUERY_INFORMATION_HANDLER QueryInformationHandler;
    W_RECONFIGURE_HANDLER ReconfigureHandler;
    W_RESET_HANDLER ResetHandler;
    W_SEND_HANDLER SendHandler;
    W_SET_INFORMATION_HANDLER SetInformationHandler;
    W_TRANSFER_DATA_HANDLER TransferDataHandler;
//
// MajorNdisVersion must be set to 0x04 with following members
//
    W_RETURN_PACKET_HANDLER ReturnPacketHandler;
    W_SEND_PACKETS_HANDLER SendPacketsHandler;
    W_ALLOCATE_COMPLETE_HANDLER AllocateCompleteHandler;
} NDIS_MINIPORT_CHARACTERISTICS, *PNDIS_MINIPORT_CHARACTERISTICS;

An NDIS intermediate driver should initialize this structure with zeros before setting up any of the following members:

MajorNdisVersion
Specifies the major version of the NDIS library the driver is using. The current value is 0x04, although the NDIS library continues to support existing miniports developed for NDIS V3.0. Consequently, an NDIS intermediate driver that binds itself only to legacy miniports might set this member to 0x03.

This member must be set to 0x04 if the caller sets entry points in any members following Name.

MinorNdisVersion
Specifies the minor version of the NDIS library the driver is using. The current value is 0x00, although NDIS continues to support existing drivers.
Reserved
This member is reserved for system use.
CheckForHangHandler
Specifies the entry point of the caller’s MiniportCheckForHang function, if any, or NULL.
DisableInterruptHandler
Specifies NULL.
EnableInterruptHandler
Specifies NULL.
HaltHandler
Specifies the entry point of the caller’s MiniportHalt function.
HandleInterruptHandler
Specifies NULL.
InitializeHandler
Specifies the entry point of the caller’s MiniportInitialize function.
ISRHandler
Specifies NULL.
QueryInformationHandler
Specifies the entry point of the caller’s MiniportQueryInformation function.
ReconfigureHandler
Specifies NULL.
ResetHandler
Specifies the entry point of the caller’s MiniportReset function.
SendHandler
Specifies the entry point of the caller’s MiniportSend function, MiniportWanSend function, or NULL if the caller supplies a MiniportSendPackets function.

If the driver supports multipacket sends or media-specific information, it sets the SendPacketsHandler member instead and sets this member to NULL.

SetInformationHandler
Specifies the entry point of the caller’s MiniportSetInformation function.
TransferDataHandler
Specifies the entry point of the caller’s MiniportTransferData function, if any, or NULL. This miniport function is required unless the caller is the driver of a WAN NIC or the caller supports multipacket receives and, therefore, supplies the entry point of its MiniportReturnPacket function at ReturnPacketHandler.
ReturnPacketHandler
Specifies the entry point of the caller’s MiniportReturnPacket function, if any, or NULL.
SendPacketsHandler
Specifies the entry point of the caller’s MiniportSendPackets function, if any, or NULL.
AllocateCompleteHandler
Specifies NULL.
CharacteristicsLength
Specifies the length in bytes of the caller-supplied characteristics buffer. Depending on the value of MajorNdisVersion, this parameter must be either sizeof(NDIS40_MINIPORT_CHARACTERISTICS) or sizeof(NDIS30_MINIPORT_CHARACTERISTICS).

If the driver includes the build instruction NDIS40_MINIPORT (or NDIS30_MINIPORT, as appropriate) in its sources or if the driver writer uses the -NDIS40_MINIPORT (or ..30..) compiler switch, this parameter is set when the driver is built.

DriverHandle
Points to a variable in which NdisIMRegisterLayeredMiniport, if this call is successful, returns a handle that the caller should save. The caller subsequently must pass this handle to NdisIMInitializeDeviceInstance, usually from its ProtocolBindAdapter function.

Return Value

NdisIMRegisterLayeredMiniport returns NDIS_STATUS_SUCCESS if it registered the caller as a miniport, or it can return one of the following status values:

NDIS_STATUS_BAD_CHARACTERISTICS
The CharacteristicsLength is too small for the MajorNdisVersion specified in the buffer at MiniportCharacteristics.
NDIS_STATUS_BAD_VERSION
The MajorNdisVersion or MinorNdisVersion specified in the characteristics structure is invalid.
NDIS_STATUS_RESOURCES
A shortage of resources, possibly memory, prevented the NDIS library from registering the caller as a miniport.
NDIS_STATUS_FAILURE
This is a default error status, returned when none of the preceding errors caused the registration to fail. For example, if the NDIS library cannot load the driver’s image and lock it into system memory, it returns this error.

Comments

Any NDIS intermediate driver that exports both MiniportXxx and ProtocolXxx functions usually sets up a characteristics structure and calls NdisIMRegisterLayeredMiniport from its DriverEntry function after DriverEntry calls NdisMInitializeWrapper. This structure is copied in the NdisIMRegisterLayeredMiniport request to the NDIS library’s internal storage. Thus, once it has registered, such a driver cannot change its handler functions.

After such an NDIS intermediate driver has called NdisIMRegisterLayeredMiniport successfully, it must call NdisRegisterProtocol to register its ProtocolXxx functions with the NDIS library. Such a driver usually has both ProtocolBindAdapter and ProtocolUnbindAdapter functions. Its ProtocolBindAdapter function will be called next when the underlying NIC driver has initialized successfully. ProtocolBindAdapter then can establish a binding to that NIC driver with NdisOpenAdapter.

An NDIS intermediate driver should have a MiniportSendPackets function if an underlying NIC driver might support multipacket sends or consume media-specific information, such as packet priorities, sent down in a packet array from a higher-level protocol. An NDIS intermediate driver should have a MiniportReturnPacket function if an underlying NIC driver might support multipacket receive indications or indicate packet arrays containing media-specific information. The NDIS library handles packet arrays transferred between an underlying NIC driver and higher-level protocol that support only single-packet transfers on behalf of such an intermediate driver.

An intermediate driver can call NdisMRegisterMiniport instead of NdisIMRegisterLayeredMiniport if it is prepared for an immediate call to the MiniportInitialize function.

Callers of NdisIMRegisterLayeredMiniport run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Miniport Drivers, DriverEntry of NDIS Protocol Drivers, MiniportCheckForHang, MiniportHalt, MiniportInitialize, MiniportQueryInformation, MiniportReset, MiniportReturnPacket, MiniportSend, MiniportSendPackets, MiniportSetInformation, MiniportTransferData, MiniportWanSend, NdisIMInitializeDeviceInstance, NdisMRegisterMiniport, NdisOpenAdapter, NdisRegisterProtocol, NdisZeroMemory, ProtocolBindAdapter, ProtocolReceivePacket, ProtocolUnbindAdapter


NdisIMRevertBack

VOID
NdisIMRevertBack(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE SwitchHandle
);

NdisIMRevertBack returns the function of an intermediate driver to the context in which it was executing before it called NdisIMSwitchToMiniport.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
SwitchHandle
Specifies the handle returned by the immediately preceding call to NdisIMSwitchToMiniport.

Comments

An intermediate driver should call NdisIMRevertBack as quickly as possible after a driver function makes a successful call to NdisIMSwitchToMiniport.

For example, after a successful call to NdisIMSwitchToMiniport, the ProtocolXxx function of an NDIS intermediate driver can safely call NdisM Xxx functions commonly called from the MiniportHandleInterrupt or MiniportTimer functions of underlying NIC drivers. In particular, such a ProtocolXxx function can call NdisM Xxx functions that indicate up to higher-level protocols bound to the intermediate driver’s virtual NIC.

Until it calls NdisIMRevertBack, the execution of such a ProtocolXxx function prevents other MiniportXxx functions in the same driver from running.

A call to NdisIMRevertBack without a SwitchHandle returned by a preceding call to NdisIMSwitchToMiniport is a fatal error.

Callers of NdisIMRevertBack are running at IRQL DISPATCH_LEVEL.

See Also

NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMIndicateStatus, NdisMTrIndicateReceive, ProtocolReceive, ProtocolReceivePacket, ProtocolStatus


NdisIMSwitchToMiniport

BOOLEAN
NdisIMSwitchToMiniport(
IN NDIS_HANDLE MiniportAdapterHandle,
 OUT PNDIS_HANDLE SwitchHandle
);

NdisIMSwitchToMiniport allows an intermediate driver function to call NdisM Xxx normally called only from MiniportXxx functions, as, for example, to make indications up to higher-level protocols.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
SwitchHandle
Points to a variable in which this function returns a handle if the call succeeds.

Return Value

NdisIMSwitchToMiniport returns TRUE if the caller can safely call miniport-only NdisM Xxx functions until it makes a reciprocal call to NdisIMRevertBack.

Comments

An intermediate driver must make indications up to higher-level protocols as if from a NIC driver’s MiniportXxx functions. Attempts to simply forward indications to bound protocols directly from an intermediate driver function that receives them will cause deadlocks.

For example, after a successful call to NdisIMSwitchToMiniport, the ProtocolXxx function of an NDIS intermediate driver can safely call NdisM Xxx functions commonly called from the MiniportHandleInterrupt or MiniportTimer functions of underlying NIC drivers. In particular, such a ProtocolXxx function can call NdisM Xxx functions, such as NdisMIndicateReceivePacket or NdisMIndicateStatus, that indicate up to higher-level protocols bound to the intermediate driver’s virtual NIC.

After a successful call to NdisIMSwitchToMiniport, an intermediate driver should call NdisIMRevertBack as quickly as possible. Until it calls NdisIMRevertBack, the execution of such a ProtocolXxx function prevents other MiniportXxx functions in the same driver from running.

If NdisIMSwitchToMiniport returns FALSE, the intermediate driver should call NdisIMQueueMiniportCallback. The MiniportCallback function passed to NdisIMQueueMiniportCallback should do essentially the same work as the caller’s miniport-only code would have done if its call to NdisIMSwitchToMiniport had succeeded. That is, such a MiniportCallback function replaces the miniport-only code bracketed by calls to NdisIMSwitchToMiniport and NdisIMRevertBack, as well as setting up sufficient state to communicate with the nonminiport code paths of the intermediate driver.

If an NDIS intermediate driver has internal functions called by both its ProtocolXxx and MiniportXxx functions, such an internal driver function cannot call NdisIMSwitchToMiniport, NdisIMRevertBack, or NdisIMQueueMiniportCallback. A call to any of these NdisIM Xxx functions from a MiniportXxx code path is a fatal error.

Callers of NdisIMSwitchToMiniport run at IRQL <= DISPATCH_LEVEL.

See Also

NdisIMQueueMiniportCallback, NdisIMRevertBack, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMIndicateStatus, NdisMTrIndicateReceive, NdisMWanIndicateReceive, ProtocolReceive, ProtocolReceivePacket, ProtocolStatus


NdisInitAnsiString

VOID
NdisInitAnsiString(
IN OUT PNDIS_ANSI_STRING DestinationString,
IN PCSTR SourceString
);

NdisInitAnsiString initializes a counted ANSI string.

Parameters

DestinationString
Points to a caller-allocated buffer in which this function should store the counted ANSI string.
SourceString
Points to a zero-terminated string with which to initialize the counted string.

Comments

The DestinationString is initialized to point to the SourceString. The length and maximum length for the DestinationString are initialized to the length of the string at SourceString. If SourceString is NULL, the length is zero.

Callers of NdisInitAnsiString can be running at IRQL <= DISPATCH_LEVEL if the DestinationString buffer is allocated from nonpaged memory. Usually, callers are running at IRQL PASSIVE_LEVEL during driver initialization.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisAnsiStringToUnicodeString, NdisFreeString, NdisInitializeString, NdisInitUnicodeString, NdisUnicodeStringToAnsiString, ProtocolBindAdapter


NDIS_INIT_FUNCTION

NDIS_INIT_FUNCTION(FunctionName)

NDIS_INIT_FUNCTION is a macro used to mark a driver function to be run once only during initialization.

Parameters

FunctionName
The name of the function that is only used during initialization.

Comments

Any driver function marked with NDIS_INIT_FUNCTION is discarded when the DriverEntry function returns control.

For example, such a function is declared in the driver source as follows:

NDIS_STATUS DriverEntry(...);
#pragma NDIS_INIT_FUNCTION(DriverEntry)
    ...  //other function declarations
NDIS_STATUS
DriverEntry(...)
{
    ...
}
    ...

NDIS drivers can use this macro on their DriverEntry functions and on internal driver functions that are called only by their DriverEntry functions.

A NIC driver cannot use this macro on its MiniportInitialize function, nor on any internal driver function called by MiniportInitialize, because this function can be called at runtime in a Plug-and-Play environment.

See Also

DriverEntry of NDIS Miniport Drivers, DriverEntry of NDIS Protocol Drivers, NDIS_PAGABLE_FUNCTION


NdisInitializeEvent

VOID
NdisInitializeEvent(
IN PNDIS_EVENT Event
);

NdisInitializeEvent sets up an event object during driver initialization to be used subsequently as a synchronization mechanism.

Parameters

Event
Points to caller-supplied storage for the event object, which is opaque to drivers.

Comments

The Event pointer passed to NdisInitializeEvent is a required parameter to all other Ndis..Event functions.

While driver functions that run at IRQL <= DISPATCH_LEVEL can call NdisSetEvent and NdisResetEvent, calling NdisWaitEvent from any IRQL > PASSIVE_LEVEL is a fatal error.

Callers of NdisInitializeEvent run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisResetEvent, NdisSetEvent, NdisWaitEvent


NdisInitializeListHead

VOID
NdisInitializeListHead(
IN PLIST_ENTRY ListHead
);

NdisInitializeListHead initializes a doubly linked, driver-maintained interlocked queue or doubly linked list.

Parameters

ListHead
Points to driver-allocated nonpaged storage for the head of the interlocked queue or list.

Comments

NdisInitializeListHead can be called from a MiniportInitialize function or from a protocol driver’s DriverEntry function if the driver queues requests internally. However, miniport drivers seldom set up internal queues because the NDIS library serializes requests and packets sent to miniports.

Any NDIS driver that maintains an internal queue is responsible for synchronizing driver functions’ accesses to queued entries. The NdisInterlocked..List functions ensure that only one driver function can access queued entries at any given moment, even if the driver is running on a multiprocessor machine, because the queue is protected by a caller-supplied spin lock.

For an interlocked queue, the driver also must provide nonpaged storage for a spin lock. It must initialize the spin lock with NdisAllocateSpinLock before passing a pointer to that spin lock to any of the NdisInterlocked..List functions.

Callers of NdisInitializeListHead usually run at IRQL PASSIVE_LEVEL, because this function is commonly called at initialization.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisAllocateSpinLock, NdisInterlockedInsertHeadList, NdisInterlockedInsertTailList, NdisInterlockedRemoveHeadList


NdisInitializeString

VOID
NdisInitializeString(
IN OUT PNDIS_STRING DestinationString,
IN PUCHAR SourceString
);

NdisInitializeString allocates storage for and initializes a counted string in the system-default character set.

Parameters

DestinationString
Specifies NULL when NdisInitializeString is called; on return from this function, points to a buffer containing an initialized counted string.
SourceString
Points to a zero-terminated string with which to initialize the counted string.

Comments

NdisInitializeString sets the Length and MaximumLength for the destination string and terminates the destination string with zero. For Windows NT drivers, this function converts the supplied source string to Unicode characters.

If SourceString is NULL, the lengths are set to zero.

The buffer allocated by this function should be released promptly with NdisFreeString.

Callers of NdisInitializeString run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisAnsiStringToUnicodeString, NdisEqualString, NdisFreeString, NdisInitAnsiString, NdisInitUnicodeString, NdisUnicodeStringToAnsiString


NdisInitUnicodeString

VOID
NdisInitUnicodeString(
IN OUT PNDIS_STRING DestinationString,
IN PCWSTR SourceString
);

NdisInitUnicodeString initializes a counted Unicode string.

Parameters

DestinationString
Points to a caller-allocated buffer in which this function should store the counted Unicode string.
SourceString
Points to a zero-terminated string with which to initialize the counted string.

Comments

The DestinationString is initialized to point to the SourceString. The length and maximum length for the DestinationString are initialized to the length of the string at SourceString. If SourceString is NULL, the length is zero.

Callers of NdisInitUnicodeString can be running at IRQL <= DISPATCH_LEVEL if the DestinationString buffer is allocated from nonpaged memory. Usually, callers are running at IRQL PASSIVE_LEVEL during driver initialization.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisAnsiStringToUnicodeString, NdisFreeString, NdisInitAnsiString, NdisInitializeString, NdisUnicodeStringToAnsiString, ProtocolBindAdapter


NdisInterlockedAddUlong

VOID
NdisInterlockedAddUlong(
IN PULONG Addend,
IN ULONG Increment,
IN PNDIS_SPIN_LOCK SpinLock
);

NdisInterlockedAddUlong adds an unsigned long value to a given unsigned integer as an atomic operation, using a caller-supplied spin lock to synchronize access to the integer variable.

Parameters

Addend
Points to the variable for which the value is to be adjusted by the given Increment.
Increment
Specifies the value to be added to that at Addend.
SpinLock
Points to a caller-initialized spin lock.

Comments

The caller must provide resident storage for the spin lock, which must be initialized with NdisAllocateSpinLock before the initial call to NdisInterlockedAddUlong.

The SpinLock passed to NdisInterlockedAddUlong is used to assure that the addition to the variable at Addend is atomic with respect to any other operations on the same variable that synchronize with the same spin lock.

NdisInterlockedAddUlong raises IRQL to DISPATCH_LEVEL when it acquires the given spin lock and restores the original IRQL before it returns control. Consequently, any driver function that calls NdisInterlockedAddUlong cannot be pageable code.

Callers of NdisInterlockedAddUlong run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAcquireSpinLock, NdisAllocateSpinLock, NdisReleaseSpinLock, NdisRetrieveUlong, NdisStoreUlong


NdisInterlockedInsertHeadList

PLIST_ENTRY
NdisInterlockedInsertHeadList(
IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY ListEntry,
IN PNDIS_SPIN_LOCK SpinLock
);

NdisInterlockedInsertHeadList inserts an entry, usually a packet, at the head of a doubly linked list so that access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the doubly linked list into which an entry is to be inserted.
ListEntry
Points to the entry to be inserted at the head of the list.
SpinLock
Points to a caller-supplied spin lock, used to synchronize access to the list.

Return Value

NdisInterlockedInsertHeadList returns a pointer to the entry that was at the head of the queue before the given entry was inserted. If the queue was empty, it returns NULL.

Comments

Before calling NdisInterlockedInsertHeadList, a driver must initialize the variable at ListHead with NdisInitializeListHead and the variable at SpinLock with NdisAllocateSpinLock. The driver also must provide resident storage for these variables and for its internal queue.

The caller-supplied spin lock prevents any other function from accessing the driver’s internal queue while NdisInterlockedInsertHeadList is inserting the given entry, even when the driver is running on a multiprocessor machine.

NdisInterlockedInsertHeadList raises IRQL to DISPATCH_LEVEL when it acquires the given spin lock and restores the original IRQL before it returns control. Consequently, any driver function that calls NdisInterlockedInsertHeadList cannot be pageable code.

Most NDIS drivers process packets in FIFO order, so any driver that uses an interlocked queue tends to call NdisInterlockedInsertTailList far more frequently than NdisInterlockedInsertHeadList. Such a driver usually calls NdisInterlockedInsertHeadList only to requeue a packet for a retry operation.

To convert a returned value back to the address of the inserted entry, a driver can use the CONTAINING_RECORD macro (see the Kernel-Mode Driver Reference).

Callers of NdisInterlockedInsertHeadList run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateSpinLock, NdisInitializeListHead, NdisInterlockedInsertTailList, NdisInterlockedRemoveHeadList


NdisInterlockedInsertTailList

PLIST_ENTRY
NdisInterlockedInsertTailList(
IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY ListEntry,
IN PNDIS_SPIN_LOCK SpinLock
);

NdisInterlockedInsertTailList inserts an entry, usually a packet, at the tail of a doubly linked list so that access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the doubly linked list into which an entry is to be inserted.
ListEntry
Points to the entry to be inserted at the end of the list.
SpinLock
Points to a caller-supplied spin lock, used to synchronize access to the list.

Return Value

NdisInterlockedInsertTailList returns a pointer to the entry that was at the tail of the queue before the given entry was inserted. If the queue was empty, it returns NULL.

Comments

Before calling NdisInterlockedInsertTailList, a driver must initialize the variable at ListHead with NdisInitializeListHead and the variable at SpinLock with NdisAllocateSpinLock. The driver also must provide resident storage for these variables and for its internal queue.

The caller-supplied spin lock prevents any other function from accessing the driver’s internal queue while NdisInterlockedInsertTailList is inserting the given entry, even when the driver is running on a multiprocessor machine.

NdisInterlockedInsertTailList raises IRQL to DISPATCH_LEVEL when it acquires the given spin lock and restores the original IRQL before it returns control. Consequently, any driver function that calls NdisInterlockedInsertTailList cannot be pageable code.

To convert a returned value back to the address of the inserted entry, a driver can use the CONTAINING_RECORD macro (see the Kernel-Mode Driver Reference).

Callers of NdisInterlockedInsertTailList run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateSpinLock, NdisInitializeListHead, NdisInterlockedInsertHeadList, NdisInterlockedRemoveHeadList


NdisInterlockedRemoveHeadList

PLIST_ENTRY
NdisInterlockedRemoveHeadList(
IN PLIST_ENTRY ListHead,
IN PNDIS_SPIN_LOCK SpinLock
);

NdisInterlockedRemoveHeadList removes an entry, usually a packet, from the head of a doubly linked list so that access to the list is synchronized in a multiprocessor-safe way.

Parameters

ListHead
Points to the head of the doubly linked list from which an entry is to be removed.
SpinLock
Points to a caller-supplied spin lock, used to synchronize access to the list.

Return Value

NdisInterlockedRemoveHeadList returns a pointer to the dequeued entry. If the list was empty, it returns NULL.

Comments

Before calling any NdisInterlocked..List function, a driver must initialize the variable at ListHead with NdisInitializeListHead and the variable at SpinLock with NdisAllocateSpinLock. The driver also must provide resident storage for these variables and for its internal queue.

Before calling NdisInterlockedRemoveHeadList, entries are queued with one or more calls to the NdisInterlockedInsert..List functions.

The caller-supplied spin lock prevents any other function from accessing the driver’s internal queue while NdisInterlockedRemoveHeadList is removing an entry, even when the driver is running on a multiprocessor machine.

NdisInterlockedRemoveHeadList raises IRQL to DISPATCH_LEVEL when it acquires the given spin lock and restores the original IRQL before it returns control. Consequently, any driver function that calls NdisInterlockedRemoveHeadList cannot be pageable code.

To convert a returned value back to the address of the inserted entry, a driver can use the CONTAINING_RECORD macro (see the Kernel-Mode Driver Reference).

Callers of NdisInterlockedRemoveHeadList run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateSpinLock, NdisInitializeListHead, NdisInterlockedInsertHeadList, NdisInterlockedInsertTailList


NdisMAllocateMapRegisters

NDIS_STATUS
NdisMAllocateMapRegisters(
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT DmaChannel,
IN BOOLEAN Dma32BitAddresses,
IN ULONG PhysicalMapRegistersNeeded,
IN ULONG MaximumPhysicalMapping
 );

NdisMAllocateMapRegisters reserves system resources during NIC driver initialization for subsequent busmaster DMA operations.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
DmaChannel
Specifies the bus-relative DMA channel for an ISA busmaster NIC. If the NIC is on another type of I/O bus, this parameter must be zero.
Dma32BitAddresses
Specifies TRUE if the NIC is capable of using 32-bit addressing for DMA operations.
PhysicalMapRegistersNeeded
Specifies the maximum number of map registers, each mapping a discrete physical page or contiguous range within a physical page, that the NIC can use in a DMA operation. Together with MaximumPhysicalMapping, this parameter describes the DMA-transfer capabilities of the NIC.
MaximumPhysicalMapping
Specifies the maximum number of bytes that the NIC can transfer as a single DMA operation.

Return Value

NdisMAllocateMapRegisters can return one of the following:

NDIS_STATUS_SUCCESS
The requested number of map registers has been allocated for the NIC and, if DmaChannel was nonzero, the specified channel has been reserved.
NDIS_STATUS_RESOURCES
Either the caller specified incompatible parameters when MiniportInitialize called NdisMSetAttributesEx or the requested number of map registers could not be allocated due to system resource constraints.

Comments

Drivers of busmaster DMA NICs call NdisMAllocateMapRegisters from their MiniportInitialize functions.

Such a driver’s MiniportInitialize function must call NdisMSetAttributes or NdisMSetAttributesEx before it calls NdisMAllocateMapRegisters. It should call NdisQueryMapRegisterCount before calling NdisMAllocateMapRegisters as well.

The given PhysicalMapRegistersNeeded is constrained by one of the following:

  • The number of physical discontiguities backing any specified virtual range for a DMA transfer that the NIC can handle

  • The relative sizes of the NIC’s MaximumPhysicalMapping and the memory PAGE_SIZE of the current platform

    For a busmaster NIC, MaximumPhysicalMapping is the largest number of bytes in any virtual range described by an NDIS_BUFFER-type descriptor that can be passed to NdisMStartBufferPhysicalMapping subsequently.

  • The number of available map registers for the NIC’s I/O bus in the current platform

Map registers are a limited system resource, so a miniport should never allocate more map registers than its NIC can use in a DMA transfer operation. NdisQueryMapRegisterCount returns the maximum number of map registers that can be allocated on the current platform for the type of I/O bus the NIC uses. MiniportInitialize should pass the minimum of the following values for PhysicalMapRegistersNeeded to NdisMAllocateMapRegisters:

  • The result of (MaximumBytesPerTransferOfNIC / PAGE_SIZE + 1)

  • The number of physical discontiguites per virtual transfer range that the NIC can handle in any DMA transfer operation

  • The value returned by NdisQueryMapRegisterCount

The map registers allocated with NdisMAllocateMapRegisters are implicitly numbered from zero through (PhysicalMapRegistersNeeded - 1). When calling NdisMStartBufferPhysicalMapping, the driver of a busmaster NIC specifies which map register to use in each call by specifying that map register’s zero-based index.

The driver of a busmaster DMA NIC must call NdisMAllocateMapRegisters before it calls NdisMAllocateSharedMemory.

Drivers of NICs that use PIO or the host DMA controller as slave devices do not call NdisMAllocateMapRegisters during initialization.

Callers of NdisMAllocateMapRegisters run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMAllocateSharedMemory, NdisMFreeMapRegisters, NdisMRegisterDmaChannel, NdisMSetAttributes, NdisMSetAttributesEx, NdisMStartBufferPhysicalMapping, NdisQueryMapRegisterCount


NdisMAllocateSharedMemory

VOID
NdisMAllocateSharedMemory(
IN NDIS_HANDLE MiniportAdapterHandle,
IN ULONG Length,
IN BOOLEAN Cached,
OUT PVOID *VirtualAddress,
OUT PNDIS_PHYSICAL_ADDRESS PhysicalAddress
 );

NdisMAllocateSharedMemory allocates and maps a host memory range so it is simultaneously accessible from both the system and a busmaster DMA NIC.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
Length
Specifies the number of bytes to allocate.
Cached
Specifies TRUE if the range can be allocated from cached memory.
VirtualAddress
Points to a caller-supplied variable in which this function returns the base virtual address of the allocation for use by the miniport. If NdisMAllocateSharedMemory cannot satisfy its caller, it returns NULL to indicate that no memory was allocated.
PhysicalAddress
Points to a caller-supplied variable in which this function returns a physical address, suitable for use by the NIC, that corresponds to that returned at VirtualAddress, or it returns NULL.

Comments

NdisMAllocateSharedMemory provides both the mapped virtual address range that the driver uses to access the shared memory block and the NDIS_PHYSICAL_ADDRESS-type range that the NIC uses. A value returned at PhysicalAddress can be doubly mapped by the system. That is, a “physical” address range described by the value at PhysicalAddress and Length can be a range of mapped logical addresses that do not match the host physical addresses for the allocation in every possible platform.

NdisMAllocateSharedMemory can be called only from MiniportInitialize. How large an allocation to request depends on how the driver writer, knowing the capabilities and features of the NIC, decides to make the tradeoff between the following performance versus size dilemma:

  • In periods of high network traffic, a miniport cannot maintain high I/O throughput if it runs low on shared memory space for device-accessible data buffers.

    For example, the miniport could be indicating receive buffers in shared memory faster than such buffers are being returned from bound protocols when a flood of receives come in to its NIC. If all its shared memory space is consumed by outstanding receive buffers, the miniport might have to disable receive interrupts on its NIC until it has some shared memory space available for receive buffers.

  • On the other hand, calling NdisMAllocateSharedMemory with a Length chosen to anticipate some maximum transfer demand makes the driver’s image larger and its resource usage quite uneconomical, except for rare periods of very high I/O demand. Moreover, NdisMAllocateSharedMemory might not give the driver such a large block if insufficient system memory is available, forcing the driver to fail initialization.

A NIC driver that supplies a MiniportAllocateComplete function has considerably more flexibility in resolving the preceding performance versus size dilemma. MiniportInitialize should allocate only enough shared memory with NdisMAllocateSharedMemory for a moderate demand for network transfer operations through the NIC if the driver has a MiniportAllocateComplete function. Such a miniport can call NdisMAllocateSharedMemoryAsync dynamically to allocate more shared memory in periods of heavier transfer demand on its NIC. When the high demand for transfers subsides, such a driver calls NdisMFreeSharedMemory to release the additional memory it allocated.

NdisMAllocateSharedMemory and NdisMAllocateSharedMemoryAsync are the only Ndis Xxx functions that can be called to allocate host memory that is shared between the driver, which uses virtual addresses, and a busmaster NIC, which uses the corresponding logical addresses.

Whenever possible, a NIC driver calls NdisMAllocateSharedMemory with Cached set to TRUE because its request is more likely to succeed. In any platform, noncached memory is always a scarce system resource. Usually, drivers can get larger allocations from cached memory as well. A NIC driver must allocate its shared memory space from noncached memory if either of the following is true:

  • The NIC or miniport writes directly into receive buffers before the miniport indicates the newly received data.

    For example, a NIC that sets flags in each received frame after it has been transferred must have access to receive buffers in noncached memory. Otherwise, the miniport could not determine when it should issue a flush to maintain cache coherency: either the miniport would take a performance hit by waiting for a fail-safe interval to flush the cached receive buffer, or the miniport would make indications in which the frame flags were randomly set.

  • The NIC transfers some number of received frames sequentially into contiguous physical memory within the shared memory space.

    If such a NIC transferred incoming frames into contiguous cached memory, its driver cannot maintain data integrity for all such frames when any frame might straddle a cache-line boundary. When the miniport flushed the range for such a frame, it also might flush the cache space containing some of the next frame if it was already transferred, thereby making that next frame incoherent.

A miniport should align the buffers it allocates from shared cached memory on an integral of the host data-cache-line boundary to prevent cache-line tearing during DMA. Cache-line tearing can cause data-integrity problems in the driver or degrade the driver’s (and the system’s) I/O performance by requiring excessive data-cache flushing to maintain data integrity. MiniportInitialize can call NdisGetCacheFillSize to determine the alignment boundary in the current platform for device-accessible buffers that the driver will set up within an allocated range of shared memory.

MiniportInitialize also might call NdisSystemProcessorCount before it calls NdisMAllocateSharedMemory if the driver writer decides to allocate a larger shared memory block in multiprocessor machines on the assumption that any SMP machine is likely to be a network server with higher network-transfer demands on the NIC than a workstation.

MiniportInitialize must call NdisMAllocateMapRegisters before it calls NdisMAllocateSharedMemory; otherwise, no memory can be allocated. NDIS allocates an additional map register internally for the shared memory range when it is allocated.

If MiniportInitialize did not specify that the NIC is a busmaster when it called NdisMSetAttributes or NdisMSetAttributesEx, NdisMAllocateSharedMemory simply returns control without attempting to make an allocation.

If its call to NdisMAllocateSharedMemory fails, MiniportInitialize can call again requesting a smaller allocation. However, if MiniportInitialize cannot allocate sufficient shared memory for the NIC, it must release all resources it has already allocated and fail initialization.

If the NIC driver subsequently indicates receives with NdisMIndicateReceivePacket, it must allocate some number of buffer descriptors from buffer pool that map the NIC’s receive buffers in the shared memory block.

If the allocated memory is cached and, therefore, needs to be flushed on transfers, the miniport must call NdisAllocateBuffer to allocate an NDIS_BUFFER-type descriptor for the shared memory range. The NIC driver must call NdisFlushBuffer with this buffer descriptor to perform such a flush. In addition, such a miniport should call NdisMUpdateSharedMemory to ensure data integrity on all possible platforms.

Any miniport that calls NdisMAllocateSharedMemoryAsync or NdisMAllocateSharedMemory must release all outstanding allocations with one or more calls to NdisMFreeSharedMemory when its NIC is removed, that is, when its MiniportHalt function is called.

Callers of NdisMAllocateSharedMemory run at IRQL PASSIVE_LEVEL.

See Also

MiniportAllocateComplete, MiniportHalt, MiniportInitialize, NdisAllocateBuffer, NdisAllocateBufferPool, NdisCreateLookaheadBufferFromSharedMemory, NdisDestroyLookaheadBufferFromSharedMemory, NdisFlushBuffer, NdisGetCacheFillSize, NdisMAllocateMapRegisters, NdisMAllocateSharedMemoryAsync, NdisMFreeSharedMemory, NdisMIndicateReceivePacket, NdisMSetAttributes, NdisMSetAttributesEx, NdisMStartBufferPhysicalMapping, NdisMUpdateSharedMemory, NdisSystemProcessorCount


NdisMAllocateSharedMemoryAsync

NDIS_STATUS
NdisMAllocateSharedMemoryAsync(
IN NDIS_HANDLE MiniportAdapterHandle,
IN ULONG Length,
IN BOOLEAN Cached,
 IN PVOID Context
);

NdisMAllocateSharedMemoryAsync allocates additional memory shared between a miniport and its busmaster DMA NIC, usually when the miniport is running low on available NIC receive buffers.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Length
Specifies the number of bytes to allocate.
Cached
Specifies TRUE if the memory can be cached.
Context
Points to driver-detemined context to be passed to the MiniportAllocateComplete function when it is called.

Return Value

NdisMAllocateSharedMemoryAsync can return one of the following:

NDIS_STATUS_PENDING
The caller’s MiniportAllocateComplete function will be called with mapped virtual and device-accessible logical base addresses for the shared memory range, its Length and the Context pointer when the requested memory is allocated. Otherwise, MiniportAllocateComplete will be called with NULL pointers if the attempt to allocate shared memory fails.
NDIS_STATUS_FAILURE
The requested memory could not be allocated at this time. If NdisMAllocateSharedMemoryAsync returns this status, a subsequent call with the same parameters might succeed, depending on whether system resources have become available.

Comments

Drivers of busmaster DMA NICs call NdisMAllocateSharedMemoryAsync to dynamically allocate shared memory for transfer operations when high network traffic places excessive demands on the shared memory space that the driver allocated during initialization.

Such a NIC driver usually maintains one or more state variables to track the number of shared memory buffers available for incoming transfers. When the number of available buffers reaches a driver-determined low, the miniport calls NdisMAllocateSharedMemoryAsync to allocate more buffer space in shared memory. When the number of available buffers climbs to a driver-determined high, it calls NdisMFreeSharedMemory one or more times to release its preceding dynamic allocation(s).

Usually, such a driver retains the block of shared memory that its MiniportInitialize function allocated with NdisMAllocateSharedMemory until the NIC is removed, when its MiniportHalt function is called. This allocation is sufficient to handle an average demand for transfers through the NIC.

Any miniport that calls NdisMAllocateSharedMemoryAsync or NdisMAllocateSharedMemory must release all outstanding allocations with one or more calls to NdisMFreeSharedMemory when its NIC is removed.

Callers of NdisMAllocateSharedMemoryAsync run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportAllocateComplete, MiniportHalt, MiniportInitialize, NdisAllocateBuffer, NdisMAllocateSharedMemory, NdisMFreeSharedMemory


NdisMapFile

VOID
NdisMapFile(
OUT PNDIS_STATUS Status,
OUT PVOID *MappedBuffer,
IN NDIS_HANDLE FileHandle
);

NdisMapFile maps an already open file into a caller-accessible buffer if the file is currently unmapped.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the mapping operation, which can be one of the following:
NDIS_STATUS_SUCCESS
The caller has exclusive access to the file contents until NdisUnmapFile is called.
NDIS_STATUS_ALREADY_MAPPED
The caller cannot access the file contents at this time.
MappedBuffer
Points to a caller-supplied variable in which this function returns the base virtual address of the mapped file contents or NULL.
FileHandle
Specifies the handle returned by a preceding call to NdisOpenFile.

Comments

NdisMapFile associates (maps) a virtual address range with an opened file so the driver can access the file contents. NdisMapFile allows only one mapping of a particular file to be outstanding at any time. Consequently, a successful caller is given exclusive access to the file data until NdisUnmapFile or NdisCloseFile is called.

A NIC driver can map and unmap such an open file as necessary, using alternating calls to NdisMapFile and NdisUnmapFile. A call to NdisCloseFile releases the FileHandle and deallocates the buffer containing the file contents.

A NIC driver can call NdisMapFile only during initialization.

Callers of NdisMapFile run at IRQL <= DISPATCH_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisCloseFile, NdisOpenFile, NdisUnmapFile


NdisMArcIndicateReceive

VOID
NdisMArcIndicateReceive(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PUCHAR HeaderBuffer,
IN PUCHAR DataBuffer,
 IN UINT Length
 );

NdisMArcIndicateReceive notifies NDIS that an ARCNET packet (or some initial lookahead portion of the packet) has arrived so NDIS can forward the received packet to bound protocols.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
HeaderBuffer
Specifies the base virtual address of the packet header. The header buffer includes all bytes preceding the protocol ID.
DataBuffer
Specifies the virtual base virtual address of a buffer containing Length bytes of received packet data.
Length
Specifies the number of bytes in the buffer at DataBuffer.

Comments

A miniport calls NdisMArcIndicateReceive if it designates its NIC as of type NdisMediumArcnet878_3 or NdisMediumArcnetRaw in response to the OID_GEN_MEDIA_IN_USE query, unless the driver supports multipacket receives. Any miniport that supports multipacket receives calls NdisMIndicateReceivePacket with a packet array, rather than calling any of the medium-type-specific NdisM..IndicateReceive functions.

When a miniport calls NdisMArcIndicateReceive, NDIS passes a pointer to the header of the packet and a pointer to some or all of the data in the packet to the ProtocolReceive function(s) of bound protocol driver(s). Each protocol that receives such an indication can do the following:

  1. Inspect the header and data in the packet to decide whether the indication is of interest to the protocol’s client(s).

  2. If it is, copy as much of the header or data as is visible into protocol-allocated memory, using NdisMoveMemory.

  3. Get the remaining data, if any, for the indication by allocating a packet descriptor with sufficient chained buffer descriptors and calling NdisTransferData. The call to NdisTransferData causes the NIC driver’s MiniportTransferData function to copy the data into the protocol-supplied packet.

For any protocol driver that receives an indication through NdisMArcIndicateReceive, the HeaderBuffer and DataBuffer addresses are valid only during the current call to its ProtocolReceive function, and these buffers are read-only. A protocol driver cannot retain a pointer to the indicated packet with these associated buffers nor can it retain any pointers to these buffers for later use. Any data that a protocol driver needs from such a packet must be copied and saved during the receive indication.

The data in the header is the same as that received on the NIC. A miniport need not remove any headers or trailers from the data its NIC receives.

The transmitting driver adds padding to any packet that is too short for the requirements of the medium. The receiving miniport can include such padding in the data and length it subsequently indicates. Each bound protocol is responsible for detecting any such padding and ignoring it.

Callers of NdisMArcIndicateReceive run at IRQL DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportQueryInformation, MiniportTimer, MiniportTransferData, NdisMArcIndicateReceiveComplete, NdisMIndicateReceivePacket, NdisMoveMemory, NdisTransferData, ProtocolReceive


NdisMArcIndicateReceiveComplete

VOID
NdisMArcIndicateReceiveComplete(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMArcIndicateReceiveComplete notifies NDIS that an ARCNET packet, identified in a preceding call to NdisMArcIndicateReceive, has been fully transferred by the NIC so that NDIS can notify the appropriate bound protocols.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

After any ARCNET receive indication, a miniport eventually must call NdisMArcIndicateReceiveComplete to indicate the end of the transfer operation over the net, even if a particular packet is accepted by no bound protocol.

NdisMArcIndicateReceiveComplete both indicates the completion of a transfer operation for a receive and notifies any bound protocol that has already consumed the initial indication that it can begin postprocessing the received data.

When a miniport calls NdisMArcIndicateReceiveComplete, NDIS indicates the completion of the receive operation to each such protocol by calling its ProtocolReceiveComplete function.

ProtocolReceiveComplete functions need not operate under the severe time constraints of the corresponding ProtocolReceive functions. However, another receive indication to the ProtocolReceive function can come in while the ProtocolReceiveComplete function is running.

A miniport need not call NdisMArcIndicateReceiveComplete in one-to-one correspondence with its calls to NdisMArcIndicateReceive. That is, a NIC driver can issue a single receive-complete indication for some number of receive indications, particularly when network traffic is high. For example, when a NIC driver is continually indicating receives from the same source, it might call NdisMArcIndicateReceiveComplete once for every ten indications. On the other hand, a miniport might call NdisMArcIndicateReceiveComplete once for each indication when network traffic is low so that the driver can regain ownership of the memory it allocated for receives promptly.

Callers of NdisMArcIndicateReceiveComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMArcIndicateReceive, ProtocolReceive, ProtocolReceiveComplete


NdisMCancelTimer

VOID
NdisMCancelTimer(
IN PNDIS_MINIPORT_TIMER Timer,
OUT PBOOLEAN TimerCancelled
 );

NdisMCancelTimer cancels a preceding call to NdisMSetPeriodicTimer or cancels the immediately preceding call to NdisMSetTimer if the interval given to NdisMSetTimer has not yet expired.

Parameters

Timer
Points to a timer object, already initialized with NdisMInitializeTimer and set with NdisMSetPeriodicTimer or NdisMSetTimer.
TimerCancelled
Points to a caller-supplied variable, which this function sets to TRUE if the timer was queued and then cancelled, or to FALSE if the timer either was not set or has already fired.

Comments

All NdisM..Timer functions operate on an opaque timer object, for which a NIC driver must supply resident storage. The MiniportInitialize function must call NdisMInitializeTimer with a caller-supplied pointer to this storage, thereby associating a driver-supplied MiniportTimer function with the timer object. MiniportInitialize must call NdisMInitializeTimer before the driver passes the Timer pointer to any other NdisM..Timer function.

Drivers of NICs that do not generate interrupts have a polling MiniportTimer function to monitor the NIC state for incoming receives and for send completions.

Calls to the NdisMSet..Timer functions insert the timer object in the system timer queue. Calls to NdisMCancelTimer dequeue the timer object if it is currently queued. Only one instantiation of a particular timer object can be queued at any given moment.

A call to NdisMCancelTimer while the timer object designated by Timer is queued removes the timer object from the queue, thereby cancelling the execution of the associated MiniportTimer function.

A subsequent call to NdisMSetTimer or NdisMSetPeriodicTimer requeues the timer object so the MiniportTimer function again will be run when the given interval expires.

A call to NdisMCancelTimer can occur just after a timer has fired but before the associated MiniportTimer function is run. Usually, a MiniportTimer function sets a state variable immediately on entry to indicate that it is running. When another miniport function calls NdisMCancelTimer, it can check such a TimerCancelled variable on return from this call to determine whether its call to NdisMCancelTimer also cancelled execution of the MiniportTimer function.

A call to NdisMCancelTimer while the MiniportTimer function is running has no effect on the execution of MiniportTimer. It continues to run until it returns control.

Callers of NdisMCancelTimer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportTimer, NdisMInitializeTimer, NdisMSetPeriodicTimer, NdisMSetTimer


NdisMCloseLog

VOID
NdisMCloseLog(
IN NDIS_HANDLE LogHandle
);

NdisMCloseLog releases resources that were used for logging.

Parameters

LogHandle
Specifies the handle returned by NdisMCreateLog.

Comments

NdisMCloseLog closes the temporary log file allocated by NdisMCreateLog and releases all resources associated with the driver’s logging operations when a NIC driver is unloading.

Callers of NdisMCloseLog run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, NdisMCreateLog, NdisMFlushLog, NdisMWriteLogData


NdisMCompleteBufferPhysicalMapping

VOID
NdisMCompleteBufferPhysicalMapping(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PNDIS_BUFFER Buffer,
IN ULONG PhysicalMapRegister
 );

NdisMCompleteBufferPhysicalMapping flushes any data remaining in a busmaster NIC’s internal cache at the end of a transfer that was set up with NdisMStartBufferPhysicalMapping.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
Buffer
Points to the buffer descriptor, for a shared memory buffer previously mapped with NdisMStartBufferPhysicalMapping.
PhysicalMapRegister
Specifies the index of the map register used for the mapping. This value is identical to the PhysicalMapRegister index passed with the descriptor at Buffer in the preceding call to NdisMStartBufferPhysicalMapping.

Comments

PhysicalMapRegister is a zero-based value that falls within the index range for the map registers the miniport allocated during initialization with NdisMAllocateMapRegisters.

Callers of NdisMCompleteBufferPhysicalMapping run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportSend, MiniportSendPackets, MiniportTimer, MiniportWanSend, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync, NdisMStartBufferPhysicalMapping


NdisMCompleteDmaTransfer

VOID
NdisMCompleteDmaTransfer(
OUT PNDIS_STATUS Status,
IN PNDIS_HANDLE MiniportDmaHandle,
IN PNDIS_BUFFER Buffer,
IN ULONG Offset,
IN ULONG Length,
IN BOOLEAN WriteToDevice
 );

NdisMCompleteDmaTransfer indicates that a system DMA transfer operation has completed. It resets the system DMA controller in preparation for further DMA transfers.

Parameters

Status
Points to a caller-supplied variable in which this function returns the final status of the DMA transfer, which can be one of the following:
NDIS_STATUS_SUCCESS
The data has been transferred and flushed to host memory or to the device to maintain data integrity.
NDIS_STATUS_RESOURCES
The DMA controller was released but the data transfer might be incoherent.
MiniportDmaHandle
Specifies the handle returned when MiniportInitialize called NdisMRegisterDmaChannel.
Buffer
Points to the buffer descriptor previously passed to NdisMSetupDmaTransfer.
Offset
Specifies the byte offset at which the transfer began. This value also was passed to NdisMSetupDmaTransfer.
Length
Specifies the length in bytes of the transfer. This value also was passed to NdisMSetupDmaTransfer.
WriteToDevice
Specifies TRUE if the transfer was from the host to the NIC, as, for example, a send operation.

Comments

NdisMCompleteDmaTransfer must be called with WriteToDevice set to TRUE before the transferred data is considered present in the NIC’s memory. NdisMCompleteDmaTransfer must be called with WriteToDevice set to FALSE before the transferred data can be read from host memory.

Callers of NdisMCompleteDmaTransfer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportTimer, NdisMDeregisterDmaChannel, NdisMRegisterDmaChannel, NdisMSetupDmaTransfer


NdisMCreateLog

NDIS_STATUS
NdisMCreateLog(
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT Size,
OUT PNDIS_HANDLE LogHandle
);

NdisMCreateLog allocates and opens a log file in which a NIC miniport can write data to be displayed by a driver-dedicated Win32 application.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
Size
Specifies how many bytes to allocate for the log file. NDIS creates a temporary file that is not stored on disk.
LogHandle
Points to a caller-supplied variable in which this function returns a handle to the log file. This handle is a required parameter to the Ndis..Log functions that the miniport calls subsequently.

Return Value

NdisMCreateLog can return one of the following:

NDIS_STATUS_SUCCESS
The miniport can use the handle returned at LogHandle to write data to the NDIS-allocated log file.
NDIS_STATUS_RESOURCES
A log file of the specified size could not be allocated.
NDIS_STATUS_FAILURE
The driver already called NdisMCreateLog successfully.

Comments

A NIC miniport can call the NdisM..Log functions to provide any information the driver writer chooses. Whatever the miniport logs can be displayed by a driver-dedicated Win32 application. Such an application calls the Win32 function DeviceIoControl with IOCTL_NDIS_GET_LOG_DATA periodically to retrieve whatever the miniport has written to the log file. For example, an under-development miniport might write test data to be displayed by its corresponding application.

If NdisMCreateLog returns NDIS_STATUS_RESOURCES, the driver can adjust the original Size down and try calling this function again. However, a miniport cannot call NdisMCreateLog to create more than one log file after a call succeeds.

Whatever size of log file is allocated, subsequent calls to NdisMWriteLogData store data in this file, which is treated as a circular buffer. That is, a sequence of calls to NdisMWriteLogData eventually overwrites the data originally written to the log file.

Callers of NdisMCreateLog run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMCloseLog, NdisMFlushLog, NdisMWriteLogData


NdisMDeregisterAdapterShutdownHandler

VOID
NdisMDeregisterAdapterShutdownHandler(
IN NDIS_HANDLE MiniportHandle
);

NdisMDeregisterAdapterShutdownHandler removes a NIC-driver-supplied MiniportShutdown function from the list of registered shutdown handlers to be called.

Parameters

MiniportHandle
Specifies the handle originally input to MiniportInitialize.

Comments

A miniport driver that called NdisMRegisterAdapterShutdownHandler from MiniportInitialize must call NdisMDeregisterAdapterShutdownHandler when it is cleaning up the resources it allocated before being unloaded.

MiniportInitialize also should call NdisMDeregisterAdapterShutdownHandler if the driver has already registered the MiniportShutdown function but MiniportInitialize will fail the initialization.

Callers of NdisMDeregisterAdapterShutdownHandler run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, MiniportInitialize, MiniportShutdown, NdisMRegisterAdapterShutdownHandler


NdisMDeregisterDmaChannel

VOID
NdisMDeregisterDmaChannel(
IN PNDIS_HANDLE MiniportDmaHandle
 );

NdisMDeregisterDmaChannel releases a miniport driver’s claim on a DMA channel for its NIC.

Parameters

MiniportDmaHandle
Specifies the handle returned by NdisMRegisterDmaChannel.

Comments

The caller should consider MiniportDmaHandle invalid as soon as it is passed to NdisMDeregisterDmaChannel. This function releases the NIC’s claim on the DMA channel in the registry.

NdisMDeregisterDmaChannel can be called only from a NIC driver’s MiniportInitialize and MiniportHalt functions.

Callers of NdisMDeregisterDmaChannel run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NdisMCompleteDmaTransfer, NdisMRegisterDmaChannel, NdisMSetupDmaTransfer


NdisMDeregisterInterrupt

VOID
NdisMDeregisterInterrupt(
IN PNDIS_MINIPORT_INTERRUPT Interrupt
 );

NdisMDeregisterInterrupt stops a miniport’s ISR from being called to handle NIC interrupts.

Parameters

Interrupt
Points to an interrupt object previously registered with NdisMRegisterInterrupt.

Comments

NdisMDeregisterInterrupt releases the association between the initialized interrupt object at Interrupt and the caller’s MiniportISR function. It also releases the driver’s claim on the interrupt vector or level in the registry.

NdisMDeregisterInterrupt can be called from the MiniportInitialize or MiniportHalt function only if MiniportInitialize previously made a successful call to NdisMRegisterInterrupt.

The miniport should disable its NIC from generating interrupts before it calls NdisMDeregisterInterrupt. After NdisMDeregisterInterrupt returns control, the miniport cannot call NdisMSynchronizeWithInterrupt.

Callers of NdisMDeregisterInterrupt run at IRQL PASSIVE_LEVEL.

See Also

MiniportDisableInterrupt, MiniportHalt, MiniportInitialize, MiniportISR, NdisMRegisterInterrupt, NdisMSynchronizeWithInterrupt


NdisMDeregisterIoPortRange

VOID
NdisMDeregisterIoPortRange(
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT InitialPort,
 IN UINT NumberOfPorts,
IN PVOID PortOffset
 );

NdisMDeregisterIoPortRange releases a mapping that was set up with NdisMRegisterIoPortRange during driver initialization.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
InitialPort
Specifies the bus-relative address of the first port in the range of ports.
NumberOfPorts
Specifies the number of ports in the range.
PortOffset
Specifies the mapped base port address returned by NdisMRegisterIoPortRange.

Comments

The miniport must pass the same InitialPort and NumberOfPorts to NdisMDeregisterIoPortRange that were passed when MiniportInitialize called NdisMRegisterIoPortRange to get the mapped PortOffset value. That is, a miniport cannot call NdisMDeregisterIoPortRange to release a subrange of a mapped port range.

NdisMDeregisterIoPortRange can be called from the MiniportInitialize or MiniportHalt functions only if MiniportInitialize previously made a successful call to NdisMRegisterIoPortRange.

NdisMDeregisterIoPortRange also releases the driver’s claim on the I/O port range in the registry.

After it calls NdisMRegisterIoPortRange, the miniport can no longer access the NIC’s port range with calls to the NdisRaw..Port Xxx functions.

Callers of NdisMDeregisterIoPortRange run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NdisMRegisterIoPortRange


NdisMEthIndicateReceive

VOID
NdisMEthIndicateReceive(
 IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE MiniportReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookaheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
 );

NdisMEthIndicateReceive notifies NDIS that an Ethernet packet (or some initial lookahead portion of the packet) has arrived so NDIS can forward the packet to bound protocols.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
MiniportReceiveContext
Specifies a context handle supplied by the caller. NDIS returns this handle as an input parameter to the MiniportTransferData function if it is called. The miniport can use the context area designated by this handle to determine which packet, on which NIC, is being received.
HeaderBuffer
Specifies the base virtual address of the buffered packet header.
HeaderBufferSize
Specifies the size in bytes of the packet header.
LookaheadBuffer
Specifies the base virtual address of a buffer containing the initial LookaheadBufferSize bytes of the data in the packet.
LookaheadBufferSize
Specifies the size in bytes of the lookahead buffer. This value must be at least as large as the lookahead size set when MiniportSetInformation was called with OID_GEN_CURRENT_LOOKAHEAD, or as large as the packet, whichever is smaller. When the packet is smaller than the lookahead size, the lookahead buffer contains the entire frame.

A miniport can (and should) set LookaheadBufferSize to something larger than the minimum required if sufficient received data is available on its NIC.

PacketSize
Specifies the size in bytes of the received packet data. This value does not include the HeaderBufferSize.

When PacketSize is larger than the given LookaheadBufferSize, a protocol driver call to NdisTransferData causes a subsequent call to the MiniportTransferData function, which transfers the remaining data in the packet.

Comments

A miniport calls NdisMEthIndicateReceive if it designates its NIC as of type NdisMedium802_3 or NdisMediumDix in response to the OID_GEN_MEDIA_IN_USE query, unless the miniport indicates receives with NdisMIndicateReceivePacket.

When a miniport calls NdisMEthIndicateReceive, NDIS passes a pointer to the header of the packet and a pointer to some or all of the data in the packet to the ProtocolReceive function(s) of bound protocol driver(s). Each protocol that receives the indication can do the following:

  1. Inspect the header and data in the packet to decide whether the indication is of interest to the protocol’s client(s).

  2. If it is, copy as much of the header or data as is visible into protocol-allocated memory with NdisMoveMemory.

  3. Get the remaining data, if any, for the indication by allocating a packet descriptor with sufficient chained buffer descriptors and calling NdisTransferData. The call to NdisTransferData causes the NIC driver’s MiniportTransferData function to copy the data into the protocol-supplied packet.

For any protocol driver that receives an indication through NdisMEthIndicateReceive, the HeaderBuffer and LookaheadBuffer addresses are valid only during the current call to its ProtocolReceive function, and these buffers are read-only. A protocol driver cannot retain a pointer to the indicated packet with these associated buffers nor can it retain any pointers to these buffers for later use. Any data that a protocol driver needs from such a packet must be copied and saved during the receive indication.

The data in the header is the same as that received on the NIC. A miniport need not remove any headers or trailers from the data its NIC receives.

The transmitting driver adds padding to any packet that is too short for the requirements of the medium. The receiving miniport can include such padding in the data and length it subsequently indicates. Each bound protocol is responsible for detecting any such padding and ignoring it.

Callers of NdisMEthIndicateReceive run at IRQL DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportQueryInformation, MiniportSetInformation, MiniportTimer, MiniportTransferData, NdisMEthIndicateReceiveComplete, NdisMIndicateReceivePacket, NdisMoveMemory, NdisTransferData, ProtocolReceive


NdisMEthIndicateReceiveComplete

VOID
NdisMEthIndicateReceiveComplete(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMEthIndicateReceiveComplete notifies NDIS that an Ethernet receive packet, identified in a preceding call to NdisMEthIndicateReceive, has been fully transferred by the NIC so that NDIS can notify the appropriate bound protocols.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

After any Ethernet receive indication, a miniport eventually must call NdisMEthIndicateReceiveComplete to indicate the end of the transfer operation over the net, even if a particular packet is accepted by no bound protocol.

NdisMEthIndicateReceiveComplete both indicates the completion of a transfer operation for a receive and notifies any bound protocol that has already consumed the initial indication that it can begin postprocessing the received data.

When a miniport calls NdisMEthIndicateReceiveComplete, NDIS indicates the completion of the receive operation to each such protocol by calling its ProtocolReceiveComplete function.

ProtocolReceiveComplete functions need not operate under the severe time constraints of the corresponding ProtocolReceive functions. However, another receive indication to the ProtocolReceive function can come in while the ProtocolReceiveComplete function is running.

A miniport need not call NdisMEthIndicateReceiveComplete in one-to-one correspondence with its calls to NdisMEthIndicateReceive. That is, a NIC driver can issue a single receive-complete indication for some number of receive indications, particularly when network traffic is high. For example, when a miniport is continually indicating receives from the same source, it might call NdisMEthIndicateReceiveComplete once for every ten indications. On the other hand, a miniport might call NdisMEthIndicateReceiveComplete once for each indication when network traffic is low so that the driver can regain ownership of the memory it allocated for receives promptly.

Callers of NdisMEthIndicateReceiveComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMEthIndicateReceive, ProtocolReceive, ProtocolReceiveComplete


NdisMFddiIndicateReceive

VOID
NdisMFddiIndicateReceive(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE MiniportReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookaheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
 );

NdisMFddiIndicateReceive notifies NDIS that an FDDI packet (or some initial lookahead portion of the packet) has arrived so NDIS can forward the packet to bound protocols.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
MiniportReceiveContext
Specifies a context handle supplied by the caller. NDIS returns this handle as an input parameter to the MiniportTransferData function if it is called. The miniport can use the context area designated by this handle to determine which packet, on which NIC, is being received.
HeaderBuffer
Specifies the base virtual address of the buffered packet header.
HeaderBufferSize
Specifies the size in bytes of the packet header.
LookaheadBuffer
Specifies the base virtual address of a buffer containing the initial LookaheadBufferSize bytes of the data in the packet.
LookaheadBufferSize
Specifies the size in bytes of the lookahead buffer. This value must be at least as large as the lookahead size set when MiniportSetInformation was called with OID_GEN_CURRENT_LOOKAHEAD, or as large as the packet, whichever is smaller. When the packet is smaller than the lookahead size, the lookahead buffer contains the entire frame.

A miniport can (and should) set LookaheadBufferSize to something larger than the minimum required if sufficient received data is available on its NIC.

PacketSize
Specifies the size in bytes of the received packet data. This value does not include the HeaderBufferSize.

When PacketSize is larger than LookaheadBufferSize, a protocol driver call to NdisTransferData causes a subsequent call to the MiniportTransferData function, which transfers the remaining data in the packet.

Comments

A miniport calls NdisMFddiIndicateReceive if it designates its NIC as of type NdisMediumFddi in response to the OID_GEN_MEDIA_IN_USE query, unless the miniport indicates receives with NdisMIndicateReceivePacket.

When a miniport calls NdisMFddiIndicateReceive, NDIS passes a pointer to the header of the packet and a pointer to some or all of the data in the packet to the ProtocolReceive function(s) of bound protocol driver(s). Each protocol that receives the indication can do the following:

  1. Inspect the header and data in the packet to decide whether the indication is of interest to the protocol’s client(s).

  2. If it is, copy as much of the header or data as is visible into protocol-allocated memory with NdisMoveMemory.

  3. Get the remaining data, if any, for the indication by allocating a packet descriptor with sufficient chained buffer descriptors and calling NdisTransferData. The call to NdisTransferData causes the NIC driver’s MiniportTransferData function to copy the data into the protocol-supplied packet.

For any protocol driver that receives an indiation through NdisMFddiIndicateReceive, the HeaderBuffer and LookaheadBuffer addresses are valid only during the current call to its ProtocolReceive function, and these buffers are read-only. A protocol driver cannot retain a pointer to the indicated packet with these associated buffers nor can it retain any pointers to these buffers for later use. Any data that a protocol driver needs from such a packet must be copied and saved during the receive indication.

The data in the header is the same as that received on the NIC. A miniport need not remove any headers or trailers from the data its NIC receives.

The transmitting driver adds padding to any packet that is too short for the requirements of the medium. The receiving miniport can include such padding in the data and length it subsequently indicates. Each bound protocol is responsible for detecting any such padding and ignoring it.

Callers of NdisMFddiIndicateReceive run at IRQL DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportQueryInformation, MiniportSetInformation, MiniportTimer, MiniportTransferData, NdisMFddiIndicateReceiveComplete, NdisMIndicateReceivePacket, NdisTransferData, ProtocolReceive


NdisMFddiIndicateReceiveComplete

VOID
NdisMFddiIndicateReceiveComplete(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMFddiIndicateReceiveComplete notifies NDIS that an FDDI receive packet, identified in a preceding call to NdisMFddiIndicateReceive, has been fully transferred by the NIC so that NDIS can notify the appropriate bound protocol drivers.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

After any FDDI receive indication, a miniport eventually must call NdisMFddiIndicateReceiveComplete to indicate the end of the transfer operation over the net, even if a particular packet is accepted by no bound protocol.

NdisMFddiIndicateReceiveComplete both indicates the completion of a transfer operation for a receive and notifies any bound protocol that has already consumed the initial indication that it can begin postprocessing the received data.

When a miniport driver calls NdisMFddiIndicateReceiveComplete, NDIS indicates the completion of the receive operation to each such protocol by calling its ProtocolReceiveComplete function.

ProtocolReceiveComplete functions need not operate under the severe time constraints of the corresponding ProtocolReceive functions. However, another receive indication to the ProtocolReceive function can come in while the ProtocolReceiveComplete function is running.

A miniport need not call NdisMFddiIndicateReceiveComplete in one-to-one correspondence with its calls to NdisMFddiIndicateReceive. That is, a NIC driver can issue a single receive-complete indication for some number of receive indications, particularly when network traffic is high. For example, when a miniport is continually indicating receives from the same source, it might call NdisMFddiIndicateReceiveComplete once for every ten indications. On the other hand, a miniport might call NdisMFddiIndicateReceiveComplete once for each indication when network traffic is low so that the driver can regain ownership of the memory it allocated for receives promptly.

Callers of NdisMFddiIndicateReceiveComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMFddiIndicateReceive, ProtocolReceive, ProtocolReceiveComplete


NdisMFlushLog

VOID
NdisMFlushLog(
IN NDIS_HANDLE LogHandle
);

NdisMFlushLog clears the log file.

Parameters

LogHandle
Specifies the handle returned by NdisMCreateLog.

Comments

NdisMFlushLog resets the position pointers within the log file to the start of the file.

The driver must release any spin lock it is holding before calling NdisMFlushLog.

An NDIS intermediate driver cannot call NdisMFlushLog from any of its ProtocolXxx functions. Such a driver must call NdisIMSwitchToMiniport and make this call from a miniport context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback so that its driver-supplied MiniportCallback function can call NdisMFlushLog.

Callers of NdisMFlushLog can be running at IRQL <= DISPATCH_LEVEL.

See Also

NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMCloseLog, NdisMCreateLog, NdisMWriteLogData, NdisReleaseSpinLock


NdisMFreeMapRegisters

VOID
NdisMFreeMapRegisters(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMFreeMapRegisters releases a set of map registers that were allocated during initialization with NdisMAllocateMapRegisters by a busmaster DMA NIC driver.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

If it has already made a successful call to NdisMAllocateMapRegisters, the NIC driver of a busmaster DMA device calls NdisMFreeMapRegisters if any of the following occurs:

  • Its MiniportInitialize function is unable to initialize the NIC, so this function must release all existing claims on hardware resources for that NIC before it returns control.

  • The NIC for which the miniport allocated the map registers is being removed.

  • The driver is being unloaded, either because the system is shutting down or because the user reconfigured the network components used in the machine.

As soon as it calls NdisMFreeMapRegisters, the driver of a busmaster DMA NIC can no longer use the map registers it previously allocated.

NdisMFreeMapRegisters can be called only from a NIC driver’s MiniportInitialize and MiniportHalt functions.

Callers of NdisMFreeMapRegisters run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NdisMAllocateMapRegisters, NdisMFreeSharedMemory


NdisMFreeSharedMemory

VOID
NdisMFreeSharedMemory(
IN NDIS_HANDLE MiniportAdapterHandle,
IN ULONG Length,
IN BOOLEAN Cached,
IN PVOID VirtualAddress,
IN NDIS_PHYSICAL_ADDRESS PhysicalAddress
 );

NdisMFreeSharedMemory frees memory that was previously allocated by NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync by the driver of a busmaster DMA NIC.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Length
Specifies the number of bytes originally allocated.
Cached
Specifies TRUE if the original allocation was cacheable.
VirtualAddress
Specifies the base virtual address returned by NdisMAllocateSharedMemory(Async).
PhysicalAddress
Specifies the corresponding physical address returned by NdisMAllocateSharedMemory(Async).

Comments

If it has already made a successful call to NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync, the NIC driver of a busmaster DMA device calls NdisMFreeSharedMemory if any of the following occurs:

  • Its MiniportInitialize function is unable to initialize the NIC, so this function must release all existing claims on hardware resources for that NIC before it returns control.

  • The NIC for which the miniport allocated the memory is being removed.

  • The driver is being unloaded, whether because the system is shutting down or because the user reconfigured the network components used in the machine.

  • The driver allocated additional shared memory with NdisMAllocateSharedMemoryAsync when I/O demand on its NIC was high but network traffic has now subsided to an average level.

A miniport cannot call NdisMFreeSharedMemory to free a subrange within an allocated shared memory range. The parameters passed to NdisMFreeSharedMemory must match exactly those that were passed to NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync.

NdisMFreeSharedMemory cannot be called from a MiniportShutdown function

Callers of NdisMFreeSharedMemory run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportAllocateComplete, MiniportHalt, MiniportInitialize, MiniportShutdown, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync, NdisMFreeMapRegisters


NdisMIndicateReceivePacket

VOID
NdisMIndicateReceivePacket(
 IN NDIS_HANDLE MiniportAdapterHandle,
IN PPNDIS_PACKET ReceivePackets,
IN UINT NumberOfPackets
);

NdisMIndicateReceivePacket notifies NDIS that an array of received packets is available to be forwarded to the appropriate bound protocol driver(s).

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
ReceivePackets
Points to an array of packet descriptor pointers, with each descriptor set up by the caller to specify the received data.
NumberOfPackets
Specifies how many pointers are in the array at ReceivePackets. The value must be at least one.

Comments

Drivers of busmaster DMA NICs are most likely to show significant gains in performance by making multipacket receive indications with NdisMIndicateReceivePacket. However, drivers that indicate out-of-band data with receive packets, such as packet priority and/or timestamps, also can call this function with a single packet at a time.

When a miniport calls NdisMIndicateReceivePacket, NDIS passes each pointer at ReceivePackets separately, in the miniport-determined order, to the ProtocolReceivePacket function(s) of bound protocol(s) that export this function. To other bound protocols, NDIS passes each packet pointer to the ProtocolReceive function(s).

If the miniport did not designate its NIC’s medium as a type for which the system supplies a filter package in response to the OID_GEN_MEDIA_IN_USE query, NDIS forwards that driver’s receive indications to all bound protocol drivers that export a ProtocolReceivePackets function. Otherwise, NDIS automatically applies the appropriate filter library to the miniport’s receive indications.

Any caller of NdisMIndicateReceivePacket must first set up the packet array, as follows:

  • Each element is the pointer to a packet descriptor, which the caller must allocate from packet pool. Each packet descriptor can have at most 16 bytes in its ProtocolReserved section.

  • All buffer descriptors chained to such a packet descriptor must be allocated from buffer pool. The miniport must call NdisAdjustBufferLength with any buffer descriptor mapping a receive buffer on the NIC that contains less received data than the full range of the receive buffer so that the buffer descriptor will map only the received data for the indication. (The NIC driver must readjust the mapping with NdisAdjustBufferLength when it regains ownership of such a buffer descriptor, as well.)

  • If the miniport indicates timestamps for received packets, it must set the TimeReceived and/or TimeSent members in the NDIS_PACKET_OOB_DATA associated with the packet descriptor using the NDIS_SET_PACKET_TIME_RECEIVED and/or NDIS_SET_PACKET_TIME_SENT macros. It can call NdisGetCurrentSystemTime once to set the receive timestamp for all packets of a particular packet array.

  • The HeaderSize in the out-of-band data block must match the header size of each received packet for the medium. For example, a driver that selects Ethernet as its preferred medium at initialization would initialize the NDIS_PACKET_OOB_DATA block HeaderSize to 14 when it allocated the packet descriptor.

  • If the driver indicates additional out-of-band information with receives, it must set the SizeMediaSpecificInfo to the number of bytes of information supplied in the caller-allocated buffer at MediaSpecificInformation. The miniport can use the NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO macro to set these values. Otherwise, SizeMediaSpecificInfo should be zero, and MediaSpecificInformation should be NULL.

  • The miniport can set the Status member of the out-of-band data block to NDIS_STATUS_RESOURCES if it needs to retain ownership of the packet descriptor and buffer descriptors of some element(s) in a particular indication. Setting this status for a packet implicitly sets the same status for all subsequent packets of the indicated array.

    Setting NDIS_STATUS_RESOURCES forces NDIS to indicate each such packet, one at a time, to bound protocols’ ProtocolReceive functions, thereby forcing each protocol to copy the packet data and release each packet to be returned back to the miniport. Otherwise, the call to NdisMIndicateReceivePacket gives interested protocols exclusive, read-only access to the buffers chained to each packet until either of the following occurs:

    • The Status in the out-of-band data block is set to NDIS_STATUS_SUCCESS on return from NdisMIndicateReceivePacket. In this case, the miniport regains ownership of the packet descriptor and all buffers chained to the packet. It can prepare these descriptors immediately for reuse in subsequent receive indications.

    • The Status is set to NDIS_STATUS_PENDING on return from NdisMIndicateReceivePacket. In this case, the driver’s MiniportReturnPacket function will be called when the miniport regains ownership of the packet and buffer descriptors. It can then prepare these descriptors for reuse in subsequent receive indications.

The miniport should use NDIS_GET_PACKET_STATUS to determine whether it has regained ownership of indicated packets when NdisMIndicateReceivePackets returns control.

When the miniport regains ownership of an indicated packet descriptor, it can prepare the associated out-of-band data block for reuse by passing the pointer returned by NDIS_OOB_DATA_FROM_PACKET to NdisZeroMemory. As an alternative, the miniport can simply reset the relevant member(s) at the subsequent receive with the NDIS_SET_PACKET_XXX macro(s) or with the pointer returned by NDIS_OOB_DATA_FROM_PACKET.

A miniport must not pass a packet descriptor pointer to NdisZeroMemory. Doing this destroys the packet descriptor, rendering it unusable for subsequent indications. To clear the associated out-of-band data block, the driver must pass the pointer returned by NDIS_OOB_DATA_FROM_PACKET.

Packet arrays passed to NdisMIndicateReceivePacket can be allocated on the stack, which cannot be paged out in the NDIS library’s receive-indication code path.

When NDIS calls the ProtocolReceive function of a driver that exports a ProtocolReceivePacket function, ProtocolReceive can call NdisQueryReceiveInformation to retrieve the out-of-band information associated with an incoming packet that was indicated as part of a packet array.

Any miniport that calls NdisMIndicateReceivePacket must indicate full packets. Consequently, such a driver has no MiniportTransferData function.

Callers of NdisMIndicateReceivePacket can run at IRQL <= DISPATCH_LEVEL, but usually callers are running at DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportQueryInformation, MiniportReturnPacket, MiniportTimer, NdisAdjustBufferLength, NdisAllocateBuffer, NdisAllocatePacket, NdisChainBufferAtBack, NdisChainBufferAtFront, NdisGetCurrentSystemTime, NDIS_GET_PACKET_STATUS, NdisMAllocateSharedMemoryAsync, NDIS_OOB_DATA_FROM_PACKET, NdisQueryReceiveInformation, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_SENT, NdisZeroMemory, ProtocolReceive, ProtocolReceivePacket


NdisMIndicateStatus

VOID
NdisMIndicateStatus(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuffer,
IN UINT StatusBufferSize
 );

NdisMIndicateStatus indicates changes in the status of a NIC to higher-level NDIS drivers.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
GeneralStatus
Specifies the NDIS_STATUS_XXX value that indicates the general change in status for the NIC.
StatusBuffer
Points to a caller-allocated buffer containing data that is medium-specific and dependent on the value of GeneralStatus. It indicates detailed information about the change in status.
StatusBufferSize
Specifies the size in bytes of the buffer at StatusBuffer.

Comments

When a miniport calls NdisMIndicateStatus, NDIS calls each bound protocol’s ProtocolStatus function. This allows a bound protocol driver or, possibly, the configuration manager to log the change in status of an underlying NIC or to take corrective action. For example, a protocol might call NdisReset, depending on the NDIS_STATUS_XXX indicated.

When a miniport calls NdisMIndicateStatus to report a change in NIC status, NDIS can call the MiniportReset function to try restoring the NIC to a working condition. In these circumstances, NDIS can call bound protocols only with NDIS_STATUS_RESET_START and later with NDIS_STATUS_RESET_END, rather than with the GeneralStatus indicated by the miniport.

NdisMIndicateStatus provides two pieces of information:

  • An overall status value indicating the general change in status.

  • A specific reason for the general change, which can be network-specific. For example, ring-insertion failures are particular to Token Ring (802.5) networks, but are irrelevant to Ethernet (802.3) NICs and their drivers.

A miniport also can call NdisMIndicateStatus to indicate problems such as cables that have been accidentally removed and then reinserted, or a ring that has temporarily failed. For example, suppose both the following conditions occur:

  • The miniport calls NdisMIndicateStatus with NDIS_STATUS_RING_STATUS.

  • The StatusBuffer contains one of the following status values:
    NDIS_RING_LOBE_WIRE_FAULT
    NDIS_RING_HARD_ERROR
    NDIS_RING_SIGNAL_LOSS

After such a change in status has been signaled with an indication, any associated thresholds or statistics counters remain unchanged.

Callers of NdisMIndicateStatus run at IRQL DISPATCH_LEVEL.

WAN Miniport Calls to NdisMIndicateStatus

Most WAN NIC drivers indicate status with some special status codes and buffer contents. These status indications are generated by WAN NIC drivers, and NDIS protocol drivers bound to such a miniport can ignore these indications. However, processing these indications correctly usually results in improved performance for protocols and for WAN NIC drivers.

The following four types of WAN-specific indications can be sent to bound protocol drivers with NdisMIndicateStatus:

  1. Line up

  2. Line down

  3. Fragment

  4. TAPI line event

NDIS_STATUS_WAN_LINE_UP

A WAN miniport makes a line-up indication whenever a new link becomes active. Until this occurs, the NIC will accept frames and let them succeed or fail, but it is unlikely that they will actually be received by any remote. Until this occurs, protocols should reduce their timers and retry counts so as to quickly fail any outgoing connection attempts.

The WAN miniport makes this indication with NdisMIndicateStatus before it returns from the OID_TAPI_GET_ID request.

The status code for the line-up indication is NDIS_STATUS_WAN_LINE_UP. The buffer at StatusBuffer is formatted as an NDIS_MAC_LINE_UP structure.

NDIS_STATUS_WAN_LINE_DOWN

A WAN miniport makes a line-down indication whenever a link goes down. When this occurs, bound protocols should reduce their timers and retry counts until the next line-up indication.

The status code for the line-down indication is NDIS_STATUS_WAN_LINE_DOWN. The buffer at StatusBuffer is formatted as an NDIS_MAC_LINE_DOWN structure.

NDIS_STATUS_WAN_FRAGMENT

A WAN miniport makes a fragment indication whenever it receives a partial packet from the remote node. When this occurs, a bound protocol should send frames to the remote that will notify it of this situation, rather than waiting for a timeout to occur.

The status code for the fragment indication is NDIS_STATUS_WAN_FRAGMENT. The buffer at StatusBuffer is formatted as an NDIS_MAC_FRAGMENT structure.

NDISWAN monitors dropped packets by counting the number of fragment indications on each link.

NDIS_TAPI_EVENT

A WAN miniport must use a GeneralStatus value of NDIS_STATUS_TAPI_INDICATION for TAPI status indications. The buffer at StatusBuffer is formatted as an NDIS_TAPI_EVENT structure.

See Also

MiniportInitialize, MiniportReset, NDIS_MAC_FRAGMENT, NDIS_MAC_LINE_DOWN, NDIS_MAC_LINE_UP, NdisMIndicateStatusComplete, NDIS_TAPI_EVENT, ProtocolStatus


NdisMIndicateStatusComplete

VOID
NdisMIndicateStatusComplete(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMIndicateStatusComplete notifies NDIS (or NDISWAN) that the NIC driver, which has called NdisMIndicateStatus one or more times to indicate hardware anomalies or runtime state changes in the NIC, is ready to resume normal device I/O operations.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

When a miniport calls NdisMIndicateStatusComplete, NDIS calls the ProtocolStatusComplete function(s) of bound protocol(s) that were previously notified of the immediately preceding state change(s) in the underlying NIC with calls to their ProtocolStatus functions.

A miniport eventually must call NdisMIndicateStatusComplete after it calls NdisMIndicateStatus. However, a miniport need not deliver NdisMIndicateStatusComplete indications in one-to-one correspondence with its NdisMIndicateStatus indications. It can choose to issue a single status-complete indication for several status indications that have occurred, particularly when network traffic is high.

A protocol driver writer should assume that interrupts are enabled on the NIC while the ProtocolStatusComplete function is running. A protocol driver writer also should assume that the ProtocolStatus function is re-entrant while the ProtocolStatusComplete function is running, particularly on an SMP platform.

Callers of NdisMIndicateStatusComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMIndicateStatus, ProtocolStatusComplete


NdisMInitializeTimer

VOID
NdisMInitializeTimer(
 IN OUT PNDIS_MINIPORT_TIMER Timer,
IN NDIS_HANDLE MiniportAdapterHandle,
 IN PNDIS_TIMER_FUNCTION TimerFunction,
 IN PVOID FunctionContext
 );

NdisMInitializeTimer initializes a timer object associated with a driver’s MiniportTimer function.

Parameters

Timer
Points to caller-supplied, resident storage for a timer object. The timer object is opaque to the caller, but, when it has been initialized, this pointer is a required parameter to the NdisM..Timer functions.
MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
TimerFunction
Specifies the entry point of a caller-supplied MiniportTimer function.
FunctionContext
Points to a caller-supplied context area to be passed to the given MiniportTimer function when it is called.

Comments

The MiniportInitialize function must call NdisMInitializeTimer to set up the driver’s MiniportTimer function(s), if any. MiniportInitialize must call NdisMInitializeTimer before the driver passes the MiniportTimer pointer to any other NdisM..Timer function.

Drivers of NICs that do not generate interrupts initialize at least one timer object for a polling MiniportTimer function that monitors the NIC state for incoming receives and for send completions.

Calls to the NdisMSet..Timer functions insert the timer object in the system timer queue. Calls to NdisMCancelTimer dequeue the timer object if it is currently queued. Only one instantiation of a particular timer object can be queued at any given moment.

When a driver calls NdisMSetTimer, the timer object at Timer is queued until the interval specified in the call expires. When it does, the timer object is dequeued and the caller-supplied TimerFunction is run once at IRQL DISPATCH_LEVEL as soon as a processor becomes available. When a driver calls NdisMSetPeriodicTimer, the timer object is dequeued and the caller-supplied TimerFunction is run when the given interval expires, but the timer object is automatically requeued for the same fixed interval so the MiniportTimer function runs periodically, also at IRQL DISPATCH_LEVEL.

MiniportInitialize can call NdisMInitializeTimer more than once to initialize more than one timer object, each associated with a distinct MiniportTimer function. For example, a NIC driver might have one MiniportTimer function that polls the NIC state periodically and another that the miniport uses only to time-out a particular operation on its NIC. Each such MiniportTimer function has different functionality, and each such function should be associated with a different timer object by calling NdisMInitializeTimer from MiniportInitialize.

Callers of NdisMInitializeTimer run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, MiniportTimer, NdisMCancelTimer, NdisMSetPeriodicTimer, NdisMSetTimer


NdisMInitializeWrapper

VOID
NdisMInitializeWrapper(
OUT PNDIS_HANDLE NdisWrapperHandle,
IN PVOID SystemSpecific1,
 IN PVOID SystemSpecific2,
 IN PVOID SystemSpecific3
 );

NdisMInitializeWrapper notifies NDIS that a new miniport is initializing.

Parameters

NdisWrapperHandle
Points to a caller-supplied variable in which NDIS returns a handle that represents itself.
SystemSpecific1
Specifies a pointer of a system-specific type.

For Windows NT drivers, this is a pointer to an opaque driver object input to the miniport’s DriverEntry function.

SystemSpecific2
Specifies a pointer of a system-specific type.

For Windows NT drivers, this is a pointer to an opaque registry path input to the miniport’s DriverEntry function.

SystemSpecific3
Specifies a pointer of a system-specific type.

For Windows NT drivers, this pointer should be set to NULL when the miniport calls NdisMInitializeWrapper.

Comments

A NIC driver or intermediate driver must call NdisMInitializeWrapper before it calls any other Ndis Xxx function. Usually, this call is the initial action of any NIC driver’s DriverEntry function, which calls NdisMRegisterMiniport or NdisIMRegisterLayeredMiniport with the returned NdisWrapperHandle next. NDIS intermediate drivers also must call NdisMIntializeWrapper from the DriverEntry function and, then, call NdisMRegisterMiniport or NdisIMRegisterLayeredMiniport with the returned NdisWrapperHandle.

Such a driver’s subsequent calls to Ndis Xxx initialization and configuration functions must pass the NdisWrapperHandle as a parameter. Consequently, it is an input parameter to the MiniportInitialize function.

Callers of NdisMInitializeWrapper run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Miniport Drivers, MiniportInitialize, NdisIMRegisterLayeredMiniport, NdisMRegisterMiniport, NdisTerminateWrapper


NdisMMapIoSpace

NDIS_STATUS
NdisMMapIoSpace(
OUT PVOID *VirtualAddress,
IN NDIS_HANDLE MiniportAdapterHandle,
 IN NDIS_PHYSICAL_ADDRESS PhysicalAddress,
IN UINT Length
 );

NdisMMapIoSpace maps a given bus-relative “physical” range of device RAM or registers onto a system-space virtual range.

Parameters

Virtual Address
Points to a caller-supplied variable that is set to the converted virtual address if the call is successful.
MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
PhysicalAddress
Specifies the bus-relative base physical address of the device memory range to be mapped.
Length
Specifies the number of bytes to be mapped.

Return Value

NdisMMapIoSpace can return any of the following status values:

NDIS_STATUS_SUCCESS
The device memory range was mapped successfully so the value at VirtualAddress is valid and the mapped range has been claimed in the registry for the NIC.
NDIS_STATUS_RESOURCE_CONFLICT
An attempt to claim the device memory range in the registry has failed, possibly because another driver has already claimed the range for its device. NdisMMapIoSpace logs an error if this occurs.
NDIS_STATUS_RESOURCES
The memory could not be mapped or sufficient virtual memory could not be allocated.
NDIS_STATUS_FAILURE
Either the bus type or bus number is out of range or the given PhysicalAddress and Length were invalid (possibly not within the I/O space of the current platform).

Comments

This function is called by drivers of NICs that have on-board memory or a bank of device registers appearing in the I/O space of the host. For example, the driver of a NIC that uses PIO calls NdisMMapIoSpace.

A successful call to NdisMMapIoSpace claims hardware resources in the registry for the driver’s NIC. Consequently, only MiniportInitialize functions call NdisMMapIoSpace.

NdisMMapIoSpace sets the variable at VirtualAddress to NULL if it does not return NDIS_STATUS_SUCCESS.

MiniportInitialize either gets the PhysicalAddress value from the driver’s Parameters registry key or by calling a bus-type-specific Ndis Xxx configuration function. The given physical address range must fall within the I/O space of the current platform. It is a programming error to call NdisMMapIoSpace with a host physical memory address.

A miniport must call the reciprocal NdisMUnmapIoSpace to release its claim on system resources if MiniportInitialize subsequently fails to initialize the NIC, if its NIC is removed from the machine, and/or when the driver is unloading.

Callers of NdisMMapIoSpace run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NdisMUnmapIoSpace, NdisOpenConfiguration, NdisReadEisaSlotInformation, NdisReadMcaPosInformation, NdisReadPciSlotInformation, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


NdisMoveFromMappedMemory

VOID
NdisMoveFromMappedMemory(
OUT PVOID Destination,
IN PVOID Source,
IN ULONG Length
);

NdisMoveFromMappedMemory copies data from device memory that was mapped during initialization with NdisMMapIoSpace to a system-space buffer.

Parameters

Destination
Points to a system-space buffer into which this function copies data from mapped device memory. This buffer must be at least Length bytes in size.
Source
Specifies the base virtual address within mapped device memory from which to copy the data. This address must fall at least Length bytes from the end of the mapped range.
Length
Specifies the number of bytes to copy.

Comments

A NIC driver calls NdisMoveFromMappedMemory to copy data from NIC memory into a buffer in the host, as, for example, when transferring received data into a protocol-supplied buffer or miniport-allocated staging buffer.

Both the Source and Destination pointers are virtual addresses. The virtual range specified by Source and Length must fall within a range of device memory that was mapped during initialization with NdisMMapIoSpace.

Callers of NdisMoveFromMappedMemory can run at any IRQL when the given Source and Destination are resident. Otherwise, callers must be running at IRQL < DISPATCH_LEVEL, as, for example if the Destination address is on the stack.

See Also

MiniportInitialize, NdisAllocateMemory, NdisMMapIoSpace, NdisMoveMappedMemory, NdisMoveToMappedMemory, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


NdisMoveMappedMemory

VOID
NdisMoveMappedMemory(
OUT PVOID Destination,
IN PVOID Source,
IN ULONG Length
);

NdisMoveMappedMemory copies data from one location to another within a device memory range that was mapped during initialization with NdisMMapIoSpace.

Parameters

Destination
Specifies the base virtual address within the mapped range at which the data should be copied.
Source
Specifies the base virtual address within the mapped range at which the copy should begin.
Length
Specifies the number of bytes to copy.

Comments

Both the Source and Destination pointers are virtual addresses. The virtual ranges specified both by Destination and Length and by Source and Length must fall within the range that was mapped during initialization with NdisMMapIoSpace.

Callers of NdisMoveMappedMemory can run at any IRQL.

See Also

MiniportInitialize, NdisAllocateMemory, NdisMMapIoSpace, NdisMoveFromMappedMemory, NdisMoveMemory, NdisMoveToMappedMemory


NdisMoveMemory

VOID
NdisMoveMemory(
OUT PVOID Destination,
IN PVOID Source,
IN ULONG Length
);

NdisMoveMemory copies a specified number of bytes from one caller-supplied location to another.

Parameters

Destination
Points to a system-space buffer that is the destination of the move. This buffer must be at least Length bytes in size.
Source
Points to a system-space buffer from which this function copies the data to the destination buffer. This buffer must be at least Length bytes in size.
Length
Specifies the number of bytes to copy.

Comments

Both Source and Destination are virtual addresses.

If either address falls within a range of device memory that was mapped with NdisMMapIoSpace, a NIC driver should call one of the Ndis..MappedMemory functions instead of NdisMoveMemory.

The range specified by Source and Length cannot overlap the Destination range.

Callers of NdisMoveMemory can run at any IRQL if the given Source and Destination are resident. Otherwise, callers must be running at IRQL < DISPATCH_LEVEL, as, for example if either address is on the stack.

See Also

NdisAllocateMemory, NdisMMapIoSpace, NdisMoveFromMappedMemory, NdisMoveMappedMemory, NdisMoveToMappedMemory


NdisMoveToMappedMemory

VOID
NdisMoveToMappedMemory(
OUT PVOID Destination,
IN PVOID Source,
IN ULONG Length
);

NdisMoveToMappedMemory copies data from a system-space buffer to device memory that was mapped during initialization with NdisMMapIoSpace.

Parameters

Destination
Specifies the base virtual address within a mapped device memory range where the copy should begin. This address must fall at least Length bytes from the end of the mapped range.
Source
Points to a system-space buffer from which this function copies data to the destination range. This buffer must be at least Length bytes in size.
Length
Specifies the number of bytes to copy.

Comments

A NIC driver calls NdisMoveToMappedMemory to copy data from a buffer in the host to mapped device memory, as, for example, when transferring data from a protocol-supplied buffer or miniport-allocated staging buffer to the NIC for a transmit.

Both the Source and Destination pointers are virtual addresses. The virtual range specified by Destination and Length must fall within a range that was mapped with NdisMMapIoSpace.

Callers of NdisMoveToMappedMemory can run at any IRQL when the given Source and Destination are resident. Otherwise, callers must be running at IRQL < DISPATCH_LEVEL, as, for example if the Source address is on the stack.

See Also

MiniportInitialize, NdisAllocateMemory, NdisMMapIoSpace, NdisMoveFromMappedMemory, NdisMoveMappedMemory, NdisMoveMemory, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


NdisMPciAssignResources

NDIS_STATUS
NdisMPciAssignResources(
IN NDIS_HANDLE MiniportHandle,
IN ULONG SlotNumber,
OUT PNDIS_RESOURCE_LIST *AssignedResources
 );

NdisMPciAssignResources returns a list of bus-relative hardware resources, such as IRQ, I/O ports, and device memory ranges, claimed in the registry for a PCI NIC.

Parameters

MiniportHandle
Specifies the MiniportAdapterHandle passed in to MiniportInitialize and to most other MiniportXxx functions.
SlotNumber
Specifies the slot number of the driver’s NIC on the PCI bus.
AssignedResources
Points to a caller-supplied variable in which this function returns a pointer to a list of the hardware resources the NIC can use.

Return Value

NdisMPciAssignResources returns NDIS_STATUS_SUCCESS.

Comments

NdisMPciAssignResources claims a set of hardware resources in the registry for a miniport’s PCI NIC to use, thereby preventing conflicting claims on the same resources from other devices in the machine.

The buffer specified at AssignedResources is formatted as an NDIS_RESOURCE_LIST, which is equivalent to the CM_PARTIAL_RESOURCE_LIST on Windows NT platforms. See the Kernel-Mode Driver Reference for details of this structure.

The bus-relative configuration parameters returned in this buffer can be used in the MiniportInitialize function’s subsequent calls to Ndis Xxx functions such as NdisMRegisterInterrupt, NdisMMapIoSpace, and/or NdisMRegisterIoPortRange.

Callers of NdisMPciAssignResources run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisImmediateReadPciSlotInformation, NdisMMapIoSpace, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisReadPciSlotInformation


NdisMQueryAdapterResources

VOID
NdisMQueryAdapterResources(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE WrapperConfigurationContext,
OUT PNDIS_RESOURCE_LIST ResourceList,
IN OUT PUINT BufferSize
);

NdisMQueryAdapterResources either returns a list of hardware resources for a PCMCIA NIC, or it returns NDIS_STATUS_NOT_SUPPORTED.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call. It returns NDIS_STATUS_NOT_SUPPORTED on Windows NT platforms.
WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
ResourceList
Points to a caller-allocated buffer in which this function returns a list of PCMCIA resources for the caller’s NIC.
BufferSize
Points to a variable that specifies the size in bytes of the caller-allocated buffer on input and the number of bytes of returned information on return from this function.

Comments

To get hardware configuration parameters for PCMCIA NICs, drivers that are binary-compatible across Microsoft operating systems that support Win32 applications can call NdisMQueryAdapterResources from their MiniportInitialize functions.

If NdisMQueryAdapterResources returns NDIS_STATUS_NOT_SUPPORTED, the MiniportInitialize function then gets the configuration parameters for its NIC from the registry with calls to the Ndis..Configuration functions.

Callers of NdisMQueryAdapterResources run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisOpenConfiguration, NdisReadConfiguration, NdisWriteConfiguration


NdisMQueryInformationComplete

VOID
NdisMQueryInformationComplete(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_STATUS Status
 );

NdisMQueryInformationComplete indicates that a prior call to MiniportQueryInformation, which returned NDIS_STATUS_PENDING, has completed.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Status
Specifies the return value for the completed request originally passed to MiniportQueryInformation.

Comments

A call to NdisMQueryInformationComplete causes NDIS to return the completed query request to the ProtocolRequestComplete function of the driver that originally called NdisRequest.

Before it calls NdisMQueryInformationComplete, a miniport should supply the OID-specific information originally requested from its MiniportQueryInformation function. That is, the driver should set the appropriate values at BytesWritten and/or BytesNeeded and provide as much as possible of the requested data in the buffer at InformationBuffer.

For more information about system-defined OIDs, see Chapter 5.

Callers of NdisMQueryInformationComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportQueryInformation, NDIS_REQUEST, NdisRequest, ProtocolRequestComplete


NdisMReadDmaCounter

ULONG
NdisMReadDmaCounter(
IN NDIS_HANDLE MiniportDmaHandle
 );

NdisMReadDmaCounter returns the current value of the system DMA controller’s counter.

Parameters

MiniportDmaHandle
Specifies the handle returned when the miniport called NdisMRegisterDmaChannel during initialization.

Return Value

NdisMReadDmaCounter returns the number of bytes remaining in the current DMA transfer on the channel used by the NIC.

Comments

NIC drivers of devices that use the system DMA controller’s autoinitialize mode can call NdisMReadDmaCounter.

Callers of NdisMReadDmaCounter run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMCompleteDmaTransfer, NdisMDeregisterDmaChannel, NdisMRegisterDmaChannel, NdisMSetAttributes, NdisMSetAttributesEx, NdisMSetupDmaTransfer


NdisMRegisterAdapterShutdownHandler

VOID
NdisMRegisterAdapterShutdownHandler(
IN NDIS_HANDLE MiniportHandle,
IN PVOID ShutdownContext,
IN ADAPTER_SHUTDOWN_HANDLER ShutdownHandler
);

NdisMRegisterAdapterShutdownHandler registers a NIC-driver-supplied MiniportShutdown function to be called when the system is shutting down.

Parameters

MiniportHandle
Specifies the handle input to MiniportInitialize.
ShutdownContext
Points to a driver-determined context area. This pointer will be passed to the function at ShutdownHandler when it is called.
ShutdownHandler
Specifies the entry point of the caller’s MiniportShutdown function.

Comments

Whenever a MiniportInitialize function successfully initializes a NIC, it calls NdisMRegisterAdapterShutdownHandler, usually just before MiniportInitialize returns NDIS_STATUS_SUCCESS.

Every NIC driver should register a MiniportShutdown function when it initializes. MiniportShutdown is responsible for restoring the NIC to its initial state before the system is shut down.

Callers of NdisMRegisterAdapterShutdownHandler run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, MiniportShutdown, NdisMDeregisterAdapterShutdownHandler


NdisMRegisterDmaChannel

NDIS_STATUS
NdisMRegisterDmaChannel(
OUT PNDIS_HANDLE MiniportDmaHandle,
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT DmaChannel,
IN BOOLEAN Dma32BitAddresses,
IN PNDIS_DMA_DESCRIPTION DmaDescription,
IN ULONG MaximumLength
 );

NdisMRegisterDmaChannel claims a system DMA controller channel during initialization for DMA operations on a slave NIC or on an ISA busmaster NIC.

Parameters

MiniportDmaHandle
Points to a caller-supplied variable in which this function returns a handle the miniport uses in subsequent calls to the NdisM Xxx system DMA functions.
MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
DmaChannel
Ignored. Set the DMA channel, if any, at DmaDescription.
Dma32BitAddresses
Specifies TRUE if the NIC has 32 address lines.
DmaDescription
Points to an NDIS_DMA_DESCRIPTION structure filled in by the caller. This structure is defined as follows:
typedef struct _NDIS_DMA_DESCRIPTION {
    BOOLEAN DemandMode;
    BOOLEAN AutoInitialize;
    BOOLEAN DmaChannelSpecified;
    DMA_WIDTH DmaWidth;
    DMA_SPEED DmaSpeed;
    ULONG DmaPort;
    ULONG DmaChannel;
} NDIS_DMA_DESCRIPTION, *PNDIS_DMA_DESCRIPTION;

The driver should initialize this structure with zeros before filling in the following members:

DemandMode
Specifies TRUE if the slave NIC uses the system DMA controller’s demand mode.
AutoInitialize
Specifies TRUE if the slave NIC uses the system DMA controller’s autoinitialize mode.
DmaChannelSpecified
Specifies TRUE if DmaChannel is set to the bus-relative value of the system DMA controller channel used by the NIC.
DmaWidth
Specifies the transfer width for DMA operations, one of Width8Bits, Width16Bits, or Width32Bits.
DmaSpeed
Specifies one of Compatible, TypeA, TypeB, or TypeC.
DmaPort
Specifies the bus-relative port number if the NIC is on an MCA bus; otherwise, specifies zero.
DmaChannel
Specifies the bus-relative number of the system DMA controller channel used by the NIC.
MaximumLength
Specifies the maximum number of bytes the NIC can transfer in a single DMA operation. If the NIC has unlimited transfer capacity,set this parameter to -1.

Return Value

NdisMRegisterDmaChannel can return one of the following status values:

NDIS_STATUS_SUCCESS
NDIS claimed the specified DMA channel in the registry for the caller’s NIC and set up necessary resources for subsequentl DMA operations by the miniport.
NDIS_STATUS_RESOURCE_CONFLICT
An attempt to claim the DMA channel in the registry has failed, possibly because another driver has already claimed that channel for its device. NdisMRegisterDmaChannel logs an error if this occurs.
NDIS_STATUS_RESOURCES
NDIS could not allocate the system resources it needs to support DMA operations by this NIC driver.
NDIS_STATUS_FAILURE
Either the bus type or bus number is out of range or the driver declared its NIC a busmaster on an I/O bus other than ISA.

Comments

A driver of a slave-DMA NIC must call NdisMRegisterDmaChannel from its MiniportInitialize function to reserve system resources for subsequent DMA operations and to claim them in the registry.

The driver of an ISA busmaster NIC also must call NdisMRegisterDmaChannel from MiniportInitialize to claim a system DMA controller channel for its NIC in the registry.

MiniportInitialize must call NdisMSetAttributes or NdisMSetAttributesEx before calling NdisMRegisterDmaChannel.

MiniportInitialize obtained the bus-relative values passed to NdisMRegisterDmaChannel either from the registry or by calling a bus-type-specific Ndis Xxx configuration function.

If such a driver cannot allocate the system DMA resources that its device needs, MiniportInitialize should release all resources it already allocated for its NIC and, then, fail initialization for that NIC.

Callers of NdisMRegisterDmaChannel run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMCompleteDmaTransfer, NdisMDeregisterDmaChannel, NdisMPciAssignResources, NdisMSetAttributes, NdisMSetAttributesEx, NdisMSetupDmaTransfer, NdisOpenConfiguration, NdisReadEisaSlotInformation, NdisReadMcaPosInformation, NdisReadPciSlotInformation


NdisMRegisterInterrupt

NDIS_STATUS
NdisMRegisterInterrupt(
OUT PNDIS_MINIPORT_INTERRUPT Interrupt,
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT InterruptVector,
IN UINT InterruptLevel,
IN BOOLEAN RequestIsr,
IN BOOLEAN SharedInterrupt,
IN NDIS_INTERRUPT_MODE InterruptMode
 );

NdisMRegisterInterrupt sets up a mapping between a NIC driver’s MiniportISR and MiniportHandleInterrupt functions, already registered with NdisMRegisterMiniport, and the bus-relative vector and level on which its NIC interrupts.

Parameters

Interrupt
Points to caller-allocated resident memory containing an opaque interrupt object, which NdisMRegisterInterrupt initializes and for which it returns a handle the miniport uses in subsequent calls to NdisMSynchronizeWithInterrupt.
MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
InterruptVector
Specifies the bus-relative vector number used by the NIC.
InterruptLevel
Specifies the bus-relative DIRQL for the interrupt. This value is almost always the same as that of InterruptVector.
RequestIsr
Specifies TRUE if the MiniportISR function should be called each time the NIC interrupts. If the NIC can share an interrupt with other devices on the I/O bus, this parameter must be set to TRUE.
SharedInterrupt
Specifies TRUE if other devices on the I/O bus can use this interrupt line. If this parameter is set to FALSE and another device already claimed the given interrupt vector in the registry, NdisMRegisterInterrupt will fail.
InterruptMode
Specifies the type of interrupt that the NIC generates as one of the following values:
NdisInterruptLatched
Interrupts are triggered by a transition from low to high on the interrupt line.
NdisInterruptLevelSensitive
Interrupts are active as long as the interrupt line is asserted.

Return Value

NdisMRegisterInterrupt can return one of the following:

NDIS_STATUS_SUCCESS
NDIS initialized the interrupt object and returned a valid Interrupt handle, claimed hardware resources in the registry for the NIC, such as the given vector if the NIC does not share an IRQ, and set up the MiniportISR (or MiniportDisableInterrupt) and associated MiniportHandleInterrupt functions to be called when an interrupt occurs as requested in the call to NdisMRegisterInterrupt.
NDIS_STATUS_RESOURCE_CONFLICT
An attempt to claim the vector or level in the registry has failed, possibly because another driver already claimed the interrupt for its device. NdisMRegisterInterrupt logs an error if this occurs.
NDIS_STATUS_RESOURCES
NDIS could not allocate sufficient memory to claim resources in the registry for the NIC.
NDIS_STATUS_FAILURE
The attempt to initialize the interrupt object failed, possibly due to another driver’s prior claim on the InterruptVector or InterruptLevel in the registry.

Comments

A NIC driver must call NdisMRegisterInterrupt from its MiniportInitialize function if its NIC generates interrupts. The driver of a NIC that does not generate interrupts calls NdisMInitializeTimer and NdisMSetPeriodicTimer instead of NdisMRegisterInterrupt.

MiniportInitialize must call NdisMSetAttributes or NdisMSetAttributesEx before calling NdisMRegisterInterrupt.

MiniportInitialize obtained the bus-relative values passed to NdisMRegisterInterrupt either from the registry or by calling a bus-type-specific Ndis Xxx configuration function.

If its call to NdisMRegisterInterrupt fails, MiniportInitialize should release all resources it already allocated for its NIC and, then, fail initialization for that NIC.

NdisMRegisterInterrupt distinguishes between the InterruptVector (the interrupt line that the card asserts to interrupt the system) and the InterruptLevel (the hardware priority, that is, the DIRQL, of that interrupt assigned by the system). Usually, these values can be assumed to be equal, unless the driver writer knows that they are different for a particular NIC or unless the miniport controls more than one NIC that share resources in the current platform.

If its NIC can share an IRQ with other devices on the I/O bus, the caller of NdisMRegisterInterrupt must set both the SharedInterrupt and RequestIsr parameters to TRUE. Such a NIC driver’s MiniportISR can be called when no interrupt for the NIC is outstanding. In these circumstances, MiniportISR should return FALSE as soon as possible, indicating that the interrupt was not recognized and should leave interrupts enabled on its NIC.

When interrupts are enabled on the NIC, a driver’s MiniportISR can be called at any time following a successful call to NdisMRegisterInterrupt, even during initialization. Such a driver’s MiniportInitialize function should not call NdisMRegisterInterrupt until it has set up all state the driver needs to handle an interrupt. The MiniportHandleInterrupt function is not queued for subsequent execution if the driver’s MiniportInitialize function is currently executing and an interrupt occurs.

For most drivers of NICs that do not share an IRQ, the MiniportISR function seldom runs except, possibly, during driver initialization. Such a miniport sets RequestIsr to FALSE when MiniportInitialize calls NdisMRegisterInterrupt, and such a driver has a MiniportDisableInterrupt function and, possibly, a MiniportEnableInterrupt function. The NDIS library then calls the driver’s MiniportDisableInterrupt function when an interrupt occurs on the NIC and the driver’s MiniportHandleInterrupt function does most of the I/O processing for the interrupt. Before MiniportHandleInterrupt returns control, either NDIS calls MiniportEnableInterrupt or MiniportHandleInterrupt re-enables interrupts on the NIC itself.

Whether a miniport with an ISR sets RequestIsr to TRUE when it calls NdisMRegisterInterrupt or not, NDIS acknowledges the interrupt to the operating system so that interrupts from other devices are not blocked.

If a miniport sets RequestIsr to TRUE when it calls NdisMRegisterInterrupt, it must dismiss the interrupt on its NIC by setting the state of the network interface card so that the NIC no longer asserts the interrupt. Interrupts can remain enabled on the NIC following the dismissal of the interrupt, or they can be disabled, depending on the design of the driver. The timing of such a dismissal depends on the InterruptMode specified when the driver called NdisMRegisterInterrupt, as follows:

  • For NdisInterruptLevelSensitive interrupts, MiniportISR must dismiss each interrupt as its NIC generates that interrupt. Otherwise, the interrupt is reasserted on the NIC as soon as MiniportISR returns control. If MiniportISR determines that the NIC did not generate a particular interrupt, it should return control with FALSE as soon as possible so that the ISRs for other devices on the same bus can be called promptly to dismiss the interrupt on the device that actually generated that interrupt.

  • For NdisInterruptLatched interrupts, dismissing an interrupt on the NIC is not as time-critical as it is for shared interrupts. If the miniport is designed such that each call to MiniportISR causes a subsequent call to the associated MiniportHandleInterrupt function, MiniportHandleInterrupt can dismiss the interrupt on the NIC. As an alternative, such a driver can set RequestIsr to FALSE when it calls NdisMRegisterInterrupt and supply a MiniportDisableInterrupt function, which will be called instead of MiniportISR unless MiniportHalt or MiniportInitialize is currently executing.

When a miniport supports full-duplex sends and receives, NDIS serializes calls to its MiniportISR or MiniportDisableInterrupt function for sends. NDIS separately serializes calls to its MiniportISR or MiniportDisableInterrupt function for all other operations that can cause an interrupt. Calls to the MiniportReset function are both synchronized and synchronous; NDIS prevents all other code paths in a full-duplex miniport from being entered while a device-reset operation is occurring.

If a miniport does not support full-duplex sends and receives, NDIS serializes all calls to the MiniportISR or MiniportDisableInterrupt function for any NIC the miniport controls. While such a driver’s MiniportISR or MiniportDisableInterrupt function is processing a particular NIC interrupt, it will not be called to handle a second interrupt from the same NIC on another processor in an SMP machine.

However, the ISR (or MiniportDisableInterrupt function) of any driver that controls more than one device can run concurrently in SMP platforms if two of that driver’s devices happen to generate interrupts almost simultaneously and interrupts are enabled on the NIC.

If it is possible that an interrupt can occur while another driver function is accessing resources that also can be accessed from MiniportISR or MiniportDisableInterrupt, the other function must call NdisMSynchronizeWithInterrupt to have the driver-supplied MiniportSynchronizeISR function access the shared resources at DIRQL.

Callers of NdisMRegisterInterrupt run at IRQL PASSIVE_LEVEL.

See Also

MiniportDisableInterrupt, MiniportEnableInterrupt, MiniportHandleInterrupt, MiniportInitialize, MiniportISR, MiniportReset, MiniportSynchronizeISR, MiniportTimer, NdisMDeregisterInterrupt, NdisMInitializeTimer, NdisMPciAssignResources, NdisMSetAttributes, NdisMSetAttributesEx, NdisMSetPeriodicTimer, NdisMSynchronizeWithInterrupt, NdisOpenConfiguration, NdisReadEisaSlotInformation, NdisReadMcaPosInformation, NdisReadPciSlotInformation


NdisMRegisterIoPortRange

NDIS_STATUS
NdisMRegisterIoPortRange(
OUT PVOID *PortOffset,
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT InitialPort,
IN UINT NumberOfPorts
 );

NdisMRegisterIoPortRange sets up driver access to device I/O ports with the NdisRawReadPort Xxx and NdisRawWritePort Xxx functions and claims the range of I/O port addresses in the registry for that driver’s NIC.

Parameters

PortOffset
Specifies a caller-supplied variable in which this function returns the mapped base virtual address for the given bus-relative I/O port range specified by InitialPort and NumberOfPorts.
MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
InitialPort
Specifies the bus-relative base port address for a range of ports to be mapped.
NumberOfPorts
Specifies the number of ports in the range to be mapped.

Return Value

NdisMRegisterIoPortRange can return one of the following:

NDIS_STATUS_SUCCESS
The given range of I/O ports was mapped successfully so the value at PortOffset is valid and the mapped range has been claimed in the registry for the NIC.
NDIS_STATUS_RESOURCE_CONFLICT
An attempt to claim the I/O port range in the registry has failed, possibly because another driver already claimed the range for its device. NdisMRegisterIoPortRange logs an error if this occurs.
NDIS_STATUS_RESOURCES
The port range could not be mapped or NDIS could not allocate resources to check the registry for hardware-resource conflicts.
NDIS_STATUS_FAILURE
Either the bus type or bus number is out of range or the given InitialPort and NumberOfPorts were invalid (possibly not within the I/O port space of the current platform).

Comments

A NIC driver calls NdisMRegisterIoPortRange from its MiniportInitialize function. MiniportInitialize must call NdisMSetAttributes or NdisMSetAttributesEx before calling NdisMRegisterIoPortRange.

NdisMRegisterIoPortRange maps a bus-relative device address range that the miniport can use subsequently to access an I/O port range on its NIC by calling the NdisRaw Xxx functions. A successful call claims the specified range of I/O ports in the registry for the caller’s NIC.

Because the parameters passed to the NdisRaw Xxx have been mapped, these functions run significantly faster than the corresponding NdisImmediate..Port Xxx. After a successful call to NdisMRegisterIoPortRange, a miniport cannot call any of the NdisImmediate..Port Xxx functions with either bus-relative addresses or mapped virtual addresses within such an I/O port range.

If its call to NdisMRegisterIoPortRange fails, MiniportInitialize should release all resources it already allocated for its NIC and, then, fail initialization for that NIC.

Drivers of NICs with device registers in the host memory space call NdisMMapIoSpace and, subsequently, the NdisRead/WriteRegister Xxx functions to access the NIC registers.

Callers of NdisMRegisterIoPortRange run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMDeregisterIoPortRange, NdisMMapIoSpace, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawReadPortUlong, NdisRawReadPortUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisRawWritePortUchar, NdisRawWritePortUlong, NdisRawWritePortUshort, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


NdisMRegisterMiniport

NDIS_STATUS
NdisMRegisterMiniport(
IN NDIS_HANDLE NdisWrapperHandle,
IN PNDIS_MINIPORT_CHARACTERISTICS MiniportCharacteristics,
IN UINT CharacteristicsLength
 );

NdisMRegisterMiniport registers a NIC or intermediate driver’s MiniportXxx entry points and name with the NDIS library when the driver initializes.

Parameters

NdisWrapperHandle
Specifies the handle returned by NdisMInitializeWrapper.
MiniportCharacteristics
Points to an NDIS_MINIPORT_CHARACTERISTICS structure set up by the caller. The structure at MiniportCharacteristics is defined as follows:
typedef struct _NDIS_MINIPORT_CHARACTERISTICS {
    UCHAR MajorNdisVersion;
    UCHAR MinorNdisVersion;
    UINT Reserved;
    W_CHECK_FOR_HANG_HANDLER CheckForHangHandler;
    W_DISABLE_INTERRUPT_HANDLER DisableInterruptHandler;
    W_ENABLE_INTERRUPT_HANDLER  EnableInterruptHandler;
    W_HALT_HANDLER HaltHandler;
    W_HANDLE_INTERRUPT_HANDLER  HandleInterruptHandler;
    W_INITIALIZE_HANDLER InitializeHandler;
    W_ISR_HANDLER ISRHandler;
    W_QUERY_INFORMATION_HANDLER QueryInformationHandler;
    W_RECONFIGURE_HANDLER ReconfigureHandler;
    W_RESET_HANDLER ResetHandler;
    W_SEND_HANDLER SendHandler;
    W_SET_INFORMATION_HANDLER SetInformationHandler;
    W_TRANSFER_DATA_HANDLER TransferDataHandler;
//
// MajorNdisVersion must be set to 0x04 with following members
//
    W_RETURN_PACKET_HANDLER ReturnPacketHandler;
    W_SEND_PACKETS_HANDLER SendPacketsHandler;
    W_ALLOCATE_COMPLETE_HANDLER AllocateCompleteHandler;
} NDIS_MINIPORT_CHARACTERISTICS, *PNDIS_MINIPORT_CHARACTERISTICS;

The driver should initialize this structure with zeros before setting up any of the following members:

MajorNdisVersion
Specifies the major version of the NDIS library the driver is using. The current value is 0x04, although the NDIS library continues to support existing NIC drivers developed for NDIS V3.0.

This member must be set to 0x04 if the caller sets entry points in any members following Name.

MinorNdisVersion
Specifies the minor version of the NDIS library the driver is using. The current value is 0x00, although NDIS continues to support existing drivers.
Reserved
This member is reserved for system use.
CheckForHangHandler
Specifies the entry point of the caller’s MiniportCheckForHang function, if any, or NULL.
DisableInterruptHandler
Specifies the entry point of the caller’s MiniportDisableInterrupt function, if any.
EnableInterruptHandler
Specifies the entry point of the caller’s MiniportEnableInterrupt function, if any.
HaltHandler
Specifies the entry point of the caller’s MiniportHalt function.
HandleInterruptHandler
Specifies the entry point of the caller’s MiniportHandleInterrupt function, if any.
InitializeHandler
Specifies the entry point of the caller’s MiniportInitialize function.
ISRHandler
Specifies the entry point of the caller’s MiniportISR function, if any. This miniport function is required if the NIC shares an interrupt vector.
QueryInformationHandler
Specifies the entry point of the caller’s MiniportQueryInformation function.
ReconfigureHandler
Specifies the entry point of the caller’s MiniportReconfigure function, or NULL.
ResetHandler
Specifies the entry point of the caller’s MiniportReset function.
SendHandler
Specifies the entry point of the caller’s MiniportSend function, MiniportWanSend function, or NULL if the caller supplies a MiniportSendPackets function.

If the miniport supports multipacket sends, it sets the SendPacketsHandler member instead and sets this member to NULL.

SetInformationHandler
Specifies the entry point of the caller’s MiniportSetInformation function.
TransferDataHandler
Specifies the entry point of the caller’s MiniportTransferData function, if any. This miniport function is required unless the caller is the driver of a WAN NIC or the caller supports multipacket receives and, therefore, supplies the entry point of its MiniportReturnPacket function at ReturnPacketHandler.
ReturnPacketHandler
Specifies the entry point of the caller’s MiniportReturnPacket function, if any.
SendPacketsHandler
Specifies the entry point of the caller’s MiniportSendPackets function, if any. If the caller also sets an entry point in the SendHandler member, NDIS always calls MiniportSendPackets.
AllocateCompleteHandler
Specifies the entry point of the caller’s MiniportAllocateComplete function, if any.
CharacteristicsLength
Specifies the length in bytes of the caller-supplied characteristics buffer. Depending on the value of MajorNdisVersion, this parameter must be either sizeof(NDIS40_MINIPORT_CHARACTERISTICS) or sizeof(NDIS30_MINIPORT_CHARACTERISTICS).

If the driver includes the build instruction NDIS40_MINIPORT (or NDIS30_MINIPORT, as appropriate) in its sources or if the driver writer uses the -DNDIS40_MINIPORT (or ..30..) compiler switch, this parameter is set when the driver is built.

Return Value

NdisMRegisterMiniport returns NDIS_STATUS_SUCCESS if it registered the miniport, or it can return one of the following status values:

NDIS_STATUS_BAD_CHARACTERISTICS
The CharacteristicsLength is too small for the MajorNdisVersion specified in the buffer at MiniportCharacteristics.
NDIS_STATUS_BAD_VERSION
The MajorNdisVersion or MinorNdisVersion specified in the characteristics structure is invalid.
NDIS_STATUS_RESOURCES
A shortage of resources, possibly memory, prevented the NDIS library from registering the caller.
NDIS_STATUS_FAILURE
This is a default error status, returned when none of the preceding errors caused the registration to fail. For example, if the NDIS library cannot load the driver’s image and lock it into system memory, it returns this error.

Comments

A NIC driver calls NdisMRegisterMiniport from its DriverEntry function after DriverEntry calls NdisMInitializeWrapper.

Every NIC driver exports only MiniportXxx functions must set up a characteristics structure and call NdisMRegisterMiniport. This structure is copied in the NdisMRegisterMiniport request to the NDIS library’s internal storage. Thus, once it has registered, a miniport driver cannot change its handler functions.

The NDIS library currently does not call MiniportReconfigure functions, so such a function in an existing miniport driver is dead code unless the miniport makes internal calls to its MiniportReconfigure function from MiniportInitialize.

After the driver has called NdisMRegisterMiniport, it should be prepared to be called back at the MiniportInitialize entry point specified in the characteristics structure.

NDIS intermediate drivers, which export both MiniportXxx and ProtocolXxx functions, usually call NdisIMRegisterLayeredMiniport instead of NdisMRegisterMiniport. Intermediate drivers that export only a set of MiniportXxx usually call NdisMRegisterMiniport.

Callers of NdisMRegisterMiniport run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Miniport Drivers, MiniportAllocateComplete, MiniportCheckForHang, MiniportDisableInterrupt, MiniportEnableInterrupt, MiniportHalt, MiniportHandleInterrupt, MiniportInitialize, MiniportISR, MiniportQueryInformation, MiniportReconfigure, MiniportReset, MiniportReturnPacket, MiniportSend, MiniportSendPackets, MiniportSetInformation, MiniportTransferData, MiniportWanSend, NdisIMRegisterLayeredMiniport, NdisZeroMemory


NdisMResetComplete

VOID
NdisMResetComplete(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_STATUS Status,
IN BOOLEAN AddressingReset
 );

NdisMResetComplete returns the final status of a reset request for with the NIC driver previously returned NDIS_STATUS_PENDING.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Status
Specifies the final status of the reset operation just completed
AddressingReset
Specifies TRUE if the reset affected the miniport’s network address information so that it needs to be reloaded.

Comments

If the MiniportReset function returns NDIS_STATUS_PENDING, the miniport must call NdisMResetComplete when it completes the reset operation. This call causes NDIS to forward the completion-status information to the protocol driver, if any, that called NdisReset to initiate the call to MiniportReset.

The NDIS library also can call the MiniportReset function, independent of any reset requests from protocol drivers bound to a particular NIC, when NDIS determines that a sent packet or a request that it submitted to the miniport has timed out.

Some NICs lose all multicast address, packet filter, or functional address information when a soft reset is issued. The driver of such a NIC sets AddressingReset to TRUE when it calls NdisMResetComplete, causing NDIS to call its MiniportSetInformation function to restore the addressing state.

An NDIS intermediate driver either sets AddressingReset to TRUE when it calls NdisMResetComplete or restores its own addressing information, such as medium-specific functional, multicast, and/or group addresses.

Callers of NdisMResetComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportReset, MiniportSetInformation, NdisReset, ProtocolResetComplete


NdisMSendComplete

VOID
NdisMSendComplete(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
 );

NdisMSendComplete returns the packet and final status of a completed send request for which the driver previously returned NDIS_STATUS_PENDING.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Packet
Points to the protocol-allocated packet originally input to MiniportSend.
Status
Specifies the final status of the transmit request that has just been completed.

Comments

The NDIS library queues incoming send packets on behalf of miniports so each NIC driver can deal with one device-transmit operation at a time, although a miniport can transmit several packets per operation, depending on the capabilities of its NIC. NDIS serializes the submission of incoming packet arrays to each miniport’s MiniportSendPackets function, and it serializes the submission of packets to each miniport’s MiniportSend function. NDIS queues incoming send requests in FIFO order when it cannot pass on each such request immediately because the underlying NIC driver is already busy.

If MiniportSendPackets sets the Status in the out-of-band data block for a packet to NDIS_STATUS_PENDING or if MiniportSend returns NDIS_STATUS_PENDING for an incoming packet, the miniport must eventually call NdisMSendComplete with the original packet to indicate which send request it has completed. While the status of such a send packet is pending, the miniport retains ownership of that packet.

A call to NdisMSendComplete causes NDIS to call the ProtocolSendComplete function of the driver that initiated the request with its call to NdisSendPackets or NdisSend. The miniport’s call to NdisMSendComplete releases ownership of the given packet to the protocol driver that originally allocated it.

If MiniportSend(Packets) returns a status other than NDIS_STATUS_PENDING for a given packet, the miniport never calls NdisMSendComplete with that packet. The return from MiniportSend(Packets) releases ownership of such a packet.

Consequently, a miniport must not call NdisMSendComplete with any packet for which its MiniportSend(Packets) function did not return NDIS_STATUS_PENDING. Any send packet for which a NIC driver returns something other than NDIS_STATUS_PENDING should be considered inaccessible by that miniport as soon as its MiniportSend(Packets) function returns control.

A miniport’s call to NdisMSendComplete does not necessarily mean that a particular packet has been transmitted over the wire. Usually, such a packet has been transmitted except, possibly, by the driver of a NIC in which packets can be queued in the hardware.

Miniport drivers that declared their devices of type NdisMediumWan in response to a preceding OID_GEN_MEDIA_SUPPORTED or OID_GEN_MEDIA_IN_USE request call NdisMWanSendComplete rather than NdisMSendComplete.

Callers of NdisMSendComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportSend, MiniportSendPackets, NdisMSendResourcesAvailable, NdisMWanSendComplete, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_STATUS, ProtocolSendComplete


NdisMSendResourcesAvailable

VOID
NdisMSendResourcesAvailable(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMSendResourcesAvailable notifies NDIS that a miniport has sufficient internal resources to accept another send request, even though one or more outstanding send packets are pending within the miniport.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

The NDIS library queues send requests on behalf of miniports so each NIC driver can deal with one device transmit operation at a time, although a miniport can transmit several packets per operation, depending on the capabilities of its NIC. NDIS serializes the submission of incoming packet arrays to each miniport’s MiniportSendPackets function, and it serializes the submission of packets to each miniport’s MiniportSend function. NDIS queues incoming send requests in FIFO order.

Any miniport can retains ownership of such a send packet in one of the following ways:

  • Its MiniportSendPackets function sets the Status member in the out-of-band data block for a packet to NDIS_STATUS_PENDING with NDIS_SET_PACKET_STATUS.

  • Its MiniportSend function returns NDIS_STATUS_PENDING.

In either of these circumstances, the NIC driver eventually must call NdisMSendComplete or NdisMWanSendComplete to indicate that it has released ownership of the packet and that the driver has sufficient resources available to accept the next transmit request.

However, before miniport calls NdisMSendComplete for a pending packet, it can call NdisMSendResourcesAvailable if the driver and its NIC are capable of accepting another send request immediately, assuming NDIS has one in its queue to submit to that driver. Such a call allows the driver of a high-capacity NIC to optimize transmissions over the network by sending more data at a time.

NdisMSendResourcesAvailable can be called only in the window between the return of NDIS_STATUS_PENDING for a given packet and the miniport’s call to NdisMSendComplete with that packet.

If a miniport returns any value except NDIS_STATUS_PENDING for a particular packet from MiniportSendPackets or from MiniportSend, it should neither call NdisMSendResourcesAvailable nor NdisMSendComplete. Any send packet for which a NIC driver returns something other than NDIS_STATUS_PENDING should be considered inaccessible by that miniport on return from its MiniportSendPackets or MiniportSend function.

A WAN miniport cannot call NdisMSendResourcesAvailable.

Callers of NdisMSendResourcesAvailable run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportSend, MiniportSendPackets, NdisMSendComplete, NdisMWanSendComplete, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_STATUS


NdisMSetAttributes

VOID
NdisMSetAttributes(
IN NDIS_HANDLE MiniportAdapterHandle,
 IN NDIS_HANDLE MiniportAdapterContext,
IN BOOLEAN BusMaster,
IN NDIS_INTERFACE_TYPE AdapterType
 );

NdisMSetAttributes informs the NDIS library about significant features of the caller’s NIC during initialization.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
MiniportAdapterContext
Specifies a handle for a resident context area allocated by MiniportInitialize.
BusMaster
Specifies TRUE if the caller’s NIC is a busmaster DMA device.
AdapterType
Specifies the I/O bus interface type of the caller’s NIC, which usually is the type of I/O bus on which the NIC is connected, as one of the following:
NdisInterfaceInternal
Specifies a host-specific internal interface.
NdisInterfaceIsa
Specifies the ISA interface.
NdisInterfaceEisa
Specifies the extended ISA (EISA) interface.
NdisInterfaceMca
Specifies the MicroChannel architecture interface.
NdisInterfaceTurboChannel
Specifies the Turbo Channel interface.
NdisInterfacePci
Specifies the Peripheral Component Interconnect (PCI) interface.
NdisInterfacePcMcia
Specifies the Personal Computer Memory Card International Association (PCMCIA) interface.

Comments

A MiniportInitialize function must call NdisMSetAttributes or NdisMSetAttributesEx before calling any other NdisMRegister Xxx or Ndis Xxx function that depends on the information supplied to NdisMSetAttributes(Ex). For example, a call to NdisMAllocateMapRegisters will fail if MiniportInitialize has not yet called NdisMSetAttributes with BusMaster set to TRUE or NdisMSetAttributesEx with NDIS_ATTRIBUTE_BUS_MASTER set in the AttributeFlags.

In general, a NIC driver must call NdisMSetAttributes(Ex) before it calls any Ndis Xxx function that claims hardware resources in the registry for its NIC, because NDIS must have the BusMaster or AttributeFlags value, respectively, before such a call is made and because the driver usually needs the memory at MiniportAdapterContext to store information for these calls. This restriction implies that a NIC driver’s MiniportInitialize function cannot call the following Ndis Xxx before it calls NdisMSetAttributes(Ex):

  • NdisMPciAssignResources

  • NdisMAllocateMapRegisters and NdisMAllocateSharedMemory

  • NdisMMapIoSpace and, consequently, the Ndis..Register Xxx functions

  • NdisMRegisterDmaChannel

  • NdisMRegisterInterrupt

  • NdisMRegisterIoPortRange and, consequently, the NdisRaw..Port Xxx functions

However, before calling NdisMSetAttributes(Ex), a NIC driver can call the Ndis..Configuration functions to retrieve configuration information installed in the registry and the NdisImmediate Xxx, such as NdisImmediateReadPciSlotInformation or NdisImmediateReadSharedMemory, as well as the NdisImmediate..Port Xxx functions. MiniportInitialize also can call the bus-type-specific NdisRead Xxx functions, such as NdisReadPciSlotInformation, as long as the installed registry entry for the driver’s interface type matches the bus-type-specific NdisRead Xxx MiniportInitialize calls.

The MiniportAdapterContext handle supplied to NdisMSetAttributes(Ex) becomes an input parameter to all MiniportXxx functions that were registered, along with MiniportInitialize, in the call to NdisMRegisterMiniport. Usually, this handle is a pointer to resident memory, allocated by MiniportInitialize, in which the driver maintains NIC-specific runtime state.

Intermediate drivers must call NdisMSetAttributesEx, rather than NdisMSetAttributes. NIC drivers can call either of these functions, but NdisMSetAttributesEx allows a miniport to adjust the intervals at which its MiniportCheckForHang and MiniportReset functions are called.

Callers of NdisMSetAttributes run at IRQL PASSIVE_LEVEL.

See Also

MiniportCheckForHang, MiniportInitialize, MiniportReset, NdisAllocateMemory, NdisImmediateReadPciSlotInformation, NdisImmediateReadPortUchar, NdisImmediateReadPortUlong, NdisImmediateReadPortUshort, NdisImmediateReadSharedMemory, NdisIMRegisterLayeredMiniport, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMMapIoSpace, NdisMPciAssignResources, NdisMRegisterDmaChannel, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMRegisterMiniport, NdisMSetAttributesEx, NdisOpenConfiguration, NdisReadEisaSlotInformation, NdisReadEisaSlotInformationEx, NdisReadMcaPosInformation, NdisReadPciSlotInformation, NdisWritePciSlotInformation


NdisMSetAttributesEx

VOID
NdisMSetAttributesEx(
 IN NDIS_HANDLE MiniportAdapterHandle,
 IN NDIS_HANDLE MiniportAdapterContext,
 IN UINT CheckForHangTimeInSeconds OPTIONAL,
IN ULONG AttributeFlags,
IN NDIS_INTERFACE_TYPE AdapterType
 );

NdisMSetAttributesEx informs the NDIS library about significant features of the caller’s NIC or virtual NIC during initialization.

Parameters

MiniportAdapterHandle
Specifies the handle input to MiniportInitialize.
MiniportAdapterContext
Specifies a handle for a resident context area allocated by MiniportInitialize.
CheckForHangTimeInSeconds
Specifies the interval in seconds at which NDIS should call the MiniportCheckForHang function, if any. Specifying zero for this parameter indicates that NDIS should call MiniportCheckForHang at NDIS’s default two-second interval and that NDIS should call the MiniportReset function at the default four-second time-out interval for pending sends and requests that NDIS holds queued to the caller subsequently.

Specifying a value greater than two extends both the check-for-hang and time-out intervals. NDIS uses double the specified check-for-hang interval as its time-out interval for the caller.

AttributeFlags
Specifies a bitmask that can be set with one or more (ORed) of the following flags:
NDIS_ATTRIBUTE_BUS_MASTER
Set if the caller’s NIC is a busmaster DMA device.
NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT
Set if NDIS should not attempt to time-out pending send packets that it holds queued to the caller. Intermediate drivers should set this flag, but NIC drivers should not.
NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT
Set if NDIS should not attempt to time-out pending query and set requests that it holds queued to the caller. Intermediate drivers should set this flag, but NIC drivers should not.
NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER
Set if the caller is an intermediate driver.
NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS
Set if NDIS should not call a Token Ring NIC driver’s MiniportReset function if Token Ring errors are indicated.
AdapterType
Specifies the I/O bus interface type of the caller’s NIC, which usually is the type of I/O bus on which the NIC is connected, as one of the following:
NdisInterfaceInternal
Specifies a host-specific internal interface.
NdisInterfaceIsa
Specifies the ISA interface.
NdisInterfaceEisa
Specifies the extended ISA (EISA) interface.
NdisInterfaceMca
Specifies the MicroChannel architecture interface.
NdisInterfaceTurboChannel
Specifies the Turbo Channel interface.
NdisInterfacePci
Specifies the Peripheral Component Interconnect (PCI) interface.
NdisInterfacePcMcia
Specifies the Personal Computer Memory Card International Association (PCMCIA) interface.

This parameter is irrelevant for intermediate drivers shich should pass zero for this argument to NdisMSetAttributesEx.

Comments

A MiniportInitialize function must call NdisMSetAttributesEx (or NdisMSetAttributes) before calling any other NdisMRegister Xxx or Ndis Xxx function that depends on the information supplied to NdisMSetAttributesEx. For example, a NIC driver’s call to NdisMAllocateMapRegisters will fail if MiniportInitialize has not yet called NdisMSetAttributesEx with the AttributeFlags set with NDIS_ATTRIBUTE_BUS_MASTER.

Intermediate drivers must call NdisMSetAttributesEx, rather than NdisMSetAttributes, and they must set the NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER in the AttributeFlags. Setting this flag causes NDIS to treat an intermediate driver as a full-duplex miniport, which prevents rare but intermittant deadlocks from occurring in the intermediate driver. Consequently, every intermediate driver must be capable of handling concurrent sends and indications.

NIC drivers can call either of these functions from MiniportInitialize, but NdisMSetAttributes does not allow its caller to adjust the interval at which a NIC driver’s MiniportCheckForHang and/or MiniportReset function(s) are called.

The value of CheckForHangTimeInSeconds determines the NDIS library’s time-out interval on sends and requests it holds queued to the caller. By default, NDIS times out queued sends and requests at twice the check-for-hang interval, after which it calls the MiniportReset function, unless the driver sets AttributeFlags with NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT and NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT when it calls NdisMSetAttributesEx. Intermediate drivers should set these flags when calling NdisMSetAttributesEx because such a driver cannot determine or control when the underlying device driver will process sends and requests.

NIC drivers should not set the NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT and NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT flags, although NDIS will honor such a specification by a NIC driver. However, NIC drivers can adjust the time-out interval at which their MiniportReset functions are called by specifying an explicit CheckForHangTimeInSeconds. For example, a NIC driver that emulates Ethernet over a modem might not complete every packet within the default time-out interval of the NDIS library. Whenever a packet appeared to time out on such a NIC, NDIS would assume that the NIC was no longer operating correctly and call the driver’s MiniportReset function. For the driver of such a NIC, calling NdisMSetAttributesEx with a CheckForHangTimeInSeconds set to something greater than two both prevents unnecessary resets and extends the interval at which its MiniportCheckForHang function, if any, is called to test the operational state of the NIC.

In general, a NIC driver must call NdisMSetAttributesEx before it calls any Ndis Xxx function that claims hardware resources in the registry for its NIC, because NDIS must have the AttributeFlags value before such a call is made and because the driver usually needs the memory at MiniportAdapterContext to store information for these calls. This restriction implies that a NIC driver’s MiniportInitialize function cannot call the following Ndis Xxx before it calls NdisMSetAttributesEx:

  • NdisMPciAssignResources

  • NdisMAllocateMapRegisters and NdisMAllocateSharedMemory

  • NdisMMapIoSpace and, consequently, the Ndis..Register Xxx functions

  • NdisMRegisterDmaChannel

  • NdisMRegisterInterrupt

  • NdisMRegisterIoPortRange and, consequently, the NdisRaw..Port Xxx functions

However, before calling NdisMSetAttributesEx, any driver’s MiniportInitialize function can call the Ndis..Configuration functions to retrieve configuration information installed in the registry. Before calling NdisMSetAttributesEx, a NIC driver can call the NdisImmediate Xxx, such as NdisImmediateReadPciSlotInformation or NdisImmediateReadSharedMemory, as well as the NdisImmediate..Port Xxx functions. MiniportInitialize also can call the bus-type-specific NdisRead Xxx functions, such as NdisReadPciSlotInformation, as long as the installed registry entry for the driver’s interface type matches the bus-type-specific NdisRead Xxx MiniportInitialize calls.

The MiniportAdapterContext handle supplied to NdisMSetAttributesEx becomes an input parameter to all MiniportXxx functions that were registered, along with MiniportInitialize, in the call to NdisMRegisterMiniport or NdisIMRegisterLayeredMiniport. Usually, this handle is a pointer to resident memory, allocated by MiniportInitialize, in which the driver maintains NIC-specific runtime state.

Callers of NdisMSetAttributesEx run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisAllocateMemory, NdisImmediateReadPciSlotInformation, NdisImmediateReadPortUchar, NdisImmediateReadPortUlong, NdisImmediateReadPortUshort, NdisImmediateReadSharedMemory, NdisIMRegisterLayeredMiniport, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMMapIoSpace, NdisMPciAssignResources, NdisMRegisterDmaChannel, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMRegisterMiniport, NdisOpenConfiguration, NdisReadEisaSlotInformation, NdisReadEisaSlotInformationEx


NdisMSetInformationComplete

VOID
NdisMSetInformationComplete(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_STATUS Status
 );

NdisMSetInformationComplete returns the final status of a completed set-information request for which the driver previously returned NDIS_STATUS_PENDING.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Status
Specifies the final status of the immediately preceding request input to MiniportSetInformation.

Comments

A miniport calls NdisMSetInformationComplete after it has carried out the operation orginally requested of its MiniportSetInformation function. This call releases the miniport’s ownership of all buffer addresses that were passed to MiniportSetInformation.

NdisMSetInformationComplete causes NDIS to return the original request to the ProtocolRequestComplete function of the driver, if any, that initiated the set-information operation when it called NdisRequest. The NDIS library also can call MiniportSetInformation directly with NDIS-initiated set-information requests, particularly during initialization just after MiniportQueryInformation returns control.

All calls to a driver’s MiniportSetInformation function are both synchronous and serialized. That is, NDIS never submits another such request until the miniport has completed the preceding request.

For more information about the system-defined OIDs that the MiniportSetInformation function handles, see Chapter 5.

Callers of NdisMSetInformationComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportSetInformation, NDIS_REQUEST, NdisRequest, ProtocolRequestComplete


NdisMSetPeriodicTimer

VOID
NdisMSetPeriodicTimer(
IN PNDIS_MINIPORT_TIMER Timer,
IN UINT MillisecondsPeriod
 );

NdisMSetPeriodicTimer sets a timer to fire periodically, thereby running an associated MiniportTimer function repeatedly at fixed intervals.

Parameters

Timer
Points to caller-supplied resident storage for a timer object previously initialized with NdisMInitializeTimer.
MillisecondsPeriod
Specifies the interval, in milliseconds, to time out before calling the MiniportTimer function unless the timer is cancelled or this interval is reset.

Comments

NdisMSetPeriodicTimer causes the driver-supplied MiniportTimer function, which was associated with the timer object when MiniportInitialize called NdisMInitializeTimer, to be run repeatedly whenever the given MillisecondsPeriod expires. At the initial call to NdisMSetPeriodicTimer, the timer object is queued until the MillisecondsPeriod expires, when the MiniportTimer function is run and the timer object is automatically requeued for the next interval.

By contrast, NdisMSetTimer causes the driver-supplied function associated with a particular timer object to run only once after the given MillisecondsToDelay expires. That is, execution of the function associated with the Timer passed to NdisMSetTimer is episodic, rather than periodic.

As a general rule, a miniport should allocate and initialize two timer objects if it calls both NdisMSetPeriodicTimer and NdisMSetTimer. Such a driver is likely to have two MiniportTimer functions with different functionality, each associated with a particular timer object when it is initialized with NdisMInitializeTimer. For example, a MiniportTimer function that runs periodically might poll device state at regular intervals, while another MiniportTimer function might retry a particular runtime operation only if it times out on the NIC.

If a miniport calls NdisMSetPeriodicTimer, NdisMCancelTimer, or NdisMSetTimer with the same Timer pointer originally passed to NdisMSetPeriodicTimer, the current call cancels its preceding call to NdisMSetPeriodicTimer, possibly resetting the MillisecondsPeriod specified originally. Any call to NdisMSet..Timer resets the given timer to expire at the interval specified in the most recent call and causes the associated MiniportTimer function to run when the most recently specified interval has expired.

Timer resolution on the host varies. Consequently, calling NdisMSetPeriodicTimer with very small periods does not necessarily cause the execution of the MiniportTimer function exactly when the specified interval expires. The minimum practicable interval to specify on Windows NT platforms is ten milliseconds.

Callers of NdisMSetPeriodicTimer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportTimer, NdisMCancelTimer, NdisMInitializeTimer, NdisMSetTimer


NdisMSetTimer

VOID
NdisMSetTimer(
IN PNDIS_MINIPORT_TIMER Timer,
IN UINT MillisecondsToDelay
 );

NdisMSetTimer sets a timer to fire after a specified interval, thereby running an associated MiniportTimer function when the timer fires.

Parameters

Timer
Points to caller-supplied resident storage for a timer object previously initialized with NdisMInitializeTimer.
MillisecondsToDelay
Specifies the interval, in milliseconds, to time out before calling the MiniportTimer function.

Comments

NdisMSetTimer causes the driver-supplied MiniportTimer function, which was associated with the timer object when MiniportInitialize called NdisMInitializeTimer, to run once after the given MillisecondsToDelay expires. Execution of the MiniportTimer function associated with the Timer passed to NdisMSetTimer is episodic, rather than periodic. A miniport must call NdisMSetTimer each time the associated timer function should be run.

By contrast, NdisMSetPeriodicTimer causes the associated MiniportTimer function to be run repeatedly whenever the given MillisecondsPeriod expires. At the initial call to NdisMSetPeriodicTimer, the timer object is queued until the MillisecondsPeriod expires, when the MiniportTimer function is run and the timer object is automatically requeued for the next interval.

As a general rule, a miniport should allocate and initialize two timer objects if it calls both NdisMSetPeriodicTimer and NdisMSetTimer. Such a driver is likely to have two MiniportTimer functions with different functionality, each associated with a particular timer object when it is initialized with NdisMInitializeTimer. For example, a MiniportTimer function that runs periodically might poll device state at regular intervals, while another MiniportTimer function might retry a particular runtime operation only if it times out on the NIC.

If a miniport calls NdisMSetTimer, NdisMCancelTimer, or NdisMSetPeriodicTimer with the same Timer pointer originally passed to NdisMSetTimer before the originally specified MillisecondsToDelay has expired, the current call cancels its preceding call to NdisMSetTimer. Any call to NdisMSet..Timer resets the given timer to expire at the interval specified in the most recent call and causes the associated MiniportTimer function to run when the most recently specified interval has expired.

Timer resolution on the host varies. Consequently, calling NdisMSetTimer with very small time-out values does not necessarily cause the execution of the MiniportTimer function exactly when the specified interval expires. The minimum practicable interval to specify on Windows NT platforms is ten milliseconds.

Callers of NdisMSetTimer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportTimer, NdisMCancelTimer, NdisMInitializeTimer, NdisMSetPeriodicTimer


NdisMSetupDmaTransfer

VOID
NdisMSetupDmaTransfer(
OUT PNDIS_STATUS Status,
IN PNDIS_HANDLE MiniportDmaHandle,
IN PNDIS_BUFFER Buffer,
IN ULONG Offset,
IN ULONG Length,
IN BOOLEAN WriteToDevice
 );

NdisMSetupDmaTransfer sets up the host DMA controller for a DMA transfer.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the request, which can be one of the following:
NDIS_STATUS_SUCCESS
The DMA controller has been set up to transfer the specified data, which has been flushed to or from the device to maintain data integrity.
NDIS_STATUS_RESOURCES
An attempt to set up the DMA controller for the transfer has failed, either because the channel designated by MiniportDmaHandle is currently in use transferring data or because the given Length is invalid.
MiniportDmaHandle
Specifies the handle returned by NdisMRegisterDmaChannel during initialization.
Buffer
Points to the buffer descriptor mapping the range of host memory from which or into which the data will be transferred.
Offset
Specifies the byte offset within the mapped buffer at which the transfer should start. Zero indicates the transfer should begin at the initial byte of the range specified at Buffer.
Length
Specifies the number of bytes of data to be transferred. The range specified by Offset and Length must be a proper subrange of that specified at Buffer.
WriteToDevice
Specifies TRUE for an outbound transfer from the system through the NIC.

Comments

Drivers of slave-DMA NICs call NdisMSetupDmaTransfer in response to incoming send requests, for which the driver sets WriteToDevice to TRUE. They set WriteToDevice to FALSE when they transfer received data from the NIC to host memory.

The caller of NdisMSetupDmaTransfer supplies a buffer descriptor mapping the host memory range that is the target of the transfer or that contains data for a download operation from the host to the NIC. To specify a transfer sized to suit the DMA constraints of its NIC, the caller can set up a subrange to be transferred with the Offset and Length parameters if necessary.

The caller must supply a buffer descriptor that specifies the host range into which received data will be transferred from the NIC when WriteToDevice is FALSE. Otherwise, the buffer descriptor at Buffer was chained to a packet descriptor input to the NIC driver’s MiniportSend or MiniportSendPackets function.

To improve performance for small transmit requests, such as a send request of less than 256K in length, a miniport can copy the packet data into an internal staging buffer and pass a driver-allocated buffer descriptor mapping that buffer to NdisMSetupDmaTransfer.

On return from NdisMSetupDmaTransfer, the host DMA controller has been programmed for the transfer. The miniport then programs its NIC for the transfer operation.

When the transfer is complete, the miniport must call NdisMCompleteDmaTransfer.

Callers of NdisMSetupDmaTransfer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportSend, MiniportSendPackets, MiniportTimer, NdisAllocateBuffer, NdisAllocateBufferPool, NdisAllocateMemory, NdisMCompleteDmaTransfer, NdisMDeregisterDmaChannel, NdisMRegisterDmaChannel


NdisMSleep

VOID
NdisMSleep(
IN ULONG MicrosecondsToSleep
);

NdisMSleep delays execution of the caller for a given interval in microseconds.

Parameters

MicrosecondsToSleep
Specifies the number of microseconds to delay.

Comments

For the given MicrosecondsToSleep, the caller’s thread of execution is put into a wait state, thereby allowing other threads to get work done on the current processor. When the given interval expires, the caller of NdisMSleep resumes execution.

An NDIS driver should always call NdisMSleep in preference to NdisStallExecution unless the driver is running at IRQL >= DISPATCH_LEVEL. NdisMSleep can accept a larger delay interval than NdisStallExecution, which should never be called with an interval greater than 50 microseconds.

NIC drivers can call NdisMSleep from their MiniportInitialize and, possibly, MiniportHalt functions when either function must wait for state changes to occur in the NIC before that function continues its operations.

Both NdisMSleep and NdisStallExecution allow a NIC driver to specify a delay consistently and independently of the clock speed of the host CPU. Neither function involves a timer object used by NdisSet(Periodic)Timer. The resolution of the host system clock varies, so very short delays can take slightly longer than the caller of NdisMSleep or NdisStallExecution specified.

Callers of NdisMSleep run at IRQL < DISPATCH_LEVEL.

See Also

MiniportHalt, MiniportInitialize, MiniportTimer, NdisMSetTimer, NdisMSetPeriodicTimer, NdisStallExecution, NdisWaitEvent


NdisMStartBufferPhysicalMapping

VOID
NdisMStartBufferPhysicalMapping(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PNDIS_BUFFER Buffer,
IN ULONG PhysicalMapRegister,
IN BOOLEAN WriteToDevice,
OUT PNDIS_PHYSICAL_ADDRESS_UNIT PhysicalAddressArray,
OUT PUINT ArraySize
 );

NdisMStartBufferPhysicalMapping sets up a mapping for a busmaster DMA transfer operation.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Buffer
Points to a buffer descriptor mapping the virtual range for the transfer. This buffer contains the data to be transferred.
PhysicalMapRegister
Specifies the zero-based index of the map register to be used for the transfer. This value must be no greater than one less than the number of map registers that the driver allocated during initialization with NdisMAllocateMapRegisters.
WriteToDevice
Specifies TRUE when the mapping is used for an outbound transfer from the system through the NIC.
PhysicalAddressArray
Points to a caller-supplied array of NDIS_PHYSICAL_UNIT structures, in which each element is defined as follows:
typedef struct _NDIS_PHYSICAL_ADDRESS_UNIT {
    NDIS_PHYSICAL_ADDRESS PhysicalAddress;
    UINT Length;
} NDIS_PHYSICAL_ADDRESS_UNIT, *PNDIS_PHYSICAL_ADDRESS_UNIT;

On return from a successful call to NdisMStartBufferPhysicalMapping, elements in this array specify the mapped logical ranges suitable for downloading to the NIC, with the following members in each element:

PhysicalAddress
Specifies the base physical address for the start of a discrete contiguous range of data to be transferred.
Length
Specifies the number of bytes in the mapped range.
ArraySize
Points to a caller-supplied variable in which NdisMStartBufferPhysicalMapping returns the number of ranges it mapped in the buffer at PhysicalAddressArray. This value is a count of the elements that contain mappings, so the caller can use this count as a bound on the number of physical address ranges it downloads to the NIC subsequently.

Comments

Busmaster NIC drivers call NdisMStartBufferPhysicalMapping in response to send requests originating in protocols bound to the NIC. That is, the buffer descriptor at Buffer usually was chained to a packet input to the NIC driver’s MiniportSend or MiniportSendPackets function.

Every protocol driver is responsible for setting up send packets to suit the limits of the underlying NIC. The NIC driver’s MiniportQueryInformation function already returned these limits in response to certain OID_GEN_XXX requests. Consequently, a miniport need not test incoming send packets for oversized frames or transmit blocks, nor for transmit buffer overflows. For more information about OIDs, see Chapter 5.

A caller of NdisMStartBufferPhysicalMapping must ensure that the array at PhysicalAddressArray is large enough to map the given buffer. A miniport can call NdisGetBufferPhysicalArraySize to determine how many elements are required to map the virtual range specified in the descriptor at Buffer.

For small transfer requests, such as those up to 256K in length, a miniport can achieve higher performance by copying the data to be transmitted into a staging buffer in the shared memory space already allocated with NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync. Because the NIC driver already has mapped virtual and physical addresses for such a shared memory range, it need not call NdisM..BufferPhysicalMapping functions for such small DMA transfers.

Data received by a busmaster NIC is transferred directly into device-accessible buffers within a shared memory block allocated by the miniport. Miniports perform explicit busmaster DMA operations only for transmits.

To transfer packet data for a send request, any miniport basically does the following:

  1. Maps the virtual range containing the data with NdisMStartBufferPhysicalMapping to get mapped device-accessible range addresses for use by its NIC

  2. Downloads the returned physical addresses to the NIC

  3. Calls NdisFlushBuffer and NdisMUpdateSharedMemory if the driver allocated cached memory

  4. Programs the NIC for the transfer operation

  5. Calls NdisMCompleteBufferPhysicalMapping, usually from the MiniportHandleInterrupt function, when the NIC has completed the transfer, which the NIC indicates by a transmit interrupt or, possibly, by a state change discovered by the NIC driver’s polling MiniportTimer function

Any mapping returned by NdisMStartBufferPhysicalMapping is valid only until the miniport calls NdisMCompleteBufferPhysicalMapping. When NdisMCompleteBufferPhysicalMapping returns control, the PhysicalMapRegister specified in the preceding call to NdisMStartBufferPhysicalMapping can be reused in a subsequent DMA operation.

Callers of NdisMStartBufferPhysicalMapping run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportQueryInformation, MiniportSend, MiniportSendPackets, MiniportTimer, NdisFlushBuffer, NdisGetBufferPhysicalArraySize, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync, NdisMCompleteBufferPhysicalMapping, NdisMUpdateSharedMemory


NdisMSynchronizeWithInterrupt

BOOLEAN
NdisMSynchronizeWithInterrupt(
IN PNDIS_MINIPORT_INTERRUPT Interrupt,
IN PVOID SynchronizeFunction,
IN PVOID SynchronizeContext
 );

NdisMSynchronizeWithInterrupt synchronizes execution of a miniport-supplied function with the MiniportISR or MiniportDisableInterrupt function.

Parameters

Interrupt
Points to the caller-supplied storage for the interrupt object initialized with NdisMRegisterInterrupt.
SynchronizeFunction
Specifies the entry point of the driver’s MiniportSynchronizeISR function.
SynchronizeContext
Points to miniport-determined context area to be passed to the MiniportSynchronizeISR function when it is called.

Return Value

NdisMSynchronizeWithInterrupt returns the Boolean value returned by MiniportSynchronizeISR.

Comments

The value returned by MiniportSynchronizeISR is also returned by NdisMSynchronizeWithInterrupt. This propagated value can be used to provide a status to the caller.

Any miniport function that shares resources with any other driver function that runs at DIRQL must use NdisMSynchronizeWithInterrupt to synchronize its access to those resources. The MiniportSynchronizeISR function also runs at DIRQL, and the shared resources are protected by a system-allocated spin lock on multiprocessor platforms. Thus, the shared resources are protected from simultaneous access by the MiniportISR or MiniportDisableInterrupt function and the caller whenever the driver is running on a multiprocessor machine.

NdisMSynchronizeWithInterrupt releases the system spin lock and restores the orginal IRQL of its caller before it returns control.

Callers of NdisMSynchronizeWithInterrupt run at IRQL <= DIRQL.

See Also

MiniportDisableInterrupt, MiniportISR, MiniportSynchronizeISR, NdisMDeregisterInterrupt, NdisMRegisterInterrupt


NdisMTransferDataComplete

VOID
NdisMTransferDataComplete(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status,
IN UINT BytesTransferred
 );

NdisMTransferDataComplete returns the packet and completion status for a transfer-data request for which the miniport previously returned NDIS_STATUS_PENDING.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Packet
Points to the protocol-allocated packet for which MiniportTransferData previously returned NDIS_STATUS_PENDING.
Status
Specifies the final NDIS_STATUS_XXX for the just completed transfer-data request.
BytesTransferred
Specifies the number of bytes actually transferred to the packet.

Comments

Whenever MiniportTransferData returns NDIS_STATUS_PENDING for a given packet, the driver must call NdisMTransferDataComplete when it has finished copying the requested data into that packet or must fail the request.

A miniport that indicates receives with NdisMIndicateReceivePacket never calls NdisMTransferDataComplete because such a driver always indicates full packets up to higher-level drivers.

Miniports that report their NIC’s media type as NdisMediumArcnet878_2 also do not call NdisMTransferDataComplete. NDIS copies all packet data such a driver indicates.

Callers of NdisMTransferDataComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportReturnPacket, MiniportTransferData, NdisAllocatePacket, NdisMIndicateReceivePacket, NdisTransferData, ProtocolReceive, ProtocolReceivePacket


NdisMTrIndicateReceive

VOID
NdisMTrIndicateReceive(
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE MiniportReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookaheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
 );

NdisMTrIndicateReceive notifies NDIS that a Token Ring packet (or some initial lookahead portion of the packet) has arrived so NDIS can forward the packet to bound protocols.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
MiniportReceiveContext
Specifies a context handle supplied by the caller. NDIS returns this handle as an input parameter to the MiniportTransferData function if it is called. The miniport can use the context area designated by this handle to determine which packet, on which NIC, is being received.
HeaderBuffer
Specifies the base virtual address of the buffered packet header.
HeaderBufferSize
Specifies the size in bytes of the packet header.
LookaheadBuffer
Specifies the base virtual address of a buffer containing the initial LookaheadBufferSize bytes of the data in the packet.
LookaheadBufferSize
Specifies the size in bytes of the lookahead buffer. This value must be at least as large as the lookahead size set when MiniportSetInformation was called with OID_GEN_CURRENT_LOOKAHEAD, or as large as the packet, whichever is smaller. When the packet is smaller than the lookahead size, the lookahead buffer contains the entire frame.

A miniport can (and should) set LookaheadBufferSize to something larger than the minimum required if sufficient received data is available on its NIC.

PacketSize
Specifies the size in bytes of the received packet data. This value does not include the HeaderBufferSize.

When PacketSize is larger than LookaheadBufferSize, a protocol driver call to NdisTransferData causes a subsequent call to the MiniportTransferData function, which transfers the remaining data in the packet.

Comments

A miniport calls NdisMTrIndicateReceive if it designates its NIC as of type NdisMedium802_5 in response to the OID_GEN_MEDIA_IN_USE query, unless the miniport indicates receives with NdisMIndicateReceivePacket.

When a miniport calls NdisMTrIndicateReceive, NDIS passes a pointer to the header of the packet and a pointer to some or all of the data in the packet to the ProtocolReceive function(s) of bound protocol driver(s). Each protocol that receives the indication can do the following:

  1. Inspect the header and data in the packet to decide whether the indication is of interest to the protocol’s client(s).

  2. If it is, copy as much of the header or data as is visible into protocol-allocated memory with NdisMoveMemory.

  3. Get the remaining data, if any, for the indication by allocating a packet descriptor with sufficient chained buffer descriptors and calling NdisTransferData. The call to NdisTransferData causes the NIC driver’s MiniportTransferData function to copy the data into the protocol-supplied packet.

For any protocol driver that receives an indication through NdisMTrIndicateReceive, the HeaderBuffer and LookaheadBuffer addresses are valid only during the current call to its ProtocolReceive function, and these buffers are read-only. A protocol driver cannot retain a pointer to the indicated packet with these associated buffers nor can it retain any pointers to these buffers for later use. Any data that a protocol driver needs from such a packet must be copied and saved during the receive indication.

The data in the header is the same as that received on the NIC. A miniport driver need not remove any headers or trailers from the data its NIC receives.

The transmitting driver adds padding to any packet that is too short for the requirements of the medium. The receiving miniport can include such padding in the data and length it subsequently indicates. Each bound protocol is responsible for detecting any such padding and ignoring it.

Callers of NdisMFddiIndicateReceive run at IRQL DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportQueryInformation, MiniportSetInformation, MiniportTimer, MiniportTransferData, NdisMTrIndicateReceiveComplete, NdisMIndicateReceivePacket, NdisMoveMemory, NdisTransferData, ProtocolReceive


NdisMTrIndicateReceiveComplete

VOID
NdisMTrIndicateReceiveComplete(
IN NDIS_HANDLE MiniportAdapterHandle
 );

NdisMTrIndicateReceiveComplete notifies NDIS that a Token Ring receive packet, identified in a preceding call to NdisMTrIndicateReceive, has been fully transferred by the NIC so that NDIS can notify the appropriate bound protocol drivers.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

After any Token Ring receive indication, a miniport eventually must call NdisMTrIndicateReceiveComplete to indicate the end of the transfer operation over the net, even if a particular packet is accepted by no bound protocol.

NdisMTrIndicateReceiveComplete both indicates the completion of a transfer operation for a receive and notifies any bound protocol that has already consumed the initial indication that it can begin postprocessing the received data.

When a miniport calls NdisMTrIndicateReceiveComplete, NDIS indicates the completion of the receive operation to each such protocol by calling its ProtocolReceiveComplete function.

ProtocolReceiveComplete functions need not operate under the severe time constraints of the corresponding ProtocolReceive functions. However, another receive indication to the ProtocolReceive function can come in while the ProtocolReceiveComplete function is running.

A miniport need not call NdisMTrIndicateReceiveComplete in one-to-one correspondence with its calls to NdisMTrIndicateReceive. That is, a NIC driver can issue a single receive-complete indication for some number of receive indications, particularly when network traffic is high. For example, when a miniport is continually indicating receives from the same source, it might call NdisMTrIndicateReceiveComplete once for every ten indications. On the other hand, a miniport might call NdisMTrIndicateReceiveComplete once for each indication when network traffic is low so that the driver can regain ownership of the memory it allocated for receives promptly.

Callers of NdisMTrIndicateReceiveComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMTrIndicateReceive, ProtocolReceive, ProtocolReceiveComplete


NdisMUnmapIoSpace

VOID
NdisMUnmapIoSpace(
IN NDIS_HANDLE MiniportAdapterHandle,
 IN PVOID VirtualAddress,
IN UINT Length
 );

NdisMUnmapIoSpace releases a virtual range mapped by an initialization-time call to NdisMMapIoSpace.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
VirtualAddress
Specifies the base virtual address for the mapped range that was returned by NdisMMapIoSpace.
Length
Specifies the number of bytes in the range that was mapped with NdisMMapIoSpace.

Comments

When a miniport is unloading, any memory range that it mapped during initialization with NdisMMapIoSpace must be released with a call to NdisMUnmapIoSpace.

The Length passed to NdisMUnmapIoSpace must match the Length originally passed to NdisMMapIoSpace.

NdisMUnmapIoSpace can be called only from a NIC driver’s MiniportInitialize and MiniportHalt functions.

Callers of NdisMUnmapIoSpace run at IRQL PASSIVE_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NdisMMapIoSpace


NdisMUpdateSharedMemory

VOID
NdisMUpdateSharedMemory(
IN NDIS_HANDLE MiniportAdapterHandle,
IN ULONG Length,
IN PVOID VirtualAddress,
IN NDIS_PHYSICAL_ADDRESS PhysicalAddress
 );

NdisMUpdateSharedMemory ensures that data, just transferred from a busmaster NIC, to be read from driver-allocated shared memory is current.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Length
Specifies the number of bytes in the range.
VirtualAddress
Specifies the base virtual address within a range returned by NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync.
PhysicalAddress
Specifies the physical address mapped to VirtualAddress.

Comments

NdisMUpdateSharedMemory guarantees that any data written by the NIC is present in host physical memory at the virtual address returned by NdisMAllocateSharedMemory or NdisMAllocateSharedMemoryAsync on certain platforms. On any other platform, a call to NdisMUpdateSharedMemory has no overhead.

Callers of NdisMUpdateSharedMemory run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisFlushBuffer, NdisMAllocateMapRegisters, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync, NdisMFreeSharedMemory, NdisMStartBufferPhysicalMapping


NdisMWanIndicateReceive

VOID
NdisMWanIndicateReceive(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE NdisLinkContext,
IN PUCHAR PacketBuffer,
IN UINT PacketSize
);

NdisMWanIndicateReceive notifies NDISWAN that a packet has been received for an established link to be forwarded to the appropriate bound protocol driver.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the indication, which can be one of the following:
NDIS_STATUS_SUCCESS
NDISWAN recognized and accepted the packet.
NDIS_STATUS_NOT_ACCEPTED
NDISWAN did not recognize the frame.

Any other status indicates that NDISWAN recognized the frame, but for some reason was unable to accept it.

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
NdisLinkContext
Specifies the handle supplied by NDISWAN in the NDIS_MAC_LINE_UP structure at the miniport’s initial call to NdisMIndicateStatus for this link.
PacketBuffer
Specifies the base virtual address of the buffer containing the received packet.
PacketSize
Specifies the size in bytes of the received packet data.

Comments

When a miniport calls NdisMWanIndicateReceive, NDISWAN indicates the packet by calling the ProtocolReceive functions of bound protocol drivers. The entire packet is available for inspection by protocols, because WAN miniports never indicate partial packets with only a lookahead portion of the packet data.

Consequently, such a protocol will never call NdisTransferData so the underlying miniport driver needs no MiniportTransferData function. The miniport always passes up an entire packet due to compression and encryption that might be applied to the packet from the transmitting node. Also, because the link is point-to-point, at least one higher-level protocol accepts each received packet.

For any protocol that receives an indication through NdisMWanIndicateReceive, the buffer at PacketBuffer is valid only during the current call to its ProtocolReceive function. Because the packet might be compressed, the entire packet must be available when this call is made. This buffer should contain the same data and size as the buffer sent by the driver on the remote node.

The data contained in the header is the same as that received on the NIC. A WAN miniport need not remove any headers or trailers from the data its NIC receives. The transmitting driver does not add padding to the packet.

Callers of NdisMWanIndicateReceive run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportWanSend, NdisMIndicateStatus, NdisMWanIndicateReceiveComplete, ProtocolReceive


NdisMWanIndicateReceiveComplete

VOID
NdisMWanIndicateReceiveComplete(
IN NDIS_HANDLE MiniportAdapterHandle
);

NdisMWanIndicateReceiveComplete notifies NDISWAN that one or more receives have been completed so that NDISWAN can notify the appropriate bound protocol(s).

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.

Comments

After any WAN receive indication, a miniport must eventually call NdisMWanIndicateReceiveComplete.

NdisMWanIndicateReceiveComplete both indicates the completion of a transfer operation for a receive and notifies any bound protocol that has already consumed the initial indication that it can begin postprocessing the received data.

When a miniport calls NdisMWanIndicateReceiveComplete, NDISWAN calls the ProtocolReceiveComplete function(s) of bound protocol driver(s). ProtocolReceiveComplete functions need not operate under the severe time constraints of the corresponding ProtocolReceive functions.

A WAN miniport need not call NdisMWanIndicateReceiveComplete in one-to-one correspondence with its calls to NdisMWanIndicateReceive. That is, a NIC driver can issue a single receive-complete indication for some number of receive indications, particularly when a sequence of receives is coming in to its NIC. Such a driver can call NdisMWanIndicateReceiveComplete after several calls to NdisMWanIndicateReceive have occurred, thereby giving bound protocols the opportunity to notify clients of received packets without incurring the overhead of processing one-to-one completion notifications. As a general rule, a miniport should call NdisMWanIndicateReceiveComplete once for every ten receive indications when network traffic is high.

On the other hand, a miniport might call NdisMWanIndicateReceiveComplete once for each indication when network traffic is low so that the driver can regain ownership of the memory it allocated for receives promptly.

A ProtocolReceive function is re-entrant, and ProtocolReceiveComplete can run concurrently with ProtocolReceive, particularly in SMP machines.

Callers of NdisMWanIndicateReceiveComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, NdisMWanIndicateReceive, ProtocolReceive, ProtocolReceiveComplete


NdisMWanSendComplete

VOID
NdisMWanSendComplete(
IN NDIS_HANDLE MiniportAdapterHandle,
IN PNDIS_WAN_PACKET Packet,
IN NDIS_STATUS Status
);

NdisMWanSendComplete returns the packet and final status of a completed send request for which the miniport previously returned NDIS_STATUS_PENDING.

Parameters

MiniportAdapterHandle
Specifies the handle originally input to MiniportInitialize.
Packet
Points to the protocol-supplied packet originally passed in to the MiniportWanSend function.
Status
Specifies the NDIS_STATUS_XXX code describing the outcome of the completed send operation, either NDIS_STATUS_SUCCESS or a miniport-determined error status value.

Comments

Whenever the MiniportWanSend function returns NDIS_STATUS_PENDING for an incoming packet, the miniport must eventually call NdisMWanSendComplete with a pointer to that packet. When MiniportWanSend returns any NDIS_STATUS_XXX except NDIS_STATUS_PENDING for a given packet, that packet is considered completed as soon as MiniportWanSend returns control.

Calling NdisMWanSendComplete does not necessarily imply that the packet has been transmitted over the network, although usually it has before a miniport calls this function. However, a miniport might call NdisMWanSendComplete as soon as it has successfully programmed its NIC for the transmit operation, particularly if its NIC is a so-called intelligent network card.

NdisMWanSendComplete forwards the completed packet to the ProtocolSendComplete function of the driver that initiated the original send operation. Then, the protocol regains ownership of the packet and all buffers that it allocated for the send.

Callers of NdisMWanSendComplete run at IRQL DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportWanSend, NdisSend, ProtocolSendComplete


NdisMWriteLogData

NDIS_STATUS
NdisMWriteLogData(
IN NDIS_HANDLE LogHandle,
IN PVOID LogBuffer,
IN UINT LogBufferSize
);

NdisMWriteLogData transfers driver-supplied information into the log file for consumption and display by a driver-dedicated Win32 application.

Parameters

LogHandle
Specifies the handle returned by NdisMCreateLog.
LogBuffer
Points to a driver-allocated buffer containing the information to be written.
LogBufferSize
Specifies how many bytes of data to copy into the log file.

Return Value

NdisMWriteLogData can return one of the following:

NDIS_STATUS_SUCCESS
The driver-supplied data at LogBuffer has been copied into the log file.
NDIS_STATUS_FAILURE
The given LogBufferSize is too large, that is, larger than the log file itself.

Comments

If the driver-dedicated application has an outstanding request for log file data, NdisMWriteLogData satisfies that request as soon as it has copied the driver-supplied information into the log file.

The miniport can supply a LogBuffer pointer to a location on the kernel stack if it is currently running at IRQL DISPATCH_LEVEL. Otherwise, LogBuffer must access a buffer that the driver allocated from nonpaged pool.

The driver must release any spin lock it is holding before calling NdisMWriteLogData.

An NDIS intermediate driver cannot call NdisMWriteLogData from any of its ProtocolXxx functions. Such a driver must call NdisIMSwitchToMiniport and make this call from a miniport context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback so that its driver-supplied MiniportCallback function can call NdisMWriteLogData.

Callers of NdisMWriteLogData can be running at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateMemory, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMCloseLog, NdisMCreateLog, NdisMFlushLog, NdisReleaseSpinLock


NDIS_OOB_DATA_FROM_PACKET

PNDIS_PACKET_OOB_DATA
NDIS_OOB_DATA_FROM_PACKET(
IN PNDIS_PACKET _Packet
);

NDIS_OOB_DATA_FROM_PACKET returns a pointer to the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor.

Return Value

NDIS_OOB_DATA_FROM_PACKET returns a pointer to the out-of-band data block associated with the given packet descriptor, or it returns NULL if there is no out-of-band information for the packet.

Comments

Lower-level NDIS drivers that supply out-of-band information with the packet descriptors they allocate for indications can use NDIS_OOB_DATA_FROM_PACKET to gain access to the out-of-band block associated with a driver-allocated packet descriptor. Such a driver would then set up out-of-band information before indicating a receive packet.

For example, an NDIS intermediate driver might copy the out-of-band information from a received packet into a fresh packet descriptor with NdisMoveMemory before forwarding the indication up to bound protocols. In these circumstances, the intermediate driver calls NdisMoveMemory with the pointers returned by NDIS_OOB_DATA_FROM_PACKET as the Source and Destination pointers and sizeof(NDIS_PACKET_OOB_DATA) as the Length.

Lower-level NDIS drivers also might use this macro when the Status of an indicated packet is returned from NdisMIndicateReceivePacket with NDIS_STATUS_SUCCESS or when MiniportReturnPacket is called with a packet descriptor to be reinitialized for reuse. In these circumstances, the driver can call NdisZeroMemory with the pointer returned by NDIS_OOB_DATA_FROM_PACKET and sizeof(NDIS_PACKET_OOB_DATA) to clear the out-of-band data block associated with the packet descriptor. Alternatively, such a driver can use the appropriate NDIS_SET_PACKET_XXX macros to reinitialize specific members in the out-of-band data block for subsequent indications with the returned packet descriptor.

Higher-level NDIS drivers can use NDIS_OOB_DATA_FROM_PACKET to reinitialize the out-of-band data block associated with the packet descriptors they allocate to specify sends when such a packet descriptor has been returned to the ProtocolSendComplete function. Consequently, drivers underlying such a protocol might use NDIS_OOB_DATA_FROM_PACKET to read the out-of-band information for an incoming send packet.

A driver should never pass a packet descriptor to NdisZeroMemory in an attempt to clear the out-of-band data block associated with a packet descriptor. Doing this destroys the given packet descriptor, renders it useless for subsequent indications or sends, and causes a memory leak. A driver must pass the pointer returned by NDIS_OOB_DATA_FROM_PACKET to zero-(re)initialize the out-of-band data block associated with a driver-allocated packet descriptor.

See Also

MiniportHandleInterrupt, MiniportReturnPacket, MiniportSend, MiniportSendPackets, MiniportTimer, NdisAllocatePacket, NDIS_GET_PACKET_HEADER_SIZE, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_STATUS, NDIS_GET_PACKET_TIME_RECEIVED, NDIS_GET_PACKET_TIME_SENT, NDIS_GET_PACKET_TIME_TO_SEND, NdisMIndicateReceivePacket, NdisMoveMemory, NDIS_PACKET_OOB_DATA, NDIS_SET_PACKET_HEADER_SIZE, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_STATUS, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_SENT, NDIS_SET_PACKET_TIME_TO_SEND, NdisZeroMemory, ProtocolReceive, ProtocolReceivePacket, ProtocolSendComplete


NdisOpenAdapter

VOID
NdisOpenAdapter(
OUT PNDIS_STATUS Status,
OUT PNDIS_STATUS OpenErrorStatus,
OUT PNDIS_HANDLE NdisBindingHandle,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE NdisProtocolHandle,
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_STRING AdapterName,
IN UINT OpenOptions,
IN PSTRING AddressingInformation
);

NdisOpenAdapter sets up a binding between the calling protocol and a particular underlying NIC driver or NDIS intermediate driver.

Parameters

Status
Points to a caller-supplied variable that can be one of the following values on return from this function:
STATUS_SUCCESS
The requested binding is now set up so the caller can use the values returned at NdisBindingHandle and SelectedMediumIndex in subsequent calls to Ndis Xxx.
NDIS_STATUS_PENDING
The requested operation is being handled asynchronously, and the caller’s ProtocolOpenAdapterComplete function will be called when the open is completed.
NDIS_STATUS_RESOURCES
The requested operation failed because NDIS could not allocate sufficient memory or initialize state it uses to track an open binding.
NDIS_STATUS_ADAPTER_NOT_FOUND
The requested operation failed because the name at AdapterName could not be found in the system object namespace.
NDIS_STATUS_UNSUPPORTED_MEDIA
The array at MediumArray did not specify any medium that is supported by NDIS or by the underlying driver.
NDIS_STATUS_CLOSING
Either the caller or the physical or virtual device designated at AdapterName is being closed.
NDIS_STATUS_OPEN_FAILED
The open attempt failed for none of the preceding specific reasons. For example, possibly NDIS could not initialize the filter package for the selected medium.
OpenErrorStatus
Points to a caller-supplied variable that can contain an NDIS_STATUS_XXX error supplying more information if NdisOpenAdapter returns an error at Status. For example, the driver of a Token Ring NIC might return a ring error in this variable.
NdisBindingHandle
Points to a caller-supplied variable in which NDIS returns a handle representing a successful binding between the caller and the given physical or virtual NIC specified at AdapterName.
SelectedMediumIndex
Points to a caller-supplied variable in which NDIS returns the index of the array element that specifies the type of media the underlying NDIS driver uses.
MediumArray
Points to an array of NDIS_MEDIUM-type values specifying the types of media the caller can support. Possible elements include any proper subset of the following:
NdisMedium802_3
Specifies an Ethernet (802.3) network.
NdisMedium802_5
Specifies a Token Ring (802.5) network.
NdisMediumFddi
Specifies a Fiber Distributed Data Interface (FDDI) network.
NdisMediumWan
Specifies a wide area network. This type covers various forms of point-to-point and WAN NICs, as well as variant address/header formats that must be negotiated between the protocol driver and the underlying driver after the binding is established.
NdisMediumLocalTalk
Specifies a LocalTalk network.
NdisMediumDix
Specifies an Ethernet network for which the drivers use the DIX Ethernet header format.
NdisMediumArcnetRaw
Specifies an ARCNET network.
NdisMediumArcnet878_2
Specifies an ARCNET (878.2) network.
NdisMediumAtm
This value is reserved for future use. Currently, NIC drivers support ATM through LAN emulation (LanE 1.0 as defined in the ATM Forum’s 1.0 specification and UNI 3.1 signaling). Such a driver should report its medium type as either of NdisMedium802_3 or NdisMedium802_5.
NdisMediumWirelessWan
Specifies a wireless network. This type covers various wireless media that do not include the infrared wireless types designated by NdisMediumIrda.
NdisMediumIrda
This value is reserved for future use in Windows NT drivers.
MediumArraySize
Specifies the number of elements at MediumArray.
NdisProtocolHandle
Specifies the handle returned by NdisRegisterProtocol.
ProtocolBindingContext
Specifies the handle for a caller-supplied resident context area in which the protocol maintains state about this binding after it has been established.
AdapterName
Points to a counted string, specified in the system-default character set, naming the NIC or the virtual adapter of an underlying NDIS driver that exports a set of upper-edge (MiniportXxx) functions. For Windows NT drivers, this counted string contains Unicode characters.
OpenOptions
Specifies a bitmask containing flags the caller passes to the next-lower driver, assumed to be a NIC driver. Currently, this parameter is reserved for system use.
AddressingInformation
Points to an optional variable-length counted string containing information specific to the underlying NIC that the NIC driver can use to program the netcard. This pointer can be NULL.

If it is supplied, the addressing information must remain valid until the open operation completes. An underlying NIC driver that supports an asynchronous modem can use this information for dialing.

Comments

Protocol drivers call NdisOpenAdapter either from their DriverEntry or ProtocolBindAdapter functions. NDIS intermediate drivers usually make this call from their ProtocolBindAdapter functions.

The string at AdapterName remains valid only until NdisOpenAdapter returns control, even if it returns NDIS_STATUS_PENDING at Status.

The variables at NdisBindingHandle and SelectedMediumIndex should be ignored until the ProtocolOpenAdapterComplete function is called if NdisOpenAdapter returns NDIS_STATUS_PENDING. Because these variables can remain invalid until ProtocolOpenAdapterComplete is called, they cannot be on the stack. Usually, these variables reside in the ProtocolBindingContext area since this handle is an input parameter to ProtocolOpenAdapterComplete.

A protocol driver should keep the handle returned at NdisProtocolHandle. It is a required parameter to other Ndis Xxx functions that the driver calls subsequently. The supplied ProtocolBindingContext is an input parameter to the caller’s ProtocolXxx functions.

The caller uses the value returned at SelectedMediumIndex in subsequent calls to NdisRequest. The OIDs it sets in the request packet depend on the returned NdisMedium Xxx. For example, if NdisMediumWan is returned at SelectedMediumIndex, the protocol driver calls NdisRequest specifying OID_WAN_MEDIUM_SUBTYPE in a query to determine which of the WAN media types the underlying driver uses.

If a previously issued global query of OID_NETWORK_TYPE for wireless media indicates that the driver and underlying NIC support more than one NdisMediumWirelessWan-type medium, the protocol must select one of the supported media as soon as NDIS has set up the binding and before the protocol selects the header format.

As another example, if NdisMedium802_3 is returned, a protocol driver can determine whether the underlying driver supports packet priority by calling NdisRequest specifying OID_802_3_MAC_OPTIONS as a query to check whether the underlying driver sets the flags with NDIS_802_3_MAC_OPTION_PRIORITY. If this flag is set when the query is complete, the protocol driver can pass down prioritized packets to the underlying NIC driver with NdisSendPackets. The protocol driver can expect its ProtocolReceivePacket function to get indications of prioritized packets if the underlying driver also supports multipacket receive indications.

For more information about the general and medium-specific OIDs that protocol drivers use to negotiate with a just-bound NDIS driver, see Chapter 5.

Callers of NdisOpenAdapter run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, MiniportQueryInformation, MiniportSendPackets, MiniportSetInformation, NdisCloseAdapter, NdisIMInitializeDeviceInstance, NdisIMRegisterLayeredMiniport, NdisMIndicateReceivePacket, NDIS_PACKET_OOB_DATA, NdisRegisterProtocol, NdisRequest, NdisSendPackets, ProtocolBindAdapter, ProtocolOpenAdapterComplete, ProtocolReceivePacket


NdisOpenConfiguration

VOID
NdisOpenConfiguration(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE ConfigurationHandle,
IN NDIS_HANDLE WrapperConfigurationContext
);

NdisOpenConfiguration returns a handle for an NDIS NIC driver’s ..\DriverName\Parameters registry key.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of its attempt to open the registry key. Possible return values are:
NDIS_STATUS_SUCCESS
The value at ConfigurationHandle is valid.
NDIS_STATUS_FAILURE
The key could not be opened.
ConfigurationHandle
Points to a caller-supplied variable in which this function returns a handle to the registry HKEY_LOCAL_MACHINE\System\CurrentControlSet
\Services\DriverName\Parameters key. Value entries under this key were provided by the driver’s installation script.
WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.

Comments

With the handle returned by NdisOpenConfiguration, an NDIS miniport can call NdisReadConfiguration one or more times to extract configuration information about its NIC from the registry. A miniport also can call NdisReadNetworkAddress with this handle to retrieve software-configurable network address information that was stored in the registry when its NIC was installed in the current machine.

At a minimum, a miniport’s installation script must store the following configuration information in the registry:

  • The NIC’s I/O bus interface type

  • If it is not on an ISA bus, the bus number

  • The NIC’s adapter ID value, which, depending on the I/O bus interface type, is equivalent one of following bus-type-specific configuration data values:

    • If the NIC is on an EISA bus, the value of the EISA CompressedId

    • If the NIC is on an MCA bus, the value of the POS AdapterId

    • If the NIC is on a PCI bus, the value of the PCI DeviceId

For more information about installing Windows NT drivers, see the Programmer’s Guide.

Drivers of NICs on ISA buses usually get bus-relative configuration parameters for a NIC from the registry with NdisReadConfiguration. Drivers of NICs on other types of I/O buses also can call Ndis Xxx bus-type-specific configuration functions, such as NdisReadEisaSlotInformation, to get bus-relative NIC-specific configuration parameters suitable for calling functions such as NdisMRegisterInterrupt, NdisMMapIoSpace, and/or NdisMRegisterIoPortRange.

A NIC driver should call NdisMSetAttributes or NdisMSetAttributesEx to identify the type of I/O bus on which its NIC is likely to be found before that driver calls any of the bus-type-specific configuration functions.

After a driver has consumed and, possibly, modified the registry configuration information, the driver must call NdisCloseConfiguration to release the handle obtained from NdisOpenConfiguration and to free any temporary storage NDIS allocated in the driver’s calls to NdisReadConfiguration, NdisReadNetworkAddress, and/or NdisWriteConfiguration.

Callers of NdisOpenConfiguration run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisCloseConfiguration, NdisIMRegisterLayeredMiniport, NdisMMapIoSpace, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMRegisterMiniport, NdisMSetAttributes, NdisMSetAttributesEx, NdisOpenProtocolConfiguration, NdisReadBindingInformation, NdisReadConfiguration, NdisReadEisaSlotInformation, NdisReadMcaPosInformation, NdisReadNetworkAddress, NdisReadPciSlotInformation, NdisRegisterProtocol, NdisWriteConfiguration


NdisOpenFile

VOID
NdisOpenFile(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE FileHandle,
OUT PUINT FileLength,
IN PNDIS_STRING FileName,
IN NDIS_PHYSICAL_ADDRESS HighestAcceptableAddress
);

NdisOpenFile returns a handle for an opened file.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the open file operation, which can be one of the following:
NDIS_STATUS_SUCCESS
The handle at FileHandle is valid for a subsequent call to NdisMapFile.
NDIS_STATUS_FILE_NOT_FOUND
The given string at FileName did not specify a name found in the system object namespace.
NDIS_STATUS_RESOURCES
NDIS could not allocate the resources it needed to open the file and allocate a buffer for the file contents.
NDIS_STATUS_ERROR_READING_FILE
The specified file’s data could not be read into system memory for subsequent access by the caller.
FileHandle
Points to a caller-supplied variable in which this function returns the handle of the opened file if the call succeeds.
FileLength
Points to a caller-supplied variable in which this function writes the number of bytes of data in the opened file if the call succeeds.
FileName
Points to a buffered, caller-initialized counted string, in the system-default character set, naming the file to be opened. For Windows NT drivers, this string contains Unicode characters.
HighestAcceptableAddress
Specifies the highest physical address in which the file data can be stored, or specifies -1 if the driver places no restrictions.

Comments

NdisOpenFile opens a disk file, typically a file the driver will later download to program an intelligent NIC. NdisOpenFile also allocates storage to hold file contents for the driver’s subsequent call to NdisMapFile.

A NIC driver should call NdisOpenFile only from MiniportInitialize.

When NdisOpenFile returns, the NIC driver can access file data by calling NdisMapFile. It can call NdisUnmapFile to page out the file so it does not consume resources unnecessarily while the driver is not accessing the file data. When finished using the file, MiniportInitialize must call NdisCloseFile.

Callers of NdisOpenFile run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMCreateLog, NdisCloseFile, NdisMapFile, NdisUnmapFile


NdisOpenProtocolConfiguration

VOID
NdisOpenProtocolConfiguration(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE ConfigurationHandle,
IN PNDIS_STRING ProtocolSection
);

NdisOpenProtocolConfiguration returns a handle for the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
\NICDriverInstance\Parameters\ProtocolName registry key.

Parameters

Status
Points to a caller-supplied variable that is set to NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE on return from this function.
ConfigurationHandle
Points to a caller-supplied variable in which this function returns the handle to the registry key for the named driver to which the caller will bind itself later. Value entries under this key contain protocol-installed, adapter-specific information.
ProtocolSection
Points to a counted string, specified in the system-default character set, naming the NDIS driver, effectively the adapter, for which the Parameters\ProtocolName key in the registry protocol section should be opened. For Windows NT protocols, this counted string contains Unicode characters.

For NDIS drivers that have a ProtocolBindAdapter function, this pointer is the SystemSpecific1 input parameter to ProtocolBindAdapter.

Comments

This function returns a handle that the caller can pass subsequently to the Ndis..Configuration functions to retrieve or store information in the registry.

A protocol driver calls NdisOpenProtocolConfiguration to get a registry key handle to be passed to NdisReadConfiguration or NdisWriteConfiguration if it or its installation script stored adapter-specific configuration information in the protocol section of the registry from which the protocol sets up its bindings.

Windows NT protocols call kernel-mode support routines open their own \DriverName\Parameters registry keys. For more information about these support routines, see the Kernel-Mode Driver Reference.

Callers of NdisOpenProtocolConfiguration run at IRQL PASSIVE_LEVEL.

See Also

NdisCloseConfiguration, NdisInitializeString, NdisInitUnicodeString, NdisReadConfiguration, NdisWriteConfiguration, ProtocolBindAdapter


NdisOverrideBusNumber

This function is obsolete.


NDIS_PAGABLE_FUNCTION

NDIS_PAGABLE_FUNCTION(FunctionName)

NDIS_PAGABLE_FUNCTION is a macro used to mark a driver function as pageable code.

Parameters

FunctionName
Specifies the name of the driver function to be made pageable.

Comments

Any driver function marked with NDIS_PAGABLE_FUNCTION can be paged out of memory after it returns control and paged back into memory if it is called subsequently.

For example, such a function is declared in the driver source as follows:

VOID miniportXxx(...);
#pragma NDIS_PAGABLE_FUNCTION(miniportXxx)
    ...  //other function declarations
VOID
miniportXxx(...)
{
    ...
}
    ...

Because Windows NT in-page operations run at IRQL < DISPATCH_LEVEL, any NDIS driver function that can possibly execute at IRQL >= DISPATCH_LEVEL cannot be made pageable. Marking a driver function pageable when it runs at IRQL >= DISPATCH_LEVEL can cause fatal page faults in that driver.

Usually, NIC drivers can use this macro on their MiniportInitialize and MiniportHalt functions. A miniport also can use this macro on internal driver functions called only by MiniportInitialize or MiniportHalt, as long as the functions marked pageable never call any of the Ndis Xxx that raise IRQL, such as NdisAcquireSpinLock.

NDIS protocol drivers can use this macro on any driver function that always runs at IRQL < DISPATCH_LEVEL. Such a function is never called at raised IRQL. Such a function cannot call driver or system-supplied functions that run at IRQL >= DISPATCH_LEVEL.

See Also

MiniportHalt, MiniportInitialize, NDIS_INIT_FUNCTION, ProtocolBindAdapter, ProtocolTranslate, ProtocolUnbindAdapter


NDIS_PHYSICAL_ADDRESS_CONST

VOID
NDIS_PHYSICAL_ADDRESS_CONST(
IN ULONG Low,
 IN LONG High
);

NDIS_PHYSICAL_ADDRESS_CONST initializes a static constant of type physical address.

Parameters

Low
Specifies the low-order bytes of the physical address.
High
Specifies the high-order bytes of the physical address.

Comments

A driver can be running at any IRQL when it uses the NDIS_PHYSICAL_ADDRESS_CONST macro.

See Also

NdisGetPhysicalAddressHigh, NdisGetPhysicalAddressLow, NdisMAllocateSharedMemory, NdisMAllocateSharedMemoryAsync, NdisSetPhysicalAddressHigh, NdisSetPhysicalAddressLow


NDIS_PORT_TO_PORT

This macro is reserved for use by NDIS.


NdisPrintString

ULONG
NdisPrintString(
PNDIS_STRING String
);

NdisPrintString displays a given string on the debugging window.

Parameters

String
Points to a counted string in the system default character set.

Return Value

NdisPrintString returns the number of characters written to the debugging window.

Comments

NdisPrintString supplies the equivalent of printf debugging support.

For more information about debugging, see the Programmer’s Guide.

See Also

NdisMCreateLog, NdisInitAnsiString, NdisInitializeString, NdisInitUnicodeString


NdisQueryBuffer

VOID
NdisQueryBuffer(
IN PNDIS_BUFFER Buffer,
OUT PVOID *VirtualAddress OPTIONAL,
OUT PUINT Length
);

NdisQueryBuffer retrieves the size of the range, and optionally its base virtual address, from a buffer descriptor.

Parameters

Buffer
Points to the buffer descriptor.
VirtualAddress
Points to a caller-supplied variable in which this function returns the base virtual address of the range described.
Length
Points to to a caller-supplied variable in which this function returns the number of bytes in the virtual range.

Comments

NDIS drivers use NdisQueryBuffer to retrieve information from the buffer descriptors chained to incoming packets for subsequent transfer operations.

The driver must release any spin lock it is holding before calling NdisQueryBuffer.

Callers of NdisQueryBuffer run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportSend, MiniportSendPackets, MiniportTimer, MiniportTransferData, MiniportWanSend, NdisAllocateBuffer, NdisGetFirstBufferFromPacket, NdisMoveFromMappedMemory, NdisMoveMemory, NdisMoveToMappedMemory, NdisQueryBufferOffset, NdisQueryPacket, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisReleaseSpinLock, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, ProtocolReceive, ProtocolReceivePacket


NdisQueryBufferOffset

VOID
NdisQueryBufferOffset(
IN PNDIS_BUFFER Buffer,
OUT PUINT Offset,
OUT PUINT Length
);

NdisQueryBufferOffset retrieves the base virtual address of the range specified in a given buffer descriptor.

Parameters

Buffer
Points to the buffer descriptor.
Offset
Points to a caller-supplied variable in which this function returns the zero-based byte offset within the physical page containing the buffer.
Length
Points to a caller-supplied variable in which this function returns the size in bytes of the given buffer.

Comments

NdisQueryBufferOffset returns the offset within a physical page at which a given buffer begins. Usually, the buffer descriptor was part of a packet chain.

NdisQueryBufferOffset runs faster than NdisQueryBuffer. NDIS drivers call NdisQueryBufferOffset in situations where they need to constrain packets by certain criteria. For example, NdisQueryBufferOffset can be used to determine if a buffer crosses a page boundary, if it is odd-byte aligned, and so forth.

Callers of NdisQueryBufferOffset run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateBuffer, NdisQueryBuffer, NdisQueryPacket, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront


NdisQueryMapRegisterCount

NDIS_STATUS
NdisQueryMapRegisterCount(
IN NDIS_INTERFACE_TYPE BusType,
OUT PUINT MapRegisterCount
);

NdisQueryMapRegisterCount returns the maximum number of available map registers in the current platform.

Parameters

BusType
Specifies the I/O bus interface type of the caller’s busmaster DMA NIC, which usually is the type of I/O bus on which the NIC is connected, as one of the following:
NdisInterfaceInternal
Specifies a host-specific internal interface.
NdisInterfaceIsa
Specifies the ISA interface.
NdisInterfaceEisa
Specifies the extended ISA (EISA) interface.
NdisInterfaceMca
Specifies the MicroChannel architecture interface.
NdisInterfaceTurboChannel
Specifies the Turbo Channel interface.
NdisInterfacePci
Specifies the Peripheral Component Interconnect (PCI) interface.
NdisInterfacePcMcia
Specifies the Personal Computer Memory Card International Association (PCMCIA) interface.
MapRegisterCount
Points to a caller-supplied variable in which this function returns the maximum number of map registers available to the caller.

Return Value

NdisQueryMapRegisterCount returns NDIS_STATUS_SUCCESS if the value at MapRegisterCount is valid; otherwise, it returns NDIS_STATUS_NOT_SUPPORTED.

Comments

NdisQueryMapRegisterCount returns information useful only to drivers of busmaster DMA devices.

Busmaster NIC miniports can call NdisQueryMapRegisterCount only during initialization. Such a driver’s MiniportInitialize function should call NdisQueryMapRegisterCount before it calls NdisMAllocateMapRegisters.

Callers of NdisQueryMapRegisterCount run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMAllocateMapRegisters


NdisQueryPacket

VOID
NdisQueryPacket(
IN PNDIS_PACKET Packet,
OUT PUINT PhysicalBufferCount OPTIONAL,
OUT PUINT BufferCount OPTIONAL,
OUT PNDIS_BUFFER *FirstBuffer OPTIONAL,
OUT PUINT TotalPacketLength OPTIONAL
);

NdisQueryPacket returns information about a given packet.

Parameters

Packet
Points to a packet descriptor.
PhysicalBufferCount
Points to a caller-supplied variable in which this function returns the maximum number of physical breaks mapped by the buffer descriptors chained to the given packet. This parameter can be NULL.
BufferCount
Points to a caller-supplied variable in which this function returns the number of buffer descriptors chained to the given packet. This parameter can be NULL.
FirstBuffer
Points to a caller-supplied variable in which this function returns a pointer to the initial buffer descriptor chained to the given packet. This parameter can be NULL.
TotalPacketLength
Points to a caller-supplied variable in which this function returns the number of bytes of packet data mapped by all chained buffer descriptors. This parameter can be NULL.

Comments

NdisQueryPacket returns caller-selected information about a given packet. The caller must specify at least one of the PhysicalBufferCount, BufferCount, FirstBuffer, and TotalPacketLength pointers, along with the Packet parameter. A caller can specify explicit pointers for all parameters.

Specifying an explicit PhysicalBufferCount pointer is equivalent to summing calls to NdisGetBufferPhysicalArraySize for each buffer descriptor in the packet descriptor. A driver can call NdisGetNextBuffer as many times as necessary to retrieve pointers to any subsequent buffer descriptors in the chain, unless the call returns zero indicating the packet descriptor has no data.

If the caller supplies an explicit BufferCount pointer, NdisQueryPacket returns the number of buffer descriptors in the packet chain. The returned value can be used as a loop counter to retrieve buffer descriptors in the chain with either of the NdisUnchainBuffer Xxx functions.

If the caller supplies an explicit FirstBuffer pointer but the packet’s buffer chain is empty, NdisQueryPacket returns NULL at FirstBuffer. Otherwise, the caller can use NdisQueryBuffer subsequently to get details about the initial buffer descriptor for the packet. As a faster alternative, drivers can call NdisGetFirstBufferFromPacket to get a pointer to the initial buffer descriptor.

If the caller supplies an explicit TotalPacketLength pointer, NdisQueryPacket returns the total number of bytes specified by every buffer descriptor chained to the given packet.

Callers of NdisQueryPacket can run at any IRQL.

See Also

MiniportSend, MiniportSendPackets, MiniportTransferData, MiniportWanSend, NdisGetBufferPhysicalArraySize, NdisGetFirstBufferFromPacket, NdisGetNextBuffer, NdisQueryBuffer, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, ProtocolReceive, ProtocolReceivePacket


NdisQueryReceiveInformation

NDIS_STATUS
NdisQueryReceiveInformation(
IN NDIS_HANDLE NdisBindingHandle,
IN NDIS_HANDLE MacContext,
OUT PLONGLONG TimeSent OPTIONAL,
OUT PLONGLONG TimeReceived OPTIONAL,
IN PUCHAR Buffer,
IN UINT BufferSize,
OUT PUINT SizeNeeded
);

NdisQueryReceiveInformation returns information from the out-of-band data block associated with a packet descriptor input to the ProtocolReceive function.

Parameters

NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the target NIC or the virtual adapter of the next-lower driver to which the caller is bound.
MacContext
Specifies the MacReceiveContext handle input to the caller’s ProtocolReceive function.
TimeSent
Points to a caller-supplied variable in which this function returns the TimeSent timestamp, if any, for the received packet. This parameter can be NULL.
TimeReceived
Points to a caller-supplied variable in which this function returns the TimeReceived timestamp, if any, for the received packet. This parameter can be NULL.
Buffer
Points to a caller-supplied buffer in which this function returns a copy of any medium-specific out-of-band information, such as packet priority, associated with the received packet.
BufferSize
Specifies the size in bytes of the supplied buffer.
SizeNeeded
Points to a caller-supplied variable in which this function returns how large a buffer should be supplied if the given BufferSize is too small.

Return Value

NdisQueryReceiveInformation can return one of the following values:

NDIS_STATUS_SUCCESS
The caller can use the medium-specific information returned at Buffer and/or the value(s) returned at TimeSent and TimeReceived.
NDIS_STATUS_BUFFER_TOO_SHORT
The amount of medium-specific information was too large to fit at Buffer according to the given BufferSize. SizeNeeded is set to the number of bytes of medium-specific information available for the indication.
NDIS_STATUS_NOT_SUPPORTED
The underlying driver does not indicate receives with NdisMIndicateReceivePacket and, therefore, there is no out-of-band information available with the given packet descriptor.

Comments

The ProtocolReceive functions of protocol drivers that do not supply a ProtocolReceivePacket function can call NdisQueryReceiveInformation when such a driver is bound to an underlying driver that supplies medium-specific information, such as packet priority, and timestamps when it indicates receives.

Whenever the underlying driver calls NdisMIndicateReceivePacket with a packet descriptor for which the Status member in the associated NDIS_PACKET_OOB_DATA block is set to NDIS_STATUS_RESOURCES, the ProtocolReceive functions of bound protocols are called once with each packet descriptor, rather than their ProtocolReceivePacket functions. The ProtocolReceive function of such a driver can call NdisQueryReceiveInformation to retrieve a copy of any medium-specific information and optionally the TimeSent and TimeReceived values if either or both of these timestamps were supplied by the indicating driver.

The MacContext handle passed to NdisQueryReceiveInformation is opaque to the caller of NdisQueryReceiveInformation and reserved for use by the underlying driver.

Callers of NdisQueryReceiveInformation run at IRQL <= DISPATCH_LEVEL.

See Also

NdisMIndicateReceivePacket, NDIS_PACKET_OOB_DATA, ProtocolReceive, ProtocolReceivePacket


NdisQuerySendFlags

Use NdisGetPacketFlags instead.


NdisRawReadPortBufferUchar

VOID
NdisRawReadPortBufferUchar(
IN ULONG Port,
OUT PUCHAR Buffer,
IN ULONG Length
);

NdisRawReadPortBufferUchar reads a specified number of bytes into a caller-supplied buffer.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Buffer
Points to a caller-allocated buffer, in resident memory, into which the bytes will be transferred from the NIC’s port. The caller must allocate a buffer at least sizeof(Length).
Length
Specifies how many bytes to transfer from the NIC.

Comments

NdisRawReadPortBufferUchar reads each byte, one at a time, from the given I/O port into the given buffer.

Callers of NdisRawReadPortBufferUchar can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUlong, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawWritePortBufferUchar


NdisRawReadPortBufferUlong

VOID
NdisRawReadPortBufferUlong(
IN ULONG Port,
OUT PULONG Buffer,
IN ULONG Length
);

NdisRawReadPortBufferUlong reads a specified number of ULONGs into a caller-supplied buffer.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Buffer
Points to a caller-allocated buffer, in resident memory, into which the ULONGs will be transferred from the NIC. The caller must allocate a buffer at least (sizeof(ULONG) * Length).
Length
Specifies how many ULONGs to transfer from the NIC.

Comments

NdisRawReadPortBufferUlong reads each ULONG value, one at a time, from the given I/O port into the given buffer.

Callers of NdisRawReadPortBufferUlong can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUshort, NdisRawReadPortUlong, NdisRawWritePortBufferUlong


NdisRawReadPortBufferUshort

VOID
NdisRawReadPortBufferUshort(
IN ULONG Port,
OUT PUSHORT Buffer,
IN ULONG Length
);

NdisRawReadPortBufferUshort reads a specified number of USHORTs into a caller-supplied buffer.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Buffer
Points to a caller-allocated buffer, in resident memory, into which the USHORTs will be transferred from the NIC. The caller must allocate a buffer at least (sizeof(USHORT) * Length).
Length
Specifies how many USHORTs to transfer from the NIC.

Comments

NdisRawReadPortBufferUshort reads each USHORT value, one at a time, from the given I/O port into the given buffer.

Callers of NdisRawReadPortBufferUshort can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUchar, NdisRawReadPortBufferUlong, NdisRawReadPortUshort, NdisRawWritePortBufferUshort


NdisRawReadPortUchar

VOID
NdisRawReadPortUchar(
IN ULONG Port,
OUT PUCHAR Data
);

NdisRawReadPortUchar reads a byte from a given I/O port on the NIC.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Data
Points to a caller-supplied variable in which this function returns a byte read in from the port.

Comments

NdisRawReadPortUchar runs faster than NdisImmediateReadPortUchar because it need not map a bus-relative I/O port address onto a host-dependent logical port address at every call.

Callers of NdisRawReadPortUchar can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUchar, NdisRawReadPortUlong, NdisRawReadPortUshort, NdisRawWritePortUchar


NdisRawReadPortUlong

VOID
NdisRawReadPortUlong(
IN ULONG Port,
OUT PULONG Data
);

NdisRawReadPortUlong reads a ULONG value from a given I/O port on the NIC.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Data
Points to a caller-supplied variable in which this function returns a ULONG value read in from the port.

Comments

NdisRawReadPortUlong runs faster than NdisImmediateReadPortUlong because it need not map a bus-relative I/O port address onto a host-dependent logical port address at every call.

Callers of NdisRawReadPortUlong can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUlong, NdisRawReadPortUchar, NdisRawReadPortUshort, NdisRawWritePortUlong


NdisRawReadPortUshort

VOID
NdisRawReadPortUshort(
IN ULONG Port,
OUT PUSHORT Data
);

NdisRawReadPortUshort reads a USHORT value from a given I/O port on the NIC.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Data
Points to a caller-supplied variable in which this function returns a USHORT value read in from the port.

Comments

NdisRawReadPortUshort runs faster than NdisImmediateReadPortUshort because it need not map a bus-relative I/O port address onto a host-dependent logical port address at every call.

Callers of NdisRawReadPortUshort can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUshort, NdisRawReadPortUchar, NdisRawReadPortUlong, NdisRawWritePortUshort


NdisRawWritePortBufferUchar

VOID
NdisRawWritePortBufferUchar(
IN ULONG Port,
IN PUCHAR Buffer,
IN ULONG Length
);

NdisRawWritePortBufferUchar writes a specified number of bytes from a caller-supplied buffer to a given I/O port.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Buffer
Points to a caller-allocated resident buffer containing the bytes to be written.
Length
Specifies the number of bytes to write to the I/O port.

Comments

A NIC driver calls NdisRawWritePortBufferUchar to transfer a sequence of bytes, one at a time, to its NIC.

NdisRawWritePortBufferUchar runs significantly faster than NdisImmediateWritePortBufferUchar because it need not map a bus-relative port address onto a host-dependent logical port address at every call.

Callers of NdisRawWritePortBufferUchar can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortBufferUshort, NdisRawWritePortUchar


NdisRawWritePortBufferUlong

VOID
NdisRawWritePortBufferUlong(
IN ULONG Port,
IN PULONG Buffer,
IN ULONG Length
);

NdisRawWritePortBufferUlong writes a specified number of ULONG values from a caller-supplied buffer to a given I/O port.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Buffer
Points to a caller-allocated resident buffer containing the ULONGs to be written.
Length
Specifies the number of ULONGs to write to the I/O port.

Comments

A NIC driver calls NdisRawWritePortBufferUlong to transfer a sequence of ULONGs, one at a time, to its NIC.

NdisRawWritePortBufferUlong runs significantly faster than NdisImmediateWritePortBufferUlong because it need not map a bus-relative port address onto a host-dependent logical port address at every call.

Callers of NdisRawWritePortBufferUlong can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUlong, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUshort, NdisRawWritePortUlong


NdisRawWritePortBufferUshort

VOID
NdisRawWritePortBufferUshort(
IN ULONG Port,
IN PUSHORT Buffer,
IN ULONG Length
);

NdisRawWritePortBufferUshort writes a specified number of USHORT values from a caller-supplied buffer to a given I/O port.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Buffer
Points to a caller-allocated resident buffer containing the USHORTs to be written.
Length
Specifies the number of USHORTs to write to the I/O port.

Comments

A NIC driver calls NdisRawWritePortBufferUshort to transfer a sequence of USHORTs, one at a time, to its NIC.

NdisRawWritePortBufferUshort runs significantly faster than NdisImmediateWritePortBufferUshort because it need not map a bus-relative port address onto a host-dependent logical port address at every call.

Callers of NdisRawWritePortBufferUshort can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortBufferUshort, NdisRawWritePortBufferUchar, NdisRawWritePortBufferUlong, NdisRawWritePortUshort


NdisRawWritePortUchar

VOID
NdisRawWritePortUchar(
IN ULONG Port,
IN UCHAR Data
);

NdisRawWritePortUchar writes a byte to an I/O port on the NIC.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Data
Specifies the byte to be written.

Comments

NdisRawWritePortUchar runs faster than NdisImmediateWritePortUchar because it need not map a bus-relative I/O port address onto a host-dependent logical port address at every call.

Callers of NdisRawWritePortUchar can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortUchar, NdisRawWritePortBufferUchar, NdisRawWritePortUlong, NdisRawWritePortUshort


NdisRawWritePortUlong

VOID
NdisRawWritePortUlong(
IN ULONG Port,
IN ULONG Data
);

NdisRawWritePortUlong writes a ULONG value to an I/O port on the NIC.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Data
Specifies the ULONG to be written.

Comments

NdisRawWritePortUlong runs faster than NdisImmediateWritePortUlong because it need not map a bus-relative I/O port address onto a host-dependent logical port address at every call.

Callers of NdisRawWritePortUlong can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortUlong, NdisRawWritePortBufferUlong, NdisRawWritePortUchar, NdisRawWritePortUshort


NdisRawWritePortUshort

VOID
NdisRawWritePortUshort(
IN ULONG Port,
IN USHORT Data
);

NdisRawWritePortUshort writes a USHORT value to an I/O port on the NIC.

Parameters

Port
Specifies the I/O port. This address falls in a range that was mapped during initialization with NdisMRegisterIoPortRange.
Data
Specifies the USHORT to be written.

Comments

NdisRawWritePortUshort runs faster than NdisImmediateWritePortUshort because it need not map a bus-relative I/O port address onto a host-dependent logical port address at every call.

Callers of NdisRawWritePortUshort can run at any IRQL.

See Also

MiniportInitialize, NdisMRegisterIoPortRange, NdisRawReadPortUshort, NdisRawWritePortBufferUshort, NdisRawWritePortUchar, NdisRawWritePortUlong


NdisReadConfiguration

VOID
NdisReadConfiguration(
OUT PNDIS_STATUS Status,
OUT PNDIS_CONFIGURATION_PARAMETER *ParameterValue,
IN NDIS_HANDLE ConfigurationHandle,
IN PNDIS_STRING Keyword,
IN NDIS_PARAMETER_TYPE ParameterType
);

NdisReadConfiguration returns the value of a named entry of the specified type from the registry, given the handle to an open registry key.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The buffer specified at ParameterValue contains the returned configuration information.
NDIS_STATUS_RESOURCES
NDIS could not allocate resources, usually enough memory, to return the requested information.
NDIS_STATUS_FAILURE
The requested information could not be found under the opened registry key designated by the ConfigurationHandle.
ParameterValue
Points to a caller-supplied variable in which a successful call to NdisReadConfiguration returns a pointer to an NDIS_CONFIGURATION_PARAMETER structure, defined as follows:
typedef struct _NDIS_CONFIGURATION_PARAMETER {
    NDIS_PARAMETER_TYPE ParameterType;
    union {
        ULONG IntegerData;
        NDIS_STRING StringData;
    } ParameterData;
} NDIS_CONFIGURATION_PARAMETER, *PNDIS_CONFIGURATION_PARAMETER;

The members of this structure contain the following data:

ParameterType
Matches the value at ParameterType if the call is successful.
ParameterData
Specifies the value of the given named entry. If ParameterType is a string type, this member is a pointer to a buffered, counted string in the system-default character set. For Windows NT drivers, such a string contains Unicode characters.
ConfigurationHandle
Specifies the handle returned by NdisOpenConfiguration to a NIC driver’s ..\DriverName\Parameters registry key or by NdisOpenProtocolConfiguration to a protocol’s ..\NICDriverInstance\Parameters\ProtocolName key in the protocol section of the registry.
Keyword
Points to a caller-supplied counted string, in the system-default character set, specifying the name of the entry under the open registry key for which to return the value.

This parameter also can point to one of the following predefined global keywords for all NDIS drivers:

Predefined Keyword

Predefined Return Values

Environment

NdisEnvironmentWindowsNt
NdisEnvironmentWindows

ProcessorType

NdisProcessorX86
//
// Following possible if and only if
// current machine’s Environment ==
// NdisEnvironmentWindowsNt
//
NdisProcessorMips
NdisProcessorAlpha
NdisProcessorPpc

ParameterType
Specifies the type of the value entry as one of the following:
NdisParameterInteger
Specifies an integer in decimal notation.
NdisParameterHexInteger
Specifies an integer in hexadecimal notation.
NdisParameterString
Specifies a string of type NDIS_STRING. For Windows NT drivers, this is a counted Unicode string.
NdisParameterMultiString
Specifies a multistring parameter of the Windows NT-defined REG_MULTI_SZ type.

Comments

In the Windows NT configuration registry, an NDIS keyword is a synonym for a value entry name. Such a name is a counted sequence of Unicode characters, terminated with a zero.

Every NDIS driver can set up configuration information in the registry for itself under the driver’s Parameters key using an installation script. For example, a protocol driver might store its own name as an entry with a preformatted string value that can be passed in calls to NdisRegisterProtocol.

Each NIC driver also has associated value entries under the driver’s Parameters registry key. The value entries for any particular NIC driver can be device-dependent in nature. For example, an Ethernet NIC driver might have keywords such as InterruptNumber, SharedMemoryAddress, and MulticastListSize. The value associated with such an NDIS keyword can be either an integer (ULONG-type) or a string (NDIS_STRING-type). For example, the set of possible values for the already mentioned InterruptNumber entry might be NdisParameterInteger values 2, 3, 4, or 5, or the equivalents in hexadecimal as NdisParameterHexInteger values.

NdisReadConfiguration buffers and copies the caller-supplied string at Keyword and releases the storage it allocates for this copy before it returns control to the caller. The memory it allocates for the NDIS_CONFIGURATION_PARAMETER structure is freed when the driver releases the ConfigurationHandle with NdisCloseConfiguration. The caller of NdisReadConfiguration is responsible for releasing the buffered string at Keyword.

For more information about setup and installation scripts, see the Programmer’s Guide.

Callers of NdisReadConfiguration run at IRQL PASSIVE_LEVEL.

See Also

NdisAnsiStringToUnicodeString, NdisCloseConfiguration, NdisFreeString, NdisInitAnsiString, NdisInitializeString, NdisInitUnicodeString, NdisOpenConfiguration, NdisOpenProtocolConfiguration, NdisReadBindingInformation, NdisReadNetworkAddress, NdisUnicodeStringToAnsiString, NdisWriteConfiguration


NdisReadEisaSlotInformation

VOID
NdisReadEisaSlotInformation(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE WrapperConfigurationContext,
OUT PUINT SlotNumber,
OUT PNDIS_EISA_FUNCTION_INFORMATION EisaData
);

NdisReadEisaSlotInformation returns EISA configuration information for a NIC on an EISA bus.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The NIC driver can examine the buffered configuration data to determine whether a NIC that it supports has been found and, if so, can use the bus-relative configuration values as parameters to certain Ndis Xxx initialization functions.
NDIS_STATUS_RESOURCES
NDIS could not allocate the resources it needs to collect the EISA configuration information.
NDIS_STATUS_FAILURE
This is a default status, returned if no NIC can be found on an EISA bus or if the caller supplied an invalid parameter. For example, if the caller set the AdapterType to something other than NdisInterfaceEisa when it called NdisMSetAttributes(Ex), NdisReadEisaSlotInformation returns this status.
WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
SlotNumber
Points to a variable in which this function returns the slot number of a NIC found on the EISA bus if this call is successful.
EisaData
Points to a caller-allocated buffer, which must be at least sizeof(NDIS_EISA_SLOT_INFORMATION + NDIS_EISA_FUNCTION_INFORMATION).

Comments

NdisReadEisaSlotInformation reads standard EISA bus configuration information for a NIC that it finds on an EISA I/O bus and copies the bus-relative configuration information into a buffer.

If the call to NdisReadEisaSlotInformation succeeds, this function returns information in the caller-supplied buffer at EisaData, formatted as an NDIS_EISA_SLOT_INFORMATION structure followed by an NDIS_EISA_FUNCTION_INFORMATION structure.

The NDIS_EISA_SLOT_INFORMATION structure is defined as follows:

typedef struct _NDIS_EISA_SLOT_INFORMATION {
    UCHAR ReturnCode;
    UCHAR ReturnFlags;
    UCHAR MajorRevision;
    UCHAR MinorRevision;
    USHORT Checksum;
    UCHAR NumberFunctions;
    UCHAR FunctionInformation;
    ULONG CompressedId;
} NDIS_EISA_SLOT_INFORMATION, *NDIS_EISA_SLOT_INFORMATION;

Members of interest to NIC drivers include the following:
Member | Meaning
Checksum | The checksum value allows validation of the configuration data.
NumberFunctions | The number of functions at this slot.
FunctionInformation | Whether NDIS_EISA_FUNCTION_INFORMATION is available for this slot.
CompressedId | The EISA compressed identification of the device at this slot.

The caller of NdisReadEisaSlotInformation can use the CompressedId value to confirm whether the NIC at this slot is its own. At most, NdisReadEisaSlotInformation retrieves the function information for the initial function. A NIC driver can call NdisReadEisaSlotInformationEx to retrieve the EISA configuration information for additional functions, if any.

The NDIS_EISA_FUNCTION_INFORMATION structure is defined as follows:

typedef struct _NDIS_EISA_FUNCTION_INFORMATION {
    ULONG CompressedId;
    UCHAR IdSlotFlags1;
    UCHAR IdSlotFlags2;
    UCHAR MinorRevision;
    UCHAR MajorRevision;
    UCHAR Selections[26];
    UCHAR FunctionFlags;
    UCHAR TypeString[80];
    EISA_MEMORY_CONFIGURATION EisaMemory[9];
    EISA_IRQ_CONFIGURATION EisaIrq[7];
    EISA_DMA_CONFIGURATION EisaDma[4];
    EISA_PORT_CONFIGURATION EisaPort[60];
    UCHAR InitializationData[60];
} NDIS_EISA_FUNCTION_INFORMATION, *PNDIS_EISA_FUNCTION_INFORMATION;

The members of this structure correspond to the standard EISA configuration function block, as follows:
Member | Meaning
CompressedId | Specifies the EISA compressed identification for the device. This value is identical to that of the CompressedId in the returned NDIS_EISA_SLOT_INFORMATION structure.
IdSlotFlags1 | Specifies byte 0 identifier and slot information.
IdSlotFlags2 | Specifies byte 1 identifier and slot information.
MinorRevision | Specifies the .cfg file minor revision level.
MajorRevision | Specifies the .cfg file major revision level.
Selections[26] | Specifies an array for 26 selections.
FunctionFlags | Specifies function flags.
TypeString[80] | Specifies an array for a type and subtype ASCII string.
EisaMemory[9] | Specifies an array in which each element is an EISA_MEMORY_CONFIGURATION structure, defining the bus-relative device memory range.
EisaIrq[7] | Specifies an array in which each element is an EISA_IRQ_CONFIGURATION structure, defining the bus-relative device interrupt.
EisaDma[4] | Specifies an array in which each element is an EISA_DMA_CONFIGURATION structure, defining the bus-relative DMA capacity, timing, and channel number, if any.
EisaPort[60] | Specifies an array in which each element is an EISA_PORT_CONFIGURATION structure, defining the bus-relative I/O port ranges of the device.
InitializationData[60] | Specifies an array of EISA initialization data.

NIC drivers call NdisReadEisaSlotInformation to identify their respective adapters in the current platform. When a particular driver’s NIC is found, the bus-relative configuration parameters returned at EisaData are passed in subsequent calls to functions such as NdisMRegisterInterrupt (EisaIrq), NdisMMapIoSpace (EisaMemory), and NdisMRegisterIoPortRange (EisaPort).

After consuming the returned information, the caller of NdisReadEisaSlotInformation is responsible for releasing the memory allocated at EisaData.

Callers of NdisReadEisaSlotInformation run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisFreeMemory, NdisMMapIoSpace, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMSetAttributes, NdisMSetAttributesEx, NdisOpenConfiguration, NdisReadEisaSlotInformationEx


NdisReadEisaSlotInformationEx

VOID
NdisReadEisaSlotInformationEx(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE WrapperConfigurationContext,
OUT PUINT SlotNumber,
OUT PNDIS_EISA_FUNCTION_INFORMATION *EisaData,
 OUT PUINT NumberOfFunctions
);

NdisReadEisaSlotInformationEx returns EISA function information for a NIC on an EISA bus.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The NIC driver can examine the buffered configuration data to determine whether a NIC that it supports has been found and, if so, can use the bus-relative configuration values as parameters to certain Ndis Xxx initialization functions.
NDIS_STATUS_RESOURCES
NDIS could not allocate a buffer to contain the requested EISA configuration information or could not allocate the resources it needs to collect the configuration information.
NDIS_STATUS_FAILURE
This is a default status, returned if the caller supplied an invalid parameter. For example, if the caller set the AdapterType to something other than NdisInterfaceEisa when the miniport called NdisMSetAttributes(Ex), NdisReadEisaSlotInformationEx returns this status.
WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
SlotNumber
Points to a variable in which this function returns the slot number for the NIC on the EISA bus.
EisaData
Points to a caller-supplied variable in which this function returns a pointer to the information it collected. A miniport should set the variable at EisaData to NULL before it calls NdisReadEisaSlotInformationEx.
NumberOfFunctions
Points to a caller-supplied variable in which this function returns the number of EISA functions for which configuration information was gathered.

Comments

NdisReadEisaSlotInformationEx collects more bus-relative configuration information about a NIC on an EISA bus than NdisReadEisaSlotInformation if additional configuration data is available.

If the call to NdisReadEisaSlotInformationEx succeeds, this function returns the same EISA configuration information as NdisReadEisaSlotInformation followed by any additional NDIS_EISA_FUNCTION_INFORMATION-structured data that is available. See NdisReadEisaSlotInformation for more information about these NDIS_EISA_XXX structures. NdisReadEisaSlotInformationEx allocates a buffer large enough to contain the available configuration data and resets the variable at EisaData to the address of this buffer.

Callers of NdisReadEisaSlotInformationEx run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisMSetAttributes, NdisMSetAttributesEx, NdisReadEisaSlotInformation


NdisReadMcaPosInformation

VOID
NdisReadMcaPosInformation(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE WrapperConfigurationContext,
IN PUINT ChannelNumber,
OUT PNDIS_MCA_POS_DATA McaData
);

NdisReadMcaPosInformation returns bus-relative configuration information for a NIC on an MCA bus.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The NIC driver can examine the buffered configuration data to determine whether a NIC that it supports has been found and, if so, can use the bus-relative configuration values as parameters to certain Ndis Xxx initialization functions.
NDIS_STATUS_ADAPTER_NOT_FOUND
A NIC could not be found on an MCA bus in the machine.
NDIS_STATUS_RESOURCES
NDIS could not allocate a buffer to contain the requested MCA configuration information or could not allocate the resources it needs to collect the configuration information.
NDIS_STATUS_FAILURE
This is a default status, returned if the caller supplied an invalid parameter or if POS information is unavailable. For example, if the caller sets the AdapterType to something other than NdisInterfaceMca when the miniport calls NdisMSetAttributes, NdisReadMcaPosInformation returns this status.
WrapperConfigurationContext
Specifies the handle input to MiniportInitialize.
ChannelNumber
Points to a caller-supplied variable in which this function returns the channel number of a NIC on the bus.
McaData
Points to a caller-supplied buffer in which this function returns the requested information, formatted as an NDIS_MCA_POS_DATA structure, if the call is successful.

Comments

NdisReadMcaPosInformation reads data from the POS register of an MCA NIC. The function copies this information into a buffer that the NIC driver provides.

The NDIS_MCA_POS_DATA structure is defined as follows:

typedef struct _NDIS_MCA_POS_DATA {
    USHORT AdapterId; //NIC's ID
    UCHAR PosData1;   // First byte of POS data
    UCHAR PosData2;
    UCHAR PosData3;
    UCHAR PosData4;
} NDIS_MCA_POS_DATA, *PNDIS_MCA_POS_DATA;

A NIC driver can use the returned AdapterId value to determine whether the NIC is its own. If so, the driver can call NdisMRegisterInterrupt to register its MiniportISR function. The driver also might use the returned value at ChannelNumber, for example, in a subsequent call to NdisMRegisterDmaChannel if its NIC is a slave DMA device.

The caller is responsible for freeing the buffer it allocated at McaData with NdisFreeMemory.

Callers of NdisReadMcaPosInformation run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, MiniportISR, NdisFreeMemory, NdisMRegisterDmaChannel, NdisMRegisterInterrupt, NdisMSetAttributes, NdisMSetAttributesEx


NdisReadNetworkAddress

VOID
NdisReadNetworkAddress(
OUT PNDIS_STATUS Status,
OUT PVOID *NetworkAddress,
OUT PUINT NetworkAddressLength,
IN NDIS_HANDLE ConfigurationHandle
);

NdisReadNetworkAddress returns the software-configurable network address that was stored in the registry for a NIC when it was installed in the machine.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The caller can use the address returned at NetworkAddress for its NIC.
NDIS_STATUS_FAILURE
There was no NIC address information available in the caller’s registry Parameters key or the value stored was not a string.
NetworkAddress
Points to a caller-supplied variable in which this function returns a pointer to the buffered network address, stored as a sequence of byte integers, if the call is successful.
NetworkAddressLength
Points to a caller-supplied variable in which this function returns the number of bytes returned at NetworkAddress.
ConfigurationHandle
Specifies the handle returned by NdisOpenConfiguration.

Comments

NdisReadNetworkAddress searches the registry Parameters key designated by the given ConfigurationHandle for the keyword NetworkAddress, converts the value of this string-type entry into a sequence of byte integers, and stores the requested information internally. The storage that NDIS allocates for such an address remains valid until the NIC driver calls NdisCloseConfiguration, which frees the memory.

The caller cannot use the variable at NetworkAddress as a pointer unless NdisReadNetworkAddress returns NDIS_STATUS_SUCCESS at Status.

The installation program for a NIC that supports software-configurable network addressing should store a value entry named with the NetworkAddress keyword in the appropriate ..\DriverName\Parameters key of the registry.

A NIC installer usually stores the value of a NetworkAddress entry in the registry as a string of hexadecimal digits. Optionally, an installer can store such an address as a string of paired digits, with each pair separated from the next by a hyphen. NdisReadNetworkAddress discards hyphens and converts each such pair into a single byte.

In either form, NdisReadNetworkAddress converts the address specified as a string, one character at a time, into the equivalent integer until the string is exhausted.

In the Windows NT registry, such an installed address string contains Unicode characters.

Callers of NdisReadNetworkAddress run at IRQL PASSIVE_LEVEL.

See Also

NdisCloseConfiguration, NdisOpenConfiguration


NdisReadPciSlotInformation

ULONG
NdisReadPciSlotInformation(
IN NDIS_HANDLE NdisAdapterHandle,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG Length
 );

NdisReadPciSlotInformation returns bus-specific PCI configuration information from the PCI configuration space for a device at a particular slot on the bus.

Parameters

NdisAdapterHandle
Specifies the MiniportAdapterHandle passed in to MiniportInitialize and to most other MiniportXxx functions.
SlotNumber
Specifies the slot number of a NIC on the I/O bus.
Offset
Specifies the byte offset within the PCI configuration space at which to begin transferring the configuration information.
Buffer
Points to a caller-allocated buffer in which to return the requested configuration information.
Length
Specifies the size in bytes of the buffer at Buffer and, therefore, how many bytes of configuration information to return.

Return Value

NdisReadPciSlotInformation returns how many bytes of configuration information it returned at Buffer.

Comments

NdisReadPciSlotInformation helps a driver find a NIC that it supports on a PCI bus. When it does, the driver can call NdisMPciAssignResources to determine the bus-relative configuration parameters it can use in subsequent calls to other Ndis Xxx configuration functions, such as NdisMRegisterInterrupt, NdisMMapIoSpace, and/or NdisMRegisterIoPortRange.

NdisReadPciSlotInformation returns the same bus-relative PCI configuration information as NdisImmediateReadPciSlotInformation. However, callers of NdisReadPciSlotInformation pass the MiniportAdapterHandle passed to the MiniportInitialize function, rather than the WrapperConfigurationContext handle.

The caller is responsible for releasing the memory at Buffer when the driver has consumed the configuration information with NdisFreeMemory.

Miniport drivers call this function from their MiniportInitialize functions.

Callers of NdisReadPciSlotInformation can run at IRQL <= DISPATCH_LEVEL. Usually, callers are running at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisFreeMemory, NdisMMapIoSpace, NdisMPciAssignResources, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMSetAttributes, NdisMSetAttributesEx, NdisOpenConfiguration


NdisReadRegisterUchar

VOID
NdisReadRegisterUchar(
IN PUCHAR Register,
 OUT PUCHAR Data
);

NdisReadRegisterUchar is called by the NIC driver to read a UCHAR from a memory-mapped device register.

Parameters

Register
Points to the memory-mapped register. This virtual address must fall within a range returned by an initialization-time call to NdisMMapIoSpace.
Data
Points to the caller-supplied variable in which this function returns the UCHAR read from Register.

Comments

If a driver calls this function, its NIC’s device registers must be mapped to noncached memory during driver initialization.

Callers of NdisReadRegisterUchar can be running at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisReadRegisterUlong, NdisReadRegisterUshort, NdisWriteRegisterUchar


NdisReadRegisterUlong

VOID
NdisReadRegisterUlong(
IN PULONG Register,
 OUT PULONG Data
);

NdisReadRegisterUlong is called by the NIC driver to read a ULONG from a memory-mapped device register.

Parameters

Register
Points to the memory-mapped register. This virtual address must fall within a range returned by an initialization-time call to NdisMMapIoSpace.
Data
Points to the caller-supplied variable in which this function returns the ULONG read from Register.

Comments

If a driver calls this function, its NIC’s device registers must be mapped to noncached memory during driver initialization.

Callers of NdisReadRegisterUlong can be running at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisReadRegisterUchar, NdisReadRegisterUshort, NdisWriteRegisterUlong


NdisReadRegisterUshort

VOID
NdisReadRegisterUshort(
IN PUSHORT Register,
 OUT PUSHORT Data
);

NdisReadRegisterUshort is called by the NIC driver to read a USHORT from a memory-mapped device register.

Parameters

Register
Points to the memory-mapped register. This virtual address must fall within a range returned by an initialization-time call to NdisMMapIoSpace.
Data
Points to the caller-supplied variable in which this function returns the USHORT read from Register.

Comments

If a driver calls this function, its NIC’s device registers must be mapped to noncached memory during driver initialization.

Callers of NdisReadRegisterUshort can be running at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisReadRegisterUchar, NdisReadRegisterUlong, NdisWriteRegisterUshort


NdisRecalculatePacketCounts

VOID
NdisRecalculatePacketCounts(
IN OUT PNDIS_PACKET Packet
);

NdisRecalculatePacketCounts resets the valid counts for a given packet.

Parameters

Packet
Points to the packet descriptor.

Comments

If a driver explicitly modifies a buffer descriptor chain for a packet descriptor, that driver should call NdisRecalculatePacketCounts with the modified packet. In general, any NDIS driver should avoid modifying buffer descriptor chains that it sets up, and any driver must not modify chains set up by other drivers.

This call resets the valid counts for the packet to FALSE, thereby forcing NdisQueryPacket to recalculate the buffer count for the modified packet if it is called subsequently with that packet.

Callers of NdisRecalculatePacketCounts can run at any IRQL.

See Also

NDIS_PACKET, NdisQueryPacket, NdisReinitializePacket


NdisRegisterProtocol

VOID
NdisRegisterProtocol(
OUT PNDIS_STATUS Status,
OUT PNDIS_HANDLE NdisProtocolHandle,
IN PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
IN UINT CharacteristicsLength
 );

NdisRegisterProtocol registers an NDIS driver’s ProtocolXxx entry points and name with the NDIS library when the driver initializes.

Parameters

Status
Points to a caller-supplied variable that can be one of the following values on return from this function:
NDIS_STATUS_SUCCESS
The NDIS library registered the caller as a protocol driver.
NDIS_STATUS_BAD_CHARACTERISTICS
The CharacteristicsLength is too small for the MajorNdisVersion specified in the buffer at ProtocolCharacteristics.
NDIS_STATUS_BAD_VERSION
The MajorNdisVersion specified in the buffer at ProtocolCharacteristics is invalid.
NDIS_STATUS_RESOURCES
A shortage of resources, possibly memory, prevented the NDIS library from registering the caller.
NdisProtocolHandle
Points to a caller-supplied variable in which this function returns a handle representing the registered driver.
ProtocolCharacteristics
Points to an NDIS_PROTOCOL_CHARACTERISTICS structure set up by the caller. The structure at ProtocolCharacteristics is defined as follows:
typedef struct _NDIS_PROTOCOL_CHARACTERISTICS {
    UCHAR MajorNdisVersion;
    UCHAR MinorNdisVersion;
    UINT Reserved;
    OPEN_ADAPTER_COMPLETE_HANDLER OpenAdapterCompleteHandler;
    CLOSE_ADAPTER_COMPLETE_HANDLER CloseAdapterCompleteHandler;
    SEND_COMPLETE_HANDLER SendCompleteHandler;
    TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler;
    RESET_COMPLETE_HANDLER ResetCompleteHandler;
    REQUEST_COMPLETE_HANDLER RequestCompleteHandler;
    RECEIVE_HANDLER ReceiveHandler;
    RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler;
    STATUS_HANDLER StatusHandler;
    STATUS_COMPLETE_HANDLER StatusCompleteHandler;
    NDIS_STRING Name;
//
// MajorNdisVersion must be set to 0x04
// with any of the following members.
//
    RECEIVE_PACKET_HANDLER ReceivePacketHandler;
    BIND_HANDLER BindAdapterHandler;
    UNBIND_HANDLER UnbindAdapterHandler;
    TRANSLATE_HANDLER TranslateHandler;
    UNLOAD_PROTOCOL_HANDLER UnloadHandler;
} NDIS_PROTOCOL_CHARACTERISTICS, *PNDIS_PROTOCOL_CHARACTERISTICS;

The driver should initialize this structure with zeros before setting up the following members:
MajorNdisVersion

Specifies the major version of the NDIS library the driver is using. The current value is 0x04, although the NDIS library continues to support existing drivers developed for NDIS V3.0.

This member must be set to 0x04 if the caller if the caller is an NDIS intermediate driver or if the caller sets entry points in any members following Name.

MinorNdisVersion
Specifies the minor NDIS version. The current value is 0x00, although NDIS continues to support existing drivers.
Reserved
This member is reserved for system use.
OpenAdapterCompleteHandler
Specifies the entry point of the caller’s ProtocolOpenAdapterComplete function.
CloseAdapterCompleteHandler
Specifies the entry point of the caller’s ProtocolCloseAdapterComplete function.
SendCompleteHandler
Specifies the entry point of the caller’s ProtocolSendComplete function.
TransferDataCompleteHandler
Specifies the entry point of the caller’s ProtocolTransferDataComplete function.
ResetCompleteHandler
Specifies the entry point of the caller’s ProtocolResetComplete function.
RequestCompleteHandler
Specifies the entry point of the caller’s ProtocolRequestComplete function.
ReceiveHandler
Specifies the entry point of the caller’s ProtocolReceive function.
ReceiveCompleteHandler
Specifies the entry point of the caller’s ProtocolReceiveComplete function.
StatusHandler
Specifies the entry point of the caller’s ProtocolStatus function.
StatusCompleteHandler
Specifies the entry point of the caller’s ProtocolStatusComplete function.
Name
Specifies a pointer to a buffered, caller-initialized counted string, in the system-default character set, naming the driver. For Windows NT drivers, this string contains Unicode characters.

NdisRegisterProtocol converts the supplied string to upper case, so a protocol driver writer cannot assume that changing the case of an already registered protocol name creates a unique name for the driver.

ReceivePacketHandler
Specifies the entry point of the caller’s ProtocolReceivePacket function, if any, or NULL. Protocols that bind to any driver that supports multipacket receive indications should supply a ProtocolReceivePacket function to enhance their performance.
BindAdapterHandler
Specifies the entry point of the caller’s ProtocolBindAdapter function, if any, or NULL. NDIS intermediate drivers usually supply a ProtocolBindAdapter function, which both makes them PnP-ready and allows the intermediate to call NdisIMRegisterLayeredMiniport and to defer full driver initialization until underlying NIC drivers have initialized.
UnbindAdapterHandler
Specifies the entry point of the caller’s ProtocolUnbindAdapter function, if any, or NULL. NDIS drivers that supply a ProtocolBindAdapter function also must supply a ProtocolUnbindAdapter function.
TranslateHandler
Specifies the entry point of the caller’s ProtocolTranslate function, if any, or NULL. Such a function currently is not called, and this member is reserved for future use by PnP-aware protocols.
UnloadHandler
Set to NULL by Windows NT protocol drivers.
CharacteristicsLength
Specifies the size in bytes of the structure at ProtocolCharacteristics. If the build directive NDIS40 is specified in the sources ahead of #include ndis.h, this value is supplied automatically.

Comments

The value supplied at CharacteristicsLength must be at least the sizeof(NDISXX_PROTOCOL_CHARACTERISTICS) designated by the supplied MajorNdisVersion in this structure.

For the best possible performance, any protocol that will layer itself above a NIC driver that supports multipacket receives should provide a ProtocolReceivePacket function. Any NIC driver that supports multipacket sends is also likely to indicate multipacket receives. A driver that provides a ProtocolReceivePacket function also must provide a ProtocolReceive function.

After a successful call to NdisRegisterProtocol, a driver cannot alter the set of ProtocolXxx functions it supplied.

A successfully registered driver should save the handle returned at NdisProtocolHandle. It is a required parameter to other Ndis Xxx functions that the driver calls subsequently.

After a successful call to NdisRegisterProtocol, the driver can call NdisOpenAdapter to set up a binding to the underlying NIC driver or to layer itself above any NDIS driver that registered a set of NDIS upper-edge (MiniportXxx) functions.

Callers of NdisRegisterProtocol run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, NdisDeregisterProtocol, NdisIMRegisterLayeredMiniport, NdisInitializeString, NdisInitUnicodeString, NdisOpenAdapter, NdisZeroMemory, ProtocolBindAdapter, ProtocolCloseAdapterComplete, ProtocolOpenAdapterComplete, ProtocolReceive, ProtocolReceiveComplete, ProtocolReceivePacket, ProtocolRequestComplete, ProtocolResetComplete, ProtocolSendComplete, ProtocolStatus, ProtocolStatusComplete, ProtocolTransferDataComplete, ProtocolTranslate, ProtocolUnbindAdapter


NdisReinitializePacket

VOID
NdisReinitializePacket(
IN OUT PNDIS_PACKET Packet
);

NdisReinitializePacket removes any chained buffers from a given packet and reinitializes it for reuse.

Parameters

Packet
Points to the packet descriptor, allocated by the caller.

Comments

NdisReinitializePacket reinitialize a packet descriptor so the MiniportReserved and ProtocolReserved arrays contain all zeros and the head of the buffer chain is set to NULL.

A driver can call NdisReinitializePacket only with packet descriptors allocated by that driver. For example, a MiniportReturnPacket function is likely to call NdisReinitializePacket to prepare a driver-allocated packet for use in a subsequent receive indication.

Before calling NdisReinitializePacket, a driver must call NdisFreeBuffer or NdisUnchainBufferAt Xxx as many times as necessary to release or to save all buffer descriptors chained to the packet. Otherwise, a call to NdisReinitializePacket causes a memory leak: all memory associated with the buffer descriptor chain cannot be used by NDIS nor by the driver until the system is rebooted.

A driver can call NdisQueryPacket to determine how many buffer descriptors are chained to a given packet and, therefore, how many calls to NdisFreeBuffer or NdisUnchainBufferAt Xxx are required. It can call NdisGetNextBuffer to retrieve each buffer descriptor pointer to pass to NdisFreeBuffer.

A call to NdisReinitializePacket has no effect on the NDIS_PACKET_OOB_DATA block associated with the packet descriptor. To prepare this block for reuse, the allocating driver can either call NdisZeroMemory with the pointer returned by NDIS_OOB_DATA_FROM_PACKET or it can reset the relevant members with the appropriate NDIS_SET_PACKET_XXX macro(s).

Callers of NdisReinitializePacket can run at any IRQL.

See Also

MiniportReturnPacket, NdisAllocatePacket, NdisDprAllocatePacket, NdisFreeBuffer, NdisGetNextBuffer, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryPacket, NDIS_SET_PACKET_HEADER_SIZE, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_STATUS, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_TO_SEND, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, NdisZeroMemory


NdisReleaseSpinLock

VOID
NdisReleaseSpinLock(
IN PNDIS_SPIN_LOCK SpinLock
);

NdisReleaseSpinLock releases a spin lock that was acquired in a preceding call to NdisAcquireSpinLock.

Parameters

SpinLock
Points to the acquired spin lock to be released.

Comments

A driver must initialize its spin lock with NdisAllocateSpinLock before it calls any other Ndis..SpinLock function to access the resources protected by that spin lock.

A driver must call NdisAcquireSpinLock to acquire the spin lock before it can call NdisReleaseSpinLock. Each call to NdisAcquireSpinLock requires a reciprocal call to NdisReleaseSpinLock before the driver can call NdisAcquireSpinLock again.

NdisReleaseSpinLock restores the original IRQL at which its caller was running before the call to NdisAcquireSpinLock.

Any spin lock acquired with NdisAcquireSpinLock must be released with NdisReleaseSpinLock. Any spin lock acquired with NdisDprAcquireSpinLock must be released with NdisDprReleaseSpinLock.

Callers of NdisReleaseSpinLock are running at IRQL DISPATCH_LEVEL.

See Also

NdisAcquireSpinLock, NdisAllocateSpinLock, NdisDprAcquireSpinLock


NdisRequest

VOID
NdisRequest(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE NdisBindingHandle,
IN PNDIS_REQUEST NdisRequest
);

NdisRequest forwards a request to the underlying driver that it query the capabilities or status of its NIC or that it set the state of its NIC.

Parameters

Status
Points to a caller-supplied variable that is set on return from this function. The underlying driver determines which NDIS_STATUS_XXX is returned, but it is usually one of the following values:
NDIS_STATUS_SUCCESS
The requested operation completed successfully.
NDIS_STATUS_PENDING
The request is being handled asynchronously, and the caller’s ProtocolRequestComplete function will be called when it is completed.
NDIS_STATUS_INVALID_OID
The OID_XXX code specified in the Oid member of the NDIS_REQUEST-structured buffer at NdisRequest is invalid or unsupported by the underlying driver.
NDIS_STATUS_INVALID_LENGTH or NDIS_STATUS_BUFFER_TOO_SHORT
The value specified in the InformationBufferLength member of the NDIS_REQUEST-structured buffer at NdisRequest does not match the requirements for the given OID_XXX code. If the information buffer is too small, the BytesNeeded member contains the correct value for InformationBufferLength on return from NdisRequest.
NDIS_STATUS_INVALID_DATA
The data supplied at InformationBuffer in the given NDIS_REQUEST structure is invalid for the given OID_XXX code.
NDIS_STATUS_NOT_SUPPORTED or NDIS_STATUS_NOT_RECOGNIZED
The underlying driver does not support the requested operation.
NDIS_STATUS_RESOURCES
The request could not be satisfied due to a resource shortage. Usually, this return indicates that an attempt to allocate memory was unsuccessful, but it does not necessarily indicate that the same request, submitted later, will be failed for the same reason.
NDIS_STATUS_NOT_ACCEPTED
The underlying driver attempted the requested operation, usually a set, on its NIC but it was failed by the netcard. For example, an attempt to set too many multicast addresses might cause the return of this value.
NDIS_STATUS_CLOSING or NDIS_STATUS_CLOSING_INDICATING
The underlying driver failed the requested operation because a close is in progress.
NDIS_STATUS_RESET_IN_PROGRESS
The underlying NIC driver cannot satisfy the request at this time because it is currently resetting the netcard. The caller’s ProtocolStatus function was or will be called with NDIS_STATUS_RESET_START to indicate that a reset is in progress. This return value does not necessarily indicate that the same request, submitted later, will be failed for the same reason.
NDIS_STATUS_FAILURE
This value usually is a nonspecific default, returned when none of the more specific NDIS_STATUS_XXX caused the underlying driver to fail the request.
NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the target NIC or the virtual adapter of the next-lower driver to which the caller is bound.
NdisRequest
Points to a buffered structure specifying the operation requested with a given OID_XXX code for either a query or a set.

Comments

A protocol driver must allocate sufficient memory for the information buffer associated with the OID_XXX of the operation it will request. The driver also must allocate and set up the buffer at NdisRequest before it calls NdisRequest. Both buffers must be allocated from nonpaged pool because the underlying driver runs at raised IRQL while processing the request.

For callers layered above an intermediate NDIS driver, NdisRequest forwards a request to the next-lower driver that it return information about its own capabilities or directs that driver to handle specific functionality in a caller-determined manner. If the next-lower driver is an NDIS intermediate driver, it can call NdisRequest with an OID-specific request of its own before completing the request originally submitted by the higher-level protocol.

Some errors returned at Status are recoverable, including the following:
NDIS_STATUS_INVALID_OID
NDIS_STATUS_INVALID_LENGTH
NDIS_STATUS_BUFFER_TOO_SHORT
NDIS_STATUS_INVALID_DATA
NDIS_STATUS_RESOURCES
NDIS_STATUS_RESET_IN_PROGRESS

That is, a driver can modify the packet at NdisRequest appropriately to correct the OID_XXX code and/or the size or contents of the buffer at InformationBuffer and resubmit the request packet to NdisRequest. The same packet might be satisfied on resubmission to NdisRequest if the original call indicated a reset in progress or that a resource shortage, which might be temporary, prevented that request from being carried out.

The NDIS library maintains bindings for underlying miniport drivers. NDIS can return information for binding-specific queries if a given OID is associated with a system-defined medium type for which the system provides a filter library.

For more information about the general and media-specific OIDs and their respective associated information buffers, see Chapter 5.

Callers of NdisRequest can be running at IRQL <= DISPATCH_LEVEL.

See Also

MiniportQueryInformation, MiniportSetInformation, NdisOpenAdapter, NDIS_REQUEST, ProtocolRequestComplete, ProtocolStatus


NdisReset

VOID
NdisReset(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE NdisBindingHandle
);

NdisReset forwards a reset request to an underlying driver.

Parameters

Status
Points to a caller-supplied variable that is set on return from this function. The underlying driver determines which NDIS_STATUS_XXX is returned, but it is usually one of the following values:
NDIS_STATUS_SUCCESS
The underlying driver reset its NIC (or virtual NIC) to its initial state.
NDIS_STATUS_PENDING
The request is being handled asynchronously, and the caller’s ProtocolResetComplete function will be called when it is completed.
NDIS_STATUS_RESET_IN_PROGRESS
The underlying driver is currently resetting its NIC so this request is superfluous. Moreover, the caller’s ProtocolStatus function was or will be called with NDIS_STATUS_RESET_START to indicate that a reset is in progress.
NDIS_STATUS_NOT_RESETTABLE
The underlying NIC cannot be reset by software commands.
NDIS_STATUS_ADAPTER_REMOVED or NDIS_STATUS_FAILURE
The caller’s binding is already closed so no reset will be attempted for this caller.
NDIS_STATUS_CLOSING
The caller’s binding is currently being closed so no reset will be attempted for this caller.
NDIS_STATUS_SOFT_ERRORS
The underlying NIC driver reset the netcard but one or more recoverable hardware errors occurred during this operation. The NIC driver has logged the error(s).
NDIS_STATUS_HARD_ERRORS
The underlying NIC driver attempted to reset the netcard but one or more unrecoverable hardware errors occurred during this operation. The NIC driver has logged the error(s).
NDIS_STATUS_NOT_ACCEPTED
This value usually is a nonspecific default, returned when none of the more specific NDIS_STATUS_XXX caused NDIS or the underlying NIC driver to fail the request.
NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the target NIC or the virtual adapter of the next-lower driver to which the caller is bound.

Comments

When a protocol calls NdisReset, the NDIS library does not call the MiniportReset function of the underlying NIC driver until NDIS has completed any internally queued send requests back to the initiating protocol(s). Only after NDIS has cleared its send queue for the underlying driver does it call that driver’s MiniportReset function.

If the underlying driver queues send packets internally, it also completes any send packets it is currently holding in its queue before attempting to reset its NIC.

Consequently, all protocol drivers bound above the same NIC should hold on to their outstanding send packets during a reset operation. At the start of a reset, NDIS calls the ProtocolStatus function of each bound protocol with NDIS_STATUS_RESET_START and, then, the corresponding ProtocolStatusComplete function. When the reset operation is done, NDIS calls the ProtocolStatus function of each bound protocol again with NDIS_STATUS_RESET_END and, then, the corresponding ProtocolStatusComplete function.

Like any other driver bound above the same NIC, the ProtocolStatus and ProtocolStatusComplete functions of the driver that called NdisReset receive these notifications. However, if NdisReset returns STATUS_PENDING, only the ProtocolResetComplete function of the original caller is notified when the reset operation is done.

A protocol bound to NDISWAN should never call NdisReset.

Callers of NdisReset run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportReset, MiniportSend, ProtocolResetComplete, ProtocolSendComplete, ProtocolStatus, ProtocolStatusComplete


NdisResetEvent

VOID
NdisResetEvent(
IN PNDIS_EVENT Event
);

NdisResetEvent clears the Signaled state of a given event.

Parameters

Event
Points to an initialized event object for which the caller provided the storage.

Comments

NdisResetEvent explicitly sets the state of the given event to Not-Signaled.

When an event is set to the Signaled state with NdisSetEvent, it remains in that state until an explicit call to NdisResetEvent occurs. While an event remains in the Signaled state, callers of NdisWaitEvent on that Event are dispatched for execution without waiting.

Callers of NdisResetEvent run at IRQL <= DISPATCH_LEVEL.

See Also

NdisInitializeEvent, NdisSetEvent, NdisWaitEvent


NdisRetrieveUlong

VOID
NdisRetrieveUlong(
IN PULONG DestinationAddress,
IN PULONG SourceAddress
);

NdisRetrieveUlong retrieves a ULONG value from the source address, avoiding alignment faults.

Parameters

DestinationAddress
Points to a ULONG-aligned location in which to store the value.
SourceAddress
Points to a location from which to retrieve the ULONG value.

Comments

The given DestinationAddress is assumed to be aligned on a ULONG boundary.

Callers of NdisRetrieveUlong can be running at any IRQL if the given addresses are in nonpaged pool. Otherwise, callers must be running at IRQL < DISPATCH_LEVEL.

See Also

NdisStoreUlong


NdisReturnPackets

VOID
NdisReturnPackets(
IN PNDIS_PACKET *PacketsToReturn,
IN UINT NumberOfPackets
);

NdisReturnPackets releases ownership of one or more packets after a protocol has consumed the received data.

Parameters

PacketsToReturn
Points to an array of pointers to packet descriptors to be returned to the underlying driver that allocated them for a receive indication.
NumberOfPackets
Specifies the number of pointers in the array.

Comments

An NDIS intermediate driver should call NdisReturnPackets as soon as possible after its ProtocolReceivePacket function has returned control. Otherwise, both the underlying driver that supports multipacket receive indications and the bound protocol driver that processes them suffer a performance degradation.

When an underlying NIC driver runs low on available packet pool for receive indications or its NIC runs low on empty receive buffers, the miniport can force the NDIS library to call every bound protocol driver’s ProtocolReceive function with a single packet descriptor at a time until the miniport has regained ownership of its packet descriptors and the NIC has receive buffers available for incoming net packets. A ProtocolReceive function cannot begin postprocessing a packet of received data and forwarding the processed data to clients until the driver’s ProtocolReceiveComplete function is called.

For a packet descriptor passed in to its ProtocolReceivePacket function, a highest level protocol driver can process its copy of received data and forward the processed data to clients immediately because the protocol can control how long it retains ownership of the resources allocated by the indicating driver. Assuming a highest level protocol driver does not massage the data it indicates to its clients, the protocol can even set up the range of data within the receive buffer of interest to its client, and make a receive indication to its client(s) by forwarding the input packet descriptor to those clients.

If an intermediate protocol driver’s ProtocolReceivePacket function returns a nonzero reference count to an input packet descriptor, that driver must call NdisReturnPackets one or more times. When it has called NdisReturnPackets with a particular packet descriptor as many times as the value returned by ProtocolReceivePacket, the NDIS library returns the packet descriptor to the driver that made the indication. For a highest level protocol that forwards an input packet descriptor to its clients, those clients return the packet descriptor to the NDIS driver that allocated it by making calls to TdiReturnChainedReceives.

A protocol driver can rely on NDIS to manage the reference count for every receive packet passed to the ProtocolReceivePacket function. When the reference count for a packet descriptor originally set to the return from ProtocolReceivePacket goes to zero, the NDIS library calls the indicating driver’s MiniportReturnPacket function with the released packet descriptor.

NdisReturnPackets cannot be called from a ProtocolReceivePacket function.

Callers of NdisReturnPackets run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportReturnPacket, NdisAllocatePacket, NdisFreePacket, NdisMIndicateReceivePacket, NDIS_PACKET, NDIS_PACKET_OOB_DATA, ProtocolReceivePacket, TdiReturnChainedReceives


NdisSend

VOID
NdisSend(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE NdisBindingHandle,
IN PNDIS_PACKET Packet
);

NdisSend forwards a send request to the underlying driver.

Parameters

Status
Points to a caller-supplied variable that is set on return from this function. The underlying driver determines which NDIS_STATUS_XXX is returned, but it is usually one of the following values:
NDIS_STATUS_SUCCESS
The given packet is being transmitted over the network.
NDIS_STATUS_PENDING
The request is being handled asynchronously, and the caller’s ProtocolSendComplete function will be called when it is completed.
NDIS_STATUS_INVALID_PACKET
The size of the requested transfer is too large for the NIC, or possibly the NIC indicated an erroneous packet transmission to its driver.
NDIS_STATUS_CLOSING
The underlying driver is closing.
NDIS_STATUS_RESET_IN_PROGRESS
The underlying driver is currently resetting the NIC. The caller’s ProtocolStatus function was or will be called with NDIS_STATUS_RESET_START to indicate that a reset is in progress.
NDIS_STATUS_FAILURE
This value usually is a nonspecific default, returned when none of the more specific NDIS_STATUS_XXX caused the underlying driver to fail the request.

The specific NDIS_STATUS_XXX returned for device I/O errors that occur during a transmit operation depend on the nature of the NIC and the discretion of the NIC driver writer. For example, a minport might return NDIS_STATUS_NO_CABLE if its NIC indicates this condition to the driver.

NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the target NIC or the virtual adapter of the next-lower driver to which the caller is bound.
Packet
Points to the caller-supplied packet descriptor, allocated with NdisAllocatePacket, with chained buffer descriptors mapping buffers containing the data that the underlying NIC driver should transmit over the wire.

Comments

Before calling NdisSend, a protocol driver can call NdisSetPacketFlags to set the flags in the private header (reserved for use by NDIS) of the packet descriptor it allocated. These flags specify caller-determined information about the requested send operation that is not contained in the packet data. The underlying NIC driver’s MiniportSend function is given the send flags as an input parameter. The meaning of the packet flags bits is medium-specific and defined by the pair of collaborating drivers.

However, such a pair of collaborating drivers can use the NDIS_PACKET_OOB_DATA block associated with each packet descriptor to communicate far more information than the packet flags can convey. Before calling NdisSend, a protocol can use the NDIS_SET_PACKET_TIME_TO_SEND and/or NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO macros to set up out-of-band information, if any, relevant to the underlying driver in the NDIS_PACKET_OOB_DATA block associated with the protocol-allocated packet descriptor.

When an underlying driver has insufficient resources to transmit a valid send packet, the driver has two alternatives:

  1. Its MiniportSend function can insert the packet into an internal queue and return NDIS_STATUS_PENDING.

    The driver holds the packet queued until resources become available and sends the packet when they are.

  2. Its MiniportSend function can simply return control with NDIS_STATUS_RESOURCES.

    The NDIS library holds such a returned packet in an internal queue for resubmission to the miniport. The miniport can indicate its readiness to accept send packets later by calling NdisMSendResourcesAvailable or NdisMSendComplete, whichever call occurs first.

In either of the preceding scenarios, NdisSend returns NDIS_STATUS_PENDING to the caller, and the driver’s ProtocolSendComplete function is called when the packet is transmitted.

As soon as a protocol calls NdisSend, it relinquishes ownership of the packet descriptor at Packet, of all buffers mapped by buffer descriptors it chained to the packet, and of any out-of-band information it supplied with the packet descriptor. The protocol regains ownership of these resources when the packet is completed with a status other than NDIS_STATUS_PENDING or when its ProtocolSendComplete function is called.

When either occurs, the protocol can call NdisReinitializePacket to prepare the packet for reuse after saving the buffer descriptors chained to the packet descriptor with calls to an NdisUnchainBufferAt Xxx function. Reusing such a packet descriptor yields better performance than returning the packet to driver-allocated packet pool with NdisFreePacket and, then, reallocating it for another send later.

An NDIS intermediate driver must repackage incoming sends from still higher level protocols in fresh packet descriptors before passing such a send request to the underlying miniport with NdisSend (or NdisSendPackets).

Callers of NdisSend run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportSend, NdisAllocateBuffer, NdisAllocatePacket, NdisFreePacket, NdisGetPacketFlags, NdisMSendResourcesAvailable, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisReinitializePacket, NdisSendPackets, NdisSetPacketFlags, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_TIME_TO_SEND, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, ProtocolSendComplete, ProtocolStatus


NdisSendPackets

VOID
NdisSendPackets(
IN NDIS_HANDLE NdisBindingHandle,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
);

NdisSendPackets forwards a multipacket send request, possibly with associated out-of-band information, to the underlying driver.

Parameters

NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the target NIC or the virtual adapter of the next-lower driver to which the caller is bound.
PacketArray
Points to an array of pointers to packet descriptors. Each packet descriptor in the array has chained buffer descriptors mapping buffers containing the data that the underlying NIC driver should transmit over the wire. Each packet descriptor also has an associated NDIS_PACKET_OOB_DATA block, which the caller has already set up with any timestamp and/or medium-specific out-of-band information, such as packet priority, relevant to the underlying driver.
NumberOfPackets
Specifies the number of pointers in the packet array.

Comments

Each element in the given array is a pointer to an NDIS_PACKET-type descriptor, and each packet descriptor can have caller-set TimeToSend and/or MediaSpecificInformation and MediaSpecificSize values in its associated NDIS_PACKET_OOB_DATA block. The caller must allocate each such packet descriptor with NdisAllocatePacket.

A protocol can set up this timestamp with the NDIS_SET_PACKET_TIME_TO_SEND and any out-of-band information that accompanies the send request with NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO before it calls NdisSendPackets. The underlying driver retrieves this information with the reciprocal NDIS_GET_PACKET_XXX macros.

If necessary, a protocol also can call NdisSetPacketFlags to set the packet descriptor’s flags with information for the underlying driver.

If the underlying NIC driver does not export a MiniportSendPackets handler, the NDIS library makes a sequence of calls, one for each packet in the supplied array, to that driver’s MiniportSend function. NDIS submits packets individually to an underlying driver’s MiniportSend function in the same order as they arrive in the supplied array.

NDIS submits packet arrays to an underlying driver’s MiniportSendPackets function in the same order as they are passed to NdisSendPackets, but the underlying driver can return a subset of packets in any incoming array to NDIS by setting NDIS_STATUS_RESOURCES in the out-of-band data block for a single array element. When this occurs, NDIS queues that packet and all subsequent packets in the array for resubmission to the MiniportSendPackets function later.

In other words, NDIS always preserves the protocol-determined order of send packets, whether passed in calls to NdisSend or NdisSendPackets, and sees that they are given to the underlying driver in FIFO order. Before a protocol driver calls NdisSendPackets with an array of packet descriptor pointers, the protocol must ensure that the array of pointers has been set up in the same order as the packets should be sent over the wire.

The caller of NdisSendPackets should test the returned status for each packet in such an array individually when its ProtocolSendComplete function is called with the completion Status. While NDIS submits packets for transmission in the FIFO order determined by the sending protocol, underlying drivers can complete such sends in random order.

As soon as a protocol calls NdisSendPackets, it relinquishes ownership of the following:

  • All packet descriptors at PacketArray

  • All buffers mapped by the buffer descriptors chained to those packet descriptors

  • All out-of-band data blocks associated with the packet descriptors, including any medium-specific buffers specified in the out-of-band data blocks

The protocol regains ownership of these resources, one packet at a time, as its ProtocolSendComplete function is called with each packet descriptor in the array given to NdisSendPackets.

When a packet descriptor is returned to ProtocolSendComplete, it can call NdisReinitializePacket to prepare the packet for reuse after saving the buffer descriptors chained to the packet descriptor with calls to an NdisUnchainBufferAt Xxx function. Reusing such a packet descriptor yields better performance than returning the descriptor to driver-allocated packet pool with NdisFreePacket and, then, reallocating it for another send later.

ProtocolSendComplete also can prepare the out-of-band block associated with the given packet descriptor for reuse if the protocol supplies any out-of-band information. Alternatively, the protocol can explicitly reinitialize the members it uses when setting up the packet descriptor for a subsequent send.

An NDIS intermediate driver must repackage each incoming send from still higher level protocols in fresh packet descriptors before passing such a send request to the underlying miniport with NdisSendPackets (or NdisSend).

Callers of NdisSendPackets run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportSend, MiniportSendPackets, NdisAllocateBuffer, NdisAllocatePacket, NdisFreePacket, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisReinitializePacket, NdisSetPacketFlags, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_TIME_TO_SEND, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, ProtocolSendComplete


NdisSetEvent

VOID
NdisSetEvent(
IN PNDIS_EVENT Event
);

NdisSetEvent sets a given event to the Signaled state if it was not already Signaled.

Parameters

Event
Points to an initialized event object for which the caller provides the storage.

Comments

When an event attains the Signaled state, it causes waits on the event to be satisfied and any waiters to be dispatched for execution.

After a call to NdisSetEvent, the event remains in the Signaled state until the driver calls NdisResetEvent.

Callers of NdisSetEvent run at IRQL <= DISPATCH_LEVEL.

See Also

NdisInitializeEvent, NdisResetEvent, NdisWaitEvent


NdisSetPacketFlags

VOID
NdisSetPacketFlags(
IN PNDIS_PACKET Packet,
IN UINT Flags
);

NdisSetPacketFlags sets caller-determined and medium-specific information in the flags of a given packet descriptor.

Parameters

Packet
Points to the packet descriptor in which the flags should be set.
Flags
Specifies the value to be set in the flags, which are in the private portion of the packet descriptor reserved for use by NDIS.

For protocols, the format of this value is specific to the medium that the underlying driver selected when the protocol called NdisOpenAdapter to set up the binding.

Comments

Setting the flags in a packet descriptor allows a protocol driver to pass information, usually about about a send request, that is not included in the packet data.

A protocol driver usually calls NdisSetPacketFlags to set the flags in packets sent on any medium that transmits data in an expedited manner. Some types of media include flags information in the packet header contained in buffers chained to the send packet. When a protocol driver is bound to an underlying driver for these types of media, a call to NdisSetPacketFlags is unnecessary.

When a bound protocol calls NdisSetPacketFlags, an underlying driver’s MiniportSendPackets function can read these flags only by calling NdisGetPacketFlags. For underlying drivers that have MiniportSend functions, a call to NdisGetPacketFlags is unnecessary because the flags are passed as an input parameter to the MiniportSend function.

All packet descriptors allocated with NdisAllocatePacket are zero-initialized, including the flags. If the value returned by NdisGetPacketFlags is zero, the underlying driver can assume that the packet has no protocol-set flags information.

However, protocols can supply far more media-specific information, such as the packet priority, in the out-of-band data block associated with each packet descriptor than a call to NdisSetPacketFlags with a single packet allows.

Callers of NdisSetPacketFlags run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportSend, MiniportSendPackets, NdisAllocatePacket, NdisGetPacketFlags, NdisOpenAdapter, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, ProtocolSendComplete


NDIS_SET_PACKET_HEADER_SIZE

NDIS_SET_PACKET_HEADER_SIZE(
IN PNDIS_PACKET Packet,
IN UINT HdrSize
);

NDIS_SET_PACKET_HEADER_SIZE sets the HeaderSize value in the out-of-band data block associated with a given packet descriptor for a subsequent receive indication.

Parameters

Packet
Points to a driver-allocated packet descriptor.
HdrSize
Specifies the medium-specific number of bytes in the net packet header.

Comments

The HeaderSize member in the NDIS_PACKET_OOB_DATA block associated with a packet descriptor specifies the number of bytes of medium-specific header in the buffer mapped by the initial buffer descriptor chained to the packet descriptor.

A lower-level driver sets this value with NDIS_SET_PACKET_HEADER_SIZE in the out-of-band data blocks for the receive packets it will subsequently indicate with NdisMIndicateReceivePacket. Typically, a miniport sets the HeaderSize once for each packet descriptor it allocates with NdisAllocatePacket for subsequent receive indications, depending on the medium it selected during initialization. If such a driver clears the out-of-band data block with NDIS_OOB_DATA_FROM_PACKET and NdisZeroMemory when it regains ownership of its receive packet descriptors, that driver must call NDIS_SET_PACKET_HEADER_SIZE before it reuses each packet descriptor in a subsequent indication.

When such an indication is made, bound protocols can retrieve this information with the NDIS_GET_PACKET_HEADER_SIZE macro.

Callers of NDIS_SET_PACKET_HEADER_SIZE run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportInitialize, MiniportReturnPacket, MiniportTimer, NdisAllocatePacket, NDIS_GET_PACKET_HEADER_SIZE, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NdisZeroMemory, ProtocolReceive, ProtocolReceivePacket


NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO

NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(
IN PNDIS_PACKET Packet,
IN PVOID MediaSpecificInfo,
IN UINT SizeMediaSpecificInfo
);

NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO sets the MediaSpecificInformation pointer and the SizeMediaSpecificInfo value in the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a driver-allocated packet descriptor.
MediaSpecificInfo
Specifies a pointer to a caller-supplied buffer containing medium-specific information, such as packet priority, to be set in the MediaSpecificInformation member of the NDIS_PACKET_OOB_DATA block associated with the given packet descriptor. This parameter cannot be NULL.
SizeMediaSpecificInfo
Specifies the number of bytes of information supplied at MediaSpecificInformation. This parameter cannot be zero.

Comments

Depending on the medium, an underlying driver might set these members with NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO in the out-of-band data blocks associated with the packet descriptors it allocates for receive indications. When a bound protocol is called with the indication, it would use NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO or, possibly, NdisQueryReceiveIndication, to access the supplied information.

Depending on the medium of the underlying driver to which it is bound, a protocol might set MediaSpecificInformation and SizeMediaSpecificInfo in the out-of-band blocks associated with the packet descriptors it allocates for sends. The underlying driver would use NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO to access the supplied information.

In either case, while a higher-level protocol is consuming a receive indication or a lower-level miniport is processing a send, that driver must have exclusive access to the buffer at MediaSpecificInformation. This restriction implies the following:

  • A miniport cannot access the buffer it set up with out-of-band information when NdisMIndicateReceivePacket returns control if NDIS_STATUS_PENDING is set in the Status member of the associated out-of-band block. The driver can use NDIS_GET_PACKET_STATUS to determine whether each packet in the packet array it just indicated has this status set or has been returned with NDIS_STATUS_SUCCESS.

  • A protocol driver cannot access the buffer it set up with out-of-band information for a send, nor anything else in the out-of-band data block associated with a packet descriptor it passes to NdisSendPackets or NdisSend, until the packet descriptor is returned to its ProtocolSendComplete function.

    Because NDIS queues send packets and resubmits them if the MiniportSend function returns NDIS_STATUS_RESOURCES or if the MiniportSendPackets function sets NDIS_STATUS_RESOURCES for the Status of a packet in a submitted array, the final status of each packet in a send (array) is volatile until that packet has been returned to ProtocolSendComplete.

A protocol driver can call NdisQueryReceiveInformation to obtain a copy of the NDIS_PACKET_OOB_DATA block associated with a packet descriptor when its ProtocolReceive function is given an indication. However, this call is superfluous if the protocol is bound to an underlying driver that does not supply out-of-band information with its indications.

Callers of NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportTimer, NdisAllocatePacket, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_STATUS, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryReceiveInformation, NdisSend, NdisSendPackets, NDIS_SET_PACKET_STATUS, ProtocolReceive, ProtocolReceivePacket, ProtocolSendComplete


NDIS_SET_PACKET_STATUS

NDIS_SET_PACKET_STATUS(
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
);

NDIS_SET_PACKET_STATUS sets the Status value in the out-of-band data block associated with a given packet descriptor just before a driver calls NdisMIndicateReceivePacket or before a driver’s MiniportSendPackets function returns control.

Parameters

Packet
Points to a packet descriptor either allocated by the caller for a receive indication or by a protocol for a send.
Status
Specifies the status to be set.

For packet descriptors used in receive indications, the value can be either of the following:

NDIS_STATUS_SUCCESS
The driver is relinquising ownership of the packet about to be indicated with NdisMIndicateReceivePacket until the packet descriptor is returned to its MiniportReturnPacket function.

Protocols that receive the indication can use the packet descriptor, along with all buffers mapped by buffer descriptors chained to the packet descriptor and any out-of-band information supplied with the packet, to copy the indicated net packet data, possibly forwarding the processed copy to interested clients.

NDIS_STATUS_RESOURCES
The driver is retaining ownership of the packet about to be indicated with NdisMIndicateReceivePacket and of all subsequent packets in the packet array. All these packets will be indicated, one at a time, to the ProtocolReceive functions of bound protocols.

Setting this status forces bound protocols to return ownership of each such packet descriptor, of any medium-specific buffer in the out-of-band data block for each packet descriptor, and of the memory mapped by each packet descriptor’s respective chained buffer descriptors to the indicating driver more quickly. The protocols must wait for a call to their ProtocolReceiveComplete functions to begin postprocessing the data they copied from the indication and forwarding the processed data to their clients.

An indicating driver can set this status if it is running low on packet pool or buffer pool. An indicating NIC driver usually sets this status if it is running low on NIC receive buffers due to high network traffic. A busmaster DMA NIC driver might call NdisMAllocateSharedMemoryAsync in these circumstances to get additional NIC receive buffer space.

NDIS assumes that, if this status is set for a packet in an indicated array, it is set for all subsequent packets in that array. Consequently, an indicating driver need not set the status for subsequent packet descriptors in an array after it sets this status in the out-of-band data block for one packet descriptor.

For packet descriptors used in sends, MiniportSendPackets can set one of the following values:

NDIS_STATUS_PENDING
The driver will complete the requested send operation asynchronously.

After MiniportSendPackets returns control, the driver will call NdisMSendComplete with a final completion status for the given packet descriptor, which NDIS propagates to the ProtocolSendComplete function of the driver that allocated the resources specified by the packet descriptor and initiated the send.

NDIS_STATUS_RESOURCES
The driver cannot process all sends specified in a given packet array due to current resource constraints.

NDIS assumes that the Status member for all subsequent packet descriptors in the array given to MiniportSendPackets is set to the same value. NDIS queues these packet descriptors internally in the same order for resubmission to MiniportSendPackets when the driver calls NdisMSendResourcesAvailable or NdisMSendComplete, whichever call occurs first. NDIS reflects this miniport-set status value to protocols as NDIS_STATUS_PENDING.

NDIS_STATUS_XXX
MiniportSendPackets is completing a given send request before it returns control.

Whether such a send packet is completed with NDIS_STATUS_SUCCESS or a driver-determined error status, MiniportSendPackets must set the Status member for the given packet descriptor so that NDIS can reflect this status back to the protocol that initiated the send when NDIS calls ProtocolSendComplete with the returned packet descriptor.

Comments

Only drivers that indicate receives with NdisMIndicateReceivePacket to bound protocols and/or drivers that have MiniportSendPackets functions use the NDIS_SET_PACKET_STATUS macro.

After such an underlying driver has set the Status for some number of packet descriptors in an array and called NdisMIndicateReceivePacket with the array of pointers to those packet descriptors, it must use the NDIS_GET_PACKET_STATUS macro when NdisMIndicateReceivePacket returns control. The returned Status in the NDIS_PACKET_OOB_DATA block associated with each packet descriptor that the driver indicated with NDIS_STATUS_SUCCESS determines what the indicating driver does next, as follows:

NDIS_STATUS_SUCCESS
If this is set on return from a miniport’s call to NdisMIndicateReceivePacket, the miniport regains ownership of the following:
  • The packet descriptor

  • The associated out-of-band data block associated with the packet descriptor and of any media-specific information buffer specified in this block

  • All buffers mapped by buffer descriptors chained to the packet descriptor

The driver can prepare these descriptors, the out-of-band block, and media-specific information buffer, if any, for reuse in subsequent receive indications immediately.

NDIS_STATUS_PENDING
If this is set on return from NdisMIndicateReceivePacket, protocols retain ownership of the packet descriptor, of its associated out-of-band data block and of any buffer specified in this block, and of all buffers mapped by buffer descriptors chained to the packet descriptor until the packet descriptor is returned to the indicating driver’s MiniportReturnPacket function. However, protocols are given read-only access to these miniport-allocated resources.

When the MiniportSendPackets function sets the Status for an incoming send packet to anything other than NDIS_STATUS_PENDING or NDIS_STATUS_RESOURCES, it completes the packet and relinquishes ownership to all resources specified by the associated packet descriptor. NDIS returns the packet descriptor to the ProtocolSendComplete function of the allocating driver and propagates the miniport-set status value as an input parameter to ProtocolSendComplete. Otherwise, the underlying driver will call NdisMSendComplete with the packet descriptor and final status when the asynchronous send operation is done.

Protocol drivers should never test the Status value in an associated out-of-band data block on return from NdisSendPackets or NdisSend. NDIS or the underlying driver retains ownership of the packet descriptor, of the associated out-of-band block and any buffers specified in this block, and of all buffers mapped by buffer descriptors chained to the packet descriptor until the ProtocolSendComplete function is called with the packet descriptor.

Callers of NDIS_SET_PACKET_STATUS run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportReturnPacket, MiniportSendPackets, MiniportTimer, NdisAllocatePacket, NDIS_GET_PACKET_STATUS, NdisMAllocateSharedMemoryAsync, NdisMIndicateReceivePacket, NdisMSendComplete, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, NDIS_SET_PACKET_HEADER_SIZE, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_SENT, ProtocolSendComplete


NDIS_SET_PACKET_TIME_RECEIVED

NDIS_SET_PACKET_TIME_RECEIVED(
IN PNDIS_PACKET Packet,
IN ULONGLONG TimeReceived
);

NDIS_SET_PACKET_TIME_RECEIVED sets the TimeReceived value in the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor allocated by the caller for receive indications.
TimeReceived
Specifies the system time at which the packet was received from the remote node on the network.

Comments

Drivers can use NDIS_SET_PACKET_TIME_RECEIVED to set this timestamp in the out-of-band data block associated with a packet descriptor before calling NdisMIndicateReceivePacket. Miniports that indicate multipacket receives can set the TimeReceived values for each packet in an indicated array to the same value before making an indication, particularly if the NIC has no internal clock, so the NIC driver calls NdisGetCurrentSystemTime.

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packet descriptors are expressed in system time units as the number of 100-nanosecond intervals since January 1, 160l. A driver can call NdisGetCurrentSystemTime to supply the TimeReceived argument to NDIS_SET_PACKET_TIME_RECEIVED.

When a protocol driver’s ProtocolReceivePacket function is called with such an indication, it can use NDIS_GET_TIME_RECEIVED to retrieve the TimeReceived timestamp using the Packet pointer it is given on entry.

If the ProtocolReceive function is called with an indicated packet descriptor for which the underlying driver set the TimeReceived timestamp, ProtocolReceive can call NdisQueryReceiveInformation to get this timestamp.

Callers of NDIS_SET_PACKET_TIME_RECEIVED run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportTimer, NdisGetCurrentSystemTime, NDIS_GET_PACKET_TIME_RECEIVED, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryReceiveInformation, NDIS_SET_PACKET_TIME_SENT, ProtocolReceive, ProtocolReceivePacket


NDIS_SET_PACKET_TIME_SENT

NDIS_SET_PACKET_TIME_SENT(
IN PNDIS_PACKET Packet,
IN ULONGLONG TimeSent
);

NDIS_SET_PACKET_TIME_SENT sets the TimeSent value in the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor allocated by the caller for receive indications.
TimeSent
Specifies the system time at which the packet was transmitted over the network from a remote node.

Comments

Miniports can use NDIS_SET_PACKET_TIME_SENT to set this timestamp in the out-of-band data block associated with a packet descriptor they are about to indicate with NdisMIndicateReceivePacket.

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packets are expressed in system time units as the number of 100-nanosecond intervals since January 1, 160l.

When a protocol driver’s ProtocolReceivePacket function is called with such an indication, it can use NDIS_GET_PACKET_TIME_SENT to retrieve the TimeSent timestamp using the Packet pointer it is given on entry.

If the ProtocolReceive function is called with an indicated packet descriptor for which the underlying driver set the TimeSent timestamp, ProtocolReceive can call NdisQueryReceiveInformation to get this timestamp.

Callers of NDIS_SET_PACKET_TIME_SENT run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportHandleInterrupt, MiniportTimer, NDIS_GET_PACKET_TIME_SENT, NdisMIndicateReceivePacket, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisQueryReceiveInformation, NDIS_SET_PACKET_TIME_RECEIVED, ProtocolReceive, ProtocolReceivePacket


NDIS_SET_PACKET_TIME_TO_SEND

NDIS_SET_PACKET_TIME_TO_SEND(
IN PNDIS_PACKET Packet,
 IN ULONGLONG TimeToSend
);

NDIS_SET_PACKET_TIME_TO_SEND sets the TimeToSend value in the out-of-band data block associated with a given packet descriptor.

Parameters

Packet
Points to a packet descriptor allocated by the caller for a send.
TimeToSend
Specifies the system time at which the packet should be transmitted over the network.

Comments

Protocols can use NDIS_SET_PACKET_TIME_TO_SEND to set this timestamp in the out-of-band data block associated with a driver-allocated packet descriptor to be passed in a subsequent call to NdisSendPackets or NdisSend.

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packet descriptors are expressed in system time units as the number of 100-nanosecond intervals since January 1, 160l.

A protocol can call NdisGetCurrentSystemTime and add some number of system time units to the returned value in order to supply the TimeToSend argument to NDIS_SET_PACKET_TIME_TO_SEND, unless the client that initiated the send request has already supplied such a timestamp.

Callers of NDIS_SET_PACKET_TIME_TO_SEND run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportSend, MiniportSendPackets, NdisGetCurrentSystemTime, NDIS_GET_PACKET_TIME_SENT, NDIS_GET_PACKET_TIME_TO_SEND, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisSend, NdisSendPackets, ProtocolSendComplete


NdisSetPhysicalAddressHigh

VOID
NdisSetPhysicalAddressHigh(
IN NDIS_PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG Value
);

NdisSetPhysicalAddressHigh sets the high-order part of a given physical address to a given value.

Parameters

PhysicalAddress
Specifies a physical address of an OS-dependent size.
Value
Specifies the value to be written into the high-order part of the address.

Comments

Callers of NdisSetPhysicalAddressHigh can be running at any IRQL.

See Also

NdisGetPhysicalAddressHigh, NDIS_PHYSICAL_ADDRESS, NdisSetPhysicalAddressLow


NdisSetPhysicalAddressLow

VOID
NdisSetPhysicalAddressLow(
IN NDIS_PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG Value
);

NdisSetPhysicalAddressLow sets the low-order part of a given physical address to a given value.

Parameters

PhysicalAddress
Specifies a physical address of an OS-dependent size.
Value
Specifies the value to be written into the low-order part of the address.

Comments

Callers of NdisSetPhysicalAddressLow can be running at any IRQL.

See Also

NdisGetPhysicalAddressLow, NDIS_PHYSICAL_ADDRESS, NdisSetPhysicalAddressHigh


NdisSetSendFlags

Use NdisSetPacketFlags instead.


NdisStallExecution

VOID
NdisStallExecution(
IN UINT MicrosecondsToStall
);

NdisStallExecution stalls the caller on the current processor for a given interval.

Parameters

MicrosecondsToStall
Specifies the number of microseconds to delay.

Comments

For the given MicrosecondsToStall, the caller’s thread of execution stalls on the current processor, consuming CPU cycles. When the given interval expires, the caller resumes execution.

Unless a driver is running at IRQL >= DISPATCH_LEVEL, it should call NdisMSleep, which accepts larger delay intervals without monopolizing a processor for its callers, instead of NdisStallExecution. For example, a NIC driver’s MiniportInitialize function should wait for state changes in the NIC with NdisMSleep.

Both NdisMSleep and NdisStallExecution allow a NIC driver to specify a delay consistently and independently of the clock speed of the host CPU. Neither function involves a timer object used by NdisSet(Periodic)Timer. The resolution of the host system clock varies, so very short delays can take slightly longer than the caller of NdisMSleep or NdisStallExecution specified.

A call to NdisStallExecution prevents any other operating system activity on the current processor and prevents system interrupts from occurring during the stall. Therefore, callers should always specify a stall interval of no longer than 50 microseconds.

Callers of NdisStallExecution can run at any IRQL.

See Also

MiniportInitialize, MiniportReset, NdisMSleep, NdisSetTimer


NdisStoreUlong

VOID
NdisStoreUlong(
IN PULONG DestinationAddress,
IN ULONG Value
);

NdisStoreUlong stores a ULONG value at a particular address, avoiding alignment faults.

Parameters

DestinationAddress
Points to a location in which to store a given ULONG value.
Value
Specifies the value to be stored.

Comments

Callers of NdisStoreUlong can be running at any IRQL if DestinationAddress points to nonpaged pool. Otherwise, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

NdisRetrieveUlong


NdisSystemProcessorCount

CCHAR
NdisSystemProcessorCount(VOID);

NdisSystemProcessorCount determines whether its caller is running on a uniprocessor or multiprocessor machine.

Return Value

NdisSystemProcessorCount returns the number of processors in the machine.

Comments

An NDIS driver might call this function during initialization before it allocates resources.

For example, suppose NdisSystemProcessorCount returns a value greater than one, which suggests the machine is more likely to be a server than a workstation. When this occurs, the driver could allocate more memory on the assumption that network traffic will be significantly heavier on a server.

Callers of NdisSystemProcessorCount run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize


NdisTerminateWrapper

VOID
NdisTerminateWrapper(
IN NDIS_HANDLE NdisWrapperHandle,
IN PVOID SystemSpecific
);

NdisTerminateWrapper releases system resources allocated when the NIC driver called NdisMInitializeWrapper.

Parameters

NdisWrapperHandle
Specifies the handle returned by NdisMInitializeWrapper.
SystemSpecific
Points to an OS-specific type. For Windows NT drivers, this parameter should be NULL.

Comments

A miniport calls NdisTerminateWrapper during initialization if it cannot find a NIC that it supports in the current machine or if it cannot successfully initialize at least one NIC or virtual NIC.

The NDIS library cleans up the resources it allocated if a miniport driver is unloaded after it has successfully initialized.

A driver also should call NdisTerminateWrapper from its MiniportHalt function when its last NIC is being removed.

Callers of NdisTerminateWrapper run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Miniport Drivers, MiniportHalt, MiniportInitialize, NdisMInitializeWrapper


NdisTransferData

VOID
NdisTransferData(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE NdisBindingHandle,
IN NDIS_HANDLE MacReceiveContext,
IN UINT ByteOffset,
IN UINT BytesToTransfer,
IN OUT PNDIS_PACKET Packet,
OUT PUINT BytesTransferred
);

NdisTransferData forwards a request to copy data received on the underlying NIC into a protocol-supplied packet.

Parameters

Status
Points to a caller-supplied variable that can be one of the following values on return from this function:
NDIS_STATUS_SUCCESS
The requested data has been transferred into the packet at Packet.
NDIS_STATUS_PENDING
The request is being handled asynchronously, and the caller’s ProtocolTransferComplete function will be called when it is completed.
NDIS_STATUS_RESET_IN_PROGRESS
The underlying driver is currently resetting its NIC or virtual NIC state. The caller’s ProtocolStatus function was or will be called with NDIS_STATUS_RESET_START to indicate that a reset is in progress.
NDIS_STATUS_REQUEST_ABORTED
The caller’s binding is being closed.
NDIS_STATUS_FAILURE
The given ByteOffset and/or the given BytesToTransfer is too large.
NdisBindingHandle
Specifies the handle returned by NdisOpenAdapter that identifies the target NIC or the virtual adapter of the next-lower driver to which the caller is bound.
MacReceiveContext
Specifies the underlying driver’s handle that was passed as an input parameter to the caller’s ProtocolReceive function. The protocol driver must consider this handle to be opaque.
ByteOffset
Specifies the offset from the start of the receive network packet at which to begin the transfer.
BytesToTransfer
Specifies the number of bytes to transfer. This value can be zero.
Packet
Points to the packet descriptor, provided by the caller, into which the underlying NIC driver is to copy the data.
BytesTransferred
Points to a caller-supplied variable in which this function returns the number of bytes actually transferred. The value is invalid if NdisTransferData returns NDIS_STATUS_PENDING at Status.

Comments

Several protocols can be bound to a single underlying NIC, and each such protocol driver can receive an indication for the same packet. Such a packet is read-only to protocol drivers. Each such driver’s ProtocolReceive function determines whether to make itself a copy of the indicated packet with NdisTransferData.

Before calling NdisTransferData, the protocol must allocate a packet descriptor and chain some number of buffer descriptors mapping protocol-allocated buffers into which the underlying driver will copy the data. The protocol also might set up the ProtocolReserved part of its packet descriptor before calling NdisTransferData.

A protocol driver should always allocate its packet descriptors from the packet pool that the driver set up during initialization.

The range requested in a call to NdisTransferData, specified by ByteOffset and BytesToTransfer, should be suitable to the PacketSize passed in to the caller’s ProtocolReceive function. Callers of NdisTransferData usually pass a ByteOffset value calculated from the input parameters to ProtocolReceive as (LookaheadBufferRange + 1). That is, the ProtocolReceive function already consumed the data in the lookahead buffer so it calls NdisTransferData to get the remaining data in the indicated receive packet.

Callers of NdisTransferData run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocatePacket, NdisMIndicateReceivePacket, NDIS_PACKET, ProtocolReceive, ProtocolReceivePacket, ProtocolTransferDataComplete


NdisUnchainBufferAtBack

VOID
NdisUnchainBufferAtBack(
IN OUT PNDIS_PACKET Packet,
OUT PNDIS_BUFFER *Buffer
);

NdisUnchainBufferAtBack removes the buffer descriptor at the end of the chain of buffer descriptors for a given packet.

Parameters

Packet
Points to the packet descriptor.
Buffer
Points to a caller-supplied variable in which this function returns a pointer to the removed buffer descriptor. If no buffer is chained to the given packet, this variable is set to NULL.

Comments

NdisUnchainBufferAtBack returns a pointer to the buffer descriptor it removed from the buffer descriptor chain of the given packet.

It also resets the valid counts for the packet to FALSE, thereby forcing NdisQueryPacket to recalculate information about the given packet if it is called subsequently with that packet.

Callers of NdisUnchainBufferAtBack can run at any IRQL.

See Also

NdisChainBufferAtBack, NdisQueryPacket, NdisUnchainBufferAtFront


NdisUnchainBufferAtFront

VOID
NdisUnchainBufferAtFront(
IN OUT PNDIS_PACKET Packet,
OUT PNDIS_BUFFER *Buffer
);

NdisUnchainBufferAtFront removes the buffer descriptor at the head of the chain of buffer descriptors for a given packet.

Parameters

Packet
Points to the packet descriptor.
Buffer
Points to a caller-supplied variable in which this function returns a pointer to the removed buffer descriptor. If no buffer is chained to the given packet, this variable is set to NULL.

Comments

NdisUnchainBufferAtFront returns a pointer to the buffer descriptor it removed from the buffer descriptor chain of the given packet.

It also resets the valid counts for the packet to FALSE, thereby forcing NdisQueryPacket to recalculate information about the given packet if it is called subsequently with that packet.

Callers of NdisUnchainBufferAtFront can run at any IRQL.

See Also

NdisChainBufferAtFront, NdisQueryPacket, NdisUnchainBufferAtBack


NdisUnicodeStringToAnsiString

NDIS_STATUS
NdisUnicodeStringToAnsiString(
IN OUT PNDIS_ANSI_STRING DestinationString,
IN PNDIS_STRING SourceString
);

NdisUnicodeStringToAnsiString converts a given counted Unicode string into a counted ANSI string. The translation conforms to the current system locale information.

Parameters

DestinationString
Points to a caller-allocated buffer in which this function should return the converted ANSI string.
SourceString
Points to the Unicode string to be converted.

Return Value

NdisUnicodeStringToAnsiString returns NDIS_STATUS_SUCCESS if the conversion succeeds. Otherwise, the contents of the buffer at DestinationString are unaffected.

Comments

The caller must allocate storage for both the source and destination strings and release these buffers as soon as the strings are no longer needed. The buffer at SourceString must be at least twice the size of that at DestinationString.

Callers of NdisUnicodeStringToAnsiString run at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, NdisAnsiStringToUnicodeString, NdisInitAnsiString, NdisInitializeString, NdisInitUnicodeString, ProtocolBindAdapter


NdisUnmapFile

VOID
NdisUnmapFile(
IN NDIS_HANDLE FileHandle
);

NdisUnmapFile releases a virtual address mapping of a file previously set up with NdisMapFile.

Parameters

FileHandle
Specifies the handle returned by NdisOpenFile.

Comments

To reduce resource usage, a NIC driver should always call NdisUnmapFile when it no longer needs exclusive access to the contents of a file that it opened. Such a driver can map and unmap the file as necessary, using alternating calls to NdisMapFile and NdisUnmapFile until it releases the file handle with NdisCloseFile.

Callers of NdisUnmapFile run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisCloseFile, NdisMapFile, NdisOpenFile


NdisWaitEvent

BOOLEAN
NdisWaitEvent(
IN PNDIS_EVENT Event,
IN UINT MsToWait
);

NdisWaitEvent puts the caller into a wait state until the given event is set to the Signaled state or the wait times out.

Parameters

Event
Points to an initialized event object for which the caller provides the storage.
MsToWait
Specifies the number of milliseconds the caller will wait if the event is not set to the Signaled state within that interval.

Return Value

NdisWaitEvent returns TRUE if the event is in the Signaled state when the wait is satisfied.

Comments

NdisWaitEvent returns control to its caller when the given event is Signaled or the given MsToWait interval expires, whichever is sooner. If the event is currently in the Signaled state when this call occurs, NdisWaitEvent returns control immediately.

NIC drivers can call NdisWaitEvent from their MiniportInitialize functions. Protocol drivers can call this function when they are running at IRQL < DISPATCH_LEVEL and in a nonarbitrary thread context, such as from their DriverEntry or ProtocolBindAdapter routines.

Callers of NdisWaitEvent must be running at IRQL PASSIVE_LEVEL.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportHalt, MiniportInitialize, NdisInitializeEvent, NdisResetEvent, NdisSetEvent, ProtocolBindAdapter, ProtocolUnbindAdapter


NdisWriteConfiguration

VOID
NdisWriteConfiguration(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE WrapperConfigurationContext,
IN PNDIS_STRING Keyword,
IN PNDIS_CONFIGURATION_PARAMETER *ParameterValue
);

NdisWriteConfiguration writes a caller-supplied value for a specified entry into the registry.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The supplied value at ParameterValue was written into the registry. If this is a new entry, the name at Keyword also was written into the registry.
NDIS_STATUS_NOT_SUPPORTED
The supplied ParameterType is invalid.
NDIS_STATUS_RESOURCES
NDIS could not allocate resources, usually enough memory, to transfer the requested information to the registry.
NDIS_STATUS_FAILURE
The requested information could not be written.
WrapperConfigurationContext
Specifies the handle returned by NdisOpenConfiguration or NdisOpenProtocolConfiguration.
Keyword
Points to a caller-supplied counted string, in the system-default character set, specifying the name of an entry for which to write the value. For Windows NT drivers, this string contains Unicode characters.
ParameterValue
Points to a caller-supplied variable set to the address of a filled-in NDIS_CONFIGURATION_PARAMETER structure, defined as follows:
typedef struct _NDIS_CONFIGURATION_PARAMETER {
    NDIS_PARAMETER_TYPE ParameterType;
    union {
        ULONG IntegerData;
        NDIS_STRING StringData;
    } ParameterData;
} NDIS_CONFIGURATION_PARAMETER, *PNDIS_CONFIGURATION_PARAMETER;

The members of this structure can contain the following data:

ParameterType
Specifies the type of the value entry as one of the following:
NdisParameterInteger
Specifies an integer in decimal notation.
NdisParameterHexInteger
Specifies an integer in hexadecimal notation.
NdisParameterString
Specifies a string of type NDIS_STRING.

For Windows NT drivers, this is a counted Unicode string.

NdisParameterMultiString
Specifies a multistring parameter of the
Windows NT-defined REG_MULTI_SZ type.
ParameterData
Specifies the value of the given named entry in the notation of the specified ParameterType. If ParameterType is a string type, this member is a pointer to a buffered counted string in the system-default character set.

For Windows NT drivers, such a string contains Unicode characters.

Comments

If an entry of the same name as at Keyword already exists under the opened registry key, NdisWriteConfiguration replaces its current value with the caller-supplied value. Otherwise, NdisWriteConfiguration adds a new value entry with the given name and supplied value to the registry.

In the Windows NT configuration registry, an NDIS keyword is a synonym for a value entry name. Such a name is a counted sequence of Unicode characters, terminated with a zero.

NdisWriteConfiguration buffers and copies the caller-supplied string at Keyword and the caller-supplied data specified at ParameterValue. This memory is freed when the driver releases the ConfigurationHandle with NdisCloseConfiguration. The caller of NdisWriteConfiguration is responsible for releasing the buffered string at Keyword and the memory allocated for the NDIS_CONFIGURATION_PARAMETER structure.

As an alternative to calling NdisWriteConfiguration, every NDIS driver can set up configuration information in the registry for itself under the driver’s Parameters key using an installation script.

For more information about Windows NT setup and installation scripts, see the Programmer’s Guide.

Callers of NdisWriteConfiguration run at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisAnsiStringToUnicodeString, NdisCloseConfiguration, NdisFreeMemory, NdisFreeString, NdisInitAnsiString, NdisInitializeString, NdisInitUnicodeString, NdisOpenConfiguration, NdisOpenProtocolConfiguration, NdisReadConfiguration, NdisUnicodeStringToAnsiString


NdisWriteErrorLogEntry

VOID
NdisWriteErrorLogEntry(
IN NDIS_HANDLE NdisAdapterHandle,
IN NDIS_ERROR_CODE ErrorCode,
IN ULONG NumberOfErrorValues,
IN ULONG ...
);

NdisWriteErrorLogEntry writes an entry to the system I/O error log file.

Parameters

NdisAdapterHandle
Specifies the handle representing the NIC that is the cause of the I/O error to be logged. This handle is an input parameter to MiniportInitialize.
ErrorCode
Specifies the NDIS_STATUS_XXX code describing the I/O error.
NumberOfErrorValues
Specifies the number of ULONG values to follow this parameter.
...
Specifies a variable-sized array of ULONGs associated with the error to be logged.

Comments

NdisWriteErrorLogEntry allocates an I/O error log record, fills in the record with the supplied information about the error, and then writes the record to the I/O error log file.

The system places a limit on the potential size of an error log record. The Windows NT limit is defined as ERROR_LOG_MAXIMUM_SIZE. The NumberOfErrorValues passed to NdisWriteErrorLogEntry therefore has a system-enforced limit, which is considerably less than the maximum possible value for a ULONG.

In practice, few callers of NdisWriteErrorLogEntry even approach the limit for NumberOfErrorValues because supplying many additional NDIS_STATUS_XXX values is not particularly helpful to the user or system administrator who later reads the error log, using the Win32 event viewer. Logging an I/O error at every possible opportunity is not particularly helpful to users either, so a miniport should log only critical I/O errors that can help a user or system administrator to debug a network failure for which the NIC is responsible on a particular machine or a configuration resource conflict discovered during driver initialization.

In general, a NIC driver calls NdisWriteErrorLogEntry during initialization from its MiniportInitialize function if it cannot initialize a NIC that it controls or cannot allocate the hardware resources it needs to carry out I/O operations on its NIC. A NIC driver also can call NdisWriteErrorLogEntry when a device-reset operation fails due to unrecoverable hardware error(s). Logging these kinds of I/O errors helps users or system administrators to identify a badly configured NIC or a NIC with failing hardware components.

Callers of NdisWriteErrorLogEntry run at IRQL <= DISPATCH_LEVEL.

See Also

MiniportInitialize, MiniportReset, NdisMSetAttributes, NdisMSetAttributesEx


NdisWritePciSlotInformation

ULONG
NdisWritePciSlotInformation(
IN NDIS_HANDLE NdisAdapterHandle,
IN ULONG SlotNumber,
IN ULONG Offset,
IN PVOID Buffer,
IN ULONG Length
 );

NdisWritePciSlotInformation writes a specified number of bytes to the PCI configuration space for a NIC on the PCI bus.

Parameters

NdisAdapterHandle
Specifies the MiniportAdapterHandle passed in to MiniportInitialize and to most other MiniportXxx functions.
SlotNumber
Specifies the slot number of the NIC on the I/O bus.
Offset
Specifies the byte offset within the PCI configuration space at which to begin transferring the caller-supplied configuration information.
Buffer
Points to a caller-allocated buffer containing the PCI configuration information to be written.
Length
Specifies the size in bytes of the buffer at Buffer and, therefore, how many bytes of PCI configuration information to write.

Return Value

NdisWritePciSlotInformation returns how many bytes of caller-supplied data it wrote.

Comments

NdisWritePciSlotInformation writes PCI configuration information for a NIC. However, drivers of NICs on PCI buses call NdisImmediateReadPciSlotInformation, NdisReadPciSlotInformation, or NdisMPciAssignResources far more frequently than this function.

When NIC driver finds its device on a PCI bus with Ndis(Immediate)ReadPciSlotInformation or by retrieving information from the \DriverName\Parameters key in the registry, that driver can call NdisMPciAssignResources to determine the bus-relative configuration parameters it can use in subsequent calls to other Ndis Xxx configuration functions. If NdisMPciAssignResources returns values that require the driver to reconfigure its device on the bus, it can call NdisWritePciSlotInformation.

NdisWritePciSlotInformation transfers the same type of bus-relative PCI configuration information as NdisImmediateWritePciSlotInformation. However, callers of NdisWritePciSlotInformation pass the MiniportAdapterHandle passed in to the MiniportInitialize function, rather than the WrapperConfigurationContext handle.

The caller is responsible for releasing the memory at Buffer when NdisWritePciSlotInformation returns control.

Callers of NdisWritePciSlotInformation can run at IRQL <= DISPATCH_LEVEL. Usually, callers are running at IRQL PASSIVE_LEVEL.

See Also

MiniportInitialize, NdisFreeMemory, NdisImmediateWritePciSlotInformation, NdisMMapIoSpace, NdisMPciAssignResources, NdisMRegisterInterrupt, NdisMRegisterIoPortRange, NdisMSetAttributes, NdisMSetAttributesEx, NdisReadPciSlotInformation


NdisWriteRegisterUchar

VOID
NdisWriteRegisterUchar(
IN PUCHAR Register,
 IN UCHAR Data
);

NdisWriteRegisterUchar is called by the NIC driver to write a UCHAR to a memory-mapped device register.

Parameters

Register
Points to the memory-mapped register. This virtual address must fall within a range returned by an initialization-time call to NdisMMapIoSpace.
Data
Specifies the caller-supplied UCHAR that this function transfers to the Register.

Comments

If a driver calls this function, its NIC’s device registers must be mapped to noncached memory during driver initialization.

Callers of NdisWriteRegisterUchar can be running at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisReadRegisterUchar, NdisWriteRegisterUlong, NdisWriteRegisterUshort


NdisWriteRegisterUlong

VOID
NdisWriteRegisterUlong(
IN PULONG Register,
 IN ULONG Data
);

NdisWriteRegisterUlong is called by the NIC driver to write a ULONG to a memory-mapped device register.

Parameters

Register
Points to the memory-mapped register. This virtual address must fall within a range returned by an initialization-time call to NdisMMapIoSpace.
Data
Specifies the caller-supplied ULONG that this function transfers to the Register.

Comments

If a driver calls this function, its NIC’s device registers must be mapped to noncached memory during driver initialization.

Callers of NdisWriteRegisterUlong can be running at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisReadRegisterUlong, NdisWriteRegisterUchar, NdisWriteRegisterUshort


NdisWriteRegisterUshort

VOID
NdisWriteRegisterUshort(
IN PUSHORT Register,
 IN USHORT Data
);

NdisWriteRegisterUshort is called by the NIC driver to write a USHORT to a memory-mapped device register.

Parameters

Register
Points to the memory-mapped register. This virtual address must fall within a range returned by an initialization-time call to NdisMMapIoSpace.
Data
Specifies the caller-supplied USHORT that this function transfers to the Register.

Comments

If a driver calls this function, its NIC’s device registers must be mapped to noncached memory during driver initialization.

Callers of NdisWriteRegisterUshort can be running at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisReadRegisterUshort, NdisWriteRegisterUchar, NdisWriteRegisterUlong


NdisZeroMappedMemory

VOID
NdisZeroMappedMemory(
IN PVOID Destination,
IN ULONG Length
);

NdisZeroMappedMemory fills a block of memory that was mapped with a preceding call to NdisMMapIoSpace with zeros.

Parameters

Destination
Specifies the base virtual address of a block of mapped memory.
Length
Specifies the number of bytes to be filled with zeros.

Comments

A NIC driver can call NdisZeroMappedMemory to zero-initialize mapped device memory. The given Destination and Length must be a proper subrange of the range specified when the driver called NdisMMapIoSpace.

NdisZeroMappedMemory is optimized, and a NIC driver can call this function any time that it needs to clear a mapped memory range.

Callers of NdisZeroMappedMemory can run at any IRQL.

See Also

MiniportInitialize, NdisMMapIoSpace, NdisZeroMemory


NdisZeroMemory

VOID
NdisZeroMemory(
IN PVOID Destination,
IN ULONG Length
);

NdisZeroMemory fills a block of memory with zeros.

Parameters

Destination
Specifies the base virtual address of a block of memory.
Length
Specifies how many bytes to fill with zeros.

Comments

NdisZeroMemory fills the specified range with zeros. The given Destination and Length must be a proper subrange of a caller-accessible memory range.

As long as the given range was allocated from nonpaged pool, the caller can be running at any IRQL. Otherwise, a caller must be running at IRQL < DISPATCH_LEVEL as, for example, when a caller clears a structure it allocated on the stack.

See Also

NdisAllocateMemory, NdisZeroMappedMemory


TR_COMPARE_NETWORK_ADDRESSES

VOID
TR_COMPARE_NETWORK_ADDRESSES(
IN PCHAR A,
IN PCHAR B,
OUT PINT Result
);

TR_COMPARE_NETWORK_ADDRESSES sets a caller-supplied variable to a value indicating whether a given Token Ring address is greater than, less than, or equal to another given Token Ring address.

Parameters

A
Points to the first network address.
B
Points to the second network address.
Result
Points to a caller-supplied variable in which this macro returns the result of its comparison as one of the following values:
Result < zero
B is greater than A.
Result > zero
A is greater than B.
Zero
A equals B.

Comments

Token Ring network addresses do not have a defined ordering relationship. Consequently, the results of this macro for unequal addresses depend on the argument ordering.

Callers of this macro can be running at any IRQL if all pointer parameters access resident memory.

See Also

TR_COPY_NETWORK_ADDRESS


TR_COPY_NETWORK_ADDRESS

VOID
TR_COPY_NETWORK_ADDRESS(
OUT PCHAR D,
IN PCHAR S
);

TR_COPY_NETWORK_ADDRESS copies a given Token Ring address to a given location.

Parameters

D
Points to a caller-supplied varible to which this function copies the source address.
S
Points to the source address.

Comments

Callers of this macro can be running at any IRQL as long as the source and destination address pointers are resident.

See Also

TR_COMPARE_NETWORK_ADDRESSES


TR_IS_BROADCAST

VOID
TR_IS_BROADCAST(
IN PUCHAR A,
OUT PBOOLEAN R
);

TR_IS_BROADCAST sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a broadcast address.

Parameters

A
Points to a network address.
R
Points to a caller-supplied variable in which this macro returns TRUE if the address is broadcast.

Comments

A broadcast address applies to all stations in the extended network.

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

TR_IS_FUNCTIONAL, TR_IS_GROUP, TR_IS_NOT_DIRECTED, TR_IS_SOURCE_ROUTING


TR_IS_FUNCTIONAL

VOID
TR_IS_FUNCTIONAL(
IN PUCHAR A,
OUT PBOOLEAN R
);

TR_IS_FUNCTIONAL sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a functional address.

Parameters

A
Points to a network address.
R
Points to a caller-supplied variable in which this macro returns TRUE if the address is functional.

Comments

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

TR_IS_BROADCAST, TR_IS_GROUP, TR_IS_NOT_DIRECTED, TR_IS_SOURCE_ROUTING


TR_IS_GROUP

VOID
TR_IS_GROUP(
IN PUCHAR A,
OUT PBOOLEAN R
);

TR_IS_GROUP sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a group address.

Parameters

A
Points to a network address.
R
Points to a caller-supplied variable in which this macro returns TRUE if the address is a group address.

Comments

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

TR_IS_BROADCAST, TR_IS_FUNCTIONAL, TR_IS_NOT_DIRECTED, TR_IS_SOURCE_ROUTING


TR_IS_NOT_DIRECTED

VOID
TR_IS_NOT_DIRECTED(
IN PUCHAR A,
OUT PBOOLEAN R
);

TR_IS_NOT_DIRECTED sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is neither a functional nor a group address.

Parameters

A
Points to a network address.
R
Points to a caller-supplied variable in which this macro returns TRUE if the address is not directed.

Comments

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

TR_IS_BROADCAST, TR_IS_FUNCTIONAL, TR_IS_GROUP, TR_IS_SOURCE_ROUTING


TR_IS_SOURCE_ROUTING

VOID
TR_IS_SOURCE_ROUTING(
IN PUCHAR A,
OUT PBOOLEAN R
);

TR_IS_SOURCE_ROUTING sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a source-routing address.

Parameters

A
Points to a network address.
R
Points to a caller-supplied variable in which this macro returns TRUE if the address is a source-routing address.

Comments

A source routing address has the source-routing bit asserted.

Callers of this macro can be running at any IRQL as long as the source address and supplied variable are resident.

See Also

TR_IS_BROADCAST, TR_IS_FUNCTIONAL, TR_IS_GROUP, TR_IS_NOT_DIRECTED


Chapter 4 NDIS Driver Lower-Edge Functions

This describes, in alphabetic order, the system-defined functions implemented in NDIS drivers layered immediately above any driver that exports a set of NDIS upper-edge (MiniportXxx) functions.

Two types of NDIS drivers have the functions described here:

  1. NDIS transport protocols that export a set of the ProtocolXxx functions described here and communicate with their clients through the TDI interface described later in Part 2.

  2. NDIS intermediate drivers that export the ProtocolXxx functions described here, along with a set of the MiniportXxx functions, described earlier in Chapter 1.

    Intermediate NDIS drivers appear to be NIC miniports to higher-level NDIS drivers that layered themselves above such an intermediate driver by establishing a binding. Intermediate NDIS drivers appear to be protocols to the NDIS drivers below them, including NIC drivers. To the NDIS library, such an intermediate driver appears to be both a protocol and a miniport driver.

Driver Function Names

All NDIS driver functions with the prefix Protocol have been given metanames describing their respective basic functionality in the references that follow.

Except for the DriverEntry function, NDIS-defined driver functions can have any name the driver writer chooses. For easy debugging, most system-supplied protocols simply substitute a driver-specific prefix for the Protocol prefix used in this documentation.

The initial entry point of any Windows NT kernel-mode driver has the explicit name DriverEntry in order to be loaded automatically by the system. For more information about the general requirements for and functionality of Windows NT DriverEntry routines, see also the Kernel-Mode Driver Design Guide.

Calling NdisXxx Functions at Appropriate IRQLs

NDIS lower-edge driver functions run, by default, at one of the following IRQLs, shown in order of increasing priority:

PASSIVE_LEVEL < DISPATCH_LEVEL.

Any driver function running at IRQL PASSIVE_LEVEL can be pre-empted by a thread of execution running at a higher priority IRQL. That is, every ProtocolXxx function can be pre-empted by device drivers’ ISRs that run at DIRQL when interrupts occur or by a system clock interrupt.

Each of the following DriverEntry and ProtocolXxx descriptions specifies the default IRQL at which that function runs. The default IRQLs of certain ProtocolXxx functions effectively restrict the set of Ndis Xxx functions that these ProtocolXxx can call. For example, a call to NdisCopyBuffer from a ProtocolXxx function that runs at IRQL DISPATCH_LEVEL or while the ProtocolXxx is holding a spin lock can cause a fatal page fault.

Initialization-Only and Pageable Driver Code

NDIS protocols mark their DriverEntry functions as initialization-only code, using the NDIS_INIT_FUNCTION macro.

Other driver functions can be marked as pageable code, using the NDIS_PAGABLE_FUNCTION macro. However, a driver function can be pageable code if and only if it never runs at IRQL >= DISPATCH_LEVEL. This restriction implies the following:

  • A function marked pageable cannot acquire any spin lock and should not call other functions that do.

  • A function marked pageable cannot be called by any function that is holding a spin lock when the call occurs.

  • A function marked pageable cannot run at raised IRQL.

Most of the ProtocolXxx functions described here except ProtocolBindAdapter, ProtocolOpenAdapterComplete, ProtocolUnbindAdapter, and ProtocolCloseAdapterComplete run, by default, at IRQL DISPATCH_LEVEL.


DriverEntry of NDIS Protocol Drivers

NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);

DriverEntry is a required function, which the system calls first in any NDIS protocol or intermediate driver.

Parameters

DriverObject
Points to the driver object representing this driver.
RegistryPath
Points to a driver-specific path specification for the \Registry\Machine
\System\CurrentControlSet\Services\DriverName\Parameters key.

Return Value

DriverEntry returns NTSTATUS_SUCCESS, or its equivalent NDIS_STATUS_SUCCESS, if the driver registered as an NDIS protocol and, possibly, initialized itself successfully. If DriverEntry fails initialization by propagating an error status returned by an Ndis Xxx or by a kernel-mode support routine, the driver will not remain loaded. DriverEntry must execute synchronously; that is, it cannot return NTSTATUS_PENDING or its equivalent NDIS_STATUS_PENDING.

Comments

A driver that exports only NDIS-defined driver functions, such as an NDIS intermediate driver that translates an underlying NIC driver’s native frame format into another format for higher-level protocols, simply passes its input parameters, uninterpreted, to certain Ndis Xxx functions.

Usually, the DriverEntry function of such an NDIS intermediate driver calls the following Ndis Xxx functions in the following order:

  1. NdisMInitializeWrapper to notify the NDIS library that the driver is about to register as a miniport

  2. NdisIMRegisterLayeredMiniport to register the driver’s MiniportXxx functions

  3. NdisRegisterProtocol to register the driver’s ProtocolXxx functions

    Such an intermediate driver must register ProtocolBindAdapter and ProtocolUnbindAdapter functions with the NDIS library. Its ProtocolBindAdapter function will be called later to complete driver initialization and to bind the intermediate driver to an underlying NIC miniport.

In effect, the DriverEntry function of such an NDIS intermediate driver can ignore theRegistryPath pointer after passing it to NdisMInitializeWrapper. Such a driver also can ignore the DriverObject pointer after passing it to NdisIMRegisterLayeredMiniport. However, the driver should save the DriverHandle returned by NdisIMRegisterLayeredMiniport and the NdisProtocolHandle returned by NdisRegisterProtocol for subsequent calls to Ndis Xxx functions. Such an NDIS intermediate driver binds itself to each underlying NIC driver before its MiniportInitialize function is called to initialize the intermediate driver's virtual NIC, and still higher level protocols subsequently bind themselves to the virtual NIC it creates.

If an NDIS intermediate driver calls NdisMRegisterMiniport from its DriverEntry function, the driver must set up sufficient static binding and configuration information in the registry to set up one or more virtual NICs corresponding to each underlying NIC driver to which the NDIS intermediate driver will bind itself. The MiniportInitialize function of such an intermediate driver will be called in the context of its call to NdisMRegisterMiniport, so such a driver cannot query the underlying NIC driver(s) when its MiniportInitialize function runs.

Any NDIS intermediate driver that registers ProtocolBindAdapter and ProtocolUnbindAdapter functions makes itself Plug-and-Play-ready, as well as deferring the creation of its virtual NIC(s) until the driver has bound itself to the underlying NIC driver(s). This strategy allows the intermediate driver to allocate resources at the creation of the virtual NIC according to the features of the underlying NIC driver to which it is bound.

A driver that exports ProtocolXxx but not MiniportXxx functions must call NdisRegisterProtocol to register its ProtocolXxx functions with the NDIS library. Such a driver can call certain other Ndis Xxx configuration and initialization functions from DriverEntry if it has no ProtocolBindAdapter function, such as NdisOpenProtocolConfiguration and NdisReadConfiguration to retrieve network configuration information, set up by the driver’s installation script, from the registry. For more information about driver installation scripts, see the Programmer’s Guide.

A protocol driver that exports a set of standard kernel-mode driver routines in addition to its NDIS-defined ProtocolXxx functions must set the entry points for those driver routines in the given driver object. For more information about the functionality of such a protocol’s DriverEntry function, see the Kernel-Mode Driver Design Guide. For more information about the TdiDispatch Xxx routines exported by TDI transport protocols, see Part 2.

If an attempt to allocate resources that the driver needs to carry out network I/O operations fails, DriverEntry should release all resources it already allocated before it returns control with a status other than NTSTATUS_SUCCESS or NDIS_STATUS_SUCCESS.

By default, DriverEntry runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

NdisIMRegisterLayeredMiniport, NdisMInitializeWrapper, NdisRegisterProtocol, NdisOpenProtocolConfiguration, NdisReadConfiguration, ProtocolBindAdapter, ProtocolUnbindAdapter


ProtocolBindAdapter

VOID
ProtocolBindAdapter(
OUT PNDIS_STATUS Status,
 IN NDIS_HANDLE BindContext,
 IN PNDIS_STRING DeviceName,
 IN PVOID SystemSpecific1,
 IN PVOID SystemSpecific2
);

ProtocolBindAdapter is usually implemented in an NDIS intermediate driver, both to make the driver Plug-and-Play-ready and to defer setting up its virtual NIC(s) until the underlying NIC driver(s) have initialized. Currently, it is an optional function in other NDIS protocols so that legacy protocols remain viable.

Parameters

Status
Points to a variable in which ProtocolBindAdapter returns the status of its operation(s), as one of the following:
NDIS_STATUS_SUCCESS
The driver completed initialization successfully if it was deferred from DriverEntry. The driver bound itself to the given NIC driver specified at DeviceName.
NDIS_STATUS_PENDING
The protocol will complete the bind operation asynchronously with a call to NdisCompleteBindAdapter when it is ready to accept receives from the underlying driver and to send transmit, query, and set requests down to the underlying driver.
NDIS_STATUS_XXX or NTSTATUS_XXX
The protocol’s attempt to set up a binding failed or the protocol could not allocate the resources it needed to carry out network I/O operations. Usually, such an error status is propagated from an Ndis Xxx function or a kernel-mode support routine.
BindContext
Specifies a handle, supplied by NDIS, that the protocol passes subsequently to NdisCompleteBindAdapter.
DeviceName
Points to a counted, zero-terminated Unicode string naming an underlying NIC driver or virtual NIC driver to which ProtocolBindAdapter should bind.
SystemSpecific1
Specifies a registry path pointer that is a required parameter to NdisOpenProtocolConfiguration.
SystemSpecific2
Reserved for system use.

Comments

ProtocolBindAdapter performs dynamic binding operations whenever an underlying NIC to which the protocol can bind itself becomes available. In addition, ProtocolBindAdapter continues driver initialization operations deferred from the DriverEntry function. ProtocolBindAdapter allocates sufficient memory to maintain per-binding runtime state and calls NdisOpenAdapter with the given BindContext and DeviceName to bind itself to the underlying driver.

If NdisOpenAdapter returns NDIS_STATUS_PENDING, the driver’s ProtocolOpenAdapterComplete function will be called subsequently when the binding operation has completed. ProtocolBindAdapter should store the input BindContext handle in the area it allocated for per-binding state; the ProtocolBindContext handle it supplied to NdisOpenAdapter is an input parameter to the driver’s ProtocolOpenAdapterComplete function, which must pass the BindContext handle to NdisCompleteBindAdapter subsequently. The underlying NIC driver returns NDIS_STATUS_ADAPTER_NOT_READY for any requests it receives while the open operation is pending. Consequently, ProtocolBindAdapter cannot call NdisRequest to query the underlying driver if NdisOpenAdapter returns NDIS_STATUS_PENDING.

In these circumstances, ProtocolBindAdapter simply sets Status to NDIS_STATUS_PENDING and returns control, thereby deferring whatever actions the protocol takes to set up binding-specific state and to allocate binding-specific resources to ProtocolOpenAdapterComplete if this function is called with an input Status of NDIS_STATUS_SUCCESS.

Similarly, if NdisOpenAdapter returns an error status, ProtocolBindAdapter sets Status to the returned value, releases any per-binding resources the driver has allocated, and returns control immediately. Otherwise, a successful binding has been established and the protocol can receive indications from the underlying driver to its ProtocolStatus, ProtocolReceivePacket, and/or ProtocolReceive functions.

Consequently, when NdisOpenAdapter returns NDIS_STATUS_SUCCESS, ProtocolBindAdapter allocates the resources the driver needs to carry out network I/O on the binding and sets up whatever binding-specific runtime state the protocol uses to track network I/O operations. If the driver’s installation script installed adapter-specific configuration information in the protocol section of the registry, ProtocolBindAdapter calls NdisOpenProtocolConfiguration and NdisReadConfiguration to retrieve this information. For more information about driver installation scripts, see the Programmer’s Guide.

ProtocolBindAdapter also can call NdisRequest to query the underlying driver (or NDIS) about the underlying driver’s NIC-specific limits, such as its maximum frame size, transmit/receive buffer space, and so forth, to set up appropriate state for the binding.

A protocol can determine whether the underlying driver indicates full-packet receives by comparing the values that driver returns for the OID_GEN_CURRENT_LOOKAHEAD and OID_GEN_RECEIVE_BLOCK_SIZE queries it passes to NdisRequest. If they are identical, the protocol will make no calls to NdisTransferData on this binding. A protocol can determine whether the underlying driver supports multipacket sends with the OID_GEN_MAXIMUM_SEND_PACKETS query. However, even if the underlying driver returns one, indicating that it does not have a MiniportSendPackets function, the protocol can still call NdisSendPackets on the binding, as long as the protocol does not supply out-of-band information with the packet descriptors it allocates for sends. NDIS handles the submission of packet arrays to such an underlying driver transparently to bound protocols. The protocol is responsible for ensuring that the underlying driver is capable of handling protocol-supplied out-of-band data, if any.

For more information about general and medium-specific OID requests, see Chapter 5.

A driver’s ProtocolReceivePacket or ProtocolReceive function can be called as soon as the protocol sets up a packet filter with OID_GEN_CURRENT_PACKET_FILTER for the binding. For the NULL filter, receive indications are enabled on return from a successfull call to NdisOpenAdapter.

Every NDIS protocol driver should allocate sufficient packet pool and buffer pool from which to allocate packet descriptors and buffer descriptors for subsequent network transmits and, possibly, for subsequent transfer-data requests, depending on whether the underlying driver indicates full-packet receives with NdisMIndicateReceivePacket.

The ProtocolBindAdapter function of an NDIS intermediate driver is responsible for prompting the initialization of the driver’s virtual NIC by calling NdisIMInitializeDeviceInstance, which, in turn, calls the driver’s MiniportInitialize function. Such a driver must initialize its virtual NIC after ProtocolBindAdapter (or ProtocolBindAdapterComplete) establishes a binding to the underlying driver specified at DeviceName. Still higher level protocols cannot bind to such an intermediate driver until its virtual NIC has been initialized.

If ProtocolBindAdapter cannot allocate the resources it needs to carry out subsequent network I/O operations, it should free all resources it has already allocated, set Status to an appropriate error value, and return control.

If the driver is ready to carry out network I/O on the established binding, ProtocolBindAdapter calls NdisCompleteBindAdapter with NDIS_STATUS_SUCCESS for the Status and OpenStatus arguments.

By default, ProtocolBindAdapter runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

DriverEntry of NDIS Protocol Drivers, MiniportInitialize, MiniportSendPackets, NdisAllocateBufferPool, NdisAllocateMemory, NdisAllocatePacketPool, NdisCompleteBindAdapter, NdisIMInitializeDeviceInstance, NdisInitUnicodeString, NdisMIndicateReceivePacket, NdisOpenAdapter, NdisOpenProtocolConfiguration, NDIS_PACKET_OOB_DATA, NdisReadConfiguration, NdisRequest, NDIS_REQUEST, ProtocolOpenAdapterComplete, ProtocolReceive, ProtocolReceivePacket, ProtocolStatus, ProtocolUnbindAdapter


ProtocolCloseAdapterComplete

VOID
ProtocolCloseAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
);

ProtocolCloseAdapterComplete is a required driver function that completes processing for an unbinding operation for which NdisCloseAdapter returned NDIS_STATUS_PENDING.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
Status
Indicates the final status of the close operation in the underlying driver.

Comments

NDIS delays calling ProtocolCloseAdapterComplete until every request pending on the binding has completed. When ProtocolCloseAdapterComplete is called, the NdisBindingHandle originally returned by NdisOpenAdapter is no longer valid. ProtocolCloseAdapterComplete cannot call any Ndis Xxx function that requires this handle as a parameter.

ProtocolCloseAdapterComplete releases all resources the driver allocated for per-binding network I/O operations before it returns control.

If the driver has a ProtocolUnbindAdapter function, ProtocolCloseAdapterComplete must call NdisCompleteUnbindAdapter with the BindHandle saved in the ProtocolBindingContext area by ProtocolUnbindAdapter. Consequently, ProtocolCloseAdapterComplete should not release the driver-allocated context area until it has called NdisCompleteUnbindAdapter.

By default, ProtocolCloseAdapterComplete runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

NdisCloseAdapter, NdisCompleteUnbindAdapter, NdisFreeBufferPool, NdisFreeMemory, NdisFreePacketPool, NdisOpenAdapter, ProtocolUnbindAdapter


ProtocolOpenAdapterComplete

VOID
ProtocolOpenAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
 IN NDIS_STATUS Status,
 IN NDIS_STATUS OpenErrorStatus
);

ProtocolOpenAdapterComplete is a required driver function that completes processing of a binding operation for which NdisOpenAdapter returned NDIS_STATUS_PENDING.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
Status
Specifies the final status of the NIC driver open operation, either NDIS_STATUS_SUCCESS if the binding was established or an error status determined by the underlying driver.

The underlying NIC driver returns NDIS_STATUS_ADAPTER_NOT_READY for any requests it receives while the open operation is pending. After ProtocolOpenAdapterComplete returns control, the underlying driver cannot return this status.

OpenErrorStatus
Specifies additional information about the reason for a failure if the value at Status is not NDIS_STATUS_SUCCESS and if the underlying driver supplied this information. For example, the driver of a Token Ring NIC might return a ring error that NDIS forwards to ProtocolOpenAdapterComplete. This parameter can be NULL.

Comments

When NdisOpenAdapter returns NDIS_STATUS_PENDING to the DriverEntry or ProtocolBindAdapter function of an NDIS protocol driver, NDIS calls that driver’s ProtocolOpenAdapterComplete function when the asynchronous binding operation is complete.

If the input Status is NDIS_STATUS_SUCCESS, ProtocolOpenAdapterComplete carries out whatever driver-determined per-binding operations its DriverEntry or ProtocolBindAdapter function would have done in a synchronous binding operation.

If the driver has a ProtocolBindAdapter function, ProtocolOpenAdapterComplete calls NdisCompleteBindAdapter with NDIS_STATUS_SUCCESS for the Status and OpenStatus arguments and with the BindContext handle stored at ProtocolBindingContext by the ProtocolBindAdapter function. Then, ProtocolOpenAdapterComplete returns control.

Otherwise, ProtocolOpenAdapterComplete cleans up any per-binding state already set up by DriverEntry or ProtocolBindAdapter. After releasing any state set up by ProtocolBindAdapter except for the BindContext handle, it calls NdisCompleteBindAdapter with an appropriate error for Status and the handle before it returns control.

By default, ProtocolOpenAdapterComplete runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

DriverEntry of NDIS Protocol Drivers, NdisCompleteBindAdapter, NdisFreeMemory, NdisOpenAdapter, ProtocolBindAdapter


ProtocolReceive

NDIS_STATUS
ProtocolReceive(
IN NDIS_HANDLE ProtocolBindingContext,
 IN NDIS_HANDLE MacReceiveContext,
 IN PVOID HeaderBuffer,
 IN UINT HeaderBufferSize,
 IN PVOID LookAheadBuffer,
 IN UINT LookaheadBufferSize,
 IN UINT PacketSize
);

ProtocolReceive is a required driver function that determines whether a received network packet is of interest to the protocol’s client(s) and, if so, copies the indicated data and, possibly, calls NdisTransferData to retrieve the rest of the indicated packet.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
MacReceiveContext
Specifies a context handle that the underlying NIC driver associates with the packet received from the network. This handle is opaque to the protocol, reserved for use by the underlying driver that made the indication, and a required parameter to NdisTransferData.
HeaderBuffer
Points to the base virtual address of a range containing the buffered packet header. The address is valid only within the current call to ProtocolReceive.
HeaderBufferSize
Specifies the number of bytes in the packet header.
LookAheadBuffer
Points to the base virtual address of a range that contains LookaheadBufferSize bytes of buffered network packet data. This address is valid only within the current call to ProtocolReceive.
LookaheadBufferSize
Specifies the number of bytes of network packet data in the lookahead buffer.

The indicating driver ensures this number is at least as large as the size it returned for the protocol’s preceding call to NdisRequest with OID_GEN_CURRENT_LOOKAHEAD or the size of the packet, whichever is less.

If PacketSize is less than or equal to the given LookaheadBufferSize, the lookahead buffer contains the entire packet. If the underlying driver made the indication with NdisMIndicateReceivePacket, the lookahead buffer always contains a full network packet.

PacketSize
Specifies the size, in bytes, of the network packet data. The length of the packet does not include the length of the header.

ProtocolReceive determines whether the protocol must call NdisTransferData by comparing this parameter to the given LookaheadBufferSize.

Return Value

ProtocolReceive can return either of the following:

NDIS_STATUS_NOT_ACCEPTED
The protocol has no use for the indicated packet, that is, it has no current clients interested in the indicated network data.

Returning this status quickly for rejected packets yields higher performance for the protocol and the highest possible network I/O throughput for the system as a whole.

NDIS_STATUS_SUCCESS
ProtocolReceive has processed the header information and accepted the packet, that is, it has copied the indicated network data from the header and lookahead buffers and, possibly, called NdisTransferData to retrieve the remaining data if less than a full network packet was indicated.

Comments

NDIS provides equal packet access for any number of protocol drivers bound above an underlying NIC miniport. NDIS can use the status that ProtocolReceive returns to optimize the order in which bound protocols receive indications for subsequent packets.

When a miniport calls a filter-specific NdisM..IndicateReceive function, NDIS calls the ProtocolReceive functions of bound protocols. A call to an NdisM..IndicateReceive function indicates that a network packet, or some initial lookahead portion of it, is available for inspection by bound protocols. Each ProtocolReceive function, in turn, inspects the packet header and data, optionally copies as much of the header and/or data as is made available, and calls NdisTransferData to instruct the NIC driver to copy any remaining data into a protocol-supplied buffer chained to a protocol-allocated packet descriptor if the protocol accepts the packet. ProtocolReceive can call NdisTransferData only once per receive indication.

Before a miniport calls NdisMIndicateReceivePacket with a packet array, it can set the Status member of an out-of-band block associated with a packet descriptor in the array to NDIS_STATUS_RESOURCES. This also causes NDIS to call the ProtocolReceive functions of bound protocols with the network packet header and data specified by that packet descriptor and, separately, with the received network packets specified in all subsequent descriptors in the indicated packet array. Each ProtocolReceive function, in turn, inspects the given network packet header and data and each optionally copies the indicated network packet data.

If the underlying miniport supplies out-of-band information with the receives it indicates, ProtocolReceive can call NdisQueryReceiveInformation to retrieve the out-of-band information for each such packet. Because such an underlying miniport always indicates full-packet receives, ProtocolReceive will never call NdisTransferData for a packet indicated with NdisMIndicateReceivePacket.

NDIS calls ProtocolReceive functions sequentially as calls to NdisM..IndicateReceive or NdisMIndicateReceivePacket occur, but it can indicate a packet to bound protocols using separate calls. However, every protocol must be able to handle out-of-order received packets since neither NDIS nor the underlying NIC miniport has any control over the network over which packets are transmitted.

A driver writer should minimize the amount of time spent in the ProtocolReceive function so that the protocol does not lose additional incoming packets, particularly from underlying drivers that call the filter-specific NdisM..IndicateReceive functions. ProtocolReceive must either reject an incoming packet or recognize the packet as of interest to its client(s) quickly. If it accepts the packet, ProtocolReceive must find a place to put packet data and copy the data quickly. Shortly after ProtocolReceive returns, when time constraints are not so severe, any underlying miniport that made one or more indications with NdisM..IndicateReceive will call the corresponding NdisM..IndicateReceiveComplete. The protocol driver’s ProtocolReceiveComplete function then performs any necessary postprocessing for the original indication(s).

The underlying driver does not remove any headers or trailers from the packet data received on its NIC, nor does it remove any received padding. In other words, a packet can include padding in the data and length indicated to protocols. ProtocolReceive is responsible for detecting and ignoring such padding.

The buffers at HeaderBuffer and LookAheadBuffer passed to ProtocolReceive are read-only and valid only until ProtocolReceive returns control. ProtocolReceive must copy all the indicated data that the driver needs into protocol-allocated storage. Whether the protocol can copy the indication directly depends on the NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA flag returned for the OID_GEN_MAC_OPTIONS query. If this flag was set, ProtocolReceive can use NdisMoveMemory to copy the indicated data to its own storage; otherwise, it should use TdiCopyLookaheadData. However, the protocol can call TdiCopyLookaheadData even if the underlying driver set the NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA flag.

The ProtocolReceive function of an NDIS intermediate driver cannot simply forward receive indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must repackage the indication in a fresh packet descriptor and call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from a protocol-supplied MiniportCallback function.

By default, ProtocolReceive runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportTransferData, NdisAllocatePacket, NdisGetFirstBufferFromPacket, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMArcIndicateReceive, NdisMEthIndicateReceive, NdisMFddiIndicateReceive, NdisMIndicateReceivePacket, NdisMoveMemory, NdisMTrIndicateReceive, NdisMWanIndicateReceive, NdisQueryReceiveInformation, ProtocolReceiveComplete, ProtocolReceivePacket, ProtocolTransferDataComplete, TdiCopyLookaheadData


ProtocolReceiveComplete

VOID
ProtocolReceiveComplete(
IN NDIS_HANDLE ProtocolBindingContext
);

ProtocolReceiveComplete is a required driver function in any protocol that might bind itself to an underlying NIC driver that does not make receive indications with NdisMIndicateReceivePacket. ProtocolReceiveComplete completes postprocessing of one or more preceding receive indications.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.

Comments

ProtocolReceiveComplete performs postprocessing, such as notifying interested clients of processed data received from a remote node, after one or more calls to the driver’s ProtocolReceive function.

When an underlying miniport calls NdisM..IndicateReceiveComplete, NDIS calls the ProtocolReceiveComplete function(s) of bound protocol(s). NDIS always calls ProtocolReceiveComplete after one or more calls to the driver’s ProtocolReceive function, regardless of whether any particular packet(s) are accepted by bound protocols.

An underlying miniport need not make calls to NdisM..IndicateReceive in one-to-one correspondence with its calls to NdisM..IndicateReceiveComplete. ProtocolReceiveComplete is re-entrant because the underlying NIC driver sometimes must wait to call NdisM..IndicateReceiveComplete until no receive operations are in progress.

After ProtocolReceiveComplete is called, the underlying NIC driver can make another receive indication before ProtocolReceiveComplete returns control, so ProtocolReceiveComplete can execute concurrently with the driver’s ProtocolReceive function in SMP machines.

The ProtocolReceiveComplete function of an NDIS intermediate driver cannot simply forward completion indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from a protocol-supplied MiniportCallback function.

By default, ProtocolReceiveComplete runs at IRQL PASSIVE_LEVEL in an arbitrary thread context.

See Also

NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMArcIndicateReceiveComplete, NdisMEthIndicateReceiveComplete, NdisMFddiIndicateReceiveComplete, NdisMIndicateReceivePacket, NdisMTrIndicateReceiveComplete, NdisMWanIndicateReceiveComplete, ProtocolReceive, ProtocolReceivePacket


ProtocolReceivePacket

INT
ProtocolReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet
);

ProtocolReceivePacket is an optional driver function that processes receive indications made by underlying driver(s) that call NdisMIndicateReceivePacket either with packet arrays because the underlying driver supports multipacket receive indications or with individual packets that have associated out-of-band information.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
Packet
Specifies a pointer to a packet descriptor for the received net packet. NDIS extracted this pointer from an array of packet descriptor pointers that an underlying driver passed to NdisMIndicateReceivePacket.

Comments

Any protocol driver that might bind itself to an underlying NIC driver that supports multipacket receive indications should have a ProtocolReceivePacket function. Such an underlying driver always indicates full-packet receives.

A protocol can achieve better performance if it has a ProtocolReceivePacket function. Such a protocol (or the TDI clients of a highest level protocol) can hold on to all received data, to which it is given read-only access, specified at Packet if ProtocolReceivePacket returns a nonzero value. For a highest level protocol driver, the return value for such an input Packet descriptor pointer can be a reference count of clients the protocol has notified about the received net packet by forwarding the input Packet descriptor. For an intermediate NDIS driver, the return value can be a count of higher level protocols to which it has made a corresponding indication or one if the driver tracks the number of higher level protocols to which it forwards the processed indication. For a highest level protocol that forwards the input Packet descriptor pointer to some number of interested clients, each client retains read-only access to the indicated data until the client calls TdiReturnChainedReceives. An intermediate NDIS driver retains ownership of the indicated packet until it calls NdisReturnPackets with Packet as many times as the reference count returned by ProtocolReceivePacket.

ProtocolReceivePacket cannot call NdisReturnPackets. The protocol must pass the input Packet pointer to NdisReturnPackets after ProtocolReceivePacket returns control. Consequently, ProtocolReceivePacket should save the input Packet pointer in the ProtocolBindingContext area when it will return a nonzero value. The NDIS library maintains the reference count for such an indicated packet, and the protocol retains ownership of the packet until it has called NdisReturnPackets with that packet as many times as the value returned by its ProtocolReceivePacket function.

ProtocolReceivePacket returns zero to relinquish ownership of the given Packet if the protocol has no current clients or bound higher level protocols interested in the indicated network packet or if the ProtocolReceivePacket function copies the data from the given packet and associated out-of-band block, if any, to protocol-allocated storage itself. When ProtocolReceivePacket returns zero, NDIS either calls another bound protocol with the receive indication or returns the given packet descriptor at Packet to the miniport that made the indication.

Any protocol that binds itself to an underlying driver that supplies out-of-band information for receives must have a ProtocolReceivePacket function to interpret the miniport-supplied out-of-band information when processing receive indications. Such a protocol can retrieve the associated out-of-band information for the given Packet with the appropriate NDIS_GET_PACKET_XXX macro(s).

The ProtocolReceivePacket function of an NDIS intermediate driver cannot simply forward received indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must repackage the indication in a fresh packet descriptor and call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from a protocol-supplied MiniportCallback function.

By default, ProtocolReceivePacket runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

NdisAllocatePacket, NDIS_GET_PACKET_HEADER_SIZE, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_TIME_RECEIVED, NDIS_GET_PACKET_TIME_SENT, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMIndicateReceivePacket, NDIS_PACKET, NDIS_PACKET_OOB_DATA, NdisReturnPackets, ProtocolReceive, TdiReturnChainedReceives


ProtocolRequestComplete

VOID
ProtocolRequestComplete(
IN NDIS_HANDLE ProtocolBindingContext,
 IN PNDIS_REQUEST NdisRequest,
 IN NDIS_STATUS Status
);

ProtocolRequestComplete is a required driver function that completes the processing of a protocol-initiated query or set for which NdisRequest returned NDIS_STATUS_PENDING.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
NdisRequest
Points to the protocol-supplied structure previously passed to NdisRequest.
Status
Specifies the final status of the request set by the underlying driver or by NDIS. This argument determines what ProtocolRequestComplete does with the information at NdisRequest.

Comments

ProtocolRequestComplete uses the input Status as follows:

  • If this argument is NDIS_STATUS_SUCCESS, the BytesRead or BytesWritten member of the NDIS_REQUEST structure has been set by NDIS or the underlying driver to specify how much protocol-supplied information was transferred from the buffer at InformationBuffer to the NIC in a set operation or how much information was returned at InformationBuffer in response to a query operation.

    If the protocol made a query, ProtocolRequestComplete can use the data returned at InformationBuffer in any protocol-determined way, depending on the value of the Oid member.

    For example, if the protocol originally initiated an OID_GEN_MAXIMUM_SEND_PACKETS query, ProtocolRequestComplete might set up state variables in the ProtocolBindingContext area to throttle the number of outstanding sends the driver will set up for subsequent calls to NdisSendPackets.

  • If this argument is NDIS_STATUS_INVALID_LENGTH or NDIS_STATUS_BUFFER_TOO_SHORT, the BytesNeeded member specifies the Oid-specific value of the InformationBufferLength member required to carry out the requested operation.

    In these circumstances, ProtocolRequestComplete can allocate sufficient buffer space for the request, set up another NDIS_REQUEST structure with the required InformationBufferLength and same Oid, and retry the call to NdisRequest.

    ProtocolRequestComplete can retry requests for certain other NDIS_STATUS_XXX arguments, as well, as described in the reference for NdisRequest.

  • If this argument is an NDIS_STATUS_XXX that is an unrecoverable error, ProtocolRequestComplete should release the memory allocated for the NDIS_REQUEST structure and determine whether the driver should call NdisReset, close the binding, or adjust its binding-specific state information to handle continued network I/O operations on the binding.

For global queries and sets, the underlying miniport’s call to NdisMQueryInformationComplete or NdisMSetInformationComplete causes NDIS to call the ProtocolRequestComplete function. NDIS forwards the miniport-determined Status passed to NdisM..InformationComplete as the input Status to ProtocolRequestComplete.

For binding-specific queries, NDIS calls the ProtocolRequestComplete function itself. Because the NDIS library maintains bindings for all miniports, NDIS can return binding-specific information only about underlying drivers that report their medium-type as one for which the system provides a filter library. NDIS returns NDIS_STATUS_NOT_SUPPORTED for protocol-initiated binding-specific queries to other miniports.

For more information about system-defined OIDs, see Chapter 5.

ProtocolRequestComplete can be called before the protocol has had time to inspect the status code that NdisRequest returns at Status.

The ProtocolRequestComplete function of an NDIS intermediate driver cannot simply forward completion indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from a protocol-supplied MiniportCallback function.

By default, ProtocolRequestComplete runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportQueryInformation, MiniportSetInformation, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMQueryInformationComplete, NdisMSetInformationComplete, NDIS_REQUEST, NdisRequest, NdisReset


ProtocolResetComplete

VOID
ProtocolResetComplete(
IN NDIS_HANDLE ProtocolBindingContext,
 IN NDIS_STATUS Status
);

ProtocolResetComplete is a required driver function that completes a protocol-initiated reset operation for which NdisReset returned NDIS_STATUS_PENDING.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
Status
Specifies the final status of the protocol-initiated reset operation.

Comments

ProtocolResetComplete completes processing for an asynchronous reset of the underlying driver’s NIC or virtual NIC.

The underlying miniport’s call to NdisMResetComplete causes NDIS to call the ProtocolResetComplete function. NDIS forwards the miniport-determined Status passed to NdisMResetComplete as the input Status to ProtocolStatusComplete.

ProtocolResetComplete can be called before the protocol has had time to inspect the status code that NdisReset returns at Status.

The ProtocolResetComplete function of an NDIS intermediate driver cannot simply forward completion indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from a protocol-supplied MiniportCallback function.

By default ProtocolResetComplete runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportReset, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMResetComplete, NdisReset, ProtocolStatus, ProtocolStatusComplete


ProtocolSendComplete

VOID
ProtocolSendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
 IN PNDIS_PACKET Packet,
 IN NDIS_STATUS Status
);

ProtocolSendComplete is a required driver function that completes the processing of a protocol-initiated send previously passed to NdisSendPackets or NdisSend, which returned NDIS_STATUS_PENDING.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
Packet
Points to the protocol-supplied packet descriptor for the completed send.
Status
Specifies the final status of the send operation.

Comments

ProtocolSendComplete performs whatever postprocessing is necessary for a completed transmit operation, such as notifying the client that originally requested the protocol to send data over the network.

Completion of a send operation usually implies that the underlying NIC driver actually has transmitted the given packet over the network. However, the driver of a so-called intelligent NIC can consider a send complete as soon as it downloads the net packet to its NIC. The underlying driver’s call to NdisMSendComplete or NdisMWanSendComplete causes NDIS to call the ProtocolSendComplete function.

When ProtocolSendComplete is called, the driver regains ownership of the following protocol-allocated resources:

  • The packet descriptor at Packet

  • All buffer descriptors chained to the packet descriptor that map buffers containing the net packet data and any protocol-allocated buffers mapped by these descriptors

  • Any out-of-band block associated with the packet descriptor

  • Any protocol-allocated buffer specified in the out-of-band block at MediaSpecificInformation

Consequently, ProtocolSendComplete can either release these resources or prepare them for reuse in a subsequent call to NdisSendPackets or NdisSend. As a general rule, reusing such resources yields better performance than releasing them except, possibly, in periods of low network traffic if the protocol previously allocated a surplus of these resources to handle a period of heavy I/O demand.

To prepare the buffer and packet descriptors for reuse, ProtocolSendComplete should follow these guidelines:

  • Always call an NdisUnchainBufferAt Xxx function as many times as necessary to save the buffer descriptor pointers before ProtocolSendComplete calls NdisReinitializePacket with the descriptor at Packet.

    Otherwise, NdisReinitializePacket sets the head of the buffer chain to NULL so the protocol cannot recover pointers to the buffer descriptors chained to the packet descriptor. Either the protocol loses MDL(s) mapping client-supplied buffer(s) or it loses a set of buffer descriptors the protocol allocated with NdisAllocateBuffer.

  • Always pass the pointer returned by NDIS_OOB_DATA_FROM_PACKET to NdisZeroMemory to clear an associated out-of-band data block, never the Packet pointer.

    Otherwise, NdisZeroMemory destroys the packet descriptor the protocol allocated with NdisAllocatePacket, rendering it unusable for specifying subsequent sends.

    As an alternative to clearing the out-of-band block, the protocol can reinitialize only those members that the protocol normally sets up for sends with the appropriate NDIS_SET_PACKET_XXX macros.

Until ProtocolSendComplete is called, the current status of a protocol-initiated send is volatile. A protocol temporarily releases ownership of all resources it allocated for a send when it calls NdisSendPackets or NdisSend, even if the protocol supplies out-of-band information with the packet descriptors it allocates for sends. In particular, a protocol should never attempt to examine the Status member of the associated out-of-band data block when NdisSendPackets or NdisSend returns control.

Although NDIS always submits protocol-supplied packet arrays to the underlying miniport in the protocol-determined order passed in calls to NdisSendPackets, the underlying driver can complete the given packets in random order. That is, every bound protocol can rely on NDIS to submit the packets the protocol passes to NdisSendPackets or NdisSend in FIFO order to the underlying driver, but no protocol can rely on that underlying driver to call NdisMSendComplete with those packets in the same order.

The ProtocolSendComplete function of an NDIS intermediate driver cannot simply forward completion indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from the protocol-supplied MiniportCallback function.

By default, ProtocolSendComplete runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportSend, MiniportSendPackets, MiniportWanSend, NdisAllocateBuffer, NdisAllocatePacket, NdisFreeBuffer, NdisFreePacket, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMSendComplete, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET_OOB_DATA, NdisReinitializePacket, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_TIME_TO_SEND, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, NdisZeroMemory


ProtocolStatus

VOID
ProtocolStatus(
IN NDIS_HANDLE ProtocolBindingContext,
 IN NDIS_STATUS GeneralStatus,
 IN PVOID StatusBuffer,
 IN UINT StatusBufferSize
);

ProtocolStatus is a required driver function that handles status-change notifications raised by an underlying driver or by NDIS.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
GeneralStatus
Indicates the general status code, either raised by NDIS or propagated from the underlying driver’s call to NdisMIndicateStatus.
StatusBuffer
Points to a buffer containing medium-specific data that depends on the value of GeneralStatus.

For example, if the GeneralStatus argument is NDIS_STATUS_RING_STATUS, this parameter points to a ULONG-sized bitmask and StatusBufferSize is four. As another example, if GeneralStatus is NDIS_STATUS_WAN_LINE_UP, this parameter points to an NDIS_WAN_LINE_UP structure and StatusBufferSize is sizeof(NDIS_STATUS_WAN_LINE_UP).

For some NDIS_STATUS_XXX values, this pointer is NULL and StatusBufferSize is set to zero.

StatusBufferSize
Specifies the number of bytes at StatusBuffer.

Comments

A call to ProtocolStatus notifies the protocol about changes in status of the underlying NIC or its driver.

ProtocolStatus interprets two basic pieces of information:

  1. A general status code, defined by NDIS as an NDIS_STATUS_XXX value, indicating a change on the NIC or in the underlying driver

    If NDIS has called the underlying driver’s MiniportReset function in an attempt to restore the NIC to an operational state, NDIS can substitute NDIS_STATUS_RESET_START for the GeneralStatus indicated in the miniport’s call to NdisMIndicateStatus.

  2. Depending on the status at GeneralStatus, additional information about the specific reason for the change in status, which can be medium-specific

    For example, ring-insert failures are specific to Token Ring (802.5) networks and do not apply for Ethernet (802.3) networks.

NDIS always notifies all bound protocols when an underlying driver is resetting its NIC by calling their ProtocolStatus functions, first with NDIS_STATUS_RESET_START and later, when the reset operation is done, with NDIS_STATUS_RESET_END. After each such call to a ProtocolStatus function, NDIS also calls the driver’s ProtocolStatusComplete function if NDIS or the underlying miniport initiated the reset.

Since NDIS will not accept protocol-initiated sends and requests to a miniport while a reset is in progress, the NDIS_STATUS_RESET_START notification warns bound protocols to hold off on their calls to NdisSendPackets, NdisSend, and NdisRequest on the binding designated by the respective ProtocolBindingContext given to their ProtocolStatus functions until they receive the corresponding NDIS_STATUS_RESET_END notification.

If the protocol itself initiated the reset operation with a call to NdisReset, its ProtocolStatus function receives the NDIS_STATUS_RESET_START and NDIS_STATUS_RESET_END notifications like any other protocol bound to the same underlying driver. However, if NdisReset returns NDIS_STATUS_PENDING, only the ProtocolStatusComplete function of the driver that initiated the reset operation is called.

Consequently, every protocol’s ProtocolStatus function should take whatever action is required to prevent that protocol from submitting sends and requests as soon as ProtocolStatus receives the NDIS_STATUS_RESET_START notification and should re-enable subsequent sends and requests when ProtocolStatus receives the NDIS_STATUS_RESET_END notification. Usually, ProtocolStatus simply sets and clears a ResetInProgress flag in the ProtocolBindingContext area that other driver functions check before they initiate sends or requests on the binding.

As another example, the ProtocolStatus function of any protocol bound above NDISWAN receives a line-up notification whenever the underlying WAN NIC miniport is establishing a new link. After such a protocol has traded link-specific context handles with NDISWAN, its ProtocolStatus function can receive additional link-specific line-up, line-down, and fragment indications, which the protocol can use to optimize throughput and/or performance on the link.

The ProtocolStatus function of an NDIS intermediate driver cannot simply forward status indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from the protocol-supplied MiniportCallback function.

When the underlying NIC driver signals a status change, it does not change associated event counters and thresholds.

By default, ProtocolStatus runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportReset, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMIndicateStatus, NdisRequest, NdisReset, NdisSend, NdisSendPackets, NDIS_WAN_LINE_UP, ProtocolStatusComplete


ProtocolStatusComplete

VOID
ProtocolStatusComplete(
IN NDIS_HANDLE ProtocolBindingContext
);

ProtocolStatusComplete is a required driver function that completes a status-change operation initiated when the underlying driver called NdisMIndicateStatus.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.

Comments

ProtocolStatusComplete performs whatever postprocessing is necessary after one or more calls to the ProtocolStatus function. A call to ProtocolStatusComplete indicates that the protocol can resume normal operations on the binding after taking into account the preceding status change(s) indicated to its ProtocolStatus function. For example, if the protocol previously called NdisReset and it returned NDIS_STATUS_PENDING, both ProtocolResetComplete and ProtocolStatusComplete are called when the protocol-initiated reset operation is begun and completed.

The underlying miniport’s call to NdisMIndicateStatusComplete causes NDIS to call the ProtocolStatusComplete function. Interrupts are enabled on the underlying NIC when this call occurs. Consequently, the ProtocolReceivePacket or ProtocolReceive functions can be called to process receive indications while ProtocolStatusComplete is running in SMP machines. In some circumstances, the underlying driver can call NdisMIndicateStatus, thereby causing a call to the ProtocolStatus function while ProtocolStatusComplete is running.

The ProtocolStatusComplete function of an NDIS intermediate driver cannot simply forward completion indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from the protocol-supplied MiniportCallback function.

By default, ProtocolStatusComplete runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportReset, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMIndicateStatusComplete, NdisReset, ProtocolReceive, ProtocolReceivePacket, ProtocolStatus


ProtocolTransferDataComplete

VOID
ProtocolTransferDataComplete(
IN NDIS_HANDLE ProtocolBindingContext,
 IN PNDIS_PACKET Packet,
 IN NDIS_STATUS Status,
 IN UINT BytesTransferred
);

ProtocolTransferDataComplete is a required driver function if the protocol might bind itself to an underlying NIC driver that does not indicate full-packet receives with NdisMIndicateReceivePacket. ProtocolTransferDataComplete completes the processing of a protocol-initiated transfer-data request for which NdisTransferData returned NDIS_STATUS_PENDING.

Parameters

ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
Packet
Points to the protocol-allocated packet descriptor the driver originally passed to NdisTransferData.
Status
Specifies the final status of the transfer-data operation.
BytesTransferred
Specifies the number of bytes of data that the NIC driver transferred into the buffers mapped by buffer descriptors chained to the packet descriptor at Packet. The protocol uses this value to determine whether the miniport supplied all the requested data for the originally indicated network packet.

Comments

ProtocolTransferDataComplete can be called before NdisTransferData returns control. If this occurs, the protocol driver has no opportunity to inspect the status code that NdisTransferData returns at Status. The protocol must assume that the given packet descriptor at Packet was pending until ProtocolTransferDataComplete was called.

When the underlying NIC driver calls NdisMTransferDataComplete, NDIS forwards the miniport-supplied Packet pointer and Status value to ProtocolTransferDataComplete. When this occurs, the protocol regains ownership of the resources it allocated for a preceding call to NdisTransferData. ProtocolTransferDataComplete can postprocess the miniport-supplied data in the buffers the protocol chained to the packet descriptor at Packet, along with the data ProtocolReceive copied in the corresponding indication. Then, ProtocolTransferDataComplete can notify interested clients of the received network data.

ProtocolTransferDataComplete also can either release the resources that the protocol allocated for its call to NdisTransferData or prepare the returned buffer and packet descriptors for reuse in a subsequent call to NdisTransferData. As a general rule, reusing such resources yields better performance than releasing them except, possibly, in periods of low network traffic if the protocol previously allocated a surplus of these resources to handle a period of heavy I/O demand.

To prepare the buffer and packet descriptors for reuse, ProtocolTransferDataComplete should follow these guidelines:

  • Always call an NdisUnchainBufferAt Xxx function as many times as necessary to save the buffer descriptor pointers before ProtocolTransferDataComplete calls NdisReinitializePacket with the descriptor at Packet.

    Otherwise, NdisReinitializePacket sets the head of the buffer chain to NULL so the protocol cannot recover pointers to the buffer descriptors chained to the packet descriptor. In effect, the protocol loses a set of buffer descriptors that the protocol allocated with NdisAllocateBuffer, and possibly loses access to the protocol-allocated buffers mapped by these descriptors as well.

  • Never call NdisZeroMemory with a pointer to a packet descriptor. Use NdisReinitializePacket instead.

    Otherwise, NdisZeroMemory destroys the packet descriptor the protocol allocated with NdisAllocatePacket, rendering it unusable for specifying subsequent data transfers and sends.

The ProtocolTransferDataComplete function of an NDIS intermediate driver cannot simply forward completion indications to still higher-level protocols. Such an attempt can cause a deadlock. Instead, such a driver must call NdisIMSwitchToMiniport to forward the indication from the appropriate context. If NdisIMSwitchToMiniport returns FALSE, the driver must call NdisIMQueueMiniportCallback and forward the indication from the protocol-supplied MiniportCallback function.

By default, ProtocolTransferDataComplete runs at IRQL DISPATCH_LEVEL in an arbitrary thread context.

See Also

MiniportTransferData, NdisAllocateBuffer, NdisFreeBuffer, NdisFreeMemory, NdisFreePacket, NdisIMQueueMiniportCallback, NdisIMSwitchToMiniport, NdisMIndicateReceivePacket, NdisMTransferDataComplete, NdisReinitializePacket, NdisTransferData, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, NdisZeroMemory, ProtocolReceive, ProtocolReceivePacket


ProtocolTranslate

VOID
ProtocolTranslate(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE ProtocolBindingContext,
OUT PNET_PNP_ID IdList,
IN ULONG IdListLength,
OUT PULONG BytesReturned
);

ProtocolTranslate is defined for future use in Windows NT PnP-aware protocol drivers.


ProtocolUnbindAdapter

VOID
ProtocolUnbindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE UnbindContext
);

ProtocolUnbindAdapter is a required function in any driver that registers a ProtocolBindAdapter function with the NDIS library.

Parameters

Status
Specifies the status of the unbinding operation as the value returned by NdisCloseAdapter, which can be either of the following:
NDIS_STATUS_SUCCESS
The binding was closed and all resources pertaining to that binding have been released.
NDIS_STATUS_PENDING
The request is being handled asynchronously, and the ProtocolCloseAdapterComplete function will complete the operation.
ProtocolBindingContext
Specifies the handle to a protocol-allocated context area in which the protocol driver maintains per-binding runtime state. The driver supplied this handle when it called NdisOpenAdapter.
UnbindContext
Specifies a handle, supplied by NDIS, that the protocol passes subsequently to NdisCompleteUnbindAdapter.

Comments

As the reciprocal of ProtocolBindAdapter, ProtocolUnbindAdapter releases all resources the driver allocated for per-binding network I/O operations with one possible exception: the driver-allocated context area designated by the ProtocolBindingContext handle.

ProtocolUnbindAdapter must call NdisCloseAdapter, which can, in turn, call the driver’s ProtocolCloseAdapterComplete function with the ProtocolBindingContext handle, so ProtocolUnbindAdapter cannot free the memory at ProtocolBindingContext unless NdisCloseAdapter returns NDIS_STATUS_SUCCESS.

Consequently, ProtocolUnbindAdapter should store the input UnbindContext handle in the area at ProtocolBindingContext before it calls NdisCloseAdapter. If this call is completed asynchronously, the ProtocolCloseAdapterComplete function will free all driver-allocated per-binding resources and call NdisCompleteUnbindAdapter with the UnbindContext handle.

If NdisCloseAdapter returns NDIS_STATUS_SUCCESS, ProtocolUnbindAdapter frees all per-binding resources, including the driver-allocated memory at ProtocolBindingContext, and calls NdisCompleteUnbindAdapter itself.

The ProtocolUnbindAdapter function of an NDIS intermediate driver makes a reciprocal call to NdisIMDeinitializeDeviceInstance. Such a driver's MiniportHalt function must release all driver-allocated resources associated with the intermediate driver's NIC before its virtual NIC is removed from the system.

As soon as ProtocolUnbindAdapter calls NdisCloseAdapter, the NdisBindingHandle originally returned by NdisOpenAdapter becomes invalid. The protocol cannot make any subsequent calls to Ndis Xxx functions with this handle, and the protocol cannot receive any indications from the underlying driver. However, if the underlying NIC is being removed to be reconfigured, NDIS will call the driver’s ProtocolBindAdapter function to re-establish the binding when the NIC is available again.

By default, ProtocolUnbindAdapter runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

NdisCloseAdapter, NdisCompleteUnbindAdapter, NdisFreeBufferPool, NdisFreeMemory, NdisFreePacketPool, NdisIMDeinitializeDeviceInstance, ProtocolBindAdapter, ProtocolCloseAdapterComplete


Chapter 5 NDIS Object Identifiers

This chapter describes NDIS object identifiers, the set of system-defined constants of the form OID_XXX, that higher-level NDIS drivers set in an NDIS_REQUEST-type structure for their calls to NdisRequest. Each such request is classified as one of the following:

  • A query is a call to retrieve information from or about the underlying NDIS driver, usually about the driver’s or NIC’s overall capabilities or current status.

    An NDIS protocol driver sets NdisRequestQueryInformation for the RequestType parameter to NdisRequest when it makes global queries. Such requests are handled by the MiniportQueryInformation functions of underlying NIC drivers.

  • A statistics query is a call to retrieve information about network performance. Such a request always originates in a user-mode application. Protocol drivers never set NdisRequestQueryStatistics for the RequestType parameter to NdisRequest. Such requests are handled either by NDIS or by the MiniportQueryInformation functions of underlying NIC drivers.

  • A set is a call with directions for the underlying NDIS driver, such as the header format the protocol wants used for receive indications when the underlying driver offers a choice or which optional features the miniport should enable on its NIC.

    An NDIS protocol driver sets NdisRequestSetInformation for the RequestType parameter to NdisRequest when it makes this type of request. Such requests are handled by the MiniportSetInformation functions of underlying NIC drivers.

Many system-defined OIDs are valid with more than one of the preceding NdisRequest Xxx values. Associated with each NDIS object identified by an OID_XXX is a data buffer, which varies in size and format depending on the given OID. The caller of NdisRequest supplies a pointer to this data buffer in the InformationBuffer member of the NDIS_REQUEST structure.

OIDs are either general or media-specific in nature. Each defined OID is either an operational OID or a statistics OID. Support for each NDIS-defined OID is either mandatory or optional.

Some of the information about OIDs is presented in charts. For each entry (row), these charts contain the information summarized in Table 5.1.

Table 5.1 Key to OID Charts
Column Header | Notation Meaning
Length | By default, the value indicates the number of bytes of data associated with the OID. For counters, a length of 4 indicates a 32-bit counter and 8 indicates a 64-bit counter. The NIC driver cannot reset counters; counters wrap when they reach their maximum. Values expressed in the form Arr(n), indicate an array of elements, each of size n.The underlying driver stores array elements consecutively, with no padding.
Q | An “M” indicates the OID is valid in calls to NdisRequest with RequestType value NdisRequestQueryInformation or, from components other than protocols, NdisRequestQueryStatistics. An “O” indicates the optional OID is valid in the same type of request if the underlying NDIS driver supports the OID.
S | An “M” indicates the OID is valid in calls to NdisRequest with RequestType value NdisRequestSetInformation. An “O” indicates the optional OID is valid in the same type of request if the underlying NDIS driver supports the OID.
Name | The system-defined OID_XXX, followed by a brief explanation of the constant name.

NDIS-defined OID_XXX constants are 4-byte values, encoded as shown in the following chart. However, OIDs of the form OID_TAPI_XXX do not have values that explicitly indicate whether support of each OID is mandatory or optional.
Byte (LSB to MSB) | Code(s) | Definition
1 | 0xN (unique value) | Differentiates each OID from others with the same value in the three high-order bytes.
2 | 0x01 | Mandatory
 | 0x02 | Optional
3 | 0x01 | Operational Characteristics
 | 0x02 | Statistics
4 | 0x00 | General Information
 | 0x01-0xFE | System-defined media-specific information, as follows:
 | 0x01 | Ethernet (802.3)
 | 0x02 | Token Ring (802.5)
 | 0x03 | Fiber Distributed Data Interface (FDDI)
 | 0x05 | LocalTalk
 | 0x06 | ARCNET
 | 0x04 | Wide area network (WAN)
 | 0x07 | TAPI
 | 0x08 | Reserved for future (native ATM)
 | 0x09 | Wireless
 | 0x0A | Reserved for infrared (IrDA)
 | 0xFF | Implementation-specific information

See the following for OID-specific references:

5.1 General Objects

5.2 Ethernet Objects

5.3 Token Ring Objects

5.4 FDDI Objects

5.5 LocalTalk Objects

5.6 ARCNET Objects

5.7 WAN Objects

5.8 TAPI Objects

5.9 Wireless Objects


5.1 General Objects

This section describes the set of system-defined general OIDs, both operational and statistical, that all underlying NDIS drivers support.

General Operational Characteristics

The following chart summarizes the OIDs used to get or set the general operational characteristics of miniports and/or their NICs.
Length | Q | S | Name
Arr(4) | M |  | OID_GEN_SUPPORTED_LIST List of supported OIDs
4 | M |  | OID_GEN_HARDWARE_STATUS Hardware status
Arr(4) | M |  | OID_GEN_MEDIA_SUPPORTED Media types supported (encoded)
Arr(4) | M |  | OID_GEN_MEDIA_IN_USE Media types in use (encoded)
4 | M |  | OID_GEN_MAXIMUM_LOOKAHEAD Maximum in bytes, receive lookahead size
4 | M |  | OID_GEN_MAXIMUM_FRAME_SIZE Maximum in bytes, frame size
4 | M |  | OID_GEN_LINK_SPEED Link speed in units of 100 bps
4 | M |  | OID_GEN_TRANSMIT_BUFFER_SPACE Transmit buffer space
4 | M |  | OID_GEN_RECEIVE_BUFFER_SPACE Receive buffer space
4 | M |  | OID_GEN_TRANSMIT_BLOCK_SIZE Minimum amount of storage, in bytes, that a single packet occupies in the transmit buffer space of the NIC
4 | M |  | OID_GEN_RECEIVE_BLOCK_SIZE Amount of storage, in bytes, that a single packet occupies in the receive buffer space of the NIC
4 | M |  | OID_GEN_VENDOR_ID Vendor NIC code
Var. | M |  | OID_GEN_VENDOR_DESCRIPTION Vendor network card description
4 | M |  | OID_GEN_VENDOR_DRIVER_VERSION Vendor-assigned version number of the driver
4 | M | M | OID_GEN_CURRENT_PACKET_FILTER Current packet filter (encoded)
4 | M | M | OID_GEN_CURRENT_LOOKAHEAD Current lookahead size in bytes
2 | M |  | OID_GEN_DRIVER_VERSION NDIS version number used by the driver
4 | M |  | OID_GEN_MAXIMUM_TOTAL_SIZE Maximum total packet length in bytes
4 |  | M | OID_GEN_PROTOCOL_OPTIONS Optional protocol flags (encoded)
4 | M |  | OID_GEN_MAC_OPTIONS Optional NIC flags (encoded)
4 | M |  | OID_GEN_MEDIA_CONNECT_STATUS Whether the NIC is connected to the network
4 | M |  | OID_GEN_MAXIMUM_SEND_PACKETS The maximum number of send packets the driver can accept per call to its MiniportSendPackets function

The following describes each of the general OID_XXX listed in the preceding chart more fully.

OID_GEN_SUPPORTED_LIST

Specifies an array of OIDs for objects that the underlying driver or its NIC supports. Objects include general, media-specific, and implementation-specific objects.

The underlying driver should order the OID list it returns in increasing numeric order. NDIS forwards a subset of the returned list to protocols that make this query. That is, NDIS filters any supported statistics OIDs out of the list since protocols never make statistics queries subsequentlly.

OID_GEN_HARDWARE_STATUS

Specifies the current hardware status of the underlying NIC, as one of the following NDIS_HARDWARE_STATUS-type values:

NdisHardwareStatusReady
Available and capable of sending and receiving data over the wire
NdisHardwareStatusInitializing
Initializing
NdisHardwareStatusReset
Resetting
NdisHardwareStatusClosing
Closing
NdisHardwareStatusNotReady
Not ready

OID_GEN_MEDIA_SUPPORTED

A complete list of the media types the NIC supports, as a proper subset of the following system-defined values:

NdisMedium802_3
Ethernet (802.3)
NdisMedium802_5
Token Ring (802.5)
NdisMediumFddi
FDDI
NdisMediumWan
WAN
NdisMediumLocalTalk
LocalTalk
NdisMediumDix
DEC/Intel/Xerox (DIX) Ethernet
NdisMediumArcnetRaw
ARCNET (raw)
NdisMediumArcnet878_2
ARCNET (878.2)
NdisMediumWirelessWan
Various types of NdisWireless Xxx media
NdisMediumAtm
Reserved for future use
NdisMediumIrda
Reserved for future use on Windows NT platforms

Currently, NDIS drivers provide support for ATM networks as LAN-emulation drivers. Such a driver declares its medium as NdisMedium802_3 or NdisMedium802_5, rather than NdisMediumAtm. Such a driver emulates Ethernet or Token Ring to higher-level NDIS drivers, complies with the ATM Forum’s LANE V1.0, and provides UNI 3.1 signaling support.

By contrast, a wireless-WAN NIC driver must report its medium type as NdisMediumWirelessWan. However, such a miniport also must provide NdisWWDIXEthernetFrames header format to any bound protocol that selects this format, and the miniport can provide its NIC’s native header format as well. To support existing LAN-based protocols, the driver writer can provide an NDIS intermediate driver to “translate” a wireless NIC’s native header formats and medium-specific information into a form understood by existing protocols.

If the underlying miniport returns NULL for this query or if an experimental media type is used, the driver must indicate receives with NdisMIndicateReceivePacket. Any protocol bound to such an underlying NIC driver receives all such indications, that is, the protocol cannot filter receives with OID_GEN_CURRENT_PACKET_FILTER.

OID_GEN_MEDIA_IN_USE

A complete list of the media types the NIC is currently supporting, defined as some, none (also called “the NULL filter”), or all of the following:

NdisMedium802_3
Ethernet (802.3)
NdisMedium802_5
Token Ring (802.5)
NdisMediumFddi
FDDI
NdisMediumWan
WAN
NdisMediumLocalTalk
LocalTalk
NdisMediumDix
DIX
NdisMediumArcnetRaw
ARCNET (raw)
NdisMediumArcnet878_2
ARCNET (878.2)
NdisMediumWirelessWan
Various types of NdisWireless Xxx media.

If the underlying miniport returns NULL for this query or if an experimental media type is used, the driver must indicate receives with NdisMIndicateReceivePacket. Any protocol bound to such an underlying NIC driver receives all such indications, that is, the protocol cannot filter receives with OID_GEN_CURRENT_PACKET_FILTER.

OID_GEN_MAXIMUM_LOOKAHEAD

The maximum number of bytes the NIC can always provide as lookahead data.

If the underlying driver supports multipacket receive indications, bound protocols are given full net packets on every indication. Consequently, this value is identical to that returned for OID_GEN_RECEIVE_BLOCK_SIZE.

OID_GEN_MAXIMUM_FRAME_SIZE

The maximum network packet size in bytes the NIC supports, not including a header.

For a binding emulating another medium type, the NIC driver must define the maximum frame size in such a way that it will not transform a protocol-supplied net packet of this size to a net packet too large for the true network medium.

OID_GEN_LINK_SPEED

The maximum speed of the NIC (kbps). The unit of measurement is 100 bps, so a value of 100,000 represents a hardware bit rate of 10 Mbps.

OID_GEN_TRANSMIT_BUFFER_SPACE

The amount of memory, in bytes, on the NIC available for buffering transmit data. A protocol can use this OID as a guide for sizing the amount of transmit data per send.

OID_GEN_RECEIVE_BUFFER_SPACE

The amount of memory on the NIC available for buffering receive data. A protocol driver can use this OID as a guide for advertising its receive window when it establishes sessions with remote nodes.

OID_GEN_TRANSMIT_BLOCK_SIZE

The minimum number of bytes that a single net packet occupies in the transmit buffer space of the NIC.

For example, on some NICs the transmit space is divided into 256-byte pieces so such a NIC’s transmit block size would be 256. To calculate the total transmit buffer space on such a NIC, its driver multiplies the number of transmit buffers on the NIC by its transmit block size.

For other NICs, the transmit block size is identical to its maximum packet size.

OID_GEN_RECEIVE_BLOCK_SIZE

The amount of storage, in bytes, that a single packet occupies in the receive buffer space of the NIC.

OID_GEN_VENDOR_ID

A three-byte IEEE-registered vendor code, followed by a single byte the vendor assigns to identify a particular NIC.

The IEEE code uniquely identifies the vendor and is the same as the three bytes appearing at the beginning of the NIC hardware address.

Vendors without an IEEE-registered code should use the value 0xFFFFFF.

OID_GEN_VENDOR_DESCRIPTION

Points to a zero-terminated counted string describing the NIC.

OID_GEN_VENDOR_DRIVER_VERSION

The vendor-assigned version number of the NIC driver.

The low-order half of the return value specifies the minor version, while the high-order half specifies the major version.

OID_GEN_CURRENT_PACKET_FILTER

Determines the types of net packets for which a protocol receives indications from a NIC driver that reports its medium type as one for which the system provides a filter library. The packet filter is an inclusive OR of the following types:

NDIS_PACKET_TYPE_ALL_FUNCTIONAL
All functional address packets, not just the ones in the current functional address.
NDIS_PACKET_TYPE_ALL_MULTICAST
All multicast address packets, not just the ones enumerated in the multicast address list.
NDIS_PACKET_TYPE_ALL_LOCAL
All packets sent by installed protocols and all packets indicated by the NIC identified by a given NdisBindingHandle.
NDIS_PACKET_TYPE_BROADCAST
Broadcast packets.
NDIS_PACKET_TYPE_DIRECTED
Directed packets. Directed packets contain a destination address equal to the station address of the NIC.
NDIS_PACKET_TYPE_FUNCTIONAL
Functional address packets sent to addresses included in the current functional address.
NDIS_PACKET_TYPE_GROUP
Packets sent to the current group address.
NDIS_PACKET_TYPE_MAC_FRAME
NIC driver frames that a Token Ring NIC receives.
NDIS_PACKET_TYPE_SMT
SMT packets that an FDDI NIC receives.
NDIS_PACKET_TYPE_MULTICAST
Multicast address packets sent to addresses in the multicast address list.

A protocol driver can receive Ethernet (802.3) multicast packets or Token Ring (802.5) functional address packets by specifying the multicast or functional address packet type. Setting the multicast address list or functional address determines which multicast address groups the NIC driver enables.

NDIS_PACKET_TYPE_PROMISCUOUS
Specifies all packets.
NDIS_PACKET_TYPE_SOURCE_ROUTING
All source routing packets. If the protocol driver sets this bit, the NDIS library attempts to act as a source routing bridge.

At a call to NdisOpenAdapter, NDIS disables packet reception, along with multicast and functional addresses.

For a query, NDIS returns the OR of all binding filters.

For a set, the specified packet filter replaces the previous packet filter for the binding. If the NIC driver previously enabled a packet type but the protocol driver does not specify it in a new filter, the protocol driver will not receive packets of this type.

If the NIC driver does not set a bit for a particular packet type in response to this query, the protocol driver will not receive packets of that type. Consequently, a protocol driver can disable packet reception by calling NdisRequest with a filter of zero. In these circumstances, if promiscuous mode is enabled, the protocol driver continues to receive packets even if the sending network node does not direct them to it. NDIS then sends the protocol driver all packets the NIC receives.

Setting a specific packet filter does not alter the packet filter for other protocols bound to (or above) the same NIC. For example, if one bound protocol enables promiscuous mode, other bound protocols do not receive packets they have not specifically requested with their own packet filters.

OID_GEN_CURRENT_LOOKAHEAD

The number of bytes of received packet data, excluding the header, that will be indicated to the protocol driver.

For a query, NDIS returns the largest lookahead size from among all the bindings. A protocol driver can set a suggested value for the number of bytes to be used in its binding; however, the underlying NIC driver is never required to limit its indications to the value set.

If the underlying driver supports multipacket receive indications, bound protocols are given full net packets on every indication. Consequently, this value is identical to that returned for OID_GEN_RECEIVE_BLOCK_SIZE.

OID_GEN_DRIVER_VERSION

The NDIS version in use by the NIC driver. The high byte is the major version number and the low byte is the minor version number.

OID_GEN_MAXIMUM_TOTAL_SIZE

The maximum total packet length, in bytes, the NIC supports, including the header. This value is medium-dependent. The returned length specifies the largest packet a protocol driver can pass to NdisSend or NdisSendPackets.

For a binding emulating another media type, the NIC driver must define the maximum total packet length in such a way that it will not transform a protocol-supplied net packet of this size to a net packet too large for the true network medium.

OID_GEN_PROTOCOL_OPTIONS

A bitmask that defines optional properties of the protocol driver. A protocol informs NDIS of its properties, which can optionally take advantage of them. If the protocol driver does not set its flags on a binding, NDIS assumes they are all clear.

The following flags are currently defined:

NDIS_PROT_OPTION_ESTIMATED_LENGTH
Indicates that packets can be indicated at the worst-case estimate of packet size, instead of an exact value, to this protocol.
NDIS_PROT_OPTION_NO_LOOPBACK
The protocol does will not require loopback support on the binding.

OID_GEN_MAC_OPTIONS

A bitmask that defines optional properties of the underlying driver or its NIC. A protocol that initiates this query can determine which of the flags the underlying driver sets, and can optionally take advantage of them.

The following flags are currently defined:

NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA
The protocol driver is free to access indicated data by any means. Some fast-copy functions have trouble accessing on-board device memory. NIC drivers that indicate data out of mapped device memory should never set this flag. If a NIC driver does set this flag, it relaxes the restriction on fast-copy functions.
NDIS_MAC_OPTION_RECEIVE_SERIALIZED
The NIC driver indicates packets in a serial manner. That is, such a driver does not enter a new receive indication until the previous receive and any associated calls to the driver’s MiniportTransferData function, if any, have been completed.
NDIS_MAC_OPTION_TRANSFERS_NOT_PEND
The NIC driver never returns NDIS_STATUS_PENDING from its MiniportTransferData function. The call succeeds or fails immediately in all cases.

A miniport that indicates receives with NdisMIndicateReceivePacket has no MiniportTransferData function. Such a driver should set this flag.

NDIS_MAC_OPTION_NO_LOOPBACK
The NIC has no internal loopback support so NDIS will manage loopbacks on behalf of this driver. A miniport cannot provide its own software loopback as efficiently as NDIS, so every miniport should set this flag unless its NIC has hardware loopback support. WAN NIC miniports must set this flag.
NDIS_MAC_OPTION_FULL_DUPLEX
The NIC driver supports full-duplex transmits and indications on SMP platforms. NDIS serializes calls to the MiniportSend(Packets) function of such a driver separately from its serialized calls to other MiniportXxx functions. A driver that reports its medium type as NdisMediumWan cannot set this flag, and WAN NIC miniports cannot be full-duplex drivers.

OID_GEN_MEDIA_CONNECT_STATUS

This OID requests the miniport to return the connection status of the NIC on the network as one of the following system-defined values:

NdisMediaStateConnected
NdisMediaStateDisconnected

When a NIC miniport senses that the network connection has been lost, it should call NdisMIndicateStatus with NDIS_STATUS_MEDIA_DISCONNECT. When the connection is restored, it should call NdisMIndicateStatus with NDIS_STATUS_MEDIA_CONNECT.

OID_GEN_MAXIMUM_SEND_PACKETS

The maximum number of send packets the MiniportSendPackets function can accept. If a larger packet array is given to MiniportSendPackets, it will return some packets in the array to NDIS for resubmission later.

If the underlying driver has only a MiniportSend function, it should return one for this query. Legacy NIC drivers usually return NDIS_STATUS_NOT_SUPPORTED for this query.

General Statistics

The following chart summarizes the OIDs used to get or set the general statistics for NDIS drivers and NICs.
Length | Q | S | Name
4 | M |  | OID_GEN_XMIT_OK Frames transmitted without errors.
4 | M |  | OID_GEN_RCV_OK Frames received without errors.
4 | M |  | OID_GEN_XMIT_ERROR Frames not transmitted or transmitted with errors
4 | M |  | OID_GEN_RCV_ERROR Frames received with errors
4 | M |  | OID_GEN_RCV_NO_BUFFER Frame missed, no buffers
8 | O |  | OID_GEN_DIRECTED_BYTES_XMIT Directed bytes transmitted without errors
4 | O |  | OID_GEN_DIRECTED_FRAMES_XMIT Directed frames transmitted without errors
8 | O |  | OID_GEN_MULTICAST_BYTES_XMIT Multicast bytes transmitted without errors
4 | O |  | OID_GEN_MULTICAST_FRAMES_XMIT Multicast frames transmitted without errors
8 | O |  | OID_GEN_BROADCAST_BYTES_XMIT Broadcast bytes transmitted without errors
4 | O |  | OID_GEN_BROADCAST_FRAMES_XMIT Broadcast frames transmitted without errors
8 | O |  | OID_GEN_DIRECTED_BYTES_RCV Directed bytes received without errors
4 | O |  | OID_GEN_DIRECTED_FRAMES_RCV Directed frames received without errors
8 | O |  | OID_GEN_MULTICAST_BYTES_RCV Multicast bytes received without errors
4 | O |  | OID_GEN_MULTICAST_FRAMES_RCV Multicast frames received without errors
8 | O |  | OID_GEN_BROADCAST_BYTES_RCV Broadcast bytes received without errors
4 | O |  | OID_GEN_BROADCAST_FRAMES_RCV Broadcast frames received without errors
4 | O |  | OID_GEN_RCV_CRC_ERROR Frames received with circular redundancy check (CRC) or frame check sequence (FCS) error
4 | O |  | OID_GEN_TRANSMIT_QUEUE_LENGTH Length of transmit queue

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_GEN_XMIT_OK

The number of frames transmitted without errors.

OID_GEN_RCV_OK

The number of frames the NIC receives without errors and indicates to bound protocols.

OID_GEN_XMIT_ERROR

The number of frames a NIC fails to transmit.

OID_GEN_RCV_ERROR

The number of frames a NIC receives but does not indicate to the protocols due to errors.

OID_GEN_RCV_NO_BUFFER

The number of frames the NIC cannot receive due to lack of NIC receive buffer space. Some NICs do not provide the exact number, but only the number of times they have missed at least one frame.

OID_GEN_DIRECTED_BYTES_XMIT

The number of bytes in directed packets transmitted without errors.

OID_GEN_DIRECTED_FRAMES_XMIT

The number of directed packets transmitted without errors.

OID_GEN_MULTICAST_BYTES_XMIT

The number of bytes in multicast/functional packets transmitted without errors.

OID_GEN_MULTICAST_FRAMES_XMIT

The number of multicast/functional packets transmitted without errors.

OID_GEN_BROADCAST_BYTES_XMIT

The number of bytes in broadcast packets transmitted without errors.

OID_GEN_BROADCAST_FRAMES_XMIT

The number of broadcast packets transmitted without errors.

OID_GEN_DIRECTED_BYTES_RCV

The number of bytes in directed packets received without errors.

OID_GEN_DIRECTED_FRAMES_RCV

The number of directed packets received without errors.

OID_GEN_MULTICAST_BYTES_RCV

The number of bytes in multicast/functional packets received without errors.

OID_GEN_MULTICAST_FRAMES_RCV

The number of multicast/functional packets received without errors.

OID_GEN_BROADCAST_BYTES_RCV

The number of bytes in broadcast packets received without errors.

OID_GEN_BROADCAST_FRAMES_RCV

The number of broadcast packets received without errors.

OID_GEN_RCV_CRC_ERROR

The number of frames received with checksum errors.

OID_GEN_TRANSMIT_QUEUE_LENGTH

The number of packets currently queued for transmission, whether on the NIC or in a driver-internal queue. For queries, the number returned is always the total number of packets currently queued, which can include unsubmitted send requests queued in the NDIS library.


5.2 Ethernet Objects

This describes the OIDs for Ethernet objects, both operational and statistical types.

Ethernet Operational Characteristics

The following chart summarizes the OIDs used to get or set Ethernet operational characteristics for underlying NDIS drivers and their NICs.
Length | Q | S | Name
6 | M |  | OID_802_3_PERMANENT_ADDRESS Permanent station address
6 | M |  | OID_802_3_CURRENT_ADDRESS Current station address
Arr(6) | M | M | OID_802_3_MULTICAST_LIST Current multicast address list
4 | M |  | OID_802_3_MAXIMUM_LIST_SIZE Maximum size of multicast address list
4 | M |  | OID_802_3_MAC_OPTIONS NIC flags (encoded)

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_802_3_PERMANENT_ADDRESS

The address of the NIC encoded in the hardware.

OID_802_3_CURRENT_ADDRESS

The address the NIC is currently using.

The network management software cannot set the current station address using the NDIS interface library. It must set this address as a configuration parameter.

OID_802_3_MULTICAST_LIST

The multicast address list on the NIC enabled for packet reception.

When a NIC driver initializes, it resets the NIC so the multicast address list is zero. NDIS also initializes the packet filter so it does not allow the protocol driver to receive multicast packets.

To receive a multicast packet, the protocol driver must later set the packet filter to include the NDIS_PACKET_TYPE_MULTICAST flag. At any time, it can disable multicast packet reception by canceling this flag. The order in which the protocol driver enables reception for multicast packets is not important.

The NIC driver can set a limit on the number of multicast addresses bound protocol drivers can enable simultaneously. NDIS returns NDIS_STATUS_MULTICAST_FULL if a protocol driver exceeds this limit or if it specifies an invalid multicast address.

A protocol driver can set the packet filter to enable all multicast packets, and do the filtering itself.

For a query, NDIS returns a list containing the union of all bindings’ multicast address lists.

OID_802_3_MAC_OPTIONS

A protocol can use this OID to determine features supported by the underlying driver, which could be emulating Ethernet.

The underlying driver either returns zero, indicating that it supports no options, or NDIS_802_3_OPTION_PRIORITY, indicating that it supports prioritized packets.

For example, the drivers of NICs for which the native media type is 100VG_AnyLAN or PACE should support packet priority. Such a NIC driver maps the NDIS priority range 0-3 to the medium-specific normal priority (zero) and the NDIS priority range 4-7 to the medium-specific high priority (one).

Any protocol bound to an underlying driver that supports packet priority can send prioritized packets over the network by setting up the out-of-band data block(s) associated with NDIS packet descriptor(s) before that protocol calls NdisSendPackets or NdisSend. However, such a protocol must take care to pass packet arrays in prioritized order to NdisSendPackets.

OID_802_3_MAXIMUM_LIST_SIZE

The maximum number of multicast addresses the NIC driver can manage.

This list is global for all protocols bound to (or above) the NIC. Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from the NIC driver when attempting to set the multicast address list, even if the number of elements in the given list is less than the number originally returned for this query.

Ethernet Statistics

The following chart summarizes the OIDs used to get Ethernet statistics for NICs.
Length | Q | S | Name
4 | M |  | OID_802_3_RCV_ERROR_ALIGNMENT Frames received with alignment error
4 | M |  | OID_802_3_XMIT_ONE_COLLISION Frames transmitted with one collision
4 | M |  | OID_802_3_XMIT_MORE_COLLISIONS Frames transmitted with more than one collision
4 | O |  | OID_802_3_XMIT_DEFERRED Frames transmitted after deferral
4 | O |  | OID_802_3_XMIT_MAX_COLLISIONS Frames not transmitted due to collisions
4 | O |  | OID_802_3_RCV_OVERRUN Frames not received due to overrun
4 | O |  | OID_802_3_XMIT_UNDERRUN Frames not transmitted due to underrun
4 | O |  | OID_802_3_XMIT_HEARTBEAT_FAILURE Frames transmitted with heartbeat failure
4 | O |  | OID_802_3_XMIT_TIMES_CRS_LOST Times carrier sense signal lost during transmission
4 | O |  | OID_802_3_XMIT_LATE_COLLISIONS Late collisions detected

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_802_3_RCV_ERROR_ALIGNMENT

The number of frames received with alignment errors.

OID_802_3_XMIT_ONE_COLLISION

The number of frames successfully transmitted after exactly one collision.

OID_802_3_XMIT_MORE_COLLISIONS

The number of frames successfully transmitted after more than one collision.

OID_802_3_XMIT_DEFERRED

The number of frames successfully transmitted after the NIC defers transmission at least once.

OID_802_3_XMIT_MAX_COLLISIONS

The number of frames not transmitted due to excessive collisions.

OID_802_3_RCV_OVERRUN

The number of frames not received due to overrun errors on the NIC.

OID_802_3_XMIT_UNDERRUN

The number of frames not transmitted due to underrun errors on the NIC.

OID_802_3_XMIT_HEARTBEAT_FAILURE

The number of frames successfully transmitted without detection of the collision-detect heartbeat.

OID_802_3_XMIT_TIMES_CRS_LOST

The number of times the CRS signal has been lost during packet transmission.

OID_802_3_XMIT_LATE_COLLISIONS

The number of collisions detected after the normal window.


5.3 Token Ring Objects

This describes the OIDs for Token Ring objects, both operational and statistical types.

Token Ring Operational Characteristics

The following chart summarizes the OIDs used to get or set Token Ring operational characteristics for NDIS drivers and NICs.
Length | Q | S | Name
6 | M |  | OID_802_5_PERMANENT_ADDRESS Permanent station address
6 | M |  | OID_802_5_CURRENT_ADDRESS Current station address
4 | M | M | OID_802_5_CURRENT_FUNCTIONAL Current functional address
4 | M | M | OID_802_5_CURRENT_GROUP Current group address
4 | M |  | OID_802_5_LAST_OPEN_STATUS Last open status
4 | M |  | OID_802_5_CURRENT_RING_STATUS Current ring status
4 | M |  | OID_802_5_CURRENT_RING_STATE Current ring state

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_802_5_PERMANENT_ADDRESS

The address of the NIC encoded in the hardware.

OID_802_5_CURRENT_ADDRESS

The address the NIC is currently using.

The network management software cannot set the current station address using the NDIS library. It must set the address as a configuration parameter.

OID_802_5_CURRENT_FUNCTIONAL

The functional address enabled on the NIC for packet reception.

When the underlying driver initializes its NIC, it resets the functional address to zero. NDIS also initializes the packet filter so that a protocol does not receive functional address packets.

To receive a functional address packet, a bound protocol must later set the packet filter to include the NDIS_PACKET_TYPE_FUNCTIONAL flag. At any time, it can disable packet reception by canceling this flag. The order in which the protocol enables reception for functional address packets is unimportant.

OID_802_5_CURRENT_GROUP

The group address enabled on the NIC for packet reception.

OID_802_5_LAST_OPEN_STATUS

The last open error status returned for a protocol’s call to NdisOpenAdapter.

If the open succeeded, a status code other than NDIS_STATUS_OPEN_FAILED was returned for the most recent open of the target NIC, so the value returned for this query should be zero.

OID_802_5_CURRENT_RING_STATUS

The last ring status indicated with an NDIS_RING_XXX status code, which can be any of the following values:
NDIS_RING_SIGNAL_LOSS
NDIS_RING_HARD_ERROR
NDIS_RING_SOFT_ERROR
NDIS_RING_TRANSMIT_BEACON
NDIS_RING_LOBE_WIRE_FAULT
NDIS_RING_AUTO_REMOVAL_ERROR
NDIS_RING_REMOVE_RECEIVED
NDIS_RING_COUNTER_OVERFLOW
NDIS_RING_SINGLE_STATION
NDIS_RING_RING_RECOVERY

OID_802_5_CURRENT_RING_STATE

The state of the NIC driver with respect to entering the ring, as one of the following:

NdisRingStateOpened
Specifies an open ring.
NdisRingStateClosed
Specifies a closed ring.
NdisRingStateOpening
Specifies a ring that is opening.
NdisRingStateClosing
Specifies a ring that is closing.
NdisRingStateOpenFailure
Specifies a failure of a ring open operation.
NdisRingStateRingFailure
Specifies a failure of the ring.

Token Ring Statistics

The following chart summarizes the OIDs used to get Token Ring statistics for NICs.
Length | Q | S | Name
4 | M |  | OID_802_5_LINE_ERRORS FCS or code violations detected in repeated frame
4 | M |  | OID_802_5_LOST_FRAMES Frames transmitted that have failed to return
4 | O |  | OID_802_5_BURST_ERRORS Number of five half-bit transition absences detected
4 | O |  | OID_802_5_AC_ERRORS Access control (A/C) errors
4 | O |  | OID_802_5_ABORT_DELIMITERS Frames transmitted with abort delimiter
4 | O |  | OID_802_5_FRAME_COPIED_ERRORS Frame copy errors
4 | O |  | OID_802_5_FREQUENCY_ERRORS Frequency errors detected
4 | O |  | OID_802_5_TOKEN_ERRORS Active monitor regenerations
4 | O |  | OID_802_5_INTERNAL_ERRORS Internal errors detected

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_802_5_LINE_ERRORS

The number of frames with an invalid FCS or a code violation.

OID_802_5_LOST_FRAMES

The number of frames transmitted that have not circled the ring within the maximum ring latency.

OID_802_5_BURST_ERRORS

The number of frames containing five half bits with no transition.

OID_802_5_AC_ERRORS

The number of frames with A/C errors.

OID_802_5_ABORT_DELIMITERS

The number of abort delimiters transmitted.

OID_802_5_FRAME_COPIED_ERRORS

The number of frames addressed to this NIC that another NIC on the ring has also copied.

OID_802_5_FREQUENCY_ERRORS

The number of frequency errors on the ring.

OID_802_5_TOKEN_ERRORS

The number of times the NIC has been the active monitor and has had to recreate.

OID_802_5_INTERNAL_ERRORS

The number of internal errors the NIC has detected.


5.4 FDDI Objects

This describes the mandatory OIDs for FDDI objects, both operational and statistical types.

FDDI Operational Characteristics

The following chart summarizes the mandatory OIDs used to get or set FDDI operational characteristics for NDIS drivers and NICs.
Length | Q | S | Name
6 | M |  | OID_FDDI_LONG_PERMANENT_ADDR Permanent station address
6 | M |  | OID_FDDI_LONG_CURRENT_ADDR Current station address
Arr(6) | M | M | OID_FDDI_LONG_MULTICAST_LIST Multicast list
4 | M |  | OID_FDDI_LONG_MAX_LIST_SIZE Maximum list size
2 | M |  | OID_FDDI_SHORT_PERMANENT_ADDR Permanent station address
2 | M |  | OID_FDDI_SHORT_CURRENT_ADDR Current station address
Arr(2) | M | M | OID_FDDI_SHORT_MULTICAST_LIST Multicast list
4 | M |  | OID_FDDI_SHORT_MAX_LIST_SIZE Maximum list size

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_FDDI_LONG_PERMANENT_ADDR

The address of the NIC encoded in the hardware.

OID_FDDI_LONG_CURRENT_ADDR

The address the NIC is currently using.

The network management software cannot set the current station address using the NDIS interface library. It must set this address as a configuration parameter.

OID_FDDI_LONG_MULTICAST_LIST

The list of multicast addresses the NIC is currently using.

OID_FDDI_LONG_MAX_LIST_SIZE

The maximum size multicast list the NIC supports.

OID_FDDI_SHORT_PERMANENT_ADDR

The address of the NIC encoded in the hardware.

OID_FDDI_SHORT_CURRENT_ADDR

The address the NIC is currently using.

The network management software cannot set the current station address using the NDIS interface library. It must set this address as a configuration parameter.

OID_FDDI_SHORT_MULTICAST_LIST

The list of multicast addresses the NIC is currently using.

OID_FDDI_SHORT_MAX_LIST_SIZE

The maximum size multicast list the NIC supports.

FDDI Statistics

The following chart summarizes the mandatory OIDs used to get FDDI statistics from underlying NIC drivers (or from NDIS).
Length | Q | S | Name
4 | M |  | OID_FDDI_ATTACHMENT_TYPE The direction of traffic on the ring
6 | M |  | OID_FDDI_UPSTREAM_NODE_LONG The long address of the station above this NIC on the ring
6 | M |  | OID_FDDI_DOWNSTREAM_NODE_LONG The long address of the station below this NIC on the ring
4 | M |  | OID_FDDI_FRAME_ERRORS The number of frames transmitted by this NIC that returned with errors
4 | M |  | OID_FDDI_FRAMES_LOST The number of frames transmitted by this NIC that were not returned within an acceptable interval
4 | M |  | OID_FDDI_RING_MGT_STATE The current state of the ring-management state machine.
4 | M |  | OID_FDDI_LCT_FAILURES The number of times the link confidence test has failed during connection management.
4 | M |  | OID_FDDI_LEM_REJECTS The link error monitor count of the times that a link was rejected.
4 | M |  | OID_FDDI_LCONNECTION_STATE The state of this ports physical connection managment state machine.

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_FDDI_ATTACHMENT_TYPE

Defines the attachment of the NIC to the network as one of the following:

NdisFddiTypeIsolated
NdisFddiTypeLocalA
NdisFddiTypeLocalB
NdisFddiTypeLocalAB
NdisFddiTypeLocalS
NdisFddiTypeWrapA
NdisFddiTypeWrapB
NdisFddiTypeWrapAB
NdisFddiTypeWrapS
NdisFddiTypeCWrapA
NdisFddiTypeCWrapB
NdisFddiTypeCWrapS
NdisFddiTypeThrough

OID_FDDI_UPSTREAM_NODE_LONG

The long address of the station above this NIC on the ring or zero if the address is unknown.

In a dual-ring configuration, the upstream node is the station from which frames are passed to this station on the primary ring.

OID_FDDI_DOWNSTREAM_NODE_LONG

The long address of the station below this NIC on the ring or zero if the address is unknown.

In a dual-ring configuration, the downstream node is the station to which frames are passed from this station on the primary ring.

OID_FDDI_FRAME_ERRORS

The number of frames detected in error by this NIC that have not been detected in error by another device on the ring.

OID_FDDI_FRAMES_LOST

The number of times this NIC detected a format error during frame reception such that the frame was stripped.

OID_FDDI_RING_MGT_STATE

Defines the current state of the Ring Management state machine as one of the following:

NdisFddiRingIsolated
NdisFddiRingNonOperational
NdisFddiRingOperational
NdisFddiRingDetect
NdisFddiRingNonOperationalDup
NdisFddiRingOperationalDup
NdisFddiRingDirected
NdisFddiRingTrace

OID_FDDI_LCT_FAILURES

The count of the consecutive times the link confidence test (LCT) has failed during connection management.

OID_FDDI_LEM_REJECTS

The link error monitor (LEM) count of times that a link was rejected.

OID_FDDI_LCONNECTION_STATE

Defines the state of this port’s Physical Connection Management (PCM) state machine as one of the following:

NdisFddiStateOff
NdisFddiStateBreak
NdisFddiStateTrace
NdisFddiStateConnect
NdisFddiStateNext
NdisFddiStateSignal
NdisFddiStateJoin
NdisFddiStateVerify
NdisFddiStateActive
NdisFddiStateMaintenance


5.5 LocalTalk Objects

This section describes the OIDs for LocalTalk objects, both operational and statistical types.

LocalTalk Operational Characteristics

The following chart summarizes the OID used to get operational characteristics for NDIS drivers and their NICs on a LocalTalk network.
Length | Q | S | Name
2 | M |  | OID_LTALK_CURRENT_NODE_ID Current station address

The following describes the OID_XXX listed in the preceding chart more fully.

OID_LTALK_CURRENT_NODE_ID

The address the NIC is currently using.

The network-management software cannot set the current station address using the NDIS interface library. It must set this address as a configuration parameter.

LocalTalk Statistics

The following chart summarizes the OIDs used to get LocalTalk statistics.
Length | Q | S | Name
4 | M |  | OID_LTALK_IN_BROADCASTS Broadcasts received
4 | M |  | OID_LTALK_IN_LENGTH_ERRORS Length errors
4 | O |  | OID_LTALK_OUT_NO_HANDLERS No handler
4 | O |  | OID_LTALK_COLLISIONS Collisions
4 | O |  | OID_LTALK_DEFERS Deferrals
4 | O |  | OID_LTALK_NO_DATA_ERRORS No data errors
4 | O |  | OID_LTALK_RANDOM_CTS_ERRORS Random lapCTS errors
4 | O |  | OID_LTALK_FCS_ERRORS FCS errors

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_LTALK_IN_BROADCASTS

The total number of broadcast packets received on the LocalTalk network.

OID_LTALK_IN_LENGTH_ERRORS

The total number of packets received on the LocalTalk network for which the actual length does not match the length in the header.

OID_LTALK_OUT_NO_HANDLERS

The total number of good packets received on the LocalTalk network that were not accepted by a protocol.

OID_LTALK_COLLISIONS

The total number of collisions assumed on the LocalTalk network due to lack of a lapCTS reply.

OID_LTALK_DEFERS

The total number of times the LocalTalk network has deferred to other packets.

OID_LTALK_NO_DATA_ERRORS

The total number of times the LocalTalk network has received a lapRTS packet when expecting a data packet.

OID_LTALK_RANDOM_CTS_ERRORS

The total number of times the LocalTalk network has received a lapCTS packet that a lapRTS packet has not solicited.

OID_LTALK_FCS_ERRORS

The total number of times the LocalTalk network has received a packet with an FCS error.


5.6 ARCNET Objects

This section describes the OIDs for Arcnet objects, both operational and statistical types.

ARCNET Operational Characteristics

The following chart summarizes the OIDs used to get operational characteristics for NDIS drivers and NICs on an Arcnet network.
Length | Q | S | Name
2 | M |  | OID_ARCNET_PERMANENT_ADDRESS Permanent station address
2 | M |  | OID_ARCNET_CURRENT_ADDRESS Current station address

The following describes each of the OID_XXX listed in the preceding chart more fully.

OID_ARCNET_PERMANENT_ADDRESS

The address of the NIC encoded in the hardware.

OID_ARCNET_CURRENT_ADDRESS

The address the NIC is currently using.

ARCNET Statistics

The following chart summarizes the OID used to get Arcnet NIC statistics.
Length | Q | S | Name
4 | O |  | OID_ARCNET_RECONFIGURATIONS Number of reconfigurations

The following describes the OID_XXX listed in the preceding chart more fully.

OID_ARCNET_RECONFIGURATIONS

This OID returns the number of network-reconfiguration interrupts received by the NIC driver for an ARCNET card since the system was started.

A reconfiguration takes place every time an ARCNET card is connected to or disconnected from the network. A reconfiguration also takes place every time an ARCNET card is reset. So every time an ARCNET card is reset on, added to, or removed from the network, every other NIC on the ARCNET network will augment its reconfiguration count.


5.7 WAN Objects

The following chart summarizes the OIDs used to get or set operational characteristics for NDIS drivers and WAN NICs.
Length | Q | S | Name
6 | M |  | OID_WAN_PERMANENT_ADDRESS Permanent station address
6 | M |  | OID_WAN_CURRENT_ADDRESS Current station
4 | M |  | OID_WAN_QUALITY_OF_SERVICE Quality of service
1 or 6 |  | M | OID_WAN_PROTOCOL_TYPE Protocol type
4 | M |  | OID_WAN_MEDIUM_SUBTYPE Medium subtype
4 |  | M | OID_WAN_HEADER_FORMAT Header format
4 | M |  | OID_WAN_GET_INFO Get information
4 | M |  | OID_WAN_GET_LINK_INFO Get link information
4 |  | M | OID_WAN_SET_LINK_INFO Set link information
4 | M |  | OID_WAN_LINE_COUNT Line count
50 | O |  | OID_WAN_GET_COMP_INFO Get compression information
50 |  | O | OID_WAN_SET_COMP_INFO Set compression information
60 | O |  | OID_WAN_GET_STATS_INFO Get statistics information

The following describes each of the OID_XXX listed in the preceding chart more fully.

The OID_WAN_GET_BRIDGE_INFO and OID_WAN_SET_BRIDGE_INFO codes are reserved.

OID_WAN_PERMANENT_ADDRESS

This OID requests the miniport to return the address encoded in the hardware of the NIC.

OID_WAN_CURRENT_ADDRESS

This OID requests the miniport to return the address that the NIC currently is using.

The miniport should return a unique address for the target NIC. NDISWAN presents the returned value, formatted as an Ethernet address, to higher-level protocols. Consequently, the least-significant bit must not be set in such a returned NIC-specific address to prevent it from being interpreted as an Ethernet multicast address.

The driver of a NIC manufactured by a vendor with an assigned Ethernet ID should use that ID as part of the address it returns for this OID. This prevents conflicts with other vendors’ assigned Ethernet address ranges.

OID_WAN_QUALITY_OF_SERVICE

This OID requests the miniport to return the quality of service that it supports.

The quality of service returned for this query should be a worst-case estimate, specified as one of the following system-defined values:

NdisWanRaw
Specifies a raw binding, in which the NIC driver puts all packets on the network unmodified.
NdisWanErrorControl
Specifies an error control binding.
NdisWanReliable
Specifies a reliable binding.

When a NIC driver calls NdisMIndicateStatus with a line-up indication, it provides QOS information for a specified link.

OID_WAN_PROTOCOL_TYPE

This OID informs the underlying driver of a bound protocol’s type, as a single-byte network-level protocol identifier (NLPID).

Alternatively, a protocol can specify a 6-byte Sub-Network Access Protocol (SNAP) NLPID, with 0x80-0x00-0x00-0x00 for the first four bytes and an Ethertype for the last two bytes.

OID_WAN_MEDIUM_SUBTYPE

This OID requests the miniport to return a list of the media subtypes that it can support as a proper subset of the following system-defined values:

NdisWanMediumHub
Specifies a remote access server (RAS) hub.
NdisWanMediumX_25
Specifies an X.25 medium.
NdisWanMediumIsdn
Specifies an ISDN medium.
NdisWanMediumSerial
Specifies a serial line.
NdisWanMediumFrameRelay
Specifies a frame-relay medium.
NdisWanMediumSonet
Specifies a synchronous optical network (Sonet) fiber medium.
NdisWanMediumSW56K
Specifies a switched 56K medium.

The NdisWanMediumAtm value is reserved.

OID_WAN_HEADER_FORMAT

This OID requests the miniport to return or to set the address-header format used, as either of the following system-defined values:

NdisWanHeaderEthernet
Selects Ethernet-emulation format.
NdisWanHeaderNative
Selects the format native to the NdisWanMedium Xxx subtype supported by the underlying driver. NDISWAN always uses Ethernet.

For a query, the miniport returns a list of the address formats that the NIC driver supports. For a set, the miniport subsequently uses the given type to format headers for this binding.

If an underlying NDIS driver does not support a header format that a protocol recognizes, that protocol’s attempt to bind to the underlying driver fails. A NIC driver is not required to support both address types of header format but should if it can.

OID_WAN_GET_INFO

This OID requests the miniport to return information about its or the NIC’s capabilities, formatted as an NDIS_WAN_INFO structure, defined as follows:

typedef struct _NDIS_WAN_INFO {
    OUT ULONG                  MaxFrameSize;
    OUT ULONG                  MaxTransmit;
    OUT ULONG                  HeaderPadding;
    OUT ULONG                  TailPadding;
    OUT ULONG                  Endpoints;
    OUT UINT                   MemoryFlags;
    OUT NDIS_PHYSICAL_ADDRESS  HighestAcceptableAddress;
    OUT ULONG                  FramingBits;
    OUT ULONG                  DesiredACCM;
} NDIS_WAN_INFO, *PNDIS_WAN_INFO;

The members of this structure contain the following information:

MaxFrameSize
Specifies the maximum frame size for any net packet that the NIC driver can send and receive. This value should exclude the driver’s own framing overhead and/or the PPP HDLC overhead. Typically this value is around 1500.

However, all WAN miniports should use an internal MaxFrameSize that is 32 bytes larger than the value they return for this OID. For example, a WAN miniport that returns 1500 for this OID should internally accept and send up to 1532. Such a miniport can readily support future bridging and additional protocols.

MaxTransmit
Specifies the maximum number of outstanding frames that the WAN miniport can handle. This member must be set to at least one.

NDISWAN uses the value of this member as a default throttle on how many send requests it submits to the driver’s MiniportWanSend function before NDISWAN holds incoming sends queued until the miniport completes an outstanding send. A miniport can adjust this value dynamically and on a per-link basis using the SendWindow member in the NDIS_MAC_LINE_UP structure that the driver passes to NdisMIndicateStatus. NDISWAN uses the current line-up SendWindow value as its throttle on outstanding sends unless the miniport sets SendWindow to zero for an indication, whereupon NDISWAN reverts to using the driver-set MaxTransmit value.

Since a WAN NIC driver must queue packets internally, the value of MaxTransmit is theoretically max(ULONG). However, this miniport-determined value should reflect the link speed or hardware capabilities of the NIC.

For example, if its NIC always has room for at least four frames, a miniport would set MaxTransmit to four so that any incoming packet to MiniportWanSend can be placed on the hardware immediately.

HeaderPadding
Specifies the amount of buffer space, in bytes, required at the beginning of each net packet, considering the current defaults, such as a MaxFrameSize of 1500.

For example, if its NIC requires just a flag byte preceding the frame, the miniport would set this member to one.

TailPadding
Specifies the amount of buffer space, in bytes, required at the end of each net packet, considering the current defaults, such as a MaxFrameSize of 1500.

For example, if its NIC requires three bytes at the end of each net packet for the Frame Checksum Sequence (CRC check) and the flag byte, a miniport would set this member to three.

Endpoints
Specifies the maximum number of links (sometimes called ports, or point-to-point connections) that the NIC can support at any given time.

For example, a miniport would set this member to two for an ISDN card with two channels. It would set this member to ten for an X.25 card that can accept up to ten virtual circuits.

MemoryFlags
If the NIC is not a DMA device, set this member to zero.

Otherwise, set this to the type of memory to be allocated for the NIC, that is, NDIS_MEMORY_NONCACHED and/or NDIS_MEMORY_CONTIGUOUS. Subsequently, memory of the specified type will be allocated for all packets to be transmitted by the WAN adapter.

HighestAcceptableAddress
If the NIC is not a DMA device, set this member with the macro NDIS_PHYSICAL_ADDRESS_CONST(-1,-1).

Otherwise, if the NIC can use 24-bit addresses, set this value to NDIS_PHYSICAL_ADDRESS_CONST (0x1000000, 0). Subsequently, memory within the specified range will be allocated for all packets to be transmitted by the NIC. Thus, memory can just be DMAed directly to the NIC instead of having to be double-buffered through a CPU-memory copy.

FramingBits
Set any bits (ORed) supported by the driver among the following:
RAS_FRAMING
Set only if the driver can detect older RAS framing. Only legacy drivers that supported earlier RAS framing set this flag.
RAS_COMPRESSION
Set only if the driver supports the older RAS compression scheme.
PPP_FRAMING
Should always be set. Indicates the driver can detect and support PPP framing for its medium type.
PPP_COMPRESS_ADDRESS_CONTROL
Set if the driver supports PPP address and control-field compression.

NDISWAN will remove the address and control field if this LCP option is negotiated. Some WAN medium types, such as X.25, do not support this option.

PPP_COMPRESS_PROTOCOL_FIELD
Set if the driver supports PPP protocol field compression.

NDISWAN will remove one byte from the protocol field when applicable if this LCP option is negotiated.

PPP_ACCM_SUPPORTED
Set if the driver supports Asynchronous Control Character Mapping. This bit is only valid for asynchronous media, such as modems. If this bit is set the DesiredACCM member should be valid.
PPP_MULTILINK_FRAMING
Set if the driver supports multilink framing as specified in IETF RFC 1717.
PPP_SHORT_SEQUENCE_HDR_FORMAT
Set if the driver supports header format for multilink framing as specified in IETF RFC 1717.
SLIP_FRAMING
Set if the driver can detect and support SLIP framing (asynchronous drivers only).
SLIP_VJ_COMPRESSION
Set if the driver can support Van Jacobsen TCP/IP header compression for SLIP. NDISWAN supports SLIP_VJ_COMPRESSION (with 16 slots). Asynchronous media (serial drivers) that support SLIP framing should set this bit.

Asynchronous media need not write any code to support VJ header compression. NDISWAN will take care of it.

SLIP_VJ_AUTODETECT
Set if the driver can autodetect Van Jacobsen TCP/IP header compression for SLIP. NDISWAN will auto-detect VJ header compression. Asynchronous media (serial drivers) should set this bit if they support SLIP framing.
TAPI_PROVIDER
Set if the driver supports the TAPI Service Provider OIDs. Unless this bit is set, TAPI OID calls will not be made to the driver.
MEDIA_NRZ_ENCODING
Set if the driver supports NRZ encoding, the PPP default for some media types such as ISDN. This value is reserved for future use.
MEDIA_NRZI_ENCODING
Set if the driver supports NRZI encoding. This value is reserved for future use.
MEDIA_NLPID
Set if the driver has and can set the NLPID in its frame. This value is reserved for future use.
RFC_1356_FRAMING
Set if the driver supports IETF RFC 1356 X.25 and ISDN framing. This value is reserved for future use.
RFC_1483_FRAMING
Set if the driver supports IETF RFC 1483 ATM adaptation layer-5 encapsulation. This value is reserved for future use.
RFC_1490_FRAMING
Set if the driver supports IETF RFC 1490 Frame Relay framing. This value is reserved for future use.
NBF_PRESERVE_MAC_ADDRESS
Set if the driver supports IETF framing as specified in the draft “The PPP NETBIOS Frames Control Protocol (NBFCP).”
SHIVA_FRAMING
Superseded by NBF_PRESERVE_MAC_ADDRESS.
PASS_THROUGH_MODE
Set if the driver does its own framing. If this flag is set, NDISWAN passes frames, uninterpreted and unmodified.

NIC drivers must be in the default PPP framing mode until each driver receives an NDIS_WAN_SET_LINK_INFO call. The NIC driver must auto-detect any framing that it claims to support.

For example, drivers that support old RAS framing must autodetect RAS framing from PPP framing. If a driver detects a framing scheme other than the default, that driver should automatically switch its framing into the newly detected framing.

A subsequent query with OID_WAN_GET_LINK_INFO should indicate the detected framing. If no framing is yet detected, the FramingBits should be zero in the returned NDIS_WAN_GET_LINK_INFO information.

If the WAN miniport is called subsequently with OID_WAN_SET_LINK_INFO in which the FramingBits member is zero, the driver should attempt to autodetect the framing upon reception of each frame.

DesiredACCM
The Asynchronous Control Character Map is negotiated. This member is relevant only for asynchronous media types.

OID_WAN_GET_LINK_INFO

This OID requests the miniport to return information about the current state of a link formatted as an NDIS_WAN_GET_LINK_INFO structure, defined as follows:

typedef struct _NDIS_WAN_GET_LINK_INFO {
    IN  NDIS_HANDLE     NdisLinkHandle;
    OUT ULONG           MaxSendFrameSize;
    OUT ULONG           MaxRecvFrameSize;
    OUT ULONG           HeaderPadding;
    OUT ULONG           TailPadding;
    OUT ULONG           SendFramingBits;
    OUT ULONG           RecvFramingBits;
    OUT ULONG           SendCompressionBits;
    OUT ULONG           RecvCompressionBits;
    OUT ULONG           SendACCM;
    OUT ULONG           RecvACCM;
} NDIS_WAN_GET_LINK_INFO, *PNDIS_WAN_GET_LINK_INFO;

The members of this structure contain the following information:

NdisLinkHandle
Identifies the link. This is the handle that the miniport supplied in its initial line-up indication for this link.
MaxSendFrameSize
Specifies the maximum buffer size, in bytes, that the miniport can accept for transmission on this link. From this both PreamblePadding and PostamblePadding can be calculated, along with the framing bits and ACCM. The driver’s MiniportWanSend function can reject any incoming send packet that is larger than this size.
MaxRecvFrameSize
Specifies the largest packet, not including the driver's padding, that will be received on the wire. The driver can drop any packets that are larger.
HeaderPadding
Specifies the number of padding bytes at the head of the frame.
TailPadding
Specifies the number of padding bytes at the tail of the frame.
SendFramingBits
Specifies send-framing bits indicating the type of framing that should be sent. If the miniport detects incompatibilities between SendFramingBits and RecvFramingBits, it returns NDIS_STATUS_INVALID_WAN_SETTINGS.

The proper NLPID and framing format should be used based on the framing bits wherever applicable.

RecvFramingBits
Specifies receive-framing bits indicating the type of framing that should be received.
SendCompressionBits
Reserved.
RecvCompressionBits
Reserved.
SendACCM
For asynchronous media types, logical bits 0-31 indicate the respective byte to be byte stuffed. That is, if bit 0 is set to 1, then ASCII character 0x00 should be byte stuffed, and so forth.
RecvACCM
As described for SendACCM.

Possible values for SendFramingBits and RecvFramingBits include any the driver returned in response to the OID_WAN_GET_INFO query.

OID_WAN_SET_LINK_INFO

This OID requests the miniport to set information for an established link. The protocol-supplied information is formatted as an NDIS_WAN_SET_LINK_INFO structure, defined as follows:

typedef struct _NDIS_WAN_SET_LINK_INFO {
    IN NDIS_HANDLE     NdisLinkHandle;
    IN ULONG           MaxSendFrameSize;
    IN ULONG           MaxRecvFrameSize;
    IN ULONG           HeaderPadding;
    IN ULONG           TailPadding;
    IN ULONG           SendFramingBits;
    IN ULONG           RecvFramingBits;
    IN ULONG           SendCompressionBits;
    IN ULONG           RecvCompressionBits;
    IN ULONG           SendACCM;
    IN ULONG           RecvACCM;
} NDIS_WAN_SET_LINK_INFO, *PNDIS_WAN_SET_LINK_INFO;

The members of this structure contain the following information:

NdisLinkHandle
Identifies the link. This is the handle the miniport supplied in its initial line-up indication for this link.
MaxSendFrameSize
Specifies the largest buffer, in bytes, the protocol will send for this link. This value must be less than or equal to that returned by the miniport for the OID_WAN_GET_LINK_INFO query.

From this, both PreamblePadding and PostamblePadding can be calculated, along with the framing bits and ACCM. The driver’s MiniportWanSend function can reject any send packets submitted for this link that are larger than this value.

MaxRecvFrameSize
Specifies the largest net packet, not including the driver's padding, that the protocol will receive subsequently. This value must be less than or equal to that returned by the miniport for the OID_WAN_GET_LINK_INFO query. The miniport can drop any received packets for this link that are larger.
HeaderPadding
Specifies the number of padding bytes at the head of the frame.
TailPadding
Specifies the number of padding bytes at the tail of the frame.
SendFramingBits
Specifies send-framing bits indicating the type of framing that should be sent. If the miniport detects incompatibilities between SendFramingBits and RecvFramingBits, it returns NDIS_STATUS_INVALID_WAN_SETTINGS.

The proper NLPID and framing format should be used based on the framing bits wherever applicable.

RecvFramingBits
Specifies receive-framing bits indicating the type of framing that should be received.
SendCompressionBits
Reserved.
RecvCompressionBits
Reserved.
SendACCM
For asynchronous media types, logical bits 0-31 indicate the respective byte to be byte stuffed. That is, if bit 0 is set to one then ASCII character 0x00 should be byte stuffed, and so forth.
RecvACCM
As described for SendACCM.

Possible values for SendFramingBits and RecvFramingBits include any the underlying driver returned in response to the OID_WAN_GET_INFO query.

OID_WAN_LINE_COUNT

This OID requests the miniport to return the total number of lines (endpoints) it exposes.

OID_WAN_GET_COMP_INFO

This OID requests the miniport to return information about the capabilities of the NIC or of its driver, in particular whether either supports compression. If so, the values returned are used to negotiate compression with the Point-to-Point Protocol (PPP) Compression Control Protocol. The protocol subsequently negotiates a compression scheme with an OID_WAN_SET_COMP_INFO request.

Compression information is returned in an NDIS_WAN_GET_COMP_INFO structure, defined as follows:

typedef struct _NDIS_WAN_GET_COMP_INFO {
    IN NDIS_HANDLE   NdisLinkHandle;
    OUT NDIS_WAN_COMPRESS_INFO SendCapabilities;
    OUT NDIS_WAN_COMPRESS_INFO RecvCapabilities;
} NDIS_WAN_GET_COMP_INFO, *PNDIS_WAN_GET_COMP_INFO;

The members of this structure contain the following information:

NdisLinkHandle
Identifies the link. This is the handle that the miniport supplied in the initial line-up indication for this link.
SendCapabilities
Specifies a structure containing information on compression capabilities for sending data, defined below.
RecvCapabilities
Specifies a structure containing information on compression capabilities for receiving data, defined next.

The NDIS_WAN_COMPRESS_INFO structure is defined as follows:

typedef struct _NDIS_WAN_COMPRESS_INFO {
    UCHAR    SessionKey[8];
    ULONG    MSCompType;

    // Members above reserved to indicate NDISWAN capabilities.
    // Members below indicate NIC-specific capabilities.

    UCHAR    CompType;
    USHORT   CompLength;
    union {
        struct {
            UCHAR    CompOUI[3];
            UCHAR    CompSubType;
            UCHAR    CompValues[32];
        } Proprietary;
        struct {
            UCHAR    CompValues[32];
        } Public;
    };
} NDIS_WAN_COMPRESS_INFO;

The members of this structure contain the following information:

SessionKey
Specifies a reserved variable used by NDISWAN as an encryption key. This variable should be ignored by NDIS drivers.
MsCompType
Specifies what type of compression/encryption capabilites NDISWAN has and what is currently being used. This member also is reserved for use by NDISWAN and should be ignored by NDIS drivers.
CompType
Specifies the compression capabilities of the NIC or its driver as one of the following system-defined values:
COMPTYPE_OUI
Set if the CompOUI and CompSubType members are set.
COMPTYPE_NT31RAS
Only legacy drivers that support the original RAS compression scheme set this value.
COMPTYPE_NONE
Set if compression is unsupported.
CompLength
Specifies the number of bytes in the CompValues member.
CompOUI
Specifies a NIC vendor’s IEEE-registered Organization Unique Identifier, which is the most significant three octets of an Ethernet Physical Address, assigned to the vendor by IEEE 802. This identifies the compression type as being proprietary to the vendor.
CompSubType
Specifies a compression specific to the OUI and indicates a compression type for that OUI as described by IETF draft “The PPP Compression Control Protocol (CCP).”
CompValues
Specifies additional data that is specific to the compression protocol.

OID_WAN_SET_COMP_INFO

This OID notifies the miniport of the compression scheme selected by a protocol to which the miniport already returned information in OID_WAN_GET_COMP_INFO.

The protocol supplies a specification for the compression scheme it selected in an NDIS_WAN_SET_COMP_INFO structure, defined as follows:

typedef struct _NDIS_WAN_SET_COMP_INFO {
    IN NDIS_HANDLE               NdisLinkHandle;
    IN NDIS_WAN_COMPRESS_INFO    SendCapabilities;
    IN NDIS_WAN_COMPRESS_INFO    RecvCapabilities;
} NDIS_WAN_SET_COMP_INFO, *PNDIS_WAN_SET_COMP_INFO;

The members of this structure contain the following information:

NdisLinkHandle
Identifies the link. This is the handle that the miniport supplied in the initial line-up indication for this link.
SendCapabilities
Specifies a structure containing information on compression capabilities for sending data.
RecvCapabilities
Specifies a structure containing information on compression capabilities for receiving data.

For specifics of the NDIS_WAN_COMPRESS_INFO structure, see the preceding OID_WAN_GET_COMP_INFO.

OID_WAN_GET_STATS_INFO

This OID requests the miniport to return statistics information. A WAN NIC driver is expected to keep statistics and to return these statistics for this OID in an NDIS_WAN_GET_STATS_INFO structure, defined as follows:

typedef struct _NDIS_WAN_GET_STATS_INFO {
    IN NDIS_HANDLE  NdisLinkHandle;
    OUT ULONG       BytesSent;
    OUT ULONG       BytesRcvd;
    OUT ULONG       FramesSent;
    OUT ULONG       FramesRcvd;
    OUT ULONG       CRCErrors;
    OUT ULONG       TimeoutErrors;
    OUT ULONG       AlignmentErrors;
    OUT ULONG       SerialOverrunErrors;
    OUT ULONG       FramingErrors;
    OUT ULONG       BufferOverrunErrors;
    OUT ULONG       BytesTransmittedUncompressed;
    OUT ULONG       BytesReceivedUncompressed;
    OUT ULONG       BytesTransmittedCompressed;
    OUT ULONG       BytesReceivedCompressed;
} NDIS_WAN_GET_STATS_INFO, *PNDIS_WAN_GET_STATS_INFO;

The members of this structure contain the following information:

NdisLinkHandle
Identifies the link. This is the handle that the miniport supplied in the initial line-up indication for this link.
BytesSent
Specifies the number of bytes transmitted.
BytesRcvd
Specifies the number of bytes received.
FramesSent
Specifies the number of frames (WAN packets) sent.
FramesRcvd
Specifies the number of frames received.
CRCErrors
Specifies the number of CRC errors encountered.
TimeoutErrors
Specifies the number of timeout errors encountered.
AlignmentErrors
Specifies the number of alignment errors encountered.
SerialOverrunErrors
Specifies the number of CRC errors encountered.
FramingErrors
Specifies the number of framing errors encountered.
BufferOverrunErrors
Specifies the number of buffer overruns encountered.
BytesTransmittedUncompressed
Specifies the number of bytes of uncompressed data transmitted. A miniport returns a nonzero value only if it supports compression.
BytesReceivedUncompressed
Specifies the number of bytes of uncompressed data received. A miniport returns a nonzero value only if it supports compression.
BytesTransmittedCompressed
Specifies the number of bytes of compressed data transmitted. A miniport returns a nonzero value only if it supports compression.
BytesReceivedCompressed
Specifies the number of bytes of compressed data received. A miniport returns a nonzero value only if it supports compression.

If the underlying driver or its NIC does not support compression, the driver returns zero for the Bytes..Uncompressed/Compressed members.


5.8 TAPI Objects

This defines the OIDs for TAPI objects, both for query and set information requests. These OIDs define the request interface between user-mode TAPI service providers and underlying WAN NIC miniports. Each such request goes through NDISTAPI and NDISWAN to the target miniport.

For more information about higher-level TAPI components, especially about user-mode service providers in which these OID requests originate, see the Win32 SDK.

The following chart summarizes the OIDs used to get operational characteristics for NDIS drivers and NICs that support TAPI on a WAN network.

In contrast with other OIDs, defined TAPI OID values do not indicate whether each OID is required or optional. The length of the information buffer for TAPI OID requests varies according to the OID-specific structure. Instances of some OID-specific structures also vary in size, in particular for TAPI-defined structures with members of type VAR_STRING, described later.
Q | S | Name
 |  | Mandatory OID_TAPI_XXX
 | M | OID_TAPI_ANSWER Answer
 | M | OID_TAPI_CLOSE Close
 | M | OID_TAPI_CLOSE_CALL Close Call
 | M | OID_TAPI_CONDITIONAL_MEDIA_DETECTION Conditional Media Detection
 | M | OID_TAPI_DROP Drop
M |  | OID_TAPI_GET_ADDRESS_CAPS Get Address Capabilities
M |  | OID_TAPI_GET_ADDRESS_ID Get Address ID
M |  | OID_TAPI_GET_ADDRESS_STATUS Get Address Status
M |  | OID_TAPI_GET_CALL_ADDRESS_ID Get Call Address ID
M |  | OID_TAPI_GET_CALL_INFO Get Call Information
M |  | OID_TAPI_GET_CALL_STATUS Get Call Status
M |  | OID_TAPI_GET_DEV_CAPS Get Device Capabilities
M |  | OID_TAPI_GET_ID Get ID
M |  | OID_TAPI_GET_LINE_DEV_STATUS Get Line Device Status
M |  | OID_TAPI_MAKE_CALL Make Call
M |  | OID_TAPI_OPEN Open
M |  | OID_TAPI_PROVIDER_INITIALIZE Provider Initialize
 | M | OID_TAPI_PROVIDER_SHUTDOWN Provider Shutdown
 | M | OID_TAPI_SET_APP_SPECIFIC Set Application Specific Member
 | M | OID_TAPI_SET_CALL_PARAMS Set Call Paramaters
 | M | OID_TAPI_SET_DEFAULT_MEDIA_DETECTION Set Default Media Detection
 | M | OID_TAPI_SET_MEDIA_MODE Set Media Mode
 | M | OID_TAPI_SET_STATUS_MESSAGES Set Status Messages
 |  | Optional OID_TAPI_XXX
 | O | OID_TAPI_ACCEPT Accept
O |  | OID_TAPI_CONFIG_DIALOG Configuration Dialog
O |  | OID_TAPI_DEV_SPECIFIC Device Specific
 | O | OID_TAPI_DIAL Dial Specified Number for Specified Call
O |  | OID_TAPI_GET_DEV_CONFIG Get Device Configuration
O |  | OID_TAPI_GET_EXTENSION_ID Get Extension ID
O |  | OID_TAPI_NEGOTIATE_EXT_VERSION Return Highest Acceptable Version Extension
 | O | OID_TAPI_SET_DEV_CONFIG Set Device Configuration
 | O | OID_TAPI_SECURE_CALL Secure Call
 | O | OID_TAPI_SELECT_EXT_VERSION Select Extension Version
 | O | OID_TAPI_SEND_USER_USER_INFO Send User User Information

WAN NIC miniports must support the OIDS labeled as Mandatory in the preceding chart. The extent to which a given miniport supports those labeled Optional in the preceding chart depends on the discretion of the driver writer and the capabilities of the NIC.

The functional requirements are quite simple for several of the TAPI OID requests. For example, the OID_TAPI_SET_APP_SPECIFIC request requires a NIC driver only to associate a ULONG value with a specified call instance, which can be retrieved later with an OID_TAPI_GET_CALL_INFO request. Requests that a NIC driver writer chooses to ignore should be completed with the NDIS_STATUS_TAPI_OPERATIONUNAVAIL status code.

A NIC driver can indicate the requests it supports, which might vary over time depending on line, address, or call state, by setting or clearing specific bits in the members of structures for some of the OID_TAPI_GET_XXX requests, such as in the ulAddrCapFlags member in the LINE_ADDRESS_CAPS structure.

For the declaration of LINE_ADDRESS_CAPS and other system-defined structures in which WAN miniports maintain TAPI state, see ndistapi.h. For more information about these structures, see also the Win32 SDK.

TAPI Type VAR_STRING

For several OID_TAPI_XXX, the miniport is given an OID-specific structured information buffer. More than one of these TAPI structures have a member of type VAR_STRING, which is also a structured buffer, defined as follows:

typedef struct _VAR_STRING {
    ULONG  ulTotalSize;
    ULONG  ulNeededSize;
    ULONG  ulUsedSize;
    ULONG  ulStringFormat;
    ULONG  ulStringSize;
    ULONG  ulStringOffset;
} VAR_STRING, *PVAR_STRING;

The members of this structure contain the following information:

ulTotalSize
The total size in bytes of the buffer, including the buffered string.
ulNeededSize
The size in bytes of a buffer necessary to contain a particular string. For example, the underlying driver sets this value if a protocol-supplied buffer is too short to contain a requested string.
ulUsedSize
The number of bytes in the given buffer that were actually used.
ulStringFormat
Specifies how the buffered string should be interpreted, as one of the following:
STRINGFORMAT_ASCII
STRINGFORMAT_DBCS
STRINGFORMAT_UNICODE
STRINGFORMAT_BINARY
ulStringSize
The number of bytes in the given buffered string.
ulStringOffset
The offset within this buffer at which the string begins.

The following describes the OID_XXX listed in the preceding chart and their associated TAPI structures, if any, more fully.

TAPI Query Information Requests

Query-information requests differ from set-information requests in that one or more members in the associated structure, denoted by the “OUT” prefix in the structure definition, are reserved for information the underlying miniport should return to the initiator of the request. WAN NIC drivers should treat structure members prefixed with “IN” as read-only.

The first member of all the structures used in query-information requests, ulRequestID, is reserved for future use. Miniport drivers should ignore this member.

OID_TAPI_GET_ADDRESS_CAPS

This OID requests the WAN NIC driver to return information for the specified address on the specified line about its telephony capabilities.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_ADDRESS_CAPS structure, defined as follows, to query the telephony capabilities of a specified address:

typedef struct _NDIS_TAPI_GET_ADDRESS_CAPS {
    IN ULONG               ulRequestID;
    IN ULONG               ulDeviceID;
    IN ULONG               ulAddressID;
    IN ULONG               ulExtVersion;
    OUT LINE_ADDRESS_CAPS  LineAddressCaps;
} NDIS_TAPI_GET_ADDRESS_CAPS, *PNDIS_TAPI_GET_ADDRESS_CAPS;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the link or data channel to be queried. This was set up in a preceding OID_TAPI_GET_ID request.
ulAddressID
Specifies the address on the given link for which capabilities should be returned. The value of this member is not validated by NDISTAPI when this request is made.
ulExtVersion
Specifies the version number of the miniport-specific extensions to be used. This member is zero if no device-specific extensions are used. Otherwise, the high-order bytes contain the major version number and the low-order bytes contain the minor version number. The value of this member is not validated by NDISTAPI when this request is made.
LineAddressCaps
Specifies the base virtual address of a variable-size structure of type LINE_ADDRESS_CAPS. See ndistapi.h for details.

This structure has at least sizeof(LINE_ADDRESS_CAPS) bytes reserved.

Upon successful completion of the request, the miniport fills this structure with address capabilities information.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION
NDIS_STATUS_TAPI_INVALADDRESSID
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_FAILURE

OID_TAPI_GET_ADDRESS_ID

This OID requests the WAN NIC driver to return the address ID associated with the address in a different format on the specified line.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_ADDRESS_ID structure, defined as follows:

typedef struct _NDIS_TAPI_GET_ADDRESS_ID {
    IN ULONG      ulRequestID;
    IN HDRV_LINE  hdLine;
    OUT ULONG     ulAddressID;
    IN ULONG      ulAddressMode;
    IN ULONG      ulAddressSize;
    IN CHAR       szAddress[1];
} NDIS_TAPI_GET_ADDRESS_ID, *PNDIS_TAPI_GET_ADDRESS_ID;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line for which the address ID should be retrieved. This was set up in a preceding OID_TAPI_OPEN request.
ulAddressID
Specifies the requested address ID. The miniport fills in this member with the address ID corresponding to szAddress to complete this request successfully.
ulAddressMode
Specifies the address mode of the address contained in szAddress. This member can have only the following LINEADDRESSMODE flag set:

LINEADDRESSMODE_DIALABLEADDR

ulAddressSize
Specifies the size in bytes of the address in szAddress.
szAddress
Specifies the virtual address of variable-size structure containing an address assigned to the line device. The format of the address is determined by the ulAddressMode member. If it is LINEADDRESSMODE_DIALABLEADDR, szAddress uses the common dialable number format and is zero-terminated.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALADDRESS
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_ADDRESS_STATUS

This OID requests the miniport to return the current status for the specified address.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_ADDRESS_STATUS structure, defined as follows, to query the current status for the specified address:

typedef struct _NDIS_TAPI_GET_ADDRESS_STATUS {
    IN ULONG                 ulRequestID;
    IN HDRV_LINE             hdLine;
    IN ULONG                 ulAddressID;
    OUT LINE_ADDRESS_STATUS  LineAddressStatus;
} NDIS_TAPI_GET_ADDRESS_STATUS, *PNDIS_TAPI_GET_ADDRESS_STATUS;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle for the line.
ulAddressID
Specifies an address on the given open line device. This is the address for which the status should be returned. The value of this member is not validated by NDISTAPI when this request is made.
LineAddressStatus
Specifies the virtural address of a variable-size structure of type LINE_ADDRESS_STATUS. See ndistapi.h for details.

The miniport fills this structure with address status information to complete this request successfully.

The LineAddressStatus member has at least sizeof(LINE_ADDRESS_STATUS) bytes reserved.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALADDRESSID
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_CALL_ADDRESS_ID

This OID requests the miniport to return the address ID for the specified call.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_CALL_ADDRESS_ID structure, defined as follows:

typedef struct _NDIS_TAPI_GET_CALL_ADDRESS_ID {
    IN ULONG      ulRequestID,
    IN HDRV_CALL  hdCall,
    OUT ULONG     ulAddressID
} NDIS_TAPI_GET_CALL_ADDRESS_ID, *PNDIS_TAPI_GET_CALL_ADDRESS_ID;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which the ID should be retrieved. This handle was set up either in a preceding OID_TAPI_MAKE_CALL request or when the miniport called NdisMIndicateStatus to report an incoming call.
ulAddressID
Specifies the address ID. The miniport fills this member with the corresponding address ID for hdCall to complete this request successfully.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_CALL_INFO

This OID requests the miniport to return detailed information about the specified call.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_CALL_INFO structure, defined as follows:

typedef struct _NDIS_TAPI_GET_CALL_INFO {
    IN ULONG            ulRequestID;
    IN HDRV_CALL        hdCall;
    OUT LINE_CALL_INFO  LineCallInfo;
} NDIS_TAPI_GET_CALL_INFO, *PNDIS_TAPI_GET_CALL_INFO;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which it should return information.
LineCallInfo
Specifies the virtual address variable-size data structure of type LINE_CALL_INFO. See ndistapi.h for details.

The miniport fills this structure with call-related information to complete this request successfully.

The structure at LineCallInfo has at least sizeof(LINE_CALL_INFO) bytes reserved.

The MiniportQueryInformation function can return one of the following: NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_CALL_STATUS

This OID requests the miniport to return the current status of the specified call.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_CALL_STATUS structure, defined as follows:

typedef struct _NDIS_TAPI_GET_CALL_STATUS {
    IN ULONG              ulRequestID;
    IN HDRV_CALL          hdCall;
    OUT LINE_CALL_STATUS  LineCallStatus;
} NDIS_TAPI_GET_CALL_STATUS, *PNDIS_TAPI_GET_CALL_STATUS;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which the current status should be returned.
LineCallStatus
Specifies the virtual address of a variable-size structure of type LINE_CALL_STATUS. See ndistapi.h for details.

The miniport fills this structure with call status information to complete this request successfully.

The structure at LineCallStatus has at least sizeof(LINE_CALL_STATUS) bytes reserved.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_DEV_CAPS

This OID requests the miniport to return the telephony capabilities of a specified line device. The returned information is valid for all addresses on the given line device.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_DEV_CAPS structure, defined as follows:

typedef struct _NDIS_TAPI_GET_DEV_CAPS {
    IN ULONG           ulRequestID;
    IN ULONG           ulDeviceID;
    IN ULONG           ulExtVersion;
    OUT LINE_DEV_CAPS  LineDevCaps;
} NDIS_TAPI_GET_DEV_CAPS, *PNDIS_TAPI_GET_DEV_CAPS;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the link or data channel to be queried. This was set up in a preceding OID_TAPI_GET_ID request.
ulExtVersion
Specifies the negotiated extension version number, if any.

The value has already been negotiated for this device through the OID_TAPI_NEGOTIATE_EXT_VERSION request. The value of this member is not validated when this request is made.

LineDevCaps
Specifies the virtual address of a variable-size structure of type LINE_DEV_CAPS. See ndistapi.h for details.

The NIC driver fills this structure with line device capabilities information to complete this request successfully.

The structure at LineDevCaps has at least sizeof(LINE_DEV_CAPS) bytes reserved.

The supplied provider information in the dwProviderInfoOffset member of LINE_DEV_CAPS must have the following format:
MediaName\0 ProviderName\0
where
MediaName is ISDN, SW56, FrameRelay, and so forth, as appropriate
and
ProviderName is the name of the manufacturer of the device and the unique name of the device, if any.

For example, the driver of an ISDN device from SomeIHV Inc. would set the dwProviderInfoOffset member with a pointer to the string “ISDN\0SomeIHV\0”

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_DEV_CONFIG

This OID requests the miniport to return the current configuration of a device associated one-to-one with a particular line.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_DEV_CONFIG structure, defined as follows:

typedef struct _NDIS_TAPI_GET_DEV_CONFIG {
    IN ULONG        ulRequestID;
    IN ULONG        ulDeviceID;
    IN ULONG        ulDeviceClassSize;
    IN ULONG        ulDeviceClassOffset;
    OUT VAR_STRING  DeviceConfig;
} NDIS_TAPI_GET_DEV_CONFIG, *PNDIS_TAPI_GET_DEV_CONFIG;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the line device to be queried. This value was set up in a preceding OID_TAPI_PROVIDER_INITIALIZE request.
ulDeviceClassSize
Specifies the number of bytes in the device-class string.
ulDeviceClassOffset
Specifies the offset in bytes to the zero-terminated, variable-size counted string specifying the class of the device whose configuration is requested. Valid device class strings are the same as those specified for the OID_TAPI_GET_ID request when it is applied to a “line” device (ulSelect has the value LINECALLSELECT_LINE).
DeviceConfig
Specifies the virtual address of a structure of type VAR_STRING to contain the device configuration information for the associated device.

The ulStringFormat member of the VAR_STRING structure must be set to STRINGFORMAT_BINARY.

The miniport fills this structure with the device configuration information to complete this request successfully.

In general, the contents of the NDIS_TAPI_GET_DEV_CONFIG structure are specific to the NIC driver’s line and the device class.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NNDIS_STATUS_TAPI_INVALDEVICECLASS
NDIS_STATUS_TAPI_STRUCTURETOOSMALL
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_ID

This OID requests the miniport to return a device ID for the specified device class associated with the selected line, address, or call.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_ID structure, defined as follows:

typedef struct _NDIS_TAPI_GET_ID {
    IN ULONG        ulRequestID;
    IN HDRV_LINE    hdLine;
    IN ULONG        ulAddressID;
    IN HDRV_CALL    hdCall;
    IN ULONG        ulSelect;
    IN ULONG        ulDeviceClassSize;
    IN ULONG        ulDeviceClassOffset;
    OUT VAR_STRING  DeviceID;
} NDIS_TAPI_GET_ID, *PNDIS_TAPI_GET_ID;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line.
ulAddressID
Specifies an address on the given open line device. The value of this member is not validated by NDISTAPI when this request is made.
hdCall
Specifies the miniport’s handle to the call.
ulSelect
Specifies whether the device ID is associated with the line, address, or a single call as one of the following:
LINECALLSELECT_LINE
Selects the specified line device. The hdLine member must contain a valid line handle; hdCall and ulAddressID are unused.
LINECALLSELECT_ADDRESS
Selects the specified address on the line. Both hdLine and ulAddressID must be valid; hdCall is unused.
LINECALLSELECT_CALL
Selects the specified call. The hdCall member must be valid; hdLine and ulAddressID are unused.
ulDeviceClassSize
Specifies the size in bytes of the zero-terminated, variable-size string specifying the device class of the device for which the configuration should be returned.
ulDeviceClassOffset
Specifies the offset in bytes to the device-class string.
DeviceID
Specifies the virtual address of a data structure of type VAR_STRING in which the the device ID should be returned.

The miniport fills this structure with the device ID to complete this request successfully.

Currently, there are two types of this request that must be supported by WAN NIC drivers:
1. IN DeviceClass = “ndis” // case insensitive
IN ulSelect = LINECALLSELECT_CALL
IN hdCall = ActiveCallHandle
OUT DeviceID = ConnectionWrapperID

DeviceID should be set to the NdisLinkContext handle returned by NDISWAN in the NDIS_MAC_LINE_UP structure for the initial NDIS_STATUS_WAN_LINE_UP indication to establish the link.

The miniport must make the initial line-up indication to establish a link (or open a data channel on a line) before returning from this request in order to supply this DeviceID value.

2. IN DeviceClass = “tapi/line”
IN ulSelect = LINECALLSELECT_LINE
IN hdLine = OpenLineHandle
OUT DeviceID = ulDeviceID

DeviceID will be set to the miniport-determined DeviceID associated with the line handle.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALADDRESSID
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_NODEVICE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_LINE_DEV_STATUS

This OID requests the miniport to return the current status for the specified open line device. The returned information is global to all addresses on the line.

Support for this request is mandatory.

This request uses an NDIS_TAPI_GET_LINE_DEV_STATUS structure, defined as follows:

typedef struct _NDIS_TAPI_GET_LINE_DEV_STATUS {
    IN ULONG             ulRequestID;
    IN HDRV_LINE         hdLine;
    OUT LINE_DEV_STATUS  LineDevStatus;
} NDIS_TAPI_GET_LINE_DEV_STATUS, *PNDIS_TAPI_GET_LINE_DEV_STATUS;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line to be queried.
LineDevStatus
Specifies a variable-sized structure of type LINE_DEV_STATUS. See ndistapi.h for details.

The miniport fills this structure with the line’s current device status to complete this request successfully.

The structure at LineDevStatus has at least sizeof(LINE_DEV_STATUS) bytes reserved.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_MAKE_CALL

This OID requests the miniport to place a call on the specified line to the specified destination address. Optionally, additional call parameters can be specified if the miniport is to use something other than default call-setup parameters.

Support for this request is mandatory.

This request uses an NDIS_TAPI_MAKE_CALL structure, defined as follows:

typedef struct _NDIS_TAPI_MAKE_CALL {
    IN ULONG             ulRequestID;
    IN HDRV_LINE         hdLine;
    IN HTAPI_CALL        htCall;
    OUT HDRV_CALL        hdCall;
    IN ULONG             ulDestAddressSize;
    IN ULONG             ulDestAddressOffset;
    IN BOOLEAN           bUseDefaultLineCallParams;
    IN LINE_CALL_PARAMS  LineCallParams;
} NDIS_TAPI_MAKE_CALL, *PNDIS_TAPI_MAKE_CALL;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line on which the new call is to be originated.
htCall
Specifies NDISTAPI’s handle to the call.

The miniport must save this handle for use in subsequent notifications, in particular the initial NDIS_MAC_LINE_UP notification to establish a link or data channel.

hdCall
Specifies the miniport’s handle to the call. The miniport must fill this member with its handle for the call before completing this request successfully. This handle is ignored if the miniport returns an error status.
ulDestAddressSize
Specifies the size in bytes of the variable-size buffer at ulDestAddressOffset containing the destination address.

This member can be zero for undialed addresses, such as for a hot phone, which always automatically connects to a predefined number, or when dialing will be done subsequently with an OID_TAPI_DIAL request.

ulDestAddressOffset
Specifies the offset in bytes from the beginning of this structure to the destination address. This destination addresss is in standard dialable number format. This member should be zero if ulDestAddressSize is zero.

If dialing will be done subsequently with an OID_TAPI_DIAL request, the driver should allocate an available call appearance on receipt of this request and remain in the dialtone state until dialing begins. Drivers of NICs that have inverse multiplexing capabilities can allow an application to specify many addresses at once.

bUseDefaultLineCallParams
Specifies whether the miniport should use the default call-setup parameters or the parameters specified in LineCallParams when making the call. If this member is FALSE, the driver should ignore LineCallParams.
LineCallParams
The LINE_CALL_PARAMS structure at this member specifies how the call should be set up. It selects elements such as the call’s bearer mode, data rate, expected media mode, origination address, blocking of caller ID information, and dialing parameters. See ndistapi.h for details.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_ADDRESSBLOCKED
NDIS_STATUS_TAPI_BEARERMODEUNAVAIL
NDIS_STATUS_TAPI_CALLUNAVAIL
NDIS_STATUS_TAPI_DIALBILLING
NDIS_STATUS_TAPI_DIALQUIET
NDIS_STATUS_TAPI_DIALDIALTONE
NDIS_STATUS_TAPI_DIALPROMPT
NDIS_STATUS_TAPI_INUSE
NDIS_STATUS_TAPI_INVALBEARERMODE
NDIS_STATUS_TAPI_INVALMEDIAMODE
NDIS_STATUS_TAPI_INVALLINESTATE
NDIS_STATUS_TAPI_INVALRATE
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALADDRESS
NDIS_STATUS_TAPI_INVALADDRESSID
NDIS_STATUS_TAPI_INVALCALLPARAMS
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_TAPI_RATEUNAVAIL
NDIS_STATUS_TAPI_USERUSERINFOTOOBIG
NDIS_STATUS_FAILURE

OID_TAPI_OPEN

This OID requests the miniport to open the line device designated by the given device ID and return the NIC driver’s handle for the device.

Support for this request is mandatory.

This request uses an NDIS_TAPI_OPEN structure, defined as follows:

typedef struct _NDIS_TAPI_OPEN {
    IN ULONG       ulRequestID;
    IN ULONG       ulDeviceID;
    IN HTAPI_LINE  htLine;
    OUT HDRV_LINE  hdLine;
} NDIS_TAPI_OPEN, *PNDIS_TAPI_OPEN;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the line device to be queried. This was set up in a preceding OID_TAPI_PROVIDER_INITIALIZE request.
htLine
Specifies NDISTAPI’s handle to the line device to be used in subsequent calls to the LINE_EVENT callback procedure to identify the line
hdLine
Specifies the miniport NIC driver’s handle to the line device.

The NIC driver must fill this location with its handle for the line device to complete this request successfully. This handle is ignored if the miniport completes the request with an error status.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_ALLOCATED
NDIS_STATUS_TAPI_INVALMEDIAMODE
NDIS_STATUS_FAILURE

The miniport must retain the htLine handle for the device provided by NDISTAPI for use in subsequent NDIS_TAPI_EVENT notifications using the LINE_EVENT structure.

OID_TAPI_PROVIDER_INITIALIZE

This OID requests that the miniport initialize itself as a TAPI provider. The driver should set up whatever internal state it uses to track incoming requests from TAPI.

Support for this request is mandatory.

This request uses an NDIS_TAPI_PROVIDER_INITITIALIZE structure, defined as follows:

typedef struct _NDIS_TAPI_PROVIDER_INITIALIZE {
    IN ULONG   ulRequestID;
    IN ULONG   ulDeviceIDBase;
    OUT ULONG  ulNumLineDevs;
    OUT ULONG  ulProviderID;
} NDIS_TAPI_PROVIDER_INITIALIZE, *PNDIS_TAPI_PROVIDER_INITIALIZE;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceIDBase
Specifies the lowest device ID for the line devices supported by the miniport. Subsequent to this request, the driver uses this value as the zero-based line device identifier value. That is, if ulNumLineDevs is greater than one, the ulDeviceID values subsequently passed in requests such as OID_TAPI_OPEN, contains this value for the first line, this value plus one for the second line, and so forth.
ulNumLineDevs
Specifies the number of line devices supported by the miniport to complete this request successfully.
ulProviderID
Specifies an ID for the WAN NIC driver if this request is completed successfully. This value must be unique within the set of WAN drivers that support TAPI; the NdisMiniportContext handle passed in to the MiniportInitialize function can be used to provide a guaranteed unique value.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_CONFIG_DIALOG

This OID requests the miniport to return the name of a user-mode dynamic-link library that can be loaded and called to configure the specified device.

Such a configuration DLL exports the following function by name:

LONG
WINAPI
ConfigDialog(
IN HWND hwndOwner,
IN ULONG ulDeviceID,
IN LPCSTR lpszDeviceClass
);

Support for this request is optional. This request is not made by RAS.

The NDIS_TAPI_CONFIG_DIALOG structure, defined as follows, is used with this OID to retrieve the name of a user-mode dynamic link library that can be called to configure the specified device.

typedef struct _NDIS_TAPI_CONFIG_DIALOG {
    IN ULONG  ulRequestID;
    IN ULONG  ulDeviceID;
    IN ULONG  ulDeviceClassSize;
    IN ULONG  ulDeviceClassOffset;
    IN ULONG  ulLibraryNameTotalSize;
    OUT ULONG ulLibraryNameNeededSize;
    OUT CHAR  szLibraryName[1];
} NDIS_TAPI_CONFIG_DIALOG, *PNDIS_TAPI_CONFIG_DIALOG;

The members of this structure contain the following information:

    ulRequestID

    Reserved.

ulDeviceID
Specifies the line device to be configured. This was set up in a preceding OID_TAPI_PROVIDER_INITIALIZE request.
ulDeviceClassSize
Specifies the size in bytes of the device-class string.
ulDeviceClassOffset
Specifies the offset in bytes to the zero-terminated, variable-size counted string specifying the class of the device whose configuration is requested. Valid device class strings are the same as those specified for the OID_TAPI_GET_ID request when it is applied to a “line” device (ulSelect has the value LINECALLSELECT_LINE).
ulLibraryNameTotalSize
Specifies the size in bytes of the szLibraryName member.
ulLibraryNameNeededSize
Specifies the size in bytes that is needed to hold all the returned information.
szLibraryName
Specifies space for a zero-terminated string naming a configuration DLL for the requested device. Names not containing a complete path or an extension will be subject to the rules used by the Win32 LoadLibrary function for determining default paths and extensions. If the miniport determines this buffer is too small to hold all the information, it should specify the necessary size in the ulLibraryNameNeededSize member and return an NDIS_STATUS_TAPI_STRUCTURETOOSMALL error.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_STRUCTURETOOSMALL
NDIS_STATUS_TAPI_INVALDEVICECLASS
NDIS_STATUS_TAPI_OPERATIONUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_DEV_SPECIFIC

This OID requests the miniport to provide access to features not accessible through other OID requests. The meaning of the extensions are device-specific, so the application must be fully aware of them to take advantage of these extensions.

Support for this request is optional.

This request uses an NDIS_TAPI_DEV_SPECIFIC structure, defined as follows, to query and provide access to features not specified in other TAPI OID requests:

typedef struct _NDIS_TAPI_DEV_SPECIFIC {
    IN ULONG      ulRequestID;
    IN HDRV_LINE  hdLine;
    IN ULONG      ulAddressID;
    IN HDRV_CALL  hdCall;
    IN OUT ULONG  ulParamsSize;
    IN OUT UCHAR  Params[1];
} NDIS_TAPI_DEV_SPECIFIC, *PNDIS_TAPI_DEV_SPECIFIC;

The members of this structure contain the following device-specific information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line to be operated on.
ulAddressID
Specifies the address on the specified line to be operated on.
hdCall
Specifies the miniport’s handle to the call to be operated on. This member can be NULL.
ulParamsSize
Specifies the number of bytes in the following parameter block. On entry, this member will contain the size in bytes of the parameter block passed in to the miniport.

If the miniport returns information in the parameter block, it should set this member to the size in bytes of the returned information, which cannot exceed the input ulParamsSize. Otherwise, the miniport should set this member to zero to indicate that it is returning no information.

Params
Specifies a device-specific parameter block.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALADDRESSID
NDIS_STATUS_TAPI_OPERATIONUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_GET_EXTENSION_ID

This OID requests the miniport to return the extension ID for the specified line device.

Support for this request is optional.

This request uses an NDIS_TAPI_GET_EXTENSION_ID structure, defined as follows:

typedef struct _NDIS_TAPI_GET_EXTENSION_ID {
    IN ULONG               ulRequestID;
    IN ULONG               ulDeviceID;
    OUT LINE_EXTENSION_ID  LineExtensionID;
} NDIS_TAPI_GET_EXTENSION_ID, *PNDIS_TAPI_GET_EXTENSION_ID;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the link or data channel to be queried. This was set up in a preceding OID_TAPI_GET_ID request.
LineExtensionID
Points to a structure of type LINE_EXTENSION_ID. See ndistapi.h for details.

If the miniport supports device-specific extensions, it fills this structure with the extension ID of these extensions. Otherwise, it fills this structure with zeros.

For a bound protocol, a valid extension ID cannot consist of all zeros.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_NEGOTIATE_EXT_VERSION

This OID requests the miniport to return the highest version extension number the miniport is willing to operate under for this device, given the range of possible extension versions.

Support for this request is optional.

This request uses an NDIS_TAPI_NEGOTIATE_EXT_VERSION structure, defined as follows:

typedef struct _NDIS_TAPI_NEGOTIATE_EXT_VERSION {
    IN ULONG   ulRequestID;
    IN ULONG   ulDeviceID;
    IN ULONG   ulLowVersion;
    IN ULONG   ulHighVersion;
    OUT ULONG  ulExtVersion;
} NDIS_TAPI_NEGOTIATE_EXT_VERSION, *PNDIS_TAPI_NEGOTIATE_EXT_VERSION;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the link or data channel for which the extension version negotiation is to be performed. The value INITIALIZE_NEGOTIATION cannot be used. This was set up in a preceding OID_TAPI_GET_ID request.
ulLowVersion
Specifies the lowest extension version under which NDISTAPI or its client application will operate. The most significant two bytes specify the major version and the least significant two bytes specify the minor version. The value of this member is not validated by NDISTAPI when this request is made.
ulHighVersion
Specifies the highest extension version under which NDISTAPI or its client application will operate. The most significant two bytes specify the major version and the least significant two bytes specify the minor version. The value of this member is not validated by NDISTAPI when this request is made.
ulExtVersion
Specifies the miniport’s extension version within the range specified by ulLowVersion and ulHighVersion. The most significant two bytes specifiy the major version and the least significant two bytes specify the minor version.

The miniport fills this member with the highest extension version number, within the range requested by the caller, under which it is willing to operate to complete this request successfully.

If the requested range specified in ulLowVersion and ulHighVersion does not overlap the range supported by the miniport, it returns NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION.

The MiniportQueryInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

TAPI Set Information Requests

WAN miniports return no information for set requests other than the completion status. All OID-specific structures are provided by the protocol driver, and their members are read-only to the underlying WAN NIC miniport.

OID_TAPI_ANSWER

This OID requests the miniport to answer the specified call on offer. Optionally, it requests that the miniport send the specified user-to-user information to the calling party.

Support for this request is mandatory.

The OID_TAPI_ANSWER request uses an NDIS_TAPI_ANSWER structure, defined as follows:

typedef struct _NDIS_TAPI_ANSWER {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulUserUserInfoSize;
    IN UCHAR      UserUserInfo[1];
} NDIS_TAPI_ANSWER, *PNDIS_TAPI_ANSWER;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call to be answered. The driver set this handle up when it called NdisMIndicateStatus with the LINE_EVENT_NEWCALL indication.
ulUserUserInfoSize
Specifies the size in bytes of the user-to-user information in UserUserInfo. If this member is zero, UserUserInfo should be ignored .
UserUserInfo
Specifies a string containing user-to-user information to be sent to the remote party as part of the call accept. User-to-user information is sent only if supported by the underlying network (see OID_TAPI_GET_DEV_CAPS).

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_TAPI_USERUSERINFOTOOBIG
NDIS_STATUS_FAILURE

Upon successful completion of the OID_TAPI_ANSWER request, the miniport must be prepared to respond to the OID_TAPI_GET_ID request.

The initiating application must wait for the LINECALLSTATE_CONNECTED indication before actually writing data.

OID_TAPI_CLOSE

This OID requests the miniport to close the specified open line device.

Support for this request is mandatory.

This request uses an NDIS_TAPI_CLOSE structure, defined as follows:

typedef struct _NDIS_TAPI_CLOSE {
    IN ULONG      ulRequestID;
    IN HDRV_LINE  hdLine;
} NDIS_TAPI_CLOSE, *PNDIS_TAPI_CLOSE;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line to be closed. After the line has been successfully closed, this handle is no longer valid.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING

OID_TAPI_CLOSE_CALL

This OID requests the miniport to close a specified call after completing or terminating all outstanding asynchronous requests on the call.

Support for this request is mandatory.

This request uses an NDIS_TAPI_CLOSE_CALL structure, defined as follows:

typedef struct _NDIS_TAPI_CLOSE_CALL {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
} NDIS_TAPI_CLOSE_CALL, *PNDIS_TAPI_CLOSE_CALL;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call to be closed. After the call has been successfully closed this handle is no longer valid.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING

OID_TAPI_CONDITIONAL_MEDIA_DETECTION

NDISTAPI makes this OID request to NDISWAN, which passes it down to WAN NIC miniports, whenever a client application uses LINEMAPPER as the dwDeviceID in the lineOpen function to request that lines be scanned to find one that supports the desired media mode(s) and call parameters.

NDISTAPI scans based on the union of the desired media modes and the other media modes currently being monitored on the line, to give each WAN miniport the opportunity to indicate whether it can simultaneously monitor for all of the requested media modes.

Support for this request is mandatory.

This request uses an NDIS_TAPI_CONDITIONAL_MEDIA_DETECTION structure, defined as follows:

typedef struct _NDIS_TAPI_CONDITIONAL_MEDIA_DETECTION {
    IN ULONG             ulRequestID;
    IN HDRV_LINE         hdLine;
    IN ULONG             ulMediaModes;
    IN LINE_CALL_PARAMS  LineCallParams;
} NDIS_TAPI_CONDITIONAL_MEDIA_DETECTION,
    *PNDIS_TAPI_CONDITIONAL_MEDIA_DETECTION;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the the miniport’s handle to the line on which media monitoring and parameter capabilites are to be set.
ulMediaModes
Specifies the media mode(s) currently of interest to the calling application. This parameter uses the LINEMEDIAMODE constants. See ndistapi.h for details.
LineCallParams
The LINE_CALL_PARAMS structure specifies information that the line device should provide. The only relevent members for the purpose of this request are the following:

ULONG ulBearerMode;

ULONG ulMinRate;

ULONG ulMaxRate;

ULONG ulMediaMode;

ULONG ulCallParamFlags;

ULONG ulAddressMode;

If the value of ulAddressMode is LINEADDRESSMODE_ADDRESSID, any address on the line is acceptable. Otherwise, the value of ulAddressMode is LINEADDRESSMODE_DIALABLEADDR, indicating either that the miniport should search for a specific originating address (phone number), or, if it is a provider-specific extension, the ulOrigAddressSize/Offset and the ulDevSpecificSize/Offset values are also relevant. All other members of the LINE_CALL_PARAMS structure are irrelevant to this OID request.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_TAPI_INVALMEDIAMODE
NDIS_STATUS_FAILURE

If a miniport can monitor for the specified set of media modes and support the capabilities specified in CallParams, it completes the request with NDIS_STATUS_SUCCESS, but it leaves the active media monitoring modes for the line unchanged.

OID_TAPI_DROP

This OID requests the miniport to drop or disconnect the specified call.

User-to-user information can optionally be transmitted as part of the call disconnect. This request can be made by the application at any time.

Support for this request is mandatory.

This request uses an NDIS_TAPI_DROP structure, defined as follows:

typedef struct _NDIS_TAPI_DROP {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulUserUserInfoSize;
    IN UCHAR      UserUserInfo[1];
} NDIS_TAPI_DROP, *PNDIS_TAPI_DROP;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call to be dropped.
ulUserUserInfoSize
Specifies the size in bytes of the user-to-user information in UserUserInfo. If this member is zero, the miniport should ignore the next member.
UserUserInfo
Specifies a string containing user-to-user information to be sent to the remote party as part of the call drop. User-to-user information is sent only if supported by the underlying network (see OID_TAPI_GET_DEV_CAPS).

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_USERUSERINFOTOOBIG
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

When a miniport completes this request successfully, the call should be in the idle state. However, the miniport can still receive requests, such as statistics queries, specifying the hdCall handle until it completes the OID_TAPI_CLOSE_CALL request for this handle.

OID_TAPI_PROVIDER_SHUTDOWN

This OID requests the miniport to shut down its TAPI operations when TAPI is cleaning up its state because applications are no longer using TAPI services.

On receipt of this OID, the miniport should terminate any activities associated with TAPI that it has in progress, call NdisMIndicateStatus with NDIS_MAC_LINE_DOWN to NDISWAN for any lines currently open, and clean up any state it was using to track TAPI operations.

Support for this request is mandatory.

This request uses an NDIS_TAPI_PROVIDER_SHUTDOWN structure, defined as follows:

typedef struct _NDIS_TAPI_PROVIDER_SHUTDOWN {
    IN ULONG  ulRequestID;
} NDIS_TAPI_PROVIDER_SHUTDOWN, *PNDIS_TAPI_PROVIDER_SHUTDOWN;

The member of this structure contains the following information:

ulRequestID
Reserved.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SET_APP_SPECIFIC

This OID requests the miniport to set the ulAppSpecific member of the specified call’s LINECALLINFO structure. See ndistapi.h for details of this structure.

Support for this request is mandatory.

This request uses an NDIS_TAPI_SET_APP_SPECIFIC structure, defined as follows:

typedef struct _NDIS_TAPI_SET_APP_SPECIFIC {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulAppSpecific;
} NDIS_TAPI_SET_APP_SPECIFIC, *PNDIS_TAPI_SET_APP_SPECIFIC;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which the application-specific member is to be set.
ulAppSpecific
Specifies the new content of the ulAppSpecific member for the call’s LINECALLINFO structure. This value is uninterpreted by the NIC driver. The value is not validated by NDISTAPI when this request is made.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SET_CALL_PARAMS

This OID requests the miniport to set certain call parameters for an existing call.

Support for this request is mandatory.

This request uses an NDIS_TAPI_SET_CALL_PARAMS structure, defined as follows:

struct _NDIS_TAPI_SET_CALL_PARAMS {
    IN ULONG             ulRequestID;
    IN HDRV_CALL         hdCall;
    IN ULONG             ulBearerMode;
    IN ULONG             ulMinRate;
    IN ULONG             ulMaxRate;
    IN BOOLEAN           bSetLineDialParams;
    IN LINE_DIAL_PARAMS  LineDialParams;
} NDIS_TAPI_SET_CALL_PARAMS, *PNDIS_TAPI_SET_CALL_PARAMS;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which the following parameters are to be changed.
ulBearerMode
Specifies the new bearer mode for the call. This member can have only one of the following LINEBEARERMODE flags set:

LINEBEARERMODE_VOICE

LINEBEARERMODE_SPEECH

LINEBEARERMODE_MULTIUSE

LINEBEARERMODE_DATA

LINEBEARERMODE_ALTSPEECHDATA

LINEBEARERMODE_NONCALLSIGNALING

ulMinRate
Specifies a lower bound for the call’s new data rate. The value of this member is not validated by NDISTAPI before the miniport is called.

This value indicates that the caller is willing to accept a new rate as low as this one.

ulMaxRate
Specifies an upper bound for the call’s new data rate. The value of this member is not validated by NDISTAPI before the miniport is called.

This value indicates the maximum data rate the caller would like.

Equal values for the ulMinRate and ulMaxRate members indicate that the caller requires an exact rate.

bSetLineDialParams
Specifies whether the following dialing parameters should be used for the call. If this member is FALSE, the call’s current dialing parameters should be used and the next member should be ignored.
LineDialParams
This structure specifies the new dial parameters for the call. See ndistapi.h for details.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_INVALBEARERMODE
NDIS_STATUS_TAPI_BEARERMODEUNAVAIL
NDIS_STATUS_TAPI_INVALRATE
NDIS_STATUS_TAPI_RATEUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SET_DEFAULT_MEDIA_DETECTION

This OID requests the miniport to use a new set of media modes to detect for the indicated line, thereby replacing whatever set it was using.

The miniport must not notify NDISWAN of an incoming call unless this request has been successfully completed for the incoming call’s media mode.

Support for this request is mandatory.

This request uses an NDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION structure, defined as follows:

typedef struct _NDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION {
    IN ULONG      ulRequestID;
    IN HDRV_LINE  hdLine;
    IN ULONG      ulMediaModes;
} NDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION,
    *PNDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line for which the default media mode should be set.
ulMediaModes
Specifies the media mode(s) of interest to NDISTAPI. This member is set with one or more of the LINEMEDIAMODE constants. See ndistapi.h for details.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALMEDIAMODE
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SET_DEV_CONFIG

This OID requests the miniport to restore the configuration of a device associated one-to-one with the line device from a configuration previously obtained using OID_TAPI_GET_DEV_CONFIG.

The contents of this structure are specific to the miniport’s line and device class.

Support for this request is mandatory.

This request uses an NDIS_TAPI_SET_DEV_CONFIG structure, defined as follows:

typedef struct _NDIS_TAPI_SET_DEV_CONFIG {
    IN ULONG  ulRequestID;
    IN ULONG  ulDeviceID;
    IN ULONG  ulDeviceClassSize;
    IN ULONG  ulDeviceClassOffset;
    IN ULONG  ulDeviceConfigSize;
    IN UCHAR  DeviceConfig[1];
} NDIS_TAPI_SET_DEV_CONFIG, *PNDIS_TAPI_SET_DEV_CONFIG;

The members of this structure contain the following information:

ulRequestID
Reserved.
ulDeviceID
Specifies the line device to be configured. This value was set up in a preceding OID_TAPI_PROVIDER_INITIALIZE request.
ulDeviceClassSize
Specifies the size in bytes of the device class for the device whose configuration is to be restored. Valid device class strings are the same as those specified for the OID_TAPI_GET_ID request when it is applied to a “line” device (ulSelect has the value LINECALLSELECT_LINE).
ulDeviceClassOffset
Specifies the offset in bytes to the device class of the device whose configuration is to be restored.
ulDeviceConfigSize
Specifies the number of bytes at DeviceConfig. This value was already returned in the ulStringSize member in the VAR_STRING structure returned for a preceding OID_TAPI_GET_DEV_CONFIG request.
DeviceConfig
Specifies data that was returned in the variable portion of the VAR_STRING structure for a preceding OID_TAPI_GET_DEV_CONFIG request.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALDEVICECLASS
NDIS_STATUS_TAPI_INVALPARAM
NDIS_STATUS_TAPI_NODRIVER
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SET_MEDIA_MODE

This OID requests the miniport to change a call’s media mode as stored in the LINE_CALL_INFO structure for that call. See ndistapi.h for the declaration of LINE_CALL_INFO.

Support for this request is mandatory.

This request uses an NDIS_TAPI_SET_MEDIA_MODE structure, defined as follows:

typedef struct _NDIS_TAPI_SET_MEDIA_MODE {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulMediaMode;
} NDIS_TAPI_SET_MEDIA_MODE, *PNDIS_TAPI_SET_MEDIA_MODE;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which it should change the media mode.
ulMediaMode
Specifies the new media mode(s) for the call as one or more of the LINEMEDIAMODE_XXX flags.

If the LINEMEDIAMODE_UNKNOWN flag is set, other media mode flags also can be set. Such a combination of flags specifies that the call’s media mode is not fully determined, but that it has been narrowed down to a subset of possible media modes. If the LINEMEDIAMODE_UNKNOWN flag is clear, only a single media mode can be specified.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALMEDIAMODE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SET_STATUS_MESSAGES

This OID requests the miniport to allow NDISTAPI to specify which notification messages the miniport should generate for events related to status changes for the specified line or any of its addresses.

By default, address-status and line-status reporting is initially disabled for a line.

Support for this request is mandatory.

This request uses an NDIS_TAPI_SET_STATUS_MESSAGES structure, defined as follows:

typedef struct _NDIS_TAPI_SET_STATUS_MESSAGES {
    IN ULONG      ulRequestID;
    IN HDRV_LINE  hdLine;
    IN ULONG      ulLineStates;
    IN ULONG      ulAddressStates;
} NDIS_TAPI_SET_STATUS_MESSAGES, *PNDIS_TAPI_SET_STATUS_MESSAGES;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the miniport’s handle to the line device.
ulLineStates
Specifies a bit array that identifies status changes for which messages should be sent to NDISTAPI. This parameter uses the LINEDEVSTATE constants. See ndistapi.h for details.
ulAddressStates
Specifies a bit array that identifies which address status changes the miniport should monitor to send appropriate notifications to NDISTAPI. The value is an ORed combination of LINEADDRESSSTATE flags. See ndistapi.h for details.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALLINEHANDLE
NDIS_STATUS_TAPI_INVALLINESTATE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_ACCEPT

This OID requests the miniport to accept the specified call. Optionally, it requests that the miniport send the specified user-to-user information to the calling party.

Support for this request is optional.

The request uses an NDIS_TAPI_ACCEPT structure, defined as follows:

typedef struct _NDIS_TAPI_ACCEPT {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulUserUserInfoSize;
    IN UCHAR      UserUserInfo[1];
} NDIS_TAPI_ACCEPT, *PNDIS_TAPI_ACCEPT;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call to be accepted. The driver set this handle up when it called NdisMIndicateStatus with the LINE_EVENT_NEWCALL indication.
ulUserUserInfoSize
Specifies the size in bytes of the user-to-user information in UserUserInfo. If this member is zero, the miniport should ignore the next member.
UserUserInfo
Specifies a string containing user-to-user information to be sent to the remote party as part of the call-accept operation. User-to-user information is sent only if supported by the underlying network (see OID_TAPI_GET_DEV_CAPS).

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_TAPI_USERUSERINFOTOOBIG
NDIS_STATUS_FAILURE

OID_TAPI_DIAL

This OID requests the miniport to dial the specified number on the specified call.

Support for this request is optional.

This request uses an NDIS_TAPI_DIAL structure, defined as follows:

typedef struct _NDIS_TAPI_DIAL {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulDestAddressSize;
    IN CHAR       szDestAddress[1];
} NDIS_TAPI_DIAL, *PNDIS_TAPI_DIAL;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call for which it should dial. This handle was set up in a preceding OID_TAPI_MAKE_CALL request.
ulDestAddressSize
Specifies the size in bytes of the address in the next member.
szDestAddress
Specifies the destination to be dialed in standard dialable number format.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALADDRESS
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_ADDRESSBLOCKED
NDIS_STATUS_TAPI_DIALBILLING
NDIS_STATUS_TAPI_DIALQUIET
NDIS_STATUS_TAPI_DIALDIALTONE
NDIS_STATUS_TAPI_DIALPROMPT
NDIS_STATUS_TAPI_OPERATIONUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SECURE_CALL

This OID requests the miniport to secure a particular call from any interruptions or interference that can affect the call’s media stream.

Support for this request is optional.

This request uses an NDIS_TAPI_SECURE_CALL structure, defined as follows:

typedef struct _NDIS_TAPI_SECURE_CALL {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
} NDIS_TAPI_SECURE_CALL, *PNDIS_TAPI_SECURE_CALL;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call to be secured.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_OPERATIONUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SELECT_EXT_VERSION

This OID requests the miniport to select the indicated extension version for the indicated line device. Subsequent requests on the line operate according to the selected extension version.

Support for this request is optional.

This request uses an NDIS_TAPI_SELECT_EXT_VERSION structure, defined as follows:

typedef struct _NDIS_TAPI_SELECT_EXT_VERSION {
    IN ULONG      ulRequestID;
    IN HDRV_LINE  hdLine;
    IN ULONG      ulExtVersion;
} NDIS_TAPI_SELECT_EXT_VERSION, *PNDIS_TAPI_SELECT_EXT_VERSION;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdLine
Specifies the handle to the line for which the extension version is to be selected.
ulExtVersion
Specifies the extension version to be selected. The most significant two bytes specify the major version number, and the least significant two bytes specify the minor version number. Zero indicates the miniport should cancel the current selection.

This version has been negotiated in a preceding OID_TAPI_NEGOTIATE_EXT_VERSION request.

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION
NDIS_STATUS_TAPI_OPERATIONUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE

OID_TAPI_SEND_USER_USER_INFO

This OID requests the miniport to send user-to-user information to the remote party on the specified call.

Support for this request is optional.

This request uses an NDIS_TAPI_SEND_USER_USER_INFO structure, defined as follows:

typedef struct _NDIS_TAPI_SEND_USER_USER_INFO {
    IN ULONG      ulRequestID;
    IN HDRV_CALL  hdCall;
    IN ULONG      ulUserUserInfoSize;
    IN UCHAR      UserUserInfo[1];
} NDIS_TAPI_SEND_USER_USER_INFO, *PNDIS_TAPI_SEND_USER_USER_INFO;

The members of this structure contain the following information:

ulRequestID
Reserved.
hdCall
Specifies the miniport’s handle to the call on which to send user-to-user information.
ulUserUserInfoSize
Specifies the size in bytes of the UserUserInfo string.
UserUserInfo
Specifies a string containing user-to-user information to be sent to the remote party as part of a call-accept operation. User-to-user information is sent only if supported by the underlying network (see OID_TAPI_GET_DEV_CAPS).

The MiniportSetInformation function can return one of the following:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING
NDIS_STATUS_TAPI_INVALCALLHANDLE
NDIS_STATUS_TAPI_INVALCALLSTATE
NDIS_STATUS_TAPI_USERUSERINFOTOOBIG
NDIS_STATUS_TAPI_OPERATIONUNAVAIL
NDIS_STATUS_TAPI_RESOURCEUNAVAIL
NDIS_STATUS_FAILURE


5.9 Wireless Objects

The following chart summarizes the OIDs used to get or set the general operational characteristics of wireless WAN miniports and/or their NICs.

Currently, there are no defined statistics OIDs for the wireless WAN medium.
Q | S | Indication | Name
M |  |  | OID_WW_GEN_NETWORK_TYPES_SUPPORTED
M | M | M | OID_WW_GEN_NETWORK_TYPE_IN_USE
M |  |  | OID_WW_GEN_HEADER_FORMATS_SUPPORTED
M | M | M | OID_WW_GEN_HEADER_FORMAT_IN_USE
 | M |  | OID_WW_GEN_INDICATION_REQUEST
M |  |  | OID_WW_GEN_DEVICE_INFO
M | M | M | OID_WW_GEN_OPERATION_MODE
M |  | M | OID_WW_GEN_LOCK_STATUS
M | M | M | OID_WW_GEN_DISABLE_TRANSMITTER
M | M | M | OID_WW_GEN_NETWORK_ID
M |  |  | OID_WW_GEN_PERMANENT_ADDRESS
M | M | M | OID_WW_GEN_CURRENT_ADDRESS
M | M | M | OID_WW_GEN_SUSPEND_DRIVER
M |  | M | OID_WW_GEN_BASESTATION_ID
M |  | M | OID_WW_GEN_CHANNEL_ID
M |  |  | OID_WW_GEN_ENCRYPTION_SUPPORTED
M | M | M | OID_WW_GEN_ENCRYPTION_IN_USE
M | M | M | OID_WW_GEN_ENCRYPTION_STATE
M |  | M | OID_WW_GEN_CHANNEL_QUALITYY
M |  | M | OID_WW_GEN_REGISTRATION_STATUS
M |  |  | OID_WW_GEN_RADIO_LINK_SPEED
M |  |  | OID_WW_GEN_LATENCY
M |  | M | OID_WW_GEN_BATTERY_LEVEL
M |  | M | OID_WW_GEN_EXTERNAL_POWER

The following describes each of the preceding OIDs and their respective associated structures more fully.

References for the set of network-type-dependent OID_WW_XXX follow those for the OID_WW_GEN_XXX.

Wireless Operational Characteristics

Unless otherwise specified, the requesting protocol must allocate all buffer space necessary to complete OID requests. Underlying NDIS drivers need only copy the data into the supplied buffers.

To account for differences in hardware architectures and structure packing, structure members must be aligned on an 8-byte (NDIS-defined type NDIS_PHYSICAL_ADDRESS) boundary and the sizeof operator in “C” (or an analogous function) must be used to calculate the lengths of structure elements.

Some network devices support external interfaces to configure the device. Therefore, it is possible for some objects to change dynamically, which is outside the control of the underlying miniport.

NDIS_VAR_DATA_DESC

Many of these OIDs have associated structures with members of type NDIS_VAR_DATA_DESC, defined as follows:

typedef struct  _NDIS_VAR_DATA_DESC {
    USHORT  Length;           // number of bytes of data in buffer
    USHORT  MaximumLength;    // total size of buffer in bytes
    LONG    Offset;           // offset in bytes, relative to struct
} NDIS_VAR_DATA_DESC, *PNDIS_VAR_DATA_DESC;

This structure defines a type used to describe buffer space relative to the base virtual address of the NDIS_VAR_DATA_DESC structure itself. In other words, a pointer to a buffer is not explicitly provided by this type; only the offset within the buffer space is provided. Because this descriptor does not use an actual pointer, a user-mode application can allocate a buffer together with its descriptor of this type and provide an offset within this buffer relative to the base virtual address of the descriptor. This allows structures containing these descriptors to be passed between user mode and kernel mode without address mapping. An Offset outside the range sizeof(NDIS_VAR_DATA_DESC)) is invalid.

Queries that return information of type NDIS_VAR_DATA_DESC should zero terminate buffers (that is, set the Offset member of this structure to NULL) and return zero as the buffer size if the requested information is unavailable. For example, if the revision number of the software/firmware in the device is unknown, a wireless WAN miniport should return zero for the Length and NULL for the Offset at SWVersionNum for an OID_WW_GEN_DEVICE_INFO query.

The definition of each OID_WW_GEN_XXX that follows describes the data type at InformationBuffer in the NDIS_REQUEST structure passed in to the MiniportQueryInformation or MiniportSetInformation function.

OID_WW_GEN_NETWORK_TYPES_SUPPORTED

For this OID, a miniport returns an array of types supported by the driver and its NIC that has any proper subset of the following system-defined values:

NdisWWMobitex
NdisWWPinpoint
NdisWWCDPD
NdisWWArdis
NdisWWDataTAC
NdisWWMetricom
NdisWWGSM
NdisWWCDMA
NdisWWTDMA
NdisWWAMPS
NdisWWInmarsat
NdisWWpACT
NdisWWGeneric

Any miniport that cannot claim full compliance with any particular type from the preceding list should return NdisWWGeneric for this query. Otherwise, the miniport should return a list of the particular type(s) for which it can provide fully compliant support.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_NETWORK_TYPE_IN_USE

As a query, this OID requests MiniportQueryInformation to return the current NDIS_WW_NETWORK_TYPE used by the NIC driver. See OID_WW_GEN_NETWORK_TYPES_SUPPORTED for possible values.

As a set, this OID requests that MiniportSetInformation set the NIC and itself to use the specified NDIS_WW_NETWORK_TYPE value. If a miniport supports only a single type of network, it sets this type as the network default so a bound protocol is unlikely to make this OID request of such a miniport.
NdisWWGeneric should be specified in set requests to underlying drivers that do not claim full compliance with any of the enumerated network types in response to a query.

If the underlying miniport can support more than one type of network, a bound protocol should make this OID request to set the network type immediately following its successful call to NdisOpenAdapter and prior to calling NdisRequest with OID_WW_GEN_HEADER_FORMAT_IN_USE. Such a protocol should query the current operating network type prior to setting it. If the device is operating in the correct mode, then no further action is required other than registering for the OID_WW_GEN_INDICATION_REQUEST on return from the OID_WW_GEN_NETWORK_TYPE_IN_USE request.

When a miniport has completed a set operation requested by this OID, the network type has been set for all bound protocols.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the network type changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_HEADER_FORMATS_SUPPORTED

For this OID, a miniport returns an array of header formats the NIC driver can support as one or more of the following system-defined values:

NdisWWDIXEthernetFrames
NdisWWMPAKFrames
NdisWWRDLAPFrames
NdisWWMDC4800Frames

DIX Ethernet is the default header format. Every wireless WAN miniport driver must support this format. Any miniport can support additional formats. However, a miniport is likely to support at most one other format that is native to its NIC.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_HEADER_FORMAT_IN_USE

As a query, this OID requests MiniportQueryInformation to return the current NDIS_WW_HEADER_FORMAT it is using. See OID_WW_GEN_HEADER_FORMATS_SUPPORTED for possible values.

As a set, this OID requests MiniportSetInformation to use a particular header format that it returned in response to a preceding OID_WW_GEN_HEADER_FORMATS_SUPPORTED request. If no bound protocol requests a set, the miniport uses DIX-Ethernet framing by default.

If the underlying miniport can support more than one type of header, a bound protocol should make this OID request immediately following its successful call to NdisRequest with OID_WW_GEN_NETWORK_TYPE_IN_USE. A successful set request changes the header format in use for all bound protocols.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the header format changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_INDICATION_REQUEST

This OID requests MiniportSetInformation to place the specified indication request on the driver’s event queue.

This OID allows bound protocols to register themselves to be notified of changes associated with particular wireless OIDs. Such a protocol passes an NDIS_WW_INDICATION_REQUEST structure to NdisRequest that specifies an OID_WW_GEN_XXX code and, for some OID codes, a trigger value. On return from such an OID_WW_GEN_INDICATION_REQUEST, the current value of the specified OID code will be in the InitialValue member, unless otherwise stated in the definition for the OID_WW_GEN_XXX specified in the Oid member.

The NDIS_WW_INDICATION_REQUEST structure is defined as follows:

typedef struct _NDIS_WW_INDICATION_REQUEST {
    NDIS_OID           Oid;               // IN
    UINT               uIndicationFlag    // IN
    UINT               uApplicationToken  // IN OUT
    HANDLE             hIndicationHandle; // IN OUT
    INT                iPollingInterval;  // IN OUT
    NDIS_VAR_DATA_DESC InitialValue;      // IN OUT
    NDIS_VAR_DATA_DESC OIDIndicationValue // OUT
                                          // only valid
                                          // after indication
    NDIS_VAR_DATA_DESC TriggerValue        // IN
} NDIS_WW_INDICATION_REQUEST

The members of this structure contain the following information:

Oid
The OID_WW_GEN_XXX code for which the miniport should indicate events.
uIndicationFlag
Specifies a bitmask in which one of the following flags can be set:
OID_INDICATION_REQUEST_ENABLE
The miniport should call NdisMIndicateStatus if the event specified in this structure occurs.
OID_INDICATION_REQUEST_CANCEL
The miniport should cancel notifications for the specified event. The values supplied in the Oid and hIndicationHandle members must match the values specified in a preceding OID_WW_GEN_INDICATION_REQUEST.

All other bits in uIndicationFlag are reserved for future use.

uApplicationToken
Specifies an application-defined (or protocol-defined) token that is returned by the miniport when it posts the specified event.
hIndicationHandle
Specifies a miniport-determined handle. The application will save this handle and possibly use it to cancel this request subsequently (see uIndicationFlag).
iPollingInterval
Specifies an interval, in milliseconds, at which the miniport should poll the wireless NIC to monitor the value of the given OID.

If this member is set to -1, the miniport should use its own default settings to poll the device. Before returning, the miniport must set this member to the polling interval, in milliseconds, it will use for this indication. This allows the application to determine if the driver is sampling the OID too often, to possibly cancel its original request, and to make another request that the driver poll the NIC at a more appropriate interval.

InitialValue
On input, points to an NDIS_VAR_DATA_DESC-type buffer that can contain information necessary to initialize the requested notification, for instance, as in the case of OID_WW_GEN_CURRENT_ADDRESS. The miniport sets information about the current value of the specified OID in this buffer before returning or completing this request, whichever occurs first.
OIDIndicationValue
On input, specifies NULL. Before the miniport makes the event notification, it allocates an NDIS_VAR_DATA_DESC-type buffer to contain the value of the specified OID, and fills in its copy of the NDIS_WW_INDICATION_REQUEST structure.

A protocol can reference the miniport-allocated buffer only during the status indication of the event. The allocating driver can deallocate this buffer and release all resources necessary to trigger the event on return from its call to NdisMIndicateStatus with this event.

TriggerValue
On input, specifies the virtual address of an NDIS_VAR_DATA_DESC-type buffer containing a value the underlying driver uses to decide when to indicate the specified event, or this member can be NULL, depending on the given Oid value. The miniport can ignore this member for OID codes that trigger on a change in status. See the descriptions of individual OID_WW_GEN_XXX for specifics.

The miniport must fill in the InitialValue and iPollingInterval members of the given structure with the state of the driver’s current information. When the given OID has reached the trigger value, the miniport fills in the OIDIndicationValue member of this structure. The initiator of this request cannot access anything in the NDIS_WW_INDICATION_REQUEST structure it allocated until ownership is returned to the protocol, either when this request is returned to its ProtocolRequestComplete function or because the returned status from NdisRequest is something other than NDIS_STATUS_PENDING.

After event notifications for a particular OID have been enabled by this request, a wireless WAN miniport should fill in the appropriate information and call NdisMIndicateStatus with NDIS_STATUS_WW_INDICATION to notify NDIS, which forwards such a notification to interested protocols, according to the following guidelines:

  • For OID_WW_GEN_XXX codes that use the TriggerValue:

    • While InitialValue < TriggerValue, the miniport continues to monitor for the specified condition. It posts such an event when the present value for the OID becomes >= the given TriggerValue.

    • If InitialValue = TriggerValue, the miniport fills in remaining information in the given structure and calls NdisMIndicateStatus to post the event immediately.

    • If InitialValue > TriggerValue, the miniport should post the event when the present value for the OID becomes <= TriggerValue.

  • For OID_WW_GEN_XXX codes that do not use the TriggerValue:

    The miniport should post the event when a status change occurs, as described for that OID_WW_GEN_XXX.

After a requested event is posted, the calling application must submit another indication request to receive another NDIS_STATUS_WW_INDICATION event. Due to the asynchronous nature of calls to NdisRequest, a protocol writer should be aware that the requested event could be posted prior to the return of a call to NdisRequest with OID_WW_GEN_INDICATION_REQUEST. The uApplicationToken member can be used to facilitate demultiplexing of each such event.

Any attempt to cancel an OID_WW_GEN_INDICATION_REQUEST that has already occurred can be ignored by the underlying driver.

Many OID_WW_GEN_INDICATION_REQUESTs can be outstanding for a particular OID. Making several calls to NdisRequest with this OID can establish several trigger points. When one of several OID_WW_GEN_INDICATION_REQUESTs causes an event notification, only that trigger is canceled, but all other OID_WW_GEN_INDICATION_REQUESTs remain enabled.

NDIS delivers a miniport’s event indications to every protocol that is bound to that underlying driver serially. Consequently, NIC drivers must copy the contents of the indication request. A NIC driver must fill in the hIndicationHandle value prior to scheduling the request on its internal event queue and returning from the call to MiniportSetInformation. This ensures that bound protocol(s) can detect their own respective event notifications when they occur. Consequently, a miniport should set a value in hIndicationHandle that is unique each time it processes this request, such as a monotonically increasing 32-bit value (transaction ID), to ensure that collisions do not occur. The value for this member can be reset to zero every time the miniport initializes.

When a miniport indicates an event for this OID, it must pass a pointer to its copy of the indication request as the Status parameter when it calls NdisMIndicateStatus. Bound protocol drivers that receive the indication must copy the indicated information into their own internal buffer space, assuming any particular protocol determines that the hIndicationHandle value indicates an event of interest to that protocol.

As a general rule, every protocol should check the hIndicationHandle value when an event notification is made and return control as soon as each protocol’s ProtocolStatus function determines the indicated event is not of interest. Otherwise, the effect of notifying all bound protocols of miniport-indicated events is a degradation in performance for the miniport and for all bound protocols.

Event data is guaranteed to be valid only until a ProtocolStatus function returns control. On return from NdisMIndicateStatus, the indicating miniport can release all context for the specified event.

Queries and indications are irrelevant for this OID.

OID_WW_GEN_DEVICE_INFO

This OID requests the miniport to return initialization-time information about itself and its NIC.

This request uses a WW_DEVICE_INFO structure, defined as follows:

typedef struct _WW_DEVICE_INFO{
    NDIS_VAR_DATA_DESC    Manufacturer; // WCHAR buffer
    NDIS_VAR_DATA_DESC    ModelNum;     // WCHAR buffer
    NDIS_VAR_DATA_DESC    SWVersionNum; // WCHAR buffer
    NDIS_VAR_DATA_DESC    SerialNum;    // WCHAR buffer
} WW_DEVICE_INFO;

The members of this structure contain the following information:

Manufacturer
Describes a buffer containing the name of the NIC’s manufacturer in displayable Unicode text.
ModelNum
Describes the a buffer containing the manufacturer-assigned model number of the NIC in displayable Unicode text.
SWVersionNum
Describes a buffer containing the manufacturer-assigned version number of the NIC software/firmware in displayable Unicode text.
SerialNum
Describes a buffer containing the manufacturer-assigned serial number of the NIC in displayable Unicode text.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_OPERATION_MODE

As a query, this OID requests the miniport to return information about the NIC’s current power mode.

As a set, this OID requests the miniport to put its NIC into the requested power mode.

In either case, this request uses one of the following values:
Value | Meaning
0 | Normal mode
1 | Power-saving mode
-1 | Mode unknown

MiniportSetInformation should return NDIS_STATUS_INVALID_DATA if the given value at InformationBuffer is -1.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the NIC’s power mode changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_LOCK_STATUS

This OID requests the miniport to return information about the NIC’s current lock state as one of the following values:
Value | Meaning
0 | Unlocked
1 | Locked
-1 | Lock status unknown

A device cannot not be locked or unlocked with this OID request.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the NIC’s lock status changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_GEN_DISABLE_TRANSMITTER

As a query, this OID requests the miniport to return the NIC’s current transmitter status.

As a set, this OID requests the miniport to put its NIC’s transmitter into the requested state.

In either case, this request uses one of the following values:
Value | Meaning
0 | Transmitter enabled
1 | Transmitter disabled
-1 | Transmitter state unknown

A typical use of this OID would be to temporarily disable the NIC’s transmitter before entering a construction blasting zone. MiniportSetInformation should return NDIS_STATUS_INVALID_DATA if the value at InformationBuffer for this request is -1.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the NIC’s transmitter state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_NETWORK_ID

As a query, this OID requests the miniport to return the ID of the network with which its NIC is currently configured to communicate.

As a set, this OID requests the miniport to reconfigure its NIC to communicate with the network designated by the given ID.

This request uses a WW_NETWORK_ID structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously. The buffer contains a network-ordered sequence of binary byte values.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the network its NIC communicates with changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_PERMANENT_ADDRESS

This OID requests the miniport to return its NIC’s network address in its native format, whatever the header format the driver is currently using.

This request uses a WW_PERMANENT_ADDRESS structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously.The buffer contains a network-ordered sequence of binary byte values.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_CURRENT_ADDRESS

As a query, this OID requests the miniport to return the address in use by the NIC in the format specified. At initialization, the miniport sets the current address to the value returned for OID_WW_GEN_PERMANENT_ADDRESS.

As a set, this OID requests the miniport to change the current address.

The format of the current address depends on the header format, which must be one of the supported header formats listed with OID_WW_GEN_HEADER_FORMATS_SUPPORTED.

This request uses a WW_CURRENT_ADDRESS structure, defined as follows:

typedef struct _WW_CURRENT_ADDRESS {
    NDIS_WW_HEADER_FORMAT    Format;
    NDIS_VAR_DATA_DESC       Address;
} WW_CURRENT_ADDRESS;

The members of this structure contain the following information:

Format
Specifies one of the system-defined NdisWW..Frames values, listed with OID_WW_GEN_HEADER_FORMATS_SUPPORTED.

If an incoming set request supplies anything other than an NdisWW..Frames value or if it supplies any value that the underlying driver does not support, MiniportSetInformation returns NDIS_STATUS_NOT_SUPPORTED.

Address
Describes a buffer containing the station address in use by the NIC. The miniport maps this value, which must be compatible with the value of Format, to the address of the NIC in its native format.The buffer contains a network-ordered sequence of binary byte values.

If an incoming set request supplies an invalid address specification, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.

The current address is identical for all protocols bound to the same NIC miniport. A requested change affects all bound protocols.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the current address is changed. If the current address is unknown, the miniport should set the InitialValue for the indication to NULL. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_SUSPEND_DRIVER

As a query, this OID requests the miniport to return its current suspension state.

As a set, this OID requests the miniport to change its current suspension state.

In either case, this request uses one of the following values:
Value | Meaning
0 | Driver operational
1 | Driver suspended

When a miniport suspends itself, it should release any hardware resources, such as the serial port, it uses in its operational state. This operation can be initiated in the control panel when an end user wants to reassign the hardware resource for other uses, such as sending AT commands to a serial device on the same serial port that the NDIS driver was using.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the driver’s suspension state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_BASESTATION_ID

This OID requests the miniport to return the ID of the base station or adhoc device last contacted by the NIC.

This request uses a WW_BASESTATION_ID structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously.The buffer contains a network-ordered sequence of binary byte values.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the basestation ID value changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_GEN_CHANNEL_ID

This OID requests the miniport to return the ID of the channel currently in use.

This request uses a WW_CHANNEL_ID structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously.The buffer contains a network-ordered sequence of binary byte values.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the channel ID value changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_GEN_ENCRYPTION_SUPPORTED

This OID requests the miniport to return the type(s) of encryption supported by the driver and/or the NIC, as one of the following:

NdisWWUnknownEncryption
The driver cannot determine whether support for encryption is available from its NIC.
NdisWWNoEncryption
No support for encryption is available.
NdisWWDefaultEncryption
The NIC supports encryption but the driver cannot determine what type of encryption it supports.

As additional NDIS_WW_ENCRYPTION_TYPE values are defined, the miniport should return an array of values indicating the types of encryption supported.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_ENCRYPTION_IN_USE

As a query, this OID requests the miniport to return the type of encryption currently in use. See OID_WW_GEN_ENCRYPTION_SUPPORTED for possible values.

As a set, this OID requests the miniport to change the type of encryption in use. The miniport should return NDIS_STATUS_INVALID_DATA if NdisWWUnknownEncryption is specified.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the encryption type changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_ENCRYPTION_STATE

As a query, this OID requests the miniport to return FALSE if the currently selected encryption type is disabled.

As a set, this OID requests the miniport to enable or disable encryption. TRUE directs the miniport to enable encryption.

In either case, the miniport should return NDIS_STATUS_NOT_SUPPORTED if it or its NIC does not support encryption.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the encryption state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_GEN_CHANNEL_QUALITY

This OID requests the miniport to return information about the quality of the link between its NIC and the network as follows:
Value(s) | Meaning
0 | The wireless NIC is not in contact with the network.
1-100 | The driver can communicate with the base station at the given quality for the link, expressed as a normalized value. 100 designates the highest possible quality for the link. If a NIC indicates only whether it is in or out of network contact, its driver should return zero for out of contact and 100 for in contact.
-1 | Channel quality is unknown.

Any bound protocol that simply monitors the in/out state of network contact by an underlying wireless WAN NIC should either call NdisRequest with OID_GEN_MEDIA_CONNECT_STATUS or wait for an NDIS_STATUS_MEDIA_[DIS]CONNECT indication by the miniport.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) according to the TriggerValue that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_GEN_REGISTRATION_STATUS

This OID requests the miniport to return the current registration state of its NIC on the network as follows:
Value | Meaning
0 | Registration was denied.
1 | Registration is pending on the network.
2 | Registered on the network. If a particular network does not provide registration information, a miniport should always return this value in response to this OID request. A NIC on such a network is considered to be always in the registered state.
-1 | Registration status is unknown.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the registration state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_GEN_RADIO_LINK_SPEED

This OID requests the miniport to return the radio link speed in bps. The return value is of type UINT.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_LATENCY

This OID requests the miniport to return its current estimate of the minimum latency, in milliseconds, for sending a net packet of the maximum size permitted by the network from one end point to the other on the wireless network. The return value is of type UINT.

A miniport can return a constant value for this query. However, a NIC driver that refines its latency estimates dynamically provides useful information to bound protocols that “autotune” themselves.

Sets and indications are irrelevant for this OID.

OID_WW_GEN_BATTERY_LEVEL

This OID requests the miniport to return the current charge of the battery as follows:
Value | Meaning
0-100 | The charge of the battery as a percentage, where 100 means it is fully charged.
-1 | The charge state is unknown.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) according to the TriggerValue that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_GEN_EXTERNAL_POWER

This OID requests the miniport to return information about any external power source connected to the NIC as follows:
Value | Meaning
0 | No external power source is connected.
1 | External power is connected.
-1 | Unknown.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the external power connection state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

Network-Dependent Wireless Objects

This describes the system-defined OIDs for the following types of wireless networks:

  • Metricom

  • Pinpoint

  • CDPD

  • Ardis

  • DataTAC

  • Mobitex

Metricom Wireless

The following chart shows the single Metricom-specific wireless OID.
Q | S | Indication | Name
M | M |  | OID_WW_MET_FUNCTION

OID_WW_MET_FUNCTION

As a query, this OID requests the miniport return the response to the last latched AT command.

As a set, this OID requests the miniport latch a single AT command to be executed. Such a command always begins with the prefix “AT” or “at.”

This request is generic for executing AT commands through NDIS driver(s). See the Metricom modem reference manual for information about the AT commands and S registers specific to this device. The data described by this structure is always a NUL-terminated ASCII string.

This request uses a WW_MET_FUNCTION structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously.

Although a driver must accept all supported commands, the driver writer determines when each AT command is executed in a manner that avoids conflicts with the NIC’s normal operations.

Indications are irrelevant for this OID.

Datatac Wireless

The following chart summarizes DataTAC-specific wireless OIDs.
Q | S | Indication | Name
M |  |  | OID_WW_TAC_COMPRESSION
M |  |  | OID_WW_TAC_SET_CONFIG
M |  |  | OID_WW_TAC_GET_STATUS
M | M |  | OID_WW_TAC_USER_HEADER

OID_WW_TAC_COMPRESSION

This OID requests the miniport to return a Boolean indicating whether network-level compression is being supported.

Sets and indications are irrelevant for this OID.

OID_WW_TAC_SET_CONFIG

This OID requests the miniport to return the current configuration of the device.

This OID uses the WW_TAC_SETCONFIG structure, defined as follows:

typedef struct   _WW_TAC_SETCONFIG {
    NDIS_VAR_DATA_DESC RCV_MODE;
    NDIS_VAR_DATA_DESC TX_CONTROL;
    NDIS_VAR_DATA_DESC RX_CONTROL;
    NDIS_VAR_DATA_DESC FLOW_CONTROL;
    NDIS_VAR_DATA_DESC RESET_CNF;
    NDIS_VAR_DATA_DESC READ_CNF;
} WW_TAC_SETCONFIG;

The members of this structure contain the following information:

RCV_MODE
Confirmed or unconfirmed receive mode.
TX_CONTROL
Transmitter enabled or disabled.
RX_CONTROL
Radio modem enabled or disabled.
FLOW_CONTROL
Current flow control uses the DTE and DCE.
RESET_CNF
Whether the device is configured to default settings.
READ_CNF
Reserved.

Each of these members is of type NDIS_VAR_DATA_DESC, described previously.

Support for this OID depends on the RF modem’s support for the controlling the transmitter, receiver, and flow control.

Sets and indications are irrelevant for this OID.

OID_WW_TAC_GET_STATUS

This OID requests the miniport to return configuration and status information about the device.

This OID uses the WW_TAC_GETSTATUS structure, defined as follows:

typedef  struct   _WW_TAC_GETSTATUS{
    BOOLEAN             Action;
    NDIS_VAR_DATA_DESC  Command;
    NDIS_VAR_DATA_DESC  Option;
    NDIS_VAR_DATA_DESC  Response;
} WW_TAC_GETSTATUS;

The members of this stucture contain the following information:

Action
TRUE indicates the command specified in the buffer at Command should be inspected.
Command
Specifies a command in the native-mode interface between the DTE and DCE. See the hardware reference manual for these chips for specifics.
Option
Specifies an option for the given command.
Response
Returns the response to the given command. This buffer is at most 256 bytes in size.

Several of these members are of type NDIS_VAR_DATA_DESC, described previously.

Protocols or applications might use this OID to request configuration and status information from the RF modem.

Sets and indications are irrelevant for this OID.

OID_WW_TAC_USER_HEADER

As a query, this OID requests the miniport to return the current user header.

As a set, this OID requests the miniport to set the user header.

This request uses a WW_TAC_USERHEADER structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously. At most, the information supplied in this buffer can be 64 bytes in length.The buffer contains a network-ordered sequence of binary byte values.

Indications are irrelevant for this OID.

Ardis Wireless

The following chart summarizes Ardis-specific wireless OIDs.
Q | S | Indication | Name
M |  |  | OID_WW_ARD_SNDCP
M |  |  | OID_WW_ARD_TMLY_MSG
M |  |  | OID_WW_ARD_DATAGRAM

OID_WW_ARD_SNDCP

This OID requests the miniport to return information about the SNDCP support it provides.

This OID uses a WW_ARD_SNDCP structure, defined as follows:

typedef  struct   _WW_ARD_SNDCP{
    NDIS_VAR_DATA_DESC  Version;
    INT          BlockSize;
    INT          Window;
} WW_ARD_SNDCP;

The members of this structure contain the following information:

Version
The version of the SNDCP protocol supported.
BlockSize
The block size being used for SNDCP.
Window
The window size being used in SNDCP.

The Version member is of type NDIS_VAR_DATA_DESC, which was defined previously. The value is a NUL-terminated ASCII string.

The Subnetwork Dependent Convergence Protocol (SNDCP) is ARDIS-defined.

Sets and indications are irrelevant for this OID.

OID_WW_ARD_TMLY_MSG

This OID requests the miniport to return the current status of the RF channel as a Boolean value, derived from the RF network.

Sets and indications are irrelevant for this OID.

OID_WW_ARD_DATAGRAM

This OID requests the miniport to return information about the datagram service on the network.

This request uses a WW_ARD_DATAGRAM structure, defined as follows:

typedef  struct   _WW_ARD_DATAGRAM{
    BOOLEAN  LoadLevel;
    INT      SessionTime;
    NDIS_VAR_DATA_DESC  HostAddr;
    NDIS_VAR_DATA_DESC  THostAddr;
} WW_ARD_DATAGRAM;

The members of this structure contain the following information:

LoadLevel
The current load-level status.
SessionTime
Datagram session time remaining.
HostAddr
Specifies the host address.
THostAddr
Specifies the test host address.

The HostAddr and THostAddr members are of type NDIS_VAR_DATA_DESC, which was defined previously. The value of each is a network-ordered byte string.

Sets and indications are irrelevant for this OID.

CDPD Wireless

The following chart summarizes CDPD-specific wireless OIDs.
Q | S | Indication | Name
M | M |  | OID_WW_CDPD_SPNI
M | M |  | OID_WW_CDPD_WASI
M |  | M | OID_WW_CDPD_AREA_COLOR
M |  | M | OID_WW_CDPD_TX_POWER_LEVEL
M |  |  | OID_WW_CDPD_EID
M | M |  | OID_WW_CDPD_HEADER_COMPRESSION
M | M |  | OID_WW_CDPD_DATA_COMPRESSION
M | M |  | OID_WW_CDPD_CHANNEL_SELECT
M |  | M | OID_WW_CDPD_CHANNEL_STATE
M | M |  | OID_WW_CDPD_NEI
M | M | M | OID_WW_CDPD_NEI_STATE
M | M |  | OID_WW_CDPD_SERVICE_PROVIDER_IDENTIFIER
M | M | M | OID_WW_CDPD_SLEEP_MODE
M | M |  | OID_WW_CDPD_CIRCUIT_SWITCHED
M |  |  | OID_WW_CDPD_TEI
M |  | M | OID_WW_CDPD_RSSI

OID_WW_CDPD_SPNI

This OID requests the miniport either to return or to set up an array of ten Service Provider Network Identifiers and the current operating mode with respect to this array. It allows the device to be configured to ignore SPNI during channel acquisition, to not acquire a channel if the SPNI of the channel is not in the current list, or to prefer a channel that has an SPNI in the list.

This request uses a WW_CDPD_SPNI structure, defined as follows:

typedef  struct   _WW_CDPD_SPNI {
    UINT    SPNI[10];
    INT     OperatingMode
} WW_CDPD_SPNI;

The members of this structure contain the following information:

SPNI
Specifies an array of ten UINT values, each a service provider network identifier.
OperatingMode
Specifies how the driver handles SPNI as one of the following values:

Value

Meaning

0

Ignore SPNI.

1

SPNI from list is required.

2

SPNI from list is preferred.

3

SPNI from list is excluded.

To ensure that the list of SPNI values is maintained, bound protocols should issue a query with this OID before they issue a set request.

Indications are irrelevant for this OID.

OID_WW_CDPD_WASI

This OID requests the miniport either to return or to set up a set of ten Wide Area Network Identifiers and the current operating mode with respect this array. It allows the device to be configured to ignore WASI during channel acquisition, to not acquire a channel if the WASI of the channel is not in the current list, or to prefer a channel that has a WASI in the list.

This request uses a WW_CDPD_WASI structure, defined as follows:

typedef  struct   _WW_CDPD_WASI {
    UINT    WASI[10];
    INT     OperatingMode
} WW_CDPD_WASI;

The members of this structure contain the following information:

WASI
Specifies an array of ten UINT values, each a wide-area network identifier.
OperatingMode
Specifies how the driver handles WASI as one of the following values:

Value

Meaning

0

Ignore WASI.

1

WASI from list is required.

2

WASI from list is preferred.

3

WASI from list is excluded.

To ensure that the list of WASI values is maintained, bound protocols should issue a query with this OID before they issue a set request.

Indications are irrelevant for this OID.

OID_WW_CDPD_AREA_COLOR

This OID requests the miniport to return the current area color code of the acquired channel. An area color code is an integer of type INT that identifies the Mobile Data Intermediate System to which the user’s base station is connected.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the area color state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_CDPD_TX_POWER_LEVEL

This OID requests the miniport to return the current transmit power charge as a normalized percentage of type UINT.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) according to the TriggerValue that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_CDPD_EID

This OID requests the miniport to return the unique identifier of the device associated with the subscriber to a network service.

This request uses a WW_CDPD_EID structure, defined as equivalent to the NDIS_VAR_DATA_DESC structure, which was defined previously.The buffer contains a network-ordered sequence of binary byte values.

Sets and indications are irrelevant for this OID.

OID_WW_CDPD_HEADER_COMPRESSION

This OID requests the miniport to return the current header compression scheme in use or to set the initial value to be used in negotiating the header compression scheme.

In either case, one of the following designates the header compression scheme:
Value | Meaning
0 | No header compression.
1 | Always compress headers.
2 | Compress headers if the Mobile Data Intermediate System sends a compressed header.
-1 | Unknown. If this value is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.

Indications are irrelevant for this OID.

OID_WW_CDPD_DATA_COMPRESSION

This OID requests the miniport to return the current data compression scheme in use or to set the initial compression scheme to be used in a negotiation.

In either case, one of the following designates the data compression scheme:
Value | Meaning
0 | Data compression disabled.
1 | Data compression enabled.
-1 | Unknown. If this value is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.

Indications are irrelevant for this OID.

OID_WW_CDPD_CHANNEL_SELECT

As a query, this OID requests the miniport to return the current channel and remaining time from a previously issued set request. MiniportQueryInformation should return NDIS_STATUS_INVALID_DATA if a set request has not yet been issued.

As a set, this OID requests the miniport to set the given channel number and time, in seconds, that the subscriber unit should lock onto.

This OID can be used specifically for testing and analyzing channel characteristics. It is not intended to be used during normal operation of the subscriber unit. The subscriber unit should attempt normal registration on the given channel.

In either case, this request uses a WW_CDPD_CHANNEL_SELECT structure, defined as follows:

typedef  struct _WW_CDPD_CHANNEL_SELECT {
    UINT     ChannelID;
    UINT     fixedDuration;
} WW_CDPD_CHANNEL_SELECT;

The members of this structure contain the following information:

ChannelID
Specifies the channel number.
fixedDuration
Specifies the connection time, in seconds.

Indications are irrelevant for this OID.

OID_WW_CDPD_CHANNEL_STATE

This OID requests the miniport to return the current state of the subscriber unit’s radio channel activity as one of the following:

CDPDChannelNotAvail
CDPDChannelScanning
CDPDChannelInitAcquired
CDPDChannelAcquired
CDPDChannelSleeping
CDPDChannelWaking
CDPDChannelCSDialing
CDPDChannelCSRedial
CDPDChannelCSAnswering
CDPDChannelCSConnected
CDPDChannelCSSuspended

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the channel state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_CDPD_NEI

This OID requests the miniport to return the current list of NEI addresses or to set up to ten IP or CLNP addresses used for station, multicast, or broadcast operation.

In either case, this request uses a WW_CDPD_NEI structure, defined as follows:

typedef struct _WW_CDPD_NEI {
    UINT              uNeiIndex;
    WW_CDPD_NEI_FORMAT NeiFormat;
    WW_CDPD_NEI_TYPE   NeiType;
    UINT               NeiGmid;
    NDIS_VAR_DATA_DESC NeiAddress;
} WW_CDPD_NEI;

The members of this structure contain the following information:

uNeiIndex
Specifies the index of this NEI, as a value from zero to nine.
NeiFormat
Specifies the format of this NEI as one of the following:

CDPDNeiIPv4
CDPDNeiCLNP
CDPDNeiIPv6

NeiType
Specifies the address type of this NEI as one of the following:

CDPDNeiIndividual
CDPDNeiMulticast
CDPDNeiBroadcast

NeiGmid
Specifies the group member identifier if NeiType is CDPDNeiMulticast. Otherwise, this member is meaningless.
NeiAddress
Specifies the NEI address in conformance with the preceding members.

The NeiAddress member is of type NDIS_VAR_DATA_DESC, which was defined previously.The buffer contains a network-ordered sequence of binary byte values.

Indications are irrelevant for this OID.

OID_WW_CDPD_NEI_STATE

As a query, this OID requests the miniport to return the current registration state and substate of a given NEI, designated by its index within the NEI array.

As a set, this OID requests the miniport to set the registration state for the given NEI.

In either case, this request uses a WW_CDPD_NEI_REG_STATE structure, defined as follows:

typedef struct _WW_CDPD_NEI_REG_STATE {
    UINT                  uNeiIndex;
    WW_CDPD_NEI_STATE     NeiState;
    WW_CDPD_NEI_SUB_STATE NeiSubState;
} WW_CDPD_NEI_REG_STATE;

The members of this structure contain the following information:

uNeiIndex
Specifies a value in the range zero through nine that is the index to the NEI within the current array of ten NEIs.
NeiState
Specifies the registration state of the given NEI as one of the following:

CDPDRegistered
CDPDDeregistered
CDPDUnknown

MiniportSetInformation should return NDIS_STATUS_INVALID_DATA if this member is set to CDPDUnknown.

NeiSubState
Specifies the current registration substate of the currently unregistered NEI as one of the following.
  • CDPDPending if registration of the NEI is still pending.

  • CDPDNoReason if registration of the NEI was denied categorically.

  • CDPDMDISNotCapable if MD-IS was incapable of handling M-ES and denied registration of the NEI.

  • CDPDNEINotAuthorized if registration of the NEI was denied due to lack of any authorization.

  • CDPDInsufficientAuth if registration of the NEI was denied because M-ES gave insufficient authentication credentials.

  • CDPDUnsupportedAuth if registration of the NEI was denied because M-ES gave unsupported authentication credentials.

  • CDPDUsageExceeded if registration of the NEI was denied because it exceeded usage limitations.

  • CDPDDeniedThisNetwork if registration of the NEI on this network was denied. (Possibly, the NEI can be registered with another service provider network.)

The value of this member is irrelevant for set operations. Only MiniportQueryInformation functions set values in NeiSubState.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the NeiState value changes for the specified NEI. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_CDPD_SERVICE_PROVIDER_IDENTIFIER

This OID requests the miniport either to return or to set up a set of ten Service Provider Identifiers and the current operating mode with respect to this array. It allows the device to be configured to ignore SPI during channel acquisition, to not acquire a channel if the SPI of the channel is not in the current list, or to prefer a channel that has an SPI in the list.

This request uses an WW_CDPD_SERVICE_PROVIDER_ID structure, defined as follows:

typedef  struct   _WW_CDPD_SERVICE_PROVIDER_ID {
    UINT    SPI[10];
    INT     OperatingMode
} WW_CDPD_SERVICE_PROVIDER_ID;

The members of this structure contain the following information:

SPI
Specifies an array of ten UINT values, each a service provider identifier.
OperatingMode
Specifies how the driver handles SPNI as one of the following values:

Value

Meaning

0

Ignore SPI.

1

SPI from list is required.

2

SPI from list is preferred.

3

SPI from list is excluded.

To ensure that the list of SPI values is maintained, bound protocols should issue a query with this OID before they issue a set request.

Indications are irrelevant for this OID.

OID_WW_CDPD_SLEEP_MODE

As a query, this OID requests the miniport to return the sleep-idle timer interval for its NIC. The value returned is of type INT. Zero indicates CDPD sleep is disabled.

As a set, this OID requests the miniport to negotiate with the network, using the given sleep-idle timer interval for its NIC.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) if the sleep mode changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

OID_WW_CDPD_CIRCUIT_SWITCHED

This OID can be used to query or to set the following device capabilities:

  • Circuit-switched CDPD service connection and channel information

  • Default CDPD service preference and parameters for use in circuit-switched CDPD mode

  • Control of circuit-switched CDPD operation

As a query, this OID requests the miniport to return the current state of the circuit-switched CDPD status and settings.

As a set, this OID requests the miniport to configure the circuit-switched CDPD parameters.

In either case, this request uses a WW_CDPD_CIRCUIT_SWITCHED structure, defined as follows:

typedef  struct _WW_CDPD_CIRCUIT_SWITCHED {
    INT service_preference;
    INT service_status;
    INT connect_rate;
    NDIS_VAR_DATA_DESC dial_code[20];
    UINT sid;
    INT a_b_side_selection;
    INT AMPS_channel;
    UINT action;
    NDIS_VAR_DATA_DESC default_dial[20];
    NDIS_VAR_DATA_DESC call_back[20];
    UINT sid_list[10];
    UINT inactivity_timer;
    UINT receive_timer;
    UINT conn_resp_timer;
    UINT reconn_resp_timer;
    UINT disconn_timer;
    UINT NEI_reg_timer;
    UINT reconn_retry_timer;
    UINT link_reset_timer;
    UINT link_reset_ack_timer;
    UINT n401_retry_limit;
    UINT n402_retry_limit;
    UINT n404_retry_limit;
    UINT n405_retry_limit;
} WW_CDPD_CIRCUIT_SWITCHED;

The members of this structure contain the following information:

service_preference
Specifies one of the following:

Value

Meaning

0

Always use packet switched CDPD.

1

Always use CS CDPD via AMPS.

2

Always use CS CDPD via PSTN.

3

Use circuit switched via AMPS only when packet switched is unavailable.

4

Use packet switched only when circuit switched via AMPS is unavailable.

5 or 6

Use device-manufacturer-defined service preference.

-1

Unknown. If this value is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.

service_status
Specifies one of the following:

Value

Meaning

0

Packet-switched CDPD.

1

Circuit-switched CDPD via AMPS.

2

Circuit-switched CDPD via PSTN.

-1

Unknown. If this value is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.

connect_rate
Specifies the circuit-switched connection rate in bits per second. Zero designates no active connection, and -1 indicates the rate is unknown. If -1 is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.
dial_code
Specifies the dial code last used to dial.
sid
Specifies the current AMPS system ID.
a_b_side_selection
Specifies one of the following:

Value

Meaning

0

No AMPS service.

1

AMPS “A” side channels selected.

2

AMPS “B” side channels selected.

-1

Unknown. If this value is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.

AMPS_channel
Specifies the AMPS channel in use as a value in the range 1 to 1023. Zero indicates no AMPS service, and -1 indicates the channel or state of AMPS service is unknown. If -1 is specified for a set, MiniportSetInformation returns NDIS_STATUS_INVALID_DATA.
action
Specifies one of the following:

Value

Meaning

0

No action

1

Suspend (hang up)

2

Dial

default_dial
Specifies the default dial code for circuit-switched CDPD service, encoded as defined for CDPD.
call_back
Specifies the number to be used by the circuit-switched CDPD network to call back the mobile unit, encoded as defined for CDPD.
sid_list
Specifies a set of ten 16-bit preferred AMPS system IDs for circuit-switched CDPD.
inactivity_timer
Specifies the time-out limit, in seconds, for inactivity before dropping the call.
receive_timer
Specifies the time-out limit, in seconds, to wait for a receive.
conn_resp_timer
Specifies the time-out limit, in seconds, to wait for a connection response.
reconn_resp_timer
Specifies the time-out limit, in seconds, to wait for a reconnection response.
disconn_timer
Specifies the time-out limit, in seconds, to wait for a disconnection.
NEI_reg_timer
Specifies the time-out limit, in seconds, to wait for an NEI registration.
reconn_retry_timer
Specifies the time-out limit, in seconds, for retrying a reconnection.
link_reset_timer
Specifies the time-out limit, in seconds, for resetting a link.
link_reset_ack_timer
Specifies the time-out limit, in seconds, for receiving a link-reset acknowledgment.
n401_retry_limit
As defined for circuit-switched CSPD.
n402_retry_limit
As defined for circuit-switched CSPD.
n404_retry_limit
As defined for circuit-switched CSPD.
n405_retry_limit
As defined for circuit-switched CSPD.

For all xxx_timer members in the preceding structure, anything in the range from 1 to 65535 seconds is theoretically valid, but see the CS-CDPD documentation for specifics.

The dial_code, default_dial, and call_back members are of type NDIS_VAR_DATA_DESC, which was defined previously. The values are NUL-terminated ASCII strings.

Indications are irrelevant for this OID.

OID_WW_CDPD_TEI

This OID requests the miniport to return the temporary ID assigned by the network to a data-link connection with a particular M-ES. The returned value is of type ULONG, and is used for diagnostic purposes.

Sets and indications are irrelevant for this OID.

OID_WW_CDPD_RSSI

This OID requests the miniport to return the current Received Signal Strength Indication in dBm. The returned value is of type UINT.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) according to the TriggerValue that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

Pinpoint Wireless

The following chart summarizes Pinpoint-specific wireless OIDs.
Q | S | Indication | Name
M |  |  | OID_WW_PIN_LOC_AUTHORIZE
M |  |  | OID_WW_PIN_LAST_LOCATION
M |  |  | OID_WW_PIN_LOC_FIX

OID_WW_PIN_LOC_AUTHORIZE

This OID requests the miniport to return information about whether the device has been authorized to perform location fixes on itself as one of the following:
Value | Meaning
0 | Unauthorized
1 | Authorized
-1 | Unknown

Sets and indications are irrelevant for this OID.

OID_WW_PIN_LAST_LOCATION

This OID requests the miniport to return information about the last known location of the mobile radio or NULL if the location is unknown. This request does not initiate a location fix by the device.

This request uses a WW_PIN_LOCATION structure, defined as follows:

typedef  struct   _WW_PIN_LOCATION{
    INT Latitude;
    INT Longitude;
    INT Altitude;
    INT FixTime;
    INT NetTime;
    INT LocQuality
    INT LatReg;
    INT LongReg;
    INT GMTOffset
} WW_PIN_LOCATION

The members of this structure contain the following information:

Latitude
Specifies the latitude in hundredths of a second.
Longitude
Specifies the longitude in hundredths of a second.
Altitude
Specifies the altitude in feet.
FixTime
Specifies the local time of the last location fix since midnight of the current day, in tenths of a second.
NetTime
Specifies the local network time since midnight of the current day, in tenths of a second.
LocQuality
Specifies the location quality as a value from 0 to 100, with 100 indicating the highest quality.
LatReg
Specifies the latitude registration offset, in hundredths of a second.
LongReg
Specifies the longitude registration offset, in hundredths of a second.
GMTOffset
Specifies the offset, in minutes, of the local time zone from GMT.

Sets and indications are irrelevant for this OID.

OID_WW_PIN_LOC_FIX

This OID requests the miniport to initiate a location fix and to return the information or NULL if it cannot be determined.

This request uses a WW_PIN_LOCATION structure. See OID_WW_PIN_LAST_LOCATION for details.

Sets and indications are irrelevant for this OID.

Mobitex Wireless

The following chart summarizes Mobitex-specific wireless OIDs.
Q | S | Indication | Name
 | M | M | OID_WW_MBX_SUBADDR
M |  | M | OID_WW_MBX_FLEXLIST
M |  | M | OID_WW_MBX_GROUPLIST
M |  | M | OID_WW_MBX_TRAFFIC_AREA
M |  | M | OID_WW_MBX_LIVE_DIE
 | M |  | OID_WW_MBX_TEMP_DEFAULTLIST

OID_WW_MBX_SUBADDR

This OID requests the miniport to log in or log out a personal subscription address (MAN).

This request uses a WW_MBX_PMAN structure, defined as follows:

typedef  struct   _WW_MBX_PMAN {
    BOOLEAN      ACTION;
    UINT         MAN;
    UCHAR        PASSWORD[8];
} WW_MBX_PMAN;

The members of this structure contain the following information:

ACTION
Specifies 0 if the driver should log in the given PMAN, or 1 if it should log out the given PMAN.
MAN
Specifies the personal network address of the subscriber to be logged in or out.
PASSWORD
Specifies a password of up to eight characters for a log-in or NULL for a log-out operation.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) when a personal MAN is logged in or out on the network. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST. InitialValue should remain NULL. When making an indication, all passwords should be zeroed; they are ignored by all bound protocols.

Queries are irrelevant for this OID.

OID_WW_MBX_FLEXLIST

This OID requests the miniport to return the current flexlist, which is the set of personal MANs currently logged on to the local wireless device.

This request uses a WW_MBX_FLEXLIST structure, defined as follows:

typedef struct _WW_MBX_FLEXLIST;
    INT  count;
    UINT MAN[7];
} WW_MBX_FLEXLIST;

The members of this structure contain the following information:

count
Specifies the number of MAN entries in the flexlist. Specifies zero if the flexlist is empty with all elements of MAN set to zero, as well. Otherwise, count is set to -1 if the number is unknown.
MAN
Specifies up to seven personal MAN addresses.

During radio modem startup, this information is sent within an MPAK by the radio modem to the PC. The form of the WW_MBX_FLEXLIST structure mirrors the format of the information within the MPAK.

The miniport must track all subsequent log-in and log-out requests for this set of MANs and retain this list so that protocols can query the current list as they bind to the miniport.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) when the flexlist changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_MBX_GROUPLIST

This OID requests the miniport to return the current grouplist, the set of group MANs to which the local wireless device currently belongs.

This request uses a WW_MBX_GROUPLIST structure, defined as follows:

typedef struct _WW_MBX_GROUPLIST {
    INT  count;
    UINT MAN[15];
} WW_MBX_GROUPLIST;

The members of this structure contain the following information:

count
Specifies the number of MAN entries in the current group list. Specifies zero if the group list is empty with all elements of MAN set to zero, as well. Otherwise, count is set to -1 if the number is unknown.
MAN
Specifies up to 15 MAN addresses.

This information is sent within an MPAK by the radio modem to the PC whenever the group list changes. The form of the WW_MBX_GROUPLIST structure mirrors the format of the information within the MPAK. The miniport maintains the information in this list so that protocols can query the current list as they bind to the miniport.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) when the group list changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_MBX_TRAFFIC_AREA

This OID requests the miniport to return information about the most recently received traffic-area frame as one of the following:
Value | Meaning
in_traffic_area | Mobile unit has entered a subscribed traffic area.
in _auth_traffic_area | Mobile unit is outside traffic area but is authorized.
unauth_traffic_area | Mobile unit is outside traffic area and is unauthorized.
unknown_traffic_area | The driver has no information about the current traffic area.

The radio modem sends a traffic area frame to the PC whenever the radio changes traffic area. The miniport should save the current value indicated in such a frame to support this OID.

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) when the traffic area changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_MBX_LIVE_DIE

This OID requests the miniport to return the current live/die state, denoting whether the wireless device is permitted to send data on the network, as follows:
Value |
0 | DIE most recently received, denying device sends
1 | LIVE most recently received, permitting device sends
-1 | LIVE/DIE state is unknown

If any bound protocol has enabled indications with OID_WW_GEN_INDICATION_REQUEST, the miniport must call NdisMIndicateStatus to notify such protocol(s) when the live/die state changes. It can ignore the TriggerValue member that was specified in the OID_WW_GEN_INDICATION_REQUEST.

Sets are irrelevant for this OID.

OID_WW_MBX_TEMP_DEFAULTLIST

This OID requests the miniport to use the specified temporary default list of radio frequencies for the modem.

This request uses a WW_MBX_TEMPDEFAULTLIST structure, defined as follows:

typedef  struct   _WW_MBX_TEMPDEFAULTLIST {
    UINT Length;
    MBX_CHANNEL_PAIR ChannelPair[];
} WW_MBX_TEMPDEFAULTLIST;

The members of this structure contain the following information:

Length
Specifies the number of elements in the ChannelPair array.
ChannelPair
Specifies an array of elements defined as follows:
typedef  struct  _WW_MBX_CHANNEL_PAIR {
    UINT Mobile_Tx; // transmit frequency
    UINT Mobile_Rx; // receive frequency
} WW_MBX_CHANNEL_PAIR;

Queries and indications are irrelevant for this OID.


Chapter 6 Structures Used by NDIS Drivers

The NDIS-defined and system-defined structures described here are multifunction or alternative structure parameters. That is, either pointers to these structures are passed in calls to more than one Ndis Xxx, MiniportXxx, and/or ProtocolXxx function or a pointer to more than one of these structures can be passed in calls to the same function.

For more information about structures for which a pointer is passed only to a single Ndis Xxx, MiniportXxx, or ProtocolXxx function, see the documentation for that function.

For more information about OID-specific structures at the InformationBuffer passed to NdisRequest, see Chapter 5.

Structures described next are in alphabetic order.


NDIS_MAC_FRAGMENT

typedef struct _NDIS_MAC_FRAGMENT {
    IN NDIS_HANDLE  NdisLinkContext;
    IN ULONG        Errors;
} NDIS_MAC_FRAGMENT, *PNDIS_MAC_FRAGMENT;

NDIS_MAC_FRAGMENT contains information associated with a fragment indication made by a WAN NIC miniport.

Members

NdisLinkContext
Specifies the handle, identifying the link, that the miniport set in the line-up indication when the link was established.
Errors
Specifies a bitmask set with one or more of the following system-defined flags (ORed) to indicate why the fragment was received.
WAN_ERROR_CRC
WAN_ERROR_FRAMING
WAN_ERROR_HARDWAREOVERRUN
WAN_ERROR_BUFFEROVERRUN
WAN_ERROR_TIMEOUT
WAN_ERROR_ALIGNMENT

If no direct mapping from the WAN medium error to one of the preceding error flags exists, choose the most appropriate flag or combination of flags.

Comments

A WAN NIC driver calls NdisMIndicateStatus with this type of indication when it receives a partial packet from a remote node on the wide area network.

NDISWAN forwards such a fragment indication to the affected protocol(s), formatted as an NDIS_WAN_FRAGMENT structure.

See Also

NDIS_MAC_LINE_UP, NdisMIndicateStatus, NDIS_WAN_FRAGMENT


NDIS_MAC_LINE_DOWN

typedef struct _NDIS_MAC_LINE_DOWN {
    IN NDIS_HANDLE NdisLinkContext;
} NDIS_MAC_LINE_DOWN, *PNDIS_MAC_LINE_DOWN;

NDIS_MAC_LINE_DOWN contains information associated with a line-down indication made by a WAN NIC miniport.

Member

NdisLinkContext
Specifies the handle, identifying the link, that the miniport set in the line-up indication when the link was established.

Comments

A WAN NIC driver calls NdisMIndicateStatus with this type of indication when a link between a local node and a remote node in a wide area network becomes inactive.

NDISWAN forwards such a line-down indication to the affected protocol(s), formatted as an NDIS_WAN_LINE_DOWN structure.

See Also

NDIS_MAC_LINE_UP, NdisMIndicateStatus, NDIS_WAN_LINE_DOWN


NDIS_MAC_LINE_UP

typedef struct _NDIS_MAC_LINE_UP {
    IN ULONG            LinkSpeed;
    IN NDIS_WAN_QUALITY Quality;
    IN USHORT           SendWindow;
    IN NDIS_HANDLE      ConnectionWrapperID;
    IN NDIS_HANDLE      NdisLinkHandle;
    IN OUT NDIS_HANDLE  NdisLinkContext;
} NDIS_MAC_LINE_UP, *PNDIS_MAC_LINE_UP;

NDIS_MAC_LINE_UP contains information associated with a line-up indication made by a WAN NIC miniport.

Members

LinkSpeed
Specifies the speed of the link, in units of 100 bps.

Zero indicates no change from the speed the miniport originally returned for the OID_GEN_LINK_SPEED query.

Quality
Specifies the quality of service (QOS) of the new line as returned when the protocol called NdisRequest with OID_WAN_QUALITY_OF_SERVICE.
SendWindow
Specifies the number of send packets the miniport (or its NIC) can accept at a time, effectively how many NDIS_WAN_PACKET-type send packets the WAN NIC driver queues internally. Zero indicates that NDISWAN should use the value returned for MaxTransmit in response to the OID_WAN_GET_INFO query.
ConnectionWrapperID
Specifies the handle by which this link (or data channel on a link) will be known to TAPI client(s). This handle must be unique, so the miniport uses the htCall handle supplied by NDISTAPI. Before the miniport makes the initial line-up indication that establishes such a link (or data channel), it already acquired the NDISTAPI-assigned htCall handle in one of the following ways:
  • For an outbound call, the driver’s MiniportQueryInformation function saved the htCall handle that NDISTAPI supplied in a preceding OID_TAPI_MAKE_CALL request.

  • For an incoming call, the miniport saved the htCall handle that NDISTAPI returned in ulParam2 of the NDIS_TAPI_EVENT structure that the miniport passed to NdisMIndicateStatus with ulMsg set to LINE_NEWCALL.
NdisLinkHandle
Specifies the handle, originally supplied by the NIC driver, that is passed down in subsequent calls, such as to MiniportWanSend, to identify a particular link, or to identify a particular channel on a link capable of handling more than one data stream.
NdisLinkContext
Specifies zero when a WAN NIC driver calls NdisMIndicateStatus with this structure to establish a link. On return from NdisMIndicateStatus, this member contains a handle, supplied by NDISWAN, to be used in future calls, such as to indicate receives, for the established link.

Comments

A WAN NIC driver calls NdisMIndicateStatus with this type of indication in either of the following conditions:

  • When a link between a local node and a remote node in a wide area network becomes active in the process of establishing the new link, the MiniportQueryInformation function makes this indication in the context of an OID_TAPI_GET_ID request for which ulSelect is set to LINECALLSELECT_CALL.

    When NdisMIndicateStatus returns, the miniport must save the NdisLinkContext handle, supplied by NDISWAN, for subsequent calls to NdisMIndicateStatus and NdisWan Xxx functions affecting the just established link.

    MiniportQueryInformation also must use the returned NdisLinkContext handle as the value returned to NDISTAPI at DeviceID in the NDIS_TAPI_GET_ID structure.

    If it can handle more than one data channel on a link, a WAN NIC driver’s MiniportQueryInformation function is called once with OID_TAPI_GET_ID as each such channel on the link is opened. On such a link, NdisLinkContext handles are channel-specific rather than link-specific.

  • After such a link is established, the NIC driver makes this indication with the given NdisLinkContext handle if line characteristics change, or to dynamically change the value of SendWindow specifying the number of outstanding sends the NIC driver accepts for its internal queue. For example, a miniport can make a subsequent line-up indication if the originally specified Quality of the line changes.

Typically, a WAN NIC driver provides a pointer to a driver-allocated control block for each link as NdisLinkHandle. This handle is link-specific or, if the driver manages more than one data stream per link, data-channel-specific. In either case, a WAN NIC miniport must set up its NdisLinkHandle at the first NDIS_MAC_LINE_UP indication that the miniport makes for each link or data channel.

After NDISWAN has submitted the number of packets specified in SendWindow to MiniportWanSend, NDISWAN holds incoming send packets queued until the miniport returns a completed send packet or calls NdisMWanSendComplete with a packet for which it previously returned NDIS_STATUS_PENDING, whichever occurs first. Subsequent NDIS_MAC_LINE_UPs can be indicated if the NIC driver needs to change the SendWindow value dynamically.

Whenever subsequent NDIS_MAC_LINE_UP indications are made, whether to change the SendWindow value or for changes in line characteristics, the NdisLinkHandle member must be filled with the value set in the driver’s initial line-up call to NdisMIndicateStatus for the same link.

NDISWAN supplies a unique handle at NdisLinkContext for every initial NDIS_MAC_LINE_UP indication that establishes a new link or channel. The miniport must supply the given NdisLinkContext handle when it makes any subsequent NDIS_MAC_LINE_UP, NDIS_MAC_LINE_DOWN, and NDIS_MAC_FRAGMENT indications for the same link or channel.

For more information about WAN and TAPI OIDs, see Chapter 5.

See Also

MiniportQueryInformation, MiniportSetInformation, NDIS_MAC_FRAGMENT, NDIS_MAC_LINE_DOWN, NdisMIndicateStatus, NdisMWanIndicateReceive, NdisRequest, NDIS_TAPI_EVENT, ProtocolStatus


NDIS_PACKET

typedef struct _NDIS_PACKET {
    NDIS_PACKET_PRIVATE  Private;
    union {
        struct {
             UCHAR       MiniportReserved[8];
             UCHAR       WrapperReserved[8];
        };
        struct {
             UCHAR       MacReserved[16];
        };
    };
    UCHAR                ProtocolReserved[1];
} NDIS_PACKET, *PNDIS_PACKET, **PPNDIS_PACKET;

NDIS_PACKET defines the packet descriptors with chained buffer descriptors for which pointers are passed to many Ndis Xxx, MiniportXxx, and ProtocolXxx functions.

Members

Private
This is reserved for use exclusively by NDIS. Drivers must call the appropriate Ndis Xxx functions or NDIS-supplied macros to affect the contents of this area.
MiniportReserved
NIC miniport drivers and NDIS intermediate drivers can use this eight-byte area for their own purposes, as long as each such driver is given a fresh packet descriptor.
WrapperReserved
This is reserved for use exclusively by NDIS.
MacReserved
This is reserved for use by legacy full-NIC drivers.
ProtocolReserved
Highest-level protocol drivers and NDIS intermediate drivers can use this variable-sized area for their own purposes, as long as each such driver is given a fresh packet descriptor.

Comments

NDIS drivers must call NdisAllocatePacket to allocate all packet descriptors they use to indicate receives to higher-level drivers with NdisMIndicateReceivePacket. NDIS drivers must call NdisAllocatePacket to allocate all packet descriptors they use for sends to underlying drivers with NdisSendPackets or NdisSend, as well as packet descriptors they pass to NdisTransferData.

Chained to each packet descriptor are one or more buffer descriptors mapping buffers that contain network packet data, either received or to be transmitted. NIC drivers and intermediate drivers can allocate packet descriptors with, at most, 16 bytes of ProtocolReserved space for receive indications.

Any buffers allocated by lower-level NDIS drivers must be mapped by buffer descriptors that were allocated from buffer pool with NdisAllocateBuffer. Only highest-level Windows NT protocols can use MDLs set up by still higher-level drivers as substitutes for NDIS_BUFFER-type descriptors.

NDIS drivers typically use their respective Xxx Reserved areas to maintain per-packet state about outstanding transfers. For example, a protocol might store a pointer to a protocol-allocated buffer containing lookahead data, which its ProtocolReceive function has already copied, in the ProtocolReserved area of a packet descriptor that the protocol allocates for a call to NdisTransferData.

A single driver can use the MiniportReserved area and a single driver can use the ProtocolReserved area while a particular packet descriptor is being used in a transfer operation. Consequently, NDIS intermediate drivers, which have both MiniportXxx and ProtocolXxx functions, cannot use these areas in incoming packet descriptors for their own purposes.

Instead, NDIS intermediate drivers must repackage each incoming packet in a fresh packet descriptor before it passes the transfer request down to an underlying driver or up to a higher-level driver.

This strategy ensures that the NDIS intermediate driver and the underlying driver each have a MiniportReserved area to use, that the intermediate driver and overlying protocol each have a ProtocolReserved area to use, and that the intermediate driver has a convenient and economical way to maintain per-packet state information about all current transfers. This also ensures that NDIS has correct information in the Private portion of each packet descriptor.

Drivers that supply and consume out-of-band data on network transfers and drivers that support multipacket receives and/or sends must use the member-specific NDIS_GET/SET_XXX macros or the NDIS_OOB_DATA_FROM_PACKET macro to access the NDIS_PACKET_OOB_DATA block associated with each packet descriptor.

When a protocol driver calls NdisSendPackets or NdisSend with a packet descriptor, it relinquishes ownership of the following until that packet descriptor is returned to its ProtocolSendComplete function:

  • The protocol-allocated packet descriptor(s)

  • All data buffers mapped by buffer descriptors chained to the packet descriptor(s)

  • All NDIS_PACKET_OOB_DATA blocks associated with each protocol-allocated packet descriptor

  • Any out-of-band data buffer specified in an NDIS_PACKET_OOB_DATA block for an associated buffer descriptor

See Also

MiniportSend, MiniportSendPackets, MiniportTransferData, NdisAllocateBuffer, NdisAllocatePacket, NdisChainBufferAtBack, NdisChainBufferAtFront, NdisCopyFromPacketToPacket, NdisFreePacket, NdisGetFirstBufferFromPacket, NdisGetPacketFlags, NdisMSendComplete, NdisMTransferDataComplete, NDIS_PACKET_OOB_DATA, NdisQueryPacket, NdisRecalculatePacketCounts, NdisReinitializePacket, NdisReturnPackets, NdisSend, NdisSendPackets, NdisSetPacketFlags, NdisTransferData, NdisUnchainBufferAtBack, NdisUnchainBufferAtFront, ProtocolReceive, ProtocolReceivePacket, ProtocolSendComplete, ProtocolTransferDataComplete


NDIS_PACKET_OOB_DATA

typedef struct _NDIS_PACKET_OOB_DATA {
    union {
        ULONGLONG  TimeToSend;
        ULONGLONG  TimeSent;
    };
    ULONGLONG      TimeReceived;
    UINT           HeaderSize;
    UINT           SizeMediaSpecificInfo;
    PVOID          MediaSpecificInformation;
    NDIS_STATUS    Status;
} NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA;

NDIS_PACKET_OOB_DATA specifies out-of-band information associated with a packet descriptor, used by drivers whose media supplies, requires, or accepts out-of-band information, such as packet priorities, and by drivers that support multipacket receives and/or sends.

Members

TimeToSend
Specifies the time, in system time units, at which a given packet should be transmitted over the network.

Protocols can set this timestamp before passing an array of packet descriptor pointers to NdisSendPackets or a packet descriptor pointer to NdisSend.

The MiniportSendPackets or MiniportSend functions of underlying drivers can retrieve the value of this timestamp to determine when the given packet(s) should be transmitted over the network. Usually, only the lowest-level underlying driver retrieves this timestamp.

TimeSent
Specifies the time, in system time units, at which a received packet was transmitted over the network from a remote node.

NIC drivers and, possibly, intermediate drivers that export only a set of MiniportXxx functions set this timestamp before indicating receive packet(s).

The ProtocolReceivePacket functions of drivers bound above such a miniport can retrieve the value of this timestamp to determine when any particular packet was transmitted from the remote node. If the ProtocolReceive function is given an indication and the underlying driver sets this timestamp, ProtocolReceive can call NdisQueryReceiveInformation to retrieve this timestamp.

TimeReceived
Specifies the time, in system time units, at which a given packet from a remote node on the network was received on the NIC.

NIC drivers and, possibly, intermediate drivers that export only a set of MiniportXxx functions set this timestamp before calling NdisMIndicateReceivePacket with a packet array, which can have one or more pointers to packet descriptors.

The ProtocolReceivePacket functions of drivers bound above any such miniport can retrieve the value of this timestamp to determine when the given packet(s) were received. If the ProtocolReceive function is given an indication and the underlying driver sets this timestamp, ProtocolReceive can call NdisQueryReceiveInformation to retrieve this timestamp.

HeaderSize
Specifies the size in bytes of the medium-specific header contained in the buffer mapped by the initial buffer descriptor chained to the packet descriptor.

Lower-level drivers set this member for subsequent receive indication(s) according to the medium each driver’s MiniportInitialize function elected to support.

The ProtocolReceivePacket functions of drivers bound above such a miniport can retrieve the value of this member to determine how to process the medium-specific net packet.

SizeMediaSpecificInfo
Specifies the size in bytes of the buffer at MediaSpecificInformation.

A driver must set this member if it supplies out-of-band information with a packet to be sent or indicated.

MediaSpecificInformation
Specifies the address of a driver-allocated buffer. This buffer contains any out-of-band data, such as packet priority, that accompanies the net packet specified with the packet descriptor. The out-of-band data has been set up either by the allocating protocol for a send or by the allocating miniport for a receive indication.

Either type of driver can retrieve the address of the out-of-band data buffer and its size to determine how to process or interpret the send or receive specified with the incoming packet descriptor. If the ProtocolReceive function is given an indication and the underlying driver supplies this information, ProtocolReceive can call NdisQueryReceiveInformation to retrieve the virtual range of this buffer.

Status
Specifies the current status of the packet descriptor and the ownership of all driver-allocated resources specified with the packet descriptor.

Only lower-level drivers and NDIS set this member.

A miniport can set this member before it calls NdisMIndicateReceivePacket to indicate how soon it needs to regain ownership of its allocated resources specified in the associated packet descriptor(s). It can retrieve this value on return from NdisMIndicateReceivePacket to determine whether the resources specified with the packet descriptor for its indication can be prepared for reuse immediately.

A miniport can set this member in its MiniportSendPackets function to complete an incoming send immediately, to notify NDIS that it will complete the request asynchronously, or to request that NDIS requeue some or all of the incoming packet array and resubmit those packet descriptor(s) to MiniportSendPackets later.

A protocol cannot retrieve this value to determine the completion status of a send on return from its call to NdisSendPackets or NdisSend.

Comments

Every packet descriptor allocated with NdisAllocatePacket has an associated NDIS_PACKET_OOB_DATA block. Only drivers that support multipacket sends and/or receives and drivers that supply out-of-band information, such as packet priority, with each network packet to be transferred use the OOB block. Nevertheless, every NDIS driver that allocates packet descriptors for transfers between bound drivers must allocate those packet descriptors with NdisAllocatePacket from the packet pool that each such NDIS driver usually allocates when it initializes.

In general, drivers of high-capacity busmaster DMA NICs are most likely to support multipacket receives and sends because such a NIC driver makes the greatest performance gains by supporting multipacket transfers. Any protocol that binds itself to (or above) such an underlying NIC driver also should support multipacket sends and receives for maximum performance.

If such a protocol also binds itself above another NIC driver without multipacket transfer support, NDIS handles multipacket sends and single-packet receives in a manner transparent to both drivers by always calling the protocol’s ProtocolReceive function and by calling that NIC driver’s MiniportSend function with a single packet per call. Similarly, if a protocol that does not support multipacket transfers binds itself above a NIC driver that does, NDIS handles multipacket receive indications and single packet sends in a manner transparent to both drivers, although the MiniportSendPackets function of the underlying NIC driver is given only a single send request per call to NdisSend from such a protocol.

Using Timestamps

All timestamps set in the NDIS_PACKET_OOB_DATA blocks associated with packet descriptors are expressed in system time units as the number of 100-nanosecond intervals since 12:00 a.m., January 1, 1601.

NDIS drivers can call NdisGetCurrentSystemTime when they set these timestamps with the NDIS_SET_PACKET_TIME_XXX macros. Still higher-level drivers or system components can convert these timestamps into locale-specific values more meaningful to the end user.

Supplying Out-of-Band Information with Packets

Any out-of-band information specified in the SizeMediaSpecificInfo and MediaSpecificInformation members is medium-type-specific. For example, some types of media require protocols to specify a packet priority for each packet they send and require NIC drivers to specify a packet priority with each receive packet they indicate. Such a NIC driver must call NdisMIndicateReceivePacket to fulfill the requirements of these types of media.

While NDIS provides support for a range of priority values from zero through seven, some media support only two levels of priority. For these types of media, NDIS drivers should map values zero through three to the medium-specific normal priority value (usually zero for such media types) and values four through seven to the medium-specific high priority value (usually one for such media types).

The structure of individual records within the buffer at MediaSpecificInformation is defined as follows:

typedef struct     MediaSpecificInformation {
    UINT           NextEntryOffset;
    NDIS_CLASS_ID  ClassId;
    UINT           Size;
    UCHAR          ClassInformation[1];
} MEDIA_SPECIFIC_INFORMATION;

The members of this structure contain the following information:

NextEntryOffset
Specifies the byte offset to this member in the next record, if any. Zero indicates this is the last record in the buffer; the values of all remaining members in the last record are also zero. The value of this member must be quad-aligned.
ClassId
Specifies the type of the record. Possible values are one of the following:
NdisClass802_3Priority
The underlying NIC’s medium requires that drivers specify per-packet priority, while its driver uses Ethernet emulation to communicate with bound protocols.

A bound protocol can determine whether the underlying NIC miniport’s native medium requires prioritized packets with the OID_802_3_MAC_OPTIONS query. If so, the miniport sets the NDIS_802_3_OPTION_PRIORITY flag.

NdisClassWirelessWanMbxMailbox
The underlying NIC driver examines the ULONG-sized record at ClassInformation to determine whether the mailbox flag should be set for the associated packet. (1 = set mailbox flag, 0 = do not set mailbox flag)

Size
Specifies the number of bytes in the ClassInformation array, including any padding necessary to align the NextEntryOffset of the next record on a four-byte boundary.
ClassInformation
Specifies the out-of-band information for this record.

The NDIS_CLASS_ID type is an NDIS-defined enumeration. Most common classifications for records will be specified as system-defined values. However, a range of values in this enumeration will be available for vendor-defined experimental classes.

Setting Status Before Making Receive Indications

Only lower-level NDIS drivers use the Status member of this structure for receive indications. Before such a driver calls NdisMIndicateReceivePacket, it sets the Status with NDIS_SET_PACKET_STATUS for one or more packet descriptors in the array to be indicated to either of the following values:

NDIS_STATUS_SUCCESS
Tells NDIS that the driver is relinquishing ownership of the packet about to be indicated with NdisMIndicateReceivePacket until the packet descriptor is returned to its MiniportReturnPacket function.

Protocols that receive the indication can use the packet descriptor, along with all buffers mapped by buffer descriptors chained to the packet descriptor and any out-of-band information supplied with the packet, to copy the indicated net packet data or, possibly, to forward the indicated data to interested clients.

NDIS_STATUS_RESOURCES
Tells NDIS that the driver is retaining ownership of the packet about to be indicated with NdisMIndicateReceivePacket and of all subsequent packets in the packet array. All these packets will be indicated, one at a time, to the ProtocolReceive functions of bound protocols.

NDIS assumes that, if this status is set for a packet in an indicated array, it is set for all subsequent packets in that array. Consequently, an indicating driver need not set the status for subsequent packets in an array after it sets this status for one packet descriptor.

Setting this status forces bound protocols to return ownership of each such packet descriptor, of any medium-specific buffer in the out-of-band data block for each packet descriptor, and of the memory mapped by each packet descriptor’s respective chained buffer descriptors to the indicating driver more quickly. The protocols must wait for a call to their ProtocolReceiveComplete functions to begin postprocessing the data they copied from indication(s) and forwarding the data to clients.

An indicating driver can set this status if it is running low on packet pool or buffer pool. An indicating NIC driver can set this status if it is running low on NIC receive buffers due to high network traffic. A busmaster DMA NIC driver might call NdisMAllocateSharedMemoryAsync in these circumstances to get additional NIC receive buffer space.

Getting Status on Return from NdisMIndicateReceivePacket

After such a driver has set the Status for some number of packet descriptors and called NdisMIndicateReceivePacket with the array of pointers to the packet descriptors, it must use the NDIS_GET_PACKET_STATUS macro when NdisMIndicateReceivePacket returns control to retrieve the Status set by NDIS.

The returned Status in the NDIS_PACKET_OOB_DATA block associated with each packet descriptor that the driver indicated with NDIS_STATUS_SUCCESS determines what the indicating driver does next, as follows:

NDIS_STATUS_SUCCESS
If this is set on return from a miniport’s call to NdisMIndicateReceivePacket, the miniport regains ownership of the following:
  • The packet descriptor

  • The associated out-of-band data block associated with the packet descriptor and of any media-specific information buffer specified in this block

  • All buffers mapped by buffer descriptors chained to the packet descriptor

NDIS guarantees that any packet descriptors for which the indicating driver set NDIS_STATUS_RESOURCES, as already described, will be returned from its call to NdisMIndicateReceivePacket with NDIS_STATUS_SUCCESS.

The driver can prepare these descriptors, the out-of-band block, and media-specific information buffer, if any, for reuse in subsequent receive indications immediately.

NDIS_STATUS_PENDING
If this is set on return from NdisMIndicateReceivePacket, protocols retain ownership of the packet descriptor, of its associated out-of-band data block and of any buffer specified in this block, and of all buffers mapped by buffer descriptors chained to the packet descriptor until the packet descriptor is returned to the indicating driver’s MiniportReturnPacket function.

Setting Status in MiniportSendPackets

Only underlying NDIS drivers with MiniportSendPackets functions can use the Status member of this structure for packet descriptors specifying sends. Such a driver’s MiniportSendPackets function sets the Status member in the out-of-band data blocks associated with the packet descriptors in the input array as follows:

  • If the driver will complete the send operation asynchronously, MiniportSendPackets sets NDIS_STATUS_PENDING in the Status member for the given packet descriptor.

  • If the driver cannot process all sends in a given packet array due to current resource constraints, MiniportSendPackets sets NDIS_STATUS_RESOURCES in the Status member for one packet descriptor.

    NDIS assumes that the Status members for all subsequent packet descriptors in the input array are set to the same value. NDIS queues these packet descriptors internally in the same order for resubmission to MiniportSendPackets when the driver calls NdisMSendResourcesAvailable or NdisMSendComplete, whichever occurs first. NDIS reflects this miniport-set status value to protocols as NDIS_STATUS_PENDING.

  • If MiniportSendPackets will complete a given send request before it returns control, it must set the Status member for the given packet descriptor to a driver-determined status value so that NDIS can reflect this status back to the protocol that initiated the send.

Otherwise, such a driver supplies the completion status for each packet when it calls NdisMSendComplete with the packet descriptor. Drivers that have MiniportSend instead of MiniportSendPackets functions return the status for each incoming send packet, so such a driver never sets the Status member of the out-of-band data block for a packet descriptor specifying a send.

Protocols cannot determine the completion status for a protocol-allocated packet descriptor from the Status member of the associated OOB block on return from NdisSendPackets or NdisSend. This value can change dynamically as NDIS submits, requeues, and resubmits send packet(s) to underlying drivers’ MiniportSendPackets and/or MiniportSend functions.

A protocol cannot use any NDIS_GET_PACKET_XXX macro nor the NDIS_OOB_DATA_FROM_PACKET macro to access the OOB data block for such a protocol-allocated packet descriptor until its ProtocolSendComplete function is called with the packet descriptor.

See Also

MiniportAllocateComplete, MiniportHandleInterrupt, MiniportReturnPacket, MiniportSend, MiniportSendPackets, MiniportTimer, NdisAllocateBuffer, NdisAllocateBufferPool, NdisAllocatePacket, NdisAllocatePacketPool, NdisGetCurrentSystemTime, NDIS_GET_PACKET_HEADER_SIZE, NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_GET_PACKET_STATUS, NDIS_GET_PACKET_TIME_RECEIVED, NDIS_GET_PACKET_TIME_SENT, NDIS_GET_PACKET_TIME_TO_SEND, NdisMIndicateReceivePacket, NdisMSendComplete, NdisMSendResourcesAvailable, NDIS_OOB_DATA_FROM_PACKET, NDIS_PACKET, NdisQueryReceiveInformation, NdisSend, NdisSendPackets, NDIS_SET_PACKET_HEADER_SIZE, NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO, NDIS_SET_PACKET_STATUS, NDIS_SET_PACKET_TIME_RECEIVED, NDIS_SET_PACKET_TIME_SENT, NDIS_SET_PACKET_TIME_TO_SEND, ProtocolReceive, ProtocolReceivePacket, ProtocolSendComplete


NDIS_PHYSICAL_ADDRESS

This type is equivalent to the system-defined LARGE_INTEGER type. For Windows NT drivers, the value is a 64-bit unsigned integer.

See Also

NdisGetPhysicalAddressHigh, NdisGetPhysicalAddressLow


NDIS_REQUEST

typedef struct _NDIS_REQUEST {
    UCHAR             MacReserved[16];
    NDIS_REQUEST_TYPE RequestType;
    union _DATA {
        struct QUERY_INFORMATION {
            NDIS_OID  Oid;
            PVOID     InformationBuffer;
            UINT      InformationBufferLength;
            UINT      BytesWritten;
            UINT      BytesNeeded;
        } QUERY_INFORMATION;
        struct SET_INFORMATION {
            NDIS_OID  Oid;
            PVOID     InformationBuffer;
            UINT      InformationBufferLength;
            UINT      BytesRead;
            UINT      BytesNeeded;
        } SET_INFORMATION;
   } DATA;
} NDIS_REQUEST, *PNDIS_REQUEST;

NDIS_REQUEST specifies a packet set up by NDIS or by a protocol for a call to NdisRequest. NDIS submits these requests to the underlying driver’s MiniportQueryInformation or MiniportSetInformation functions.

Members

MacReserved[16]
Specifies a 16-byte area reserved for use by the underlying driver.
RequestType
Specifies the request type as one of the following:
NdisRequestQueryInformation
Specifies a query-information request. NDIS forwards such a request to the underlying driver’s MiniportQueryInformation function.
NdisRequestSetInformation
Specifies a set-information request. NDIS forwards such a request to the underlying driver’s MiniportSetInformation function.
NdisRequestQueryStatistics
Specifies a query-statistics request. For most statistics queries, NDIS satisfies the request itself. Otherwise, NDIS forwards such request to the underlying driver’s MiniportQueryInformation function. This type of request originates in a user-mode application, never in a protocol driver.

For any of the preceding values, the OID_XXX specified in the Oid member must be compatible with the type of operation requested.

NdisRequestOpen
This type is obsolete. A protocol driver calls NdisOpenAdapter instead.
NdisRequestClose
This type is obsolete. A protocol driver calls NdisCloseAdapter instead.
NdisRequestSend
This type is obsolete. A protocol driver calls NdisSend or NdisSendPackets instead.
NdisRequestTransferData
This type is obsolete. A protocol driver calls NdisTransferData instead.
NdisRequestReset
This type is obsolete. A protocol driver calls NdisReset instead.
NdisRequestGeneric1
Specifies a NIC-driver-specific request.
NdisRequestGeneric2
Specifies a NIC-driver-specific request.
NdisRequestGeneric3
Specifies a NIC-driver-specific request.
NdisRequestGeneric4
Specifies a NIC-driver-specific request.
Oid
Specifies the object identifier of the requested operation. The value is an OID_XXX code.
InformationBuffer
Points to a buffer into which the underlying driver or NDIS returns the requested information for queries or from which the underlying driver reads caller-supplied information for sets.
InformationBufferLength
Specifies the size in bytes of the buffer at InformationBuffer. The value at Oid determines the value appropriate to this member.
BytesWritten
Specifies the number of bytes that the underlying driver or NDIS transfers into the buffer at InformationBuffer for query-information requests. If NdisRequest returns NDIS_STATUS_INVALID_LENGTH, the value of this member is meaningless.
BytesRead
Specifies the number of bytes that the underlying driver read from the buffer at InformationBuffer for set-information requests.
BytesNeeded
Specifies the number of bytes needed to return query information or to carry out the set operation requested by the given OID_XXX code.

If NdisRequest returns NDIS_STATUS_SUCCESS, the value of this member is meaningless. If the InformationBufferLength is too small for the given OID_XXX on a query, this member indicates how large a buffer is required to satisfy the request. If the buffer at InformationBuffer does not contain sufficient data for the given OID_XXX on a set, this member indicates how much data is required.

Comments

A protocol should allocate nonpaged memory for the buffer at InformationBuffer and for the NDIS_REQUEST packet itself. Passing in a packet or buffer allocated from paged memory can cause fatal page faults because the underlying driver(s) run at IRQL DISPATCH_LEVEL to carry out the requested operation.

NDIS_REQUEST contains a DATA substructure for each type of operation that a protocol driver can request of an underlying driver. Before calling NdisRequest, the protocol fills in the relevant members of the substructure that represents the query or set operation it specified in the Oid member. NDIS or the underlying driver fills in the remaining members before it returns control to the caller.

For more information about OID_XXX codes and which system-defined OIDs permit queries, statistics queries, and/or set requests, see Chapter 5.

The NdisRequestGeneric types are available for NIC drivers that create their own internal requests. To indicate a requested operation, a NIC driver sets an internal variable to one of these types.

See Also

MiniportQueryInformation, MiniportSetInformation, NdisCloseAdapter, NdisMQueryInformationComplete, NdisMSetInformationComplete, NdisOpenAdapter, NdisRequest, NdisReset, NdisSend, NdisSendPackets, NdisTransferData, ProtocolRequestComplete


NDIS_TAPI_EVENT

typedef struct _NDIS_TAPI_EVENT {
    IN    HTAPI_LINE  htLine,
    IN    HTAPI_CALL  htCall,
    IN    ULONG       ulMsg,
    IN    ULONG       ulParam1,
    IN    ULONG       ulParam2,
    IN    ULONG       ulParam3
} NDIS_TAPI_EVENT, *PNDIS_TAPI_EVENT;

NDIS_TAPI_EVENT is used when reporting spontaneously occurring events on the line or on a call on an established line to TAPI through NDISWAN.

Members

htLine
Specifies the TAPI handle for the line on which the event occurred.
htCall
Specifies the TAPI handle for the call on which the event occurred if this is a call-related event. For line-related events where there is no call, this parameter should be set to zero.
ulMsg
Specifies what kind of event is being reported. Interpretation of remaining members depends on the context indicated by ulMsg. The values of this parameter are defined in ndistapi.h, and they correspond to the names of the messages themselves, for example, LINE_NEWCALL.
ulParam1
Specifies a parameter for the message.
ulParam2
Specifies a parameter for the message.
ulParam3
Specifies a parameter for the message.

Comments

A WAN miniport calls NdisMIndicateStatus with this type of indication when a TAPI line or call event occurs, specifying NDIS_STATUS_TAPI_INDICATION as the value for GeneralStatus and supplying a pointer to an initialized structure of type NDIS_TAPI_EVENT.

See Also

NdisMIndicateStatus, ProtocolStatus


NDIS_WAN_FRAGMENT

typedef struct _NDIS_WAN_FRAGMENT {
    IN UCHAR  RemoteAddress[6];
    IN UCHAR  LocalAddress[6];
} NDIS_WAN_FRAGMENT, *PNDIS_WAN_FRAGMENT;

NDIS_WAN_FRAGMENT contains information associated with a fragment indication.

Members

RemoteAddress
Specifies the address of the remote node in Ethernet-style format, as set up when the link was established in the initial line-up indication.
LocalAddress
Specifies the address of the remote node in Ethernet-style format, as set up when the link was established in the initial line-up indication.

Comments

When a WAN miniport calls NdisMIndicateStatus with a fragment indication, NDISWAN forwards the indication to interested protocol(s), formatted in this structure.

See Also

NDIS_MAC_FRAGMENT, NdisMIndicateStatus, NDIS_WAN_LINE_DOWN, NDIS_WAN_LINE_UP, ProtocolStatus


NDIS_WAN_LINE_DOWN

typedef struct _NDIS_WAN_LINE_DOWN {
    IN UCHAR  RemoteAddress[6];
    IN UCHAR  LocalAddress[6];
} NDIS_WAN_LINE_DOWN, *PNDIS_WAN_LINE_DOWN;

NDIS_WAN_LINE_DOWN contains information associated with a line-down indication.

Members

RemoteAddress
Specifies the address of the remote node in Ethernet-style format, as set up when the link was established in the initial line-up indication.
LocalAddress
Specifies the address of the remote node in Ethernet-style format, as set up when the link was established in the initial line-up indication.

Comments

When a WAN miniport calls NdisMIndicateStatus with a line-down indication, NDISWAN forwards the indication to interested protocol(s), formatted in this structure.

See Also

NDIS_MAC_LINE_DOWN, NdisMIndicateStatus, NDIS_WAN_FRAGMENT, NDIS_WAN_LINE_UP, ProtocolStatus


NDIS_WAN_LINE_UP

typedef struct _NDIS_WAN_LINE_UP {
    IN ULONG             LinkSpeed;
    IN ULONG             MaximumTotalSize;
    IN NDIS_WAN_QUALITY  Quality;
    IN USHORT            SendWindow;
    IN UCHAR             RemoteAddress[6];
    IN OUT UCHAR         LocalAddress[6];
    IN ULONG             ProtocolBufferLength;
    IN PUCHAR            ProtocolBuffer;
    IN USHORT            ProtocolType;
    IN OUT NDIS_STRING   DeviceName;
} NDIS_WAN_LINE_UP, *PNDIS_WAN_LINE_UP;

NDIS_WAN_LINE_UP contains information associated with a line-up indication.

Members

LinkSpeed
Specifies the speed of the link, in units of 100 bps. Zero indicates no change from the speed returned when the protocol called NdisRequest with OID_GEN_LINK_SPEED.
MaximumTotalSize
Specifies the maximum number of bytes per packet that the protocol can send over the network. Zero indicates no change from the value returned when the protocol called NdisRequest with OID_GEN_MAXIMUM_TOTAL_SIZE.
Quality
Specifies the quality of service (QOS) of the new line.
SendWindow
Specifies the underlying WAN NIC driver’s send window, effectively its limit on outstanding send packets it will handle for this link. After submitting this number of packets to MiniportWanSend, NDISWAN will hold incoming sends queued until the driver completes an outstanding send.
RemoteAddress
Represents the address of the remote node on the link in Ethernet-style format. NDISWAN supplies this value.
LocalAddress
Represents the protocol-determined context for indications on this link in Ethernet-style format. NDISWAN reserves the two high-order bytes for its own use; the protocol should set the remainder to whatever should be the input parameter for this link to its ProtocolReceive and ProtocolStatus functions thereafter.
ProtocolBufferLength
Specifies the number of bytes in the buffer at ProtocolBuffer.
ProtocolBuffer
Points to a buffer, allocated by NDISWAN, containing protocol-specific information supplied by a higher-level component that makes connections through NDISWAN to the appropriate protocol(s). For example, this buffer would contain an IP address if the target protocol is IP or an IPX network number if the target protocol is IPX. NDISWAN frees this buffer when the target protocol returns control from the line-up indication.
ProtocolType
Specifies the assigned ether type of the protocol.
DeviceName
Specifies the name of the binding as a counted string in the system-default character set. For Windows NT drivers, this string contains Unicode characters. This string can be at most 64 characters.

Comments

When a WAN miniport calls NdisMIndicateStatus with a line-up indication, NDISWAN forwards the indication to interested protocol(s), formatted in this structure. At the initial line-up indication for a given link, the protocol should save any necessary state for subsequent communications on the link, including possible line-up, line-down, and fragment indications.

Subsequently, the protocol can use the SendWindow value as its upper bound on the number of calls it makes to NdisSend for this link before the protocol regains ownership of at least one of the packets it sent down to the underlying WAN NIC driver. Some NICs achieve higher throughput if they have several packets to send at once. Others could become unreliable if their drivers are flooded with send packets. NDISWAN also uses the miniport-set SendWindow value in this manner.

NDISWAN uses Ethernet-style address format to communicate with protocols, so the pair of Xxx Address members of this structure are six-byte addresses, used as follows:

  • NDISWAN uses the RemoteAddress to designate a particular link-protocol context. At the initial line-up indication for a link, the protocol should save the RemoteAddress supplied by NDISWAN. Subsequently, the protocol passes the RemoteAddress value to NDISWAN in calls to NdisSend for transmissions on this link.

  • The protocol uses LocalAddress to set up context for itself in the low-order four bytes (&LocalAddress[2]) when it receives the initial line-up indication for a link being established. Subsequently, NDISWAN passes the LocalAddress to the protocol at receive and status indications. In effect, NDISWAN uses the given LocalAddress as the destination for incoming receives and status indications on the link thereafter.

The DeviceName member is used with stacks in which a lower-level protocol has a single binding to NDISWAN but several bindings between it and a higher-level protocol in the stack, and this member might be used to support future PnP scenarios. That is, DeviceName would be used for either of the following:

  • To identify the binding (IN) to which a lower protocol should propagate the line-up indication it is given by NDISWAN

  • In a PnP environment, to report back (OUT) to NDISWAN the name of a newly created binding set up by a lower protocol at a line-up indication

For more information about the OID_XXX codes passed in calls to NdisRequest, see Chapter 5.

See Also

MiniportQueryInformation, MiniportSetInformation, NDIS_MAC_LINE_UP, NdisMIndicateStatus, NdisRequest, NDIS_WAN_FRAGMENT, NDIS_WAN_LINE_DOWN, ProtocolStatus


NDIS_WAN_PACKET

typedef struct _NDIS_WAN_PACKET {
    LIST_ENTRY          WanPacketQueue;
    PUCHAR              CurrentBuffer;
    ULONG               CurrentLength;
    PUCHAR              StartBuffer;
    PUCHAR              EndBuffer;
    PVOID               ProtocolReserved1;
    PVOID               ProtocolReserved2;
    PVOID               ProtocolReserved3;
    PVOID               ProtocolReserved4;
    PVOID               MacReserved1;
    PVOID               MacReserved2;
    PVOID               MacReserved3;
    PVOID               MacReserved4;
} NDIS_WAN_PACKET, *PNDIS_WAN_PACKET;

NDIS_WAN_PACKET defines the packet passed to the MiniportWanSend functions of WAN NIC drivers.

Members

WanPacketQueue
Specifies the head of the send packet queue, used by MiniportWanSend while the miniport is processing this packet.
CurrentBuffer
Specifies the base virtual address within the given buffer at which the net packet to be transmitted starts.
CurrentLength
Specifies the number of bytes of network packet data.
StartBuffer
Specifies the base virtual address for the given buffer, including padding.
EndBuffer
Specifies the virtual address for the end of the given buffer, including padding.
ProtocolReserved X
Specifies members reserved for use by protocols.
MacReserved X
Specifies members that the miniport can use for its own purposes.

Comments

When a protocol calls NdisSend with an NDIS_PACKET-type transmit request for an underlying WAN NIC driver, NDISWAN transforms the given packet into a packet formatted as this structure before calling the underlying driver’s MiniportWanSend function.

The StartBuffer and EndBuffer pointers specify a discrete virtual range. CurrentBuffer and CurrentLength specify a proper subrange of the range specified by StartBuffer and EndBuffer. Consequently, one of the following formulas yields the number of bytes of header or tail padding, if any, for the buffer passed in this structure to MiniportWanSend:

  • HeaderPadding = CurrentBuffer - StartBuffer

  • TailPadding = EndBuffer - (CurrentBuffer + CurrentLength)

NDISWAN always gives the underlying NIC driver at least as much header and tail padding as the driver returned in response to the OID_WAN_GET_INFO query during initialization.

MiniportWanSend can manipulate the buffer data in any manner. Until it completes a given send packet, either by returning a status other than NDIS_STATUS_PENDING from MiniportWanSend or by calling NdisMWanSendComplete, the NIC driver retains ownership of the given packet and of everything it specifies except the ProtocolReserved X members.

For more information about WAN-specific and about all system-defined OIDs, see Chapter 5.

See Also

MiniportQueryInformation, MiniportWanSend, NdisMWanSendComplete, NdisSend


Chapter 1 TDI Transport Driver Dispatch Routines

Like any other standard Windows NT kernel-mode intermediate driver, a TDI transport driver can have several Dispatch routines, up to one for each system-defined IRP_MJ_XXX code that the driver must handle.

This describes the set of I/O requests for which every transport driver that supports the TDI interface must provide one or more TdiDispatchXxx routines, and it describes the relevant members of each such IRP input to a transport driver's TdiDispatchXxx routine(s).

For more information about the architecture of standard kernel-mode intermediate drivers and the functionality of their Dispatch routines, see the Kernel-Mode Driver Design Guide. For more information about the structure of IRPs, the driver-accessible fields of device and file objects, and the kernel-mode support routines mentioned here, see the Kernel-Mode Driver Reference.

The following TdiDispatchXxx routines are described in alphabetic order. For convenience, this describes a separate TdiDispatchXxx routine for each IRP_MJ_XXX that any transport driver must handle in order to support the TDI interface.


TdiDispatchCleanup

NTSTATUS
TdiDispatchCleanup (
 IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp
 );

TdiDispatchCleanup completes any outstanding IRPs for an address, connection endpoint, or control channel that is about to be closed.

Parameters

DeviceObject
Points to the device object created by the TDI transport.
Irp
Points to an IRP with IRP_MJ_CLEANUP set as the MajorFunctionCode in the I/O stack location of the transport driver.

A transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own stack location in the IRP, shown in the following list as IrpSp. TdiDispatchCleanup can use the information set in the following members of the IRP in processing a cleanup request:

IoStatus.Status
Specifies the final status of the cleanup operation. The transport sets this member to the same value that will be returned by TdiDispatchCleanup. (IoStatus.Information is set to zero.)
IrpSp->MajorFunction
Specifies IRP_MJ_CLEANUP. The transport can ignore this member if it exports a TdiDispatchCleanup routine that handles only cleanup requests.
IrpSp->FileObject
Points to the open file object representing an address, connection endpoint, or control channel. The transport driver uses the value(s) of the FsContext and possibly FsContext2 field(s) in this file object to access the state that TdiDispatchCreate originally set up for the address, connection endpoint, or control channel when the file object was opened by the client.

Return Value

TdiDispatchCleanup returns STATUS_SUCCESS when its cleanup operation is done and the transport has completed any pending IRPs it was holding queued for the address, connection endpoint, or control channel.

Comments

TdiDispatchCleanup runs when the I/O Manager is closing the last handle to an open file object representing an address, connection endpoint, or control channel. TdiDispatchCleanup is responsible for completing any IRPs currently held in the transport that reference the open file object.

When the I/O Manager is removing its last reference to such a file object handle, it calls TdiDispatchClose. In other words, the I/O Manager always submits an IRP_MJ_CLEANUP request to a transport before it submits an IRP_MJ_CLOSE request for a particular file object.

Cleaning up for an address, connection endpoint, or control channel is an inherently synchronous operation. TdiDispatchCleanup can block waiting for internal driver functions to handle particular cleanup subtasks, but TdiDispatchCleanup must complete the input cleanup IRP itself.

Closing the handle to a file object that represents an address cancels the use of a transport address previously opened by a client. On receipt of a cleanup request for which the FileObject represents an address, the transport does the following:

  • Completes any pending requests directed to the address

  • Deregisters any client event handlers registered on the address

  • Optionally releases driver-allocated state set up to track the address, unless the transport defers resource deallocation to TdiDispatchClose

On return from TdiDispatchCleanup, the transport holds no outstanding IRPs queued for the address, but the driver can have outstanding references of its own to the file object representing that address, particularly if TdiDispatchCleanup did not release the driver-allocated context at FsContext and FsContext2 in the file object.

TdiDispatchCleanup can be called to close a connection endpoint even if the client has not made a request to disassociate the connection from the previously associated address. On receipt of a cleanup request for which the FileObject represents a connection endpoint, the transport does the following:

  • Cancels any active connection associated with the endpoint

  • If necessary, simulates the effects of a disassociation between the connection endpoint and the associated address

  • Deregisters any client event handlers registered on the connection

  • Completes any pending requests directed to the connection

  • Optionally releases driver-allocated state set up to track the connection, unless the transport defers resource deallocation to TdiDispatchClose

On return from TdiDispatchCleanup, the transport holds no outstanding sends or receives queued for the connection endpoint, but the driver can have outstanding references of its own to the file object representing that connection, particularly if TdiDispatchCleanup did not release the driver-allocated context at FsContext and FsContext2 in the file object.

On receipt of a cleanup request for which the FileObject represents a control channel, the transport completes any outstanding requests on the control channel. Such a request has no effect on addresses and connection endpoints open in the TDI driver.

How a transport handles pending IRPs when it receives a cleanup request is driver-determined. Usually, a transport sets STATUS_CANCELLED in the I/O status block and, then, calls IoCompleteRequest with each pending IRP. As an alternative, a transport might complete pending operations and complete each IRP when the requested operation was done; that is, return any resources allocated by the next lower driver for pending receives and transmit all pending sends for an address or connection endpoint, and collect a set of current statistics for a control channel before completing the corresponding IRPs.

In whatever manner a given transport handles pending IRPs for an address, connection endpoint, or control channel that is being closed, TdiDispatchCleanup cannot complete the cleanup IRP and return control until there are no outstanding IRPs that reference the FileObject in the given cleanup IRP.

By default, TdiDispatchCleanup runs at IRQL PASSIVE_LEVEL.

See Also

TdiDispatchClose, TdiDispatchCreate


TdiDispatchClose

NTSTATUS
TdiDispatchClose (
 IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp
 );

TdiDispatchClose closes an address, connection endpoint, or control channel.

Parameters

DeviceObject
Points to the device object created by the TDI transport.
Irp
Points to the IRP with IRP_MJ_CLOSE set as the MajorFunctionCode in the I/O stack location of the transport driver.

A transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own stack location in the IRP, shown in the following list as IrpSp. The transport can use the information set in the following members of the IRP in processing a close request:

IoStatus.Status
Specifies the final status of the close operation. The transport sets this member to the same value that will be returned by TdiDispatchClose. (IoStatus.Information is set to zero.)
IrpSp->MajorFunction
Specifies IRP_MJ_CLOSE. The transport can ignore this member if it exports a TdiDispatchClose routine that handles only close requests.
IrpSp->FileObject
Points to the file object representing an address, connection endpoint, or control channel. The transport driver uses the value(s) of the FsContext and possibly FsContext2 fields(s) in this file object to access any remaining driver-allocated state that TdiDispatchCleanup did not release already.

Return Value

TdiDispatchClose returns STATUS_SUCCESS when it has carried out any driver-determined operations, such as releasing resources allocated to track the given address, connection endpoint, or control channel, for the file object being closed.

Comments

TdiDispatchClose runs when the I/O Manager is releasing its last reference to the handle of the file object representing an address, connection endpoint, or control channel. Such a file object is deallocated when this last reference to the file handle has been released.

When the I/O Manager is closing the last handle to an open file object that represents an address, connection endpoint, or control channel, it calls TdiDispatchCleanup. In other words, the I/O Manager always submits an IRP_MJ_CLEANUP request to a transport before it submits an IRP_MJ_CLOSE request for a particular file object.

Closing an address, connection endpoint, or control channel is an inherently synchronous operation. TdiDispatchClose can block waiting for internal driver functions to handle particular subtasks, but TdiDispatchClose must complete the input close IRP itself.

TdiDispatchClose is responsible for releasing any driver-allocated resources associated with the given file object, unless all such resources were already released by TdiDispatchCleanup.

By default, TdiDispatchClose runs at IRQL PASSIVE_LEVEL.

See Also

TdiDispatchCleanup


TdiDispatchCreate

NTSTATUS
TdiDispatchCreate (
 IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp
 );

TdiDispatchCreate opens a file object that represents an address, connection endpoint, or control channel, setting up driver-allocated state to track subsequent I/O operations on the open file object.

Parameters

DeviceObject
Points to the device object created by the TDI transport.
Irp
Points to the IRP with IRP_MJ_CREATE set as the MajorFunctionCode in the I/O stack location of the transport driver.

A transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own stack location in the IRP, shown in the following list as IrpSp. The transport can use the information set in the following members of the IRP in processing a create request:

IoStatus.Status
Specifies the final status of the create/open operation. The transport sets this member to the same value that will be returned by TdiDispatchCreate. (IoStatus.Information is set to zero.)
IrpSp->MajorFunction
Specifies IRP_MJ_CREATE. The transport can ignore this member if it exports a TdiDispatchCreate routine that handles only create requests.
IrpSp->FileObject
Points to a file object that the I/O Manager creates to represent the address, connection endpoint, or control channel. The transport driver sets the FsContext and possibly FsContext2 fields(s) in this file object to access driver-allocated context area(s) set up in TdiDispatchCreate to track subsequent I/O requests on the address, connection endpoint, or control channel.
Irp->AssociatedIrp.SystemBuffer
Points to a FILE_FULL_EA_INFORMATION-structured buffer if the file object represents an address or a connection endpoint to be opened.

For an address, the EaName member is set to the system-defined constant TdiTransportAddress and the EA value following the EaName array is of type TRANSPORT_ADDRESS, set up by the client to specify the address to be opened. For some transports, this value can be a symbolic netBIOS or DNS name to be translated by the transport.

For a connection endpoint, the EaName member is set to the system-defined constant TdiConnectionContext and the EA value following the EaName array is a client-supplied handle, opaque to the transport driver. The transport must save this handle and subsequently pass it back to the client's registered event handlers for this connection.

If the given file object represents a control channel, this member is NULL.

IrpSp->Parameters.Create.EaLength
Specifies the size in bytes of the buffer at Irp->AssociatedIrp.SystemBuffer. If the given file object represents a control channel, specifies zero.
IrpSp->Parameters.Create.ShareAccess
If the given file object represents an address, specifies whether the client has requested an exclusive open of the file object. If the initial client to open an address sets either of FILE_SHARE_READ or FILE_SHARE_WRITE in the ShareAccess parameter to ZwCreateFile, the same address can be opened by other clients subsequently.
IrpSp->Parameters.Create.SecurityContext->AccessState
If the given file object represents a shareable address, defines the requested access to that address. The initial client to open a file object representing a particular address sets the access control list for that address. A TDI transport driver calls Security Reference Monitor (Se Xxx) routine(s) to check this member against the original ACL assigned to the corresponding file object when subsequent open-address requests occur. The transport fails subsequent open requests if the requested access is incompatible with the access rights granted in the original ACL.

Return Value

TdiDispatchCreate returns STATUS_SUCCESS if the create/open operation succeeds and the transport has set up whatever state it uses to track subsequent I/O operations for the address, connection endpoint, or control channel. Otherwise, it can return any driver-determined status, such as one of the following:

STATUS_INVALID_PARAMETER
A transport might return this value if it found EaName entries set with both TdiTransportAddress and TdiConnectionContext in the buffer at Irp->AssociatedIrp.SystemBuffer.
STATUS_SHARING_VIOLATION
A transport might return this value if an attempt is made to open a file object representing an address that has already been opened for exclusive access.
STATUS_NONEXISTENT_EA_ENTRY
A transport might return this value if an entry's EaName array is not followed by a valid address value of the given EaValueLength in the buffer at Irp->AssociatedIrp.SystemBuffer.
STATUS_INSUFFICIENT_RESOURCES
A transport might return this status if it could not allocate sufficient memory in which to maintain state information about the address, connection endpoint, or control channel.
STATUS_INVALID_DEVICE_REQUEST
A transport might return this status as a default error return or if the next lower driver's device currently is being closed.

Comments

Opening an address, connection endpoint, or control channel object is an inherently synchronous operation. TdiDispatchCreate can block waiting for internal driver functions to handle particular subtasks, such as initializing structures in driver-allocated context area(s) or checking the ACL for an address. However, TdiDispatchCreate should either set IoStatus.Status in the IRP to STATUS_SUCCESS or to an appropriate error status before it returns control.

A transport driver should fail any create/open request in which the buffer at Irp->AssociatedIrp.SystemBuffer contains EaName members set with TransportAddress and ConnectionContext. Clients must make separate calls to ZwCreateFile to open an address and a connection endpoint because a single file object cannot represent a combined address and connection endpoint.

A client opens a file object representing an address to indicate its transport address to the TDI driver. The first client to open such a file object can specify that it be either exclusive or shared. If the initial client specifies an exclusively opened file object, the transport driver fails all subsequent requests to open that address. If the initial client specifies a shared file object, the transport driver also fails all subsequent client requests for an exclusive open of that address. Otherwise, any number of clients can open the same file object for shared access of the same address.

Depending on the tranport, a client can specify a transport address to be opened or allow the transport driver to select an address. For example, a client might specify a TDI driver's broadcast address to receive datagrams in connectionless mode.

Opening a connection endpoint defines a client's end of a network connection. When TdiDispatchCreate processes such a request, it sets up the connection endpoint in an idle state. The client must eventually associate the open connection endpoint with an opened file object representing an address. That is, a transport does no I/O on an opened connection endpoint until it has been associated with an open address.

Opening a control channel sets up a control channel between a client and the TDI transport driver. A client can use an open control channel to query and set information not specifically tied to an address or connection endpoint. For example, a client might use an opened control channel to query the transport driver's broadcast address (used to receive broadcast datagrams) or to query performance statistics maintained in the transport.

By default, TdiDispatchCreate runs at IRQL PASSIVE_LEVEL.

See Also

TdiDispatchCleanup, TdiDispatchDeviceControl, TdiDispatchInternalDeviceControl, TRANSPORT_ADDRESS


TdiDispatchDeviceControl

NTSTATUS
TdiDispatchDeviceControl (
 IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp,
 IN PIO_STACK IrpSp
 );

TdiDispatchDeviceControl processes IRPs set with the major function code IRP_MJ_DEVICE_CONTROL.

Parameters

DeviceObject
Points to the device object created by the TDI driver.
Irp
Points to the IRP.
IrpSp
Points to the I/O stack location of the transport driver in the given IRP.

Return Value

TdiDispatchDeviceControl returns STATUS_SUCCESS if it satisfied the given request. Otherwise, it returns a driver-determined error status, depending on the given IOCTL_XXX minor function code in the IRP.

Comments

Usually, TdiDispatchDeviceControl calls TdiMapUserRequest and, if it returns STATUS_SUCCESS, calls the transport's TdiDispatchInternalDeviceControl function with the input DeviceObject and Irp pointers. If TdiMapUserRequest returns an error indicating that the given IOCTL_XXX in the Irp did not match any of the system-defined IOCTL_TDI_XXX codes, most transports simply complete the IRP with a driver-determined error status, such as STATUS_INVALID_DEVICE_REQUEST or STATUS_NOT_SUPPORTED.

However, if a transport supports any driver-defined "private" IOCTLs that a transport-dedicated application uses to communicate with the transport, its TdiDispatchDeviceControl switches on these IOCTL_XXX and calls internal driver functions to process them, as well as calling TdiDispatchInternalDeviceControl when TdiMapUserRequest returns STATUS_SUCCESS.

For more information about driver-defined IOCTL codes, see the Kernel-Mode Driver Reference.

See Also

TdiDispatchFastDeviceControl, TdiDispatchInternalDeviceControl, TdiMapUserRequest


TdiDispatchFastDeviceControl

BOOLEAN
TdiDispatchFastDeviceControl (
 IN STRUCT FILE_OBJECT *FileObject,
 IN BOOLEAN Wait,
 IN PVOID InputBuffer,
 IN ULONG InputBufferLength,
 IN OUT PVOID OutputBuffer,
 IN ULONG OutputBufferLength,
 IN ULONG IoControlCode,
 OUT PIO_STATUS_BLOCK IoStatus
 );

TdiDispatchFastDeviceControl is an optional TDI transport driver function that usually returns data already buffered internally by the transport.

Parameters

FileObject
Points to the file object representing the open address, connection endpoint, or control channel.
Wait
Specifies TRUE if the driver can wait in the context of the call to TdiDispatchFastDeviceControl.
InputBuffer
Optionally points to the input buffer, containing caller-supplied information required by the given IoControlCode.
InputBufferLength
Specifies the size in bytes of the input buffer.
OutputBuffer
Optionally points to the output buffer in which the transport returns any data requested.
OutputBufferLength
Specifies the size in bytes of the output buffer.
IoControlCode
Specifies the IOCTL_TDI_XXX code for the requested operation. This value must be one of the codes defined in ntddtdi.h.

The set of valid IOCTL_TDI_XXX codes depends on the fast I/O support, if any, provided by an individual transport and on what that transport currently holds in its internal buffers. In general, transports are most likely to buffer read-ahead data to satisfy anticipated IOCTL_TDI_RECEIVE requests and, possibly, data used to satisfy IOCTL_TDI_QUERY_INFORMATION requests.

The afd.sys driver, which provides support for Windows Sockets to communicate with underlying transports, also provides read-ahead fast I/O support for system transports that do not provide a fast I/O path for received data.

IoStatus
Points to a caller-supplied variable in which TdiDispatchFastDeviceControl returns the completion status, formatted as an IO_STATUS_BLOCK structure. TdiDispatchFastDeviceControl fills in the Status and Information members as appropriate for the given IoControlCode.

Return Value

TdiDispatchFastDeviceControl returns TRUE only if it satisfies or completes the kernel-mode client's request before returning control.

Comments

When preparing a fast I/O request, a client neither allocates an IRP nor uses an IRP passed in from a higher network layer.

When the I/O Manager calls TdiDispatchFastDeviceControl, a given OutputBuffer usually corresponds to the buffer at Irp->MdlAddress in a client-supplied IRP. The InputBuffer contains any client-supplied information for the request, depending on the IoControlCode. This request is different from other kernel requests in that system-defined user-mode IOCTLs identify the requests, as opposed to the TDI_XXX minor function codes listed with TdiBuildInternalDeviceControlIrp.

For example, for IOCTL_TDI_SEND, InputBuffer contains a client-supplied TDI_REQUEST_SEND structure, and OutputBuffer contains the actual data to be sent over the network. For IOCTL_TDI_RECEIVE, InputBuffer contains a TDI_REQUEST_RECEIVE structure, and the transport driver copies the receive data to OutputBuffer.

The definition of each IOCTL_TDI_XXX code in ntddtdi.h determines how buffers should be set up. See the Kernel-Mode Driver Reference for an explanation of how to interpret IOCTL_XXX codes.

When the buffer parameters have been set up for the transport, the client calls IoCallDriver to deliver the request to TdiDispatchFastDeviceControl if the transport exports this routine. If the transport cannot complete a fast I/O client request immediately, TdiDispatchFastDeviceControl returns FALSE and the I/O Manager repackages the given request into an IRP_MJ_DEVICE_CONTROL IRP and submits the IRP to the transport driver's TdiDispatchDeviceControl routine. Otherwise, TdiDispatchFastDeviceControl satisfies the request itself, possibly by calling an internal driver function to process the request, or it fails the request.

At initialization, a TDI transport driver enables its fast I/O path by setting the FastIoDispatch field in the input driver object to the address of a driver-allocated FAST_IO_DISPATCH structure. The transport must fill in the SizeOfFastIoDispatch member and set FastIoDeviceControl to the entry point of the transport's TdiDispatchFastDeviceControl function; all other members of the FAST_IO_DISPATCH structure must be set to NULL. For more information about this structure and about driver objects, see the Kernel-Mode Driver Reference.

If TdiDispatchFastDeviceControl returns TRUE, the transport relinquishes ownership of the original buffers to the client. If TdiDispatchFastDeviceControl cannot complete the request by using data already available in the transport, it should return FALSE immediately. When it returns FALSE, the I/O Manager sets up an IRP and resubmits the client's request to the transport's TdiDispatchDeviceControl routine.

By default, TdiDispatchFastDeviceControl runs at IRQL PASSIVE_LEVEL.

See Also

TdiBuildInternalDeviceControlIrp, TdiDispatchDeviceControl, TDI_QUERY_INFORMATION, TDI_RECEIVE, TDI_SEND


TdiDispatchInternalDeviceControl

NTSTATUS
TdiDispatchInternalDeviceControl (
 IN PDEVICE_OBJECT DeviceObject,
 IN PIRP Irp
 );

TdiDispatchInternalDeviceControl handles TDI_XXX device-control requests from kernel-mode clients.

Parameters

DeviceObject
Points to the device object created by the TDI driver.
Irp
Points to the IRP.

A transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. The transport can use the information set in the following members of the IRP in processing an internal device-control request:

IoStatus.Status
Specifies the final status of the operation. The transport sets this member to the same value that will be returned by TdiDispatchInternalDeviceControl.
IoStatus.Information
Usually, this member is initialized to zero. Depending on the IrpSp->MinorFunction value, the transport resets this member to the number of bytes of data it is returning to the client or to the number of bytes of data it transferred before the transport completes this IRP.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only these requests.
IrpSp->MinorFunction
Specifies the TDI_XXX code for the operation to be carried out.
IrpSp->FileObject
Points to the file object representing an address, connection endpoint, or control channel. The transport driver uses the value(s) of the FsContext and possibly FsContext2 fields(s) in this file object to access any driver-allocated state for operations on the address, connection endpoint, or control channel.

Return Value

TdiDispatchInternalDeviceControl returns STATUS_SUCCESS if it completed the requested operation successfully. Otherwise, it returns a driver-determined error status, such as STATUS_INVALID_DEVICE_REQUEST or STATUS_INVALID_DEVICE_STATE

Comments

When preparing a request, a kernel-mode client either allocates an IRP with TdiBuildInternalDeviceControlIrp or uses an IRP passed in from a higher network layer. The client calls a TdiBuild Xxx macro to set up the IRP for the underlying transport with the corresponding TDI_XXX as the MinorFunction code and IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction code in the IRP. When it has set up the IRP, the client calls IoCallDriver to submit the request to the underlying transport's TdiDispatchInternalDeviceControl routine.

Usually, a transport's TdiDispatchInternalDeviceControl routine determines whether the given file object at IrpSp->FileObject represents an address, connection endpoint, or control channel, because certain TDI_XXX are relevant only to addresses and certain are relevant only to connection endpoints, while a subset of TDI_XXX codes are relevant to both and to control channels. The following summarizes which TDI_XXX codes are likely to be processed for each:
FileObject represents an address

TDI_SEND_DATAGRAM
TDI_RECEIVE_DATAGRAM
TDI_SET_EVENT_HANDLER

FileObject represents a connection endpoint
TDI_SEND
TDI_RECEIVE
TDI_ASSOCIATE_ADDRESS
TDI_DISASSOCIATE_ADDRESS
TDI_CONNECT
TDI_DISCONNECT
TDI_LISTEN
TDI_ACCEPT
FileObject represents an address, connection endpoint, or control channel
TDI_QUERY_INFORMATION
TDI_SET_INFORMATION
TDI_ACTION

For each TDI_XXX the transport handles, its TdiDispatchInternalDeviceControl routine usually calls an internal driver function to carry out the requested operation. Possibly, the internal driver function also complete the IRP, particularly if that function transfers data because it then can set IoStatus.Information to the appropriate value, leaving TdiDispatchInternalDeviceControl to complete IRPs in which this member is set to zero. Each such internal function usually returns an NTSTATUS-type value, which TdiDispatchInternalDeviceControl propagates when it returns control.

For more information about each of the TDI_XXX mentioned here, see Chapter 2, next.

By default, TdiDispatchInternalDeviceControl runs at IRQL PASSIVE_LEVEL.

See Also

TdiBuildInternalDeviceControlIrp, TdiDispatchDeviceControl


Chapter 2 TDI IOCTLs for Transport Drivers

TDI transport drivers respond to the TDI_XXX IOCTL requests issued by their kernel-mode clients that are described here. Each IRP submitted to such a driver’s TdiDispatchInternalDeviceControl routine has the MinorFunctionCode member of the transport's I/O stack location set with one of these TDI_XXX codes.

Typically, a transport's TdiDispatchInternalDeviceControl routine switches on the TDI_XXX code to route each incoming client request to an internal driver function for further processing.

Some structures mentioned in the TDI_XXX references that follow are described in Part 2, Chapter 6. See the Kernel-Mode Driver Design Guide and Reference for more information about the internal structure of IRPs and accessible fields within file objects mentioned here.

The TDI_XXX codes described next are in alphabetic order.


TDI_ACCEPT

When a kernel-mode client makes a TDI_ACCEPT request, it asks the underlying TDI transport driver to accept an incoming connection offer from a remote-node peer and, thereby, to enable network data transfers on the endpoint-to-endpoint connection between that local-node client and its remote-node peer.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the accept request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_INVALID_CONNECTION

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_ACCEPT.
IrpSp->FileObject
Points to an open file object representing the connection endpoint at which a listen request has completed. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.
IrpSp->Parameters
Specifies a TDI_REQUEST_KERNEL_ACCEPT structure, defined as follows:
struct _TDI_REQUEST_KERNEL_ACCEPT {
    PTDI_CONNECTION_INFORMATION RequestConnectionInformation;
    PTDI_CONNECTION_INFORMATION ReturnConnectionInformation;
} TDI_REQUEST_KERNEL_ACCEPT, *PTDI_REQUEST_KERNEL_ACCEPT;

The transport uses the members of this structure as follows:

RequestConnectionInformation
Points to a TDI_CONNECTION_INFORMATION structure, possibly containing connection information supplied by the client. This information can include a pointer (in the UserData member) to a buffer of client-supplied accept data to be sent to the remote node when the transport notifies the remote-node transport that the offered connection is accepted.
ReturnConnectionInformation
Points to a client-supplied variable of type TDI_CONNECTION_INFORMATION in which the transport returns connection information, if any, to the client. This member is NULL if the transport does not return information for delayed-connection acceptances.

Comments

In normal operation, a client submits an accept request in response to a return from a preceding listen request in which it set the TDI_QUERY_ACCEPT flag. If the client did not set this flag in its TDI_LISTEN request for this connection, the transport should fail this accept request.

The transport can call already registered ClientEvent(Chained)Receive(Expedited) handler(s) with incoming receive(s) from the remote node on this connection before the I/O Manager formally completes its processing of the client's accept IRP.

Most TDI transport drivers have a time-out feature to prevent communication sequences from hanging when a remote node is not responding. Therefore, a TDI client must either accept or reject a connection as soon as possible after completion of its listen request. As a general rule, a transport-determined time-out interval between client notification of an offered connection and the local-node client's connection acceptance or rejection should be less than one second.

TdiBuildAccept is the macro a client uses to fill in this IRP.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, TdiBuildAccept, TDI_CONNECTION_INFORMATION, TdiDispatchInternalDeviceControl, TDI_LISTEN, TDI_RECEIVE


TDI_ACTION

When a kernel-mode client makes a TDI_ACTION request, it asks the underlying TDI transport driver to make transport-specific extensions available to that client.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the action request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_PENDING
STATUS_INVALID_CONNECTION
STATUS_INSUFFICIENT_RESOURCES
STATUS_NOT_IMPLEMENTED

IoStatus.Information
Specifies how many bytes of data were transferred for this request. The transport sets this member of the I/O status block before it completes the IRP.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_ACTION.
IrpSp->FileObject
Points to an open file object representing an address, connection endpoint, or control channel. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this address, connection, or control channel.
MdlAddress
Points to an MDL, possibly the initial MDL in a chain, mapping a buffer containing the client-supplied action parameter block. The initial part of this buffer always contains a system-defined TDI_ACTION_HEADER structure, followed by whatever transport-defined contents are required to carry out the action request.

Comments

The successful completion of any client's action request makes the transport-provided extension applicable to that client but not to any other TDI clients or drivers. The set of possible actions is defined by the transport at the discretion of the TDI transport driver writer.

Such a client makes an action request for transport-defined extensions related to an address, a connection, or a control channel. The client supplies a buffer containing all information concerning the requested action in the parameter block mapped at MdlAddress.

Every action parameter block begins with a TDI_ACTION_HEADER containing a transport identifier and an action code. On receipt of such a request, the transport uses the value of TransportId in this header to validate the request. The ActionCode member specifies the transport-defined operation to carry out and determines the transport-required contents for the remainder of the parameter block.

TdiBuildAction is the macro a client uses to fill in this IRP.

See Also

TDI_ACTION_HEADER, TdiBuildAction, TdiDispatchInternalDeviceControl


TDI_ASSOCIATE_ADDRESS

When a kernel-mode client makes a TDI_ASSOCIATE_ADDRESS request, it asks the underlying TDI transport driver to make an association between a particular open local-node address and an open connection endpoint.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the associate-address request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_CONNECTION_ESTABLISHED
STATUS_INVALID_CONNECTION
STATUS_INVALID_HANDLE

The IoStatus.Information member is zero since this request transfers no data.

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_ASSOCIATE_ADDRESS.
IrpSp->FileObject
Points to an open file object representing the connection endpoint to be associated with the address specified at IrpSp->Parameters. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about the connection.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_ASSOCIATE structure, defined as follows:
struct _TDI_REQUEST_KERNEL_ASSOCIATE {
    HANDLE  AddressHandle;
} TDI_REQUEST_KERNEL_ASSOCIATE, *PTDI_REQUEST_KERNEL_ASSOCIATE;

The transport uses the member of this structure as follows:

AddressHandle
Specifies a client-supplied handle to the file object representing the address.

The transport can call ObReferenceObjectByHandle to convert this handle into a pointer to the file object, which produces a reference to the file object representing the local-node address to be associated with the connection endpoint. The transport uses the FsContext and, possibly, FsContext2 fields of this file object to access the state it maintains about this address.

Comments

A kernel-mode client must make an associate-address request before it makes an endpoint-to-endpoint connection to the remote node either with a TDI_LISTEN request, optionally followed by a TDI_ACCEPT request, to the transport driver or with a TDI_CONNECT request to the transport driver. The client can get or set information about the connection with TDI_QUERY_INFORMATION or TDI_SET_INFORMATION requests before making an associate-address request.

After the connection endpoint has been associated with the address, the client can make any other TDI_XXX requests to the transport on the connection with one exception: it cannot make a TDI_ASSOCIATE_ADDRESS request again for the specific endpoint until it makes a successful TDI_DISASSOCIATE_ADDRESS request to the transport.

For example, a client might register one or more ClientEvent(Chained)Receive(Expedited) handlers on the address by submitting one or more TDI_SET_EVENT_HANDLER requests if its subsequent TDI_LISTEN request will be satisfied as soon as a remote-node peer offers a connection. On the other hand, a client that sets up a listen for a delayed-connection acceptance might defer registering receive-event handler(s) until just before it submits a TDI_ACCEPT for an offered connection.

TdiBuildAssociateAddress is the macro a client uses to fill in this IRP.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, TDI_ACCEPT, TdiBuildAssociateAddress, TDI_CONNECT, TDI_DISASSOCIATE_ADDRESS, TdiDispatchInternalDeviceControl, TDI_LISTEN, TDI_QUERY_INFORMATION, TDI_SET_EVENT_HANDLER, TDI_SET_INFORMATION


TDI_CONNECT

When a kernel-mode client makes a TDI_CONNECT request, it asks the underlying TDI transport driver to offer a connection on a particular local-node connection endpoint to a remote-node peer.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the connection request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_PENDING
STATUS_BAD_NETWORK_PATH
STATUS_INVALID_CONNECTION
STATUS_REMOTE_NOT_LISTENING
STATUS_INSUFFICIENT_RESOURCES
STATUS_REQUEST_TIMED_OUT

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_CONNECT.
IrpSp->FileObject
Points to an open file object representing the connection endpoint already associated with a local-node address. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_CONNECT structure, equivalent to the TDI_REQUEST_KERNEL structure.

The RequestFlags member is irrelevant for connect requests. The transport uses the client-supplied information, such as the remote-node address to which the connection should be offered, in the buffer at RequestConnectionInformation. The transport returns information to the client-supplied buffer at ReturnConnectionInformation. Each of these buffers is formatted as a TDI_CONNECTION_INFORMATION structure.

IrpSp->Parameters.RequestSpecific
Points to a LARGE_INTEGER variable specifying the time-out value for the connection offer or is NULL. An explicit time out is specified as the negative of the number of 100-nanosecond units, relative to the current system time, for the time-out interval. If this member is NULL, the transport must choose an appropriate time-out interval, usually something less than one second.

Comments

For a local-node client to establish an endpoint-to-endpoint connection with a remote-node peer, it must associate an idle local connection endpoint with an open transport address before making a TDI_CONNECT request. If the specified local endpoint is active or nonexistent, the transport fails the connect request.

For a TDI_CONNECT request to succeed, the remote-node client must have an open TDI_LISTEN request or have registered its ClientEventConnect handler to receive connection offers indicated by the underlying transport on the remote node.

TdiBuildConnect is the macro a client uses to fill in the IRP.

See Also

ClientEventConnect, TdiBuildConnect, TDI_CONNECTION_INFORMATION, TdiDispatchInternalDeviceControl, TDI_LISTEN, TDI_REQUEST_KERNEL


TDI_DISASSOCIATE_ADDRESS

When a kernel-mode client makes a TDI_DISASSOCIATE_ADDRESS request, it asks the underlying TDI transport driver to break an established association between a particular local-node address and a connection endpoint.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the disassociate-address request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_INVALID_CONNECTION

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_DISASSOCIATE_ADDRESS.
IrpSp->FileObject
Points to an open file object representing the connection endpoint to be disassociated from a local-node address. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about the connection.

Comments

A client makes this request to disassociate a connection endpoint for an inactive connection from the associated local-node address, whether that client initiated the disconnection from its remote-node peer or vice versa.

After the address has been disassociated, the client can reassociate it with another connection endpoint or reassociate the connection endpoint with another open local-node address by making another TDI_ASSOCIATE_ADDRESS request. Consequently, the transport cannot assume that the file objects representing such an address and connection endpoint will be closed following the completion of the TDI_DISASSOCIATE_ADDRESS request.

TdiBuildDisassociateAddress is the macro a client uses to fill in this IRP.

See Also

TDI_ASSOCIATE_ADDRESS, TdiBuildDisassociateAddress, TDI_DISCONNECT, TdiDispatchInternalDeviceControl


TDI_DISCONNECT

When a kernel-mode client makes a TDI_DISCONNECT request, it asks the underlying TDI transport driver to make a disconnect indication to the remote node, to acknowledge a disconnect indication from the remote node for an established endpoint-to-endpoint connection, or to reject an offered connection by a remote-node peer.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the disconnect request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_PENDING
STATUS_INVALID_CONNECTION
STATUS_REQUEST_TIMED_OUT

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_DISCONNECT.
IrpSp->FileObject
Points to an open file object representing the connection endpoint. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_DISCONNECT structure, equivalent to the TDI_REQUEST_KERNEL structure.

Some transport drivers simply terminate all activity on the given connection immediately. Others use the client-supplied value of the RequestFlags member to support controlled disconnects. For such a transport, RequestFlags can be set to either of the following:

TDI_DISCONNECT_ABORT
Specifies that the transport should close the connection immediately without completing its pending operations on the connection. This is the default behavior expected of all transports, including transports that do not support controlled disconnects. In effect, this value for RequestFlags directs the transport to do the same things as if RequestFlags were zero: the transport should refuse any incoming requests for the connection, stop all activity at the specified connection endpoint, and cancel any outstanding requests it is holding queued internally for the connection.

When a preceding disconnect request made with TDI_DISCONNECT_RELEASE remains pending on the connection, the client can issue another disconnect request with TDI_DISCONNECT_ABORT, thereby forcing the underlying transport to close the connection immediately.

TDI_DISCONNECT_RELEASE
Specifies that the transport should initiate a controlled disconnect or confirms a controlled disconnect that was initiated from the remote node, as described later (see Comments).

The tranport can use client-supplied information in the buffer at RequestConnectionInformation to supply disconnection data, possibly supplemented with transport-provided status information, to the transport on the remote node. This buffer is formatted as a TDI_CONNECTION_INFORMATION structure.

The transport can return disconnect data and any status information supplied by the transport on the remote node in the client-supplied buffer at ReturnConnectionInformation. This buffer is formatted as a TDI_CONNECTION_INFORMATION structure. The contents of this buffer remain valid only until the client reuses the connection in a subsequent TDI_LISTEN or TDI_CONNECT request.

The TDI_DISCONNECT_WAIT flag is unused.

IrpSp->Parameters.RequestSpecific
Points to a LARGE_INTEGER variable specifying the time-out interval for the disconnect operation or is NULL. An explicit time out is specified as the negative of the number of 100-nanosecond units, relative to the current system time, for the time-out interval. If this member is NULL, the transport must choose an appropriate time-out value, usually something less than one second.

Comments

During a disconnect operation, the underlying transport driver usually refuses any incoming requests for an established connection and stops all activity at the specified connection endpoint, unless the transport supports controlled disconnects. That is, the completion of a client's disconnect request usually implies cessation of all activity, including receive indications, on the given connection.

By default, TDI_DISCONNECT requests the disconnection of an endpoint-to-endpoint connection that is not a controlled-disconnect operation: the transport need not complete outstanding I/O requests on the connection before it completes the disconnect request and returns control. For such a disconnect operation, the transport typically cancels all outstanding requests on the given connection, and the corresponding remote-node transport need not have its client confirm the disconnection before the local-node transport can complete its client's disconnect request and return control.

Clients on an endpoint-to-endpoint connection can request a controlled disconnect if their underlying TDI transports support this. If so, the client that initiates the disconnect operation with a TDI_DISCONNECT request causes both transports to coordinate their operations so the remote-node peer is informed that its endpoint-to-endpoint connection is closing. In these circumstances, the remote-node client must confirm the disconnect before the underlying transport can return from the initiating (local-node) client's disconnect request.

The following summarizes the sequence of operations for a controlled disconnect:

  1. The local-node client initiates a disconnection by making a disconnect request with TDI_DISCONNECT_RELEASE set in RequestFlags. Subsequently, this client can no longer send data on the connection, but it can continue to receive data from its remote-node peer. When such a local-node client makes its disconnect request, the underlying transport transmits the data for any pending send requests it is currently holding and completes these requests back to its client, but it notifies the remote-node transport that a disconnect is occurring.

  2. The transport on the remote node performs the usual operations for any pending receive requests made by its client. Then, the remote-node transport notifies its client of the pending disconnect, either by calling ClientEventDisconnect if the remote-node client has registered this handler or by returning an error code on an outstanding request made by its client. After such a notification, the remote-node client can no longer receive data on the connection, but it can continue to send data until it makes a TDI_DISCONNECT request with TDI_DISCONNECT_RELEASE set in the RequestFlags.

  3. When the remote-node client has finished sending data, it makes a disconnect request to its underlying transport with TDI_DISCONNECT_RELEASE set in RequestFlags. The transport completes all outstanding sends on the connection normally before completing its client's disconnect request. When the transport completes this disconnect request, the connection is closed from the point of view of its remote-node client. However, the connection endpoint is still valid and that client can reuse it later.

  4. The remote-node transport notifies the local-node transport to confirm disconnect completion on the remote node. At this point, the original disconnect request is completed by the local-node transport and the connection is closed from the point of view of the local-node client that initiated the staged disconnection. However, the connection endpoint is still valid and the client can reuse it.

If the underlying transport supports delayed-connection acceptances, its clients also can submit TDI_DISCONNECT requests to reject incoming connection offers from remote nodes. Such a client is responding to a completed TDI_LISTEN request in which the client set the TDI_QUERY_ACCEPT flag or to a offer relayed by the transport to that client's registered ClientEventConnect handler.

TdiBuildDisconnect is the macro a client uses to fill in this IRP.

See Also

ClientEventConnect, TdiBuildDisconnect, TDI_CONNECTION_INFORMATION, TdiDispatchInternalDeviceControl, TDI_LISTEN, TDI_REQUEST_KERNEL


TDI_LISTEN

When a kernel-mode client makes a TDI_LISTEN request, it asks the underlying TDI transport driver to listen for an offer to make an endpoint-to-endpoint connection from a remote node.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the listen request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_PENDING
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_CONNECTION

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_LISTEN.
IrpSp->FileObject
Points to an open file object representing the connection endpoint. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.

This connection endpoint was previously associated with a local-node address when the client made a successful TDI_ASSOCIATE_ADDRESS request to the transport.

IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_LISTEN structure, equivalent to the TDI_REQUEST_KERNEL structure.

A client can set the RequestFlags member with TDI_QUERY_ACCEPT, directing the transport to make a delayed-connection acceptance: that is, to complete this listen request to the client when an appropriate connection offer, as specified by RequestConnectionInformation, comes in from the remote node so the client can determine whether to accept the connection by issuing a TDI_ACCEPT request or to reject the remote-node connection offer with a TDI_DISCONNECT request.

Otherwise, when RequestFlags is zero, the underlying transport should accept any appropriate incoming connection offer immediately. If TDI_QUERY_ACCEPT is set but the transport does not support delayed-connection acceptances, the transport fails this listen request.

The client supplies information in the TDI_CONNECTION_INFORMATION-type buffer at RequestConnectionInformation that the transport subsequently uses to determine whether to accept a connection offer from a remote node, as follows:

UserData
Points to a buffer of client-supplied data for the tranport to transmit to the remote node that requests a connection. This member is NULL if the transport supports delayed-connection acceptances and the client set RequestFlags with TDI_QUERY_ACCEPT.
UserDataLength
Specifies the size in bytes of the buffer at UserData, zero if UserData is NULL.
Options
Matches the value set in the RequestFlags member of the TDI_REQUEST_KERNEL_LISTEN structure.
OptionsLength
Should be set to sizeof(ULONG); irrelevant if TDI_QUERY_ACCEPT is not set in RequestFlags.
RemoteAddress
Points to a buffer specifying the remote-node address for which the local-node client listens in anticipation of a connection offer. This specification can be a partial address if the underlying transport defines the conventions and syntax for specifying partial addresses. This member also can be NULL, indicating that the client will accept an endpoint-to-endpoint connection offer from any remote node.
RemoteAddressLength
Specifies the size in bytes of the buffer at RemoteAddress. Zero indicates that a connection offer from any remote node is acceptable to this client.

The client-supplied buffer at ReturnConnectionInformation in the TDI_REQUEST_KERNEL structure is also formatted as a TDI_CONNECTION_INFORMATION structure. The transport returns information about the connection offer from the remote node in this buffer on completion of this listen request.

Comments

Depending on whether TDI_QUERY_ACCEPT is set in the RequestFlags, the transport completes the listen request either by accepting an offered remote-node connection on behalf of its local-node client or by deferring acceptance or rejection of such an offer to that client.

To initiate a listen request successfully, the local-node client must have its transport address already associated with an idle connection endpoint. That is, the transport has previously satisfied a TDI_ASSOCIATE_ADDRESS request for the file object at IrpSp->FileObject that represents the connection. Part of the context that each transport maintains at FsContext or FsContext2 for a connection endpoint is a pointer to the file object that represents the associated address, which the transport sets up when it makes the association.

After issuing a successful TDI_ASSOCIATE_ADDRESS request, a client can then issue any number of TDI_LISTEN requests, which the transport driver should process in FIFO order until the client accepts an offered connection.

The transport can process requests coming in from all its clients in any driver-determined order.

If a client registered its ClientEvent(Chained)Receive and/or ClientEvent(Chained)ReceiveExpedited handler(s) on the address associated with the connection endpoint before making its TDI_LISTEN request, receives can come in for that client as soon as the transport accepts the connection from a remote node, even before the transport has completed the client's listen request. Such a client must be prepared for an endpoint-to-endpoint connection to be established as soon as it makes a listen request, and the tranport must be prepared to indicate receives to that client concurrently with completion of the listen request.

In general, a TDI transport driver cannot call a registered ClientEventConnect handler as long as a listen request is pending on the client's open file object that represents the associated local-node address. However, the driver can call ClientEventConnect when such a listen is pending if the transport determines that the remote-address acceptance criteria supplied at IrpSp->Parameters.RequestConnectionInformation will prevent the listen operation from being completed.

Client-supplied remote-node addressing information acts as a filter that the transport applies before checking the RequestFlags for a pending listen request. When an incoming connection offer comes from a remote-node address that does not match the address specified in the listen IRP, the transport cannot complete the listen for its client. When an incoming connection offer from a remote node satisfies the given address-acceptance criteria, the transport can satisfy the client's listen request in either of the following ways:

  • If TDI_QUERY_ACCEPT is not set in the RequestFlags or if the driver does not support delayed-connection acceptance, the transport sends an acceptance to the remote node, sets up the buffer at IrpSp->Parameters.ReturnConectionInformation with appropriate data, as well as setting the status block of the IRP, and returns control.

  • If TDI_QUERY_ACCEPT is set in the RequestFlags, the transport sets up the buffer at IrpSp->Parameters.ReturnConnectionInformation and sets the status block of the IRP, sets up a time-out interval for waiting on the client's acceptance or rejection of the offered connection, and returns control. The local-node client must either accept or reject the offered connection within the time-out interval; otherwise, the tranport must send a rejection to the remote node.

TDI drivers vary in how they handle partial addresses. Because the syntax of a transport address is TDI-driver-specific, the mechanism for specifying a partial address is also specific to each transport driver.

In addition to supporting partial-address specifications on listens, TDI transports can support quality-of-service (QOS) specifications by their clients. For example, a transport might allow its clients to specify QOS using a variable-length counted string with TDI-driver-specific syntax and semantics.

TdiBuildListen is the macro a client uses to fill in this IRP.

See Also

ClientEventConnect, ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, TDI_ACCEPT, TDI_ASSOCIATE_ADDRESS, TdiBuildListen, TDI_CONNECTION_INFORMATION, TDI_DISCONNECT, TdiDispatchInternalDeviceControl, TDI_REQUEST_KERNEL, TDI_SET_EVENT_HANDLER


TDI_QUERY_INFORMATION

When a kernel-mode client makes a TDI_QUERY_INFORMATION request, it asks the underlying TDI transport driver to return information of a client-specified type, such as a broadcast address, the tranport's capabilities, or its current statistics for I/O on a particular connection.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the query-information request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_BUFFER_OVERFLOW
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_CONNECTION
STATUS_INVALID_DEVICE_REQUEST
STATUS_NOT_IMPLEMENTED

IoStatus.Information
Specifies the number of bytes of information the transport is returning to its client.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_QUERY_INFORMATION.
IrpSp->FileObject
Points to an open file object representing an address, connection endpoint, or control channel. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this address, connection endpoint, or control channel.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_QUERY_INFO structure, defined as follows:
struct _TDI_REQUEST_KERNEL_QUERY_INFO {
    LONG QueryType;
    PTDI_CONNECTION_INFORMATION RequestConnectionInformation;
} TDI_REQUEST_KERNEL_QUERY_INFORMATION, *PTDI_REQUEST_KERNEL_QUERY_INFORMATION;

A transport can ignore the RequestConnectionInformation member; it is always NULL. The transport checks the QueryType member to determine what information the client requested, which also determines the format of the information to be returned and, sometimes the client-supplied contents, of the buffer mapped at Irp->MdlAddress.

For every TDI transport, QueryType can be set to one of the following system-defined values:

TDI_QUERY_ADDRESS_INFO
Requests the transport to return information about a particular local-node address, formatted as a TDI_ADDRESS_INFO structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing the address or to a connection endpoint already associated with the address for which to return the information.

TDI_QUERY_CONNECTION_INFO
Requests the transport to return information about a particular connection, formatted as a TDI_CONNECTION_INFO structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing the connection for which to return the information.

TDI_QUERY_BROADCAST_ADDRESS
Requests the transport to return a broadcast address, formatted as a TRANSPORT_ADDRESS structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel.

TDI_QUERY_NETWORK_ADDRESS
Requests the transport to return a local-node address, formatted as a TRANSPORT_ADDRESS structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel.

TDI_QUERY_DATA_LINK_ADDRESS
Requests the transport to return the datalink address, formatted as a TRANSPORT_ADDRESS structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel.

TDI_QUERY_PROVIDER_INFO or TDI_QUERY_PROVIDER_INFORMATION
Requests the transport to return information about its own capabilities, formatted as a TDI_PROVIDER_INFO structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel.

TDI_QUERY_PROVIDER_STATISTICS
Requests the transport to return information about its resource usage, formatted as a TDI_PROVIDER_STATISTICS structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel.

TDI_QUERY_DATAGRAM_INFO
Requests the transport to return information about its limits on size and number of datagrams, formatted as a TDI_DATAGRAM_INFO structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel. A transport also can support this query on open transport addresses at the discretion of the transport writer.

TDI_QUERY_MAX_DATAGRAM_INFO
Requests the transport to return information about its limit on datagram size, formatted as a TDI_MAX_DATAGRAM_INFO structure, in the client-supplied buffer mapped at MdlAddress.

IrpSp->FileObject must point to a file object representing a control channel. A transport also can support this query on open transport addresses at the discretion of the transport writer.

In addition to the preceding queries, which every TDI transport must support, a particular transport might support others. For example, NetBIOS transports also must respond to three additional system-defined TDI_QUERY_XXX, as described later (see Comments).

MdlAddress
Points to an MDL, possibly the initial MDL in a chain of MDLs, mapping a client-supplied buffer containing whatever client-supplied contents are required to carry out the request. The input contents and format of this buffer depend on the given QueryType.

Comments

For any NetBIOS transport, QueryType also can specify one of the following:

TDI_QUERY_FIND_NAME
Requests the NetBIOS transport to return the information, formatted as FIND_NAME_HEADER followed by FIND_NAME_BUFFER structures, in the client-supplied buffer mapped at MdlAddress.
TDI_QUERY_SESSION_STATUS
Requests the transport to return the information, formatted as SESSION_HEADER followed by SESSION_BUFFER structures, in the client-supplied buffer mapped at MdlAddress.
TDI_QUERY_ADAPTER_STATUS
Requests the transport to return the information, formatted as an ADAPTER_STATUS structure, in the client-supplied buffer mapped at MdlAddress.

For more information about the preceding NetBIOS-specific structures, see the Win32 SDK.

TdiBuildQueryInformation is the macro a client uses to fill in this IRP.

See Also

TDI_ADDRESS_INFO, TDI_CONNECTION_INFO, TDI_CONNECTION_INFORMATION, TDI_DATAGRAM_INFO, TdiDispatchInternalDeviceControl, TDI_MAX_DATAGRAM_INFO, TDI_PROVIDER_INFO, TDI_PROVIDER_STATISTICS, TdiBuildQueryInformation, TRANSPORT_ADDRESS


TDI_RECEIVE

When a kernel-mode client makes a TDI_RECEIVE request, it asks the underlying TDI transport driver to indicate a normal or expedited TSDU on an established endpoint-to-endpoint connection.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the receive request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_PENDING
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_CONNECTION
STATUS_BUFFER_OVERFLOW

IoStatus.Information
Specifies the number of bytes of received data the transport is returning in the client-supplied buffer.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_RECEIVE.
IrpSp->FileObject
Points to an open file object representing the connection endpoint. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.

This client already established an endpoint-to-endpoint connection with a remote-node peer on this connection endpoint.

IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_RECEIVE structure, defined as follows:
struct _TDI_REQUEST_KERNEL_RECEIVE {
    ULONG ReceiveLength;
    ULONG ReceiveFlags;
} TDI_REQUEST_KERNEL_RECEIVE, *PTDI_REQUEST_KERNEL_RECEIVE;

The transport uses the members of this structure as follows:

ReceiveLength
Specifies the size in bytes of the client-supplied buffer mapped at Irp->MdlAddress.
ReceiveFlags
Specifies the type of receive this client is requesting, as described later (see Comments).
MdlAddress
Points to an MDL, possibly the initial MDL in a chain, mapping a client-supplied buffer in which the transport returns the received data.

Comments

When a client calls TdiBuildReceive to set up this IRP, it can specify the type of receive it wants. The transport finds this information at IrpSp->Parameters in the ReceiveFlags member. On input, ReceiveFlags can be zero or set with any combination (ORed) of the following flags:

TDI_RECEIVE_NORMAL
The transport should return a normal TSDU to this client.
TDI_RECEIVE_EXPEDITED
The transport should return an expedited TSDU to this client.
TDI_RECEIVE_PEEK
The transport should return any available received data that it has buffered internally at once, even if it is only a partial TSDU, to this client.

If ReceiveFlags is zero or set with both (ORed) TDI_RECEIVE_NORMAL and TDI_RECEIVE_EXPEDITED on input, either normal or expedited data is acceptable to the client. If only one of these flags is set, the transport should return only the type of TSDU requested. If TDI_RECEIVE_PEEK is set, the transport should return any received data it has buffered internally and retain the data in its internal buffer awaiting a subsequent receive request for the remainder of the TSDU from this client.

Typically, a transport receives normal data until it fills the receive buffer or it receives an end-of-record indication from the remote node. However, if the transport is receiving normal data and expedited data arrives, the transport must pre-empt its normal receive operation, completing the IRP for the normal receive immediately with any normal data it has already transferred into the client's buffer. Then, the transport must satisfy the client's receive request(s) for the expedited data. When the transport has indicated all the expedited data to the client, the transport resumes indicating normal receives when the next receive request is submitted by that client.

If the client has registered any ClientEventReceive, ClientEventReceiveExpedited, ClientEventChainedReceive, and/or ClientEventChainedReceiveExpedited handler(s) for receives on the given connection endpoint, the transport stops indicating receives to the ClientEvent(Chained)Receive(Expedited) routine(s) until the transport has satisfied the receive IRP.

When it calls ClientEventReceive or ClientEventReceiveExpedited, the transport supplies information about the type of receive event it is indicating, which the client can use to set up the ReceiveFlags for a subsequent TDI_RECEIVE request if it is not given the full TSDU. The transport supplies this information to its client by setting the ReceiveFlags parameter to ClientEventReceive(Expedited) with one or more (ORed) of the following flags:

TDI_RECEIVE_NORMAL
The transport is receiving a normal TSDU from the remote node.
TDI_RECEIVE_EXPEDITED
The transport is receiving an expedited TSDU from the remote node.
TDI_RECEIVE_ENTIRE_MESSAGE
If this flag is set, the transport already has a full TSDU available for receipt by the client. When this flag is set, ClientEventReceive is expected to copy the full net packet into an internal buffer and return control without postprocessing the received data, deferring any necessary postprocessing until later.

If this flag remains clear, it is equivalent to setting the (obsolete, but possibly still set by legacy transports) TDI_RECEIVE_PARTIAL flag to indicate that only the initial part of a TSDU is currently available from the transport.

TDI_RECEIVE_COPY_LOOKAHEAD
The transport has received only the lookahead buffer for an indication from the underlying NIC driver, so it sets BytesAvailable to something less than BytesIndicated when the transport calls ClientEventReceive(Expedited) with these parameters. The client should copy the BytesAvailable into an internal buffer (see TdiCopyLookaheadData) and, then, submit one or more receive requests to obtain the remaining data in the TSDU.
TDI_RECEIVE_PEEK
The transport has buffered some receive data internally, but not yet the full TSDU. The client handles such an indication in a similar manner to one in which TDI_RECEIVE_COPY_LOOKAHEAD is set.
TDI_RECEIVE_TRUNCATED
The transport received a TSDU that was truncated.
TDI_RECEIVE_FRAGMENT
The transport received a TSDU that is fragmented.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The transport received the TSDU at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing the receive indication. For example, the client cannot call TdiBuildInternalDeviceControlIrp, which can be called only at IRQL PASSIVE_LEVEL, in the context of this receive indication.

When the transport ORs TDI_RECEIVE_ENTIRE_MESSAGE with TDI_RECEIVE_NORMAL and calls the ClientEventChainedReceive handler, the client is given direct read-only access to a full TSDU in the underlying NIC's receive buffer(s) until that client calls TdiReturnChainedReceives with the input TsduDescriptor. For such an indication, the client has no need to issue a TDI_RECEIVE request. In effect, such a transport forwards receive indications from the underlying NDIS driver directly to its client with only the minimal overhead of setting up the parameters to ClientEventChainedReceive, as follows:

  • The transport uses the NDIS_PACKET-type descriptor input to its ProtocolReceivePacket function as the TsduDescriptor passed to ClientEventChainedReceive. For each client to which the transport forwards the receive indication, the transport augments the value to be returned by ProtocolReceivePacket by one.

  • The transport retrieves the buffer containing the indicated data from the packet descriptor supplied by the underlying NDIS driver, possibly by calling NdisGetFirstBufferFromPacket. The transport uses the pointer to this NDIS_BUFFER-type (equivalent to the MDL type) chain as the Tsdu for ClientEventChainedReceive.

  • From the full range of receive data indicated by the underlying NDIS driver, the transport selects the subrange of interest to its client. The driver uses this subrange as the StartingOffset and ReceiveLength for ClientEventChainedReceive.

In a similar manner, such a transport gives ClientEventChainedReceiveExpedited direct read-only access to a full TSDU when TDI_RECEIVE_ENTIRE_MESSAGE is ORed with TDI_RECEIVE_EXPEDITED in the input RequestFlags.

For a TSDU indicated to a ClientEventChainedReceive(Expedited) handler, the client retains control of all resources for the receive indication until it calls TdiReturnChainedReceives with the TsduDescriptor. As each client that received the indication calls TdiReturnChainedReceives, the NDIS library decrements the counter it maintains for the packet descriptor, which was set originally to the value returned by the transport's ProtocolReceivePacket function. When all clients that received the indication have called TdiReturnChainedReceives, NDIS returns control of all resources specified by that packet descriptor to the NDIS driver that allocated these resources.

When such a transport is called at its ProtocolReceive function with a full-packet indication made by the underlying NDIS driver, the transport must not call a registered ClientEventChainedReceive or ClientEventChainedReceiveExpedited handler with the indication. Instead, it should copy the indicated data for its client into a transport-allocated buffer and call the ClientEventReceive(Expedited) handler. An NDIS call to the transport's ProtocolReceive function implies that the underlying driver expects all higher level drivers to copy the receive data it is indicating and to return its packet descriptor (and all the lower driver's resources that it describes) as quickly as possible because the lowest level NDIS driver is short on NIC receive buffer space.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, NdisGetFirstBufferFromPacket, NDIS_PACKET, ProtocolReceive, ProtocolReceivePacket, TdiBuildReceive, TdiCopyLookaheadData, TdiDispatchInternalDeviceControl, TdiReturnChainedReceives


TDI_RECEIVE_DATAGRAM

When a kernel-mode client makes a TDI_RECEIVE_DATAGRAM request, it asks the underlying TDI transport driver to indicate a TSDU, as a datagram, on a specified address.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the receive-datagram request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_PENDING
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_ADDRESS
STATUS_BUFFER_OVERFLOW
STATUS_BUFFER_TOO_SMALL

IoStatus.Information
Specifies the number of bytes of received data the transport is returning in the client-supplied buffer.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_RECEIVE_DATAGRAM.
IrpSp->FileObject
Points to an open file object representing the local-node address. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this address.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_RECEIVEDG structure, defined as follows:
struct _TDI_REQUEST_KERNEL_RECEIVEDG {
    ULONG ReceiveLength;
    PTDI_CONNECTION_INFORMATION ReceiveDatagramInformation;
    PTDI_CONNECTION_INFORMATION ReturnInformation;
    ULONG ReceiveFlags;
} TDI_REQUEST_KERNEL_RECEIVEDG, *PTDI_REQUEST_KERNEL_RECEIVEDG;

The transport uses the members of this structure as follows:

ReceiveLength
Specifies the maximum size in bytes of the client-supplied receive buffer. If this member is zero, the transport can use the full buffer mapped at Irp->MdlAddress.
ReceiveDatagramInformation
Points to a TDI_CONNECTION_INFORMATION structure, specifying either the remote address from which the client expects to receive a datagram or zero in the RemoteAddressLength member if a datagram from any remote node is acceptable to this client.
ReturnInformation
Points to a caller-supplied buffer, formatted as a TDI_CONNECTION_INFORMATION structure, in which the transport returns the remote-node address from which the returned datagram was received.
ReceiveFlags
Specifies the type of datagram the client expects as none (zero), one, or a combination (ORed) of the following flags:

TDI_RECEIVE_NORMAL - return normal datagram

TDI_RECEIVE_PEEK - return any available portion of datagram

MdlAddress
Points to an MDL, possibly the initial MDL in a chain, mapping a client-supplied buffer in which the transport returns the received datagram.

Comments

Because a datagram is never associated with an endpoint-to-endpoint connection, the transport must return the address of the remote-node client to the receiving client along with each datagram.

TDI transports do not fragment datagrams. Consequently, a client makes a single receive-datagram request for a full datagram, unless it sets the TDI_RECEIVE_PEEK flag. When a TDI transport driver receives a datagram from a remote node, it completes a client's receive-datagram request if both of the following are true:

  • The just received datagram has a destination address that matches the local-node address represented by the file object at IrpSp->FileObject.

  • The just received datagram has a source address that either matches the value specified in RemoteAddress at IrpSp->Parameters or RemoteAddressLength is zero and RemoteAddress is NULL.

If the received datagram is too large for the client-supplied buffer mapped at Irp->MdlAddress, the transport fills this buffer with ReceiveLength bytes and completes the client's receive-datagram IRP. In effect, a client receives a truncated datagram if that client does not provide a large enough buffer with its receive-datagram request.

A local-node client can also receive datagrams by issuing TDI_SET_EVENT_HANDLER request(s) to register its ClientEventReceiveDatagram and/or ClientEventChainedReceiveDatagram handler(s) for a particular address. The transport supplies this information to its client by setting the ReceiveDatagramFlags parameter to ClientEvent(Chained)ReceiveDatagram with any of the flags listed in the reference for TDI_RECEIVE, except TDI_RECEIVE_EXPEDITED. In addition, for datagram indications, the ReceiveDatagramFlags can be set with either of the following:

TDI_RECEIVE_BROADCAST
The received TSDU was broadcast.
TDI_RECEIVE_MULTICAST
The received TSDU was multicast.

A NetBIOS TDI client can receive a broadcast datagram by opening a file object that represents the underlying transport's broadcast address. Any broadcast address has a driver-specific format, which the client can obtain by issuing a TDI_QUERY_INFORMATION request to the underlying transport. A transport does not allow its clients to send datagrams from such a broadcast address, and the transport fails any client-issued TDI_ASSOCIATE_ADDRESS requests that specify its broadcast address.

Windows NT currently specifies the reception of broadcast datagrams only for NetBIOS TDI clients. Other TDI transports can indicate received broadcast datagrams to their clients in the same manner as any other type of datagram.

TdiBuildReceiveDatagram is the macro a client uses to fill in the IRP.

See Also

ClientEventChainedReceiveDatagram, ClientEventReceiveDatagram, TdiBuildReceiveDatagram, TDI_CONNECTION_INFORMATION, TdiDispatchInternalDeviceControl, TDI_QUERY_INFORMATION, TDI_SET_EVENT_HANDLER


TDI_SEND

When a kernel-mode client makes a TDI_SEND request, it asks the underlying TDI transport driver to transmit a normal or expedited TSDU on a specified connection endpoint to its remote-node peer.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the send request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_PENDING
STATUS_INVALID_CONNECTION
STATUS_INVALID_PARAMETER
STATUS_DEVICE_NOT_READY

IoStatus.Information
Specifies the number of bytes of client-supplied data the driver transferred from the client-supplied buffer mapped at Irp->MdlAddress.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_SEND.
IrpSp->FileObject
Points to an open file object representing the connection endpoint. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.

The client already established an endpoint-to-endpoint connection with a remote-node peer on this connection endpoint.

IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_SEND structure, defined as follows:
struct _TDI_REQUEST_KERNEL_SEND {
    ULONG SendLength;
    ULONG SendFlags;
} TDI_REQUEST_KERNEL_SEND, *PTDI_REQUEST_KERNEL_SEND;

The transport uses the members of this structure as follows:

SendLength
Specifies the number of bytes of data to be sent in the buffer mapped at Irp->MdlAddress.
SendFlags
Specifies the type of TSDU the client expects to be sent as none (zero), one, or a combination (ORed) of TDI_SEND_XXX flags (see Comments).
MdlAddress
Points to an MDL, possibly the initial MDL in a chain, mapping a client-supplied buffer containing the data to be sent.

Comments

When a client calls TdiBuildSend to set up this IRP, it can specify the type of send it wants. The transport finds this information at IrpSp->Parameters in the SendFlags member. On input, SendFlags can be zero or set with any combination (ORed) of the following flags:

TDI_SEND_EXPEDITED
The given data should be sent ahead of any normal send requests the transport is currently holding queued for transmission on this endpoint-to-endpoint connection. If the transport does not support expedited transfers, it can ignore this flag.
TDI_SEND_PARTIAL
The given data is not a complete message-mode TSDU. The client will send the remaining data for the TSDU later. This flag is irrelevant to stream-mode transports.

If this flag is clear in a send submitted to a message-mode transport, the driver inserts an end-of-record mark in the line flow after sending all data supplied in the client's buffer.

TDI_SEND_NO_RESPONSE_EXPECTED
The caller is giving a hint to the underlying transport that it does not expect a response to this send from its remote-node peer. This flag should disable piggybacking of the TSDU acknowledgment by the remote-node transport.
TDI_SEND_NON_BLOCKING
If the underlying transport currently has no internal buffer space available for the given data, it should just complete the IRP with STATUS_DEVICE_NOT_READY. If the transport has some buffer space available, it should copy as much data as it can from the client-supplied buffer, set the IoStatus.Information member to the number of bytes it copied, and complete the IRP with STATUS_SUCCESS.

This flag is irrelevant to transports that do not buffer sends internally.

Each local-node client send request is associated with exactly one of the following:

  • A receive request issued by the remote-node client to its underlying transport

  • A remote-node transport's call to its client's registered ClientEventReceive, ClientEventReceiveExpedited, ClientEventChainedReceive, or ClientEventChainedReceiveExpedited handler

The local-node TDI transport driver can queue any number of send requests on a particular connection, but it must process them in FIFO order, unless the client issues an expedited send. Each expedited send request must be queued and transmitted ahead of any normal send requests the transport is holding queued, and each expedited send also must be transmitted in FIFO order for the client.

The transport must complete each send IRP in a timely manner, whether with a success or error status. The driver should determine a reasonable time-out based on its knowledge of underlying network conditions.

Depending on the underlying transport, clients can send stream-mode or message-mode TSDUs, the latter with one or more send requests. For stream-mode transports, the client does not delimit messages and the TDI_SEND_PARTIAL flag is irrelevant. For message-mode transports, the client can send a TSDU as a sequence of send requests, each specifying the TDI_SEND_PARTIAL flag. If this flag is clear in a submitted send request, a message-mode transport considers the data to be the end of such a sequence or a complete TSDU. The remote-node TDI transport notifies the receiving client of partial TSDUs through receive indicators passed in the corresponding receive indication(s).

In its send request, the sending client provides a buffer containing the TSDU. The client can provide a buffer of any size up to the transport-determined limit. The transport is given ownership of this client-supplied buffer until it completes the send. The transport fails any send request for which a client specifies a SendLength larger than the transport supports.

Clients can determine their underlying transports' send-size limits by submitting TDI_QUERY_PROVIDER_INFO-type queries.

If the transport buffers client-supplied send data internally, the client can request a nonblocking send by setting the TDI_SEND_NON_BLOCKING flag. When such a transport is given a send request with this flag set, it completes the IRP with STATUS_DEVICE_NOT_READY if it cannot copy the given data into its internal buffer space. Such a transport returns STATUS_SUCCESS if it buffers only a part of the given TSDU internally, but it must set IoStatus.Information in such an IRP to the number of bytes it buffered internally so its client can submit a subsequent send for the remaining data in the original send request.

Any client that submits nonblocking send requests usually registers a ClientEventSendPossible handler. The underlying transport calls this handler after it has rejected a send request with STATUS_DEVICE_NOT_READY as soon as that transport has internal buffer space available for holding send data.

A transport also can support zero-length send requests. A zero-length send in which the TDI_SEND_PARTIAL flag is clear actually forces protocol flow. The client of such a transport can issue zero-length send to satisfy a receive request. If the driver does not support zero-length sends, it should return an error when its clients submit zero-length send requests.

TdiBuildSend is the macro a client uses to fill in the IRP.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, ClientEventSendPossible, TdiBuildSend, TdiDispatchInternalDeviceControl, TDI_QUERY_INFORMATION, TDI_RECEIVE


TDI_SEND_DATAGRAM

When a kernel-mode client makes a TDI_SEND_DATAGRAM request, it asks the underlying TDI transport driver to transmit a TSDU, as a datagram, to a specified remote-node address.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the send-datagram request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_PENDING
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_ADDRESS
STATUS_INVALID_PARAMETER

IoStatus.Information
Specifies the number of bytes of client-supplied data the driver transferred from the client-supplied buffer mapped at Irp->MdlAddress.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_SEND_DATAGRAM.
IrpSp->FileObject
Points to an open file object representing the local-node address. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this address.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_SENDDG structure, defined as follows:
struct _TDI_REQUEST_KERNEL_SENDDG {
    ULONG SendLength;
    PTDI_CONNECTION_INFORMATION SendDatagramInformation;
} TDI_REQUEST_KERNEL_SENDDG, *PTDI_REQUEST_KERNEL_SENDDG;

The transport uses the members of this structure as follows:

SendLength
Specifies the number of bytes in the datagram to send.
SendDatagramInformation
Points to a TDI_CONNECTION_INFORMATION structure specifying the remote-node address to which the local-node client wants the datagram to be sent.
MdlAddress
Points to an MDL, possibly the initial MDL in a chain, mapping a client-supplied buffer containing the datagram to be sent.

Comments

A TDI transport does not send fragmented datagrams. Consequently, its client makes one send-datagram request to send each datagram, which is associated only with the particular request for a connectionless data transfer operation.

In its send-datagram request, the sending client provides a buffer containing the TSDU. The client can provide a buffer of any size up to the transport-determined limit. The transport is given ownership of this client-supplied buffer until it completes the send-datagram IRP. The transport fails any send-datagram request for which a client specifies a SendLength larger than the transport supports.

Clients can determine their underlying transports' send-size limits by submitting query-information requests with QueryType set to any of TDI_QUERY_PROVIDER_INFO, TDI_QUERY_DATAGRAM_INFO, or TDI_QUERY_MAX_DATAGRAM_INFO.

A transport can allow its clients to send zero-length datagrams. For example, a zero-length send-datagram request might force protocol flow.

Some transports allow clients to direct datagrams to remote-node multicast and/or broadcast addresses. The syntax of such a multicast or broadcast address is transport-specific.

As a connectionless transfer, a datagram-send is inherently unreliable. The transport can drop or duplicate a datagram at the discretion of the driver writer. The transport must complete each send-datagram IRP in a timely manner, whether with a success or error status. The driver should determine a reasonable time-out based on its knowledge of underlying network conditions.

If it queues datagrams internally, the transport must process send-datagram requests in FIFO order. Send-datagram requests should be queued separately from endpoint-to-endpoint send requests.

TdiBuildSendDatagram is the macro a client uses to fill in this IRP.

See Also

TdiBuildSendDatagram, TDI_CONNECTION_INFORMATION, TdiDispatchInternalDeviceControl, TDI_QUERY_INFORMATION, TDI_RECEIVE_DATAGRAM


TDI_SET_EVENT_HANDLER

When a kernel-mode client makes a TDI_SET_EVENT_HANDLER request, it asks the underlying TDI transport driver to call the specified ClientEventXxx routine whenever the corresponding network event occurs.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the set-event-handler request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_INVALID_ADDRESS_COMPONENT
STATUS_INVALID_PARAMETER

The IoStatus.Information member is zero since no data is transferred for this request.

IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_SET_EVENT_HANDLER.
IrpSp->FileObject
Points to an open file object representing a local-node address. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this address.

Depending on the type of ClientEventXxx handler to be registered, this address might be or become associated with an established endpoint-to-endpoint connection made by this client.

IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_SET_EVENT structure, defined as follows:
struct _TDI_REQUEST_KERNEL_SET_EVENT {
    LONG EventType;
    PVOID EventHandler;
    PVOID EventContext;
} TDI_REQUEST_KERNEL_SET_EVENT, *PTDI_REQUEST_KERNEL_SET_EVENT;

The transport uses the members of this structure as follows:

EventType
Specifies the type of ClientEventXxx handler to be registered (see Comments).
EventHandler
Specifies the entry point of the ClientEventXxx routine to be called when this type of event occurs. This member can be NULL if the client is deregistering a previously registered event handler.
EventContext
Specifies a pointer to be passed, uninterpreted by the transport, to the given ClientEventXxx routine whenever it is called. This member can be NULL if EventHandler is NULL.

Comments

When its client first opens a file object that represents an address, a TDI transport initializes all event handlers associated with that address to NULL for the client.

Subsequently when the client calls TdiBuildSetEventHandler to set up this IRP, it specifies the type of handler to be registered. The transport finds this information at IrpSp->Parameters in the EventType member. On input, EventType can be one of the following system-defined values:

TDI_EVENT_CONNECT
Register the given ClientEventConnect handler to be called when an endpoint-to-endpoint connection is offered by a remote-node peer.
TDI_EVENT_DISCONNECT
Register the given ClientEventDisconnect handler to be called when an established endpoint-to-endpoint connection is being closed by the remote-node peer.
TDI_EVENT_RECEIVE
Register the given ClientEventReceive handler to be called when a normal receive comes in on an endpoint-to-endpoint connection from the remote node and the transport will give the client the opportunity to copy all or part of the buffered TSDU.
TDI_EVENT_CHAINED_RECEIVE
Register the given ClientEventChainedReceive handler to be called when a normal receive comes in on an endpoint-to-endpoint connection from the remote node and the transport can give this client read-only access to the buffered full TSDU until the client calls TdiReturnChainedReceives with the TsduDescriptor.
TDI_EVENT_RECEIVE_EXPEDITED
Register the given ClientEventReceiveExpedited handler to be called when an expedited receive comes in on an endpoint-to-endpoint connection from the remote node and the transport will give this client the opportunity to copy all or part of the buffered TSDU.
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED
Register the given ClientEventChainedReceiveExpedited handler to be called when an expedited receive comes in on an endpoint-to-endpoint connection from the remote node and the transport can give this client read-only access to the buffered full TSDU until the client calls TdiReturnChainedReceives with the TsduDescriptor.
TDI_EVENT_RECEIVE_DATAGRAM
Register the given ClientEventReceive handler to be called when a datagram comes in from a remote node and the transport will give this client the opportunity to copy all or part of the buffered TSDU.
TDI_EVENT_CHAINED_RECEIVE_DATAGRAM
Register the given ClientEventChainedReceiveDatagram handler to be called when a datagram comes in from a remote node and the transport can give this client read-only access to the buffered full TSDU until the client calls TdiReturnChainedReceives with the TsduDescriptor.
TDI_EVENT_ERROR
Register the given ClientEventError handler to be called if an error occurs in the transport or in an underlying driver such that network I/O on this local-node address has become unreliable or impossible.
TDI_EVENT_SEND_POSSIBLE
Register the given ClientEventSendPossible handler to be called when the underlying transport, which previously returned a submitted send request with STATUS_DEVICE_NOT_READY, next has internal buffer space available into which it can copy the resubmitted client-supplied data to be sent.

In addition to the preceding system-defined events, a transport can extend this interface at the discretion of the driver writer to support notifications of transport-specific events to kernel-mode clients. Such a transport must define TDI_EVENT_XXX codes in which the most significant bit is set.

A TDI transport can indicate an event to a client while an event of the same kind is in progress. In other words, ClientEventXxx handlers are re-entrant.

A TDI transport can call a client-supplied ClientEvent(Chained)ReceiveXxx handler immediately if it is receiving data from the remote node, even before the transport completes the set-event-handler IRP that provided this client entry point.

In general, a transport calls ClientEventChainedReceiveXxx handlers when the transport's ProtocolReceivePacket function is called with a receive indication. A call to ProtocolReceivePacket implies that the underlying NDIS driver is temporarily relinquishing ownership of all its resources for the indication specified by the input NDIS_PACKET-type descriptor until that packet descriptor is returned to the underlying NDIS driver.

The transport must set up such an indication as previously described (see TDI_RECEIVE), passing the NDIS_PACKET-type descriptor as the TsduDescriptor input to its clients' ClientEventChainedReceiveXxx handlers. The clients' subsequent calls to TdiReturnChainedReceives returns the TsduDescriptor and all resources it describes to the indicating driver underlying the transport.

On the other hand, a transport calls ClientEventReceiveXxx handlers when the transport's ProtocolReceive function is called with a receive indication. A call to ProtocolReceive implies that the underlying NDIS driver expects all higher level network components to copy the indicated data and return as quickly as possible so the lower driver can regain control of the resources it used to make the receive indication.

A client can disable an already registered event handler by making a subsequent TDI_SET_EVENT_HANDLER request in which the EventType member specifies the type of handler but the EventHandler and EventContext members are NULL.

TdiBuildSetEventHandler is the macro a client uses to fill in the IRP.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveDatagram, ClientEventChainedReceiveExpedited, ClientEventConnect, ClientEventDisconnect, ClientEventError, ClientEventReceive, ClientEventReceiveDatagram, ClientEventReceiveExpedited, ClientEventSendPossible, ProtocolReceive, ProtocolReceivePacket, TdiBuildSetEventHandler, TdiDispatchInternalDeviceControl, TDI_RECEIVE, TdiReturnChainedReceives


TDI_SET_INFORMATION

When a kernel-mode client makes a TDI_SET_INFORMATION request, it asks the underlying TDI transport driver to set information of a client-specified type on a particular address, connection, or control channel.

IRP

The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:

IoStatus.Status
Specifies the final status of the set-information request. The transport sets this member before it completes the IRP, possibly to one of the following:

STATUS_SUCCESS
STATUS_INVALID_ADDRESS_COMPONENT
STATUS_INVALID_CONNECTION
STATUS_INVALID_DEVICE_REQUEST
STATUS_INVALID_PARAMETER
STATUS_INSUFFICIENT_RESOURCES
STATUS_NOT_IMPLEMENTED

IoStatus.Information
Specifies the number of bytes of client-supplied data the driver used from the buffer mapped at Irp->MdlAddress.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_SET_INFORMATION.
IrpSp->FileObject
Points to an open file object representing a local-node address, connection endpoint, or control channel. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this address, connection, or control channel.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_SET_INFORMATION structure, defined as follows:
struct _TDI_REQUEST_KERNEL_SET_INFO {
    LONG SetType;
    PTDI_CONNECTION_INFORMATION RequestConnectionInformation;
} TDI_REQUEST_KERNEL_SET_INFORMATION, *PTDI_REQUEST_KERNEL_SET_INFORMATION;

The transport uses the members of this structure as follows:

SetType
Specifies the type of set operation to carry out (see Comments).
RequestConnectionInformation
Points to a TDI_CONNECTION_INFORMATION structure containing input connection information if the given file object represents a connection. Otherwise, this member is NULL.
MdlAddress
Points to an MDL mapping a client-supplied buffer containing the data to be set. The format and contents of this buffer depend on the value of SetType.

Comments

When a client calls TdiBuildSetInformation to set up this IRP, it must specify the type of set operation it wants done and must provide a buffer containing appropriate data. The transport finds this information at IrpSp->Parameters in the SetType member. On input, SetType can be any of the following subset of the system-defined TDI_QUERY_XXX:

TDI_QUERY_ADDRESS_INFO
Set information for an address. The client-supplied buffer mapped at MdlAddress contains data formatted as a TDI_ADDRESS_INFO structure.
TDI_QUERY_CONNECTION_INFO
Set information for a connection endpoint. The client-supplied buffer mapped at MdlAddress contains data formatted as a TDI_CONNECTION_INFO structure.
TDI_QUERY_PROVIDER_INFO
Set information for a control channel. The client-supplied buffer mapped at MdlAddress contains data formatted as a TDI_PROVIDER_INFO structure.
TDI_QUERY_PROVIDER_STATISTICS
Set statistics information for a control channel. The client-supplied buffer mapped at MdlAddress contains data formatted as a TDI_PROVIDER_STATISTICS structure.

A transport can extend this interface at the discretion of the driver writer to support additional set operations for its clients. Such a transport must define TDI_QUERY_XXX codes in which the most significant bit is set.

The transport must fail any set-information request in which the given file object is inappropriate to the specified SetType.

The transport also can fail any set-information request for which the client supplies values that exceed a driver-determined limit. For example, most transports do not allow their clients to reset the MaxDatagramSize or MaxSendSize with TDI_QUERY_PROVIDER_INFO-type set-information requests.

TdiBuildSetInformation is the macro a client uses to fill in this IRP.

See Also

TDI_ADDRESS_INFO, TDI_CONNECTION_INFO, TDI_CONNECTION_INFORMATION, TdiBuildSetInformation, TdiDispatchInternalDeviceControl, TDI_PROVIDER_INFO, TDI_PROVIDER_STATISTICS


Chapter 3 TDI Functions

This describes system-supplied Tdi Xxx functions called by transport drivers and/or by their kernel-mode clients.

The functions described here are in alphabetic order.


TdiCompleteRequest

VOID
TdiCompleteRequest(
IN PIRP Irp,
IN NTSTATUS Status
);

TdiCompleteRequest completes a given IRP with a transport-supplied status.

Parameters

Irp
Points to the IRP to be completed.
Status
Specifies the final status of the request.

Comments

TdiCompleteRequest sets the given Status value in the IoStatus.Status member of the given IRP and completes the IRP with the system-defined IO_NETWORK_INCREMENT priority boost to the thread that originally made the I/O request. Consequently, transports usually call TdiCompleteRequest with STATUS_SUCCESS, because IRPs to be failed with an error status should be completed with IO_NO_INCREMENT as the PriorityBoost passed to IoCompleteRequest.

Only transports call TdiCompleteRequest.

Callers of TdiCompleteRequest run at IRQL <= DISPATCH_LEVEL.

See Also

TdiDispatchCleanup, TdiDispatchClose, TdiDispatchCreate, TdiDispatchDeviceControl, TdiDispatchInternalDeviceControl


TdiCopyBufferToMdl

NTSTATUS
TdiCopyBufferToMdl (
 IN PVOID SourceBuffer,
 IN ULONG SourceOffset,
 IN ULONG SourceBytesToCopy,
 IN PMDL DestinationMdlChain,
 IN ULONG DestinationOffset,
 IN PULONG BytesCopied
 );

TdiCopyBufferToMdl copies data from a buffer range into a set of one or more destination buffers mapped by a given MDL chain.

Parameters

SourceBuffer
Specifies the base virtual address of the source buffer containing the data to be copied.
SourceOffset
Specifies the byte offset within the source buffer at which to begin the copy.
SourceBytesToCopy
Specifies the number of bytes to copy from SourceBuffer to the buffer mapped by the given MDL.
DestinationMdlChain
Points to the initial MDL in a chain of linked MDLs mapping the destination buffers into which to copy the data.
DestinationOffset
Specifies the byte offset within the destination buffer(s) at which to begin the copy. Effectively, this is the number of bytes mapped by the given MDLs to skip before beginning the copy operation.
BytesCopied
Points to a caller-supplied variable in which TdiCopyBufferToMdl returns the number of bytes copied from the source to the destination buffer.

Return Value

TdiCopyBufferToMdl returns STATUS_SUCCESS if it copied SourceBytesToCopy into the mapped destination buffer(s). Otherwise, it returns STATUS_BUFFER_OVERFLOW if SourceBytesToCopy was too large to fit into the destination buffer space available from the given DestinationOffset.

Comments

TdiCopyBufferToMdl always copies as much data as possible from the source buffer to the destination buffer(s), even if it returns STATUS_BUFFER_OVERFLOW. At most, the return value of BytesCopied is equal to the given SourceBytesToCopy.

Both transports and their kernel-mode clients can call TdiCopyBufferToMdl.

Callers of TdiCopyBufferToMdl can be running at IRQL <= DISPATCH_LEVEL if the source and destination buffers are resident. If either is pageable, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

TdiCopyLookaheadData, TdiCopyMdlToBuffer


TdiCopyLookaheadData

VOID
TdiCopyLookaheadData(
IN PVOID Destination,
IN PVOID Source,
IN ULONG Length,
IN ULONG ReceiveFlags
);

TdiCopyLookaheadData safely copies receive data indicated in a lookahead buffer to the transport protocol by the underlying NDIS driver.

Parameters

Destination
Points to a caller-supplied buffer into which to copy the data.
Source
Points to the source buffer from which to copy the data.
Length
Specifies how many bytes of data to copy.
ReceiveFlags
Specifies a combination (ORed) of TDI_RECEIVE_XXX flags of which only the following is pertinent to this call:
TDI_RECEIVE_COPY_LOOKAHEAD
Must be set if the underlying NIC driver set the NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA flag in response to the protocol's binding-time call to NdisRequest with the OID_GEN_MAC_OPTIONS query. Otherwise, this flag must be clear in the given ReceiveFlags.

Comments

TdiCopyLookaheadData can be called by a transport's ProtocolReceive function to copy receive data from a lookahead buffer that was indicated up by the underlying NIC driver. In particular, TdiCopyLookaheadData must be called if the underlying NIC driver did not set the NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA flag in response to the transport's OID_GEN_MAC_OPTIONS query. For more information about this OID request, see Part 1, Chapter 5.

However, TdiCopyLookaheadData can be called by any NDIS driver's ProtocolReceive function, whatever the nature of the receive buffers on the underlying NIC.

Any driver that exports a ProtocolReceive function can call TdiCopyLookaheadData.

Callers of TdiCopyLookaheadData can be running at any IRQL if the buffer at Destination was allocated from nonpaged pool. Otherwise, a caller must be running at IRQL < DISPATCH_LEVEL.

See Also

NdisRequest, ProtocolReceive


TdiCopyMdlToBuffer

NTSTATUS
TdiCopyMdlToBuffer (
 IN PMDL SourceMdlChain,
 IN ULONG SourceOffset,
 IN PVOID DestinationBuffer,
 IN ULONG DestinationOffset,
 IN ULONG DestinationBufferSize,
 OUT PULONG BytesCopied
 );

TdiCopyMdlToBuffer copies data from a set of buffer(s) mapped by a given MDL chain into a caller-supplied destination buffer range.

Parameters

SourceMdlChain
Points to the initial MDL in a chain of MDLs mapping buffers containing the data to be copied.
SourceOffset
Specifies the byte offset within the mapped source buffer(s) at which to begin the copy. Effectively, this is the number of mapped source bytes to skip before beginning the copy.
DestinationBuffer
Points to the caller-supplied destination buffer.
DestinationOffset
Specifies the byte offset within the destination buffer at which to begin copying the data from the source buffer(s).
DestinationBufferSize
Specifies the size in bytes of the buffer at DestinationBuffer.
BytesCopied
Points to a caller-supplied variable in which TdiCopyMdlToBuffer returns the number of bytes it copied.

Return Value

TdiCopyMdlToBuffer returns STATUS_SUCCESS if it copied (DestinationBufferSize - DestinationOffset) bytes into the destination buffer. Otherwise, it returns STATUS_BUFFER_OVERFLOW if the given SourceMdlChain, starting at SourceOffset, maps more data than the range (DestinationBufferSize - DestinationOffset) can contain.

Comments

TdiCopyMdlToBuffer always copies as much data as possible from the given mapped source buffer(s) into the given destination buffer range, even if it returns STATUS_BUFFER_OVERFLOW.

At most, the return value of BytesCopied can be the given DestinationBufferSize if DestinationOffset is zero.

Both transports and their kernel-mode clients can call TdiCopyMdlToBuffer.

Callers of TdiCopyMdlToBuffer can be running at IRQL <= DISPATCH_LEVEL if the source and destination buffers are resident. If either is pageable, the caller must be running at IRQL < DISPATCH_LEVEL.

See Also

TdiCopyLookaheadData, TdiCopyBufferToMdl


TdiMapBuffer

This function is obsolete. It simply returns control.


TdiMapUserRequest

NTSTATUS
TdiMapUserRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IrpSp
);

TdiMapUserRequest converts a given IRP in which MajorFunction is set to IRP_MJ_DEVICE_CONTROL into an IRP_MJ_INTERNAL_DEVICE_CONTROL request if it recognizes the IOCTL_TDI_XXX code specified in the input IRP.

Parameters

DeviceObject
Points to the device object created by the transport driver.
Irp
Points to the user IOCTL_XXX request to be converted.
IrpSp
Points to the I/O stack location to be converted.

The transport's TdiDispatchDeviceControl routine already called IoGetCurrentIrpStackLocation to obtain this pointer.

Return Value

TdiMapUserRequest returns STATUS_SUCCESS if it converted the given IOCTL_TDI_XXX device-control request into a TDI_XXX internal device-control request. Otherwise, it can return either of the following:

STATUS_INVALID_PARAMETER
If IOCTL_TDI_SET_EVENT_HANDLER is specified in the given I/O stack location of the input IRP
STATUS_NOT_IMPLEMENTED
If TdiMapUserRequest does not recognize the IOCTL_XXX code specified in the given I/O stack location of the input IRP

Comments

For the majority of system-defined IOCTL_TDI_XXX codes that can be set in IRPs passed to a transport's TdiDispatchDeviceControl routine, TdiMapUserRequest converts the current I/O stack location into the format of the corresponding kernel-mode TDI_XXX request.

For example, if IrpSp->Parameters.DeviceIoControl.IoControlCode is set to IOCTL_TDI_LISTEN in the input IRP, TdiMapUserRequest reformats the I/O stack location as follows:

  • Resets MajorFunction to IRP_MJ_INTERNAL_DEVICE_CONTROL

  • Sets MinorFunction to TDI_LISTEN

  • If the input Irp->AssociatedIrp.SystemBuffer is not NULL, sets IrpSp->Parameters to the address of a TDI_REQUEST_KERNEL_LISTEN structure, which it sets up with the caller-supplied information

TdiDispatchDeviceControl can call TdiDispatchInternalDeviceControl with such a converted IRP, thereby saving the transport from having duplicate code paths for processing system-defined IOCTL_TDI_XXX and the corresponding TDI_XXX requests.

While the kernel-mode TDI_REGISTER_EVENT_HANDLER has a corresponding user IOCTL_TDI_REGISTER_EVENT_HANDLER, TdiMapUserRequest fails any attempt to map such an input IRP. Only kernel-mode clients can register event handlers with a TDI transport, so a transport's TdiDispatchDeviceControl routine cannot be called with a valid request to set up an event handler.

If a transport defines any private IOCTL_XXX codes to be used by a transport-dedicated Win32 application for communication with the transport, its TdiDispatchDeviceControl routine can process such a request whenever TdiMapUserRequest returns STATUS_NOT_SUPPORTED. For more information about defining driver-specific, private IOCTLs, see the Kernel-Mode Driver Reference.

Callers of TdiMapUserRequest run at IRQL PASSIVE_LEVEL.

See Also

TdiDispatchDeviceControl, TdiDispatchInternalDeviceControl


TdiReturnChainedReceives

VOID
TdiReturnChainedReceives(
IN PVOID *TsduDescriptors,
IN ULONG NumberOfTsdus
);

TdiReturnChainedReceives returns an array of TSDU descriptors for one or more chained receive indications previously forwarded to a transport's client, which returned STATUS_PENDING for each such chained receive indication.

Parameters

TsduDescriptors
Points to an array of descriptors of type NDIS_PACKET.
NumberOfTsdus
Specifies the number of elements in the array at TsduDescriptors.

Comments

For faster network throughput, a TDI transport can pass to its clients direct (but read-only) access to a chain of buffers containing a full received network packet, subject to the following conditions:

  • The transport registered an NDIS ProtocolReceivePacket function with NdisRegisterProtocol, so it can receive indications that permit direct, read-only access to the contents of the underlying NIC driver's receive buffer(s). If the transport is bound directly to the driver of a physical NIC, rather than to an NDIS intermediate driver, its ProtocolReceivePacket function is given access to the NIC's receive buffer(s).

  • The transport is bound to a underlying NIC driver that calls NdisMIndicateReceivePacket to make receive indications, thereby allowing any bound protocol that supplies a ProtocolReceivePacket function to hold on to received data in the NIC driver's receive buffer(s) until the protocol no longer needs the indicated packet. Such a receive indication is always a full network packet, mapped by the buffer descriptors (MDLs) chained to each indicated NDIS_PACKET-type TSDU descriptor.

  • The client has registered its handler for TDI_EVENT_CHAINED_RECEIVE or TDI_EVENT_RECEIVE_DATAGRAM events with the transport.

When such a transport's ProtocolReceivePacket function is called, it can forward the receive indication to the appropriate ClientEventReceive(Datagram) function, giving the client the opportunity to process the indicated TSDU without the overhead of double buffering such a received packet in the transport. If the ClientEventReceive(Datagram) callback returns STATUS_PENDING, the client retains exclusive read-only access to the indicated packet until that client calls TdiReturnChainedReceives with the TSDU descriptor. Its call to TdiReturnChainedReceives returns the indicated descriptor and all resources that it describes to the underlying NDIS driver that originally allocated the packet descriptor.

Any packets indicated by the underlying NDIS driver to the transport's ProtocolReceive function must be forwarded to such a client's ClientEventReceive, ClientEventReceiveExpedited, or ClientEventReceiveDatagram function. Calls to these client event handlers direct the client to make a copy of the indicated data as quickly as possible, and ClientEventReceive(Expedited/Datagram) relinquishes the client's access to the buffer(s) containing the indicated data when the event handler returns control.

Only kernel-mode clients with registered ClientEventChainedReceive, ClientEventChainedReceiveExpedited, and/or ClientEventChainedReceiveDatagram handlers call TdiReturnChainedReceives.

Callers of TdiReturnChainedReceives run at IRQL <= DISPATCH_LEVEL.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveDatagram, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveDatagram, ClientEventReceiveExpedited, NdisMIndicateReceivePacket, NDIS_PACKET, NdisReturnPackets, ProtocolReceive, ProtocolReceivePacket


TdiUnmapBuffer

This function is obsolete. It simply returns control.


Chapter 4 TdiBuildXxx Macros and Functions

This describes the system-supplied TdiBuild Xxx macros, used by kernel-mode clients to set up the TDI_XXX internal device control IRPs they subsequently submit to underlying TDI transport drivers with IoCallDriver.

Parameters to these macros always include pointers or handles to file objects representing addresses, connection endpoints, or control channels already opened with calls to ZwCreateFile. A successful call to ZwCreateFile returns a handle to an open file object, from which the client can obtain a pointer to that file object by calling ObReferenceObjectByHandle.

This also describes some TdiBuild Xxx functions that only kernel-mode clients might call:

  • TdiBuildInternalDeviceControlIrp to allocate an IRP to be passed to a TdiBuild Xxx macro and, then, to IoCallDriver when a kernel-mode client makes a request to its underlying transport on its own behalf

  • TdiBuildNetbiosAddressEa to set up a buffered EA describing an address with which the client of a NetBIOS transport can subsequently call ZwCreateFile to open that NetBIOS address

  • TdiBuildNetbiosAddress to set up a NetBIOS address for the client of a NetBIOS transport

For more information about how transport drivers handle the TDI_XXX requests set up by the build-IRP macros, see Chapter 2. See the Kernel-Mode Driver Design Guide and Reference for more information about the kernel-mode support routines, such as IoCallDriver, ZwCreateFile, and ObReferenceObjectByHandle, mentioned here.

The macros and functions described here are in alphabetic order.


TdiBuildAccept

VOID
TdiBuildAccept (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PTDI_CONNECTION_INFORMATION RequestConnectionInfo,
 OUT PTDI_CONNECTION_INFORMATION ReturnConnectionInfo
 );

TdiBuildAccept sets up an internal device control IRP for a TDI_ACCEPT request to the underlying transport in which a local-node client has already opened a connection endpoint and associated it with a local-node address and to which the client has already issued a TDI_LISTEN request.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing a connection endpoint. The client previously made a successful request, set up with TdiBuildAssociateAddress, to the transport to set up an association between this connection and a local-node address. When the association was established, the client also issued a listen request, set up with TdiBuildListen, to the transport.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
RequestConnectionInfo
Points to a caller-supplied buffer containing a TDI_CONNECTION_INFORMATION structure, set up to specify a valid remote-node client address from which the caller, a local-node client, can accept a connection. The local-node client also can provide buffered accept data, specified at UserData in this structure, that the underlying transport should send to the remote node when it transmits acceptance of the offered connection (see TdiBuildListen for details).
ReturnConnectionInfo
Points to a caller-supplied buffer in which the transport returns the remote-node client's address with which it has established an endpoint-to-endpoint connection for the local-node client. This buffer is also formatted as a TDI_CONNECTION_INFORMATION structure. If the underlying transport does not provide this information, this parameter should be NULL.

Comments

TdiBuildAccept sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_ACCEPT as the MinorFunction codes in the transport's I/O stack location of the given IRP.

When a kernel-mode client issues a TDI_ACCEPT request, it asks the underlying transport to accept an incoming request to connect from a remote-node client and, thereby, to enable network data reception on an established endpoint-to-endpoint connection.

Usually, a client sends an accept request in response to a return from its preceding listen request, set up with TdiBuildListen with the TDI_QUERY_ACCEPT option selected. If a client did not set this value in the Flags for its listen request, its accept request fails because the underlying transport has already established an endpoint-to-endpoint connection when the listen request completes.

Most TDI transports have a time-out feature that prevents attempts to establish endpoint-to-endpoint connections from hanging when a remote node does not respond. Therefore, a client must either accept or reject a connection as soon as possible after completion of its listen request. The interval between client notification of a remote-node peer connection offer and that local client's connection acceptance or rejection is subject to a transport-determined time-out limit, usually something less than one second.

A client indicates its acceptances of a connection to the remote-node peer by submitting a request set up by TdiBuildAccept to the underlying transport. It indicates its rejection of a connection offer by submitting an IRP set up with TdiBuildDisconnect.

The local client's registered ClientEvent(Chained)Receive(Expedited) handler(s) can be called before the I/O Manager formally completes its processing of the accept IRP. That is, the remote-node peer can start sending data to the local client as soon as the remote node receives an acceptance of the offered connection.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventConnect, ClientEventReceive, ClientEventReceiveExpedited, TDI_ACCEPT, TdiBuildDisconnect, TdiBuildInternalDeviceControlIrp, TdiBuildListen, TDI_CONNECTION_INFORMATION


TdiBuildAction

VOID
TdiBuildAction (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PMDL MdlAddr
 );

TdiBuildAction sets up an internal device control IRP for a TDI_ACTION request to the underlying transport in which a local-node client has already opened an address, connection endpoint, or control channel to which the requested action applies.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing an address, connection endpoint, or control channel.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
MdlAddr
Points to an MDL, possibly the head of an MDL chain, mapping a client-supplied buffer containing the action parameter block set up by the client. This buffer contains a system-defined header, followed by whatever transport-determined information is necessary to carry out the action request.

Comments

TdiBuildAction sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_ACTION as the MinorFunction codes in the transport's I/O stack location of the given IRP.

A client can make an action request for transport-provided extensions pertaining to an address, a connection, or a control channel. The client supplies a buffer containing all transport-required information concerning such a request in an action parameter block.

Every action parameter block begins with a system-defined TDI_ACTION_HEADER structure containing a transport identifier and an action code. On receipt of such a request, the transport uses the value of TransportId in this header to validate the request. The ActionCode member specifies the transport-defined operation to carry out and determines the transport-required contents for the remainder of the parameter block. The underlying transport defines the set of possible action codes and the required formats of the remaining parameter block for each such action.

The successful completion of any client's action request makes the transport-provided extension applicable to that client but not to any other TDI clients or drivers.

See Also

TDI_ACTION, TDI_ACTION_HEADER, TdiBuildInternalDeviceControlIrp


TdiBuildAssociateAddress

VOID
TdiBuildAssociateAddress (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN HANDLE AddrHandle
 );

TdiBuildAssociateAddress sets up an internal device control IRP for a TDI_ASSOCIATE_ADDRESS request to the underlying transport in which a local-node client has already opened an address and a connection endpoint.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing the connection endpoint.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
AddrHandle
Specifies a handle to a file object representing a local-node address.

Comments

TdiBuildAssociateAddress sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_ASSOCIATE_ADDRESS as the MinorFunction codes in the transport's I/O stack location of the given IRP.

A kernel-mode client must open both a connection endpoint and an address with successful calls to ZwCreateFile before it calls TdiBuildAssociateAddress. The client must make the associate-address request before it makes a connection to the remote node either with a request set up with TdiBuildListen, optionally followed by one set up with TdiBuildAccept, to the transport driver or with a request set up with TdiBuildConnect to the transport driver.

However, a client can get and/or set information about the open connection or address with one or more requests set up with TdiBuildQueryInformation and/or TdiBuildSetInformation before making the associate-address request.

After the client associates the connection endpoint with the address, it can make any other TDI_XXX requests to the transport on the just-associated connection with one exception: it cannot make a TDI_ASSOCIATE_ADDRESS request again for the specific endpoint until it makes a successful TDI_DISASSOCIATE_ADDRESS request to the transport.

See Also

TDI_ASSOCIATE_ADDRESS, TdiBuildAccept, TdiBuildConnect, TdiBuildDisassociateAddress, TdiBuildInternalDeviceControlIrp, TdiBuildListen, TdiBuildQueryInformation, TdiBuildSetInformation


TdiBuildConnect

VOID
TdiBuildConnect (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PLARGE_INTEGER Time,
 IN PTDI_CONNECTION_INFORMATION RequestConnectionInfo,
 OUT PTDI_CONNECTION_INFORMATION ReturnConnectionInfo
 );

TdiBuildConnect sets up an internal device control IRP for a TDI_CONNECT request to the underlying transport in which a local-node client has already associated an address and a connection endpoint.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing the connection endpoint.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
Time
Points to a variable specifying a system-relative time-out interval that the transport is to use for the connection attempt, or this parameter can be NULL.

An explicit time-out must be specified as the negative of the number of 100-nanosecond intervals, relative to the current system time, that its connection attempt is allowed to remain pending before the transport fails this connect request. If this pointer is NULL, the transport uses its own default time-out interval for establishing a connection with any remote node.

RequestConnectionInfo
Points to a caller-supplied buffer containing a TDI_CONNECTION_INFORMATION structure that specifies the remote-node client address to which the local-node client is requesting an endpoint-to-endpoint connection.
ReturnConnectionInfo
Points to a caller-supplied buffer in which the transport returns the remote-node client address with which it has established a connection. This buffer is also formatted as TDI_CONNECTION_INFORMATION structure. If a local-node client requires no output information or if the underlying transport does not return such information, this parameter should be NULL.

Comments

TdiBuildConnect sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_CONNECT as the MinorFunction codes in the transport's I/O stack location of the given IRP.

If the specified endpoint is inactive or nonexistent, the transport fails the connect request. If the specified endpoint has not been associated with a local-node address, the transport also fails the connect request.

For a local-node client to establish an endpoint-to-endpoint connection with a remote-node peer process, it must first associate an idle local connection endpoint with a local-node address. A client cannot initiate a connection attempt to a remote-node peer until it has made a successful TDI_ASSOCIATE_ADDRESS request, which it set up with TdiBuildAsociateAddress, to its underlying transport.

For a local-node client's connect request to succeed, the remote-node client must have an open listen request or a registered ClientEventConnect routine that can receive notification from its underlying transport of connection offers.

See Also

ClientEventConnect, TdiBuildAssociateAddress, TdiBuildInternalDeviceControlIrp, TdiBuildListen, TdiBuildSetEventHandler, TDI_CONNECT, TDI_CONNECTION_INFORMATION


TdiBuildDisassociateAddress

VOID
TdiBuildDisassociateAddress (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt
 );

TdiBuildDisassociateAddress sets up an internal device control IRP for a TDI_DISASSOCIATE_ADDRESS request to the underlying transport in which a local-node client has already associated an address and a connection endpoint.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing the connection endpoint. The client previously made a successful request, set up with TdiBuildAssociateAddress, to the transport to set up an association between this connection and a local-node address.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.

Comments

TdiBuildDisassociateAddress sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_DISASSOCIATE_ADDRESS as the MinorFunction codes in the transport's I/O stack location of the given IRP.

A kernel-mode client makes this request to disassociate a connection endpoint for an inactive connection from the associated address object, whether that client initiated the disconnection from its remote-node peer or vice versa.

If this request is successful, the client can reassociate the connection endpoint with another local-node address or the local-node address with another connection endpoint by setting up another IRP for the underlying transport with TdiBuildAssociateAddress.

See Also

TdiBuildAssociateAddress, TdiBuildDisconnect, TdiBuildInternalDeviceControlIrp, TDI_DISASSOCIATE_ADDRESS


TdiBuildDisconnect

VOID
TdiBuildDisconnect (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PLARGE_INTEGER Time,
 IN PULONG Flags,
IN PTDI_CONNECTION_INFORMATION RequestConnectionInfo,
 OUT PTDI_CONNECTION_INFORMATION ReturnConnectionInfo
 );

TdiBuildDisconnect sets up an internal device control IRP for a TDI_DISCONNECT request to the underlying transport through which a local-node client has already established an endpoint-to-endpoint connection with a remote-node peer or to which that client previously made a listen request in anticipation of establishing such a connection.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing the connection endpoint.

The client previously made a successful request, set up with TdiBuildAssociateAddress, to the transport to set up an association between this connection endpoint and a local-node address. In addition, the client previously did one of the following:

  • Made a successful request, set up with TdiBuildConnect, to establish an endpoint-to-endpoint connection with a remote-node peer

  • Accepted this connection offer from its remote-node peer, which initiated the TDI_CONNECT request

  • Submitted a successful request, set up with TdiBuildListen, that has just been satisfied by a connection offer from a remote-node peer
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
Time
Points to a variable specifying a system-relative time-out interval that the transport is to use for the disconnection attempt, or this parameter can be NULL.

An explicit time-out must be specified as the negative of the number of 100-nanosecond intervals, relative to the current system time, that its disconnection attempt is allowed to remain pending before the transport fails this disconnect request. If this pointer is NULL, the transport uses its own default time-out interval for disconnecting from any remote node.

Flags
Points to a variable that is either zero or, if the underlying transport supports controlled disconnects, to one of the following:
TDI_DISCONNECT_ABORT
Specifies that the underlying transport should close the connection immediately without attempting to complete any pending requests. This value is the default, equivalent to setting Flags to zero.

A client can make a disconnect request with TDI_DISCONNECT_ABORT at any time its connection is not idle. For example, such a client might want to cancel a pending connect or listen request that it made previously. As another example, a client can force the underlying transport to close an endpoint-to-endpoint connection by issuing a second disconnect request with TDI_DISCONNECT_ABORT when its preceding disconnect request with TDI_DISCONNECT_RELEASE remains pending.

TDI_DISCONNECT_RELEASE
Specifies that the underlying transport should initiate a controlled disconnect, as described later (see Comments). The remote-node client also issues a disconnect request with this value to confirm a controlled disconnect.

The TDI_DISCONNECT_WAIT flag is not used.

Clients can set only one of the preceding options in a disconnect request. The transport will fail any attempt to set more than one of these options.

RequestConnectionInfo
Points to a client-supplied buffer containing a TDI_CONNECTION_INFORMATION structure, possibly containing disconnection data if the client sends any when initiating an endpoint-to-endpoint controlled disconnection. This parameter should be NULL unless the caller initiated a disconnection with TDI_DISCONNECT_RELEASE.
ReturnConnectionInfo
Points to a caller-supplied buffer, also formatted as TDI_CONNECTION_INFORMATION structure, in which the transport returns any disconnect data it received from the remote node, assuming the remote-node client initiated a controlled disconnection. This parameter should be NULL unless the caller is acknowledging a disconnection with TDI_DISCONNECT_RELEASE. The local-node client cannot retrieve any returned disconnect data after it reuses the connection in a request set up with TdiBuildListen or TdiBuildConnect.

Comments

TdiBuildDisconnect sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_DISCONNECT as the MinorFunction codes in the transport's I/O stack location of the given IRP. This IRP requests that an already established endpoint-to-endpoint connection with a remote-node peer be broken.

A client also can issue a disconnect request to reject an incoming request to connect that was initiated by a remote-node peer. In this case, the client is responding to a completed TDI_LISTEN request that it made previously with the TDI_QUERY_ACCEPT option selected, or the client is rejecting an offered connection from its ClientEventConnect function. Such a client calls TdiBuildDisconnect either with zero or with TDI_DISCONNECT_ABORT at Flags.

While an established endpoint-to-endpoint connection is being disconnected, the underlying transport driver usually refuses any incoming requests for the connection and stops all activity at the specified connection endpoint. However, that connection endpoint remains valid for reuse by the client when the disconnect operation is done.

Because any disconnection operation takes time to complete, the client can receive additional TSDUs on an established endpoint-to-endpoint connection before its disconnect IRP is fully completed. Therefore, a client can assume that the context it maintains for such a connection remains valid until it is notified of disconnect completion, for example, by a call to the IoCompletion routine it supplied to TdiBuildDisconnect. Disconnect-completion notification implies the cessation of all activity, including receive indications, on the connection.

Unless a client requests a controlled disconnect with TDI_DISCONNECT_RELEASE, the transport completes no outstanding I/O requests on the connection and does not confirm that the remote-node peer has been notified of the disconnection before it returns control from a disconnect request. For such a disconnect request, the underlying transport simply cancels all outstanding requests on the given connection, completes its client's disconnect request, and returns control.

Either client on an established endpoint-to-endpoint connection can request a controlled disconnect if the underlying TDI transports support this. If so, the client that initiates the disconnection operation with a TDI_DISCONNECT request causes both transports to coordinate their operations so the remote-node peer is informed that the connection is closing and both transports complete any pending transfers on the connection before it is broken. In these circumstances, the remote-node client must confirm the disconnect before the underlying transport can return from the initiating (local-node) client's disconnect request.

The following summarizes the sequence of operations for a controlled disconnect:

  1. The local-node client initiates a disconnection by making a disconnect request in which TDI_DISCONNECT_RELEASE was set at Flags. Subsequently, this client can no longer send data on the connection, but it can continue to receive data from its remote-node peer. The underlying transport carries out any pending send operations and completes the pending send requests it is currently holding when such a local-node client makes its disconnect request.

  2. The transport on the remote node completes any pending receive requests its client has made. Then, the remote-node transport notifies its client of the pending disconnect, either by calling ClientEventDisconnect if the remote-node client has registered this event handler or by returning an error code on an outstanding request made by its client. After such a notification, the remote-node client can no longer receive data on the connection, but it can continue to send data until it submits disconnect request with the TDI_DISCONNECT_RELEASE option to the underlying transport.

  3. When the remote-node client has finished sending data, it makes a disconnect request to its underlying transport with the TDI_DISCONNECT_RELEASE option selected. The underlying transport completes all outstanding sends on the connection normally before completing its client's disconnect request. When this transport returns from the disconnect, the connection has been closed from the point of view of its remote-node client. However, the connection endpoint is still valid and that client can reuse it later.

  4. The remote-node transport notifies the local-node transport to confirm disconnect-completion at the remote node. At this point, the original controlled disconnect request is completed by the local-node transport and the connection has been closed from the point of view of the local-node client. However, the connection endpoint is still valid and that client can reuse it later.

See Also

ClientEventDisconnect, TdiBuildConnect, TdiBuildListen, TdiBuildInternalDeviceControlIrp, TDI_CONNECTION_INFORMATION, TDI_DISCONNECT


TdiBuildInternalDeviceControlIrp

PIRP
TdiBuildInternalDeviceControlIrp (
 IN CCHAR IrpSubFunction,
 IN PDEVICE_OBJECT DeviceObject,
 IN PFILE_OBJECT FileObject,
 IN PKEVENT Event,
 IN PIO_STATUS_BLOCK IoStatusBlock
 );

TdiBuildInternalDeviceControlIrp allocates an IRP for a client-initiated internal device control request. After it returns, the client calls another TdiBuild Xxx macro with the returned IRP to set up the I/O stack location of the underlying transport driver before making the request with IoCallDriver.

Parameters

IrpSubFunction
Specifies the TDI_XXX code that will be set in a subsequent call to a TdiBuild Xxx macro as the MinorFunction for the returned IRP. This parameter can be one of the following:
TDI_ASSOCIATE_ADDRESS
The caller will pass the returned IRP to TdiBuildAssociateAddress.
TDI_DISASSOCIATE_ADDRESS
The caller will pass the returned IRP to TdiBuildDisassociateAddress.
TDI_CONNECT
The caller will pass the returned IRP to TdiBuildConnect.
TDI_LISTEN
The caller will pass the returned IRP to TdiBuildListen.
TDI_ACCEPT
The caller will pass the returned IRP to TdiBuildAccept.
TDI_DISCONNECT
The caller will pass the returned IRP to TdiBuildDisconnect.
TDI_SEND
The caller will pass the returned IRP to TdiBuildSend.
TDI_RECEIVE
The caller will pass the returned IRP to TdiBuildReceive.
TDI_SEND_DATAGRAM
The caller will pass the returned IRP to TdiBuildSendDatagram.
TDI_RECEIVE_DATAGRAM
The caller will pass the returned IRP to TdiBuildReceiveDatagram.
TDI_SET_EVENT_HANDLER
The caller will pass the returned IRP to TdiBuildSetEventHandler.
TDI_QUERY_INFORMATION
The caller will pass the returned IRP to TdiBuildQueryInformation.
TDI_SET_INFORMATION
The caller will pass the returned IRP to TdiBuildSetInformation.
TDI_ACTION
The caller will pass the returned IRP to TdiBuildAction.
DeviceObject
Points to the device object created by the next lower TDI transport driver.
FileObject
Points to a file object representing an address, connection endpoint, or control channel, depending on the given IrpSubFunction.
Event
Points to an initialized event object for which the caller provides the storage.
IoStatusBlock
Points to a caller-supplied variable of type IO_STATUS_BLOCK.

Return Value

TdiBuildInternalDeviceControlIrp returns a pointer to the IRP it allocated, or it returns NULL if the allocation attempt failed.

Comments

Kernel-mode clients of TDI transports frequently pass IRPs sent down by still higher level network components directly to TdiBuild Xxx macros without calling TdiBuildInternalDeviceControlIrp.

However, such a client should never hijack an incoming IRP and reuse it to make an internal device control request to the underlying transport on its own behalf. Instead the client can call TdiBuildInternalDeviceControlIrp to get an IRP of its own with which to make such requests.

After TdiBuildInternalDeviceControl returns a pointer to an IRP, the client must pass the IRP to the appropriate TdiBuild Xxx macro, along with the additional parameters the TDI_XXX requires, before it passes the IRP on to the transport with IoCallDriver. TdiBuildInternalDeviceControl partially sets up the I/O stack location of the underlying transport in the IRP it allocates. However, it does not format the I/O stack location according to a TDI transport's expectations for a TDI_XXX request as the TdiBuild Xxx macros do.

If the client supplies an Event pointer when it calls TdiBuildInternalDeviceControlIrp, the I/O Manager will set the event to the Signaled state when the IRP is completed by lower drivers. However, the client cannot wait on that event for completion of its IRP unless it waits at IRQL PASSIVE_LEVEL in an appropriate thread context, either that of a client-created thread or, possibly, that of the thread in which it was executing when it made this call.

Callers of TdiBuildInternalDeviceControlIrp must be running at IRQL PASSIVE_LEVEL. Consequently, a client should preallocate a few IRPs with this function so the client can use them to submit TDI_XXX requests when it is running at a higher IRQL.


TdiBuildListen

VOID
TdiBuildListen (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN ULONG Flags,
 IN PTDI_CONNECTION_INFORMATION RequestConnectionInfo,
 OUT PTDI_CONNECTION_INFORMATION ReturnConnectionInfo
 );

TdiBuildListen sets up an internal device control IRP for a TDI_LISTEN request to the underlying transport in which the local-node client has already associated an address and a connection endpoint.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing the connection endpoint. The client previously made a successful request, set up with TdiBuildAssociateAddress, to the transport to set up an association between this connection and a local-node address.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the client's IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
Flags
Specifies zero or, possibly, TDI_QUERY_ACCEPT if the underlying transport supports delayed-connection acceptances.

If Flags is zero, the underlying transport accepts the connection from the remote node as soon as it is offered. The client can assume the endpoint-to-endpoint connection is fully operational when its listen request completes. In fact, receives on the connection endpoint can occur even before this listen IRP is fully completed back to the client.

If the transport supports delayed-connection acceptances and its client sets TDI_QUERY_ACCEPT, that client must either accept or reject the offered connection as quickly as possible when the listen request completes. That is, on completion of this IRP, the client must set up another request to the transport with either TdiBuildAccept or TdiBuildDisconnect, respectively.

RequestConnectionInfo
Points to a client-supplied buffer containing a TDI_CONNECTION_INFORMATION structure that specifies the remote-node client address from which the local-node client expects an endpoint-to-endpoint connection offer.

Depending on the value of Flags, the client also provides additional information in the following members of this structure:

UserData
Points to a buffer of data the client wants sent with an acceptance of the connection from the remote node if Flags is zero.

A client sets this member to NULL if the underlying transport supports delayed-connection acceptances and the client sets Flags to TDI_QUERY_ACCEPT. Such a client can provide accept data when it subsequently accepts an offered connection, assuming it does.

UserDataLength
Specifies the size in bytes of the buffer at UserData. This member is zero if UserData is NULL.
Options
Set to the same value as Flags.
OptionsLength
Set to sizeof(ULONG).
RemoteAddress
Points to a buffer specifying the remote-node client transport address from which the local-node client expects a connection offer that the local-node client will accept. This member can be NULL if an offered connection from any remote node is acceptable to the client.

For some transports, a local-node client can specify a partial address. However, TDI drivers vary in their ability to handle partial addresses. Because the syntax of any transport address is TDI-driver-specific, the underlying transport defines the conventions for specifying a partial address if it supports partial-address specifications.

RemoteAddressLength
Specifies the size in bytes of the buffer at RemoteAddress. This member should be set to zero if RemoteAddress is NULL.
ReturnConnectionInfo
Points to a caller-supplied buffer in which the transport returns the remote-node address from which a connection offer was made. This buffer is also formatted as TDI_CONNECTION_INFORMATION structure.

If the local-node client set Flags to TDI_QUERY_ACCEPT, it examines the returned information to determine whether to accept or reject the connection when this listen request completes.

If the client requires no output information or if the underlying transport does not return such information, this parameter should be NULL.

Comments

TdiBuildListen sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_LISTEN as the MinorFunction codes in the transport's I/O stack location of the given IRP.

Before it calls the underlying transport with the IRP set up by TdiBuildListen, a client must make a preceding associate-address request, set up with TdiBuildAssociateAddress, that succeeded in associating an idle connection endpoint with a local-node address. After this association is established, a client can issue numerous listen requests until it accepts an offered endpoint-to-endpoint connection.

The underlying transport usually processes each client's listen requests in FIFO order unless it supports quality-of-service (QOS) for its clients and, therefore, prioritizes incoming listen requests. However, a client cannot make assumptions about the order in which the underlying transport processes requests from all its current clients.

Client-supplied addressing information at RequestConnectionInfo acts as a filter that the underlying transport driver applies, before checking whether the client set TDI_QUERY_ACCEPT if the transport supports delayed-connection acceptances. If an incoming connection offer does not match an address specified in the client's listen request, the transport cannot complete the listen request for the client. Otherwise, when an incoming connection offer is made from a specified remote-node address, the local-node client's listen request is completed in one of the following ways:

  • If the client set Flags to TDI_QUERY_ACCEPT indicating delayed-connection acceptance, the transport completes the listen request, and the client must either accept or reject the offered endpoint-to-endpoint connection by issuing a TDI_ACCEPT or TDI_DISCONNECT request to the transport immediately.

  • If the client set Flags to zero, possibly because the underlying transport does not support delayed-connection acceptances, the transport transmits an acceptance, along with any accept data the client supplied, to the remote node and completes the listen request.

Before making a listen request, a client can set up one or more IRPs with TdiBuildSetEventHandler and register its ClientEvent(Chained)Receive(Expedited) handler(s) for the local address associated with the connection endpoint. If it does this, the ClientEvent(Chained)Receive(Expedited) routine(s) can receive TSDUs before the client's listen request completes. Such a client must be prepared to accept an immediate connection when it makes a listen request.

While a listen for a client's connection endpoint is pending on a particular local-node address, that client's ClientEventConnect function cannot be called except under special circumstances. That is, the underlying transport can call ClientEventConnect if a listen operation is pending and the transport determines that the remote address acceptance criteria would prevent the listen operation from being completed.

As mentioned previously, some transports support the specification of remote-node partial addresses on listens. A transport also might support the specification of QOS options. For example, such a TDI transport's clients might specify the QOS for a listen using a variable-length counted string with transport-defined syntax and semantics.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventConnect, ClientEventReceive, ClientEventReceiveExpedited, TdiBuildAccept, TdiBuildDisconnect, TdiBuildInternalDeviceControlIrp, TdiBuildSetEventHandler, TDI_LISTEN, TDI_CONNECTION_INFORMATION


TdiBuildNetbiosAddress

VOID
TdiBuildNetbiosAddress (
 IN PUCHAR NetbiosName,
 IN BOOLEAN IsGroupName,
 IN OUT PTA_NETBIOS_ADDRESS NetworkName
 );

TdiBuildNetbiosAddress sets up a TA_NETBIOS_ADDRESS structure for the caller.

Parameters

NetbiosName
Points to 16-byte buffer containing a NetBIOS name.
IsGroupName
Specifies TRUE if the given name is a group address.
NetworkName
Points to a caller-supplied buffer, which must be at least sizeof(TA_NETBIOS_ADDRESS) bytes in length.

TdiBuildNetbiosAddress fills in the members of this structure.

See Also

TA_NETBIOS_ADDRESS, TdiBuildNetbiosAddressEa


TdiBuildNetbiosAddressEa

NTSTATUS
TdiBuildNetbiosAddressEa(
IN PUCHAR Buffer,
IN BOOLEAN GroupName,
IN PUCHAR NetbiosName
);

TdiBuildNetbiosAddressEa sets up a buffered NetBIOS address that the caller can pass subsequently to ZwCreateFile as the EaBuffer parameter.

Parameters

Buffer
Points to a caller-supplied buffer, which must be at least 40 bytes in length.
GroupName
Specifies TRUE if the address to be set up is a group address.
NetbiosName
Points to 16-byte buffer containing a NetBIOS name.

Return Value

TdiBuildNetbiosAddressEa returns STATUS_SUCCESS if it set up the given EA buffer. Otherwise, it can return STATUS_UNSUCCESSFUL if the given buffer is too small or, possibly, a propagated error status if attempting to access the given parameters raised an exception.

Comments

TdiBuildNetbiosAddressEa sets up the buffer in a manner suitable for calling ZwCreateFile to open a NetBIOS address, with a TA_NETBIOS_ADDRESS structure as the EA value.

See Also

TA_NETBIOS_ADDRESS, TdiBuildNetbiosAddress


TdiBuildQueryInformation

VOID
TdiBuildQueryInformation (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN UINT QType,
 IN PMDL MdlAddr
 );

TdiBuildQueryInformation sets up an internal device control IRP for a TDI_QUERY_INFORMATION request to the underlying transport in which the local-node client has already opened a file object representing an address, a connection endpoint, or a control channel.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing an address, connection endpoint, or control channel.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
QType
Specifies what the client is querying. This parameter can be transport-defined or one of the following system-defined values, for which all transports support queries:
TDI_QUERY_ADDRESS_INFO
Specifies that the transport should return the information, formatted as a TDI_ADDRESS_INFO structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a transport address or a connection endpoint already associated with a transport address.

TDI_QUERY_CONNECTION_INFO
Specifies that the transport should return the information, formatted as a TDI_CONNECTION_INFO structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a connection endpoint.

TDI_QUERY_BROADCAST_ADDRESS
Specifies that the transport should return the information, formatted as a TRANSPORT_ADDRESS structure, in the client-supplied buffer mapped at MdlAddr.

FileObject must point to an open file object representing a control channel.

TDI_QUERY_NETWORK_ADDRESS
Specifies that the transport should return the information, formatted as a TRANSPORT_ADDRESS structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a control channel.

TDI_QUERY_DATA_LINK_ADDRESS
Specifies that the transport should return the information, formatted as a TRANSPORT_ADDRESS structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a control channel.

TDI_QUERY_PROVIDER_INFO or TDI_QUERY_PROVIDER_INFORMATION
Specifies that the transport should return the information, formatted as a TDI_PROVIDER_INFO structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a control channel.

TDI_QUERY_PROVIDER_STATISTICS
Specifies that the transport should return the information, formatted as a TDI_PROVIDER_STATISTICS structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a control channel.

TDI_QUERY_DATAGRAM_INFO
Specifies that the transport should return the information, formatted as a TDI_DATAGRAM_INFO structure, in the client-supplied buffer mapped at MdlAddr.

FileObj usually points to an open file object representing a control channel. If the underlying transport supports it, FileObj can point to an open file object representing an address.

TDI_QUERY_MAX_DATAGRAM_INFO
Specifies that the transport should return the information, formatted as a TDI_MAX_DATAGRAM_INFO structure, in the client-supplied buffer mapped at MdlAddr.

FileObj usually points to an open file object representing a control channel. If the underlying transport supports it, FileObj can point to an open file object representing an address.

The underlying transport can support additional types of query-information requests at the discretion of the driver writer. Such a transport must define TDI_QUERY_XXX codes in which the most significant bit is set.

In addition to the preceding, clients of a NetBIOS transport can specify any of the following for QType:

TDI_QUERY_FIND_NAME
Specifies that the transport should return the information, formatted as FIND_NAME_HEADER followed by FIND_NAME_BUFFER structures, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing an address.

TDI_QUERY_SESSION_STATUS
Specifies that the transport should return the information, formatted as SESSION_HEADER followed by SESSION_BUFFER structures, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a control channel.

TDI_QUERY_ADAPTER_STATUS
Specifies that the transport should return the information, formatted as an ADAPTER_STATUS structure, in the client-supplied buffer mapped at MdlAddr.

FileObj must point to an open file object representing a control channel.

For more information about the preceding structures, see the Win32 SDK.

MdlAddr
Points to an MDL mapping a client-supplied buffer containing any necessary request-specific information set up by the client. The transport returns the requested information in this buffer, formatted according to the given QType.

Comments

TdiBuildQueryInformation sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_QUERY_INFORMATION as the MinorFunction codes in the transport's I/O stack location of the given IRP.

A TDI client can use the query request to ask its underlying transport for information, such as connection-status information, a broadcast address it can use, or the features of the transport such as its size limits for sends, datagrams, and user-connect data.

The client can query information about an address, a connection endpoint, or a control channel, depending on the specified QType. The client must supply a FileObj pointer to TdiBuildQueryInformation to an open file object that is appropriate to the given QType. Otherwise, the underlying transport cannot satisfy its query request.

See Also

TDI_ADDRESS_INFO, TdiBuildInternalDeviceControlIrp, TDI_CONNECTION_INFO, TDI_DATAGRAM_INFO, TDI_MAX_DATAGRAM_INFO, TDI_PROVIDER_INFO, TDI_PROVIDER_STATISTICS, TDI_QUERY_INFORMATION, TRANSPORT_ADDRESS


TdiBuildReceive

VOID
TdiBuildReceive (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PMDL MdlAddr,
 IN ULONG InFlags,
IN ULONG ReceiveLen
 );

TdiBuildReceive sets up an internal device control IRP for a TDI_RECEIVE request to the underlying transport in which the local-node client has established an endpoint-to-endpoint connection with a remote-node peer.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing a connection endpoint.

The caller previously made a successful request, set up with TdiBuildAssociateAddress, to the transport to set up an association between this connection endpoint and a local-node address. When the association was established, the caller also established an endpoint-to-endpoint connection with a remote-node peer, by issuing a successful request set up with TdiBuildConnect or TdiBuildListen, the latter possibly followed by a successful request set up with TdiBuildAccept.

CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
MdlAddr
Points to an MDL, possibly the initial MDL in a chain of MDLs, mapping a client-supplied buffer into which the transport is to transfer the received TSDU according to the given InFlags.
InFlags
Specifies the type of receive to be returned by the transport as one or more (Ored) or none (zero) of the following:
TDI_RECEIVE_NORMAL
Return a normal TSDU to the client-supplied buffer.
TDI_RECEIVE_EXPEDITED
Return an expedited TSDU to the client-supplied buffer.

If both TDI_RECEIVE_NORMAL and TDI_RECEIVE_EXPEDITED are set or if neither is set, the underlying transport will indicate either type of TSDU for this receive request.

TDI_RECEIVE_PEEK
Return indicated data as soon as a receive arrives from the remote node, even if it is a partial TSDU, and the client will request another receive for the remainder of the TSDU to be transferred into this buffer if the packet is of interest to this client. This flag is valid only if the underlying transport buffers received data internally.
ReceiveLen
Specifies the size in bytes of the client-supplied receive buffer.

Comments

TdiBuildReceive sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_RECEIVE as the MinorFunction codes in the transport's I/O stack location of the given IRP.

If the client has registered any of its ClientEvent(Chained)Receive(Expedited) handler(s) for the given connection endpoint, issuing a receive request suppresses calls to ClientEvent(Chained)Receive(Expedited) until the receive IRP is satisfied. Then, the transport resumes indicating receives to the appropriate ClientEvent(Chained)Receive(Expedited) handler(s).

Unless the TDI_RECEIVE_PEEK flag is set, the underlying transport fills the given buffer with received data until the client's buffer is full or the transport receives an end-of-record indication from the remote node. If this flag is set, the client must issue subsequent receive request(s) when this IRP is satisfied to obtain the full TSDU.

If the underlying transport is receiving normal data and expedited data arrives from the remote node, the transport pre-empts its normal receive operation and returns immediately to the client with any normal data it has already transferred into the client-supplied buffer. When this occurs, the client must make as many receive requests as necessary to receive the expedited TSDU. When the client has retrieved all the arriving expedited data, it issues another receive request to resume receiving normal data.

A client can also receive normal TSDUs on an endpoint-to-endpoint connection by calling ZwReadFile. However, the client cannot receive expedited data in this manner because the transport has no way to indicate receive flags. In addition, a call to ZwReadFile does not tell the client whether the underlying transport has received an end-of-record indication.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, TdiBuildAccept, TdiBuildConnect, TdiBuildInternalDeviceControlIrp, TdiBuildListen, TdiBuildReceiveDatagram, TdiBuildSend, TdiBuildSetEventHandler, TDI_RECEIVE


TdiBuildReceiveDatagram

VOID
TdiBuildReceiveDatagram (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PMDL MdlAddr,
 IN ULONG ReceiveLen,
 IN PTDI_CONNECTION_INFORMATION ReceiveDatagramInfo,
 OUT PTDI_CONNECTION_INFORMATION ReturnInfo,
 ULONG InFlags
 );

TdiBuildReceiveDatagram sets up an internal device control IRP for a TDI_RECEIVE_DATAGRAM request to the underlying transport in which the local-node client has already opened a file object representing an address.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing an address.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
MdlAddr
Points to an MDL, possibly the initial MDL in a chain of MDLs, mapping a client-supplied buffer into which the transport is to transfer the received datagram.
ReceiveLen
Specifies the maximum size in bytes of the datagram to return in the buffer mapped at MdlAddr. If this parameter is zero, the transport can use the whole buffer mapped at MdlAddr.
ReceiveDatagramInfo
Points to a TDI_CONNECTION_INFORMATION structure specifying a particular remote-node address from which this local-node client will accept a datagram. If the RemoteAddressLength member is zero, a datagram from any remote node is acceptable to this client. A RemoteAddressLength of zero implies a RemoteAddress set to NULL.
ReturnInfo
Points to caller-supplied buffer in which the transport is to return the source address of the received datagram, formatted as a TDI_CONNECTION_INFORMATION structure.
InFlags
Specifies the type of receive to be returned by the transport as one or more (Ored) or none (zero) of the following:
TDI_RECEIVE_NORMAL
Return a normal TSDU to the client-supplied buffer.
TDI_RECEIVE_PEEK
Return an indicated datagram as soon as it arrives from a remote node, even if it is a partial TSDU, and the client will request another receive for the remainder of the datagram if it is of interest to this client. This flag is valied only if the underlying transport buffers received datagrams internally.

Comments

TdiBuildReceiveDatagram sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_RECEIVE_DATAGRAM as the MinorFunction codes in the transport's I/O stack location of the given IRP.

Because a datagram is not associated with an established connection, the TDI driver returns the transport address of the remote-node sender to the receiving client along with the datagram.

The transport satisfies this IRP when it receives a datagram directed to the local-node address represented by the client's open file object and a source address matching the client's specification at ReceiveDatagramInfo. If such a received datagram is too large for the client-supplied buffer, the underlying transport truncates the datagram, discarding any superfluous data, and completes the client's receive-datagram request.

TDI transports never fragment datagrams. Consequently, their clients make one receive-datagram request to receive each datagram. To determine the maximum possible value for ReceiveLen, the client usually issued a preceding query-information request, set up with TdiBuildQueryInformation for any of the QType values TDI_QUERY_PROVIDER_INFO, TDI_QUERY_DATAGRAM_INFO, or TDI_QUERY_MAX_DATAGRAM_INFO.

A client of a NetBIOS transport can receive a broadcast datagram by opening a broadcast address that has a transport-specific format, which can be obtained with a query-information request. However, such a client cannot send a datagram from the broadcast address, nor can it establish an association between a connection of its own and that broadcast address.

Windows NT currently specifies the reception of broadcast datagrams only for NetBIOS TDI clients. From some other transports, a client can receive a broadcast datagram in the same way it receives a nonbroadcast datagram.

A client also can receive datagrams by registering its ClientEventReceiveDatagram and/or ClientEventChainedReceiveDatagram handler(s) on a particular address.

See Also

ClientEventChainedReceiveDatagram, ClientEventReceiveDatagram, TdiBuildInternalDeviceControlIrp, TdiBuildQueryInformation, TdiBuildSendDatagram, TdiBuildSetEventHandler, TDI_CONNECTION_INFORMATION, TDI_RECEIVE_DATAGRAM


TdiBuildSend

VOID
TdiBuildSend (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PMDL MdlAddr,
 IN ULONG InFlags,
 IN ULONG SendLen
 );

TdiBuildSend sets up an internal device control IRP for a TDI_SEND request to the underlying transport in which the local-node client has already opened a file object representing a connection endpoint and established an endpoint-to-endpoint connection with a remote-node peer.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing a connection endpoint.

The caller previously made a successful request, set up with TdiBuildAssociateAddress, to the transport to set up an association between this connection endpoint and a local-node address. When the association was established, the caller also established an endpoint-to-endpoint connection with a remote-node peer by issuing a successful request set up with TdiBuildConnect or TdiBuildListen, the latter possibly followed by a successful request set up with TdiBuildAccept.

CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
MdlAddr
Points to an MDL, possibly the initial MDL in a chain of MDLs, mapping a client-supplied buffer from which the transport is to transfer the data to be sent.
InFlags
Specifies how the data should be transmitted. If this parameter is zero, the transport should send the given data as a normal TSDU. Otherwise, this parameter can be one or a combination (Ored) of the following flags:
TDI_SEND_EXPEDITED
The given data should be sent ahead of any normal send requests the transport is currently holding queued for transmission on this endpoint-to-endpoint connection, assuming the underlying transport supports expedited transfers.
TDI_SEND_PARTIAL
The given data is terminated by an end-of-record but is not a complete message-mode TSDU. The client will send the additional data later. This flag is irrelevant to clients of stream-mode transports.
TDI_SEND_NO_RESPONSE_EXPECTED
The caller is giving a hint to the underlying transport that it does not expect a response to this send from its remote-node peer. A client uses this flag to disable piggybacking of the TSDU acknowledgment by the remote-node transport, assuming both transports support this optimization. Otherwise, its underlying transport might time out, waiting for a piggybacked acknowledgment of the send from the remote node.
TDI_SEND_NON_BLOCKING
If the underlying transport currently has no internal buffer space available for the given data, it should fail this send request with STATUS_DEVICE_NOT_READY, rather than blocking until it can buffer the given data internally. The transport should call the ClientEventSendPossible handler when it again has internal buffer space available for sends. This flag is irrelevant to clients of transports that do no internal buffering.
SendLen
Specifies the size in bytes of the buffer mapped at MdlAddr. The maximum for this parameter depends on the underlying transport. Whether zero is valid also depends on the underlying transport. (See Comments.)

Comments

TdiBuildSend sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_SEND as the MinorFunction codes in the transport's I/O stack location of the given IRP.

In each send request set up with TdiBuildSend, the caller provides a buffer containing the TSDU to be sent to its remote-node peer, or part of a TSDU if the underlying transports support message-mode transfers. A client can send any amount of data up to the maximum size the TDI driver allows. To obtain the transport-specific maximum size for sends, the client can make a TDI_QUERY_PROVIDER_INFO request, set up with TdiBuildQueryInformation.

After the caller of TdiBuildSend passes the send IRP to the underlying transport with IoCallDriver, the client-supplied buffer at MdlAddress is inaccessible to that client until the send operation is completed. The client must not attempt to modify or use its send buffer until the IRP is returned to the client's IoCompletion routine or the client is certain (through protocol semantics) that its underlying transport has completed send operations for the data in that buffer.

The local-node transport can queue several send requests internally, but it always transmits them in FIFO order. If the transport supports expedited sends, it queues incoming expedited-send requests in FIFO order ahead of any normal sends it is currently holding. The transport is responsible for completing incoming sends in a timely manner, with either a success or error status. The transport can complete a send with a time-out error based on a driver-determined estimate of a reasonable time-out interval for current network conditions.

Each local-node client's send request on an endpoint-to-endpoint connection corresponds to one of the following:

  • One receive request issued by the remote-node client to its underlying TDI transport

  • One call to the remote-node client's registered ClientEventReceive, ClientEventReceiveExpedited, ClientEventChainedReceive, or ClientEventChainedReceiveExpedited handler by its underlying TDI transport

If the underlying transport supports message-mode sends, its client can send a TSDU in pieces as a sequence of send requests, each except the last specifying the TDI_SEND_PARTIAL flag. When this flag is clear, a message-mode transport considers such a sequence of partial sends to be at an end. In this transfer scenario, the transport on the remote node notifies the receiving client of partial TSDUs by leaving the TDI_RECEIVE_ENTIRE_MESSAGE flag clear in the corresponding receive indications until it indicates the last receive in such a sequence.

The caller of TdiBuildSend can request a nonblocking send by setting the TDI_SEND_NON_BLOCKING flag if the underlying transport has internal buffers into which it copies its clients' send data. Depending on the amount of internal buffer space available, the underlying transport handles such a send request as follows:

  • If it currently has sufficient internal buffer space, the transport copies the data from the client-supplied buffer and completes the send IRP with STATUS_SUCCESS.

  • If it currently has insufficient buffer space for the given SendLength, the transport copies as much data as possible from the client's buffer and returns STATUS_SUCCESS.

  • If the driver currently has no internal buffer space, the transport completes the send IRP with STATUS_DEVICE_NOT_READY.

    The client of such a transport registers a ClientEventSendPossible handler, which the transport will call when it again has internal buffer space available for the rejected send.

When its send is completed with STATUS_SUCCESS, the client of such a transport can determine how much data the transport has buffered for transmission by checking the IoStatus.Information member of the completed IRP. If necessary, the client's IoCompletion routine can resubmit another send request for the remaining data to be sent.

Some transports allow their clients to issue zero-length send requests. For such a transport, a zero-length send actually forces protocol flow, as long as the TDI_SEND_PARTIAL flag is clear. The client of such a transport can issue such a zero-length send as a message that satisfies a receive request. Transports that do not support this feature return an error when a client issues a zero-length send request.

A TDI client also can send a normal TSDU on an endpoint-to-endpoint connection with a call to ZwWriteFile. Because this routine does not allow the specification of TDI_SEND_XXX flags, client-supplied data given to ZwWriteFile is transmitted by the underlying transport as if it were packaged as a TdiBuildSend request with both TDI_SEND_PARTIAL and TDI_SEND_EXPEDITED clear in the SendFlags. The transport assumes that all the data in the buffer at MdlAddr should be sent, and the caller's ByteOffset and Key arguments to ZwWriteFile are ignored.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, ClientEventSendPossible, TdiBuildAccept, TdiBuildAssociateAddress, TdiBuildConnect, TdiBuildInternalDeviceControlIrp, TdiBuildListen, TdiBuildReceive, TdiBuildSetEventHandler, TDI_SEND


TdiBuildSendDatagram

VOID
TdiBuildSendDatagram (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN PMDL MdlAddr,
 IN ULONG SendLen,
 IN PTDI_CONNECTION_INFORMATION SendDatagramInfo
 );

TdiBuildSendDatagram sets up an internal device control IRP for a TDI_SEND_DATAGRAM request to the underlying transport in which the local-node client has already opened a file object representing an address.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing an address.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
MdlAddr
Points to an MDL, possibly the initial MDL in a chain of MDLs, mapping a client-supplied buffer from which the transport is to transfer the datagram to be sent.
SendLen
Specifies the size in bytes of the buffer mapped at MdlAddr. The maximum datagram size depends on the underlying transport. Whether zero is valid also depends on the underlying transport.
SendDatagramInfo
Points to a TDI_CONNECTION_INFORMATION structure specifying the remote-node address to which the caller-supplied datagram is to be sent.

Comments

TdiBuildSendDatagram sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_SEND_DATAGRAM as the MinorFunction codes in the transport's I/O stack location of the given IRP.

TDI transports never fragment datagrams. Consequently, their clients make one send-datagram request to transmit each datagram, which is associated only with the particular request for a connectionless data transfer to the given destination address. To determine the maximum possible value for SendLen, the client usually issued a preceding query-information request, set up with TdiBuildQueryInformation for any of the QType values TDI_QUERY_PROVIDER_INFO, TDI_QUERY_DATAGRAM_INFO, or TDI_QUERY_MAX_DATAGRAM_INFO.

Certain transports support broadcast and/or multicast datagram transmissions. The client of such a transport can send a datagram to a multicast or broadcast address on the remote node. However, the syntax of the multicast or broadcast address is driver-specific. The client can obtain such a transport's broadcast address by issuing a query-information request with the QType value TDI_QUERY_BROADCAST_ADDRESS.

Any connectionless transfer is inherently unreliable. The underlying transport can lose or duplicate any given datagram at the discretion of the driver writer.

When a client issues send-datagram request, the transport usually queues the request internally. Transports process send-datagram requests in FIFO order. They queue send-datagram requests separately from endpoint-to-endpoint send requests from the same client.

See Also

ClientEventChainedReceiveDatagram, ClientEventReceiveDatagram, TdiBuildInternalDeviceControlIrp, TdiBuildQueryInformation, TdiBuildReceiveDatagram, TdiBuildSend, TDI_CONNECTION_INFORMATION, TDI_SEND_DATAGRAM


TdiBuildSetEventHandler

VOID
TdiBuildSetEventHandler (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN INT InEventType,
 IN PVOID InEventHandler,
 IN PVOID InEventContext
 );

TdiBuildSetEventHandler sets up an internal device control IRP for a TDI_SET_EVENT_HANDLER request to the underlying transport in which the local-node client has already opened a file object representing an address.

Parameters

Irp
Points to a client-supplied IRP, either originating in a higher level network component or, usually, allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing an address.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
InEventType
Specifies the type of event handler being registered as one of the following:
TDI_EVENT_CONNECT
Register the given ClientEventConnect handler to be called when an endpoint-to-endpoint connection is offered by a remote-node peer.
TDI_EVENT_DISCONNECT
Register the given ClientEventDisconnect handler to be called when an established endpoint-to-endpoint connection is being closed by the remote-node peer.
TDI_EVENT_RECEIVE
Register the given ClientEventReceive handler to be called when a normal receive comes in on an endpoint-to-endpoint connection from the remote node and the client will be given the opportunity to copy all or part of the buffered TSDU.
TDI_EVENT_CHAINED_RECEIVE
Register the given ClientEventChainedReceive handler to be called when a normal receive comes in on an endpoint-to-endpoint connection from the remote node and the client will be given read-only access to the buffered full TSDU until it calls TdiReturnChainedReceives.
TDI_EVENT_RECEIVE_EXPEDITED
Register the given ClientEventReceiveExpedited handler to be called when an expedited receive comes in on an endpoint-to-endpoint connection from the remote node and the client will be given the opportunity to copy all or part of the buffered TSDU.
TDI_EVENT_CHAINED_RECEIVE_EXPEDITED
Register the given ClientEventChainedReceiveExpedited handler to be called when an expedited receive comes in on an endpoint-to-endpoint connection from the remote node and the client will be given read-only access to the buffered full TSDU until it calls TdiReturnChainedReceives.
TDI_EVENT_RECEIVE_DATAGRAM
Register the given ClientEventReceiveDatagram handler to be called when a datagram comes in from a remote node and the client will be given the opportunity to copy all or part of the buffered TSDU.
TDI_EVENT_CHAINED_RECEIVE_DATAGRAM
Register the given ClientEventChainedReceiveDatagram handler to be called when a datagram comes in from a remote node and the client will be given read-only access to the buffered full TSDU until it calls TdiReturnChainedReceives.
TDI_EVENT_ERROR
Register the given ClientEventError handler to be called if an error occurs in an underlying driver such that network I/O on this local-node address has become unreliable or impossible.
TDI_EVENT_SEND_POSSIBLE
Register the given ClientEventSendPossible handler to be called when the underlying transport, which previously returned a submitted send request with STATUS_DEVICE_NOT_READY, again has internal buffer space available into which it can copy the resubmitted client-supplied data to be sent.

In addition to the preceding system-defined events, a transport can extend this interface to support transport-specific notifications of events to its clients. Such a transport must define TDI_EVENT_XXX codes in which the most significant bit is set.

InEventHandler
Specifies the entry point of the ClientEventXxx handler being registered. The client can set this parameter to NULL to cancel notifications to a previously registered event handler.
InEventContext
Points to caller-determined context to be passed in to the ClientEventXxx routine when it is called by the transport. This parameter must be NULL if InEventHandler is NULL.

Comments

When its client first opens a file object that represents an address, the underlying transport initializes all event handlers to NULL.

TdiBuildSetEventHandler sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_SET_EVENT_HANDLER as the MinorFunction codes in the transport's I/O stack location of the given IRP.

With each IRP set up by TdiBuildSetEventHandler and submitted to the underlying transport with IoCallDriver, the client registers one event handler for a given event type that can occur on a particular transport address. Indications from lower level NDIS drivers that are forwarded by the transport to ClientEventXxx routines usually occur at IRQL DISPATCH_LEVEL. For these client-supplied event handlers, the caller should allocate the area at InEventContext from nonpaged pool.

A client can register its ClientEvent(Chained)ReceiveDatagram and ClientEventError handlers as soon as it has opened a file object representing an address with a successful call to ZwCreateFile. It can register its ClientEventConnect handler as soon as it has opened file objects representing an address and a connection endpoint and made a successful associate-address request to the underlying transport.

For some InEventType values, the address represented by the given file object should be associated with a connection endpoint by the client before it registers certain ClientEventXxx handlers. In particular, a client should associate a connection endpoint with a local-node address before it registers its ClientEvent(Chained)Receive(Expedited), ClientEventDisconnect, and/or ClientEventSendPossible handlers for network transfers that are made on a connection-specific basis. Unless its transport supports delayed-connection acceptances, the client should assume that an endpoint-to-endpoint connection, already associated with the open file object representing the address, is active when it makes the set-event-handler request to register any of these event handlers.

Only clients of transports that buffer send data internally can register a ClientEventSendPossible handler.

In general, a TDI transport can indicate an event to a client while an event of the same kind is in progress. In other words, ClientEventXxx handlers are re-entrant. The transport also can call ClientEvent(Chained)Receive(Expedited) or ClientEvent(Chained)Receive(Datagram) before the client's set-event-handler IRP to register one of these routines is fully completed. Consequently, the client must be prepared to handle receives as soon as it requests the registration of a ClientEvent(Chained)Receive(Xxx) handler.

Calls to registered ClientEvent(Chained)Receive(Xxx) handlers are disabled whenever the client submits a receive or receive-datagram IRP set up with TdiBuildReceive or TdiBuildReceiveDatagram to the underlying transport. After it is called with such an IRP, the underlying transport carries out all operations necessary to complete that IRP before it resumes calling registered ClientEvent(Chained)Receive(Xxx) routine(s) with incoming receive indications.

A client can disable any registered event handler by making another set-event-handler request with the same InEventType but specifying NULL as the InEventHandler and InEventContext parameters to TdiBuildSetEventHandler.

See Also

TdiBuildAccept, TdiBuildAssociateAddress, TdiBuildConnect, TdiBuildDisconnect, TdiBuildListen, TdiBuildInternalDeviceControlIrp, TdiBuildReceive, TdiBuildReceiveDatagram, TDI_SET_EVENT_HANDLER, TdiReturnChainedReceives


TdiBuildSetInformation

VOID
TdiBuildSetInformation (
 IN PIRP Irp,
 IN PDEVICE_OBJECT DevObj,
 IN PFILE_OBJECT FileObj,
 IN PVOID CompRoutine,
 IN PVOID Contxt,
 IN UINT SType,
 IN PMDL MdlAddr
 );

TdiBuildSetInformation sets up an internal device control IRP for a TDI_SET_INFORMATION request to the underlying transport in which the local-node client has already opened a file object representing an address, connection endpoint, or control channel.

Parameters

Irp
Points to a client-supplied IRP, usually one allocated with TdiBuildInternalDeviceControlIrp.
DevObj
Points to the device object created by the next lower TDI transport driver.
FileObj
Points to a file object representing an address, connection endpoint, or control channel. This file object must be appropriate to the given SType.
CompRoutine
Specifies the entry point of a client-supplied IoCompletion routine or NULL. The I/O Manager calls this routine when the given IRP is completed, unless the client sets this parameter to NULL.
Contxt
Points to a client-determined context. This client-supplied pointer is passed in to the IoCompletion routine when it is called with the completed IRP. Contxt should be NULL if CompRoutine is NULL.
SType
Specifies the type of information the client wants to be set. This parameter can be transport-defined or one of the following system-defined values:
TDI_QUERY_ADDRESS_INFO
Set the supplied information for an address. The buffer mapped at MdlAddr is formatted as a TDI_ADDRESS_INFO structure.
TDI_QUERY_CONNECTION_INFO
Set the supplied information for a connection endpoint. The buffer mapped at MdlAddr is formatted as a TDI_CONNECTION_INFO structure.
TDI_QUERY_PROVIDER_INFO
Set the supplied information for a control channel. The buffer mapped at MdlAddr is formatted as a TDI_PROVIDER_INFO structure.
TDI_QUERY_PROVIDER_STATISTICS
Set the supplied information for a control channel. The buffer mapped at MdlAddr is formatted as a TDI_PROVIDER_STATISTICS structure.

The underlying transport can support additional types of set-information requests at the discretion of the driver writer. Such a transport must define TDI_QUERY_XXX codes in which the most significant bit is set.

MdlAddr
Points to an MDL mapping the client-supplied buffer containing the data to be set. The format and contents of this buffer depend on the given SType.

Comments

TdiBuildSetInformation sets IRP_MJ_INTERNAL_DEVICE_CONTROL as the MajorFunction and TDI_SET_INFORMATION as the MinorFunction codes in the transport's I/O stack location of the given IRP.

A client can set information about an address, a connection, or a control channel, depending on the SType it passes to TdiBuildSetInformation. The given file object must be appropriate to the given SType or the transport will fail the request.

The underlying transport also can refuse to reset anything for which the client supplies an inappropriate value in the buffer at MdlAddr. For example, a client cannot force the underlying transport to extend its MaxSendSize or MaxDatagramSize beyond the transport-determined limit by issuing a TDI_QUERY_PROVIDER_INFO set-information request.

See Also

TDI_ADDRESS_INFO, TdiBuildInternalDeviceControlIrp, TdiBuildQueryInformation, TDI_CONNECTION_INFO, TDI_PROVIDER_INFO, TDI_PROVIDER_STATISTICS, TDI_SET_INFORMATION


Chapter 5 TDI Client Event Handlers

This describes the system-defined ClientEventXxx callback routines that can be implemented in kernel-mode clients of TDI transports. Such a client registers each such callback by setting up an IRP for the underlying transport driver with TdiBuildSetEventHandler, to contain the IOCTL TDI_SET_EVENT_HANDLER code, and calling IoCallDriver with the IRP.

The names of these client-supplied callback routines in this reference are metanames, chosen to describe their basic functionality. Because any client passes only the entry points for these event handlers in calls to TdiBuildSetEventHandler, their names actually can be anything the writer of each client chooses.

The following ClientEventXxx routine references are in alphabetic order.


ClientEventConnect

NTSTATUS
ClientEventConnect (
 IN PVOID TdiEventContext,
 IN LONG RemoteAddressLength,
 IN PVOID RemoteAddress,
 IN LONG UserDataLength,
 IN PVOID UserData,
 IN LONG OptionsLength,
 IN PVOID Options,
 OUT CONNECTION_CONTEXT *ConnectionContext,
 OUT PIRP *AcceptIrp
 );

ClientEventConnect is an event handler the TDI driver calls in response to an incoming endpoint-to-endpoint connection offer from a remote node.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventConnect was registered with the underlying transport.
RemoteAddressLength
Specifies the size in bytes of the buffer at RemoteAddress.
RemoteAddress
Points to a buffer containing the transport address of the remote-node client that offered this connection.
UserDataLength
Specifies the size in bytes of the buffer at UserData. Zero indicates either that no data is available or that the underlying transport(s) do not support sending data with an offered connection. UserData is NULL if this parameter is zero.
UserData
Points to a buffer containing connect data provided by the remote-node client with its connection offer. This parameter is NULL if UserDataLength is zero.
OptionsLength
Specifies the size in bytes of the buffer at Options. Zero implies no option string. Options is NULL if this parameter is zero.
Options
Points to a buffer containing options for the connection in a transport-specific format. This parameter is NULL if OptionsLength is zero.
ConnectionContext
Points to a caller-supplied variable in which ClientEventConnect returns a pointer to the client's context area in which it maintains client-determined state for this connection endpoint. Usually, the specified address is identical to the value that the client set for the value at EaBuffer when it originally called ZwCreateFile to open the local connection endpoint. If ClientEventConnect rejects the offered connection, the value it returns is NULL.
AcceptIrp
Points to a caller-supplied variable in which ClientEventConnect returns a pointer to an IRP, set up by ClientEventConnect with TdiBuildAccept. The transport completes this IRP if it is supplied as it does any normal accept request. If ClientEventConnect rejects the connection offer, this parameter is NULL.

Return Value

ClientEventConnect can return one of the the following:

STATUS_MORE_PROCESSING_REQUIRED
ClientEventConnect is accepting the offered connection and has supplied an accept request at AcceptIrp.

Some transports assume the connection offer is being rejected if ClientEventConnect returns anything other than this value.

STATUS_CONNECTION_REFUSED
ClientEventConnect is rejecting the offered connection.
STATUS_INSUFFICIENT_RESOURCES
ClientEventConnect could not allocate sufficient resources to accept the offered connection. The transport will send a rejection to the remote node.

Comments

This call notifies the local-node client of an incoming connection offer from a remote-node peer process. The local-node ClientEventConnect handler inspects the transport-supplied address information identifying the remote-node client and any data at UserData and/or Options to determine whether to accept this connection offer.

To accept an offered endpoint-to-endpoint connection, ClientEventConnect must set up the buffer at ConnectionContext, together with a TDI_ACCEPT request, and, then, return STATUS_MORE_PROCESSING_REQUIRED. The local-node transport notifies the remote node of the acceptance, transmitting any connect data that its client supplied in the accept IRP, and completes the TDI_ACCEPT request normally.

A client should preallocate the IRP it will use to accept a connection offer before its ClientEventConnect handler is called. In general, any client should preallocate a few IRPs for the TDI_XXX IOCTL requests it might subsequently submit to the underlying transport while running at IRQL DISPATCH_LEVEL. Client calls to TdiBuildInternalDeviceControlIrp can occur at IRQL PASSIVE_LEVEL.

When ClientEventConnect returns control with STATUS_MORE_PROCESSING_REQUIRED, the underlying transport can indicate incoming receives on the endpoint-to-endpoint connection to the client's registered ClientEvent(Chained)Receive(Expedited) handler(s), even before the transport completes the given accept IRP back to its local-node client. A client must be prepared to accept received data as soon as it agrees to accept an endpoint-to-endpoint connection offer from a remote-node peer.

To reject an offered endpoint-to-endpoint connection, ClientEventConnect can simply return STATUS_CONNECTION_REFUSED. For this return status, the local-node transport notifies the remote-node transport that the connection offer has been rejected.

If the client either does not have or cannot allocate sufficient resources to accept the connection offer, ClientEventConnect returns STATUS_INSUFFICIENT_RESOURCES. For this return status, the TDI transport either sends a rejection of the remote-node client's connection offer to the corresponding transport on the remote node or simply drops the connection offer.

By default, ClientEventConnect runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, TDI_ACCEPT, TdiBuildAccept, TdiBuildInternalDeviceControlIrp, TdiBuildListen, TdiBuildSetEventHandler, TdiDispatchCreate, TA_ADDRESS


ClientEventChainedReceive

NTSTATUS
ClientEventChainedReceive(
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG ReceiveLength,
IN ULONG StartingOffset,
IN PMDL Tsdu,
IN PVOID TsduDescriptor
);

ClientEventChainedReceive is an event handler that the underlying TDI transport calls in response to an incoming receive from a remote node with which the client has an established endpoint-to-endpoint connection.

The transport calls this handler, rather than ClientEventReceive, when it is indicating a full TSDU and the client can be given direct read-only access to the buffered TSDU until the client has consumed the data.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventChainedReceive was registered with the underlying transport.
ConnectionContext
Points to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.
ReceiveFlags
Specifies the nature of the receive indication as a combination (ORed) of the following flags:
TDI_RECEIVE_NORMAL
The buffer mapped at Tsdu contains normal data received from the client's remote-node peer. This flag and TDI_RECEIVE_EXPEDITED are mutually exclusive.
TDI_RECEIVE_EXPEDITED
The buffer mapped at Tsdu contains expedited data received from the client's remote-node peer. This flag can be set only if the client did not register either or both ClientEventChainedReceiveExpedited and/or ClientEventReceiveExpedited handler(s) and if the underlying transports support expedited transfers.
TDI_RECEIVE_ENTIRE_MESSAGE
The buffer mapped at Tsdu contains a full TSDU, and the client retains read-only access to this buffer until the client consumes the indicated data if ClientEventChainedReceive returns STATUS_PENDING. This flag is always set when ClientEventChainedReceive is called.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The receive is being indicated at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing this indication. Some transports never set this flag, whatever the current IRQL, when making receive-event notifications.
ReceiveLength
Specifies the number of bytes of client data in the buffer mapped at Tsdu.
StartingOffset
Specifies the byte offset at which the client data starts within the buffer mapped at Tsdu.
Tsdu
Points to an MDL, possibly the initial MDL in a chain, mapping the buffer containing the received TSDU.
TsduDescriptor
Points to a descriptor for the received TSDU. The client must call TdiReturnChainedReceives with this pointer subsequently if it returns STATUS_PENDING for this receive indication. This pointer should be treated as a handle to an opaque variable, to be used by the client only as a parameter to TdiReturnChainedReceives if ClientEventChainedReceive returns STATUS_PENDING.

Return Value

ClientEventChainedReceive can return one of the following:

STATUS_SUCCESS
Indicates the client consumed all the data in the given TSDU and is returning ownership of the buffer (and TsduDescriptor) immediately.
STATUS_PENDING
Indicates the client is retaining ownership of the buffer containing the given TSDU until it calls TdiReturnChainedPackets with the given TsduDescriptor.
STATUS_DATA_NOT_ACCEPTED
Indicates the client is not interested in the TSDU.

If the underlying transport buffers receives internally, the client might retrieve the data with a TDI_RECEIVE request, unless the transport discards buffered data indicated to ClientEventChainedReceive(Xxx) handlers.

Comments

A call to ClientEventChainedReceive gives the client read-only access to the indicated TSDU for the range within the buffer specified by the input StartingOffset and ReceiveLength. If the indicated data is of interest to the client, ClientEventChainedReceive either copies the indicated range of TSDU data into a client-allocated internal buffer and returns STATUS_SUCCESS immediately or retains control of the buffer by returning STATUS_PENDING. If it returns STATUS_PENDING, the client must call TdiReturnChainedReceives subsequently with the input TsduDescriptor to relinquish control of the buffer after the client has consumed the data.

In general, such a call to TdiReturnChainedReceives should occur as quickly as possible. Holding on to a buffer passed to ClientEventChainedReceive for any extended period constrains I/O throughput in underlying driver(s), because the driver that allocated the buffer cannot reuse this resource for subsequent receive indications until TdiReturnChainedReceives is called.

Because calls to ClientEventChainedReceive always indicate the availability of a full TSDU, the client never has to set up TDI_RECEIVE requests for such an indication, as the corresponding ClientEventReceive handler sometimes must to obtain a full TSDU. Consequently, receive indications made to ClientEventChainedReceive increase network I/O throughput and performance by decreasing call overhead for the client, for its underlying transport, and for the system overall.

If the client has registered only ClientEventChainedReceive and ClientEventReceive handlers for the endpoint-to-endpoint connection, ClientEventChainedReceive can determine whether the TSDU is normal or expedited data by checking the ReceiveFlags, assuming both transports support expedited sends and receives. A transport never calls the corresponding ClientEventReceive handler with the same indication it makes to ClientEventChainedReceive.

The transport does not call ClientEvent(Chained)Receive while the client has an outstanding normal receive request or has rejected previously indicated data for a particular incoming normal receive until that receive is done. However, a transport that supports expedited data can call ClientEvent(Chained)Receive(Expedited) in the process of indicating a normal TSDU if an expedited TSDU comes in from the remote-node peer.

When ClientEventChainedReceive returns control with either STATUS_SUCCESS or STATUS_DATA_NOT_ACCEPTED, the underlying transport assumes the client is done with this receive indication.

By default, ClientEventChainedReceive runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventChainedReceiveExpedited, ClientEventReceive, TdiBuildReceive, TdiBuildSetEventHandler, TdiReturnChainedReceives


ClientEventChainedReceiveDatagram

NTSTATUS
ClientEventChainedReceiveDatagram(
IN PVOID TdiEventContext,
IN LONG SourceAddressLength,
IN PVOID SourceAddress,
IN LONG OptionsLength,
IN PVOID Options,
IN ULONG ReceiveDatagramFlags,
IN ULONG ReceiveDatagramLength,
IN ULONG StartingOffset,
IN PMDL Tsdu,
IN PVOID TsduDescriptor
);

ClientEventChainedReceiveDatagram is an event handler that the underlying TDI transport calls in response to an incoming receive from a remote node that was directed to a local-node transport address opened by the client.

The transport calls this handler, rather than ClientEventReceiveDatagram, when it is indicating a full TSDU and the client can be given direct read-only access to the buffered TSDU until the client has consumed the data.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventChainedReceiveDatagram was registered with the underlying transport.
SourceAddressLength
Specifies the size in bytes of the buffer at SourceAddress.
SourceAddress
Points to a buffer specifying the remote-node transport address from which the datagram was sent.
OptionsLength
Specifies the size in bytes of the buffer at Options. Zero implies no option string is available. Options is NULL if this parameter is zero.
Options
Points to a buffer specifying a string of transport-specific options sent with the datagram. This pointer is NULL if OptionsLength is zero.
ReceiveDatagramFlags
Specifies the nature of the receive-datagram indication as a combination (ORed) of the following flags:
TDI_RECEIVE_ENTIRE_MESSAGE
The buffer mapped at Tsdu contains a full TSDU, and the client retains read-only access to this buffer until the client consumes the indicated data if ClientEventChainedReceiveDatagram returns STATUS_PENDING. This flag is always set when ClientEventChainedReceiveDatagram is called.
TDI_RECEIVE_BROADCAST
The receive was broadcast.
TDI_RECEIVE_MULTICAST
The receive was multicast.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The receive is being indicated at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing this indication. Some transports never set this flag, whatever the current IRQL, when making receive-event notifications.
ReceiveLength
Specifies the number of bytes of client data in the buffer mapped at Tsdu.
StartingOffset
Specifies the byte offset at which the client data starts within the buffer mapped at Tsdu.
Tsdu
Points to an MDL, possibly the initial MDL in a chain, mapping the buffer containing the received TSDU.
TsduDescriptor
Points to a descriptor for the received TSDU. The client must call TdiReturnChainedReceives with this pointer subsequently if ClientEventChainedReceiveDatagram returns STATUS_PENDING for this receive indication. This pointer should be treated as a handle to an opaque variable, to be used by the client only as a parameter to TdiReturnChainedReceives if ClientEventChainedReceiveDatagram returns STATUS_PENDING.

Return Value

ClientEventChainedReceiveDatagram can return one of the following:

STATUS_SUCCESS
Indicates the client consumed all the data in the given TSDU and is returning ownership of the buffer to the underlying driver immediately.
STATUS_PENDING
Indicates the client is retaining ownership of the buffer containing the given TSDU until it calls TdiReturnChainedPackets with the given TsduDescriptor.
STATUS_DATA_NOT_ACCEPTED
Indicates the client is not interested in the TSDU.

Comments

When a datagram arrives from a remote node, the transport calls the registered ClientEvent(Chained)ReceiveDatagram handlers for each client with an open file object representing an address that matches the destination address of the datagram. Because a datagram is not associated with an established connection, the TDI driver passes the transport address of the remote-node sender at SourceAddress to the receiving client along with the datagram.

A call to ClientEventChainedReceiveDatagram gives the client read-only access to the indicated TSDU for the range within the buffer specified by the input StartingOffset and ReceiveLength. If the indicated data is of interest to the client, ClientEventChainedReceiveDatagram either copies the indicated range of TSDU data into a client-allocated internal buffer and returns STATUS_SUCCESS immediately or retains control of the buffer by returning STATUS_PENDING. If it returns STATUS_PENDING, the client must call TdiReturnChainedReceives subsequently with the input TsduDescriptor to relinquish control of the buffer after the client has consumed the data.

In general, such a call to TdiReturnChainedReceives should occur as quickly as possible. Holding on to a buffer passed to ClientEventChainedReceiveDatagram for any extended period constrains I/O throughput in underlying driver(s), because the driver that allocated the buffer cannot reuse this resource for subsequent receive indications until TdiReturnChainedReceives is called.

Because calls to ClientEventChainedReceiveDatagram always indicate the availability of a full TSDU, the client never has to set up TDI_RECEIVE_DATAGRAM requests for such an indication, as the corresponding ClientEventReceiveDatagram handler sometimes must to obtain a full TSDU. Consequently, receive indications made to ClientEventChainedReceiveDatagram increase network I/O throughput and performance by decreasing call overhead for the client, for its underlying transport, and for the system overall. A transport never calls the corresponding ClientEventReceiveDatagram handler with the same indication it makes to ClientEventChainedReceiveDatagram.

When ClientEventChainedReceiveDatagram returns control with either STATUS_SUCCESS or STATUS_DATA_NOT_ACCEPTED, the underlying transport assumes the client is done with this receive indication.

The underlying TDI transport does not call ClientEventChainedReceiveDatagram if the client has an outstanding receive or receive-datagram request on the local transport address that is the target of an incoming datagram.

To receive indications for broadcast datagrams that the TDI driver receives on the network, the client must open the broadcast address of its underlying transport and register its ClientEvent(Chained)ReceiveDatagram handler(s).

By default, ClientEventChainedReceiveDatagram runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventReceiveDatagram, TdiBuildSetEventHandler, TdiReturnChainedReceives, TA_ADDRESS


ClientEventChainedReceiveExpedited

NTSTATUS
ClientEventChainedReceiveExpedited(
IN PVOID TdiEventContext,
IN CONNECTION_CONTEXT ConnectionContext,
IN ULONG ReceiveFlags,
IN ULONG ReceiveLength,
IN ULONG StartingOffset,
IN PMDL Tsdu,
IN PVOID TsduDescriptor
);

ClientEventChainedReceiveExpedited is an event handler that the underlying TDI transport calls in response to an incoming expedited receive from a remote node with which the client has an established endpoint-to-endpoint connection.

The transport calls this handler, rather than ClientEventReceiveExpedited, when it is indicating a full TSDU and the client can be given direct read-only access to the buffered TSDU until the client has consumed the data.

Parameters

TdiEventContext
Points to the context the client provided in the IRP it previously set up with TdiBuildSetEventHandler to register ClientEventChainedReceiveExpedited.
ConnectionContext
Points to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.
ReceiveFlags
Specifies the nature of the receive indication as a combination (ORed) of the following flags:
TDI_RECEIVE_EXPEDITED
The buffer mapped at Tsdu contains expedited data received from the client's remote-node peer. This flag is always set when ClientEventChainedReceiveExpedited is called.
TDI_RECEIVE_ENTIRE_MESSAGE
The buffer mapped at Tsdu contains a full TSDU, and the client retains read-only access to this buffer until the client consumes the indicated data if ClientEventChainedReceiveExpedited returns STATUS_PENDING. This flag is always set when ClientEventChainedReceiveExpedited is called.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The receive is being indicated at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing this indication. Some transports never set this flag, whatever the current IRQL, when making receive-event notifications.
ReceiveLength
Specifies the number of bytes of client data in the buffer mapped at Tsdu.
StartingOffset
Specifies the byte offset at which the client data starts within the buffer mapped at Tsdu.
Tsdu
Points to an MDL, possibly the initial MDL in a chain, mapping the buffer containing the received TSDU.
TsduDescriptor
Points to a descriptor for the received TSDU. The client must call TdiReturnChainedReceives with this pointer subsequently if ClientEventChainedReceiveExpedited returns STATUS_PENDING for this receive indication. This pointer should be treated as a handle to an opaque variable, to be used by the client only as a parameter to TdiReturnChainedReceives if ClientEventChainedReceiveExpedited returns STATUS_PENDING.

Return Value

ClientEventChainedReceiveExpedited can return one of the following:

STATUS_SUCCESS
Indicates the client consumed all the data in the given TSDU and is returning ownership of the buffer immediately.
STATUS_PENDING
Indicates the client is retaining ownership of the buffer containing the given TSDU until it calls TdiReturnChainedPackets with the given TsduDescriptor.
STATUS_DATA_NOT_ACCEPTED
Indicates the client is not interested in the TSDU.

If the underlying transport buffers receives internally, the client might retrieve the data with a TDI_RECEIVE request, unless the transport discards buffered data indicated to ClientEventChainedReceive(Xxx) handlers.

Comments

A call to ClientEventChainedReceiveExpedited gives the client read-only access to the indicated TSDU for the range within the buffer specified by the input StartingOffset and ReceiveLength. If the indicated data is of interest to the client, ClientEventChainedReceiveExpedited either copies the indicated range of TSDU data into a client-allocated internal buffer and returns STATUS_SUCCESS immediately or retains control of the buffer by returning STATUS_PENDING. If it returns STATUS_PENDING, the client must call TdiReturnChainedReceives subsequently with the input TsduDescriptor to relinquish control of the buffer after the client has consumed the data.

In general, such a call to TdiReturnChainedReceives should occur as quickly as possible. Holding on to a buffer passed to ClientEventChainedReceiveExpedited for any extended period constrains I/O throughput in underlying driver(s), because the driver that allocated the buffer cannot reuse this resource for subsequent receive indications until TdiReturnChainedReceives is called.

Because calls to ClientEventChainedReceiveExpedited always indicate the availability of a full TSDU, the client never has to set up TDI_RECEIVE requests for such an indication, as the corresponding ClientEventReceiveExpedited handler sometimes must to obtain a full TSDU. Consequently, receive indications made to ClientEventChainedReceiveExpedited increase network I/O throughput and performance by decreasing call overhead for the client, for its underlying transport, and for the system overall. A transport never calls the corresponding ClientEventReceiveExpedited handler with the same indication it makes to ClientEventChainedReceiveExpedited.

When ClientEventChainedReceiveExpedited returns control with either STATUS_SUCCESS or STATUS_DATA_NOT_ACCEPTED, the underlying transport assumes the client is done with this receive indication.

By default, ClientEventChainedReceiveExpedited runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventChainedReceive, ClientEventReceiveExpedited, TdiBuildSetEventHandler, TdiReturnChainedReceives


ClientEventDisconnect

NTSTATUS
ClientEventDisconnect (
 IN PVOID TdiEventContext,
 IN CONNECTION_CONTEXT ConnectionContext,
 IN LONG DisconnectDataLength,
 IN PVOID DisconnectData,
 IN LONG DisconnectInformationLength,
 IN PVOID DisconnectInformation,
 IN ULONG DisconnectFlags
 );

ClientEventDisconnect is an event handler that the underlying TDI transport calls in response to an incoming disconnection notification from a remote node.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventDisconnect was registered with the underlying transport.
ConnectionContext
Points to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.
DisconnectDataLength
Specifies the size in bytes of the buffer at DisconnectData. Zero indicates that no disconnect data was transmitted from the remote node.
DisconnectData
Points to a buffer containing disconnect data received from the remote node by the underlying transport. This pointer is NULL if DisconnectDataLength is zero.
DisconnectInformationLength
Specifies the size in bytes of the buffer at DisconnectInformation. Zero indicates that no additional disconnect information was transmitted from the remote node.
DisconnectInformation
Points to a buffer containing any additional transport-specific disconnect information. This pointer is NULL if DisconnectInformationLength is zero.
DisconnectFlags
Specifies the nature of the disconnection operation as zero or as one of the following flags:
TDI_DISCONNECT_ABORT
The transport is closing the endpoint-to-endpoint connection immediately without completing any pending operations on the connection. In effect, this flag is equivalent to a DisconnectFlags of zero.
TDI_DISCONNECT_RELEASE
The transport is engaged in a controlled disconnect operation with the remote node.

Return Value

ClientEventDisconnect can return STATUS_SUCCESS.

Comments

A call to ClientEventDisconnect notifies the local-node client that its remote-node peer is closing their established endpoint-to-endpoint connection. Depending on the input DisconnectFlags, the disconnect can be either controlled or uncontrolled. ClientEventDisconnect is the last event handler the TDI driver calls on an endpoint-to-endpoint connection.

If TDI_DISCONNECT_RELEASE is set in the DisconnectFlags, both transports are coordinating a controlled disconnection. (See TDI_DISCONNECT for details.)

A call to ClientEventDisconnect notifies the local-node client that endpoint-to-endpoint activity has ceased or is ceasing on the connection endpoint opened by that client. However, the connection endpoint is still valid after the endpoint-to-endpoint connection has been broken. The local-node client can reuse its open connection endpoint in a subsequent operation after ClientEventDisconnect returns control.

ClientEventDisconnect can be called at IRQL DISPATCH_LEVEL.

See Also

TdiBuildDisconnect, TdiBuildSetEventHandler, TDI_DISCONNECT


ClientEventError

NTSTATUS
ClientEventError (
 IN PVOID TdiEventContext,
 IN NTSTATUS Status
 );

ClientEventError is an event handler that the underlying TDI transport calls in response to an error, either in the transport itself or in a still lower network driver, that makes I/O on a particular local transport address unreliable or impossible.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventError was registered with the underlying transport.
Status
Specifies an NTSTATUS value that provides information about the condition that caused this call to ClientEventError.

Return Value

ClientEventError can return STATUS_SUCCESS.

Comments

A call to ClientEventError notifies the local-node client of an error condition in the underlying TDI transport, in one of the lower protocol layers of the transport stack, or in the underlying NDIS NIC driver to which the transport stack is bound. If such an error occurs, the TDI transport calls all currently registered ClientEventError handlers for each client on every open address that depends on the underlying driver that encountered the error condition.

In general, a call to ClientEventError indicates a catastrophic failure. After this call, the client should cease to use, and in some cases close, the file object representing the transport address associated with this notification. If this address is associated with an endpoint-to-endpoint connection, ClientEventError can assume the connection is broken as well.

Consequently, a ClientEventError handler usually cleans up any state it was maintaining for the address and for all connection endpoints it has associated with that address. That is, the client notifies higher level components for which it is handling pending operations on the address of the failure. Then, the client releases all resources it previously allocated that are relevant to those operations.

ClientEventError can be called at IRQL DISPATCH_LEVEL.

See Also

TdiBuildSetEventHandler


ClientEventReceive

NTSTATUS
ClientEventReceive (
 IN PVOID TdiEventContext,
 IN CONNECTION_CONTEXT ConnectionContext,
 IN ULONG ReceiveFlags,
 IN ULONG BytesIndicated,
 IN ULONG BytesAvailable,
 OUT ULONG *BytesTaken,
 IN PVOID Tsdu,
 OUT PIRP *IoRequestPacket
 );

ClientEventReceive is an event handler that the underlying TDI transport calls in response to an incoming receive from a remote node with which the client has an established endpoint-to-endpoint connection. Usually, this is a normal TSDU unless the client has not registered a ClientEventReceiveExpedited handler.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventReceive was registered with the underlying transport.
ConnectionContext
Points to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.
ReceiveFlags
Specifies the nature of the receive indication as a combination (ORed) of one or more of the following flags:
TDI_RECEIVE_NORMAL
The buffer mapped at Tsdu contains normal data received from the client's remote-node peer. This flag and TDI_RECEIVE_EXPEDITED are mutually exclusive.
TDI_RECEIVE_EXPEDITED
The buffer mapped at Tsdu contains expedited data received from the client's remote-node peer. This flag can be set only if the client did not register a ClientEventReceiveExpedited and/or ClientEventChainedReceiveExpedited handler.
TDI_RECEIVE_ENTIRE_MESSAGE
The buffer mapped at Tsdu contains a full TSDU. The client should return control as quickly as possible after copying the indicated data into an internal buffer if it accepts the TSDU.

If this flag is clear, ClientEventReceive must check the BytesIndicated and BytesAvailable parameters to determine how much of the TSDU has been provided. Although legacy TDI transports continue to set the TDI_RECEIVE_PARTIAL flag, newer transports leave clear the TDI_RECEIVE_ENTIRE_MESSAGE flag to indicate partial TSDUs to their clients.

TDI_RECEIVE_COPY_LOOKAHEAD
Unless TDI_RECEIVE_ENTIRE_MESSAGE is set, BytesIndicated is something less than BytesAvailable, and ClientEventReceive should copy the number of indicated bytes into an internal buffer, set the variable at BytesTaken, and return control. The client will subsequently submit one or more TDI_RECEIVE requests to get the remainder of the TSDU or ClientEventReceive will be called again to copy the remainder of the TSDU.
TDI_RECEIVE_FRAGMENT
The buffer mapped at Tsdu contains a fragmented TSDU.
TDI_RECEIVE_PEEK
The transport has buffered some receive data internally, but not yet the full TSDU. The client can examine the buffer mapped at Tsdu to decide whether to submit a TDI_RECEIVE request for the TSDU.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The receive is being indicated at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing this indication. Some transports never set this flag, whatever the current IRQL, when making receive-event notifications.
BytesIndicated
Specifies the number of bytes of message-mode or stream-mode data in the buffer mapped at Tsdu. This parameter is always less than or equal to the value of BytesAvailable. A TDI transport provides at least 128 bytes of data in a receive indication to its client, unless the received message or stream segment is less than 128 bytes in length. If BytesAvailable is greater than BytesIndicated, the transport has received data that it does not make available when it calls ClientEventReceive.
BytesAvailable
Specifies the total number of bytes in the received TSDU.
BytesTaken
Points to a caller-supplied variable in which ClientEventReceive returns the number of bytes of data it copies from the TSDU.
Tsdu
Points to an MDL, possibly the initial MDL in a chain, mapping the buffer containing the received TSDU data.
IoRequestPacket
Points to a variable in which ClientEventReceive returns a pointer to a caller-allocated IRP that was set up with TdiBuildReceive. If this variable is set to NULL, the client will not be making a receive request for the remainder of the TSDU.

Return Value

ClientEventReceive can return one of the following:

STATUS_SUCCESS
Indicates the client copied all the data in the given TSDU.
STATUS_MORE_PROCESSING_REQUIRED
Indicates the client has supplied an IRP requesting the remainder of the TSDU, usually after copying BytesIndicated into an internal buffer.
STATUS_DATA_NOT_ACCEPTED
Indicates this client is not interested in the TSDU.

Comments

ClientEventReceive accepts or rejects a TSDU that its underlying TDI driver has received on an established endpoint-to-endpoint connection. The TSDU consists of stream-mode or message-mode data that represents a segment of a data stream, an entire message, or a piece of a message. The transport has removed the transport-layer header from the TSDU before it calls ClientEventReceive.

ClientEventReceive copies normal data the TDI driver receives over the network. The transport does not call ClientEvent(Chained)Receive while the client has an outstanding normal receive request or has rejected previously indicated data for a particular incoming normal receive until that receive is done. However, a transport that supports expedited data can call ClientEvent(Chained)ReceiveExpedited in the process of indicating a normal TSDU if an expedited TSDU comes in from the remote-node peer.

A message-mode transport delimits messages using end-of-record marks. Such a transport can make a sequence of separate receive indications for each block of data terminated with an end-of-record mark within a particular received TSDU. Such a receive usually indicates that the remote-node client issued a sequence of sends to transmit the TSDU.

When ClientEventReceive is called, it can do one of the following:

  • Reject the indicated TSDU if it has no use for the data by returning STATUS_DATA_NOT_ACCEPTED

  • Copy all of the data into an internal buffer if TDI_RECEIVE_ENTIRE_MESSAGE is set in the ReceiveFlags and return STATUS_SUCCESS

  • If TDI_RECEIVE_ENTIRE_MESSAGE is clear (or TDI_RECEIVE_PARTIAL is set), copy some or all of the indicated data and return either of the following, depending on which operation it carries out:

    STATUS_MORE_PROCESSING_REQUIRED if the client is supplying a TDI_RECEIVE request at IoRequestPacket to obtain the remaining TSDU data

    STATUS_SUCCESS if the transport is expected to call ClientEventReceive again with the remaining TSDU data

When it has finished copying receive data, ClientEventReceive sets the variable at BytesTaken to the number of bytes of data accepted before it returns control.

If ClientEventReceive supplies a TDI_RECEIVE request at IoRequestPacket for the underlying driver, the transport retrieves the data that it has not yet delivered to the client, along with any new data that is available. Retrieval continues until the client-supplied buffer in the IRP is full or the transport encounters an end-of-record mark. The transport always completes the given IRP before it issues a subsequent call to ClientEvent(Chained)Receive handler for this client. In effect, a client processes each normal TSDU received to completion before the transport will indicate a subsequent normal receive to the client. This allows each client to synchronize receive data correctly.

If ClientEventReceive returns STATUS_DATA_NOT_ACCEPTED after the client has accepted some data from a TSDU, the TDI driver proceeds according to the amount of indicated data the client has accepted and the availability of additional data, as follows:

  • If the driver has passed all indicated data and no more data was available at the time of the indication, the driver calls ClientEventReceive again when it receives more data for the TSDU from the network.

  • If the driver has not passed all indicated data to the client, or if it has additional data available that it did not make available in the preceding call to ClientEventReceive, the transport assumes that its client has no internal buffer space for the data and makes no further indications until the client issues an explicit TDI_RECEIVE request.

    In these circumstances, a transport can optionally initiate protocol-flow-control action to prevent retransmissions of data from the remote-node transport until the local-node client makes the receive request.

A TDI transport is not required to correlate data reception from the network with TDI_EVENT_RECEIVE indications. If the driver provides internal buffering, it can acknowledge data at any time and make receive indications as necessary, for instance, when it has received a certain amount of data or when its internal buffers are nearly full. A buffering driver should retain any data that its client does not accept in an indication until that client notifies the transport that the client's receive request is satisfied. A transport that does not support internal data buffering can acknowledge received data after its client has accepted the data from ClientEventReceive or after the driver has transferred the received data into a client-supplied IRP.

A received TSDU can be any length up to a transport-specific limit, which the client can find by submitting a request set up with TdiBuildQueryInformation for the QType TDI_QUERY_CONNECTION_INFO and examining the value returned for ReceiveBufferSize if the transport buffers received data internally. However, ClientEventReceive does not necessarily receive an entire TSDU, or even the available portion of a TSDU, each time this handler is called. Each TDI transport can set its own limits on the minimum and maximum amounts of data it will indicate at each receive to its clients, as long as its minimum is at least 128 bytes. The client can find these limits by submitting another query-information request for the QType TDI_QUERY_PROVIDER_INFO and examining the values returned for MinimumLookaheadData and MaximumLookaheadData.

By default, ClientEventReceive runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventChainedReceive, ClientEventReceiveExpedited, ClientEventReceiveDatagram, TdiBuildInternalDeviceControlIrp, TdiBuildQueryInformation, TdiBuildReceive, TdiBuildSetEventHandler


ClientEventReceiveDatagram

NTSTATUS
ClientEventReceiveDatagram (
 IN PVOID TdiEventContext,
 IN LONG SourceAddressLength,
 IN PVOID SourceAddress,
 IN LONG OptionsLength,
 IN PVOID Options,
 IN ULONG ReceiveDatagramFlags,
 IN ULONG BytesIndicated,
 IN ULONG BytesAvailable,
 OUT ULONG * BytesTaken,
 IN PVOID Tsdu,
 OUT PIRP * IoRequestPacket
 );

ClientEventReceiveDatagram is an event handler that the underlying TDI transport calls in response to an incoming receive from a remote node that was directed to a local-node transport address that the client has opened.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventReceiveDatagram was registered with the underlying transport.
SourceAddressLength
Specifies the size in bytes of the buffer at SourceAddress.
SourceAddress
Points to a buffer specifying the remote-node transport address from which the datagram was sent.
OptionsLength
Specifies the size in bytes of the buffer at Options. Zero implies no option string is available. Options is NULL if this parameter is zero.
Options
Points to a buffer specifying a string of transport-specific options sent with the datagram. This pointer is NULL if OptionsLength is zero.
ReceiveDatagramFlags
Specifies the nature of the receive-datagram indication as a combination (ORed) of the following flags:
TDI_RECEIVE_ENTIRE_MESSAGE
The buffer mapped at Tsdu contains a full TSDU. The client should return control as quickly as possible after copying the indicated data into an internal buffer if it accepts the TSDU.

If this flag is clear (or if TDI_RECEIVE_PARTIAL is set by a legacy transport), ClientEventReceiveDatagram must check the BytesAvailable and BytesIndicated parameters to determine how much of the TSDU has been provided. Although legacy transports continue to set the TDI_RECEIVE_PARTIAL flag, newer transports leave clear the TDI_RECEIVE_ENTIRE_MESSAGE flag to indicate partial TSDUs to their clients.

TDI_RECEIVE_COPY_LOOKAHEAD
Unless TDI_RECEIVE_ENTIRE_MESSAGE is set, BytesAvailable is something less than BytesIndicated, and ClientEventReceiveDatagram should copy the number of bytes available into an internal buffer, set the variable at BytesTaken, and return control. The client will subsequently submit one or more TDI_RECEIVE_DATAGRAM requests to get the remainder of the TSDU.
TDI_RECEIVE_BROADCAST
The receive was broadcast.
TDI_RECEIVE_MULTICAST
The receive was multicast.
TDI_RECEIVE_PEEK
The transport has buffered some receive data internally, but not yet the full TSDU. The client can examine the buffer mapped at Tsdu to decide whether to submit a TDI_RECEIVE_DATAGRAM request for the TSDU.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The receive is being indicated at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing this indication. Some transports never set this flag, whatever the current IRQL, when making receive-event notifications.
BytesIndicated
Specifies the number of bytes of data in the buffer at Tsdu. This parameter is always less than or equal to the value of BytesAvailable. A TDI transport provides at least 128 bytes of data in a receive indication to its client, unless the received datagram is less than 128 bytes in length. If BytesAvailable is greater than BytesIndicated, the transport has received data that it does not present when it calls ClientEventReceiveDatagram.
BytesAvailable
Specifies the total number of bytes in the received TSDU.
BytesTaken
Points to a caller-supplied variable in which ClientEventReceiveDatagram returns the number of bytes of data it copied from the datagram.
Tsdu
Points to an MDL, possibly the initial MDL in a chain, mapping the buffer containing the received TSDU data.
IoRequestPacket
Points to a caller-supplied variable in which ClientEventReceiveDatagram returns a pointer to an IRP that it set up with TdiBuildReceiveDatagram. If this parameter is NULL, the client will not be making a receive request for the remainder of the TSDU.

Return Value

ClientEventReceiveDatagram can return one of the following:

STATUS_SUCCESS
Indicates the client copied all the data in the given TSDU and set the variable at BytesTaken to the value at BytesIndicated.
STATUS_MORE_PROCESSING_REQUIRED
Indicates the client has supplied an IRP requesting the remainder of the TSDU, usually after copying BytesIndicated into an internal buffer.
STATUS_DATA_NOT_ACCEPTED
Indicates this client is not interested in the TSDU.

Comments

When a datagram arrives from a remote node, the transport calls the registered ClientEvent(Chained)ReceiveDatagram handlers for each client with an open file object representing an address that matches the destination address of the datagram. Because a datagram is not associated with an established connection, the TDI driver passes the transport address of the remote-node sender at SourceAddress to the receiving client along with the datagram.

ClientEventReceiveDatagram accepts or rejects a TSDU that the TDI driver has received on a particular transport address that the client opened with a preceding call to ZwCreateFile. A datagram TSDU is discrete. Unlike a normal or expedited TSDU, it cannot be transmitted as a sequence of records. In other words, a full datagram is available to the TDI transport, although the driver might not pass all of it in a single call to ClientEventReceiveDatagram.

The transport has removed the transport-layer header from the TSDU before it calls ClientEventReceiveDatagram.

The driver does not call ClientEventReceiveDatagram if the client has an outstanding receive or receive-datagram request on the local transport address that is the target of an incoming datagram.

When ClientEventReceiveDatagram is called, it can do one of the following:

  • Reject the indicated TSDU if it has no use for the data by returning STATUS_DATA_NOT_ACCEPTED

  • Copy all of the data into an internal buffer if TDI_RECEIVE_ENTIRE_MESSAGE is set in the ReceiveFlags and return STATUS_SUCCESS

    When it has finished copying receive data, ClientEventReceiveDatagram sets the variable at BytesTaken to the number of bytes of data accepted before it returns control.

  • If TDI_RECEIVE_ENTIRE_MESSAGE is clear (or TDI_RECEIVE_PARTIAL is set), copy all of the indicated data, set up an IRP for a TDI_RECEIVE_DATAGRAM request, and return STATUS_MORE_PROCESSING_REQUIRED

    If the ClientEventReceiveDatagram issues a receive-datagram request, the transport retrieves any data that ClientEventReceiveDatagram did not receive, along with any new data the TDI transport has available. If the client does not make such a receive-datagram request, it loses this data.

Any TDI transport driver that supports internal buffering should retain a datagram during its clients' receive-datagram operations to satisfy any subsequent receive-datagram requests. The client of such a TDI driver can determine the total amount of buffer space it allocates for received datagrams by submitting a request, set up with TdiBuildQueryInformation for the QType TDI_QUERY_DATAGRAM_INFO or TDI_QUERY_MAX_DATAGRAM_INFO. Usually, such a transport's buffer space for datagrams is around four kilobytes.

To receive indications for broadcast datagrams that the TDI driver receives on the network, the client must open the local-node broadcast address of its underlying transport and register a ClientEvent(Chained)ReceiveDatagram handler on the address.

By default, ClientEventReceiveDatagram runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventChainedReceiveDatagram, ClientEventReceive, TdiBuildInternalDeviceControlIrp, TdiBuildReceiveDatagram, TdiBuildSetEventHandler, TA_ADDRESS


ClientEventReceiveExpedited

TDI_STATUS
ClientEventReceiveExpedited (
 IN PVOID TdiEventContext,
 IN CONNECTION_CONTEXT ConnectionContext,
 IN ULONG ReceiveFlags,
 IN ULONG BytesIndicated,
 IN ULONG BytesAvailable,
 OUT ULONG *BytesTaken,
 IN PVOID Tsdu,
 OUT PIRP *IoRequestPacket
 );

ClientEventReceiveExpedited is an event handler that the underlying TDI transport calls in response to an incoming expedited receive from a remote node with which the client has an established endpoint-to-endpoint connection.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventReceiveExpedited was registered with the underlying transport.
ConnectionContext
Points to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.
ReceiveFlags
Specifies the nature of the receive indication as a combination (ORed) of the following flags:
TDI_RECEIVE_EXPEDITED
The buffer mapped at Tsdu contains expedited data received from the client's remote-node peer. This flag is always set when ClientEventReceiveExpedited is called.
TDI_RECEIVE_ENTIRE_MESSAGE
The buffer mapped at Tsdu contains a full TSDU. ClientEventReceiveExpedited should return control as quickly as possible after copying the indicated data into an internal buffer if it accepts the TSDU.

If this flag is clear (or TDI_RECEIVE_PARTIAL is set by a legacy transport), ClientEventReceiveExpedited must check the BytesIndicated and BytesAvailable parameters to determine how much of the TSDU has been provided. Although legacy transports continue to set the TDI_RECEIVE_PARTIAL flag, newer transports leave clear the TDI_RECEIVE_ENTIRE_MESSAGE flag to indicate partial TSDUs to their clients.

TDI_RECEIVE_COPY_LOOKAHEAD
Unless TDI_RECEIVE_ENTIRE_MESSAGE is set, BytesIndicated is something less than BytesAvailable, and ClientEventReceiveExpedited should copy the number of indicated bytes into an internal buffer, set the variable at BytesTaken, and return control. The client will subsequently submit one or more TDI_RECEIVE requests to get the remainder of the TSDU or ClientEventReceiveExpedited will be called again to copy the remainder of the TSDU.
TDI_RECEIVE_FRAGMENT
The buffer mapped at Tsdu contains a fragmented TSDU.
TDI_RECEIVE_PEEK
The transport has buffered some receive data internally, but not yet the full TSDU. The client can examine the buffer mapped at Tsdu to decide whether to submit a TDI_RECEIVE request for the TSDU.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The receive is being indicated at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing this indication. Some transports never set this flag, whatever the current IRQL, when making receive-event notifications.
BytesIndicated
Specifies the number of bytes of expedited data in the buffer mapped at Tsdu. This parameter is always less than or equal to the value of BytesAvailable. A TDI transport provides at least 128 bytes of data in a receive indication to its client, unless the received message or stream segment is less than 128 bytes in length. If BytesAvailable is greater than BytesIndicated, the transport has received data that it does not make available when it calls ClientEventReceiveExpedited.
BytesAvailable
Specifies the total number of bytes in the received TSDU.
BytesTaken
Points to a caller-supplied variable in which ClientEventReceiveExpedited returns the number of bytes of data it copies from the TSDU.
Tsdu
Points to an MDL, possibly the initial MDL in a chain, mapping the buffer containing the received TSDU data.
IoRequestPacket
Points to a variable in which ClientEventReceiveExpedited returns a pointer to an IRP that was set up with TdiBuildReceive. If this parameter is NULL, the client will not be making a receive request for the remainder of the TSDU.

Return Value

ClientEventReceiveExpedited can return one of the following:

STATUS_SUCCESS
Indicates the client copied all the data in the given TSDU.
STATUS_MORE_PROCESSING_REQUIRED
Indicates the client has supplied an IRP requesting the remainder of the TSDU, after copying BytesIndicated into an internal buffer.
STATUS_DATA_NOT_ACCEPTED
Indicates this client is not interested in the TSDU.

Comments

ClientEventReceiveExpedited accepts or rejects an expedited TSDU that the TDI driver has received on an established endpoint-to-endpoint connection. This handler is almost identical to ClientEventReceive, except that expedited data flow supersedes normal data flow.

Consequently, the underlying transport can call ClientEventReceiveExpedited on a connection while a normal receive operation or an indication to ClientEventReceive (or ClientEventChainedReceive) is in progress. If this occurs, normal data flow does not resume until ClientEventReceiveExpedited has returned and the client has received all the expedited data for the TSDU. If ClientEventReceiveExpedited provides a TDI_RECEIVE request at IoRequestPacket, this request also has priority over any client-issued request for nonexpedited data.

When ClientEventReceiveExpedited is called, it can do one of the following:

  • Reject the indicated TSDU if it has no use for the data by returning STATUS_DATA_NOT_ACCEPTED

  • Copy all of the data into an internal buffer if TDI_RECEIVE_ENTIRE_MESSAGE is set in the ReceiveFlags and return STATUS_SUCCESS

  • If TDI_RECEIVE_ENTIRE_MESSAGE is clear (or TDI_RECEIVE_PARTIAL is set), copy some or all of the indicated data and return either of the following, depending on which operation it carries out:

    STATUS_MORE_PROCESSING_REQUIRED if the client is supplying a TDI_RECEIVE request at IoRequestPacket to obtain the remaining TSDU data

    STATUS_SUCCESS if the transport is expected to call ClientEventReceiveExpedited again with the remaining TSDU data

When it has finished copying receive data, ClientEventReceiveExpedited sets the variable at BytesTaken to the number of bytes of data accepted before it returns control.

By default, ClientEventReceiveExpedited runs at IRQL DISPATCH_LEVEL.

See Also

ClientEventChainedReceiveExpedited, ClientEventReceive, TdiBuildInternalDeviceControlIrp, TdiBuildReceive, TdiBuildSetEventHandler


ClientEventSendPossible

TDI_STATUS
ClientEventSendPossible (
 IN PVOID TdiEventContext,
 IN PVOID ConnectionContext,
 IN ULONG BytesAvailable
 );

ClientEventSendPossible is an event handler that the underlying TDI transport, which buffers sends internally but returned STATUS_DEVICE_NOT_READY for this client's preceding TDI_SEND request, calls when the transport has internal buffer space available to accept a resubmitted send request from the client.

Parameters

TdiEventContext
Points to the client-supplied context provided in the IRP that was set up with TdiBuildSetEventHandler when ClientEventSendPossible was registered with the underlying transport.
ConnectionContext
Points to the client's context area for this connection endpoint. The client previously supplied this value to its underlying transport when its ClientEventConnect handler accepted a connection offer from the remote-node peer and/or when it opened the connection endpoint with ZwCreateFile.
BytesAvailable
Specifies the number of bytes currently available in the TDI driver for buffering client-supplied send data.

Return Value

ClientEventSendPossible can return STATUS_SUCCESS.

Comments

A call to ClientEventSendPossible indicates that its underlying transport now has buffer space available for sends and how much buffer space it has when the transport makes this call.

However, ClientEventSendPossible cannot necessarily submit a successful nonblocking TDI_SEND request for the full BytesAvailable. For example, another client of the same transport might submit a send that uses the transport's available buffer space before this client's send request reaches the transport.

ClientEventSendPossible can be called at IRQL DISPATCH_LEVEL.

See Also

TdiBuildSend, TdiBuildSetEventHandler, TDI_SEND


Chapter 6 TDI Structures

This chapter describes structures used by TDI transport drivers and/or by their kernel-mode clients.

The structures described here are in alphabetic order.


TA_ADDRESS

typedef struct _TA_ADDRESS {
    USHORT  AddressLength;
    USHORT  AddressType;
    UCHAR   Address[1];
} TA_ADDRESS, *PTA_ADDRESS;

TA_ADDRESS defines a single transport address of a specific type, for example, NetBIOS.

Members

AddressLength
Specifies the number of bytes in an address of the specified AddressType.

For example, each NetBIOS-type address is TDI_ADDRESS_LENGTH_NETBIOS bytes in length.

AddressType
Specifies the type of the transport address. The system defines symbolic constants for possible address types, some corresponding to Windows Sockets values and some extensions defined by TDI. Defined address types include the following:
TDI_ADDRESS_TYPE_IP
Internetwork: UDP, TCP, etc.
TDI_ADDRESS_TYPE_APPLETALK
AppleTalk address.
TDI_ADDRESS_TYPE_NETBIOS
NetBIOS address.
TDI_ADDRESS_TYPE_NETBIOS_EX
Extended NetBIOS address.
TDI_ADDRESS_TYPE_IPX
Netware IPX.
TDI_ADDRESS_TYPE_VNS
Banyan VINES IP address.
TDI_ADDRESS_TYPE_8022
802.2 netcard-level address; TDI only.
TDI_ADDRESS_TYPE_NETONE
Net/One address. TDI only.
TDI_ADDRESS_TYPE_NS
Xerox NS protocols.
TDI_ADDRESS_TYPE_OSI_TSAP
OSI, TSAP address; TDI only.

The following are defined for future use:

TDI_ADDRESS_TYPE_IMPLINK
Arpanet implink address.
TDI_ADDRESS_TYPE_PUP
Pup protocols, such as BSP.
TDI_ADDRESS_TYPE_CHAOS
MIT CHAOS protocols.
TDI_ADDRESS_TYPE_NBS
NBS protocols.
TDI_ADDRESS_TYPE_ECMA
European computer manufacturers association protocols.
TDI_ADDRESS_TYPE_DATAKIT
Datakit protocols.
TDI_ADDRESS_TYPE_CCITT
CCITT protocols.
TDI_ADDRESS_TYPE_SNA
IBM SNA.
TDI_ADDRESS_TYPE_DECnet
DECnet.
TDI_ADDRESS_TYPE_DLI
Direct data-link interface (DLC).
TDI_ADDRESS_TYPE_LAT
LAT.
TDI_ADDRESS_TYPE_HYLINK
NSC Hyperchannel.
TDI_ADDRESS_TYPE_UNIX
Local to host, for pipes, portals, etc.
TDI_ADDRESS_TYPE_UNSPEC
Unspecified type.
Address[1]
Specifies a variable-sized array containing the transport address. The structure of the contents depends on the specified AddressType.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS.

For certain TDI_ADDRESS_TYPE_XXX values, transports and their kernel-mode clients can use system-defined substructures for Address and corresponding TDI_ADDRESS_LENGTH_XXX constants for AddressLength.

For convenience, TDI defines TA_ADDRESS_XXX structures that specify a single address of certain TDI_ADDRESS_TYPE_XXX types.

See Also

TDI_ADDRESS_8022, TDI_ADDRESS_APPLETALK, TDI_ADDRESS_IP, TDI_ADDRESS_IPX, TDI_ADDRESS_NETBIOS, TDI_ADDRESS_NETBIOS_EX, TDI_ADDRESS_NETONE, TDI_ADDRESS_NS, TDI_ADDRESS_OSI_TSAP, TDI_ADDRESS_VNS, TRANSPORT_ADDRESS


TA_APPLETALK_ADDR

typedef struct _TA_APPLETALK_ADDR {
    LONG  TAAddressCount;
    struct  _AddrAtalk {
        USHORT                 AddressLength;
        USHORT                 AddressType;
        TDI_ADDRESS_APPLETALK  Address[1];
    } Address [1];
} TA_APPLETALK_ADDRESS, *PTA_APPLETALK_ADDRESS;

TA_APPLETALK_ADDR contains a single AppleTalk transport address, suitable for use as a TRANSPORT_ADDRESS.

Members

TAAddressCount
Specifies one.
AddressLength
Specifies the length (4 bytes) of an AppleTalk address.
AddressType
Specifies TDI_ADDRESS_TYPE_APPLETALK.
Address[1]
Specifies a single address of type TDI_ADDRESS_APPLETALK.

Comments

TDI defines this structure for convenience in situations requiring a single TDI_ADDRESS of type TDI_ADDRESS_TYPE_APPLETALK.

See Also

TA_ADDRESS, TDI_ADDRESS_APPLETALK, TRANSPORT_ADDRESS


TA_ADDRESS_IP

typedef struct _TA_ADDRESS_IP {
    LONG  TAAddressCount;
    struct  _AddrIp {
        USHORT          AddressLength;
        USHORT          AddressType;
        TDI_ADDRESS_IP  Address[1];
    } Addresss[1];
} TA_IP_ADDRESS, *PTA_IP_ADDRESS;

TA_ADDRESS_IP contains a single IP transport address, suitable for use as a TRANSPORT_ADDRESS.

Members

TAAddressCount
Specifies one.
AddressLength
Specifies the length (14 bytes) of an IP address.
AddressType
Specifies TDI_ADDRESS_TYPE_IP.
Address[1]
Specifies a single address of type TDI_ADDRESS_IP.

Comments

TDI defines this structure for convenience in situations requiring a single TDI_ADDRESS of type TDI_ADDRESS_TYPE_IP.

See Also

TA_ADDRESS, TDI_ADDRESS_IP, TRANSPORT_ADDRESS


TA_ADDRESS_IPX

typedef struct _TA_ADDRESS_IPX {
    LONG  TAAddressCount;
    struct  _AddrIpx {
        USHORT          AddressLength;
        USHORT          AddressType;
        TDI_ADDRESS_IP  Address[1];
    } Addresss[1];
} TA_IPX_ADDRESS, *PTA_IPX_ADDRESS;

TA_ADDRESS_IPX contains a single IPX transport address, suitable for use as a TRANSPORT_ADDRESS.

Members

TAAddressCount
Specifies one.
AddressLength
Specifies the length (12 bytes) of an IPX address.
AddressType
Specifies TDI_ADDRESS_TYPE_IPX.
Address[1]
Specifies a single address of type TDI_ADDRESS_IPX.

Comments

TDI defines this structure for convenience in situations requiring a single TDI_ADDRESS of type TDI_ADDRESS_TYPE_IPX.

See Also

TA_ADDRESS, TDI_ADDRESS_IPX, TRANSPORT_ADDRESS


TA_NETBIOS_ADDRESS

typedef struct _TA_NETBIOS_ADDRESS {
    LONG  TAAddressCount;
    struct _Addr{
        USHORT               AddressLength;
        USHORT               AddressType;
        TDI_ADDRESS_NETBIOS  Address[1];
    } Address [1];
} TA_NETBIOS_ADDRESS, *PTA_NETBIOS_ADDRESS;

TA_NETBIOS_ADDRESS contains a single NetBIOS transport address, suitable for use as a TRANSPORT_ADDRESS.

Members

TAAddressCount
Specifies one.
AddressLength
Specifies the length (18 bytes) of a NetBIOS address.
AddressType
Specifies TDI_ADDRESS_TYPE_NETBIOS.
Address[1]
Specifies a single address of type TDI_ADDRESS_NETBIOS.

Comments

TDI defines this structure for convenience in situations requiring a single TDI_ADDRESS of type TDI_ADDRESS_TYPE_NETBIOS.

See Also

TA_ADDRESS, TDI_ADDRESS_NETBIOS, TRANSPORT_ADDRESS


TA_ADDRESS_NS

typedef struct _TA_ADDRESS_NS {
    LONG  TAAddressCount;
    struct  _AddrNs {
        USHORT          AddressLength;
        USHORT          AddressType;
        TDI_ADDRESS_NS  Address[1];
    } Addresss[1];
} TA_NS_ADDRESS, *PTA_NS_ADDRESS;

TA_ADDRESS_NS contains a single (X)NS transport address, suitable for use as a TRANSPORT_ADDRESS.

Members

TAAddressCount
Specifies one.
AddressLength
Specifies the length (12 bytes) of an NS address.
AddressType
Specifies TDI_ADDRESS_TYPE_NS.
Address[1]
Specifies a single address of type TDI_ADDRESS_NS.

Comments

TDI defines this structure for convenience in situations requiring a single TDI_ADDRESS of type TDI_ADDRESS_TYPE_NS.

See Also

TA_ADDRESS, TDI_ADDRESS_NS, TRANSPORT_ADDRESS


TA_ADDRESS_VNS

typedef struct _TA_ADDRESS_VNS {
    LONG  TAAddressCount;
    struct  _AddrVns {
        USHORT           AddressLength;
        USHORT           AddressType;
        TDI_ADDRESS_VNS  Address[1];
    } Addresss[1];
} TA_VNS_ADDRESS, *PTA_VNS_ADDRESS;

TA_ADDRESS_VNS contains a single VNS transport address, suitable for use as a TRANSPORT_ADDRESS.

Members

TAAddressCount
Specifies one.
AddressLength
Specifies the length (14 bytes) of a VNS address.
AddressType
Specifies TDI_ADDRESS_TYPE_VNS.
Address[1]
Specifies a single address of type TDI_ADDRESS_VNS.

Comments

TDI defines this structure for convenience in situations requiring a single TDI_ADDRESS of type TDI_ADDRESS_TYPE_VNS.

See Also

TA_ADDRESS, TDI_ADDRESS_VNS, TRANSPORT_ADDRESS


TDI_ACTION_HEADER

typedef struct _TDI_ACTION_HEADER {
    ULONG   TransportId;
    USHORT  ActionCode;
    USHORT  Reserved;
} TDI_ACTION_HEADER, *PTDI_ACTION_HEADER;

TDI_ACTION_HEADER is the initial structure in any client-supplied action parameter block passed in a TDI_ACTION request to the underlying transport driver.

Members

TransportId
Specifies a value identifying the TDI transport.

Any Microsoft-supplied transport defines an identifier as a 4-character string beginning with the letter "M." By convention, other transport vendors define their respective identifiers to begin with another letter.

ActionCode
Specifies the transport-defined action code, which must be unique to the transport driver designated by TransportId.
Reserved
Specifies a reserved member that must be set to zero.

Comments

The remainder of an action parameter block is a transport-specific extension to the TDI interface. That is, the transport driver defines the structure and required contents for the remainder of the buffer that its kernel-mode clients must supply in each TDI_ACTION request.

See Also

TDI_ACTION, TdiBuildAction


TDI_ADDRESS_8022

typedef struct _TDI_ADDRESS_8022 {
    UCHAR MACAddress[6];
} TDI_ADDRESS_8022, *PTDI_ADDRESS_8022;

TDI_ADDRESS_8022 contains an 802.2 netcard-level address, packed and aligned correctly.

Members

MACAddress[6]
Specifies a character array containing the address.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_8022 defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_8022.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_8022.

See Also

TA_ADDRESS, TRANSPORT_ADDRESS


TDI_ADDRESS_APPLETALK

typedef struct _TDI_ADDRESS_APPLETALK {
    USHORT  Network;
    UCHAR   Node;
    UCHAR   Socket;
} TDI_ADDRESS_APPLETALK, *PTDI_ADDRESS_APPLETALK;

TDI_ADDRESS_APPLETALK contains an AppleTalk address, packed and aligned correctly.

Members

Network
Specifies the AppleTalk network identifier.
Node
Specifies the AppleTalk node identifier.
Socket
Specifies the AppleTalk socket identifier.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_APPLETALK defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_APPLETALK.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_APPLETALK.

See Also

TA_ADDRESS, TA_APPLETALK_ADDR, TRANSPORT_ADDRESS


TDI_ADDRESS_INFO

typedef struct _TDI_ADDRESS_INFO {
    ULONG              ActivityCount;
    TRANSPORT_ADDRESS  Address;
} TDI_ADDRESS_INFO, *PTDI_ADDRESS_INFO;

TDI_ADDRESS_INFO defines the structure of the information returned for a TDI_QUERY_INFORMATION request in which the IrpSp->Parameters.QueryType is set to TDI_QUERY_ADDRESS_INFO.

Members

ActivityCount
Specifies the number of kernel-mode clients that currently have open file objects representing the given transport address.
Address
Specifies a TRANSPORT_ADDRESS structure containing the transport address associated with a particular open file object.

Comments

Each client process that calls ZwCreateFile successfully to open a particular transport address is given a handle to its own file object representing that address. Thus, a single transport address can have many associated file objects at any particular time.

Any kernel-mode client that has opened a particular transport address can make a query to determine how many other clients are currently using the same transport address. Such a client sets up an IRP with TdiBuildQueryInformation, passing in the QType TDI_QUERY_ADDRESS_INFO, and submits the IRP to the underlying transport to get this information.

TDI_ADDRESS_INFO defines the format in which the transport returns the requested information for such a query.

See Also

TdiBuildQueryInformation, TdiDispatchInternalDeviceControl, TDI_QUERY_INFORMATION, TRANSPORT_ADDRESS


TDI_ADDRESS_IP

typedef struct _TDI_ADDRESS_IP {
    USHORT  sin_port;
    ULONG   in_addr;
    UCHAR   sin_zero[8];
} TDI_ADDRESS_IP, *PTDI_ADDRESS_IP;

TDI_ADDRESS_IP contains an IP address, packed and aligned correctly.

Members

sin_port
Specifies a TCP or UDP port number.
in_addr
Specifies the IP address.
sin_zero[8]
Specifies an array filled with zeros.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_IP defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_IP.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_IP.

See Also

TA_ADDRESS, TA_ADDRESS_IP, TRANSPORT_ADDRESS


TDI_ADDRESS_IPX

typedef struct _TDI_ADDRESS_IPX {
    ULONG   NetworkAddress;
    UCHAR   NodeAddress[6];
    USHORT  Socket;
} TDI_ADDRESS_IPX, *PTDI_ADDRESS_IPX;

TDI_ADDRESS_IPX contains an IPX address, packed and aligned correctly.

Members

NetworkAddress
Specifies the IPX network address.
NodeAddress[6]
Specifies the IPX node address.
Socket
Specifies the IPX socket identifier.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_IPX defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_IPX.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_IPX.

See Also

TA_ADDRESS, TA_ADDRESS_IPX, TRANSPORT_ADDRESS


TDI_ADDRESS_NETBIOS

typedef struct _TDI_ADDRESS_NETBIOS {
    USHORT  NetbiosNameType;
    UCHAR   NetbiosName[16];
} TDI_ADDRESS_NETBIOS, *PTDI_ADDRESS_NETBIOS

TDI_ADDRESS_NETBIOS contains a NetBIOS address, packed and aligned correctly.

Members

NetbiosNameType
Specifies a NetBIOS name type. A TDI transport uses this member only when a file object representing an address is opened. Otherwise, the transport can ignore this member. This member can be one of the following values:
TDI_NETBIOS_TYPE_UNIQUE
The value at NetbiosName is unique.
TDI_NETBIOS_TYPE_GROUP
The value at NetbiosName is a group name.
TDI_NETBIOS_TYPE_QUICK_UNIQUE
The value at NetbiosName is unique. The transport adds this name to its name table without checking for conflicts on the network.
TDI_NETBIOS_TYPE_QUICK_GROUP
The value at NetbiosName is a group name. The transport adds this name to its name table without checking for conflicts on the network.
NetbiosName[16]
Specifies the NetBIOS name.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_NETBIOS defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_NETBIOS.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_NETBIOS.

See Also

TA_ADDRESS, TA_NETBIOS_ADDRESS, TDI_ADDRESS_NETBIOS_EX, TdiDispatchCreate, TRANSPORT_ADDRESS


TDI_ADDRESS_NETBIOS_EX

typedef struct _TDI_ADDRESS_NETBIOS_EX {
   UCHAR               EndpointName[16];
   TDI_ADDRESS_NETBIOS NetbiosAddress;
} TDI_ADDRESS_NETBIOS_EX, *PTDI_ADDRESS_NETBIOS_EX;

TDI_ADDRESS_NETBIOS_EX contains an extended NetBIOS address, packed and aligned correctly.

Members

EndpointName
Specifies the name to be presented to the destination machine in the NETBIOS session-setup request packet.
NetbiosAddress
Specifies a NetBIOS address from which the local-node transport derives the transport address of the target remote node.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_NETBIOS_EX defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_NETBIOS_EX.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_NETBIOS_EX.

This structure can be used by a kernel-mode client connecting to a NETBIOS service on a remote machine, assuming the underlying TDI transport supports this operation.

For example, suppose the registered name for the NETBIOS service on a remote machine is "FooService" and the DNS name for the remote machine is something like "MyRemoteMachine.Microsoft.com." Such a client could use a TDI_ADDRESS_NETBIOS_EX structure in which EndpointName was set to FooService and NetbiosAddress was set to MyRemoteMachine.Microsoft.com when the client issued the TDI_CONNECT request to its underlying transport.

The system-supplied NETBT (NetBIOS over TCP/IP) transport uses the TDI_ADDRESS_NETBIOS_EX structure in this manner to support the SMB file server/redirector.

See Also

TA_ADDRESS, TA_NETBIOS_ADDRESS, TDI_ADDRESS_NETBIOS, TRANSPORT_ADDRESS


TDI_ADDRESS_NETONE

typedef struct _TDI_ADDRESS_NETONE {
    USHORT  NetoneNameType;
    UCHAR   NetoneName[20];
} TDI_ADDRESS_NETONE, *PTDI_ADDRESS_NETONE;

TDI_ADDRESS_NETONE contains a Net/One address, packed and aligned correctly.

Members

NetoneNameType
Specifies a Net/One name type as one of the following:
TDI_ADDRESS_NETONE_TYPE_UNIQUE
A unique name.
TDI_ADDRESS_NETONE_TYPE_ROTORED
A rotored name.
NetoneName[20]
Specifies the Net/One name.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_NETONE defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_NETONE.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_NETONE.

See Also

TA_ADDRESS, TRANSPORT_ADDRESS


TDI_ADDRESS_NS

typedef struct _TDI_ADDRESS_NS {
    ULONG   NetworkAddress;
    UCHAR   NodeAddress[6];
    USHORT  Socket;
} TDI_ADDRESS_NS, *PTDI_ADDRESS_NS;

TDI_ADDRESS_NS contains a XEROX (X)NS address, packed and aligned correctly.

Members

NetworkAddress
Specifies the NS network address.
NodeAddress[6]
Specifies the NS node address.
Socket
Specifies the NS socket identifier.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_NS defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_NS.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_NS.

See Also

TA_ADDRESS, TA_ADDRESS_NS, TRANSPORT_ADDRESS


TDI_ADDRESS_OSI_TSAP

tyepdef struct _TDI_ADDRESS_OSI_TSAP {
    USHORT  tp_addr_type;
    USHORT  tp_taddr_len;
    USHORT  tp_tsel_len;
    UCHAR   tp_addr[ISO_MAX_ADDR_LENGTH];
} TDI_ADDRESS_OSI_TSAP, *PTDI_ADDRESS_OSI_TSAP;

TDI_ADDRESS_OSI_TSAP contains an ISO TP4-compliant TSAP address, packed and aligned correctly.

Members

tp_addr_type
Specifies the type of the address, as one of the following:
ISO_HIERARCHICAL
The tp_addr contains both the transport selector and the network address.
ISO_NON_HIERARCHICAL
The tp_addr contains only the transport address. Such a specification must be translated into the transport selector and network address.
tp_taddr_len
Specifies the size in bytes of the transport address, which is <= 52 if tp_addr_type is ISO_NON_HIERARCHICAL or <= 32 otherwise.
tp_tsel_len
Specifies the size in bytes of the transport selector (<= 32) if tp_addr_type is ISO_HIERARCHICAL. Otherwise, this member is zero.
tp_addr
Specifies the address according to the given tp_addr_type. This member can be at most 64 bytes in length.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_OSI_TSAP defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_OSI_TSAP.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_OSI_TSAP.

See Also

TA_ADDRESS, TRANSPORT_ADDRESS


TDI_ADDRESS_VNS

tyepdef struct _TDI_ADDRESS_VNS {
    UCHAR  net_address[4];
    UCHAR  subnet_addr[2];
    UCHAR  port[2];
    UCHAR  hops;
    UCHAR  filler[5];
} TDI_ADDRESS_VNS, *PTDI_ADDRESS_VNS;

TDI_ADDRESS_VNS contains a Banyan VINES IP address, packed and aligned correctly.

Members

net_address
Specifies the network address, which is static.
subnet_addr
Specifies the subnet address, which is dynamic.
port
Specifies the port number.
hops
Specifies the limit on hops for broadcasts.
filler
Filled with zeros.

Comments

A TRANSPORT_ADDRESS structure contains a specified number of elements of type TA_ADDRESS. TDI_ADDRESS_VNS defines the Address member of a TA_ADDRESS structure in which the AddressType is set to TDI_ADDRESS_TYPE_VNS.

The AddressLength member(s) of the TA_ADDRESS substructure(s) can be set to the system-defined constant TDI_ADDRESS_LENGTH_VNS.

See Also

TA_ADDRESS, TA_ADDRESS_VNS, TRANSPORT_ADDRESS


TDI_CONNECTION_INFO

typedef struct _TDI_CONNECTION_INFO {
    ULONG          State;
    ULONG          Event;
    ULONG          TransmittedTsdus;
    ULONG          ReceivedTsdus;
    ULONG          TransmissionErrors;
    ULONG          ReceiveErrors;
    LARGE_INTEGER  Throughput;
    LARGE_INTEGER  Delay;
    ULONG          SendBufferSize;
    ULONG          ReceiveBufferSize;
    BOOLEAN        Unreliable;
} TDI_CONNECTION_INFO, *PTDI_CONNECTION_INFO;

TDI_CONNECTION_INFO defines the structure of the information returned for a TDI_QUERY_INFORMATION request in which the IrpSp->Parameters.QueryType is set to TDI_QUERY_CONNECTION_INFO.

Members

State
Specifies the current state of the connection on the network.
Event
Specifies the type of event the TDI driver most recently indicated to the client. The value is expressed as a TDI_EVENT_XXX code.
TransmittedTsdus
Specifies the number of TSDUs transmitted on the connection endpoint.
ReceivedTsdus
Specifies the number of TSDUs received on the connection endpoint.
TransmissionErrors
Specifies the number of TSDUs that have had an error during transmission on the connection endpoint.
ReceiveErrors
Specifies the number of TSDUs that have had an error during reception on the connection endpoint.
Throughput
Specifies the estimated throughput on the connection, expressed in bytes per second, for sends and receives. Zero indicates that the driver cannot calculate the throughput.
Delay
Specifies the estimated delay on the connection, expressed as a negative value. Delay, which is essentially constant regardless of packet size, affects the transmission time of each TSDU sent to the remote node.
SendBufferSize
If the transport buffers sends internally, specifies the size in bytes of its send buffer. Otherwise, this member is zero.
ReceiveBufferSize
If the transport buffers receives internally, specifies the size in bytes of its receive buffer. Otherwise, this member is zero.
Unreliable
Specifies TRUE if the transport determines that the connection has become unreliable. The transport is likely to send a disconnection event to its client if this member is TRUE, and sends or receives on this connection are likely to fail.

Comments

A kernel-mode client that has opened a file object representing a connection with ZwCreateFile can make a query to determine the current state of its local connection. Such a client sets up an IRP with TdiBuildQueryInformation, passing in the QType TDI_QUERY_CONNECTION_INFO, and submits the IRP to the underlying transport to get this information.

TDI_CONNECTION_INFO defines the format in which the transport returns the requested information for such a query.

Until a client has established an endpoint-to-endpoint connection with a remote-node peer, only a subset of this information is potentially useful to the client, such as the SendBufferSize and ReceiveBufferSize of an underlying transport that supports internal buffering. A TDI transport can simply return its default or initialization values for members that are not yet relevant or even fail the query-information request if an endpoint-to-endpoint connection is not yet established.

Consequently, kernel-mode clients usually submit this request to their transports after making an endpoint-to-endpoint connection with a remote-node peer. For example, the client can estimate the transmission time for sends over the network using the Delay and Throughput values returned for this query, assuming the underlying transport does not return a zero for either. The estimated time to transmit a TSDU of size n bytes can be calculated as SendTime = Delay + (n * Throughput).

See Also

TdiBuildQueryInformation, TdiDispatchInternalDeviceControl, TDI_QUERY_INFORMATION


TDI_CONNECTION_INFORMATION

typedef struct _TDI_CONNECTION_INFORMATION {
    LONG   UserDataLength;
    PVOID  UserData;
    LONG   OptionsLength;
    PVOID  Options;
    LONG   RemoteAddressLength;
    PVOID  RemoteAddress;
} TDI_CONNECTION_INFORMATION, *PTDI_CONNECTION_INFORMATION;

TDI_CONNECTION_INFORMATION defines the structure of information that a kernel-mode client passes to or receives from the underlying transport for a subset of the TDI_XXX internal-device-control requests, particularly those relevant to endpoint-to-endpoint connections.

Members

UserDataLength
Specifies the size in bytes of the buffer at UserData or zero if UserData is NULL.
UserData
Points to client-supplied buffer for user data, generally sent or received when an endpoint-to-endpoint connection is being established or, possibly, disconnected.
OptionsLength
Specifies the size in bytes of the buffer at Options.
Options
Points to a client-supplied buffer, either containing client-supplied data or awaiting transport-returned data for the given IOCTL request.
RemoteAddressLength
Specifies the size in bytes of the buffer at RemoteAddress.
RemoteAddress
Points to a buffer containing a remote-node transport address either specified by the client or returned by the transport to the client. The syntax of this address depends on the type of network and/or the underlying transport (see TA_ADDRESS).

Comments

While several TdiBuild Xxx macros and the corresponding TDI_XXX IOCTL requests use this structure to pass information between TDI transports and their clients, each does not use every member of the TDI_CONNECTION_INFORMATION structure.

As a general rule, a transport does not return any information in this structure if it returns STATUS_PENDING for a given IOCTL request. Instead, the driver defers setting the data in this structure until just before it completes the IRP.

Supplying and Receiving UserData

After an endpoint-to-endpoint connection is established, a local-node client can send TSDUs to its remote-node peer by submitting TDI_SEND requests to the underlying transport. The local-node client can either receive TSDUs from its remote-node peer by submitting TDI_RECEIVE requests or through calls to its registered ClientEvent(Chained)Receive(Expedited) handlers.

However, until such a connection is established, two TDI clients in the process of establishing an endpoint-to-endpoint connection can send data to each other by supplying it in a buffer at UserData with their TDI_CONNECT, TDI_LISTEN, and, possibly, TDI_ACCEPT requests to their respective underlying transports. In a similar manner, two such clients also can supply disconnection data to each other in the process of controlled-disconnect operation, assuming their respective transports support controlled disconnections.

A client can determine the transport-specific limit on the amount of user data by submitting a query-information request, set up with TdiBuildQueryInformation for the QType TDI_QUERY_PROVIDER_INFO.

When the client can send user data with a particular TDI IOCTL request, a UserDataLength set to zero implies that the client has provided no user data in its request to the transport. When the client can receive user data with a particular TDI IOCTL request, a UserDataLength set to zero implies that the transport returned no data in the buffer at UserData. Otherwise, the transport sets UserDataLength to the number of bytes of data it received for the client.

If the client specifies a nonzero UserDataLength and the transport receives more data from the remote node than it can fit into the client-supplied buffer, the transport returns as much data as it can in the client's buffer. In this case, the transport completes the operation normally but returns a status indicating data truncation to its client.

UserDataLength must be zero and UserData must NULL for the following TDI_XXX IOCTL requests:

TDI_RECEIVE_DATAGRAM and TDI_SEND_DATAGRAM
The TDI_CONNECTION_INFORMATION structure is used only to specify the remote-node address for these requests.
TDI_LISTEN if the client requests a delayed-connection acceptance
Such a client can supply any necessary user data with its subsequent TDI_ACCEPT request when it accepts a connection offer from a remote-node peer.

When the transport does not allow its clients to supply user data for a particular TDI IOCTL request, the transport fails all such requests in which a client specifies a nonzero UserDataLength.

Using Options and OptionsLength

For a TDI_LISTEN request, the OptionsLength is either zero or sizeof(ULONG) if the client set TDI_QUERY_ACCEPT in the Flags parameter to TdiBuildListen, and Options is either NULL or the buffer contains TDI_QUERY_ACCEPT.

Except for the TDI_LISTEN request, the transport defines the syntax and semantics of any additional options it supports for its clients, usually along the following lines:

  • When a client can supply transport-specific information at Options, setting OptionsLength to zero implies that the underlying driver can use its default values for options. When the transport can return information at Options, setting OptionsLength to zero implies that the client would not use the returned data so the transport should ignore Options.

  • If such a client sets OptionsLength to a nonzero value, but the transport receives an option string too large for the client-supplied buffer, the transport truncates the options string and completes the client's request normally with a status that indicates data truncation occurred.

  • When the client of such a transport uses Options to specify option values for the driver to transfer, the values the driver actually writes can be different from the client-specified values.

Using RemoteAddress and RemoteAddressLength

Use of these members depends on the TDI_XXX IOCTL and on the size of the transport-specific address. In general, a local-node client uses RemoteAddress to specify the target remote-node address when it initiates network-transfer operations, such as connection offers and sends. The transport returns information to RemoteAddress when such an operation was initiated at the remote node, such as to satisfy listen and receive requests.

If the client specifies zero for RemoteAddressLength, the transport assumes RemoteAddress is NULL. If the client specifies a nonzero value for RemoteAddressLength but the transport has received an address too long for the client-supplied buffer, the driver function truncates the address to fit in the client's buffer. In this case, the transport completes the operation normally but returns a status code indicating data truncation to the client.

See Also

TA_ADDRESS, TDI_ACCEPT, TdiBuildQueryInformation, TDI_CONNECT, TDI_DISCONNECT, TDI_LISTEN, TDI_RECEIVE_DATAGRAM, TDI_SEND_DATAGRAM, TDI_SET_INFORMATION


TDI_DATAGRAM_INFO

typedef struct _TDI_DATAGRAM_INFO {
    ULONG  MaximumDatagramBytes;
    ULONG  MaximumDatagramCount;
} TDI_DATAGRAM_INFO, *PTDI_DATAGRAM_INFO;

TDI_DATAGRAM_INFO defines the structure of the information returned for a TDI_QUERY_INFORMATION request in which the IrpSp->Parameters.QueryType is set to TDI_QUERY_DATAGRAM_INFO.

Members

MaximumDatagramBytes
Specifies the maximum size in bytes for a datagram.
MaximumDatagramCount
Specifies the maximum number of active datagrams.

Comments

Any kernel-mode client that has opened a control channel can make a query to determine the transport's limits on datagram size and activity. Such a client sets up an IRP with TdiBuildQueryInformation, passing in the QType TDI_QUERY_DATAGRAM_INFO, and submits the IRP to the underlying transport to get this information.

A transport also can support this query on open transport addresses at the discretion of the transport writer.

TDI_DATAGRAM_INFO defines the format in which the transport returns the requested information for such a query.

See Also

TdiBuildQueryInformation, TdiDispatchInternalDeviceControl, TDI_MAX_DATAGRAM_INFO, TDI_QUERY_INFORMATION, TDI_PROVIDER_INFO, TRANSPORT_ADDRESS


TDI_MAX_DATAGRAM_INFO

typedef struct _TDI_DATAGRAM_INFO {
    ULONG  MaxDatagramSize;
} TDI_DATAGRAM_INFO, *PTDI_DATAGRAM_INFO;

TDI_MAX_DATAGRAM_INFO defines the structure of the information returned for a TDI_QUERY_INFORMATION request in which the IrpSp->Parameters.QueryType is set to TDI_QUERY_MAX_DATAGRAM_INFO.

Member

MaxDatagramSize
Specifies the maximum number of bytes for a datagram.

Comments

Any kernel-mode client that has opened a control channel can make a query to determine the transport's limits on datagram size. Such a client sets up an IRP with TdiBuildQueryInformation, passing in the QType TDI_QUERY_MAX_DATAGRAM_INFO, and submits the IRP to the underlying transport to get this information.

A transport also can support this query on open transport addresses at the discretion of the transport writer.

TDI_MAX_DATAGRAM_INFO defines the format in which the transport returns the requested information for such a query.

See Also

TdiBuildQueryInformation, TdiDispatchInternalDeviceControl, TDI_DATAGRAM_INFO, TDI_QUERY_INFORMATION, TDI_PROVIDER_INFO, TRANSPORT_ADDRESS


TDI_PROVIDER_INFO

struct _TDI_PROVIDER_INFO {
    ULONG          Version;
    ULONG          MaxSendSize;
    ULONG          MaxConnectionUserData;
    ULONG          MaxDatagramSize;
    ULONG          ServiceFlags;
    ULONG          MinimumLookaheadData;
    ULONG          MaximumLookaheadData;
    ULONG          NumberOfResources;
    LARGE_INTEGER  StartTime;
} TDI_PROVIDER_INFO, *PTDI_PROVIDER_INFO;

TDI_PROVIDER_INFO defines the structure of the information returned for a TDI_QUERY_INFORMATION request in which the IrpSp->Parameters.QueryType is set to TDI_QUERY_PROVIDER_INFO or TDI_QUERY_PROVIDER_INFORMATION.

Members

Version
Specifies the TDI version number. The low-order bytes specify the minor version number, and the high-order bytes the major version number.
MaxSendSize
Specifies the maximum number of bytes a client can provide per TDI_SEND request.
MaxConnectionUserData
Specifies the maximum number of bytes a client can provide as user data in a TDI_CONNECT, TDI_LISTEN, or TDI_ACCEPT request.
MaxDatagramSize
Specifies the maximum size, in bytes, of a datagram a client can send (or receive).
ServiceFlags
Specifies features that the transport offers as a combination (Ored) of the following flags:
TDI_SERVICE_CONNECTION_MODE
This transport supports connection-oriented traffic (endpoint-to-endpoint transfers).
TDI_SERVICE_CONNECTIONLESS_MODE
This transport supports connectionless traffic (datagram sends and receives).
TDI_SERVICE_BROADCAST_SUPPORTED
This transport supports broadcast datagrams.
TDI_SERVICE_MULTICAST_SUPPORTED
This transport supports multicast datagrams.
TDI_SERVICE_ROUTE_DIRECTED
This transport supports directed packets, which can reach remote nodes that datagrams cannot reach.
TDI_SERVICE_ORDERLY_RELEASE
This transport supports controlled disconnects.
TDI_SERVICE_DELAYED_ACCEPTANCE
This transport supports delayed-connection acceptances of incoming connection offers from remote nodes.
TDI_SERVICE_EXPEDITED_DATA
This transport supports expedited sends and receives.
TDI_SERVICE_INTERNAL_BUFFERING
This transport buffers TSDUs internally.
TDI_SERVICE_MESSAGE_MODE
This transport supports message-mode sends and receives.
TDI_SERVICE_NO_ZERO_LENGTH
This transport does not support zero-length sends.
TDI_SERVICE_ERROR_FREE_DELIVERY
This transport supports error-free delivery.
TDI_SERVICE_POINT_TO_POINT
This transport is bound to an underlying point-to-point WAN NIC driver of type NdisMediumWan.
TDI_SERVICE_SECURITY_LEVEL
This transport supports a security validator.
TDI_SERVICE_HALF_DUPLEX
The transport supports controlled disconnect operations such that after receiving a disconnect request from the remote node, it continues to indicate receives to the local-node client until its client also acknowledges the disconnection.
MinimumLookaheadData
Specifies the lower limit on the number of bytes this transport guarantees to indicate as lookahead data for receive and receive-datagram events.
MaximumLookaheadData
Specifies the upper limit on the number of bytes this transport guarantees to indicate as lookhead data for receive and receive-datagram events. If the underlying NIC driver supports multipacket receive indications, this value can be the maximum packet size supported by the network.
NumberOfResources
Specifies the number of TDI_PROVIDER_RESOURCE_STATS structures (resources) this transport returns if a client submits a query-information request of the type TDI_QUERY_PROVIDER_STATISTICS.
StartTime
Specifies the current system time at which the TDI driver became active.

Comments

Any kernel-mode client that has opened a control channel can make a query to determine the supported features and limits of its underlying transport. Such a client sets up an IRP with TdiBuildQueryInformation, passing in the QType TDI_QUERY_PROVIDER_INFO, and submits the IRP to the underlying transport to get this information.

TDI_PROVIDER_INFO defines the format in which the transport returns the requested information for such a query.

For example, a client might use the values returned in the Xxx LookaheadData and NumberOfResources members to size client-allocated buffers for subsequent client requests to the transport or to allocate buffers of an appropriate size before it registers ClientEvent(Chained)Receive and/or ClientEvent(Chained)ReceiveDatagram handlers.

See Also

ClientEventChainedReceive, ClientEventChainedReceiveDatagram, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveDatagram, ClientEventReceiveExpedited, TdiBuildQueryInformation, TdiBuildSetEventHandler, TDI_ACCEPT, TDI_CONNECT, TDI_LISTEN, TDI_QUERY_INFORMATION, TDI_PROVIDER_STATISTICS


TDI_PROVIDER_STATISTICS

struct _TDI_PROVIDER_STATISTICS {
    ULONG          Version;
    ULONG          OpenConnections;
    ULONG          ConnectionsAfterNoRetry;
    ULONG          ConnectionsAfterRetry;
    ULONG          LocalDisconnects;
    ULONG          RemoteDisconnects;
    ULONG          LinkFailures;
    ULONG          AdapterFailures;
    ULONG          SessionTimeouts;
    ULONG          CancelledConnections;
    ULONG          RemoteResourceFailures;
    ULONG          LocalResourceFailures;
    ULONG          NotFoundFailures;
    ULONG          NoListenFailures;
    ULONG          DatagramsSent;
    LARGE_INTEGER  DatagramBytesSent;
    ULONG          DatagramsReceived;
    LARGE_INTEGER  DatagramBytesReceived;
    ULONG          PacketsSent;
    ULONG          PacketsReceived;
    ULONG          DataFramesSent;
    LARGE_INTEGER  DataFrameBytesSent;
    ULONG          DataFramesReceived;
    LARGE_INTEGER  DataFrameBytesReceived;
    ULONG          DataFramesResent;
    LARGE_INTEGER  DataFrameBytesResent;
    ULONG          DataFramesRejected;
    LARGE_INTEGER  DataFrameBytesRejected;
    ULONG          ResponseTimerExpirations;
    ULONG          AckTimerExpirations;
    ULONG          MaximumSendWindow;
    ULONG          AverageSendWindow;
    ULONG          PiggybackAckQueued;
    ULONG          PiggybackAckTimeouts;
    LARGE_INTEGER  WastedPacketSpace;
    ULONG          WastedSpacePackets;
    ULONG          NumberOfResources;
    TDI_PROVIDER_RESOURCE_STATS  Resources[1];
} TDI_PROVIDER_STATISTICS, *PTDI_PROVIDER_STATISTICS;

TDI_PROVIDER_STATISTICS defines the structure of the information returned for a TDI_QUERY_INFORMATION request in which the IrpSp->Parameters.QueryType is set to TDI_QUERY_PROVIDER_STATISTICS.

Members

Version
Specifies the TDI version number. The low-order bytes specify the minor version number, and the high-order bytes the major version number.
OpenConnections
Specifies the number of open connection endpoints in the transport.
ConnectionsAfterNoRetry
Specifies the number of connections the transport has established with remote-node clients without doing frame retransmissions.
ConnectionsAfterRetry
Specifies the number of connections the transport has established with remote node clients after doing some frame retransmissions.
LocalDisconnects
Specifies the number of endpoint-to-endpoint connections that the transport's local-node clients have closed.
RemoteDisconnects
Specifies the number of endpoint-to-endpoint connections that remote-node clients have closed.
LinkFailures
Specifies the number of connections lost due to link-level failures.
AdapterFailures
Specifies the number of connections lost due to problems in the underlying NIC driver.
SessionTimeouts
Specifies the number of connections lost due to session-level time-outs.
CancelledConnections
Specifies the number of rejected connection offers.
RemoteResourceFailures
Specifies the number of connections that have failed due to resource problems at a remote node.
LocalResourceFailures
Specifies the number of connections that have failed due to local-node resource problems.
NotFoundFailures
Specifies the number of connection attempts that have failed because the transport could not find the specified remote node on the network.
NoListenFailures
Specifies the number of incoming connection offers failed because local-node clients were not listening for incoming connections.
DatagramsSent
Specifies the number of datagrams the TDI transport has sent.
DatagramBytesSent
Specifies the number of bytes in all datagrams the transport has sent.
DatagramsReceived
Specifies the number of datagrams the TDI transport has received.
DatagramBytesReceived
Specifies the number of bytes in all datagrams the transport has received.
PacketsSent
Specifies the number of packets, including control, data, and other frames, the transport has sent through NDIS to the physical medium.
PacketsReceived
Specifies the number of packets, including control, data, and other frames, the transport has received from the physical medium through NDIS.
DataFramesSent
Specifies the number of data frames the underlying NDIS NIC driver has sent.
DataFrameBytesSent
Specifies the number of bytes of frame data the underlying NDIS NIC driver has sent.
DataFramesReceived
Specifies the number of data frames the underlying NDIS NIC driver has received.
DataFrameBytesReceived
Specifies the number of bytes of frame data the underlying NDIS NIC driver has received.
DataFramesResent
Specifies the number of data frames the underlying NDIS NIC driver has re-sent.
DataFrameBytesResent
Specifies the number of bytes of frame data the underlying NDIS NIC driver has re-sent.
DataFramesRejected
Specifies the number of data frames the underlying NDIS NIC driver has received but rejected.
DataFrameBytesRejected
Specifies the number of bytes of frame data the underlying NDIS NIC driver has received but rejected.
ResponseTimerExpirations
Specifies the number of times the response timer (for example, T1 for NetBIOS) has expired.
AckTimerExpirations
Specifies the number of times the acknowledgment timer (for example, T2 for NetBIOS) has expired.
MaximumSendWindow
Specifies the maximum send window, in bytes, of the transport.
AverageSendWindow
Specifies the average send window, in bytes, of the transport.
PiggybackAckQueued
Specifies the number of times the transport has started a timer waiting for reverse traffic on which it can piggyback a data acknowledgment to a remote node.
PiggybackAckTimeouts
Specifies the number of times a piggyback acknowledgment timer has expired before the transport could send reverse traffic to a remote node.
WastedPacketSpace
Specifies the total number of bytes of wasted buffer space for transferred packets.
WastedSpacePackets
Specifies how many packets contributed to the value of WastedPacketSpace.
NumberOfResources
Specifies the number of elements of type TDI_PROVIDER_RESOURCE_STATS in the array at Resources.
Resources[1]
Specifies an array of TDI_PROVIDER_RESOURCE_STATS structures, each defined as follows:
struct _TDI_PROVIDER_RESOURCE_STATS {
    ULONG ResourceId;
    ULONG MaximumResourceUsed;
    ULONG AverageResourceUsed;
    ULONG ResourceExhausted;
} TDI_PROVIDER_RESOURCE_STATS, *PTDI_PROVIDER_RESOURCE_STATS;

ResourceId
Specifies a transport-specific value identifying a resource.
MaximumResourceUsed
Specifies the maximum number of resources of the specified type the transport can use concurrently.
AverageResourceUsed
Specifies the average number of resources of the specified type the transport uses overall.
ResourceExhausted
Specifies the number of times the transport has failed to allocate the specified resource.

Comments

Any kernel-mode client that has opened a control channel can make a query to determine the current statistics maintained by its underlying transport. Such a client sets up an IRP with TdiBuildQueryInformation, passing in the QType TDI_QUERY_PROVIDER_STATISTICS, and submits the IRP to the underlying transport to get this information.

TDI_PROVIDER_STATISTICS defines the format in which the transport returns the requested information for such a query.

See Also

TdiBuildQueryInformation, TDI_QUERY_INFORMATION, TDI_PROVIDER_INFO


TDI_REQUEST

typedef struct _TDI_REQUEST {
    union {
        HANDLE              AddressHandle;
        CONNECTION_CONTEXT  ConnectionContext;
        HANDLE              ControlChannel;
    } Handle;
    PVOID      RequestNotifyObject;
    PVOID      RequestContext;
    TDI_STATUS TdiStatus;
} TDI_REQUEST, *PTDI_REQUEST;

TDI_REQUEST contains information common to most TDI requests. This structure can be used by transport drivers to pass request-specific information between internal driver routines.

Members

AddressHandle
Specifies the handle to an open file object representing an address.
ConnectionContext
Specifies the client-supplied context associated with an open file object representing a connection endpoint.
ControlChannel
Specifies the handle to an open file object representing a control channel.
RequestNotifyObject
Points to a client-supplied IoCompletion routine, if any. A value of REQNOTIFY_NULL (a system-specific manifest) implies that request is synchronous.
RequestContext
Points to a client-supplied context for its IoCompletion routine, if any. This value is passed, uninterpreted by the system or the transport, to the IoCompletion routine when it is called on completion of this request.
TdiStatus
Specifies the final completion status of this request. If the request is synchronous, this is set to the value returned by the transport routine that handles this request.

See Also

TDI_REQUEST_KERNEL


TDI_REQUEST_KERNEL

typedef struct _TDI_REQUEST_KERNEL {
    ULONG                        RequestFlags;
    PTDI_CONNECTION_INFORMATION  RequestConnectionInformation;
    PTDI_CONNECTION_INFORMATION  ReturnConnectionInformation;
    PVOID                        RequestSpecific;
} TDI_REQUEST_KERNEL, *PTDI_REQUEST_KERNEL;

TDI_REQUEST_KERNEL defines a parameter format common to certain kernel-mode TDI_XXX IOCTL requests.

Members

RequestFlags
Specifies a flag or combination of flags for certain TDI_XXX IOCTLs.
RequestConnectionInformation
Points to a TDI_CONNECTION_INFORMATION structure containing client-supplied data.
ReturnConnectionInformation
Points to a caller-supplied buffer in which the transport returns data, formatted as TDI_CONNECTION_INFORMATION structure.
RequestSpecific
Points to a caller-supplied value of a request-specific nature, used only for IOCTL requests that need additional parameters, such as a time-out value.

Comments

Although not all members of the TDI_REQUEST_KERNEL structure are relevant to each such TDI_XXX IOCTL code, this structure is used as the common format at IrpSp->Parameters in the following TDI_XXX IOCTL requests to TDI transport drivers:

  • For TDI_CONNECT requests, the client supplies a remote-node address to which a connection should be offered, and possibly user connect data, at RequestConnectionInformation. The client can supply a time-out for its connection offer at RequestSpecific, or this member can be NULL, which directs the transport to use its own default time-out for the connection operation. The transport returns connection acceptance information at ReturnConnectionInformation.

  • For TDI_DISCONNECT requests, the client can set RequestFlags if the transport supports controlled disconnects; otherwise, this member is zero. The client can supply a time-out for the disconnection at RequestSpecific, or this member can be NULL, which directs the transport to use its own default time-out for the disconnection operation. The transport returns any disconnect data and status information supplied by the corresponding remote-node transport at ReturnConnectionInformation.

  • For TDI_LISTEN requests, the client can set RequestFlags with TDI_QUERY_ACCEPT if the transport supports delayed-connection acceptances; otherwise, this member is zero, which directs the transport to immediately accept any appropriate connection offer that comes in from a remote node. The client can specify a remote-node address from which a connection offer should be accepted and, if RequestFlags is zero, also some connect data to be transmitted to the remote node when the connection offer is accepted at RequestConnectionInformation. The transport returns any connect data transmitted from the remote node, as well as the remote-node address, at ReturnConnectionInformation when it accepts an endpoint-to-endpoint connection for its client, thereby satisfying its client's listen request.

Kernel-mode clients use the TdiBuild Xxx macros to set up Parameters in the transport's I/O stack location of a given IRP with the TDI_KERNEL_REQUEST structure for the preceding kernel-mode IOCTL requests. For TDI_DISASSOCIATE_ADDRESS requests, neither the client nor the transport pass any information in this structure.

The format at Parameters is request-specific for all other TDI_XXX. For more information about these structures, see the TDI_XXX references in Chapter 2.

See Also

TdiBuildConnect, TdiBuildDisconnect, TdiBuildListen, TDI_CONNECT, TDI_CONNECTION_INFORMATION, TDI_DISCONNECT, TDI_LISTEN


TDI_REQUEST_STATUS

typedef struct _TDI_REQUEST_STATUS {
    TDI_STATUS Status;
    PVOID RequestContext;
    ULONG BytesTransferred;
} TDI_REQUEST_STATUS, *PTDI_REQUEST_STATUS;

TDI_REQUEST_STATUS contains status information a TDI driver function sets when it completes its operation.This structure can be used by transport drivers to pass request-specific information between internal driver routines.

Members

Status
Specifies the final completion status of the function. For a synchronous function, this member contains the return value.
RequestContext
Points to the handle the client passes to the driver function in a TDI_REQUEST structure.
BytesTransferred
Specifies the number of bytes of data the function transfers. For an asynchronous receive operation, for example, this member contains the number of bytes of data the client receives when the transport completes the IRP.

See Also

TDI_REQUEST


TRANSPORT_ADDRESS

typedef struct _TRANSPORT_ADDRESS {
    LONG        TAAddressCount;
    TA_ADDRESS  Address[1];
} TRANSPORT_ADDRESS, *PTRANSPORT_ADDRESS;

TRANSPORT_ADDRESS defines the general format for an array of TDI driver-specific transport addresses.

Members

TAAddressCount
Specifies the number of elements in the array at Address.
Address[1]
Specifies an array of one or more TA_ADDRESS structures each specifying a transport address of a particular TDI_ADDRESS_TYPE_XXX.

Comments

Although any address specified in a TRANSPORT_ADDRESS structure is always driver-specific, all transports of a particular protocol type (for example, all NetBIOS transports or all IPX transports) use the same address format.

When it is given a TRANSPORT_ADDRESS structure, the transport uses the first recognizable address in the array at Address. For instance, if its client passes in several TDI_ADDRESS_TYPE_NETBIOS addresses, a NetBIOS transport would use only the first such address. If a transport can use some number of TDI_ADDRESS_TYPE_XXX addresses and it is given an array of mixed types, it uses only the first valid address of each such type.

A transport returns an error if it is given a TRANSPORT_ADDRESS structure containing elements of a type it is unable to parse. If the transport is given an address of a recognizable type that it cannot use, the transport can search the array elements for a usable address and return an error if it finds no valid address.

See Also

TA_ADDRESS, TA_APPLETALK_ADDR, TA_ADDRESS_IP, TA_ADDRESS_IPX, TA_NETBIOS_ADDRESS, TA_ADDRESS_NS, TA_ADDRESS_VNS,


Chapter 7 Windows Sockets Helper Functions

This describes the system-defined WSHXxx functions that a transport driver provides in a user-mode dynamic-link library to support the Windows Sockets interface.

For an overview of the relationship between a transport driver's Windows Sockets helper DLL and higher layers of the Windows Sockets interface, see the Network Driver Design Guide.

The functions described next are in alphabetic order.


WSHAddressToString

INT
WSHAddressToString(
IN LPSOCKADDR Address,
 IN INT AddressLength,
 IN LPWSAPROTOCOL_INFOW ProtocolInfo OPTIONAL,
 OUT LPWSTR AddressString,
 IN OUT LPDWORD AddressStringLength
 );

WSHAddressToString returns a logical string representation of a socket address that is generally used for display purposes.

Parameters

Address
Points to a SOCKADDR to convert.
AddressLength
Specifies the number of bytes at Address.
ProtocolInfo
Points to a WSAPROTOCOL_INFOW structure passed in a user-mode call to WSAAddressToString, or is NULL.
AddressString
Points to a caller-provided buffer into which the helper DLL should copy the logical string representation of the address.
AddressStringLength
On input, points to a variable specifying the length in bytes of the buffer provided at AddressString. On return, the helper DLL should reset the value to the number of bytes it actually copied into the buffer at AddressString.

Return Value

WSHAddressToString either returns zero to indicate success or one of the following:

WSAEFAULT
The buffer specified at AddressString is too small to copy the logical representation of the address.
WSAEINVAL
The address specified at Address is not a valid address to be translated.

See Also

WSHStringToAddress


WSHEnumProtocols

INT
WSHEnumProtocols(
IN LPINT lpiProtocols OPTIONAL,
IN LPWSTR lpTransportKeyName,
IN OUT LPVOID lpProtocolBuffer,
 IN OUT LPDWORD lpdwBufferLength
 );

WSHEnumProtocols returns a set of PROTOCOL_INFO structures that inform the caller of the Win32 function EnumProtocols about protocols supported by this helper DLL.

Parameters

lpiProtocols
Optionally specifies a filter selecting information to be returned about the protocols supported by the helper DLL. If this is NULL, the helper DLL should return information for all protocols it supports.

Otherwise, lpiProtocols points to a zero-terminated array of protocol ID numbers. WSHEnumProtocols should return information only on protocols whose ID appears in the lpiProtocols array. Protocol ID numbers are the third parameter to the socket call (for example, IPPROTO_TCP).

lpTransportKeyName
Points to a buffer that contains the name of the registry key that Windows Sockets uses to access this helper DLL. This parameter can be irrelevant; it is useful only if the helper DLL supports protocols that set up several registry keys.
lpProtocolBuffer
Points to the output buffer in which WSHEnumProtocols returns the requested set of PROTOCOL_INFO structures. The PROTOCOL_INFO structures must be copied into this buffer as a contiguous array starting at the beginning of the buffer. Variable-length data, such as the protocol name pointed to by the PROTOCOL_INFO structure’s lpProtocol member must be written at the end of the buffer.
lpdwBufferLength
On input, points to a variable specifying the total length in bytes of the buffer at lpProtocolBuffer. On output, this variable is set to the number of bytes of information returned or available from WSHEnumProtocols for this call. If the given input buffer is too small, WSHEnumProtocols sets the value at lpdwBufferLength to the number of bytes of available information before it returns control.

Return Value

WSHEnumProtocols either returns a count of PROTOCOL_INFO structures written to the buffer at lpProtocolBuffer, or it returns SOCKET_ERROR if there was an error. If -1 is returned, WSHEnumProtocols must call SetLastError to set the Windows Sockets error code of the error which occurred.


WSHGetBroadcastSockaddr

INT
WSHGetBroadcastSockaddr(
IN PVOID HelperDllSocketContext,
OUT PSOCKADDR Sockaddr,
OUT PINT SockaddrLength
);

WSHGetBroadcastSockaddr obtains a valid broadcast address for a socket.

Parameters

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket2 function.
Sockaddr
Points to a buffer in which WSHGetBroadcasrSockaddr returns a valid broadcast address, formatted as a SOCKADDR structure.
SockaddrLength
Points to a variable in which WSHGetBroadcastSockaddr returns the number of bytes it set in the SOCKADDR structure.

Return Value

WSHGetBroadcastSockaddr returns NO_ERROR to indicate success, or WSAEFAULT to indicate that the buffer at Sockaddr is insufficient in size to contain a full SOCKADDR structure.


WSHGetProviderGuid

INT
WSHGetProviderGuid(
IN LPWSTR ProviderName,
OUT LPGUID ProviderGuid
);

WSHGetProviderGuid returns the GUID that identifies the protocols supported by the helper DLL. This function is called only during setup of the WSH DLL.

Parameters

ProviderName
Points to a zero-terminated string containing the name of the provider.
ProviderGuid
Points to a variable in which WSHGetProviderGuid returns the GUID that identifies the protocols supported by the helper DLL. A GUID can be generated through UUIDGEN.EXE from the Win32 SDK. See the Win32 SDK for more information regarding this utility.

Return Value

WSHGetProviderGuid returns NO_ERROR for success or WSAEINVAL if the string at ProviderName does not designate this helper DLL.


WSHGetSockaddrType

INT
WSHGetSockaddrType(
IN PSOCKADDR Sockaddr,
IN DWORD SockaddrLength,
OUT PSOCKADDR_INFO SockaddrInfo
);

WSHGetSockaddrType parses a socket address and returns information about it to Windows Sockets.

Parameters

Sockaddr
Points to the SOCKADDR structure to be evaluated.
SockaddrLength
Specifies the length, in bytes, of the SOCKADDR structure at Sockaddr.
SockaddrInfo
Points to a buffer in which the helper DLL returns information about the socket address, formatted as a SOCKADDR_INFO structure.

Return Value

WSHGetSockaddrType returns zero if the given socket address is valid for the address family supported by this helper DLL. Otherwise, it returns a Windows Sockets error code.

Comments

Windows Sockets calls WSHGetSockaddrType when it needs information about a SOCKADDR. The helper DLL evaluates both the machine address and endpoint portions of the SOCKADDR. The machine-address portion specifies a particular machine, for example, the IP address in an internet address. The endpoint specifies a particular service on a machine, corresponding to the port number in an internet address.

The SOCKADDR_INFO structure is defined as follows:

typedef struct _SOCKADDR_INFO {
    SOCKADDR_ADDRESS_INFO AddressInfo;
    SOCKADDR_ENDPOINT_INFO EndpointInfo;
} SOCKADDR_INFO, *PSOCKADDR_INFO;

SOCKADDR_ADDRESS_INFO is an enumerated type, with the following possible values:

SockaddrAddressInfoNormal
The address portion of the SOCKADDR has no unusual characteristics.
SockaddrAddressInfoWildcard
The address portion of the SOCKADDR is a “wildcard,” meaning that it does not specify a particular address. For example, this corresponds to INADDR_ANY (0.0.0.0) for IP addresses, which an application passes to bind when it has no particular requirements for the IP address used for a socket.
SockaddrAddressInfoBroadcast
The address portion of the SOCKADDR refers to the address family’s broadcast address. For example, in IP addresses, this is INADDR_BROADCAST (255.255.255.255).
SockaddrAddressInfoLoopback
The address portion of the SOCKADDR refers to the address family’s loopback address. For example, in internet addresses, this is INADDR_LOOPBACK (127.0.0.1).

SOCKADDR_ENDPOINT_INFO also is an enumerated type, with the following possible values:

SockaddrEndpointInfoNormal
The endpoint portion of the SOCKADDR has no unusual characteristics.
SockaddrEndpointInfoWildcard
The endpoint portion of the SOCKADDR is a wildcard, meaning that it could refer to any endpoint. An application, typically a client, can pass a wildcard endpoint to bind when it has no particular requirements for the endpoint assigned. For example, in IP addresses, a port set to zero is a wildcard endpoint.
SockaddrEndpointInfoReserved
The endpoint of the SOCKADDR is reserved, meaning that special privilege is required to bind to it. For example, in internet addresses, a port with a value less than IPPORT_RESERVED (1024) is a reserved endpoint.


WSHGetSocketInformation

INT
WSHGetSocketInformation(
IN PVOID HelperDllSocketContext,
IN SOCKET SocketHandle,
IN HANDLE TdiAddressObjectHandle,
IN HANDLE TdiConnectionObjectHandle,
IN INT Level,
IN INT OptionName,
OUT PCHAR OptionValue,
OUT INT OptionLength
);

WSHGetSocketInformation is called when getsockopt is passed an option that Windows Sockets does not explicitly handle.

Parameters

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket or WSHOpenSocket2 function.
SocketHandle
Specifies the handle to the socket returned by socket or accept.
TdiAddressObjectHandle
Specifies the file object handle representing an open transport address associated with the socket, or NULL if the socket is not yet bound to an address. A nonNULL handle allows the WSH DLL to perform operations directly with the transport through the use of this handle.
TdiConnectionObjectHandle
Specifies the file object handle representing an open connection endpoint associated with the socket, or NULL if the socket is a datagram socket or is not yet connected. A nonNULL handle allows the helper DLL to perform operations directly with the transport through the use of this handle.
Level
Specifies the level passed in the getsockopt call.
OptionName
Specifies the optname passed in the getsockopt call. If this is NULL, the Windows Sockets DLL is simply querying the length of the helper DLL’s context information for the socket.
OptionValue
Points to a buffer in which WSHGetSocketInformation returns the requested information.
OptionLength
Points to a variable in which WSHGetSocketInformation returns the number of bytes transferred into the buffer at OptionValue.

Return Value

WSHGetSocketInformation returns zero if it successfully interprets the given option and carries out whatever operation it requires. Otherwise it returns a Windows Sockets error code, the getsockopt call is failed, and its return is set to the error returned by this helper DLL function.

Comments

WSHGetSocketInformation can provide support for arbitrary options beyond those defined by Windows Sockets. Certain defined options also are passed to the helper DLL because of the different ways in which transport drivers provide support for these options, in particular for the SO_KEEPALIVE and SO_DONTROUTE options.

Any option passed in an application call to getsockopt that the Windows Sockets DLL does not recognize is forwarded to WSHGetSocketInformation. The Windows Sockets DLL relies on each helper DLL to fail calls for which the given option is invalid or unsupported by the helper DLL.

WSHGetSocketInformation and WSHSetSocketInformation are also used to support socket handles shared between separate processes. However, the context information maintained by a helper DLL is not automatically shared between processes because virtual memory in a process is private to that process unless both processes set up shared memory.

To support shared socket handles, the Windows Sockets DLL calls WSHGetSocketInformation with the Level SOL_INTERNAL and the OptionName SO_CONTEXT. If necessary, the Windows Sockets DLL calls WSHGetSocketInformation twice: first to determine how large a buffer to allocate for context information and again to get the context information.

Windows Sockets allocates a buffer of the size of the helper DLL's context for the socket before it calls WSHGetSocketInformation with a pointer to the allocated buffer at OptionValue. This call requests WSHGetSocketInformation to retrieve all context information the helper DLL has for the socket. The helper DLL copies its context information into the buffer at OptionValue and returns.

When a socket handle is valid in a new process, either by inheritance of the handle or a call to the Win32 function DuplicateHandle, Windows Sockets first calls WSHOpenSocket or WSHOpenSocket2 in the normal manner to inform the helper DLL that a new socket is being opened. Next Windows Sockets calls WSHSetSocketInformation with the OptionLevel SOL_INTERNAL, the OptionName SO_CONTEXT, and the context information returned by WSHGetSocketInformation in the OptionValue buffer. WSHSetSocketInformation uses the supplied context information to recreate the socket context for the new process.

See Also

WSHOpenSocket, WSHOpenSocket2, WSHSetSocketInformation


WSHGetWildcardSockaddr

INT
WSHGetWildcardSockaddr(
IN PVOID HelperDllSocketContext,
OUT PSOCKADDR Sockaddr,
OUT PINT SockaddrLength
);

WSHGetWildcardSockaddr returns a socket address that can be used to bind a socket to an arbitrary or wildcard address.

Parameters

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket or WSHOpenSocket2 function.
Sockaddr
Points to a buffer in which the helper DLL returns the socket address, formatted in a SOCKADDR structure.
SockaddrLength
Specifies the length in bytes of the buffer at Sockaddr.

Return Value

WSHGetWildcardSockaddr returns zero if it returned an address at Sockaddr. If it could not determine a value for the wildcard address, it returns a WSAXXX error code.

Comments

Windows Sockets calls WSHGetWildcardSockaddr when it needs to perform an automatic bind of a socket. Such a bind is necessary when an application, typically a client-side application, requests an operation that requires a handle to a file object representing a transport address but that application has not bound the socket to an address.

For example, an application that calls either connect or sendto without having called bind causes a call to WSHGetWildcardSockAddr because the Windows Sockets DLL must bind the socket to an arbitrary local address. In these circumstances, WSHGetWildcardSockaddr returns any address that can be used for this bind, while Windows Sockets uses the returned address.

If a transport does not support automatic binds to an arbitrary address, the helper DLL's WSHGetWildcardSockaddr either can fail all calls or the helper DLL can simply not export this function. Such a tranport requires Windows Sockets applications running over that transport to always bind before making any calls to Windows Sockets functions that require a bound socket.


WSHGetWinsockMapping

DWORD
WSHGetWinsockMapping(
OUT PWINSOCK_MAPPING Mapping,
IN DWORD MappingLength
);

WSHGetWinSockMapping returns information about the address family, socket type, and protocol parameter triples supported by this helper DLL.

Parameters

Mapping
Points to a WINSOCK_MAPPING-structured buffer in which WSHGetWinsockMapping returns information that describes the parameters supported by this helper DLL.
MappingLength
Specifies the size in bytes of the buffer at Mapping.

Return Value

WSHGetWinsockMapping returns the number of bytes of WINSOCK_MAPPING information used by this helper DLL. If the given MappingLength is too small, the return value indicates how large a buffer is necessary to contain the helper DLL's WINSOCK_MAPPING information.

Comments

WSHGetWinsockMapping returns information about the types of sockets supported by this helper DLL. The WINSOCK_MAPPING structure is defined as follows:

typedef struct WINSOCK_MAPPING {
    DWORD Rows;
    DWORD Columns;
    struct {
        DWORD AddressFamily;
        DWORD SocketType;
        DWORD Protocol;
    } Mapping[1];
} WINSOCK_MAPPING, *PWINSOCK_MAPPING;

Members of this structure contain the following information:

Rows
Specifies the number of entries in the Mapping array.
Columns
Specifies three.
Mapping
Is an array with Rows elements, each of which specifies an address family, socket type, and protocol triple supported by the helper DLL.

The Mapping returned should be exactly the same as the value stored under the transport driver's ..Parameters\Winsock registry key for Mapping. The setup utility routines mentioned in the Network Driver Design Guide actually call WSHGetWinsockMapping to obtain the mapping information to be written into the registry.


WSHGetWSAProtocolInfo

INT
WSHGetWSAProtocolInfo(
IN LPWSTR ProviderName,
OUT LPWSAPROTOCOL_INFOW *ProtocolInfo,
OUT LPDWORD ProtocolInfoEntries
);

WSHGetWSAProtocolInfo returns a pointer to a WSAPROTOCOL_INFOW structure, which describes the characteristics of the protocol(s) supported by this helper DLL. This function is only called during setup of the WSH DLL.

Parameters

ProviderName
Points to a buffered zero-terminated string specifying the name of the provider.
ProtocolInfo
Points to a buffer in which WSHGetWSAProtocolInfo returns the characteristics of the protocol, formatted as a WSAPROTOCOL_INFOW structure.
ProtocolInfoEntries
Points to a variable in which WSHGetWSAProtocolInfo returns the number of entries in the array returned at ProtocolInfo.

Return Value

WSHGetWSAProtocolInfo returns zero if the operation was successful. Otherwise, WSHGetWSAProtocolInfo returns WSAEFAULT to indicate a problem with the input buffer parameters.


WSHIoctl

INT
WSHIoctl(
IN PVOID HelperDllSocketContext,
IN SOCKET SocketHandle,
IN HANDLE TdiAddressObjectHandle,
IN HANDLE TdiConnectionObjectHandle,
IN DWORD IoControlCode,
IN LPVOID InputBuffer,
IN DWORD InputBufferLength,
IN LPVOID OutputBuffer,
IN DWORD OutputBufferLength,
OUT LPDWORD NumberOfBytesReturned,
IN LPWSAOVERLAPPED Overlapped,
IN LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine,
OUT LPBOOL NeedsCompletion
);

WSHIoctl returns information or carries out a particular operation specified by the given IoControlCode.

Parameters

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket or WSHOpenSocket2 function.
SocketHandle
Specifies a handle to the socket that is the target of the requested operation.
TdiAddressObjectHandle
Specifies the handle to a file object representing the open transport address for the socket. If the socket is not currently bound to an address, this parameter is NULL.
TdiConnectionObjectHandle
Specifies the handle to a file object representing the connection endpoint associated with the socket. If the socket is not currently connected, this parameter is NULL.
IoControlCode
Specifies an SIO_XXX code requesting a specific operation. The following are possible system-defined IoControlCodes:
SIO_FIND_ROUTE
Requests that the route to this socket be determined. InputBuffer will be NULL. However, OutputBuffer must not be null.
SIO_FLUSH
Requests that the current contents of the send queue for the socket be flushed. InputBuffer and OutputBuffer will be NULL.
SIO_MULTIPOINT_LOOPBACK
Requests that, whenever data is sent on a multipoint session socket, the data also be sent to the socket on the local host if the data in InputBuffer is TRUE. Otherwise, the socket is requested to terminate that behavior.
SIO_MULTICAST_SCOPE
Sets, for the socket specified, wheter data sent on a multipoint sessions socket will cross routers. If the InputBuffer contains zero, no data should be sent except to multipoint session members on the local host. The value one indicates that no routers should be crossed. Values greater than one indicate that n-1 routers may be crossed when data is sent.

A helper DLL also can define its own codes.

InputBuffer
Points to an input buffer for the operation requested by the given IoControlCode.
InputBufferLength
Specifies the length in bytes of the buffer at InputBuffer.
OutputBuffer
Points to an output buffer for the operation requested by the given IoControlCode. This buffer is used to return information or query results to the caller.
OutputBufferLength
Specifies the length in bytes of the buffer at OutputBuffer.
NumberOfBytesReturned
On return, specifies the number of bytes copied into the buffer at OutputBuffer.
Overlapped
Specifies a pointer to a WSAOVERLAPPED structure to facilitate asynchronous I/O.
CompletionRoutine
Specifies a pointer to a WSAOVERLAPPED_COMPLETION_ROUTINE provided by the caller.
NeedsCompletion
On output, specifies whether the service provider should perform I/O completion operations for overlapped requests.

Return Value

WSHIoctl returns zero to indicate successful operation. Otherwise, it returns a Winsock Error code, as outlined in the following:
WSAENETDOWN

Specifies that the network has failed.

WSAEFAULT
Specifies that one or more of the buffers at InputBuffer or OutputBuffer are too small for successful operation.
WSAEINVAL
Specifies that the IoControlCode is not a valid command or that a supplied parameter is invalid.
WSAEINPROGRESS
Specifies that WSHIoctl was called while there was a completion routine callback in progress. This error can be returned only if the helper DLL handles I/O completion of overlapped requests itself.
WSAEOPNOTSUPP
Specifies that a normally valid IoControlCode is inappropriate to this socket. This generally indicates that the requested operation is not supported by the underlying protocol.
WSA_IO_PENDING
Specifies that the request is still in progress and will be completed at a later time. This error code can be returned only if overlapped information has been supplied at Overlapped or OverlappedRoutine.
WSAEWOULDBLOCK
Specifies that this operation would block the socket but the socket has been marked as a non-blocking socket.

Comments

WSHIoctl is called by the service provider to satisfy several types of requests. In addition, it is possible for a helper DLL to support operations designated by privately defined I/O control codes specific to a protocol that the helper DLL supports. Such privately defined codes will be transmitted to the helper DLL by WSAIoctl. For more information on WSAIoctl or custom control codes, see the Win32 SDK.

Because some operations that are normally requested through WSHIoctl can require a significant amount of time to complete, an overlapped I/O mechanism is provided. A caller either specifies a WSAOVERLAPPED structure in Overlapped or specifies a callback routine in OverlappedRoutine.

When such an I/O operation is completed, the helper DLL can either handle I/O completion in the overlapped manner itself, or allow the service provider to handle such operations. This is controlled by the NeedsCompletion parameter.

If the helper DLL chooses to handle the I/O completion itself, the following guidelines apply:

  • If a completion routine is specified in OverlappedRoutine, the structure provided at Overlapped should be ignored.

  • If a completion routine is not specified, then the Overlapped parameter is used. The event handle provided in this structure should be signalled to indicate to the caller that the I/O has completed.

  • When calling a completion routine provided at OverlappedRoutine, the number of bytes transferred, normally returned in NumberOfBytesReturned and an error code (zero indicating success) must be passed as the defined parameters to this caller-supplied I/O completion routine.


WSHJoinLeaf

INT
WSHJoinLeaf(
IN PVOID HelperDllSocketContext,
IN SOCKET SocketHandle,
IN HANDLE TdiAddressObjectHandle,
IN HANDLE TdiConnectionObjectHandle,
IN PVOID LeafHelperDllSocketContext,
IN SOCKET LeafSocketHandle,
IN PSOCKADDR Sockaddr,
IN DWORD SockaddrLength,
IN LPWSABUF CallerData,
IN LPWSABUF CalleeData,
IN LPQOS SocketQOS,
IN LPQOS GroupQOS,
IN DWORD Flags
 );

WSHJoinLeaf performs any protocol-specific actions that must be taken to add a socket to a multipoint session as a leaf node.

Parameters

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket or WSHOpenSocket2 function.
SocketHandle
Specifies the handle to the socket that was used to establish a multipoint session.
TdiAddressObjectHandle
Specifies the handle to a file object representing the open transport address for the socket.
TdiConnectionObjectHandle
Specifies the handle to a file object representing the connection endpoint associated with the socket. If the socket is not currently connected, this parameter is NULL.
LeafHelperDllSocketContext
Points to a helper DLL-defined socket context for a newly created socket (created by WSHOpenSocket2) that is being added as a new leaf node on the multipoint session.
LeafSocketHandle
Specifies the handle to the newly created socket that is being added to the multipoint session.
Sockaddr
Points to the value from WSAJoinLeaf for the name of the peer socket with which the leaf will be merged.
SockaddrLength
Specifies the length in bytes at Sockaddr.
CallerData
Points to a buffer containing data that should be transmitted to the peer socket when the sockets are joined.
CalleeData
Points to a buffer in which WSAJoinLeaf returns any data transmitted by the peer socket when the sockets are joined.
SocketQOS
Points to a quality-of-service flow specification for the multipoint session of the original socket as specified by the given SocketHandle. This parameter specifies quality of service for each direction of data.
GroupQOS
Points to a quality-of-service flow specification for the socket group. This specification cannot be used to change the QOS of the socket group, but can be used to modify the newly joined socket's QOS as part of that group.
Flags
Specifies whether the socket being created as a new leaf node acts as a sender, receiver, or both as one of the following:
JL_SENDER_ONLY
The new leaf node will act as a sender of data only.
JL_RECEIVER_ONLY
The new leaf node will act as a receiver of data only
JL_BOTH
The new leaf node will act as both a sender and receiver of data.

Return Value

WSHJoinLeaf returns zero if the input parameters are valid and the function successful. Otherwise, it returns a nonzero value (see Comments) corresponding to a Windows Sockets error code, defined in winsock2.h.

Comments

WSHJoinLeaf is responsible for performing any protocol-specific actions required to add a new socket to a multipoint session, including verifying parameters for applicability to the protocol being supported. WSHJoinLeaf must verify all parameters because no parameter checking is done between the call to WSAJoinLeaf and WSHJoinLeaf.

When the multipoint session is established, the helper DLL should transmit any data specified at CallerData to the peer-level socket when the connection is made. Any data returned by the peer socket should be copied into the buffer at CalleeData. If the protocol does not support the transmission of caller-supplied data and CallerData is nonNULL, the helper DLL should return WSAEPROTONOSUPPORT.

If the buffer sizes for CalleeData, SocketQOS, or GroupQOS are too small, then the error WSAEFAULT should be returned.

If a quality-of-service specification is provided at SocketQOS or GroupQOS but quality of service is not supported by the protocol, WSAEOPNOTSUPP should be returned. This error should also be returned if the given quality of service specification(s) cannot be met.

If an attempt to allocate memory to complete the join request fails, WSHJoinLeaf should return WSAENOBUFS to indicate that a low memory condition exists.

See Also

WSHOpenSocket2


WSHNotify

INT
WSHNotify(
IN PVOID HelperDllSocketContext,
IN SOCKET SocketHandle,
IN HANDLE TdiAddressObjectHandle,
IN HANDLE TdiConnectionObjectHandle,
IN DWORD NotifyEvent
);

WSHNotify is called by the Windows Sockets DLL when a socket-state transition occurs for which the helper DLL's WSHOpenSocket or WSHOpenSocket2 function already requested notifications.

Parameters

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket or WSHOpenSocket2 function.
SocketHandle
Specifies the handle to the socket returned by socket or accept.
TdiAddressObjectHandle
Specifies the handle to a file object representing the open transport address for the socket. If the socket is not currently bound to an address, this parameter is NULL.
TdiConnectionObjectHandle
Specifies the handle to a file object representing the connection endpoint associated with the socket. If the socket is not currently connected, this parameter is NULL.
NotifyEvent
Specifies the state change that just occurred. Exactly one bit of the WSH_NOTIFY_XXX values, defined in wsahelp.h, is set in this parameter at each call to WSHNotify. The WSH_NOTIFY_XXX values include the following:
WSH_NOTIFY_BIND
A successful call to bind occurred.
WSH_NOTIFY_LISTEN
A successful call to listen occurred.
WSH_NOTIFY_ACCEPT
A socket handle is being returned from a call to accept.
WSH_NOTIFY_CONNECT
A successful call to connect occurred.
WSH_NOTIFY_SHUTDOWN_RECEIVE
A successful call to shutdown for the receive side of the socket occurred.
WSH_NOTIFY_SHUTDOWN_SEND
A successful call to shutdown for the send side of the socket occurred.
WSH_NOTIFY_SHUTDOWN_ALL
A successful call to shutdown for both sides of the socket occurred.
WSH_NOTIFY_CLOSE
The socket is being closed.
WSH_NOTIFY_CONNECT_ERROR
A failed call to WSAConnect or connect.

The helper DLL's WSHOpenSocket(2) function already selected the set of possible events for which WSHNotify will be called from the preceding values.

Return Value

WSHNotify returns zero to confirm that the specified state-transition occurred. Otherwise, it returns a Windows Sockets error code. When the return value is nonzero, the application's call to the socket function that would cause a state transition fails and the error is set to the value returned by WSHNotify.

Because WSHNotify is called after all other aspects of a particular state-change succeeded, the socket can be left in a state where the only valid operation is to close the socket when WSHNotify returns an error.

Comments

WSHNotify serves two purposes:

  1. To allow a helper DLL to track the state of a socket

  2. To give a helper DLL an opportunity to support options requiring the socket to be in a specific state

For example, the Windows Sockets helper DLL for TCP/IP uses this routine to set a reminder to enable its keep-alive option when an application calls setsockopt before the socket is connected. In these circumstances, no TDI connection object exists when the keep-alive reminder is set, but the helper DLL can actually set the option as soon as the socket becomes connected.

Whenever a helper DLL's WSHNotify function receives a WSH_NOTIFY_CLOSE event, it can free any context it has set up for the socket. After a successful call to WSHNotify with this event, the Windows Sockets DLL never again calls the helper DLL with the same HelperDllSocketContext unless that helper DLL reuses the context areas it allocates. That is, the helper DLL returns the same value from a subsequent call to WSHOpenSocket or WSHOpenSocket2.

See Also

WSHOpenSocket, WSHOpenSocket2


WSHOpenSocket

INT
WSHOpenSocket(
IN OUT PINT AddressFamily,
IN OUT PINT SocketType,
IN OUT PINT Protocol,
OUT PUNICODE_STRING TransportDeviceName,
OUT PVOID HelperDllSocketContext,
OUT PDWORD NotificationEvents
);

WSHOpenSocket is called by the Windows Sockets DLL when opening a socket.

Parameter

AddressFamily
On input, propagates the address family specified in the call to socket. WSHOpenSocket resets this to the canonicalized value for the address family.

For example, if this is AF_UNSPEC on input, socket type is SOCK_STREAM, and protocol is IPPROTO_TCP, WSHOpenSocket resets this parameter to AF_INET.

SocketType
On input, propagates the socket type specified in the call to socket. WSHOpenSocket resets this to the canonicalized value for the socket type.

For example, if this is zero on input, address family is AF_INET, and protocol is IPPROTO_TCP, WSHOpenSocket resets this parameter to SOCK_STREAM.

Protocol
On input, this is set to the value specified in the call to socket. WSHOpenSocket resets this to the canonicalized value for the protocol.

For example, if this is zero on input, address family is AF_INET, and socket type is SOCK_STREAM, WSHOpenSocket resets this parameter to IPPROTO_TCP.

TransportDeviceName
Points to a Unicode counted string, specifying the name of the TDI transport that will handle the socket.
HelperDllSocketContext
Points to a variable that the helper DLL sets to a pointer to a context area that the helper DLL allocates. The Windows Sockets DLL treats this as a handle, passing it uninterpreted to the helper DLL in all subsequent calls to WSH Xxx functions involving the socket currently being opened. The helper DLL uses its context area to maintain state for tracking operations on the open socket.
NotificationEvents
Points to a bitmask variable that the helper DLL sets to specify the state-change events for which the Windows Sockets DLL should call the helper DLL's WSHNotify function. A helper DLL can request notifications by setting this variable to a combination (ORed) of the following, as defined in wsahelp.h:
WSH_NOTIFY_BIND
Call WSHNotify on a successful call to bind.
WSH_NOTIFY_LISTEN
Call WSHNotify on a successful call to listen.
WSH_NOTIFY_ACCEPT
Call WSHNotify when a socket handle is being returned from the accept function.
WSH_NOTIFY_CONNECT
Call WSHNotify on a successful call to connect.
WSH_NOTIFY_SHUTDOWN_RECEIVE
Call WSHNotify on a successful call to shutdown for the receive side of the socket.
WSH_NOTIFY_SHUTDOWN_SEND
Call WSHNotify on a successful call to shutdown for the send side of the socket.
WSH_NOTIFY_SHUTDOWN_ALL
Call WSHNotify on a successful call to shutdown for both sides of the socket.
WSH_NOTIFY_CLOSE
Call WSHNotify when the socket is being closed.
WSH_NOTIFY_CONNECT_ERROR
Call WSHNotify when on a failed called to connect occurred.

Return Value

WSHOpenSocket returns zero if the given parameters are valid. Otherwise, it returns a Windows Sockets error code. If its return value is nonzero, the socket or accept call fails and the error is set to the value returned by WSHOpenSocket.

Comments

The Windows Sockets DLL calls WSHOpenSocket whenever it is creating a new socket, either when an application calls socket or when a newly connected socket is being created by the accept function. If the helper DLL exports WSHOpenSocket2, it supersedes WSHOpenSocket.

WSHOpenSocket verifies and canonicalizes the address family, socket type, and protocol parameters so that the Windows Sockets DLL can depend on a unique triple for each type of socket.

WSHOpenSocket also allocates any necessary context structure to track subsequent operations on the open socket, and, through the NotificationEvents parameter, indicates to the Windows Sockets DLL the state-transition events at which the helper DLL does additional processing.

A WSH DLL must set NotificationEvents with WSH_NOTIFY_CLOSE if it allocates memory for per-socket context and returns a pointer at HelperDllSocketContext. Otherwise, a memory leak can occur.

See Also

WSHNotify, WSHOpenSocket2


WSHOpenSocket2

INT
WSHOpenSocket2(
IN OUT PINT AddressFamily,
IN OUT PINT SocketType,
IN OUT PINT Protocol,
IN GROUP Group,
IN DWORD Flags,
OUT PUNICODE_STRING TransportDeviceName,
OUT PVOID *HelperDllSocketContext,
OUT PDWORD NotificationEvents
);

WSHOpenSocket2 is called by the MSAFD support DLL in response to an application call to WSASocket.

Parameters

AddressFamily
On input, points to a variable that is set to the address family as specified in the WSASocket call. On output, the variable should be set to the canonicalized value for the address family.
SocketType
On input, points to a variable that is set to the socket type as specified in the WSASocket call. On output, the variable should be set to the canonicalized value for the socket type.
Protocol
On input, points to a variable that is set to the protocol as specified in the WSASocket call. On output, the variable should be set to the canonicalized value for the protocol.
Group
Identifies the group to which the socket that is being opened should belong if nonzero. (For more information about socket grouping, see the Win32 SDK.) Alternatively, this can be one of the following constants to create a new group of sockets.
SG_UNCONSTRAINED_GROUP
Create an new unconstrained group of sockets and have this socket become the first member of that group.
SG_CONSTRAINED_GROUP
Create a new constrained group of sockets and have this socket become the first member of that group.

MSAFD will handle all group mechanisms. The group number, if specified instead of one of the preceding flags, is provided for informational purposes.

Flags
Specifies attributes for the new socket that is to be created. (For more information about Windows Sockets 2 multipoint and root/leaf sockets, see the Win32 SDK.) The following lists valid flags that can be specified:
WSA_FLAG_OVERLAPPED
Indicates that a socket capable of handing overlapped I/O should be created. If this flag is set, overlapped I/O can be further utilized on calls to WSASend, WSASendTo, WSARecv, WSARecvFrom and WSAIoctl.
WSA_FLAG_MULTIPOINT_C_ROOT
Indicates that the socket should be created as a c_root in a multipoint session.
WSA_FLAG_MULTIPOINT_C_LEAF
Indicates that the socket should be created as a c_leaf in a multipoint session.
WSA_FLAG_MULTIPOINT_D_ROOT
Indicates that the socket should be created as a d_root in a multipoint session.
WSA_FLAG_MULTIPOINT_D_LEAD
Indicates that the socket should be created as a d_leaf in a multipoint session.
TransportDeviceName
Points to a buffered Unicode string that specifies the device name of the transport driver that will support this socket type. Storage for the UNICODE_STRING structure is allocated by the caller, and the string initialized with the transport device name, such as \\Device\\Tcp.
HelperDllSocketContext
Points to a variable in which WSHOpenSocket2 returns the address of a memory block that can be dynamically allocated. The returned pointer is passed to the helper DLL in all subsequent calls regarding the newly created socket. The helper DLL can use this block of memory to maintain per-socket information.
Notification Events
Points to a bitmask variable that the helper DLL sets to specify the state-change events for which the Windows Sockets DLL should call the helper DLL's WSHNotify function. A helper DLL can request notifications by setting this variable to a combination (ORed) of the following, as defined in wsahelp.h:
WSH_NOTIFY_BIND
Call WSHNotify on a successful call to bind.
WSH_NOTIFY_LISTEN
Call WSHNotify on a successful call to listen.
WSH_NOTIFY_ACCEPT
Call WSHNotify when a socket handle is being returned from the WSAAccept or accept function.
WSH_NOTIFY_CONNECT
Call WSHNotify on a successful call to WSAConnect or connect.
WSH_NOTIFY_SHUTDOWN_RECEIVE
Call WSHNotify on a successful call to shutdown for the receive side of the socket.
WSH_NOTIFY_SHUTDOWN_SEND
Call WSHNotify on a successful call to shutdown for the send side of the socket.
WSH_NOTIFY_SHUTDOWN_ALL
Call WSHNotify on a successful call to shutdown for both sides of the socket.
WSH_NOTIFY_CLOSE
Call WSHNotify when the socket is being closed.
WSH_NOTIFY_CONNECT_ERROR
Call WSHNotify when a socket fails a call to WSAConnect or connect occurred.

Return Value

WSHOpenSocket2 returns zero to indicate a successful creation of a socket. If this function returns a nonzero value, the corresponding call to WSASocket or WSAAccept fails. If so, the helper DLL should return an appropriate Windows Sockets error code as defined in winsock2.h.

Comments

WSHOpenSocket2 is called by the sockets service provider, such as MSAFD.DLL, to create a new socket. A new socket will be created by a call to WSASocket or by WSAAccept.

The helper DLL should canonicalize and verify the input parameters so that Windows Sockets 2 can rely on a unique triple for each type of socket. The parameters for creating a root or leaf depend on the underlying protocol.

Before it returns control, WSHOpenSocket2 should allocate necessary memory for a socket-specific context structure, set a notification bitmask, and set the name of the transport driver that handles this socket.

A WSH DLL must set NotificationEvents with WSH_NOTIFY_CLOSE if it allocates memory for per-socket context and returns a pointer at HelperDllSocketContext. Otherwise, a memory leak can occur.

See Also

WSHNotify


WSHSetSocketInformation

INT
WSHSetSocketInformation(
IN PVOID HelperDllSocketContext,
IN SOCKET SocketHandle,
IN HANDLE TdiAddressObjectHandle,
IN HANDLE TdiConnectionObjectHandle,
IN INT Level,
IN INT OptionName,
IN PCHAR OptionValue,
IN INT OptionLength
);

WSHSetSocketInformation is called by the Windows Sockets DLL whenever an application calls setsockopt with an option that Windows Sockets does not explicitly handle.

Parameter

HelperDllSocketContext
Points to a per-socket context area, allocated and initialized by the WSH DLL WSHOpenSocket or WSHOpenSocket2 function.
SocketHandle
Specifies the handle of the socket returned by socket or accept.
TdiAddressObjectHandle
Specifies the handle to a file object representing the open transport address for the socket. If the socket is not currently bound to an address, this parameter is NULL.
TdiConnectionObjectHandle
Specifies the handle to a file object representing the connection endpoint associated with the socket. If the socket is not currently connected, this parameter is NULL.
Level
Specifies the level passed in the setsockopt call.
OptionName
Specifies the optname passed in the setsockopt call.
OptionValue
Points to a buffer passed as the optval argument to setsockopt. This buffer contains caller-supplied data for the set operation.
OptionLength
Specifies the size in bytes of the buffer at OptionValue.

Return Value

WSHSetSocketInformation returns zero if it successfully interprets the given option and carries out whatever operation it requires. Otherwise it returns a Windows Sockets error code. If the return value is nonzero, the setsockopt call fails and the error is set to the value returned by WSHSetSocketInformation.

Comments

WSHSetSocketInformation can provide support for arbitrary options beyond those defined by Windows Sockets. Certain defined options also are passed to the helper DLL because of the different ways in which transport drivers provide support for these options, in particular for the SO_KEEPALIVE and SO_DONTROUTE options.

Any option passed in an application call to setsockopt that the Windows Sockets DLL does not explicitly support is forwarded to WSHSetSocketInformation. The Windows Sockets DLL relies on each helper DLL to fail calls for which the given option is invalid or unsupported by the helper DLL or underlying transport.

See Also

WSHGetSocketInformation


WSHStringToAddress

INT
WSHStringToAddress(
IN LPWSTR AddressString,
IN DWORD AddressFamily,
IN LPWSAPROTOCOL_INFOW ProtocolInfo OPTIONAL,
OUT LPSOCKADDR Address,
IN OUT LPDWORD AddressStringLength
);

WSHStringToAddress converts a logical string representation of a socket address into a SOCKADDR structure.

Parameters

AddressString
Points to a buffer containing the logical string representation of a socket address.
AddressFamily
Specifies the address family that AddressString belongs to.
Protocol Info
Points to a structure that contains protocol information or is NULL.
Address
Points to a buffer in which WSHStringToAddress returns the completed SOCKADDR structure that the AddressString represented.
AddressStringLength
On input, points to a variable containing the size of bytes of the buffer provided at Address. WSHStringToAddress resets the value to the number of bytes actually copied into Address.

Return Value

WSHStringToAddress returns zero if the function successfuly translated the given string into a SOCKADDR structure. Otherwise, WSHStringToAddress returns one of the following:

WSAEFAULT
The buffer provided at Address is too small to hold the translated SOCKADDR structure.
WSAEINVAL
The function was given a string it was unable to translate into a SOCKADDR structure.

Comments

WSHStringToAddress should make a best-effort attempt to convert any missing components of the supplied address string. For example, a missing port number could be interpreted as being a zero for certain protocols. If WSHStringToAddress cannot make a reasonable translation of the string, it returns WSAEINVAL.

See Also

WSHAddressToString


Appendix A Full-NIC Drivers

This contains reference information about NDIS V3.0 full-NIC drivers, including the following:

A.1 Revising Legacy Full-NIC Drivers

A.2 Functions Exported by Full-NIC Drivers

A.3 Summary of NDIS Library Functions for Full-NIC Drivers

A.4 NDIS Library Functions Called Only by Full-NIC Drivers

This material is intended to help driver developers revise existing Windows NT® full-NIC drivers into NDIS V4.0 miniport drivers. Subsequent releases of the Windows NT DDK are unlikely to include the information presented here.


A.1 Revising Legacy Full-NIC Drivers

While the NDIS V4.0 library will continue to support NDIS V3.0 full-NIC drivers for legacy netcards, only NDIS miniport drivers can take advantage of the enhanced functionality and performance characteristics of NDIS V4.0 for their NICs. Only NDIS miniport drivers can take advantage of the enhancements and functionality planned for future versions of the NDIS library.

Moreover, NDIS miniport drivers are essentially source-compatible across Microsoft operating systems that support Win32® while NDIS V3.0 full-NIC drivers are not. Except for minor differences in installation scripts and recompilation for the growing number of Windows NT platforms, an NDIS miniport driver can run in any Microsoft operating system that supports Win32 and on any machine with a NIC-compatible I/O bus that such an operating system controls.

The NDIS library handles a lot of work for miniports that full-NIC drivers must handle for themselves. For example, NDIS maintains state about all bindings to miniport drivers’ NICs so miniports need not support binding-specific queries.

Turning a legacy full-NIC driver into an NDIS miniport results in a smaller NIC driver, a more easily debugged and maintained NIC driver, and a more readily extensible NIC driver as the NDIS library adds functionality and support for new networks. For example, NDIS V4.0 adds support for full-duplex sends and receives in miniport drivers running on multiprocessor platforms, for multipacket sends and receives, and for wireless media types.

For more information about NDIS V4.0 miniport driver design and NDIS V4.0 support for miniports, see also the Network Drivers Design Guide.


A.2 Functions Exported by Full-NIC Drivers

This describes, in alphabetical order, the system-defined functions that legacy NDIS V3.0 full-NIC drivers provide. Each See Also section shows the corresponding MiniportXxx function, if there is one, as an aid to revising a full-NIC driver into a miniport that can take advantage of the enhanced functionality of NDIS V4.0 and of future versions of the NDIS library.

NDIS V3.0 full-NIC driver functions with the prefix Mac have metanames describing their respective basic functionality. Except for the DriverEntry function, full-NIC driver functions can have any name the driver writer chose. For easy debugging, most NDIS V3.0 full-NIC driver writers substituted a driver-specific prefix for the Mac prefix.

The initial entry point of any Windows NT kernel-mode driver has the explict name DriverEntry in order to be loaded automatically by the system. For more information about the general requirements for and functionality of Windows NT DriverEntry routines, see also the Kernel-Mode Driver Design Guide.

Initialization-Only and Pageable Driver Code

Windows NT full-NIC drivers mark their DriverEntry functions as initialization-only code, using the NDIS_INIT_FUNCTION macro.

Other full-NIC functions that neither acquire spin locks nor are called with spin locks already held can be marked as pageable code, using the NDIS_PAGABLE_FUNCTION macro. In general, pageable full-NIC driver functions include at least the following system-defined functions:

  • MacAddAdapter

  • MacRemoveAdapter

  • MacUnload

Other pageable functions include any driver internals that only pageable MacXxx functions call. Driver functions that run, even for the shortest possible interval, at IRQL >= DISPATCH_LEVEL cannot be pageable.


DriverEntry of NDIS Full-NIC Drivers

ULONG
DriverEntry(
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
);

DriverEntry is the initial entry point for Windows NT NDIS drivers, called automatically when the driver is loaded by the system.

Parameters

SystemSpecific1
Points to an OS-specific argument. For Windows NT drivers, this parameter is a DriverObject pointer.
SystemSpecific2
Points to an OS-specific argument. For Windows NT drivers, this parameter is a RegistryPath pointer.

Return Value

The DriverEntry function of a full-NIC driver usually returns the value returned by NdisRegisterMac.

Comments

DriverEntry first must call NdisInitializeWrapper, passing the pointers it was given as parameters to NdisInitializeWrapper. The returned NdisWrapperHandle is a required parameter for its subsequent call to NdisRegisterMac.

Next, DriverEntry calls NdisRegisterMac. If its call to NdisRegisterMac returns something other than NDIS_STATUS_SUCCESS, the driver will not remain loaded after DriverEntry returns control.

If NdisRegisterMac returns something other than NDIS_STATUS_SUCCESS, DriverEntry calls NdisTerminateWrapper and returns control with the error NDIS_STATUS_XXX returned by NdisRegisterMac or, possibly, with the general error NDIS_STATUS_FAILURE.

Otherwise, DriverEntry returns control with NDIS_STATUS_SUCCESS, and the driver’s MacAddAdapter function is called next to initialize and configure each NIC the driver supports.

DriverEntry runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

DriverEntry of NDIS Miniport Drivers, MacAddAdapter, NdisInitializeWrapper, NdisRegisterMac, NdisTerminateWrapper


MacAddAdapter

NDIS_STATUS
MacAddAdapter(
IN NDIS_HANDLE MacMacContext,
IN NDIS_HANDLE WrapperConfigurationContext,
 IN PNDIS_STRING AdapterName
 );

MacAddAdapter is called to add support for each network interface card on the network.

Parameters

MacMacContext
Points to a context area in which NDIS maintains state about the NIC.
WrapperConfigurationContext
Specifies the context that the NDIS library associates with registry information for the NIC. The DriverEntry function obtained this handle from its call to NdisInitializeWrapper.
AdapterName
Points to a string in the OS-default character set naming the NIC. For Windows NT drivers, this string is a counted, zero-terminated Unicode string. The NIC driver passes this name to NdisRegisterAdapter.

Return Value

MacAddAdapter can return the value returned by NdisRegisterAdapter or possibly one of the following status codes:

NDIS_STATUS_ADAPTER_NOT_FOUND
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS only if NdisRegisterAdapter returned this value

Comments

Using calls to Ndis Xxx configuration functions, such as NdisOpenConfiguration, NdisReadConfiguration, and NdisReadEisaSlotInformation, the NIC driver reads the hardware configuration information from the registry or from the I/O bus to determine the correct NIC parameters.

When it has collected the NIC configuration data it needs, MacAddAdapter calls NdisRegisterAdapter for the associated NIC, including the name of the logical adapter representing the NIC. This name allows NDIS to recognize the NIC when a protocol driver binds to the NIC using NdisOpenAdapter.

Other initialization functions the MacAddAdapter function calls can include NdisAllocateDmaChannel or NdisAllocateSharedMemory, NdisAllocateMemory, NdisInitializeInterrupt, and NdisMapIoSpace.

A successful call to NdisInitializeInterrupt enables calls to the NIC driver’s MacInterruptServiceRoutine. Unless MacAddAdapter disables interrupts on the NIC before it calls NdisInitializeInterrupt or its NIC cannot generate interrupts in its current state, the driver must be prepared to handle a device interrupt during initialization.

MacAddAdapter should register the driver’s MacShutdown function with NdisRegisterAdapterShutdownHandler.

MacAddAdapter also can initialize a filter library database with a call to EthCreateFilter, FddiCreateFilter, or TrCreateFilter, as appropriate to the medium type it supports.

If MacAddAdapter’s call to NdisRegisterAdapter succeeds but it subsequently fails to initialize its NIC or to allocate necessary resources to support its NIC, MacAddAdapter must release any resources it has already allocated, such as by calling NdisFreeDmaChannel or NdisFreeSharedMemory, NdisRemoveInterrupt, NdisUnmapIoSpace, NdisCloseConfiguration, NdisDeregisterAdapterShutdownHandler, and so forth. It also must call NdisDeregisterAdapter before it returns control with an error status.

If MacAddAdapter cannot recognize, configure, and initialize at least one NIC in the machine, the NIC driver will not remain loaded. If MacAddAdapter does not returns NDIS_STATUS_SUCCESS for at least one adapter, the NDIS library interprets this as a failure and the driver will not remain loaded.

MacAddAdapter runs at IRQL PASSIVE_LEVEL in the context of a system thread.

See Also

DriverEntry of NDIS Full-NIC Drivers, EthCreateFilter, FddiCreateFilter, MiniportInitialize, NdisAllocateDmaChannel, NdisAllocateMemory, NdisAllocateSharedMemory, NdisAllocateSpinLock, NdisDeregisterAdapter, NdisInitializeInterrupt, NdisMapIoSpace, NdisOpenAdapter, NdisOpenConfiguration, NdisPciAssignResources, NdisReadConfiguration, NdisReadEisaSlotInformation, NdisReadEisaSlotInformationEx, NdisReadMcaPosInformation, NdisReadNetworkAddress, NdisReadPciSlotInformation, NdisRegisterAdapter, NdisRegisterAdapterShutdownHandler, NdisWriteConfiguration, NdisWritePciSlotInformation, TrCreateFilter


MacChangeEthAddresses

NDIS_STATUS
 MacChangeEthAddresses(
 IN UINT OldAddressCount,
IN CHAR OldAddresses[][ETH_LENGTH_OF_ADDRESS],
IN UINT NewAddressCount,
IN CHAR NewAddresses[][ETH_LENGTH_OF_ADDRESS],
IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_REQUEST NdisRequest,
IN BOOLEAN Set
);

MacChangeEthAddresses is a NIC driver action function the Ethernet filter library calls when the multicast address list changes.

Parameters

OldAddressCount
Specifies the number of addresses in the array of old multicast addresses(OldAddresses).
OldAddresses[][ETH_LENGTH_OF_ADDRESS]
Specifies a two-dimensional array listing the old multicast addresses.
NewAddressCount
Specifies the number of addresses in the array of new multicast addresses (NewAddresses).
NewAddresses[][ETH_LENGTH_OF_ADDRESS]
Specifies a two-dimensional array listing the new multicast addresses.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisRequest
Points to a request structure.
Set
Specifies TRUE if changes are being caused by a set request rather than the closing of a binding.

Return Value

MacChangeEthAddresses can return the following status codes:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver supplied the entry point for MacChangeEthAddresses when it called EthCreateFilter to set up the Ethernet filter database.

EthChangeFilterAddresses, which the NIC driver calls after it acquires the filter database spin lock, actually calls MacChangeEthAddresses only after checking the current multicast address list for all bound protocols and determining that it requires changes.

If MacChangeEthAddresses returns anything other than NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING, EthChangeFilterAddresses discards any changes this function made to the multicast address list.

MacChangeEthAddresses runs at IRQL DISPATCH_LEVEL.

See Also

EthChangeFilterAddresses, EthCreateFilter, MacAddAdapter, NdisAcquireSpinLock


MacChangeFddiAddresses

NDIS_STATUS
 MacChangeFddiAddresses(
 IN UINT OldLongAddressCount,
IN CHAR OldLongAddresses[][FDDI_LENGTH_OF_LONG_ADDRESS],
IN UINT NewLongAddressCount,
IN CHAR NewLongAddresses[][FDDI_LENGTH_OF_LONG_ADDRESS],
IN UINT OldShortAddressCount,
IN CHAR OldShortAddresses[][FDDI_LENGTH_OF_SHORT_ADDRESS],
IN UINT NewShortAddressCount,
IN CHAR NewShortAddresses[][FDDI_LENGTH_OF_SHORT_ADDRESS],
IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_REQUEST NdisRequest,
IN BOOLEAN Set
);

MacChangeFddiAddresses is a NIC driver action function the FDDI filter library calls when the multicast long or short address list changes.

Parameters

OldLongAddressCount
Specifies the number of addresses in the array of old multicast long addresses (OldLongAddresses).
OldLongAddresses[][FDDI_LENGTH_OF_LONG_ADDRESS]
Specifies a two-dimensional array listing the old multicast long addresses.
NewLongAddressCount
Specifies the number of addresses in the array of new multicast long addresses (NewLongAddresses).
NewLongAddresses[][FDDI_LENGTH_OF_LONG_ADDRESS]
Specifies a two-dimensional array listing the new multicast long addresses.
OldShortAddressCount
Specifies the number of addresses in the array of old multicast short addresses (OldShortAddresses).
OldShortAddresses[][FDDI_LENGTH_OF_SHORT_ADDRESS]
Specifies a two-dimensional array listing the old multicast short addresses.
NewShortAddressCount
Specifies the number of addresses in the array of new multicast short addresses (NewShortAddresses).
NewShortAddresses[][FDDI_LENGTH_OF_SHORT_ADDRESS]
Specifies a two-dimensional array listing the new multicast short addresses.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisRequest
Points to a request structure.
Set
Specifies TRUE if changes are being caused by a set request rather than the closing of a binding.

Return Value

MacChangeFddiAddresses can return the following status codes:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver supplied the entry point for MacChangeFddiAddresses when it called FddiCreateFilter to set up the filter database.

FddiChangeFilterLongAddresses or FddiChangeFilterShortAddresses, which the NIC driver calls after it acquires the filter database spin lock, actually calls MacChangeFddiAddresses only after checking the current multicast long or short address list for all bound protocols and determining that it requires changes.

If MacChangeFddiAddresses returns anything other than NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING, FddiChangeFilter..Addresses discards any changes this function made to the corresponding multicast address list.

MacChangeFddiAddresses runs at IRQL DISPATCH_LEVEL.

See Also

FddiChangeFilterLongAddresses, FddiChangeFilterShortAddresses, FddiCreateFilter, MacAddAdapter, NdisAcquireSpinLock


MacChangeFilter

NDIS_STATUS
 MacChangeFilter(
 IN UINT OldFilterClasses,
IN UINT NewFilterClasses,
IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_REQUEST NdisRequest,
IN BOOLEAN Set
);

MacChangeFilter is called by an Ethernet, Token Ring, or FDDI filter library function to change a binding filter class.

Parameters

OldFilterClasses
Specifies the value representing the original combined filter classes.
NewFilterClasses
Specifies the value representing the new combined filter classes.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisRequest
Points to a request structure.
Set
Specifies TRUE if changes are being caused by a set request rather than the closing of a binding.

Return Value

MacChangeFilter can return the following status codes:

NDIS_STATUS_FAILURE
NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver supplied the entry point for MacChangeFilter when it called EthCreateFilter, FddiCreateFilter, or TrCreateFilter to set up the corresponding filter database.

The NIC driver first acquires the spin lock protecting the filter database and then calls one of the following functions, which, in turn, calls MacChangeFilter:

  • EthFilterAdjust or EthDeleteFilterOpenAdapter

  • FddiFilterAdjust or FddiDeleteFilterOpenAdapter

  • TrFilterAdjust or TrDeleteFilterOpenAdapter

The filter library also calls MacChangeFilter when it sets a filter class for the first time or when no more bindings require the filter class.

If MacChangeFilter returns anything other than NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING, the filter function discards any changes MacChangeFilter made.

MacChangeFilter runs at IRQL DISPATCH_LEVEL.

See Also

EthCreateFilter, EthDeleteFilterOpenAdapter, EthFilterAdjust, FddiCreateFilter, FddiDeleteFilterOpenAdapter, FddiFilterAdjust, MacAddAdapter, NdisAcquireSpinLock, TrCreateFilter, TrDeleteFilterOpenAdapter, TrFilterAdjust


MacChangeGroup

NDIS_STATUS
 MacChangeGroup(
 IN TR_GROUP_ADDRESS OldGroupAddress,
IN TR_GROUP_ADDRESS NewGroupAddress,
IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_REQUEST NdisRequest,
IN BOOLEAN Set
);

MacChangeGroup is a NIC driver action function the Token Ring filter library calls to initiate processing when the group address for all bindings has changed.

Parameters

OldGroupAddress
Specifies the original group address.
NewGroupAddress
Specifies the new group address.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding that causes the change.
NdisRequest
Points to a request structure.
Set
Specifies TRUE if changes are being caused by a set request rather than the closing of the binding.

Return Value

MacChangeGroup can return the following status codes:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver supplied the entry point for MacChangeGroup when it called TrCreateFilter to set up the Token Ring filter database.

TrChangeGroupAddress, which the NIC driver calls after it acquires the filter database spin lock, actually calls MacChangeGroup only after checking the current group address list for all bound protocols and determining that it requires changes.

If MacChangeGroup returns anything other than NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING, TrChangeGroupAddress discards any changes this function made to the group address.

MacChangeGroup runs at IRQL DISPATCH_LEVEL.

See Also

MacAddAdapter, NdisAcquireSpinLock, TrChangeGroupAddress, TrCreateFilter


MacChangeTrAddress

NDIS_STATUS
 MacChangeTrAddress(
 IN TR_FUNCTIONAL_ADDRESS OldFunctionalAddress,
IN TR_FUNCTIONAL_ADDRESS NewFunctionalAddress,
IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_REQUEST NdisRequest,
IN BOOLEAN Set
);

MacChangeTrAddress is a NIC driver action function the Token Ring filter library calls when the bitmap for the functional address changes for the NIC.

Parameters

OldFunctionalAddress
Specifies the old functional address.
NewFunctionalAddress
Specifies the new functional address.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisRequest
Points to a request structure.
Set
Specifies TRUE if changes are being caused by a set request rather than the closing of a binding.

Return Value

MacChangeTrAddress can return the following status codes:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver supplied the entry point for MacChangeTrAddress when it called TrCreateFilter to set up the Token Ring filter database.

TrChangeFunctionalAddress, which the NIC driver calls after it acquires the filter database spin lock, actually calls MacChangeTrAddress only after checking the current functional address list for all bound protocols and determining that it requires changes.

If MacChangeTrAddress returns anything other than NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING, TrChangeFunctionalAddress discards any changes this function made to the address list.

MacChangeTrAddress runs at IRQL DISPATCH_LEVEL.

See Also

MacAddAdapter, NdisAcquireSpinLock, TrChangeFunctionalAddress, TrCreateFilter


MacCloseAction

VOID
 MacCloseAction(
 IN NDIS_HANDLE MacBindingHandle
);

MacCloseAction is a NIC driver action function the Ethernet, Token Ring, or FDDI filter library calls when a binding is closed with active receive indications.

Parameters

MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.

Comments

The NIC driver supplied the entry point for MacCloseAction when it called EthCreateFilter, FddiCreateFilter, or TrCreateFilter to set up the corresponding filter database.

The NIC driver first acquires the spin lock protecting the filter database and then calls one of the following functions:

  • EthFilterIndicateReceive

  • FddiFilterIndicateReceive

  • TrFilterIndicateReceive

If a binding closes while the receive indication is being processed, NDIS calls MacCloseAction to decrement the driver’s reference count for that binding.

MacCloseAction runs at IRQL DISPATCH_LEVEL.

See Also

EthCreateFilter, EthFilterIndicateReceive, FddiCreateFilter, FddiFilterIndicateReceive, MacAddAdapter, NdisAcquireSpinLock, TrCreateFilter, TrFilterIndicateReceive


MacCloseAdapter

NDIS_STATUS
 MacCloseAdapter(
 IN NDIS_HANDLE MacBindingHandle
);

MacCloseAdapter is called when a protocol driver releases a binding with NdisCloseAdapter.

Parameters

MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.

Return Value

MacCloseAdapter can return the following status codes:

NDIS_STATUS_CLOSING
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

Because several protocol drivers can have open bindings to the same NIC, the NIC driver keeps a reference count of open handles. If any requests remain on a binding when a protocol driver calls NdisCloseAdapter, the function returns NDIS_STATUS_PENDING, regardless of the state of the network interface card.

MacCloseAdapter terminates a network interface card binding, cleaning up the state the driver maintains about that binding, such as decrementing the NIC driver’s count of open handles, and releasing the resources allocated when that binding was set up by MacOpenAdapter, unless MacCloseAdapter must handle the close as an asynchronous operation.

If MacCloseAdapter finishes the close operation, it returns a status code other than NDIS_STATUS_PENDING.

Otherwise, MacCloseAdapter handles the close as an asynchronous operation and returns NDIS_STATUS_PENDING. For example, the following circumstances should cause MacCloseAdapter to handle a close asynchronously:

  • Its call to the EthDeleteFilterOpenAdapter, FddiDeleteFilterOpenAdapter, or TrDeleteFilterOpenAdapter filter library function returned NDIS_STATUS_PENDING or NDIS_STATUS_CLOSING_INDICATING. These return values indicate that operations are pending on the NIC that require the close to be deferred until these operations complete.

  • The preceding call returned NDIS_STATUS_RESET_IN_PROGRESS. This return value indicates that NDIS should make both NDIS_STATUS_RESET_START and NDIS_STATUS_RESET_END calls to the ProtocolStatus function of the driver that initiated the close before the binding is released.

For asynchronous closes, the NIC driver eventually calls NdisCompleteCloseAdapter, which calls the ProtocolCloseAdapterComplete function of the driver that initiated the close. Asynchronous completion allows the ProtocolCloseAdapterComplete function to perform any necessary postprocessing of the NIC close request.

After the NIC driver has closed a binding, a protocol driver cannot submit further requests on that binding. In fact, a protocol driver cannot call any Ndis Xxx function with a binding handle that it already passed to NdisCloseAdapter.

MacCloseAdapter runs at IRQL DISPATCH_LEVEL.

See Also

EthDeleteFilterOpenAdapter, FddiDeleteFilterOpenAdapter, MacOpenAdapter, NdisCloseAdapter, NdisCompleteCloseAdapter, NdisReset, ProtocolCloseAdapterComplete, ProtocolStatus, TrDeleteFilterOpenAdapter


MacDeferredProcessingRoutine

VOID
 MacDeferredProcessingRoutine(
 IN PVOID SystemSpecific1,
IN PVOID InterruptContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
);

MacDeferredProcessingRoutine is called to complete interrupt processing that the driver’s MacInterruptServiceRoutine, which should return control as quickly as possible, does not.

Parameters

SystemSpecific1
Points to a first OS-specific value. In Windows NT drivers, this pointer is reserved for use by NDIS.
InterruptContext
Points to the context that the NIC driver associates with the adapter that generated the interrupt. The NIC driver provided this context area (MacAdapterContext) when it called NdisInitializeInterrupt during initialization.
SystemSpecific2
Points to a second OS-specific value. In Windows NT drivers, this pointer is reserved.
SystemSpecific3
Points to a third OS-specific value. In Windows NT drivers, this pointer is reserved.

Comments

MacDeferredProcessingRoutine runs at a lower hardware priority than MacInterruptServiceRoutine and typically executes immediately after the ISR returns control.

MacDeferredProcessingRoutine does all the work of interrupt handling except for determining whether the interrupt belongs to the network interface card and, if necessary, saving enough interrupt state information for the driver’s MacDeferredProcessingRoutine to complete the operation that caused the interrupt. Because it runs at DIRQL, the MacInterruptServiceRoutine handles only the minimum necessary operations before it returns control.

MacDeferredProcessingRoutine must be able to handle processing for any number of NIC interrupts unless the NIC driver’s MacInterruptServiceRoutine disables interrupts on the NIC. In such a driver, MacDeferredProcessingRoutine clears the state of the network interface card and re-enables interrupts, handling only the most recent device interrupt.

If MacDeferredProcessingRoutine shares resources, such as state, with other nonISR driver functions, it can call NdisDprAcquireSpinLock and NdisDprReleaseSpinLock to synchronize its access to the shared resources. Like any other driver function that shares resources with the ISR, MacDeferredProcessingRoutine must call NdisSynchronizeWithInterrupt when it needs to access resources shared with MacInterruptServiceRoutine.

MacDeferredProcessingRoutine runs at IRQL DISPATCH_LEVEL.

See Also

MacAddAdapter, MacInterruptServiceRoutine, MacSynchronizeFunction, MiniportHandleInterrupt, NdisDprAcquireSpinLock, NdisDprReleaseSpinLock, NdisInitializeInterrupt, NdisRegisterAdapter, NdisSynchronizeWithInterrupt


MacInterruptServiceRoutine

BOOLEAN
 MacInterruptServiceRoutine(
 IN PVOID InterruptContext
);

MacInterruptServiceRoutine is a full-NIC driver’s ISR, called when the NIC generates an interrupt.

Parameters

InterruptContext
Points to the context that the NIC driver associates with the interrupt. The NIC driver provided this context area (MacAdapterContext) when it called NdisInitializeInterrupt during initialization.

Return Value

MacInterruptServiceRoutine returns TRUE if its NIC generated the interrupt. TRUE also tells NDIS to queue a call to MacDeferredProcessingRoutine, so drivers of NICs that do not share IRQs always return TRUE from their ISRs.

Comments

If the driver called NdisInitializeInterrupt with the InterruptMode set to NdisInterruptLevelSensitive, its MacInterruptServiceRoutine must dismiss the interrupt on the NIC before it returns control. Otherwise, the interrupt remains active, and NDIS calls MacInterruptServiceRoutine repeatedly until the interrupt is dismissed on the NIC.

If the NIC shares an IRQ with other devices, MacInterruptServiceRoutine first determines whether its NIC generated the interrupt and returns FALSE immediately if another device generated the interrupt. Otherwise, MacInterruptServiceRoutine preserves enough state about the operation that caused the interrupt for the driver’s MacDeferredProcessingRoutine to complete the operation.

In other words, a NIC driver does the absolute minimum amount of work in its ISR. When this function returns control, NDIS calls the driver’s MacDeferredProcessingRoutine as soon as possible to complete interrupt processing at a lower hardware priority.
MacInterruptServiceRoutine runs at the DIRQL assigned by the system when the driver called NdisInitializeInterrupt. Consequently, MacInterruptServiceRoutine can call only a small subset of NDIS library functions, while the driver’s MacDeferredProcessingRoutine can call any system-supplied function that is safe to call from IRQL <= DISPATCH_LEVEL.

Because it runs at DIRQL, MacInterruptServiceRoutine can preempt any code that runs at lower IRQL, including most functions in the NDIS library and in the NIC driver itself. If a NIC interrupt can occur while a nonISR NIC driver function is accessing resources that MacInterruptServiceRoutine also could access if an interrupt occurs, the nonISR function must call NdisSynchronizeWithInterrupt to synchronize its access with the ISR.

MacInterruptServiceRoutine runs at DIRQL, which is > DISPATCH_LEVEL.

See Also

MacAddAdapter, MacDeferredProcessingRoutine, MacSynchronizeFunction, MiniportISR, NdisAcquireSpinLock, NdisInitializeInterrupt, NdisReleaseSpinLock, NdisSynchronizeWithInterrupt


MacOpenAdapter

NDIS_STATUS
 MacOpenAdapter(
 OUT PNDIS_STATUS OpenErrorStatus,
OUT NDIS_HANDLE *MacBindingHandle,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE NdisBindingContext,
IN NDIS_HANDLE MacAdapterContext,
IN UINT OpenOptions,
IN PSTRING AddressingInformation OPTIONAL
);

MacOpenAdapter is called when a protocol driver requests a binding to the NIC with NdisOpenAdapter.

Parameters

OpenErrorStatus
Points to the caller-supplied variable in which MacOpenAdapter returns an NDIS_STATUS_XXX indicating the reason for a failure to open the NIC if the open fails.
MacBindingHandle
Points to the caller-supplied variable in which MacOpenAdapter returns a handle that the NIC driver associates with the binding. This value is typically a pointer to a driver-defined structure in resident memory containing binding-specific information. All future requests that NDIS forwards from the protocol driver to the NIC driver specify this value if the request concerns this binding.
SelectedMediumIndex
Points to the caller-supplied variable in which this function returns the index of the NIC driver’s medium type, selected from the supplied values at MediumArray.
MediumArray
Points to an array of media types the protocol driver supports.
MediumArraySize
Specifies the number of elements in MediumArray.
NdisBindingContext
Specifies the context that NDIS associates with the binding. The NIC driver uses this handle when it sends an indication to the protocol driver.
MacAdapterContext
Specifies the context that the NIC driver associates with the NIC.
OpenOptions
Specifies a bitmask containing flags the protocol driver passes to the NIC driver. Currently, the bitmask is undefined.
AddressingInformation
Points to an optional variable-length string containing specific information the NIC driver can use to program the network interface card. A NIC driver supporting an asynchronous modem can use this information for dialing. The addressing information, if specified, remains valid until this function completes. This parameter can be NULL.

Return Value

MacOpenAdapter can return the following status codes:

NDIS_STATUS_ADAPTER_NOT_FOUND
NDIS_STATUS_FAILURE
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_OPEN_FAILED
NDIS_STATUS_OPEN_LIST_FULL
NDIS_STATUS_PENDING
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS
NDIS_STATUS_UNSUPPORTED_MEDIA

Comments

In its call to NdisOpenAdapter, the protocol driver specifies an array containing the media types that it supports. The NIC driver reads this array and writes the index of its own media type in SelectedMediumIndex.

A NIC driver can imitate a medium type other than the true underlying type of its NIC, and this imitation is transparent to the protocol driver. If the NIC driver cannot find a medium type that it and the protocol driver both support, MacOpenAdapter returns NDIS_STATUS_UNSUPPORTED_MEDIA.

When the protocol driver calls NdisOpenAdapter, MacOpenAdapter activates the NIC if it is not already physically enabled. Subsequently, the NIC driver does not disable the network interface card until its reference count for open bindings returns to zero. The maximum number of simultaneous opens the NIC driver supports can be configured.

Before MacOpenAdapter completes the open request, the filter library causes the NIC driver to reset all multicast and functional addresses for the newly bound protocol driver. The reset of filter database information has no effect on other protocol drivers that are also bound to the NIC.

MacOpenAdapter completes synchronously if it finishes the open operation before it returns control with a status code other than NDIS_STATUS_PENDING.

If MacOpenAdapter cannot complete the open operation before it returns control, it returns NDIS_STATUS_PENDING. For example, MacOpenAdapter usually returns NDIS_STATUS_PENDING at the initial open for its NIC. Then, the NIC driver eventually calls NdisCompleteOpenAdapter, which, in turn, calls the ProtocolOpenAdapterComplete function of the driver that initiated the open. This asynchronous completion allows protocol driver postprocessing of the NIC open request.

Whether MacOpenAdapter completes an open synchronously or asynchronously, it must set values for MacBindingHandle and SelectedMediumIndex before it returns control.

MacOpenAdapter runs at IRQL DISPATCH_LEVEL.

See Also

EthNoteFilterOpenAdapter, FddiNoteFilterOpenAdapter, NdisCompleteOpenAdapter, NdisOpenAdapter, ProtocolOpenAdapterComplete, TrNoteFilterOpenAdapter


MacQueryGlobalStatistics

NDIS_STATUS
 MacQueryGlobalStatistics(
 IN NDIS_HANDLE MacAdapterContext,
IN PNDIS_REQUEST NdisRequest
);

MacQueryGlobalStatistics is called by NDIS to query current NIC driver statistics in the MIB that the network interface card supports.

Parameters

MacAdapterContext
Specifies the context that the driver associates with its NIC.
NdisRequest
Points to a request structure that defines the query.

Return Value

MacQueryGlobalStatistics can return the following status codes:

NDIS_STATUS_INVALID_LENGTH
NDIS_STATUS_INVALID_OID
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

MacQueryGlobalStatistics allows the NDIS library to make calls on behalf of a network-management entity instead of a particular protocol driver bound to the network interface card.

Calls to MacQueryGlobalStatistics use the same OIDs as calls to MacRequest for the corresponding local query. For global queries, the OIDs identify the MIB. For a complete description of NDIS OIDs, see Part I.

MacQueryGlobalStatistics cannot call NdisCompleteRequest because a global statistics query is not specific to any particular bound protocol driver.

If MacQueryGlobalStatistics returns NDIS_STATUS_PENDING, it is asynchronous and the statistics query requires postprocessing. To initiate this postprocessing, the NIC driver later calls NdisCompleteQueryStatistics.

MacQueryGlobalStatistics runs at IRQL <= DISPATCH_LEVEL, usually at PASSIVE_LEVEL during initialization.

See Also

MacRequest, MiniportQueryInformation, NdisCompleteQueryStatistics, NdisRequest


MacRemoveAdapter

VOID
 MacRemoveAdapter(
 IN NDIS_HANDLE MacAdapterContext
);

MacRemoveAdapter is called by NDIS to remove a network interface card that the NIC driver has registered.

Parameters

MacAdapterContext
Specifies the context that the NIC driver associates with the network interface card.

Comments

When MacRemoveAdapter is called, NDIS guarantees that all open bindings for the NIC are closed and prevents further opens of the NIC.

MacRemoveAdapter releases the NIC-specific resources allocated by the MacAddAdapter function during initialization.

The driver rejects any incoming requests for the NIC being removed with NDIS_STATUS_ADAPTER_REMOVED. Resource removal includes disabling interrupts on the NIC, canceling any queued timers, and releasing all corresponding NIC-specific resources that the driver allocated, such as calling NdisRemoveInterrupt.

When the driver has finished releasing resources, it calls NdisDeregisterAdapter to complete the NIC-removal operation.

MacRemoveAdapter runs at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, MacInterruptServiceRoutine, NdisCancelTimer, NdisDeregisterAdapter, NdisRemoveInterrupt


MacRequest

NDIS_STATUS
 MacRequest(
 IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_REQUEST NdisRequest
);

MacRequest is called when a protocol driver calls NdisRequest to query or set NIC information in the underlying driver’s MIB.

Parameters

MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisRequest
Points to a request structure containing the OID identifying the object to query or set.

Return Value

MacRequest can return any of the following status codes:

NDIS_STATUS_FAILURE
NDIS_STATUS_INVALID_DATA
NDIS_STATUS_INVALID_LENGTH
NDIS_STATUS_INVALID_OID
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_NOT_SUPPORTED
NDIS_STATUS_PENDING
NDIS_STATUS_REQUEST_ABORTED
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

Comments

In its call to NdisRequest, the protocol driver passes an NDIS_REQUEST-type structure containing an OID for the MIB object.

The protocol-driver-supplied NDIS_REQUEST structure contains a substructure filled with data related to either querying information or setting information, depending on the RequestType specified. If the protocol driver calls NdisRequest with a query, MacRequest writes the requested information for the specified OID in the appropriate members of the NDIS_REQUEST structure. If the protocol driver requests a set, MacRequest uses the protocol-driver-supplied, OID-specific information from the NDIS_REQUEST structure to set the information for the NIC.

If the NDIS_REQUEST buffer is not large enough to return queried information, MacRequest writes the number of bytes needed for the data in the BytesNeeded member of NDIS_REQUEST and returns NDIS_STATUS_INVALID_LENGTH. It does not fill in the BytesWritten member. If a query request specifies an OID that the NIC driver does not support, MacRequest sets the BytesWritten member to zero and returns the appropriate status code.

For a set operation, MacRequest determines whether the protocol-supplied information is valid. If not, MacRequest writes the number of bytes processed in the BytesRead member of NDIS_REQUEST and returns NDIS_STATUS_INVALID_DATA.

For more information about system-defined OIDs and OID-specific data structures, see Part I.

MacRequest completes synchronously if it finishes processing the request before it returns with any status code other than NDIS_STATUS_PENDING. If MacRequest satisfies a given request, it fills the request’s BytesWritten or BytesRead member with the number of bytes transferred before it returns NDIS_STATUS_SUCCESS.

The driver completes such a request asynchronously if MacRequest returns NDIS_STATUS_PENDING. The NIC driver eventually calls NdisCompleteRequest, which, in turn, calls the ProtocolRequestComplete function of the driver that initiated the query or set request. This asynchronous completion allows protocol driver postprocessing of the completed request.

MacRequest runs at IRQL DISPATCH_LEVEL.

See Also

MacQueryGlobalStatistics, MiniportQueryInformation, MiniportSetInformation, NdisCompleteRequest, NDIS_REQUEST, NdisRequest, ProtocolRequestComplete


MacReset

NDIS_STATUS
 MacReset(
 IN NDIS_HANDLE MacBindingHandle
);

MacReset is called when a bound protocol driver calls NdisReset.

Parameters

MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.

Return Value

MacReset can return the following status codes:

NDIS_STATUS_FAILURE
NDIS_STATUS_HARD_ERRORS
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_NOT_RESETTABLE
NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SOFT_ERRORS
NDIS_STATUS_SUCCESS

Comments

NdisReset instructs the NIC driver to issue a hardware reset to its NIC and to reset its own software state. A call to NdisReset should not alter NIC parameters as viewed by a protocol driver, such as the packet filter and lookahead size that are in effect before the reset operation.

If a hardware reset changes the current station address, the NIC driver automatically restores the address following the reset operation. MacReset reprograms any multicast or functional addressing masks that it resets before it returns control by calling the appropriate filter library functions.

During reset processing, the NIC driver calls NdisIndicateStatus twice for each bound protocol driver. At the beginning of the reset, it calls NdisIndicateStatus with the general status code NDIS_STATUS_RESET_START. At the end of the reset, it must call NdisIndicateStatus with the general status code NDIS_STATUS_RESET_END.

The NIC driver must ensure that NdisIndicateStatus delivers NDIS_STATUS_RESET_START to all currently bound protocol drivers.

In some circumstances, the NIC driver can return NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING shortly after the NDIS_STATUS_RESET_START indication.

MacReset completes synchronously if it finishes the reset operation before it returns control with a status code other than NDIS_STATUS_PENDING.

If the NIC does not support a reset under software control, MacReset returns with NDIS_STATUS_NOT_RESETTABLE. If the NIC driver attempts a reset and finds recoverable hardware errors, MacReset logs the error(s) and completes successfully with NDIS_STATUS_SOFT_ERRORS. If the NIC resets with unrecoverable hardware errors, this function again logs the error(s) and returns NDIS_STATUS_HARD_ERRORS. Only if the NIC reset has no errors does MacReset return NDIS_STATUS_SUCCESS.

If it cannot complete reset operations before it returns control, MacReset returns NDIS_STATUS_PENDING. The NIC driver eventually calls NdisCompleteReset, which, in turn, calls the ProtocolResetComplete function of the driver that initiated the reset operation. This asynchronous completion allows protocol postprocessing of the reset request.

While MacReset is executing, other NIC driver functions must reject all incoming requests with NDIS_STATUS_RESET_IN_PROGRESS. During the reset operation, the NIC driver cancels pending requests with NDIS_STATUS_REQUEST_ABORTED and blocks new requests with NDIS_STATUS_RESET_IN_PROGRESS. These are the only status codes the driver can issue until it determines the reset operation is complete. When this occurs, the driver calls NdisIndicateStatus, which issues NDIS_STATUS_RESET_END to all bound protocol drivers. At this point, the NIC driver ceases to block incoming requests.

Exceptions to the preceding rules are calls to the driver’s MacOpenAdapter and MacCloseAdapter functions. These NIC driver functions usually return NDIS_STATUS_PENDING during resets and complete the open/close operation asynchronously.

See Also

MacCloseAdapter, MacOpenAdapter, MiniportReset, NdisCompleteReset, NdisIndicateStatus, NdisReset, NdisWriteErrorLogEntry, ProtocolResetComplete


MacSend

NDIS_STATUS
 MacSend(
 IN NDIS_HANDLE MacBindingHandle,
IN PNDIS_PACKET Packet
);

MacSend is called when a protocol driver calls NdisSend.

Parameters

MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
Packet
Points to a descriptor for the packet that the NIC driver transmits over the network.

Return Value

MacSend can return the following status codes:

NDIS_STATUS_FAILURE
NDIS_STATUS_INVALID_PACKET
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_NOT_COPIED
NDIS_STATUS_NOT_RECOGNIZED
NDIS_STATUS_PENDING
NDIS_STATUS_REQUEST_ABORTED
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS
NDIS_STATUS_UNAUTHORIZED

Comments

NdisSend instructs the NIC driver to send a packet through its NIC over the network. When the protocol driver provides a packet to NdisSend, it transfers ownership of both the packet descriptor and the packet data to MacSend.

MacSend configures the packet for transmission to the NIC in the same way as the packet data is configured. The overlying protocol driver determines packet requirements, based on the medium type of the underlying NIC driver. The only exception is padding, which the NIC driver can provide for any packet that is shorter than the minimum for the NIC medium. If MacSend pads a packet, it must explicitly initialize the padding to a known value, such as a blank.

A protocol driver (and higher-level network software components) should not create buffer descriptor chains that are unnecessarily long. However, there is no defined limit on the number of buffer descriptors an NDIS protocol driver can chain to form a packet descriptor. Consequently, MacSend must be capable of handling arbitrarily long chains. To handle a large number of buffers efficiently, MacSend can copy overly fragmented packets into contiguous storage before it transmits them as a block send on the network medium.

MacSend should always process incoming requests in FIFO order. Reordering packets to be transmitted can severely degrade performance in some protocol drivers.

A full-NIC driver must provide software loopback if its NIC does not support loopback. If a packet is received locally as if transmitted from another station (in other words, self-directed), the NIC driver must indicate the appropriate receive events locally. The driver also must determine whether such a self-directed packet actually has to go out on the medium.

MacSend completes synchronously if it finishes the send operation before it returns control with a status code other than NDIS_STATUS_PENDING. At this point, the protocol driver regains ownership of the packet.

If MacSend cannot complete the transmission before it returns control, it returns NDIS_STATUS_PENDING. MacSend usually queues such a packet internally. For asynchronous sends, the NIC driver eventually calls NdisCompleteSend, which, in turn, calls the ProtocolSendComplete function of the driver that initiated the transmit operation. This asynchronous completion allows protocol postprocessing of the send request. When NdisCompleteSend is called, the protocol driver regains ownership of the packet.

MacSend runs at IRQL DISPATCH_LEVEL.

See Also

MiniportSend, MiniportSendPackets, MiniportWanSend, NdisCompleteSend, NdisSend, ProtocolSendComplete


MacShutdown

VOID
 MacShutdown(
 IN PVOID ShutdownContext
);

MacShutdown is called by the NDIS library when the system is shutting down.

Parameters

ShutdownContext
Points to the context the NIC driver associates with shutdown processing. The NIC driver provided this pointer when it called NdisRegisterAdapterShutdownHandler during initialization.

Comments

MacShutdown makes final preparations before the operating system shuts down. In particular, MacShutdown resets the NIC to its power-on state.

MacShutdown runs at IRQL PASSIVE_LEVEL in a system thread context when an end user explictly shuts the machine down. However, MacShutdown runs at an arbitrary IRQL if a bugcheck causes the system to shut down. Consequently, MacShutdown should call only those Ndis Xxx functions that the driver’s ISR can call.

See Also

MacAddAdapter, MiniportShutdown, NdisDeregisterAdapterShutdownHandler, NdisRegisterAdapterShutdownHandler


MacSynchronizeFunction

BOOLEAN
 MacSynchronizeFunction(
 IN PVOID SynchronizeContext
);

MacSynchronizeFunction is called when a nonISR driver function calls NdisSynchronizeWithInterrupt.

Parameters

SynchronizeContext
Points to the context that the NIC driver associates with the synchronization, that is, to device state information, memory, or registers shared with the ISR.

Return Value

MacSynchronizeFunction returns a Boolean value to NdisSynchronizeWithInterrupt.

Comments

MacSynchronizeFunction synchronizes access to resources, such as state, device memory, or NIC registers, shared between nonISR driver functions and the driver’s MacInterruptServiceRoutine.

A MacSynchronizeFunction is the only nonISR driver-supplied function that can directly access such shared resources in a multiprocessor-safe way. Resources shared with the ISR are protected by a system-supplied interrupt spin lock that was set up when the driver originally called NdisInitializeInterrupt. The ISR can access shared resources without calling NdisSynchronizeWithInterrupt, but all other driver functions must call this function to have MacSynchronizeFunction access the same resources on their behalf.

MacSynchronizeFunction runs at the DIRQL assigned by the system when the driver called NdisInitializeInterrupt. Consequently, MacSynchronizeFunction can call only the small subset of NDIS library functions that MacInterruptServiceRoutine can call.

See Also

MacInterruptServiceRoutine, MiniportSynchronizeISR, NdisInitializeInterrupt, NdisSynchronizeWithInterrupt


MacTimerFunction

VOID
 MacTimerFunction(
 IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
);

MacTimerFunction is called when the interval specified in a driver call to NdisSetTimer expires.

Parameters

SystemSpecific1
Points to a first OS-specific value. For Windows NT drivers, this is reserved for use by NDIS.
FunctionContext
Points to the context that the NIC driver associates with the timer expiration.
SystemSpecific2
Points to a second OS-specific value. For Windows NT drivers, this is reserved for system use.
SystemSpecific3
Points to a third OS-specific value. For Windows NT drivers, this is reserved for system use.

Comments

MacTimerFunction is called, at most, once for each driver call to NdisSetTimer. If a full-NIC driver calls NdisSetTimer twice and the interval specified in the initial call has not yet expired, the second call effectively cancels the first. In these circumstances, MacTimerFunction runs when the interval specified in the most recent call to NdisSetTimer expires, unless the driver calls NdisCancelTimer before that interval expires.

MacTimerFunction runs at IRQL DISPATCH_LEVEL.

See Also

MiniportTimer, NdisCancelTimer, NdisInitializeTimer, NdisSetTimer


MacTransferData

NDIS_STATUS
 MacTransferData(
 IN NDIS_HANDLE MacBindingHandle,
IN NDIS_HANDLE MacReceiveContext,
IN UINT ByteOffset,
IN UINT BytesToTransfer,
OUT PNDIS_PACKET Packet,
OUT PUINT BytesTransferred
);

MacTransferData is called when a protocol driver calls NdisTransferData.

Parameters

MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
MacReceiveContext
Specifies the context value that the NIC driver associates with a packet received from the network. The driver uses this value to determine which packet to transfer on which NIC.
ByteOffset
Specifies the offset within the packet at which this function begins the transfer data operation. If the function is to copy the entire packet, ByteOffset must be zero.
BytesToTransfer
Specifies the number of bytes to transfer. The sum of the ByteOffset and BytesToTransfer values must be less than or equal to the value of the packet size (specified by NdisIndicateReceive, EthIndicateReceive, FddiFilterIndicateReceive, or TrFilterIndicateReceive). The NIC driver does not modify the trailing portion of the packet. This parameter can be zero.
Packet
Points to a protocol-supplied memory location in which MacTransferData returns the packet descriptor for the transferred data.
BytesTransferred
Points to a protocol-supplied variable in which this function returns the number of bytes transferred. This value is invalid if MacTransferData returns NDIS_STATUS_PENDING, so it should be set to zero when MacTransferData handles these requests asynchronously.

Return Value

MacTransferData can return the following status codes:

NDIS_STATUS_FAILURE
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_NOT_INDICATING
NDIS_STATUS_PENDING
NDIS_STATUS_REQUEST_ABORTED
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

NdisTransferData instructs a NIC driver to access a packet it has received from its NIC. MacTransferData copies packet data into the given packet buffers, but it does not copy the packet header. The values at ByteOffset and BytesToTransfer, which are provided by the protocol driver, do not include the packet header.

A protocol driver can call NdisTransferData from its ProtocolReceive function only once per receive indication. Because several protocol drivers can have a single network interface card open and each such protocol driver can receive transferred data, MacTransferData must be capable of transferring a packet more than once. If the NIC driver services a network interface card from which it can read a packet only once, the driver must copy receive packets into a staging buffer when several protocol drivers are bound to its NIC.

If it finishes the transfer-data operation before it returns control, MacTransferData returns a status code other than NDIS_STATUS_PENDING.

For asynchronous transfers, MacTransferData returns NDIS_STATUS_PENDING, and the NIC driver cannot deliver any more receive indications to the protocol driver designated by the MacBindingHandle while the transfer-data operation is pending. The NIC driver eventually calls NdisCompleteTransferData, which, in turn, calls the ProtocolTransferDataComplete function of the driver that initiated the data transfer. This asynchronous completion allows protocol postprocessing of the transfer-data request.

A MacTransferData function that handles requests asynchronously makes NIC DMA operations more efficient. Drivers for PIO NICs usually support NdisTransferData synchronously.

MacTransferData runs at IRQL DISPATCH_LEVEL.

See Also

EthFilterIndicateReceive, FddiFilterIndicateReceive, MiniportReturnPacket, MiniportTransferData, NdisCompleteTransferData, NdisIndicateReceive, NdisTransferData, ProtocolReceive, ProtocolTransferDataComplete, TrFilterIndicateReceive


MacUnload

VOID
 MacUnload(
 IN NDIS_HANDLE MacMacContext
);

MacUnload is called by NDIS to remove the NIC driver.

Parameters

MacMacContext
Specifies the context that the NIC driver associates with itself.

Comments

Before calling MacUnload, NDIS calls the NIC driver’s MacRemoveAdapter function to deregister all the driver’s NICs that were registered.

The unload process for a NIC driver is OS-dependent. Windows NT NIC drivers must have a MacUnload function. Windows NT protocol drivers also have an Unload routine, which they set in the driver object during initialization.

MacUnload first frees any resources that were allocated for the driver itself. It decrements the reference count in the NIC driver block to zero. MacUnload then calls NdisDeregisterMac to release the resources NDIS allocated when the driver originally called NdisRegisterMac. The call to NdisDeregisterMac renders the MacMacContext handle invalid.

After NDIS informs bound protocols of the NIC driver’s termination, all bound protocol drivers call NdisDeregisterProtocol to release the resources that NDIS allocated when they registered themselves.

The last driver being unloaded when the network is shutting down should make a final call to NdisTerminateWrapper to close the NDIS interface library.

If the operating system does not support unloading drivers, MacUnload simply returns control and NDIS unloads the NIC driver.

MacUnload runs at IRQL PASSIVE_LEVEL.

See Also

MacRemoveAdapter, NdisDeregisterMac, NdisDeregisterProtocol, NdisTerminateWrapper


A. 3 Summary of NDIS Library Functions for Full-NIC Drivers

This summarizes, in alphabetical order, all NDIS and filter library functions that legacy full-NIC drivers can call and the macros they can use.

Part I of the Network Driver Reference contains references for all functions and macros mentioned here that are also called by NDIS miniport and protocol drivers. References for functions called only by full-NIC drivers follow this summary. However, this summary has jumps to the references for every Eth Xxx, Fddi Xxx, Ndis Xxx, and Tr Xxx function and all macros that existing full-NIC drivers might use, including those in Part I.

For more information about Windows NT kernel-mode support routines and macros that an existing full-NIC driver might use, see also the Kernel-Mode Driver Reference.

EthChangeFilterAddresses
Checks the current multicast address list for all currently bound protocols and, if changes are necessary, calls the driver’s MacChangeEthAddress function, which was set up with EthCreateFilter, given a pointer to the filter database, the handle to the open indicating the binding, the packet causing the change of addresses, whether the changes are caused by a set request (rather than a close), a variable-sized array of address specifications, and a count of elements in the array. This function restores the original multicast address list if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
ETH_COPY_NETWORK_ADDRESS
Copies a given Ethernet address to a given location.
EthCreateFilter
Creates and initializes an Ethernet filter database, given the address of the NIC, the number of multicast addresses it can support, three driver-supplied functions to be called when certain runtime changes occur, a caller-initialized spin lock used to synchronize access to the filter database, and a pointer to a variable that is set to the address of the filter database on return from this function.
EthDeleteFilter
Releases the resources used for an Ethernet filter database when there are no remaining active filters.
EthDeleteFilterOpenAdapter
Removes a binding that was set up with EthNoteFilterOpenAdapter from the filter database when the NIC is closed by the protocol, calling driver-supplied functions as necessary to modify the database, given the pointer to the Ethernet filter database, the binding and context handles set up when the NIC was opened by the protocol, and a pointer to the filter handle returned by EthNoteFilterOpenAdapter.
EthFilterAdjust
Calls the driver’s MacChangeFilter function, which was set up with EthCreateFilter, when a particular filter class changes from being used to not used or vice versa, given a pointer to the filter database, the handle to the open indicating the binding, the packet causing the change of class usage, whether the change is caused by a set request (rather than a close), and the classes to be added or deleted. This function restores the original classes if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
EthFilterIndicateReceive
Indicates the arrival of an Ethernet receive packet, passing in a pointer to the Ethernet filter database, a pointer to caller-supplied context that the protocol must pass back if it later requests a transfer of the packet data, the destination address in the received packet, the packet header and its size in bytes, a pointer to a lookahead buffer containing some packet data and its size in bytes, and the total size in bytes of the packet. This function forwards the receive information to the appropriate bound protocol(s).
EthFilterIndicateReceiveComplete
Indicates that a receive operation is done, that is, that EthFilterIndicateReceive has returned to the caller. EthFilterIndicateReceiveComplete, given a pointer to the Ethernet filter database, notifies the appropriate bound protocol(s).
EthNoteFilterOpenAdapter
Adds a new binding to the existing filter database, given the pointer to an Ethernet filter database already initialized with EthCreateFilter, the binding and context handles set up when the NIC was opened by the protocol, and a pointer to caller-supplied storage for the filter handle that this function returns if the call succeeds.
ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES
Returns the number of multicast addresses in the Ethernet database multicast list for the NIC, given a pointer to the filter database returned by EthCreateFilter.
EthNumberOfOpenFilterAddresses
Returns how any multicast addresses a particular binding is using, given a pointer to the Ethernet filter database and the binding handle.
EthQueryGlobalFilterAddresses
Returns a variable-sized array of the multicast addresses currently in use by all bound protocols, given a pointer to the Ethernet filter database, the number of addresses obtained from ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES, and pointers to caller-supplied variables for returned status, the array of addresses, and the number of addresses actually returned in the array.
ETH_QUERY_FILTER_CLASSES
Returns a value indicating the currently enabled filter classes, given a pointer to the filter database returned by EthCreateFilter.
EthQueryOpenFilterAddresses
Returns a variable-sized array of the multicast addresses a particular binding is using, given a pointer to the Ethernet filter database, the binding handle, the number of addresses obtained from EthNumberOfOpenFilterAddresses, and pointers to caller-supplied variables for returned status, the array of addresses, and the number of addresses actually returned in the array.
ETH_QUERY_PACKET_FILTER
Returns a value indicating the currently enabled filter classes for a particular binding, given a pointer to the filter database returned by EthCreateFilter and a handle to the binding.
EthShouldAddressLoopBack
Returns FALSE if a packet bound for a given Ethernet address cannot possibly loop back.
FDDI_COMPARE_NETWORK_ADDRESSES
Sets a caller-supplied variable to a value indicating whether a given FDDI address is greater than, less than, or equal to another given FDDI address.
FddiChangeFilterLongAddresses
Checks the current multicast long address list for all currently bound protocols and, if changes are necessary, calls the driver’s MacChangeFddiAddresses function, which was set up with FddiCreateFilter, given a pointer to the filter database, the binding handle, the packet causing the change of addresses, whether the changes are caused by a set request (rather than a close), a variable-sized array of long address specifications, and a count of elements in the array. This function restores the original multicast address list if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
FddiChangeFilterShortAddresses
Checks the current multicast short address list for all currently bound protocols and, if changes are necessary, calls the driver’s MacChangeFddiAddresses function, which was set up with FddiCreateFilter, given a pointer to the filter database, the binding handle, the packet causing the change of addresses, whether the changes are causesd by a set request (rather than a close), a variable-sized array of short address specifications, and a count of elements in the array. This function restores the original multicast address list if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
FddiCreateFilter
Creates and initializes an FDDI filter database, given the long and short address of the NIC, the number of multicast long and short addresses it can support, three driver-supplied functions to be called when certain runtime changes occur, a caller-initialized spin lock used to synchronize access to the filter database, and a pointer to a variable that is set to the address of the filter database on return from this function.
FddiDeleteFilter
Releases the resources used for an FDDI filter database when there are no remaining active filters.
FddiDeleteFilterOpenAdapter
Removes a binding that was set up with FddiNoteFilterOpenAdapter from the filter database when the NIC is closed by the protocol, calling driver-supplied functions as necessary to modify the database, given the pointer to the FDDI filter database, the binding and context handles set up when the NIC was opened by the protocol, and a pointer to the filter handle returned by FddiNoteFilterOpenAdapter.
FddiFilterAdjust
Calls the driver’s MacChangeFilter function, which was set up with FddiCreateFilter, when a particular filter class changes from being used to not used or vice versa, given a pointer to the filter database, the binding handle, the packet causing the change of class usage, whether the change is caused by a set request (rather than a close), and the classes to be added or deleted. This function restores the original classes if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
FddiFilterIndicateReceive
Indicates the arrival of an FDDI receive packet, passing in a pointer to the FDDI filter database, a pointer to caller-supplied context that the protocol must pass back if it later requests a transfer of the packet data, the destination address in the received packet and the address’s length, the packet header and its size in bytes, a pointer to a lookahead buffer containing some packet data and its size in bytes, and the total size in bytes of the packet. This function forwards the receive information to the appropriate bound protocol(s).
FddiFilterIndicateReceiveComplete
Indicates that a receive operation is done, that is, that FddiFilterIndicateReceive has returned to the caller. FddiFilterIndicateReceiveComplete, given a pointer to the FDDI filter database, notifies the appropriate bound protocol(s).
FDDI_IS_BROADCAST
Sets a caller-supplied variable to a Boolean value indicating whether a given FDDI address is a broadcast address.
FDDI_IS_MULTICAST
Sets a caller-supplied variable to a Boolean value indicating whether a given FDDI address is a multicast address.
FDDI_IS_SMT
Sets a caller-supplied variable to a Boolean value indicating whether a given FDDI frame is SMT.
FddiNoteFilterOpenAdapter
Adds a new binding to the existing filter database, given the pointer to an FDDI filter database already initialized with FddiCreateFilter, the binding and context handles set up when the NIC was opened by the protocol, and a pointer to caller-supplied storage for the filter handle that this function returns if the call succeeds.
FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES
Returns the number of addresses in the FDDI database long address list for the NIC, given a pointer to the filter database returned by FddiCreateFilter.
FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES
Returns the number of addresses in the FDDI database short address list for the NIC, given a pointer to the filter database returned by FddiCreateFilter.
FddiNumberOfOpenFilterLongAddresses
Returns how many long addresses are in use by a particular binding, given the binding handle and a pointer to the FDDI filter database.
FddiNumberOfOpenFilterShortAddresses
Returns how many short addresses are in use by a particular binding, given the binding handle and a pointer to the FDDI filter database.
FDDI_QUERY_FILTER_CLASSES
Returns a value indicating the currently enabled filter classes, given a pointer to the filter database returned by FddiCreateFilter.
FddiQueryGlobalFilterLongAddresses
Returns a variable-sized array of the long addresses currently in use by all bound protocols, given a pointer to the FDDI filter database, the number of addresses obtained from FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES, and pointers to caller-supplied variables for returned status, the array of addresses, and the number of addresses actually returned in the array.
FddiQueryGlobalFilterShortAddresses
Returns a variable-sized array of the short addresses currently in use by all bound protocols, given a pointer to the FDDI filter database, the number of addresses obtained from FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES, and pointers to caller-supplied variables for returned status, the array of addresses, and the number of addresses actually returned in the array.
FddiQueryOpenFilterLongAddresses
Returns a variable-sized array of the long addresses currently in use by a particular bound protocol, given a pointer to the FDDI filter database, the binding handle set up when the protocol opened the adapter, the number returned by FddiNumberOfOpenFilterLongAddresses, and pointers to caller-supplied variables for returned status, the array of addresses, and the number of addresses actually returned in the array.
FddiQueryOpenFilterShortAddresses
Returns a variable-sized array of the short addresses currently in use by a particular bound protocol, given a pointer to the FDDI filter database, the binding handle set up when the protocol opened the adapter, the number returned by FddiNumberOfOpenFilterShortAddresses, and pointers to caller-supplied variables for returned status, the array of addresses, and the number of addresses actually returned in the array.
FDDI_QUERY_PACKET_FILTER
Returns a value indicating the currently enabled filter classes for a particular binding, given a pointer to the filter database returned by FddiCreateFilter and a handle to the binding.
FddiShouldAddressLoopBack
Returns FALSE if a packet bound for a given FDDI address cannot possibly loop back.
NdisAcquireSpinLock
Acquires a spin lock so the caller can synchronize access to resources shared among nonISR driver routines in a multiprocessor-safe way.
NdisAdjustBufferLength
Modifies the length specified in a given buffer descriptor that was allocated or copied by the caller.
NdisAllocateBuffer
Creates a buffer descriptor describing a specified virtual (sub)range within an already allocated, nonpaged memory block, given a handle returned by NdisAllocateBufferPool.
NdisAllocateBufferPool
Returns a handle with which the caller can allocate buffer descriptors by calling NdisAllocateBuffer.
NdisAllocateDmaChannel
Reserves system resources during driver initialization for subsequent slave DMA operations and claims a DMA channel in the registry, returning a handle to be passed in subsequent calls to the Ndis Xxx DMA functions.
NdisAllocateMemory
Allocates resident (nonpaged) system-space memory, optionally within a specified address limit, as a physically contiguous allocation, and/or as a noncached allocation.
NdisAllocatePacket
Allocates a packet, given a handle returned by a preceding call to NdisAllocatePacketPool.
NdisAllocatePacketPool
Allocates and initializes a block of nonpaged packet pool, given the number of packet descriptors and how many bytes each fixed-size packet should contain.
NdisAllocateSharedMemory
Allocates and maps a memory range so it is simultaneously accessible from both the system and a busmaster DMA device, given a handle for the NIC returned by NdisRegisterAdapter, the number of bytes to be allocated, and whether the memory should be cached. Returns the base (system) virtual address of the allocation for use by the driver and a mapped base logical address for use by the NIC.
NdisAllocateSpinLock
Initializes a variable of type NDIS_SPIN_LOCK, used to synchronize access to data shared among nonISR driver routines. An initialized spin lock, for which the caller supplies resident storage, is a required parameter to the Ndis..SpinLock and NdisInterlocked Xxx functions.
NDIS_BUFFER_LINKAGE
Returns a pointer to a linked buffer, given a pointer to an NDIS buffer descriptor.
NdisCancelTimer
Cancels the immediately preceding call to NdisSetTimer, provided that the interval given to NdisSetTimer has not yet expired.
NdisChainBufferAtBack
Links a given buffer descriptor at the end of the chain of buffer descriptors for a given packet.
NdisChainBufferAtFront
Links a given buffer descriptor at the start of the chain of buffer descriptors for a given packet.
NdisCloseConfiguration
Releases the handle returned by NdisOpenConfiguration after the caller has retrieved hardware configuration information for a NIC and/or, possibly, stored additional information about its NIC in the registry.
NdisCloseFile
Releases a handle returned by NdisOpenFile and frees the memory allocated to hold the file contents when it was opened.
NdisCompleteBufferPhysicalMapping
Flushes any data remaining in a busmaster NIC’s internal cache at the end of a transfer operation that was set up with NdisStartBufferPhysicalMapping.
NdisCompleteCloseAdapter
Notifies NDIS that the driver has completed a close-adapter request for which it previously returned NDIS_STATUS_PENDING, given the binding handle that identifies the protocol and the final status to be returned for the request. This function also releases resources that were allocated when the adapter was opened.
NdisCompleteDmaTransfer
Completes a slave DMA transfer operation begun with a preceding call to NdisSetupDmaTransfer, ensuring that the data is flushed to system memory or to the NIC, depending on the transfer direction.
NdisCompleteOpenAdapter
Notifies NDIS that the driver has completed an open-adapter request for which it previously returned NDIS_STATUS_PENDING, given the binding handle that identifies the protocol, the final status to be returned for the open request, and a second status with additional information if the driver set anything other than NDIS_STATUS_SUCCESS for the final status.
NdisCompleteQueryStatistics
Notifies NDIS that the driver has completed a non-protocol-specific open or query-information request for which it previously returned NDIS_STATUS_PENDING, given the handle to the NIC (returned by NdisRegisterAdapter), a pointer to the request, and the final status assigned by the caller.
NdisCompleteRequest
Returns the packet and final status of a completed query- or set-information request for which the driver previously returned NDIS_STATUS_PENDING, so that NDIS can return the completed packet to the appropriate bound protocol driver.
NdisCompleteReset
Returns the final status of a reset request for which the driver previously returned NDIS_STATUS_PENDING and indicates whether the reset operation on the NIC affected its network address information.
NdisCompleteSend
Returns the packet and final status of a completed send request for which the driver previously returned NDIS_STATUS_PENDING, so that NDIS can return the completed packet to the appropriate bound protocol driver.
NdisCompleteTransferData
Returns the packet and final status of a completed transfer request for which the driver previously returned NDIS_STATUS_PENDING, so that NDIS can return the completed packet to the appropriate bound protocol driver.
NdisCopyBuffer
Creates a buffer descriptor for a specified virtual (sub)range, given a handle for a block of already allocated memory and a pointer to the memory descriptor for the block.
NdisCopyFromPacketToPacket
Copies a specified number of bytes of data from one packet to another, starting at a specified offset in a given source packet’s data and copying the data to a specified offset in the target packet. Returns the number of bytes actually copied, which can be less than the requested number, depending on the source packet and specified starting offset for the copy operation.
NdisCreateLookaheadBufferFromSharedMemory
Returns the virtual address of a buffer within a block of memory shared with a busmaster DMA NIC so the caller can read(ahead) data in the buffer.
NdisDeregisterAdapter
Releases resources that were originally allocated with NdisRegisterAdapter, given the adapter handle. Drivers call NdisDeregisterAdapter when they are unloading.
NdisDeregisterAdapterShutdownHandler
Removes a driver function when the driver is unloading from the set of shutdown handlers that were registered with NdisRegisterAdapterShutdownHandler.
NdisDeregisterMac
Removes the caller from the set of drivers registered with NdisRegisterMac. Drivers call NdisDeregisterMac when they are unloading.
NdisDestroyLookaheadBufferFromSharedMemory
Releases access to the buffer obtained in the preceding call to NdisCreateLookaheadBufferFromSharedMemory.
NdisDprAcquireSpinLock
Acquires a spin lock so the caller can synchronize access to resources shared among nonISR driver routines in a multiprocessor-safe way, provided that the caller is running at IRQL DISPATCH_LEVEL.
NdisDprReleaseSpinLock
Releases a spin lock that was acquired in a preceding call to NdisDprAcquireSpinLock.
NdisFlushBuffer
Flushes the memory region described by a given buffer descriptor from all processor caches.
NdisFreeBuffer
Releases a buffer descriptor obtained in a preceding call to NdisAllocateBuffer or NdisCopyBuffer.
NdisFreeBufferPool
Releases a handle obtained in a preceding call to NdisAllocateBufferPool.
NdisFreeDmaChannel
Releases system resources, including claimed hardware resources in the registry, that were acquired with NdisAllocateDmaChannel. Drivers of slave DMA NICs call NdisFreeDmaChannel when they are unloading.
NdisFreeMemory
Releases a handle, usually when the driver is being unloaded, that was obtained in a preceding call to NdisAllocateMemory.
NdisFreePacket
Releases a packet that was allocated from packet pool with NdisAllocatePacket and returns it to the free list.
NdisFreePacketPool
Releases a handle to a block of packet pool that was allocated with NdisAllocatePacketPool.
NdisFreeSharedMemory
Releases system resources that were acquired with NdisAllocateSharedMemory. Drivers call NdisFreeSharedMemory when they are unloading.
NdisFreeSpinLock
Releases a spin lock initialized in a preceding call to NdisAllocateSpinLock, usually when a driver is unloading.
NdisFreeString
Frees the buffered string allocated with NdisInitializeString.
NdisGetBufferPhysicalArraySize
Returns the number of discontiguous physical blocks in a buffer, given a buffer descriptor.
NdisGetCacheFillSize
Returns the size of the cache boundary to drivers setting up DMA alignment requirements during initialization.
NdisGetNextBuffer
Returns the next buffer descriptor in a chain, given the current buffer descriptor.
NdisGetPhysicalAddressHigh
Returns the high-order part of a given physical address.
NdisGetPhysicalAddressLow
Returns the low-order part of a given physical address.
NdisImmediateReadPciSlotInformation
Returns a specified range of PCI configuration information to a caller-supplied buffer, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisImmediateReadPortUchar
Reads a byte from a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to read the data.
NdisImmediateReadPortUlong
Reads a ULONG from a specified port on an I/O bus during driver initialization, given a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to read the data.
NdisImmediateReadPortUshort
Reads a USHORT from a specified port on an I/O bus during driver initialization, given a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to read the data.
NdisImmediateReadSharedMemory
Reads a specified number of bytes into a caller-supplied buffer from shared device RAM, given the base physical address for the shared memory and a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative memory range in order to read the data.
NdisImmediateWritePciSlotInformation
Writes data from a caller-supplied buffer to a specified range of the PCI configuration space, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisImmediateWritePortUchar
Writes a byte to a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to write the data.
NdisImmediateWritePortUlong
Writes a ULONG to a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to write the data.
NdisImmediateWritePortUshort
Writes a USHORT to a specified port on an I/O bus during driver initialization, given the handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative port address in order to write the data.
NdisImmediateWriteSharedMemory
Writes a specified number of bytes from a caller-supplied buffer to shared device RAM, given the base physical address for the shared memory and a handle to the wrapper configuration context. If necessary, this function maps the specified bus-relative memory range in order to write the data.
NdisIndicateReceive
Notifies NDIS that a packet is being received on a particular NIC, identified by the adapter handle, and that the packet or some initial lookahead portion of the packet, along with the packet header, is available to be forwarded to the appropriate bound protocol driver(s).
NdisIndicateReceiveComplete
Notifies NDIS that a receive packet, identified in a preceding call to NdisIndicateReceive, has been fully transferred through a particular NIC, identified by the given adapter handle, so that NDIS can notify the appropriate bound protocol driver(s).
NdisIndicateStatus
Notifies NDIS of a change in the runtime status of the NIC that might be corrected with a reset operation or possibly even require the NIC to be taken out of service, depending on the given NDIS_STATUS_XXX and additional driver-supplied information.
NdisIndicateStatusComplete
Notifies NDIS that the driver, which has previously called NdisIndicateStatus one or more times to indicate hardware anomalies or runtime state changes in the NIC, is ready to resume normal device I/O operations.
NDIS_INIT_FUNCTION
Marks a driver function as initialization-only driver code to be discarded after the function has executed.
NdisInitializeInterrupt
Sets up a mapping between the driver’s ISR and DPC functions, and the bus-relative vector (or level), mode, and sharing mode at which its NIC interrupts, given a pointer to caller-allocated storage for interrupt state (to be used by the NDIS library), a pointer to a variable in which status is returned, and a handle for the NIC. The interrupt handle initialized in this call is a required parameter to NdisSynchronizeWithInterrupt.
NdisInitializeListHead
Sets up a queue header for a driver’s internal queue, given a pointer to driver-supplied resident storage for the queue header and queue. Then, entries (usually packets) can be added to and removed from the doubly-linked queue with the NdisInterlocked..List functions.
NdisInitializeString
Allocates a buffer for and initializes a counted string in the system-default character set, given a source string and a NULL pointer.
NdisInitializeTimer
Sets up a caller-supplied time-out DPC function to be associated with a timer object during driver initialization; returns a handle at the caller-supplied resident storage for the timer object (to be used by the NDIS library). This handle is a required parameter to all other Ndis..Timer functions.
NdisInitializeWrapper
Notifies NDIS that a new NIC driver is initializing and returns an NdisWrapperHandle to be passed in subsequent calls to Ndis Xxx initialization and configuration functions.
NdisInterlockedAddUlong
Adds an unsigned long value to a given unsigned integer as an atomic operation, using a caller-supplied and initialized spin lock.
NdisInterlockedInsertHeadList
Inserts an entry, usually a packet, at the head of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among nonISR driver functions.
NdisInterlockedInsertTailList
Inserts an entry, usually a packet, at the end of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among nonISR driver functions.
NdisInterlockedRemoveHeadList
Removes an entry, usually a packet, from the head of a doubly linked list using a caller-supplied and initialized spin lock so that access to queued entries is synchronized in a multiprocessor-safe way among nonISR driver functions.
NdisMapFile
Maps the contents of an already open file to a caller-accessible buffer if the file currently is unmapped, given a handle returned by NdisOpenFile.
NdisMapIoSpace
Maps a given bus-relative physical range of device memory or a driver-allocated, long-term internal buffer onto system-space nonpaged pool and returns the mapped virtual base address.
NdisMoveFromMappedMemory
Copies data from device memory or a driver-allocated buffer that was mapped with NdisMapIoSpace to a system-space buffer.
NdisMoveMappedMemory
Copies data from one location to another in a memory block that was mapped with NdisMapIoSpace.
NdisMoveMemory
Copies a specified number of bytes from one caller-supplied location to another.
NdisMoveToMappedMemory
Copies data from a system-space buffer to device memory or a driver-allocated buffer that was mapped with NdisMapIoSpace.
NdisOpenConfiguration
Returns a handle for a driver’s HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
\DriverName\Parameters registry key. This handle is a required parameter to all other Ndis Xxx configuration functions.
NdisOpenFile
Returns a handle for an opened file, given its name and the highest acceptable address at which to allocate memory for the file data. The returned handle is a required parameter to NdisMapFile, which gives the caller exclusive access to the file data until it calls NdisUnmapFile.
NDIS_PAGABLE_FUNCTION
Marks a driver function as pageable driver code.
NdisPciAssignResources
Claims resources in the registry for the caller and returns the hardware configuration information, given a handle passed in to the driver’s DriverEntry function and the slot number of its NIC on the PCI bus.
NDIS_PHYSICAL_ADDRESS_CONST
Initializes a constant of type NDIS_PHYSICAL_ADDRESS.
NdisQueryBuffer
Retrieves the size of the range, and optionally its base virtual address, specified in a given buffer descriptor.
NdisQueryBufferOffset
Retrieves the base virtual address of the range specified in a given buffer descriptor.
NdisQueryPacket
Returns at least a count of buffers chained to a given packet and can also return the total number of bytes in all buffers chained to the packet, the total number of physically discontiguous pieces for all chained buffers, and a pointer to the initial buffer in the chain.
NdisQuerySendFlags
Retrieves the flags set by the protocol from a given send packet.
NdisRawReadPortBufferUchar
Reads a specified number of bytes into a caller-supplied buffer, given an I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawReadPortBufferUlong
Reads a specified number of ULONGs into a caller-supplied buffer, given an I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawReadPortBufferUshort
Reads a specified number of USHORTs into a caller-supplied buffer, given an I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawReadPortUchar
Reads a byte from a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawReadPortUlong
Reads a ULONG from a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawReadPortUshort
Reads a USHORT from a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawWritePortBufferUchar
Writes a specified number of bytes from a caller-supplied buffer to a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawWritePortBufferUlong
Writes a specified number of ULONGs from a caller-supplied buffer to a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawWritePortBufferUshort
Writes a specified number of USHORTs from a caller-supplied buffer to a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawWritePortUchar
Writes a byte to a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawWritePortUlong
Writes a ULONG to a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisRawWritePortUshort
Writes a USHORT to a given I/O port mapped in the initialization-time call to NdisRegisterAdapter.
NdisReadBindingInformation
Returns a pointer to the name of the NIC under a NIC driver’s Parameters key in the registry, given a handle obtained from NdisOpenConfiguration. Layered drivers can call this function, passing the returned string to NdisOpenAdapter.
NdisReadConfiguration
Reads the value of a named entry of the specified type, given the registry handle for the caller’s Parameters key returned by NdisOpenConfiguration.
NdisReadDmaCounter
Returns the number of bytes remaining to be transferred during a slave DMA operation if the NIC uses the host DMA controller’s autoinitialize mode, given a handle returned by NdisAllocateDmaChannel.
NdisReadEisaSlotInformation
Returns EISA configuration information to a caller-supplied buffer during initialization, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisReadEisaSlotInformationEx
Returns EISA configuration information, including the number of functions, to a caller-supplied buffer during initialization, given a handle to the wrapper configuration context and the slot number for the NIC on the I/O bus.
NdisReadMcaPosInformation
Returns POS configuration information to a caller-supplied buffer during initialization, given a handle to the wrapper configuration context and the channel number for the NIC on the I/O bus.
NdisReadNetworkAddress
Returns the size and value, in binary, of the NetworkAddress entry, specifying the setup-configured network address of the NIC, from the caller’s Parameters key in the registry, given the handle obtained from NdisOpenConfiguration.
NdisReadPciSlotInformation
Returns a specified range of PCI configuration information to a caller-supplied buffer during initialization, given a handle to the adapter and the slot number for the NIC on the I/O bus.
NdisReadPortBufferUchar
Reads a specified number of bytes from a given I/O port into a caller-supplied buffer, after mapping the port address if necessary.
NdisReadPortBufferUlong
Reads a specified number of ULONGs from a given I/O port into a caller-supplied buffer, after mapping the port address if necessary.
NdisReadPortBufferUshort
Reads a specified number of USHORTs from a given I/O port into a caller-supplied buffer, after mapping the port address if necessary.
NdisReadPortUchar
Reads a byte from a given I/O port, after mapping the port address if necessary.
NdisReadPortUlong
Reads a ULONG from a given I/O port, after mapping the port address if necessary.
NdisReadPortUshort
Reads a USHORT from a given I/O port, after mapping the port address if necessary.
NdisReadRegisterUchar
Reads a byte from a given register address.
NdisReadRegisterUlong
Reads a ULONG from a given register address.
NdisReadRegisterUshort
Reads a USHORT from a given register address.
NdisRecalculatePacketCounts
Resets the valid count for a given packet.
NdisRegisterAdapter
Registers a NIC, recognized by the driver during initialization with calls to the Ndis Xxx configuration functions, with the NDIS library. This function also allocates system resources and claims hardware resources in the registry for the NIC, given the handle returned by NdisRegisterMac, a pointer to caller-allocated resident storage in which the driver maintains runtime context for the NIC, the handle returned by NdisInitializeWrapper, a pointer to a caller-supplied string naming the NIC, and a pointer to a caller-initialized NDIS_ADAPTER_INFORMATION structure specifying hardware configuration requirements of the NIC. NdisRegisterAdapter returns the status of the registration attempt and, if the call is successful, an adapter handle for the NIC, which can be used by protocols in open-adapter requests.
NdisRegisterAdapterShutdownHandler
Registers a driver-supplied MacShutdown function to be called with a pointer to a caller-supplied context buffer when the system is about to be shut down, given a handle passed in to the driver’s DriverEntry function. Such a MacShutdown function restores the NIC to a known state.
NdisRegisterMac
Registers a driver’s upper-edge entry points and name with the NDIS library, given a handle obtained from NdisInitializeWrapper and a pointer to the NDIS_MAC_CHARACTERISTICS structure set up by the caller. NdisRegisterMac returns the status of the registration attempt and, if the call is successful, a handle to the driver.
NdisReinitializePacket
Removes any chained buffers from a given packet and reinitializes it for reuse.
NdisReleaseAdapterResources
Releases claimed hardware resources for a NIC in the registry when the driver is unloading.
NdisReleaseSpinLock
Releases a spin lock that was acquired in a preceding call to NdisAcquireSpinLock.
NdisRemoveInterrupt
Releases the mapping created with an initialization-time call to NdisInitializeInterrupt. Drivers call NdisRemoveInterrupt when they are unloading.
NdisSetPhysicalAddressHigh
Changes the high-order part of a given physical address to a given value.
NdisSetPhysicalAddressLow
Changes the low-order part of a given physical address to a given value.
NdisSetTimer
Sets a caller-initialized timer object to a specified interval and queues the driver’s timer-DPC function to run when the given interval expires.
NdisSetupDmaTransfer
Sets up the host DMA controller to transfer a specified range of data from a given buffer, given the handle returned by a preceding call to NdisAllocateDmaChannel and the transfer direction.
NdisStallExecution
Stalls the caller on the current processor for a given interval.
NdisStartBufferPhysicalMapping
Sets up a busmaster NIC for a DMA transfer operation, given a handle for the NIC, the direction of the transfer, a pointer to the buffer descriptor, an index to the initial map register, a pointer to caller-allocated memory for a variable-size array of NDIS_PHYSICAL_UNIT structures, and the number of elements in the array.
NdisSynchronizeWithInterrupt
Synchronizes the execution of a caller-supplied function with that of the ISR associated with the given initialized interrupt handle. When it runs, the supplied function can access data shared with the ISR in a multiprocessor-safe way.
NdisTerminateWrapper
Releases system resources allocated when the driver called NdisInitializeWrapper. Drivers call NdisTerminateWrapper during initialization if they cannot find and successfully initialize at least one NIC or when they are unloading.
NdisUnchainBufferAtBack
Removes a given buffer descriptor at the end of the chain of buffer descriptors for a given packet and returns a pointer to the buffer descriptor.
NdisUnchainBufferAtFront
Removes a given buffer descriptor at the start of the chain of buffer descriptors for a given packet and returns a pointer to the buffer descriptor.
NdisUnmapFile
Releases the file mapping set up by the preceding call to NdisMapFile.
NdisUnmapIoSpace
Releases a mapping obtained with NdisMapIoSpace, usually when a driver is unloading.
NdisUpdateSharedMemory
Ensures that data read from shared memory is current when the NIC has just written data to shared memory on platforms that require explicit coherency support from the system.
NdisWriteConfiguration
Writes a caller-supplied value entry in the registry, given the handle for the caller’s Parameters key returned by NdisOpenConfiguration
NdisWriteErrorLogEntry
Allocates an error-log record, fills it in, and writes it to the I/O error log, given a handle for the NIC, an NDIS_XXX error code, a count of caller-supplied error values, and a corresponding number of ULONGs specifying additional information about the I/O error.
NdisWritePciSlotInformation
Writes data from a caller-supplied buffer to a specified range of the PCI configuration space, given a handle to the adapter and the slot number for the NIC on the I/O bus.
NdisWritePortBufferUchar
Writes a specified number of bytes from a caller-supplied buffer to a given I/O port, after mapping the port address if necessary.
NdisWritePortBufferUlong
Writes a specified number of ULONGs from a caller-supplied buffer to a given I/O port, after mapping the port address if necessary.
NdisWritePortBufferUshort
Writes a specified number of USHORTs from a caller-supplied buffer to a given I/O port, after mapping the port address if necessary.
NdisWritePortUchar
Writes a given byte to a given I/O port, mapping the port address if necessary.
NdisWritePortUlong
Writes a given ULONG to a given I/O port, after mapping the port address if necessary.
NdisWritePortUshort
Writes a given USHORT to a given I/O port, after mapping the port address if necessary.
NdisWriteRegisterUchar
Writes a given byte to a given register address.
NdisWriteRegisterUlong
Writes a given ULONG to a given register address.
NdisWriteRegisterUshort
Writes a given USHORT to a given register address.
NdisZeroMappedMemory
Fills a block of memory that was mapped in a preceding call to NdisMapIoSpace with zeros, given a pointer to the block and the length in bytes to be filled.
NdisZeroMemory
Fills a block of memory with zeros, given a pointer to the block and the length in bytes to be filled.
TrChangeFunctionalAddress
Checks the current functional address array for all currently bound protocols and, if a change is necessary, calls the driver’s MacChangeTrAddress function, which was set up with TrCreateFilter, given a pointer to the filter database, the binding handle for the protocol that initiated the set/close request, the packet causing the change of functional addresses, whether the change is caused by a set request (rather than a close), and an address specification. This function restores the original functional address array if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
TrChangeGroupAddress
Checks the current group address array for all currently bound protocols and, if a change is necessary, calls the driver’s MacChangeGroup function, which was set up with TrCreateFilter, given a pointer to the filter database, the binding handle for the protocol that initiated the set/close request, the packet causing the change of group addresses, whether the change is caused by a set request (rather than a close), and an address specification. This function restores the original group address array if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
TR_COMPARE_NETWORK_ADDRESSES
Sets a caller-supplied variable to a value indicating whether a given Token Ring address is greater than, less than, or equal to another given Token Ring address.
TR_COPY_NETWORK_ADDRESS
Copies a given Token Ring address to a given location.
TrCreateFilter
Creates and initializes a Token Ring filter database, given the address of the NIC, the number of multicast addresses it can support, four driver-supplied functions to be called when certain runtime changes occur, a caller-initialized spin lock used to synchronize access to the filter database, and a pointer to a variable that is set to the address of the filter database on return from this function.
TrDeleteFilter
Releases the resources used for a Token Ring filter database when there are no remaining active filters.
TrDeleteFilterOpenAdapter
Removes a binding that was set up with TrNoteFilterOpenAdapter from the filter database when the NIC is closed by the protocol, calling driver-supplied functions as necessary to modify the database, given the pointer to the Token Ring filter database, the binding and context handles set up when the NIC was opened by the protocol, and a pointer to the filter handle returned by TrNoteFilterOpenAdapter.
TrFilterAdjust
Calls the driver’s MacChangeFilter function, which was set up with TrCreateFilter, when a particular filter class changes from being used to not used or vice versa, given a pointer to the filter database, the binding handle, the packet causing the change of class usage, whether the change is caused by a set request (rather than a close), and the classes to be added or deleted. This function restores the original classes if the caller-supplied function returns anything except NDIS_STATUS_SUCCESS or NDIS_STATUS_PENDING.
TrFilterIndicateReceive
Indicates the arrival of a Token Ring receive packet, passing in a pointer to the Token Ring filter database, a pointer to caller-supplied context that the protocol must pass back if it later requests a transfer of the packet data, the packet header and its size in bytes, a pointer to a lookahead buffer containing some packet data and its size in bytes, and the total size in bytes of the packet. This function forwards the receive information to the appropriate bound protocol(s).
TrFilterIndicateReceiveComplete
Indicates that a receive operation is done, that is, that TrFilterIndicateReceive has returned to the caller. TrFilterIndicateReceiveComplete, given a pointer to the TokenRing filter database, notifies the appropriate bound protocol(s).
TR_IS_BROADCAST
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a broadcast address.
TR_IS_FUNCTIONAL
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a functional address.
TR_IS_GROUP
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a group address.
TR_IS_NOT_DIRECTED
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is neither a functional nor a group address.
TR_IS_SOURCE_ROUTING
Sets a caller-supplied variable to a Boolean value indicating whether a given Token Ring address is a source-routing address.
TrNoteFilterOpenAdapter
Adds a new binding to the existing filter database, given the pointer to a Token Ring filter database already initialized with TrCreateFilter, the binding and context handles set up when the NIC was opened by the protocol, and a pointer to caller-supplied storage for the filter handle that this function returns if the call succeeds.
TR_QUERY_FILTER_ADDRESSES
Returns a value indicating the currently enabled functional address, given a pointer to the filter database returned by TrCreateFilter.
TR_QUERY_FILTER_BINDING_ADDRESS
Returns a value indicating the functional address for a particular binding, given a pointer to the filter database returned by TrCreateFilter and the binding handle.
TR_QUERY_FILTER_BINDING_GROUP
Returns TRUE if a particular binding is using the currently enabled group address, given a pointer to the filter database returned by TrCreateFilter and the binding handle.
TR_QUERY_FILTER_CLASSES
Returns a value indicating the currently enabled filter classes, given a pointer to the filter database returned by TrCreateFilter.
TR_QUERY_FILTER_GROUP
Returns a value indicating the currently enabled group address, given a pointer to the filter database returned by TrCreateFilter.
TR_QUERY_PACKET_FILTER
Returns a value indicating the currently enabled filter classes for a particular binding, given a pointer to the filter database returned by TrCreateFilter and the binding handle.
TrShouldAddressLoopBack
Returns FALSE if a packet bound for a given Token Ring address cannot possibly loop back.


A.4 NDIS Library Functions Called Only by Full-NIC Drivers

This provides references for NDIS library functions that only legacy full-NIC drivers call. These references are in alphabetical order.

In addition to the functions described here, existing full-NIC drivers call some of the NDIS functions described in Part I of the Network Driver Reference. For a complete list, which includes only functions and macros that NDIS V3.0 also exported, see the preceding section, A.3 Summary of NDIS Library Functions for Full-NIC Drivers.


ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES

UINT
 ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES(
IN PETH_FILTER Filter
);

ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES returns the total number of addresses in the multicast address list for the NIC.

Parameters

Filter
Points to a previously created and initialized filter library database. This pointer was returned by EthCreateFilter.

Return Value

ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES returns the total number of multicast addresses.

Comments

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

EthQueryGlobalFilterAddresses


ETH_QUERY_FILTER_CLASSES

UINT
 ETH_QUERY_FILTER_CLASSES(
IN PETH_FILTER Filter
);

ETH_QUERY_FILTER_CLASSES is called by the NIC driver to query the Ethernet filter classes for a network interface card.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

ETH_QUERY_FILTER_CLASSES returns a value representing the currently enabled filter classes.

Comments

A NIC driver calls this macro before it alters registers on the network interface card to effect filtering. The timing of the call is important if a binding has altered the multicast address list.

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.


ETH_QUERY_PACKET_FILTER

UINT
 ETH_QUERY_PACKET_FILTER(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

ETH_QUERY_PACKET_FILTER is called by the NIC driver to query the Ethernet filter classes for a binding.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

ETH_QUERY_PACKET_FILTER returns a value representing the currently enabled filter classes for the binding.

Comments

A NIC driver calls this macro before it alters registers on the network interface card to effect filtering. The timing of the call is important if a binding has altered the multicast address list.

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.


EthChangeFilterAddresses

NDIS_STATUS
 EthChangeFilterAddresses(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN UINT AddressCount,
 IN CHAR Addresses[][ETH_LENGTH_OF_ADDRESS],
 IN BOOLEAN Set
 );

EthChangeFilterAddresses is called by a NIC driver to attempt to change the multicast address list for a binding filter in the Ethernet filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.
AddressCount
Specifies the number of elements to be added to the multicast address list.
Addresses[][ETH_LENGTH_OF_ADDRESS]
Specifies a two-dimensional character array that contains the new multicast address list for the specified binding. The ETH_LENGTH_OF_ADDRESS constant specifies the length in bytes of an Ethernet address (defined in EFILTER.H). The array is a sequence of 48-bit Ethernet addresses (no padding between addresses).
Set
Specifies TRUE if changes do not result from the closing of the binding. This function does not use this value but passes it to a NIC driver action function.

Return Value

EthChange FilterAddresses can return the following status codes:

NDIS_STATUS_MULTICAST_FULL
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

If EthChangeFilterAddresses does not change the multicast address list for a binding filter, it has no effect on the filter address list or the multicast address list for the MIC. If this function does change the filter address list, it calls a NIC driver action function and waits for it to return. If the action function returns a status code that indicates neither pending nor success, EthChangeFilterAddresses restores the filter library database to its original state.

A caller must be holding the database spin lock to call EthChangeFilterAddresses, so the driver is running at IRQL DISPATCH_LEVEL.


EthCreateFilter

BOOLEAN
 EthCreateFilter(
IN UINT MaximumMulticastAddresses,
 IN ETH_ADDRESS_CHANGE AddressChangeAction,
 IN ETH_FILTER_CHANGE FilterChangeAction,
 IN ETH_DEFERRED_CLOSE CloseAction,
 IN PUCHAR AdapterAddress,
 IN PNDIS_SPIN_LOCK Lock,
 OUT PETH_FILTER *Filter
);

EthCreateFilter is called by a NIC driver. It creates and initializes an Ethernet filter library database associated with one network interface card.

Parameters

MaximumMulticastAddresses
Specifies the maximum number of multicast addresses supported by the filter.
AddressChangeAction
Specifies the address of the caller’s MacChangeEthAddresses function, which the Ethernet filter library calls when a change occurs in the list of multicast addresses that the network interface card enables.
FilterChangeAction
Specifies the address of the caller’s MacChangeFilter function, which the Ethernet filter library calls when a binding sets or clears a particular filter class. The binding must be the first or only binding using the filter class.
CloseAction
Specifies the address of the caller’s MacCloseAction function, which the Ethernet filter library calls if a binding closes while it is indicating incoming data through NdisIndicateReceive.
AdapterAddress
Points to the network address of the network interface card.
Lock
Points to an initialized spin lock that the driver must hold when it requires exclusive access to the Ethernet filter library database.
Filter
Points to a caller-supplied variable in which this function writes a pointer to the created filter.

Return Value

EthCreateFilter returns TRUE if it creates the Ethernet filter library database successfully.

Comments

The NIC driver uses the Ethernet filter library to filter received network packets. Using the destination address of a received packet, the filter library determines the protocol driver bindings that receive indications for the packet.

Callers of EthCreateFilter run at IRQL PASSIVE_LEVEL.

See Also

EthDeleteFilter, MacChangeEthAddresses, MacChangeFilter, MacCloseAction, NdisAllocateSpinLock, NdisIndicateReceive


EthDeleteFilter

VOID
 EthDeleteFilter(
IN PETH_FILTER Filter
);

EthDeleteFilter is called by a NIC driver to delete the memory associated with an Ethernet filter library database.

Parameters

Filter
Points to the filter library database memory to be deleted.

Comments

This function assumes that any active filters have been cleared from the database.

Drivers usually call EthDeleteFilter when they are unloading.

Callers of EthDeleteFilter run at IRQL PASSIVE_LEVEL.

See Also

EthCreateFilter


EthDeleteFilterOpenAdapter

NDIS_STATUS
 EthDeleteFilterOpenAdapter(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest
 );

EthDeleteFilterOpenAdapter is called by a NIC driver to delete NIC binding filter information from the Ethernet filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.

Return Value

EthDeleteFilterOpenAdapter can return the following status codes:

NDIS_STATUS_CLOSING_INDICATING
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

EthDeleteFilterOpenAdapter calls NIC driver’s functions associated with clearing filter classes and addresses. The NIC driver should not call this function if the action functions can return a status code that indicates neither pending nor success.

The NIC driver must be holding the filter library spinlock when it calls this function.

Callers of EthDeleteFilterOpenAdapter run at IRQL DISPATCH_LEVEL.

See Also

EthNoteFilterOpenAdapter


EthFilterAdjust

NDIS_STATUS
 EthFilterAdjust(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN UINT FilterClasses,
 IN BOOLEAN Set
);

EthFilterAdjust is called by a NIC driver to adjust an Ethernet filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.
FilterClasses
Specifies a bitmap of filter classes that this function adds and deletes.
Set
Specifies TRUE if changes do not result from the closing of the binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

EthFilterAdjust can return the following status codes:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

EthFilterAdjust calls a NIC driver’s function if no binding has used a particular filter class and at least one binding starts using that class. The function also calls an action function for the reverse situation. If the NIC driver action function returns a status code that indicates neither pending nor success, EthFilterAdjust restores the filter library database to its original state.

The NIC driver must be holding the filter library spinlock when it calls this function.

Callers of EthFilterAdjust run at IRQL DISPATCH_LEVEL.


EthFilterIndicateReceive

VOID
 EthFilterIndicateReceive(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE MacReceiveContext,
 IN PCHAR Address,
 IN PVOID HeaderBuffer,
 IN UINT HeaderBufferSize,
 IN PVOID LookaheadBuffer,
 IN UINT LookaheadBufferSize,
 IN UINT PacketSize
);

EthFilterIndicateReceive is called by a NIC driver to indicate a received packet to all filter library database bindings associated with an Ethernet address.

Parameters

Filter
Points to a previously created and initialized filter library database.
MacReceiveContext
Specifies a context the NIC driver associates with a packet received from the network.
Address
Points to the destination address the NIC driver has already extracted from the received packet.
HeaderBuffer
Points to the virtual address of the virtually contiguous buffer containing the packet header. The buffer is valid only within the current call to this function.
HeaderBufferSize
Indicates the length, in bytes, of the packet header.
LookaheadBuffer
Points to the virtual address of the virtually contiguous buffer containing the first bytes of packet data. The packet buffer is valid only for the current call to this function.
LookaheadBufferSize
Specifies the size, in bytes, of the lookahead buffer.
PacketSize
Specifies the size, in bytes, of the received packet data. This parameter number is not related to the lookahead buffer, but rather indicates the size of the received packet data. This allows a protocol driver to call NdisTransferData and to transfer data in the packet, as necessary. The data length does not include the packet header length.

Comments

Instead of calling NdisIndicateReceive without filtering, the NIC driver calls EthFilterIndicateReceive to filter a received packet so only the appropriate bindings receive it. EthFilterIndicateReceive then calls the ProtocolReceive function of each protocol driver to which the binding filter applies.

In its calls to ProtocolReceive, this function passes each protocol driver a pointer to the packet header as well as a pointer to some or all of the packet data. The protocol driver can inspect the header and data in the packet, optionally copy as much of the header or data as is visible, or optionally call NdisTransferData to instruct the NIC driver to copy the data into a protocol-supplied packet.

Callers of EthFilterIndicateReceive run at IRQL <= DISPATCH_LEVEL.

See Also

EthFilterIndicateReceiveComplete, MacTransferData, NdisIndicateReceive, NdisTransferData, ProtocolReceive


EthFilterIndicateReceiveComplete

VOID
 EthFilterIndicateReceiveComplete(
IN PETH_FILTER Filter
);

EthFilterIndicateReceiveComplete is called by a NIC driver to indicate to all bound protocol drivers that EthFilterIndicateReceive has returned.

Parameters

Filter
Points to a previously created and initialized Ethernet filter library database.

Comments

To deliver the necessary indications, EthFilterIndicateReceiveComplete calls each protocol’s ProtocolReceiveComplete function once for each indication.

Callers of EthFilterIndicateReceiveComplete run at IRQL <= DISPATCH_LEVEL.

See Also

EthFilterIndicateReceive, ProtocolReceiveComplete


EthNoteFilterOpenAdapter

BOOLEAN
 EthNoteFilterOpenAdapter(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE MacBindingHandle,
 IN NDIS_HANDLE NdisBindingContext,
 OUT PNDIS_HANDLE NdisFilterHandle
);

EthNoteFilterOpenAdapter is called by the NIC driver to add a binding filter to the Ethernet filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
MacBindingHandle
Specifies the handle the NIC driver associates with the binding.
NdisBindingContext
Specifies the context the NDIS interface library associates with the binding.
NdisFilterHandle
Points to a caller-supplied memory location in which this function returns the handle that the filter library associates with the binding filter.

Return Value

EthNoteFilterOpenAdapter returns TRUE if it adds the binding filter to the filter library database. The function returns FALSE if it cannot add the binding filter because the database already contains the maximum number of filters.

Comments

A caller must be holding the database spin lock to call EthNoteFilterOpenAdapter, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

EthDeleteFilterOpenAdapter


EthNumberOfOpenFilterAddresses

UINT
 EthNumberOfOpenFilterAddresses(
IN PETH_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

EthNumberOfOpenFilterAddresses is called by the NIC driver to count the number of Ethernet filter library database multicast addresses in a binding filter.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

EthNumberOfOpenFilterAddresses returns the number of multicast addresses in the binding filter.

Comments

A caller must be holding the database spin lock to call EthNumberOfOpenFilterAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

EthQueryOpenFilterAddresses


EthQueryGlobalFilterAddresses

VOID
 EthQueryGlobalFilterAddresses(
OUT PNDIS_STATUS Status,
 IN PETH_FILTER Filter,
 IN UINT SizeOfArray,
 OUT PUINT NumberOfAddresses,
 IN OUT CHAR AddressArray[][ETH_LENGTH_OF_ADDRESS]
);

EthQueryGlobalFilterAddresses is called by the NIC driver to query the Ethernet filter library database for a list of multicast addresses associated with the network interface card.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the query operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_SUCCESS

Filter
Points to a previously created and initialized filter library database.
SizeOfArray
Specifies the byte count for the two-dimensional array to hold the addresses.
NumberOfAddresses
Points to the caller-supplied variable in which this function returns the number of addresses that it writes to the array.
AddressArray[][ETH_LENGTH_OF_ADDRESS]
Specifies the two-dimensional, caller-supplied array in which this function returns addresses currently associated with the network interface card.

Comments

The NIC driver calls this function before it alters registers on the network interface card to effect filtering. The timing of the call is important if a binding has altered the multicast address list. Before calling this function, the NIC driver uses ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES to obtain the number of multicast addresses to be retrieved.

A caller must be holding the database spin lock to call EthQueryGlobalFilterAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

ETH_NUMBER_OF_GLOBAL_FILTER_ADDRESSES


EthQueryOpenFilterAddresses

VOID
 EthQueryOpenFilterAddresses(
OUT PNDIS_STATUS Status,
 IN PETH_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN UINT SizeOfArray,
 OUT PUINT NumberOfAddresses,
 OUT CHAR AddressArray[][ETH_LENGTH_OF_ADDRESS]
);

EthQueryOpenFilterAddresses is called by the NIC driver to query the Ethernet filter library database for a list of multicast addresses associated with a binding.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the query operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_SUCCESS

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
SizeOfArray
Specifies the byte count for the two-dimensional array to contain the addresses.
NumberOfAddresses
Points to the caller-supplied variable in which this function returns the number of addresses that it writes to the array.
AddressArray[][ETH_LENGTH_OF_ADDRESS]
Specifies the two-dimensional, caller-supplied array to which this function writes addresses currently associated with the binding.

Comments

The NIC driver calls this function before it alters registers on the network interface card to effect filtering. The timing of the call is important if another binding has altered the multicast address list. Before calling this function, the NIC driver calls EthNumberOfOpenFilterAddresses to obtain the number of multicast addresses to be retrieved.

A caller must be holding the database spin lock to call EthQueryOpenFilterAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

EthNumberOfOpenFilterAddresses


EthShouldAddressLoopBack

BOOLEAN
 EthShouldAddressLoopBack(
IN PETH_FILTER Filter,
IN CHAR Address[ETH_LENGTH_OF_ADDRESS]
);

EthShouldAddressLoopBack returns FALSE if a packet bound for a given Ethernet address cannot possibly loop back.

Parameters

Filter
Points to a previously created and initialized filter library database.
Address[ETH_LENGTH_OF_ADDRESS]
Specifies an Ethernet address this function uses to determine a need for packet loopback.

Return Value

EthShouldAddressLoopBack returns TRUE if the packet is likely to loop back. It returns FALSE if there is no chance the packet will loop back.

Comments

EthShouldAddressLoopBack does not check whether the source address is equal to the destination address.

Callers of EthShouldAddressLoopBack run at IRQL <= DISPATCH_LEVEL.

See Also

ETH_COPY_NETWORK_ADDRESS


FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES

UINT
 FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES(
IN PFDDI_FILTER Filter
);

FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES is called by the NIC driver or another FDDI filter library function or macro to count the number of filter library database multicast long addresses.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES returns the number of multicast long addresses associated with the FDDI filter library database.

Comments

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNumberOfOpenFilterLongAddresses


FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES

UINT
 FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES(
IN PFDDI_FILTER Filter
);

FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES is called by the NIC driver or another FDDI filter library function or macro to count the number of filter library database multicast short addresses.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES returns the number of multicast short addresses associated with the FDDI filter library database.

Comments

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNumberOfOpenFilterShortAddresses


FDDI_QUERY_FILTER_CLASSES

UINT
 FDDI_QUERY_FILTER_CLASSES(
IN PFDDI_FILTER Filter
);

FDDI_QUERY_FILTER_CLASSES is called by the NIC driver to query the FDDI filter classes for a network interface card.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

FDDI_QUERY_FILTER_CLASSES returns a value representing the currently enabled filter classes.

Comments

The NIC driver calls this macro before it alters registers on its NIC to effect filtering. The timing of the call is important if a binding has altered the multicast address list. Before calling this macro, the NIC driver should call FddiNumberOfOpenFilterLongAddresses, FddiNumberOfOpenFilterShortAddresses, or both.

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNumberOfOpenFilterLongAddresses, FddiNumberOfOpenFilterShortAddresses


FDDI_QUERY_PACKET_FILTER

UINT
 FDDI_QUERY_PACKET_FILTER(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

FDDI_QUERY_PACKET_FILTER is called by the NIC driver to query the FDDI filter classes for a binding.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

FDDI_QUERY_PACKET_FILTER returns a value representing the currently enabled filter classes for the binding.

Comments

The NIC driver calls this macro before it alters registers on the network interface card to effect filtering. The timing of the call is important if another binding has altered the filter classes. Before calling this macro, the NIC driver should call FddiNumberOfOpenFilterLongAddresses or FddiNumberOfOpenFilterShortAddresses to obtain the number of multicast addresses in the binding filter.

A caller must be holding the database spin lock to use this macro, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNumberOfOpenFilterLongAddresses, FddiNumberOfOpenFilterShortAddresses


FddiChangeFilterLongAddresses

NDIS_STATUS
 FddiChangeFilterLongAddresses(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN UINT AddressCount,
 IN CHAR Addresses[][FDDI_LENGTH_OF_LONG_ADDRESS],
 IN BOOLEAN Set
);

FddiChangeFilterLongAddresses is called by the NIC driver to attempt to change the multicast long address list for a binding filter in the FDDI filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.
AddressCount
Specifies the number of elements to be added to the multicast long address list.
Addresses[][FDDI_LENGTH_OF_LONG_ADDRESS]
Specifies a two-dimensional character array to contain the new multicast long address list for the specified binding. FDDI_LENGTH_OF_LONG_ADDRESS is a constant specifying the length, in bytes, of an FDDI long address (defined in ffilter.h). The array is a sequence of 48-bit FDDI addresses (no padding between addresses).
Set
Specifies TRUE if changes do not result from the closing of a binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

FddiChangeFilterLongAddresses can return the following status codes:

NDIS_STATUS_MULTICAST_FULL
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

If FddiChangeFilterLongAddresses does not change the filter long address list, it has no effect on this list or the multicast address list for the network interface card. If this function does change the filter long address list, it calls a NIC driver action function and waits for it to return. If the action function returns a status code that indicates neither pending nor success, FddiChangeFilterLongAddresses restores the filter library database to its original state.

A caller must be holding the database spin lock to call FddiChangeFilterLongAddresses, so the driver is running at IRQL DISPATCH_LEVEL.


FddiChangeFilterShortAddresses

NDIS_STATUS
 FddiChangeFilterShortAddresses(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN UINT AddressCount,
 IN CHAR Addresses[][FDDI_LENGTH_OF_SHORT_ADDRESS],
 IN BOOLEAN Set
);

FddiChangeFilterShortAddresses is called by the NIC driver to attempt to change the multicast short address list for a binding filter in the FDDI filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.
AddressCount
Specifies the number of elements to be added to the multicast short address list.
Addresses[][FDDI_LENGTH_OF_SHORT_ADDRESS]
Specifies a two-dimensional character array to contain the new multicast short address list for the specified binding. FDDI_LENGTH_OF_SHORT_ADDRESS is a constant specifying the length, in bytes, of an FDDI short address (defined in ffilter.h). The array is a sequence of 2-byte FDDI addresse with no padding between addresses.
Set
Specifies TRUE if changes do not result from the closing of a binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

FddiChangeFilterShortAddresses can return the following status codes:

NDIS_STATUS_MULTICAST_FULL
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

If FddiChangeFilterShortAddresses does not change the filter short address list, it has no effect on this list or the multicast address list for the network interface card. If this function does change the filter short address list, it calls a NIC driver action function and waits for it to return. If the action function returns a status code that indicates neither pending nor success, FddiChangeFilterShortAddresses restores the filter library database to its original state.

A caller must be holding the database spin lock to call FddiChangeFilterShortAddresses, so the driver is running at IRQL DISPATCH_LEVEL.


FddiCreateFilter

BOOLEAN
 FddiCreateFilter(
IN UINT MaximumMulticastLongAddresses,
 IN UINT MaximumMulticastShortAddresses,
 IN FDDI_ADDRESS_CHANGE AddressChangeAction,
 IN FDDI_FILTER_CHANGE FilterChangeAction,
 IN FDDI_DEFERRED_CLOSE CloseAction,
 IN PUCHAR AdapterLongAddress,
 IN PUCHAR AdapterShortAddress,
 IN PNDIS_SPIN_LOCK Lock,
 OUT PFDDI_FILTER *Filter
);

FddiCreateFilter is called by the NIC driver. It creates and initializes an FDDI filter library database associated with one network interface card.

Parameters

MaximumMulticastLongAddresses
Specifies the maximum number of multicast long addresses to be supported by the filter.
MaximumMulticastShortAddresses
Specifies the maximum number of multicast short addresses to be supported by the filter.
AddressChangeAction
Specifies the address of the caller’s MacChangeFddiAddresses function, which the FDDI filter library calls when a change occurs in the list of multicast addresses that the network interface card enables.
FilterChangeAction
Specifies the address of the caller’s MacChangeFilter function, which the FDDI filter library calls when a binding sets or clears a particular filter class. The binding must be the first or only binding using the filter class.
CloseAction
Specifies the address of the caller’s MacCloseAction function, which the FDDI filter library calls if a binding closes while it is indicating incoming data through NdisIndicateReceive.
AdapterLongAddress
Points to the long network address of the network interface card.
AdapterShortAddress
Points to the short network address of the network interface card.
Lock
Points to an initialized spin lock that the driver holds when it requires exclusive access to the filter library database.
Filter
Points to a calling function location to which this function writes a pointer to the created filter.

Return Value

FddiCreateFilter returns TRUE if it creates the FDDI filter library database successfully.

Comments

The NIC driver uses the FDDI filter library to filter received network packets. Using the destination address of a received packet, the filter library determines the protocol driver bindings that receive indications for the packet.

Callers of FddiCreateFilter run at IRQL PASSIVE_LEVEL.

See Also

FddiDeleteFilter, MacChangeFddiAddresses, MacChangeFilter, MacCloseAction, NdisAllocateSpinLock, NdisIndicateReceive


FddiDeleteFilter

VOID
 FddiDeleteFilter(
IN PFDDI_FILTER Filter
);

FddiDeleteFilter is called by a NIC driver to delete the memory associated with an FDDI filter library database.

Parameters

Filter
Points to the filter library database memory to be deleted.

Comments

This function assumes that any active filters have been cleared from the database.

Drivers usually call FddiDeleteFilter when they are unloading.

Callers of FddiDeleteFilter run at IRQL PASSIVE_LEVEL.

See Also

FddiCreateFilter


FddiDeleteFilterOpenAdapter

NDIS_STATUS
 FddiDeleteFilterOpenAdapter(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest
);

FddiDeleteFilterOpenAdapter is called by a NIC driver to delete NIC binding filter information from the FDDI filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.

Return Value

FddiDeleteFilterOpenAdapter can return the following status codes:

NDIS_STATUS_CLOSING_INDICATING
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

FddiDeleteFilterOpenAdapter calls NIC driver action functions associated with clearing filter classes and addresses. The NIC driver should not call this function if the action functions can return a status code that indicates neither pending nor success.

A caller must be holding the database spin lock to call FddiDeleteFilterOpenAdapter, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNoteFilterOpenAdapter


FddiFilterAdjust

NDIS_STATUS
 FddiFilterAdjust(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN UINT FilterClasses,
 IN BOOLEAN Set
);

FddiFilterAdjust is called by a NIC driver to adjust an FDDI filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.
FilterClasses
Specifies a bitmap of filter classes that this function adds and deletes.
Set
Specifies TRUE if changes do not result from the closing of the binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

FddiFilterAdjust can return the following status codes:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

This function calls a NIC driver action function if no binding has used a particular filter class and at least one binding starts using that class. The function also calls an action function for the reverse situation. If the NIC driver action function returns a status code that indicates neither pending nor success, FddiFilterAdjust restores the filter library database to its original state.

A caller must be holding the database spin lock to call FddiFilterAdjust, so the driver is running at IRQL DISPATCH_LEVEL.


FddiFilterIndicateReceive

VOID
 FddiFilterIndicateReceive(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE MacReceiveContext,
 IN PCHAR Address,
 IN UINT AddressLength,
 IN PVOID HeaderBuffer,
 IN UINT HeaderBufferSize,
 IN PVOID LookaheadBuffer,
 IN UINT LookaheadBufferSize,
 IN UINT PacketSize
);

FddiFilterIndicateReceive is called by a NIC driver to indicate a received packet to all filter library database bindings associated with an FDDI address.

Parameters

Filter
Points to a previously created and initialized filter library database.
MacReceiveContext
Specifies a context that the NIC driver associates with a packet received from the network.
Address
Points to the destination address that the NIC driver has already extracted from the received packet.
AddressLength
Specifies the length, in bytes, of the destination address.
HeaderBuffer
Points to the virtual address of the virtually contiguous buffer containing the packet header. The buffer is valid only within the current call to this function.
HeaderBufferSize
Indicates the length, in bytes, of the packet header.
LookaheadBuffer
Points to the virtual address of the virtually contiguous buffer containing the first bytes of packet data. The packet buffer is valid only for the current call to this function.
LookaheadBufferSize
Specifies the size, in bytes, of the lookahead buffer.
PacketSize
Specifies the size, in bytes, of the received packet data. This number has nothing to do with the lookahead buffer. It indicates the size of the received packet data so that a protocol driver can call NdisTransferData to transfer the data in the packet, as necessary. The data length does not include the packet header length.

Comments

Instead of calling NdisIndicateReceive without filtering, the NIC driver calls FddiFilterIndicateReceive to filter a received packet so that only the appropriate bindings receive it. FddiFilterIndicateReceive then calls the ProtocolReceive function for each protocol driver to which the binding filter applies.

In its calls to ProtocolReceive, FddiFilterIndicateReceive passes each protocol driver a pointer to the packet header and a pointer to some or all of the packet data. The protocol driver can: inspect the header and data in the packet; optionally copy as much of the header or data as is visible; or optionally call NdisTransferData to instruct the NIC driver to copy the data into a protocol-supplied packet.

Callers of FddiFilterIndicateReceive run at IRQL <= DISPATCH_LEVEL.

See Also

FddiFilterIndicateReceiveComplete, MacTransferData, NdisIndicateReceive, NdisTransferData, ProtocolReceive


FddiFilterIndicateReceiveComplete

VOID
 FddiFilterIndicateReceiveComplete(
IN PFDDI_FILTER Filter
);

FddiFilterIndicateReceiveComplete is called by a NIC driver to indicate to all bound protocol drivers that FddiFilterIndicateReceive has returned.

Parameters

Filter
Points to a previously created and initialized FDDI filter library database.

Comments

To deliver the necessary indications, FddiFilterIndicateReceiveComplete calls the ProtocolReceiveComplete of each bound protocol once for each indication.

Callers of FddiFilterIndicateReceiveComplete run at IRQL <= DISPATCH_LEVEL.

See Also

FddiFilterIndicateReceive, ProtocolReceiveComplete


FddiNoteFilterOpenAdapter

BOOLEAN
 FddiNoteFilterOpenAdapter(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE MacBindingHandle,
 IN NDIS_HANDLE NdisBindingContext,
 OUT PNDIS_HANDLE NdisFilterHandle
);

FddiNoteFilterOpenAdapter is called by the NIC driver to add a binding filter to the FDDI filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
NdisFilterHandle
Points to a caller-supplied variable in which this function returns the handle that the filter library associates with the binding filter.

Return Value

FddiNoteFilterOpenAdapter returns TRUE if it adds the binding filter to the filter library database. The function returns FALSE if it cannot add the binding filter because the database already contains the maximum number of filters.

Comments

A caller must be holding the database spin lock to call FddiNoteFilterOpenAdapter, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiDeleteFilterOpenAdapter


FddiNumberOfOpenFilterLongAddresses

UINT
 FddiNumberOfOpenFilterLongAddresses(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

FddiNumberOfOpenFilterLongAddresses is called by the NIC driver to count the number of FDDI filter library database multicast long addresses in a binding filter.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

FddiNumberOfOpenFilterLongAddresses returns the number of multicast long addresses in the binding filter.

Comments

A caller must be holding the database spin lock to call FddiNumberOfOpenFilterLongAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES, FddiQueryOpenFilterLongAddresses


FddiNumberOfOpenFilterShortAddresses

UINT
 FddiNumberOfOpenFilterShortAddresses(
IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

FddiNumberOfOpenFilterShortAddresses is called by the NIC driver to count the number of FDDI filter library database multicast short addresses in a binding filter.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

FddiNumberOfOpenFilterShortAddresses returns the number of multicast short addresses in the binding filter.

Comments

A caller must be holding the database spin lock to call FddiNumberOfOpenFilterShortAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES, FddiQueryOpenFilterShort Addresses


FddiQueryGlobalFilterLongAddresses

VOID
 FddiQueryGlobalFilterLongAddresses(
OUT PNDIS_STATUS Status,
 IN PFDDI_FILTER Filter,
 IN UINT SizeOfArray,
 OUT PUINT NumberOfAddresses,
 IN OUT CHAR AddressArray[][FDDI_LENGTH_OF_LONG_ADDRESS]
);

FddiQueryGlobalFilterLongAddresses is called by the NIC driver to query the FDDI filter library database for a list of multicast long addresses associated with the network interface card.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the query operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_SUCCESS

Filter
Points to a previously created and initialized filter library database.
SizeOfArray
Specifies the byte count for the two-dimensional array that contains the addresses.
NumberOfAddresses
Points to the caller-supplied variable in which this function returns the number of multicast long addresses that it writes to the array.
AddressArray[][FDDI_LENGTH_OF_LONG_ADDRESS]
Specifies the two-dimensional, caller-supplied array in which this function writes multicast long addresses currently associated with the network interface card.

Comments

The NIC driver calls FddiQueryGlobalFilterLongAddresses before it alters hardware registers on the network interface card to effect filtering. The timing of the call is important if a binding has altered the multicast address list. Before calling this function, the NIC driver calls FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES to obtain the number of multicast long addresses to be retrieved.

A caller must be holding the database spin lock to call FddiQueryGlobalFilterLongAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FDDI_NUMBER_OF_GLOBAL_FILTER_LONG_ADDRESSES


FddiQueryGlobalFilterShortAddresses

VOID
 FddiQueryGlobalFilterShortAddresses(
OUT PNDIS_STATUS Status,
 IN PFDDI_FILTER Filter,
 IN UINT SizeOfArray,
 OUT PUINT NumberOfAddresses,
 IN OUT CHAR AddressArray[][FDDI_LENGTH_OF_SHORT_ADDRESS]
);

FddiQueryGlobalFilterShortAddresses is called by the NIC driver to query the FDDI filter library for a list of multicast short addresses associated with the network interface card.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the query operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_SUCCESS

Filter
Points to a previously created and initialized filter library database.
SizeOfArray
Specifies the byte count for the two-dimensional array that contains the addresses.
NumberOfAddresses
Points to the caller-supplied variable in which this function returns the number of multicast short addresses that it writes to the array.
AddressArray[][FDDI_LENGTH_OF_SHORT_ADDRESS]
Specifies the two-dimensional, caller-supplied array in which this function writes multicast short addresses currently associated with the network interface card.

Comments

The NIC driver calls FddiQueryGlobalFilterShortAddresses before it alters hardware registers on the network interface card to effect filtering. The timing of the call is important if a binding has altered the multicast address list. Before calling this function, the NIC driver should call FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES to obtain the number of multicast short addresses to be retrieved.

A caller must be holding the database spin lock to call FddiQueryGlobalFilterShortAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FDDI_NUMBER_OF_GLOBAL_FILTER_SHORT_ADDRESSES


FddiQueryOpenFilterLongAddresses

VOID
 FddiQueryOpenFilterLongAddresses(
OUT PNDIS_STATUS Status,
 IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN UINT SizeOfArray,
 OUT PUINT NumberOfAddresses,
 OUT CHAR AddressArray[][FDDI_LENGTH_OF_LONG_ADDRESS]
);

FddiQueryOpenFilterLongAddresses is called by the NIC driver to query the FDDI filter library for a list of multicast long addresses associated with a binding.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the query operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_SUCCESS

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
SizeOfArray
Specifies the byte count for the two-dimensional array to contain the multicast long addresses.
NumberOfAddresses
Points to the caller-supplied variable in which this function returns the number of multicast long addresses that it writes to the array.
AddressArray[][FDDI_LENGTH_OF_LONG_ADDRESS]
Specifies the two-dimensional caller-supplied array in which this function writes multicast long addresses currently associated with the binding.

Comments

The NIC driver calls FddiQueryOpenFilterLongAddresses before it alters registers on the network interface card to effect filtering. The timing of the call is important if another binding has altered the multicast address list. Before calling this function, the NIC driver calls FddiNumberOfOpenFilterLongAddresses to obtain the number of multicast long addresses to be retrieved.

A caller must be holding the database spin lock to call FddiQueryOpenFilterLongAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNumberOfOpenFilterLongAddresses


FddiQueryOpenFilterShortAddresses

VOID
 FddiQueryOpenFilterShortAddresses(
OUT PNDIS_STATUS Status,
 IN PFDDI_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN UINT SizeOfArray,
 OUT PUINT NumberOfAddresses,
 OUT CHAR AddressArray[][FDDI_LENGTH_OF_SHORT_ADDRESS]
);

FddiQueryOpenFilterShortAddresses is called by the NIC driver to query the FDDI filter library for a list of multicast short addresses associated with a binding.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the query operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_SUCCESS

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
SizeOfArray
Specifies the byte count for the two-dimensional array to contain the multicast short addresses.
NumberOfAddresses
Points to the caller-supplied variable in which this function returns the number of multicast short addresses that it writes to the array.
AddressArray[][FDDI_LENGTH_OF_SHORT_ADDRESS]
Specifies the two-dimensional, caller-supplied array in which this function writes multicast short addresses currently associated with the binding.

Comments

The NIC driver calls FddiQueryOpenFilterShortAddresses before it alters registers on the network interface card to effect filtering. The timing of the call is important if another binding has altered the multicast address list. Before calling this function, the NIC driver calls FddiNumberOfOpenFilterShortAddresses to obtain the number of multicast short addresses to be retrieved.

A caller must be holding the database spin lock to call FddiQueryOpenFilterShortAddresses, so the driver is running at IRQL DISPATCH_LEVEL.

See Also

FddiNumberOfOpenFilterShortAddresses


FddiShouldAddressLoopBack

BOOLEAN
 FddiShouldAddressLoopBack(
IN PFDDI_FILTER Filter,
IN CHAR Address[],
IN UINT LengthOfAddress
);

FddiShouldAddressLoopBack returns FALSE if a packet bound for a given FDDI address cannot possibly loop back.

Parameters

Filter
Points to a previously created and initialized filter library database.
Address[]
Specifies an FDDI network address this function uses to determine a need for packet loopback.
LengthOfAddress
Specifies the length of the Address array.

Return Value

FddiShouldAddressLoopBack returns TRUE if the packet is likely to loop back. It returns FALSE if there is no chance the packet will loop back.

Comments

FddiShouldAddressLoopBack does not check whether the source address is equal to the destination address.

Callers of FddiShouldAddressLoopBack run at IRQL <= DISPATCH_LEVEL.

See Also

FDDI_COMPARE_NETWORK_ADDRESSES


NdisAllocateDmaChannel

VOID
 NdisAllocateDmaChannel(
OUT PNDIS_STATUS Status,
 OUT PNDIS_HANDLE NdisDmaHandle,
 IN NDIS_HANDLE NdisAdapterHandle,
 IN PNDIS_DMA_DESCRIPTION DmaDescription,
 IN ULONG MaximumLength
);

NdisAllocateDmaChannel is called by the NIC driver to set up a DMA channel for future DMA operations.

Parameters

Status
Points to the caller-supplied variable in which this function returns the status of the channel allocation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_RESOURCE_CONFLICT
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

NdisDmaHandle
Points to a caller-supplied variable in which this function returns the handle that the NDIS interface library associates with the DMA channel.
NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
DmaDescription
Points to the description of the DMA transfer, defined as follows:

The NDIS_DMA_DESCRIPTION structure is defined as follows:

typedef struct _NDIS_DMA_DESCRIPTION {
    BOOLEAN DemandMode;
    DMA_WIDTH DmaWidth;
    DMA_SPEED DmaSpeed;
    ULONG DmaPort;
} NDIS_DMA_DESCRIPTION, *PNDIS_DMA_DESCRIPTION;

Member

Value

DemandMode

TRUE if an ISA device operates in demand mode rather than single-byte mode.

DmaWidth

The transfer width of the DMA operation, one of: Width8Bits, Width16Bits, or Width32Bits.

DmaSpeed

The transfer speed of the DMA operation, one of: Compatible, TypeA, TypeB, or TypeC.

DmaPort

For a NIC on an I/O bus with the interface type MicroChannel, the I/O port used. Otherwise, this member must be zero.

MaximumLength
Specifies the maximum length, in bytes, of the DMA transfer using the channel.

Comments

The NIC driver specifies the DMA channel in an NDIS_ADAPTER_INFORMATION structure that it passes to NdisRegisterAdapter.
The NIC driver should call NdisAllocateDmaChannel only during NIC driver initialization from its DriverEntry or MacAddAdapter function.

Slave DMA network interface cards that initiate their own DMA operations use this function. The driver of a busmaster card does not need to allocate a DMA channel.

Callers of NdisAllocateDmaChannel run at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, NdisFreeDmaChannel, NdisReadDmaCounter, NdisRegisterAdapter


NdisAllocateSharedMemory

VOID
 NdisAllocateSharedMemory(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Length,
 IN BOOLEAN Cached,
 OUT PVOID *VirtualAddress,
 OUT PNDIS_PHYSICAL_ADDRESS PhysicalAddress
);

NdisAllocateSharedMemory is called by the NIC driver to request the operating system to allocate memory the NIC driver shares with a busmaster network interface card during DMA operations.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Length
Specifies the size, in bytes, of the memory.
Cached
Specifies TRUE if the driver requests cached memory.
VirtualAddress
Points to a caller-supplied memory location at which this function writes the base virtual address of the allocated memory. If memory of the specified type is not available, the pointer value is NULL.
PhysicalAddress
Points to a caller-supplied variable in which this function returns the corresponding physical address for the shared memory.

Comments

NIC drivers call this function during initialization. The operating system allocates the memory as physically contiguous, either cached or noncached. Because this function writes the physical address of the shared memory, the NIC driver must not call NdisGetBufferPhysicalMapping.

If the allocated memory is cached and the cache needs flushing, the NIC driver must call NdisAllocateBuffer to allocate a buffer descriptor for the memory. It then calls NdisFlushBuffer to flush the cache.

Callers of NdisAllocateSharedMemory run at IRQL PASSIVE_LEVEL.

See Also

NdisAllocateBuffer, NdisFlushBuffer, NdisFreeSharedMemory


NdisCancelTimer

VOID
 NdisCancelTimer(
IN PNDIS_TIMER Timer,
 OUT PBOOLEAN TimerCancelled
);

NdisCancelTimer is called by the NIC driver to cancel a timer object that NdisSetTimer has set.

Parameters

Timer
Points to an initialized and set timer object.
TimerCancelled
Points to a caller-supplied variable in which this function returns TRUE if it cancels a set timer that has not expired. The function returns FALSE if the timer has expired or has never been set.

Comments

This function cancels the immediately preceding call to NdisSetTimer only if the interval passed to NdisSetTimer has not yet expired.

Callers of NdisCancelTimer run at IRQL <= DISPATCH_LEVEL.

See Also

NdisInitializeTimer, NdisSetTimer


NdisCompleteBufferPhysicalMapping

VOID
 NdisCompleteBufferPhysicalMapping(
IN NDIS_HANDLE NdisAdapterHandle,
 IN PNDIS_BUFFER Buffer,
 IN ULONG PhysicalMapRegister
);

NdisCompleteBufferPhysicalMapping is called by the NIC driver to indicate to the operating system that a physical address mapping is no longer needed.

Parameters

NdisAdapterHandle
Identifies the handle that the NDIS interface library associates with the network interface card.
Buffer
Points to a buffer descriptor.
PhysicalMapRegister
Specifies the index of the map register used for the mapping.

Comments

This function releases map registers allocated with a preceding call to NdisStartBufferPhysicalMapping when a DMA transfer is complete.

Callers of NdisCompleteBufferPhysicalMapping run at IRQL <= DISPATCH_LEVEL.

See Also

NdisStartBufferPhysicalMapping


NdisCompleteCloseAdapter

VOID
 NdisCompleteCloseAdapter(
IN NDIS_HANDLE NdisBindingContext,
 IN NDIS_STATUS Status
);

NdisCompleteCloseAdapter is called by the NIC driver for an asynchronous close operation.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
Status
Specifies the final status of the network interface card close operation.

Comments

NdisCompleteCloseAdapter calls the ProtocolCloseAdapterComplete function of the protocol driver that initiated the close operation.

Callers of NdisCompleteCloseAdapter run at IRQL PASSIVE_LEVEL.

See Also

MacCloseAdapter, NdisCloseAdapter, ProtocolCloseAdapterComplete


NdisCompleteDmaTransfer

VOID
 NdisCompleteDmaTransfer(
OUT PNDIS_STATUS Status,
 IN PNDIS_HANDLE NdisDmaHandle,
 IN PNDIS_BUFFER Buffer,
 IN ULONG Offset,
 IN ULONG Length,
 IN BOOLEAN WriteToDevice
);

NdisCompleteDmaTransfer is called by the NIC driver to indicate that a DMA transfer between host and network interface card has completed.

Parameters

Status
Points to the caller-supplied variable in which this function returns the status of the transfer completion. Possible values are:

NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

NdisDmaHandle
Points to the handle that the NDIS interface library associates with the DMA channel.
Buffer
Points to the DMA buffer descriptor.
Offset
Specifies the DMA buffer offset.
Length
Specifies the length, in bytes, of the DMA transfer.
WriteToDevice
Specifies TRUE for a download (transmit) operation.

Comments

For a transfer from the host to the network interface card (download), the NIC driver must call NdisCompleteDmaTransfer to verify the network interface card has actually received data for transmission over the network. For a transfer from the NIC to the host (upload), the driver must call this function before the driver can read the received data from host memory.

NdisCompleteDmaTransfer also resets the host DMA controller in preparation for further DMA transfers.

Callers of NdisCompleteDmaTransfer run at IRQL <= DISPATCH_LEVEL.

See Also

NdisReadDmaCounter, NdisSetupDmaTransfer


NdisCompleteOpenAdapter

VOID
 NdisCompleteOpenAdapter(
IN NDIS_HANDLE NdisBindingContext,
 IN NDIS_STATUS Status,
 IN NDIS_STATUS OpenErrorStatus
);

NdisCompleteOpenAdapter is called by the NIC driver for an asynchronous open operation.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
Status
Specifies the final status of the network interface card open operation.
OpenErrorStatus
Specifies additional information about the reason for a failure of the network interface card open operation.

Comments

NdisCompleteOpenAdapter calls the ProtocolOpenAdapterComplete function of the protocol driver that initiated the open operation.

Callers of NdisCompleteOpenAdapter run at IRQL PASSIVE_LEVEL.

See Also

MacOpenAdapter, NdisOpenAdapter, ProtocolOpenAdapterComplete


NdisCompleteQueryStatistics

VOID
 NdisCompleteQueryStatistics(
IN NDIS_HANDLE NdisAdapterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN NDIS_STATUS Status
);

NdisCompleteQueryStatistics is called by the NIC driver to perform postprocessing for an asynchronous global query operation.

Parameters

NdisAdapterHandle
Identifies the handle that the NDIS interface library associates with the network interface card.
NdisRequest
Points to a request structure. The data in the InformationBuffer member must be valid, and the BytesWritten member must reflect how much data is provided.
Status
Indicates the final status that the caller’s MacQueryGlobalStatistics function returns.

Comments

This function completes differently from other Ndis Xxx functions because NDIS never calls a driver’s MacQueryGlobalStatistics function for a specific network interface card binding.

Callers of NdisCompleteQueryStatistics run at IRQL <= DISPATCH_LEVEL.

See Also

MacQueryGlobalStatistics


NdisCompleteRequest

VOID
 NdisCompleteRequest(
IN NDIS_HANDLE NdisBindingContext,
 IN PNDIS_REQUEST NdisRequest,
 IN NDIS_STATUS Status
);

NdisCompleteRequest is called by the NIC driver for an asynchronous request operation.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
NdisRequest
Points to a request structure.
Status
Specifies the final status of the request.

Comments

NdisCompleteRequest calls the ProtocolRequestComplete function of the protocol driver that originally called NdisRequest.

Callers of NdisCompleteRequest run at IRQL <= DISPATCH_LEVEL.

See Also

MacRequest, NdisRequest, ProtocolRequestComplete


NdisCompleteReset

VOID
 NdisCompleteReset(
IN NDIS_HANDLE NdisBindingContext,
 IN NDIS_STATUS Status
);

NdisCompleteReset is called by the NIC driver for an asynchronous reset operation. It forwards reset information to the ProtocolResetComplete function of the protocol driver that initiated the reset.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
Status
Specifies the final status of the reset operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_HARD_ERRORS
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_NOT_RESETTABLE
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SOFT_ERRORS
NDIS_STATUS_SUCCESS

Comments

NdisCompleteReset calls the ProtocolResetComplete function of the protocol driver that originally called NdisReset.

Callers of NdisCompleteReset run at IRQL <= DISPATCH_LEVEL.

See Also

MacReset, NdisReset, ProtocolResetComplete


NdisCompleteSend

VOID
 NdisCompleteSend(
IN NDIS_HANDLE NdisBindingContext,
 IN PNDIS_PACKET Packet,
 IN NDIS_STATUS Status
);

NdisCompleteSend is called by the NIC driver for an asynchronous send operation.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
Packet
Points to the caller-supplied descriptor for the packet that the protocol driver passes for transmission on the medium.
Status
Indicates the final status of the send operation.

Comments

NdisCompleteSend calls the ProtocolSendComplete function of the protocol driver that originally called NdisSend.

The NIC driver can call NdisCompleteSend before the initiating call to NdisSend completes. It can call NdisCompleteSend even before the protocol driver has time to inspect the status that NdisSend provides.

Callers of NdisCompleteSend run at IRQL <= DISPATCH_LEVEL.

See Also

MacSend, NdisSend, ProtocolSendComplete


NdisCompleteTransferData

VOID
 NdisCompleteTransferData(
IN NDIS_HANDLE NdisBindingContext,
 IN PNDIS_PACKET Packet,
 IN NDIS_STATUS Status,
 IN UINT BytesTransferred
);

NdisCompleteTransferData is called by the NIC driver for an asynchronous data-transfer operation.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
Packet
Points to the packet descriptor.
Status
Specifies the final status of the transfer data operation.
BytesTransferred
Specifies the number of bytes of data that the NIC driver transfers.

Comments

NdisCompleteTransferData calls the ProtocolRequestComplete function of the protocol driver that originally called NdisTransferData.

Callers of NdisCompleteTransferData run at IRQL <= DISPATCH_LEVEL.

See Also

MacTransferData, NdisTransferData, ProtocolTransferDataComplete


NdisDeregisterAdapter

NDIS_STATUS
 NdisDeregisterAdapter(
IN NDIS_HANDLE NdisAdapterHandle
);

NdisDeregisterAdapter is called by the NIC driver to complete network interface card removal.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.

Return Value

NdisDeregisterAdapter can return the following status codes:

NDIS_STATUS_FAILURE
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

Comments

Drivers usually call NdisDeregisterAdapter when they are unloading. This function terminates the registration of the network interface card with the NDIS interface library and frees all the resources that were allocated when the NIC driver called NdisRegisterAdapter.

Before a driver calls this function, it must call NdisFreeMemory to free all storage associated with the network interface card.

Callers of NdisDeregisterAdapter run at IRQL PASSIVE_LEVEL.

See Also

MacRemoveAdapter, NdisFreeMemory, NdisRegisterAdapter


NdisDeregisterAdapterShutdownHandler

VOID
 NdisDeregisterAdapterShutdownHandler(
IN NDIS_HANDLE NdisAdapterHandle
);

NdisDeregisterAdapterShutdownHandler is called by the NIC driver to remove its registered MacShutdown function from the list of those to be called during system shutdown.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.

Comments

Drivers usually call NdisDeregisterAdapterShutdownHandler when they are unloading.

Callers of NdisDeregisterAdapterShutdownHandler run at IRQL PASSIVE_LEVEL.

See Also

MacShutdown, NdisRegisterAdapterShutdownHandler


NdisDeregisterMac

VOID
 NdisDeregisterMac(
OUT PNDIS_STATUS Status,
 IN NDIS_HANDLE NdisMacHandle
);

NdisDeregisterMac is called by a NIC driver’s MacUnload function.

Parameters

Status
Points to the caller-supplied variable in which this function returns the final status of the deregistration. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_PENDING
NDIS_STATUS_SUCCESS

NdisMacHandle
Specifies the handle that the NDIS interface library associates with the NIC driver.

Comments

NdisDeregisterMac removes the driver from the NDIS interface library connection.

Callers of NdisDeregisterMac run at IRQL PASSIVE_LEVEL.

See Also

MacUnload, NdisRegisterMac


NdisFreeDmaChannel

VOID
 NdisFreeDmaChannel(
IN PNDIS_HANDLE NdisDmaHandle
);

NdisFreeDmaChannel is called by the NIC driver to free a DMA channel allocated by a call to NdisAllocateDmaChannel.

Parameters

NdisDmaHandle
Points to the handle that the NDIS interface library associates with the DMA channel.

Comments

Drivers usually call NdisFreeDmaChannel when they are unloading.

Callers of NdisFreeDmaChannel run at IRQL PASSIVE_LEVEL.

See Also

MacUnload, NdisAllocateDmaChannel


NdisFreeSharedMemory

VOID
 NdisFreeSharedMemory(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Length,
 IN BOOLEAN Cached,
 IN PVOID VirtualAddress,
 IN NDIS_PHYSICAL_ADDRESS PhysicalAddress
);

NdisFreeSharedMemory is called by the NIC driver to free memory obtained with NdisAllocateSharedMemory.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Length
Specifies the size, in bytes, of the memory.
Cached
Specifies TRUE if the driver is freeing cached memory.
VirtualAddress
Points to the virtual address of the memory.
PhysicalAddress
Specifies the physical address of the memory.

Comments

Drivers usually call NdisFreeSharedMemory when they are unloading.

Callers of NdisFreeSharedMemory run at IRQL PASSIVE_LEVEL.

See Also

MacUnload, NdisAllocateSharedMemory


NdisIndicateReceive

VOID
 NdisIndicateReceive(
OUT PNDIS_STATUS Status,
 IN NDIS_HANDLE NdisBindingContext,
 IN NDIS_HANDLE MacReceiveContext,
 IN PVOID HeaderBuffer,
 IN UINT HeaderBufferSize,
 IN PVOID LookaheadBuffer,
 IN UINT LookaheadBufferSize,
 IN UINT PacketSize
);

NdisIndicateReceive is called by the NIC driver to forward a receive indication to a bound protocol driver.

Parameters

Status
Points to the caller-supplied variable in which this function returns the final status of the receive operation. Possible values are:

NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
MacReceiveContext
Specifies the context that the NIC driver associates with a packet received from the network.
HeaderBuffer
Points to the virtual address of the virtually contiguous buffer containing the packet header. The buffer is valid only within the current call to this function.
HeaderBufferSize
Specifies the length, in bytes, of the packet header.
LookaheadBuffer
Points to the virtual address of the virtually contiguous buffer containing the first LookaheadBufferSize bytes of the packet data (or less if the packet data size is shorter). The buffer is valid only within the current call to this function. (All NIC drivers must be able to support at least 256 bytes of lookahead data.)
LookaheadBufferSize
Specifies the size, in bytes, of the lookahead buffer. The NIC driver ensures this number is at least as large as the size set in a call to the NIC driver’s MacRequest function or the size of the packet, whichever value is smaller. If the packet size is smaller than the lookahead size, the lookahead buffer contains the entire packet.
PacketSize
Specifies the size, in bytes, of the packet data. This number has nothing to do with the lookahead buffer, but indicates the size of the packet data so the protocol driver can make subsequent calls to NdisTransferData to transfer the entire packet as necessary.The packet length does not include the header length.

Comments

To deliver the indication, this function calls the ProtocolReceive function of the driver identified by the given handle at NdisBindingContext.

Callers of NdisIndicateReceive run at IRQL <= DISPATCH_LEVEL.

See Also

MacRequest, MacTransferData, NdisTransferData, ProtocolReceive


NdisIndicateReceiveComplete

VOID
 NdisIndicateReceiveComplete(
IN NDIS_HANDLE NdisBindingContext
);

NdisIndicateReceiveComplete is called by the NIC driver to forward a receive complete indication to a protocol driver.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.

Comments

To deliver the indication, NdisIndicateReceiveComplete, calls the ProtocolReceiveComplete function of the driver identified by the given handle at NdisBindingContext.

Callers of NdisIndicateReceiveComplete run at IRQL <= DISPATCH_LEVEL.

See Also

ProtocolReceiveComplete


NdisIndicateStatus

VOID
 NdisIndicateStatus(
IN NDIS_HANDLE NdisBindingContext,
 IN NDIS_STATUS GeneralStatus,
 IN PVOID StatusBuffer,
 IN UINT StatusBufferSize
);

NdisIndicateStatus is called by the NIC driver to forward an indication of changes in network interface card status to a bound protocol driver.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
GeneralStatus
Specifies the NDIS_STATUS_XXX the NIC driver is indicating.
StatusBuffer
Points to a buffer containing media-specific data that depends on the value of GeneralStatus.
StatusBufferSize
Specifies the length, in bytes, of StatusBuffer.

Comments

To deliver the indication, NdisIndicateStatus, calls the ProtocolStatus function of the driver identified by the given handle at NdisBindingContext.

Callers of NdisIndicateStatus can be running at any IRQL.

See Also

MacReset, ProtocolStatus


NdisIndicateStatusComplete

VOID
 NdisIndicateStatusComplete(
IN NDIS_HANDLE NdisBindingContext
);

NdisIndicateStatusComplete is called by the NIC driver to forward a status complete indication to a protocol driver.

Parameters

NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.

Comments

To deliver the indication, NdisIndicateStatusComplete calls the ProtocolStatusComplete function of the driver identified by the given handle at NdisBindingContext.

Interrupts are enabled during the NdisIndicateStatusComplete operation. The NIC driver does not have to make calls to this function in a one-to-one correspondence with NdisIndicateStatus calls. It can issue a single NdisIndicateStatusComplete call for several calls to NdisIndicateStatus.

Callers of NdisIndicateStatusComplete can be running at any IRQL.

See Also

MacReset, NdisIndicateStatus, ProtocolStatusComplete


NdisInitializeInterrupt

VOID
 NdisInitializeInterrupt(
OUT PNDIS_STATUS Status,
 IN OUT PNDIS_INTERRUPT Interrupt,
 IN NDIS_HANDLE NdisAdapterHandle,
 IN PNDIS_INTERRUPT_SERVICE InterruptServiceRoutine,
 IN PVOID InterruptContext,
 IN PNDIS_DEFERRED_PROCESSING DeferredProcessingRoutine,
 IN UINT InterruptVector,
 IN UINT InterruptLevel,
 IN BOOLEAN SharedInterrupt,
 IN NDIS_INTERRUPT_MODE InterruptMode
);

NdisInitializeInterrupt is called by the NIC driver to prepare for interrupt processing during driver initialization.

Parameters

Status
Points to the caller-supplied variable in which this function returns the final status of the interrupt initialization. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_RESOURCE_CONFLICT
NDIS_STATUS_SUCCESS

Interrupt
Points to the caller-supplied variable of type NDIS_INTERRUPT in which this function fills in information about the interrupt. This varible is reserved for use by NDIS.
NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
InterruptServiceRoutine
Points to the caller’s MacInterruptServiceRoutine.
InterruptContext
Points to a caller-supplied context area. NDIS passes this pointer to the MacInterruptServiceRoutine when the NIC interrupts and to the MacDeferredProcessingRoutine.
DeferredProcessingRoutine
Points to the caller’s MacDeferredProcessingRoutine.
InterruptVector
Specifies the interrupt number that the network interface card uses. This function allows a distinction between InterruptVector and InterruptLevel. However, these parameters are equal for most network interface cards.
InterruptLevel
Specifies the interrupt level for the network interface card. For most NICs, InterruptLevel has the same value as InterruptVector.
SharedInterrupt
Specifies TRUE if the NIC driver shares the IRQ with other drivers. If the value is FALSE and another NIC driver is already registered on the interrupt vector, NdisInitializeInterrupt fails.
InterruptMode
Specifies the type of interrupt generated by the network interface card:

Type

Description

NdisInterruptLatched

Interrupts are latched. In other words, they are triggered by a transition from low to high on the interrupt line.

NdisInterruptLevelSensitive

Interrupts are level-sensitive. In other words, they are active as long as the interrupt line is asserted. If the MacInterruptServiceRoutine does not modify the network interface card registers so they no longer assert the interrupt line, the operating system can call this function again immediately after it returns, before the MacDeferredProcessingRoutine has a chance to run.

Comments

A NIC driver can call NdisInitializeInterrupt only during NIC driver initialization from its DriverEntry or MacAddAdapter function.

NdisInitializeInterrupt associates a NIC interrupt with the caller’s ISR and DPC functions, MacInterruptServiceRoutine and MacDeferredProcessingRoutine, respectively.

The operating system, through NDIS, calls the ISR and DPC functions any time the network interface card generates an interrupt. The MacInterruptServiceRoutine runs at DIRQL immediately after the interrupt. When it returns, the operating system calls the MacDeferredProcessingRoutine, which completes interrupt processing at IRQL DISPATCH_LEVEL.

There is no guarantee of a one-to-one correspondence between calls to the driver’s MacInterruptServiceRoutine and to MacDeferredProcessingRoutine functions. When the ISR returns control, the corresponding DPC is queued and this function typically executes immediately. However, another NIC interrupt can occur before the DPC is run, causing the ISR to run again in a uniprocessor machine. In a multiprocessor platform, the ISR might run concurrently on different processors. In either case, when the second iteration of the ISR returns control, a queued DPC is run once and only once.

The NIC driver is responsible for dismissing an interrupt on the network interface card itself. Dismissal refers to setting the state of the network interface card so the output interrupt line is no longer asserted (does not necessarily mean that further interrupts are enabled). The timing of this dismissal depends on the hardware mode of the network interface card, which is either level-sensitive or latched (also known as edge-triggered). The operating system acknowledges a network interface card interrupt in the host hardware interrupt controller, which allows other host interrupts.

When a level-sensitive interrupt is active, the NIC driver’s MacInterruptServiceRoutine must dismiss the interrupt. Timing for a latched interrupt is not as important as that for a level-sensitive interrupt. If necessary, the NIC driver can leave latched interrupt dismissal to its MacDeferredProcessingRoutine.

Callers of NdisInitializeInterrupt run at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, MacDeferredProcessingRoutine, MacInterruptServiceRoutine, NdisRegisterMac, NdisRemoveInterrupt,


NdisInitializeTimer

VOID
 NdisInitializeTimer(
IN OUT PNDIS_TIMER Timer,
 IN PNDIS_TIMER_FUNCTION TimerFunction,
 IN PVOID FunctionContext
);

NdisInitializeTimer is called by the NIC driver to initialize an object that represents a timer.

Parameters

Timer
Points to the caller-supplied memory location of an NDIS_TIMER-type timer object.
TimerFunction
Points to the caller’s MacTimerFunction.
FunctionContext
Points to the context passed to MacTimerFunction.

Comments

A driver must call NdisInitializeTimer, usually during initialization, if it has a MacTimerFunction. After initialization of the timer object, the NIC driver calls NdisSetTimer to set the timer delay. When the timer expires, the MacTimerFunction is run.

If a driver calls NdisCancelTimer before the previously set interval expires, its MacTimerFunction does not run until the driver again calls NdisSetTimer.

If a driver calls NdisSetTimer twice in succession with overlapping intervals, its MacTimerFunction runs once when the interval given in the second call expires.

Callers of NdisInitializeTimer run at IRQL PASSIVE_LEVEL.

See Also

MacTimerFunction, NdisCancelTimer, NdisSetTimer


NdisInitializeWrapper

VOID
 NdisInitializeWrapper(
OUT PNDIS_HANDLE NdisWrapperHandle,
 IN PVOID SystemSpecific1,
 IN PVOID SystemSpecific2,
 IN PVOID SystemSpecific3
);

NdisInitializeWrapper is called by a NIC driver from its DriverEntry routine to register itself with the NDIS library.

Parameters

NdisWrapperHandle
Points to the caller-supplied variable in which this function returns the handle that the NDIS interface library associates with its wrapper.

For Windows NT drivers, the next two parameters refer to the driver object and configuration registry path that the operating system passes to the DriverEntry function of the NIC driver.

SystemSpecific1
Points to a first OS-specific type.
SystemSpecific2
Points to a second OS-specific type.
SystemSpecific3
Points to a third OS-specific type. For Windows NT drivers, this pointer should be NULL.

Comments

This is the first Ndis Xxx function called in the DriverEntry routine of full-NIC drivers.

NdisInitializeWrapper allocates resources and initializes data structures that the NDIS library associates with the NIC driver. After this function returns, the driver calls NdisRegisterMac to register network interface cards.

Callers of NdisInitializeWrapper run at IRQL PASSIVE_LEVEL.

See Also

NdisRegisterMac, NdisTerminateWrapper


NdisMapIoSpace

VOID
 NdisMapIoSpace(
OUT PNDIS_STATUS Status,
 OUT PVOID *VirtualAddress,
 IN NDIS_HANDLE NdisAdapterHandle,
 IN NDIS_PHYSICAL_ADDRESS PhysicalAddress,
 IN UINT Length
);

NdisMapIoSpace maps a physical I/O address into a virtual address for a network interface card that has memory with a physical address in the host I/O space.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the mapping operation. Possible values are:

NDIS_STATUS_FAILURE
NDIS_STATUS_RESOURCE_CONFLICT
NDIS_STATUS_SUCCESS

Virtual Address
Points to the caller-supplied variable in which this function returns the mapped virtual base address. If the operating system cannot map the I/O space requested, this function returns NULL.
NdisAdapterHandle
Identifies the handle that the NDIS interface library associates with the network interface card.
PhysicalAddress
Indicates the physical address.
Length
Specifies the size in bytes of the device memory range, starting at the given physical address.

Comments

A NIC driver calls NdisMapIoSpace only during initialization from its DriverEntry or MacAddAdapter function. The NIC driver can then access its NIC’s device memory using the returned virtual address.

When a driver no longer needs the mapping, it calls NdisUnmapIoSpace, usually from its MacUnload or MacShutdown function.
A NIC driver cannot use this function to create a virtual mapping to something other than the device memory for its NIC.

Callers of NdisMapIoSpace run at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, MacShutdown, MacUnload, NdisUnmapIoSpace


NdisPciAssignResources

NDIS_STATUS
NdisPciAssignResources(
 IN NDIS_HANDLE NdisMacHandle,
 IN NDIS_HANDLE NdisWrapperHandle,
 IN NDIS_HANDLE WrapperConfigurationContext,
 IN ULONG SlotNumber,
 OUT PNDIS_RESOURCE_LIST * AssignedResources
);

NdisPciAssignResources returns a list of bus-relative hardware resources (such as an IRQ, I/O ports, device memory ranges) that the PCI NIC is using.

NdisMacHandle
The handle that the NDIS interface library associates with the NIC driver. This handle is a parameter returned by NdisRegisterMac.
NdisWrapperHandle
Specifies the handle associated with the NDIS interface library. For full-NIC drivers, this handle is passed to the caller’s MacAddAdapter function.
WrapperConfigurationContext
The handle passed to the caller’s MacAddAdapter function.
SlotNumber
The slot number of the caller’s PCI device.
AssignedResources
Points to a caller-supplied buffer in which this function returns a list of resources owned by the PCI device.

Return Value

NdisPciAssignResources returns STATUS_SUCCESS if it claimed a set of hardware resources for the caller in the registry.

Comments

Drivers of PCI devices call NdisPciAssignResources during initialization to claim hardware resources in the configuration registry.

The resource list returned by NdisPciAssignResources has been configured so that the resources used by the caller’s NIC do not conflict with other devices on the PCI bus nor in the system. A driver uses this list to make subsequent calls to other NDIS initialization functions, such as NdisInitializeInterrupt and NdisMapIoSpace.

Callers of NdisPciAssignResources run at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, NdisInitializeInterrupt, NdisMapIoSpace, NdisRegisterMac.


NdisReadBindingInformation

VOID
 NdisReadBindingInformation(
OUT PNDIS_STATUS Status,
OUT PNDIS_STRING *Binding,
IN NDIS_HANDLE ConfigurationHandle
);

NdisReadBindingInformation returns a pointer to the name of a NIC under a driver’s Parameters key in the registry.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the call as one of the following:
NDIS_STATUS_SUCCESS
The buffer at Binding contains a counted string, in the system-default character set, naming the adapter to which the caller will bind itself subsequently. For Windows NT drivers, this string contains Unicode characters.
NDIS_STATUS_FAILURE
The return value at Binding is NULL.
Binding
Points to a caller-supplied variable in which this function returns a pointer to the name that it retrieved from the registry.
ConfigurationHandle
Specifies the registry key handle returned by NdisOpenConfiguration.

Comments

NdisReadBindingInformation returns a pointer to a string that a higher-level NDIS driver can use to call NdisOpenAdapter. This function reads the registry to retrieve the name of the lower-level driver’s adapter to which the caller will bind itself.

NdisReadBindingInformation places this name in internally allocated storage that remains valid until the protocol calls NdisCloseConfiguration with the same ConfigurationHandle.

Callers of NdisReadBindingInformation run at IRQL PASSIVE_LEVEL.

See Also

NdisCloseConfiguration, NdisOpenAdapter, NdisOpenConfiguration, NdisReadConfiguration


NdisReadDmaCounter

ULONG
 NdisReadDmaCounter(
IN NDIS_HANDLE NdisDmaHandle
);

NdisReadDmaCounter is called by the driver of a slave NIC to retrieve the number of bytes remaining in the current DMA transfer.

Parameters

NdisDmaHandle
Specifies the handle that the NDIS interface library associates with the DMA channel.

Return Value

NdisReadDmaCounter returns the number of bytes remaining in the current DMA transfer.

Comments

This function reads the current value of a counter in the DMA controller currently using the DMA channel. Before the NIC driver calls this function, it must set up the DMA channel in a call to NdisAllocateDmaChannel.

Callers of NdisReadDmaCounter run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateDmaChannel


NdisReadPortBufferUchar

VOID
 NdisReadPortBufferUchar(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 OUT PUCHAR Buffer,
 IN ULONG Length
);

NdisReadPortBufferUchar is called by the NIC driver to read a specified number of UCHARs from its NIC into a buffer.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Buffer
Points to the caller-supplied buffer in which this function writes the UCHAR values read from Port.
Length
Specifies the number of pieces of data to read from Port to Buffer.

Comments

NdisReadPortBufferUchar reads sequentially each byte from the port. It determines how the host accesses the I/O port and processes the read request accordingly.

Callers of NdisReadPortBufferUchar can be running at any IRQL.

See Also

NdisRawReadPortBufferUchar, NdisReadPortUchar, NdisWritePortBufferUchar


NdisReadPortBufferUlong

VOID
 NdisReadPortBufferUlong(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 OUT PULONG Buffer,
 IN ULONG Length
);

NdisReadPortBufferUlong is called by the NIC driver to read a specified number of ULONGs from its NIC into a buffer.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Buffer
Points to the caller-supplied buffer in which this function writes the ULONG values read from Port.
Length
Specifies the number of pieces of data to read from Port to Buffer.

Comments

NdisReadPortBufferUlong reads sequentially each ULONG from the port. It determines how the host accesses the I/O port and processes the read request accordingly.

Callers of NdisReadPortBufferUlong can be running at any IRQL.

See Also

NdisRawReadPortBufferUlong, NdisReadPortUlong, NdisWritePortBufferUlong


NdisReadPortBufferUshort

VOID
 NdisReadPortBufferUshort(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 OUT PUSHORT Buffer,
 IN ULONG Length
);

NdisReadPortBufferUshort is called by the NIC driver to read a specified number of USHORTs from its NIC into a buffer.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Buffer
Points to the caller-supplied buffer in which this function writes the USHORT values read from Port.
Length
Specifies the number of pieces of data to read from Port to Buffer.

Comments

NdisReadPortBufferUshort reads sequentially each ULONG from the port. It determines how the host accesses the I/O port and processes the read request accordingly.

Callers of NdisReadPortBufferShort can be running at any IRQL.

See Also

NdisRawReadPortBufferUshort, NdisReadPortUshort, NdisWritePortBufferUshort


NdisReadPortUchar

VOID
 NdisReadPortUchar(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 OUT PUCHAR Data
);

NdisReadPortUchar is called by the NIC driver to read a UCHAR from its NIC’s I/O port.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Data
Points to the caller-supplied variable in which this function returns the byte read from Port.

Comments

Callers of NdisReadPortUchar can be running at any IRQL.

See Also

NdisRawReadPortUchar, NdisReadPortBufferUchar, NdisWritePortUchar


NdisReadPortUlong

VOID
 NdisReadPortUlong(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 OUT PULONG Data
);

NdisReadPortUlong is called by the NIC driver to read a ULONG from its NIC’s I/O port.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Data
Points to the caller-supplied variable in which this function returns the ULONG read from Port.

Comments

Callers of NdisReadPortUlong can be running at any IRQL.

See Also

NdisRawReadPortUlong, NdisReadPortBufferUlong, NdisWritePortUlong


NdisReadPortUshort

VOID
 NdisReadPortUshort(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 OUT PUSHORT Data
);

NdisReadPortUshort is called by the NIC driver to read a USHORT from its NIC’s I/O port.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Data
Points to the caller-supplied variable in which this function returns the USHORT read from Port.

Comments

Callers of NdisReadPortUshort can be running at any IRQL.

See Also

NdisRawReadPortUshort, NdisReadPortBufferUshort, NdisWritePortUshort


NdisRegisterAdapter

NDIS_STATUS
 NdisRegisterAdapter(
OUT PNDIS_HANDLE NdisAdapterHandle,
 IN NDIS_HANDLE NdisMacHandle,
 IN NDIS_HANDLE MacAdapterContext,
 IN NDIS_HANDLE WrapperConfigurationContext,
 IN PNDIS_STRING AdapterName,
 IN PVOID AdapterInformation
);

NdisRegisterAdapter associates a name with the caller’s NIC so the NIC driver can receive NdisOpenAdapter requests that protocol drivers direct to that netcard.

Parameters

NdisAdapterHandle
Points to a caller-supplied variable in which this function returns the handle that the NDIS interface library associates with the NIC.
NdisMacHandle
Specifies the handle that the NDIS interface library associates with the NIC driver.
MacAdapterContext
Specifies the context that the NIC driver associates with the network interface card. This value is typically a pointer to storage that the driver maintains to describe the network interface card. This state information is sometimes called the logical adapter.
WrapperConfigurationContext
Specifies the context that the NDIS interface library associates with configuration registry information for the network interface card.
AdapterName
Points to a variable-length string specifying the name of the NIC in the OS-default character set. For Windows NT drivers, this is a counted Unicode string.
AdapterInformation
Points to an NDIS_ADAPTER_INFORMATION structure set up by the caller. The structure at AdapterInformation is defined as follows:
typedef struct _NDIS_ADAPTER_INFORMATION {
    ULONG DmaChannel;
    BOOLEAN Master;
    BOOLEAN Dma32BitAddresses;
    NDIS_INTERFACE_TYPE AdapterType;
    ULONG PhysicalMapRegistersNeeded;
    ULONG MaximumPhysicalMapping;
    ULONG NumberOfPortDescriptors;
    NDIS_PORT_DESCRIPTOR PortDescriptors[1];
} NDIS_ADAPTER_INFORMATION, *PNDIS_ADAPTER_INFORMATION;

The driver should initialize this structure with NdisZeroMemory before it sets values in the following members:

DmaChannel
Specifies the bus-relative number of the DMA channel that a slave DMA NIC uses. If the NIC is not a DMA device or is a busmaster, this member must be zero.
Master
Specifies TRUE if the NIC is a busmaster DMA device. If the NIC is not a DMA device or is a slave, this member must be FALSE.
Dma32BitAddresses
Specifies TRUE for a busmaster DMA NIC that has 32 address lines and that can access memory with physical addresses greater than 0x00FFFFFF.
AdapterType
Specifies the I/O bus or internal interface for the NIC as one of the following values:

NdisInterfaceInternal
NdisInterfaceIsa
NdisInterfaceEisa
NdisInterfaceMca (MicroChannel)
NdisInterfaceTurboChannel
NdisInterfacePci (PCI)
NdisInterfacePcMcia (PCMCIA)

PhysicalMapRegistersNeeded
Specifies the maximum number of map registers that a busmaster DMA NIC can use per transfer operation. If the NIC uses slave DMA or is not a DMA device, this member must be zero.

For more information about allocating map registers, see NdisStartBufferPhysicalMapping.

MaximumPhysicalMapping
Specifies the maximum number of bytes that a busmaster DMA NIC can transfer in a single DMA operation. If the NIC uses slave DMA or is not a DMA device, this member must be zero.

For a busmaster DMA NIC, this value is the length of the longest buffer the driver ever passes to NdisStartBufferPhysicalMapping.

NumberOfPortDescriptors
Specifies the number of elements in the PortDescriptors array.
PortDescriptors
Specifies a variable-sized array with elements of type NDIS_PORT_DESCRIPTOR, defined as follows:
typedef struct _NDIS_PORT_DESCRIPTOR {
    ULONG InitialPort;   // first port in range
    ULONG NumberOfPorts; // number of ports in range
    PVOID *PortOffset;   // bus-relative offset of NIC ports
} NDIS_PORT_DESCRIPTOR, *PNDIS_PORT_DESCRIPTOR;

If the call to NdisRegisterAdapter succeeds, all ranges described in the PortDescriptors array are mapped so the driver can call the high-performance NdisRaw Xxx functions to communicate with its NIC. After a successful call, the driver must not pass these mapped ports to the slower NdisImmediate Xxx functions.

Return Value

NdisRegisterAdapter can return the following status codes:

NDIS_STATUS_CLOSING
NDIS_STATUS_DEVICE_FAILED
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

Comments

A NIC driver should call NdisRegisterAdapter only during its initialization from its DriverEntry or MacAddAdapter function.

A NIC driver calls NdisRegisterAdapter only after it has called the appropriate Ndis Xxx configuration functions. Calling these functions occurs during the addition of a network interface card to the network, usually in the driver’s MacAddAdapter function.

When a NIC driver supports more than one NIC, it must register each NIC separately. Each registration supplies a unique adapter name so that protocol drivers can bind themselves to each NIC such a multiNIC driver supports.

A NIC driver should initialize the entire NDIS_ADAPTER_INFORMATION structure with zeros before setting values in this structure. If the driver later maps device memory with NdisMapIoSpace, it must set zero in the PhysicalMapRegistersNeeded and MaximumPhysicalMapping members of this structure.

When a full-NIC driver unloads, it deregisters each of its NICs with call(s) to NdisDeregisterAdapter.

Callers of NdisRegisterAdapter run at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, NdisDeregisterAdapter, NdisInitializeString, NdisMapIoSpace, NdisOpenAdapter, NdisStartBufferPhysicalMapping, NdisZeroMemory


NdisRegisterAdapterShutdownHandler

VOID
 NdisRegisterAdapterShutdownHandler(
IN NDIS_HANDLE NdisAdapterHandle,
 IN PVOID ShutdownContext,
 IN ADAPTER_SHUTDOWN_HANDLER ShutdownHandler
);

NdisRegisterAdapterShutdownHandler is called by a NIC driver to register its MacShutdown handler, so this function will be called when system shutdown commences.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
ShutdownContext
Points to the context that the NIC driver associates with shutdown processing.
ShutdownHandler
Specifies the address of the caller’s MacShutdown function.

Comments

Callers of NdisRegisterAdapterShutdownHandler run at IRQL PASSIVE_LEVEL.

See Also

MacShutdown, NdisDeregisterAdapterShutdownHandler


NdisRegisterMac

VOID
 NdisRegisterMac(
OUT PNDIS_STATUS Status,
 OUT PNDIS_HANDLE NdisMacHandle,
 IN NDIS_HANDLE NdisWrapperHandle,
 IN NDIS_HANDLE MacMacContext,
 IN PNDIS_MAC_CHARACTERISTICS MacCharacteristics,
 IN UINT CharacteristicsLength
);

NdisRegisterMac is called by the NIC driver immediately after it calls NdisInitializeWrapper.

Parameters

Status
Points to the caller-supplied variable in which this function writes the status of the registration. Possible values are:

NDIS_STATUS_BAD_CHARACTERISTICS
NDIS_STATUS_BAD_VERSION
NDIS_STATUS_FAILURE
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

NdisMacHandle
Points to the caller-supplied variable in which this function writes the handle that the NDIS interface library associates with the NIC driver. This value is a required parameter to several Ndis Xxx functions.
NdisWrapperHandle
Specifies the handle associated with the NDIS interface library.
MacMacContext
Specifies the context that the NIC driver associates with itself. This value is typically a pointer to storage that the NIC driver maintains and describes. The NDIS interface library records the context and passes it to the caller’s MacAddAdapter and MacUnload functions.
MacCharacteristics
Points to an NDIS_MAC_CHARACTERISTICS structure, defined as follows:
typedef struct _NDIS_MAC_CHARACTERISTICS {
    UCHAR MajorNdisVersion;
    UCHAR MinorNdisVersion;
    UINT Reserved;
    OPEN_ADAPTER_HANDLER OpenAdapterHandler;
    CLOSE_ADAPTER_HANDLER CloseAdapterHandler;
    SEND_HANDLER SendHandler;
    TRANSFER_DATA_HANDLER TransferDataHandler;
    RESET_HANDLER ResetHandler;
    REQUEST_HANDLER RequestHandler;
    QUERY_GLOBAL_STATISTICS_HANDLER
                      QueryGlobalStatisticsHandler;
    UNLOAD_MAC_HANDLER UnloadMacHandler;
    ADD_ADAPTER_HANDLER AddAdapterHandler;
    REMOVE_ADAPTER_HANDLER RemoveAdapterHandler;
    NDIS_STRING Name;
} NDIS_MAC_CHARACTERISTICS, *PNDIS_MAC_CHARACTERISTICS;

This structure’s members provide the following information:

MajorNdisVersion
Specifies the major NDIS version. This value should be set to 0x03.
MinorNdisVersion
Specifies the minor NDIS version. This value should be set to NDIS 3.0.
Reserved
This member is reserved for system use.
OpenAdapterHandler
Specifies the address of the caller’s MacOpenAdapter function.
CloseAdapterHandler
Specifies the address of the caller’s MacCloseAdapter function.
SendHandler
Specifies the address of the caller’s MacSend function.
TransferDataHandler
Specifies the address of the caller’s MacTransferData function.
ResetHandler
Specifies the address of the caller’s MacReset function.
RequestHandler
Specifies the address of the caller’s MacRequest function.
QueryGlobalStatisticsHandler
Specifies the address of the caller’s MacQueryGlobalStatistics function.
UnloadMacHandler
Specifies the address of the caller’s MacUnload function.
AddAdapterHandler
Specifies the address of the caller’s MacAddAdapter function.
RemoveAdapterHandler
Specifies the address of the caller’s MacRemoveAdapter function.
Name
Specifies the address of a caller-initialized counted string, in the system-default character set, naming the driver. For Windows NT drivers, this string contains Unicode characters.
CharacteristicsLength
Specifies the size in bytes of the characteristics structure.

Comments

NdisRegisterMac provides the NDIS library with information about the NIC driver, including the addresses of its upper-edge driver functions. In response, the NDIS library registers the NIC driver and provides a connection for it.

Every NIC driver should initialize the characteristics structure with NdisZeroMemory before it sets values in this structure and calls NdisRegisterMac.

NdisRegisterMac saves the information at MacCharacteristics in the NDIS library.

All upper-edge function addresses in the characteristics structure must be valid. After it has registered, the NIC driver cannot alter its upper-edge functions.

After NdisRegisterMac returns control, the NDIS interface library calls the supplied MacAddAdapter function.

Callers of NdisRegisterMac run at IRQL PASSIVE_LEVEL.

See Also

MacAddAdapter, MacCloseAdapter, MacOpenAdapter, MacQueryGlobalStatistics, MacRemoveAdapter, MacRequest, MacReset, MacSend, MacTransferData, MacUnload, NdisDeregisterMac, NdisInitializeString, NdisInitializeWrapper, NdisRegisterAdapter, NdisZeroMemory


NdisReleaseAdapterResources

VOID
 NdisReleaseAdapterResources(
IN NDIS_HANDLE NdisAdapterHandle
);

NdisReleaseAdapterResources is called by the NIC driver, typically from its MacRemoveAdapter function.

Parameters

NdisAdapterHandle
Identifies the handle that the NDIS interface library associates with the network interface card.

Comments

Calling NdisReleaseAdapterResources indicates that the resources claimed in the registry for the network interface card (I/O ports, memory mappings, interrupts, and DMA channels) are released. The NDIS library can reuse these resources for another network interface card.

Callers of NdisReleaseAdapterResources run at IRQL PASSIVE_LEVEL.

See Also

MacRemoveAdapter


NdisRemoveInterrupt

VOID
 NdisRemoveInterrupt(
IN PNDIS_INTERRUPT Interrupt
);

NdisRemoveInterrupt is called by the NIC driver during a network interface card removal operation.

Parameters

Interrupt
Points to an interrupt object that NdisInitializeInterrupt has associated with the driver’s MacInterruptServiceRoutine and MacDeferredProcessingRoutine.

Comments

NdisRemoveInterrupt prevents the NIC driver’s ISR, MacInterruptServiceRoutine, from receiving interrupts. The operating system disconnects the ISR from its associated network interface card interrupt.

A driver should call this function before it calls NdisReleaseAdapterResources and NdisRemoveAdapter.

Callers of NdisRemoveInterrupt run at IRQL PASSIVE_LEVEL.

See Also

MacDeferredProcessingRoutine, MacInterruptServiceRoutine, MacRemoveAdapter, NdisInitializeInterrupt, NdisReleaseAdapterResources


NdisSetTimer

VOID
 NdisSetTimer(
IN PNDIS_TIMER Timer,
 IN UINT MillisecondsToDelay
);

NdisSetTimer is called by the NIC driver to set a timer object to expire after a specified number milliseconds.

Parameters

Timer
Points to an NDIS_TIMER object that NdisInitializeTimer has initialized.
MillisecondsToDelay
Specifies the number of milliseconds the operating system delays before calling the MacTimerFunction. This delay is relative to the current system time.

Comments

If the NIC driver calls NdisSetTimer with a value for MillisecondsToDelay that overlaps the interval set in its immediately preceding call to NdisSetTimer, its MacTimerFunction runs only once when the interval set in its most recent call to NdisSetTimer expires.

The resolution of the host timer can vary, so specifying a very short delay does not guarantee that the caller’s MacTimerFunction will be run at the end of the given interval. The granularity of the system clock is usually around ten milliseconds.

Callers of NdisSetTimer run at IRQL <= DISPATCH_LEVEL.

See Also

MacTimerFunction, NdisCancelTimer, NdisInitializeTimer


NdisSetupDmaTransfer

VOID
 NdisSetupDmaTransfer(
OUT PNDIS_STATUS Status,
 IN PNDIS_HANDLE NdisDmaHandle,
 IN PNDIS_BUFFER Buffer,
 IN ULONG Offset,
 IN ULONG Length,
 IN BOOLEAN WriteToDevice
);

NdisSetupDmaTransfer is called by a NIC driver in preparation for a slave DMA transfer between host and its NIC.

Parameters

Status
Points to a caller-supplied variable in which this function returns the status of the setup transfer operation. Possible values are:

NDIS_STATUS_RESOURCES
NDIS_STATUS_SUCCESS

NdisDmaHandle
Specifies the handle that the NDIS interface library associates with the DMA channel.
Buffer
Points to the DMA buffer descriptor.
Offset
Specifies the DMA buffer offset at which the transfer starts.
Length
Specifies the length, in bytes, of the DMA transfer. The sum of the values of Offset and Length must not be greater than the address of the last byte of Buffer.
WriteToDevice
Specifies TRUE for a download operation or FALSE for an upload operation.

Comments

The NIC driver calls NdisSetupDmaTransfer to set up the host DMA controller for a data transfer. In its call, the driver specifies a buffer that contains the data for a download operation, or that will receive the data for an upload.

After NdisSetupDmaTransfer returns, the NIC driver can program its NIC to initiate the DMA transfer. When the transfer completes, the driver calls NdisCompleteDmaTransfer.

Callers of NdisSetupDmaTransfer run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateDmaChannel, NdisCompleteDmaTransfer, NdisReadDmaCounter


NdisStartBufferPhysicalMapping

VOID
 NdisStartBufferPhysicalMapping(
IN NDIS_HANDLE NdisAdapterHandle,
 IN PNDIS_BUFFER Buffer,
 IN ULONG PhysicalMapRegister,
 IN BOOLEAN WriteToDevice,
 OUT PNDIS_PHYSICAL_ADDRESS_UNIT PhysicalAddressArray,
 OUT PUINT ArraySize
);

NdisStartBufferPhysicalMapping is called by a busmaster NIC driver to start a mapping the NDIS interface library uses to associate the virtual address of a buffer with the addresses of the physical segments that comprise the buffer.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Buffer
Points to the buffer descriptor.
PhysicalMapRegister
Specifies the index of the map register that the NDIS interface library uses to manage physical buffer addresses.
WriteToDevice
Specifies TRUE if the NIC driver uses the mapping for a download operation, or FALSE if it uses the mapping for an upload.
PhysicalAddressArray
Points to the caller-supplied location in which this function writes an array of NDIS_PHYSICAL_ADDRESS_UNIT structures. Before calling this function, the NIC driver must ensure the array is large enough by calling NdisGetBufferPhysicalArraySize.
ArraySize
Points to the caller-supplied location in which this function writes the number of physical segments comprising the buffer. This number is a zero-based value.

Comments

Before calling NdisStartBufferPhysicalMapping, the NIC driver must call NdisGetBufferPhysicalArraySize to determine the number of physical segments comprising the buffer. Then the driver allocates an array large enough to contain a mapping and calls NdisStartBufferPhysicalMapping. The mapping (array) that this function returns remains valid until the NIC driver calls NdisCompleteBufferPhysicalMapping.

To upload data to the host, the driver uses the following steps:

  1. Sets up the mapping using NdisStartBufferPhysicalMapping.

  2. Informs the network interface card of the physical buffer addresses that the function writes, and programs it to perform the upload operation.

  3. When the upload is complete, cancels the mapping by calling NdisCompleteBufferPhysicalMapping.

  4. Reads the data from host memory.

To download data to the network interface card, the driver must:

  1. Copy the data into host memory.

  2. Set up the mapping using NdisStartBufferPhysicalMapping.

  3. Inform the network interface card of the physical buffer addresses the function writes, and program it to perform the download operation.

  4. When the download is complete, cancel the mapping by calling NdisCompleteBufferPhysicalMapping.

Callers of NdisStartBufferPhysicalMapping run at IRQL <= DISPATCH_LEVEL.

See Also

NdisCompleteBufferPhysicalMapping, NdisMStartBufferPhysicalMapping, NdisGetBufferPhysicalArraySize


NdisSynchronizeWithInterrupt

BOOLEAN
 NdisSynchronizeWithInterrupt(
IN PNDIS_INTERRUPT Interrupt,
 IN PVOID SynchronizeFunction,
 IN PVOID SynchronizeContext
);

NdisSynchronizeWithInterrupt synchronizes access to data the ISR (MacInterruptServiceRoutine) shares with nonISR functions in the NIC driver.

Parameters

Interrupt
Points to the interrupt object associated with the caller’s MacInterruptServiceRoutine.
SynchronizeFunction
Points to the caller-supplied MacSynchronizeFunction.
SynchronizeContext
Points to the context that the NIC driver associates with the synchronization.

Return Value

NdisSynchronizeWithInterrupt propagates the Boolean value returned by the MacSynchronizeFunction.

Comments

NdisSynchronizeWithInterrupt is called by any NIC driver function except its ISR and MacSynchronizeFunction.

NdisSynchronizeWithInterrupt raises IRQL to the DIRQL of the ISR and runs the caller-supplied MacSynchronizeFunction at DIRQL. This prevents the ISR from accessing the shared data while the MacSynchronizeFunction touches the data. When its data access is complete, NdisSynchronizeWithInterrupt returns the same Boolean value that MacSynchronizeFunction returns and restores the original IRQL.

Callers of NdisSynchronizeWithInterrupt can be running at IRQL <= DIRQL, but calls to this function almost never occur in a driver’s ISR.

See Also

MacInterruptServiceRoutine, MacSynchronizeFunction, NdisInitializeInterrupt


NdisUnmapIoSpace

VOID
 NdisUnmapIoSpace(
IN NDIS_HANDLE NdisAdapterHandle,
 IN PVOID VirtualAddress,
 IN UINT Length
);

NdisUnmapIoSpace is called by the NIC driver to release a mapping obtained with NdisMapIoSpace.

Parameters

NdisAdapterHandle
Identifies the handle that the NDIS interface library associates with the network interface card.
VirtualAddress
Specifies the base virtual address mapped to the base physical (device) I/O address.
Length
Specifies the length, in bytes, of the mapped range starting at VirtualAddress.

Comments

Drivers usually call NdisUnmapIoSpace when they are unloading.

Callers of NdisUnmapIoSpace run at IRQL PASSIVE_LEVEL.

See Also

NdisMapIoSpace


NdisUpdateSharedMemory

VOID
 NdisUpdateSharedMemory(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Length,
 IN PVOID VirtualAddress,
 IN NDIS_PHYSICAL_ADDRESS PhysicalAddress
);

NdisUpdateSharedMemory ensures that data the driver reads from a shared memory region is kept current.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Length
Specifies the size, in bytes, of the shared memory.
VirtualAddress
Specifies the virtual address of the shared memory.
PhysicalAddress
Identifies the physical address of the shared memory.

Comments

NdisUpdateSharedMemory ensures that any data the network interface card writes is present in physical memory at the virtual address written by NdisAllocateSharedMemory.

For cached shared memory, this function does not ensure that the cache fully reflects physical memory. The NIC driver must flush the cache before the read operation begins and protect the physical memory from access until the read operation has completed.

Callers of NdisUpdateSharedMemory run at IRQL <= DISPATCH_LEVEL.

See Also

NdisAllocateSharedMemory, NdisFlushBuffer


NdisWritePortBufferUchar

VOID
 NdisWritePortBufferUchar(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 IN PUCHAR Buffer,
 IN ULONG Length
);

NdisWritePortBufferUchar is called by the NIC driver to write UCHARs from a buffer to its NIC.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Buffer
Points to the caller-supplied buffer from which this function writes the UCHAR values to Port.
Length
Specifies the number of UCHARs to write to Port.

Comments

NdisWritePortBufferUchar sequentially writes each UCHAR to the port. It determines how the host accesses the I/O port and processes the write request accordingly.

Callers of NdisWritePortBufferUchar can be running at any IRQL.

See Also

NdisRawWritePortBufferUchar, NdisReadPortBufferUchar, NdisWritePortUchar


NdisWritePortBufferUlong

VOID
 NdisWritePortBufferUlong(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 IN PULONG Buffer,
 IN ULONG Length
);

NdisWritePortBufferUlong is called by the NIC driver to write ULONGs from a buffer to its NIC.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Buffer
Points to the caller-supplied buffer from which this function writes the ULONG values to Port.
Length
Specifies the number of ULONGs to write to Port.

Comments

NdisWritePortBufferUlong sequentially writes each ULONG to the port. It determines how the host accesses the I/O port and processes the write request accordingly.

Callers of NdisWritePortBufferUlong can be running at any IRQL.

See Also

NdisRawWritePortBufferUlong, NdisReadPortBufferUlong, NdisWritePortUlong


NdisWritePortBufferUshort

VOID
 NdisWritePortBufferUshort(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 IN PUSHORT Buffer,
 IN ULONG Length
);

NdisWritePortBufferUshort is called by the NIC driver to write USHORTs from a buffer to its NIC.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Buffer
Points to the caller-supplied buffer from which this function writes the USHORT values to Port.
Length
Specifies the number of USHORTs to write to Port.

Comments

NdisWritePortBufferUshort sequentially writes each USHORT to the port. It determines how the host accesses the I/O port and processes the write request accordingly.

Callers of NdisWritePortBufferUshort can be running at any IRQL.

See Also

NdisRawWritePortBufferUshort, NdisReadPortBufferUshort, NdisWritePortUshort


NdisWritePortUchar

VOID
 NdisWritePortUchar(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 IN UCHAR Data
);

NdisWritePortUchar is called by the NIC driver to write a UCHAR to an I/O port.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Data
Specifies the UCHAR value to write to Port.

Comments

Callers of NdisWritePortUchar can be running at any IRQL.

See Also

NdisRawWritePortUchar, NdisReadPortUchar, NdisWritePortBufferUchar


NdisWritePortUlong

VOID
 NdisWritePortUlong(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 IN ULONG Data
);

NdisWritePortUlong is called by the NIC driver to write a ULONG to an I/O port.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Data
Specifies the ULONG value to write to Port.

Comments

Callers of NdisWritePortUlong can be running at any IRQL.

See Also

NdisRawWritePortUlong, NdisReadPortUlong, NdisWritePortBufferUlong


NdisWritePortUshort

VOID
 NdisWritePortUshort(
IN NDIS_HANDLE NdisAdapterHandle,
 IN ULONG Port,
 IN USHORT Data
);

NdisWritePortUshort is called by the NIC driver to write a USHORT to an I/O port.

Parameters

NdisAdapterHandle
Specifies the handle that the NDIS interface library associates with the network interface card.
Port
Specifies the I/O port.
Data
Specifies the USHORT value to write to Port.

Comments

Callers of NdisWritePortUshort can be running at any IRQL.

See Also

NdisRawWritePortUshort, NdisReadPortUshort, NdisWritePortBufferUshort


TR_QUERY_FILTER_ADDRESSES

UINT
 TR_QUERY_FILTER_ADDRESSES(
IN PTR_FILTER Filter
);

TR_QUERY_FILTER_ADDRESSES is called by the NIC driver to query the combined functional address for the network interface card.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

TR_QUERY_FILTER_ADDRESSES returns a value that represents the currently enabled combined functional address for the network interface card.

Comments

The NIC driver must be holding the filter library spinlock when it uses this macro.

Callers of TR_QUERY_FILTER_ADDRESSES run at IRQL DISPATCH_LEVEL.

See Also

TR_QUERY_FILTER_BINDING_ADDRESS


TR_QUERY_FILTER_BINDING_ADDRESS

UINT
 TR_QUERY_FILTER_BINDING_ADDRESS(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

TR_QUERY_FILTER_BINDING_ADDRESS is called by the NIC driver to query the functional address for a Token Ring binding filter.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

TR_QUERY_FILTER_BINDING_ADDRESS returns a value that represents the currently enabled functional address for a binding filter.

Comments

The NIC driver must be holding the filter library spinlock when it uses this macro.

Callers of TR_QUERY_FILTER_BINDING_ADDRESS run at IRQL DISPATCH_LEVEL.

See Also

TR_QUERY_FILTER_ADDRESSES


TR_QUERY_FILTER_BINDING_GROUP

BOOLEAN
 TR_QUERY_FILTER_BINDING_GROUP(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

TR_QUERY_FILTER_BINDING_GROUP is called by the NIC driver to query the group address for a Token Ring binding filter.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

TR_QUERY_FILTER_BINDING_GROUP returns TRUE if the binding filter is using the currently enabled group address.

Comments

The NIC driver must be holding the filter library spinlock when it uses this macro.

Callers of TR_QUERY_FILTER_BINDING_GROUP run at IRQL DISPATCH_LEVEL.

See Also

TR_QUERY_FILTER_GROUP


TR_QUERY_FILTER_CLASSES

UINT
 TR_QUERY_FILTER_CLASSES(
IN PTR_FILTER Filter
);

TR_QUERY_FILTER_CLASSES is called by the NIC driver to query the Token Ring filter classes for a network interface card.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

TR_QUERY_FILTER_CLASSES returns a value representing the currently enabled filter classes.

Comments

The NIC driver calls this macro before it alters network interface card registers to effect filtering. The timing of the call is important if a binding has altered the multicast address list.

The NIC driver must be holding the filter library spinlock when it uses this macro.

Callers of TR_QUERY_FILTER_CLASSES run at IRQL DISPATCH_LEVEL.


TR_QUERY_FILTER_GROUP

UINT
 TR_QUERY_FILTER_GROUP(
IN PTR_FILTER Filter
);

TR_QUERY_FILTER_GROUP is called by the NIC driver to query the group address for the network interface card.

Parameters

Filter
Points to a previously created and initialized filter library database.

Return Value

TR_QUERY_FILTER_GROUP returns a value that represents the currently enabled group address for the network interface card.

Comments

The NIC driver must be holding the filter library spinlock when it uses this macro.

Callers of TR_QUERY_FILTER_GROUP run at IRQL DISPATCH_LEVEL.

See Also

TR_QUERY_FILTER_BINDING_GROUP


TR_QUERY_PACKET_FILTER

UINT
 TR_QUERY_PACKET_FILTER(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle
);

TR_QUERY_PACKET_FILTER is called by the NIC driver to query the Token Ring filter classes for a binding.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.

Return Value

TR_QUERY_PACKET_FILTER returns a value representing the currently enabled filter classes for the binding.

Comments

The NIC driver calls this macro before it alters network interface card registers to effect filtering. The timing of the call is important if another binding has altered the filter classes.

The NIC driver must be holding the filter library spinlock when it uses this macro.

Callers of TR_QUERY_PACKET_FILTER run at IRQL DISPATCH_LEVEL.


TrChangeFunctionalAddress

NDIS_STATUS
 TrChangeFunctionalAddress(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN CHAR FunctionalAddressArray[TR_LENGTH_OF_FUNCTIONAL],
 IN BOOLEAN Set
);

TrChangeFunctionalAddress is called by the NIC driver to change the overall functional address for the network interface card.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to a request structure this function passes to a NIC driver action function.
FunctionalAddressArray[TR_LENGTH_OF_FUNCTIONAL]
Specifies the array containing the new functional address for the binding.
Set
Specifies TRUE if changes do not result from the closing of the binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

Possible return values include:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver must be holding the filter library spinlock when it calls this function.

If TrChangeFunctionalAddress needs to change the bitmap for the address, it calls the NIC driver’s MacChangeTrAddress function.

When TrChangeFunctionalAddress calls MacChangeTrAddress, it returns the status code the latter returns. If MacChangeTrAddress returns a status code that indicates neither pending nor success, the call to TrChangeFunctionalAddress has no effect on the network interface card or its functional address.

If TrChangeFunctionalAddress does not call the NIC driver’s MacChangeTrAddress, it returns NDIS_STATUS_SUCCESS. This status code indicates that the new functional address does not change the combined address.

Callers of TrChangeFunctionalAddress run at IRQL DISPATCH_LEVEL.

See Also

MacChangeTrAddress


TrChangeGroupAddress

NDIS_STATUS
 TrChangeGroupAddress(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN CHAR GroupAddressArray[TR_LENGTH_OF_FUNCTIONAL],
 IN BOOLEAN Set
);

TrChangeGroupAddress is called by the NIC driver to change the overall network interface card group address associated with a Token Ring binding filter.

Parameters

Filter
Points to the previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to a request structure this function passes to a NIC driver action function.
GroupAddressArray[TR_LENGTH_OF_FUNCTIONAL]
Specifies the array containing the new network interface card group address for the binding filter.
Set
Specifies TRUE if the address change does not result from the closing of the binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

Possible return values include:

NDIS_STATUS_PENDING
NDIS_STATUS_RESET_IN_PROGRESS
NDIS_STATUS_SUCCESS

Comments

The NIC driver must be holding the filter library spinlock when it calls this function.

If TrChangeGroupAddress needs to change the bitmap for the group address, it calls the NIC driver’s MacChangeGroup function.

When TrChangeGroupAddress calls MacChangeGroup, it returns the status code that the latter returns. If MacChangeGroup returns a status code other than NDIS_STATUS_PENDING or NDIS_STATUS_SUCCESS, the call to TrChangeGroupAddress has no effect on the network interface card or its group address.

If TrChangeGroupAddress does not call MacChangeGroup, it returns NDIS_STATUS_SUCCESS. This status code indicates that the new group address does not change the combined mask of all binding filters.

Callers of TrChangeGroupAddress run at IRQL DISPATCH_LEVEL.

See Also

MacChangeGroup


TrCreateFilter

BOOLEAN
 TrCreateFilter(
IN TR_ADDRESS_CHANGE AddressChangeAction,
 IN TR_GROUP_CHANGE GroupChangeAction,
 IN TR_FILTER_CHANGE FilterChangeAction,
 IN TR_DEFERRED_CLOSE CloseAction,
 IN PUCHAR AdapterAddress,
 IN PNDIS_SPIN_LOCK Lock,
 OUT PTR_FILTER *Filter
);

TrCreateFilter is called by a NIC driver. It creates and initializes a Token Ring filter library database associated with one network interface card.

Parameters

AddressChangeAction
Specifies the address of the caller’s MacChangeTrAddress function, which the Token Ring filter library calls when a change occurs in the ORing of the functional addresses desired by all the bindings.
GroupChangeAction
Specifies the address of the caller’s MacChangeGroup function, which the Token Ring filter library calls when a change occurs in the group address for the bindings.
FilterChangeAction
Specifies the address of the caller’s MacChangeFilter function, which the Token Ring filter library calls when a binding sets or clears a particular filter class. The binding must be the first or only binding using the filter class.
CloseAction
Specifies the address of the caller’s MacCloseAction function, which the Token Ring filter library calls if a binding closes while it is indicating incoming data through NdisIndicateReceive.
AdapterAddress
Points to the network address of the NIC.
Lock
Points to an already initialized spin lock that the driver holds when it requires exclusive access to the filter library database.
Filter
Points to a caller-supplied memory location in which this function writes a pointer to the created filter.

Return Value

TrCreateFilter returns TRUE if it creates the Token Ring filter library database successfully.

Comments

The NIC driver uses the Token Ring filter library to filter received network packets. Using the destination address of a received packet, the filter library determines the protocol driver bindings that receive indications for the packet.

Callers of TrCreateFilter run at IRQL PASSIVE_LEVEL.

See Also

MacChangeFilter, MacChangeGroup, MacChangeTrAddress, MacCloseAction, NdisAllocateSpinLock, TrDeleteFilter


TrDeleteFilter

VOID
 TrDeleteFilter(
IN PTR_FILTER Filter
);

TrDeleteFilter is called by a NIC driver, usually when it is unloading, to delete the memory allocated for a Token Ring filter library database.

Parameters

Filter
Points to the filter library database memory to be deleted.

Comments

The NIC driver must clear any active filters from the database before it calls TrDeleteFilter.

Callers of TrDeleteFilter run at IRQL PASSIVE_LEVEL.

See Also

TrCreateFilter, TrDeleteFilterOpenAdapter


TrDeleteFilterOpenAdapter

NDIS_STATUS
 TrDeleteFilterOpenAdapter(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest
);

TrDeleteFilterOpenAdapter is called by a NIC driver to delete NIC binding filter information from the Token Ring filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure that this function passes to a NIC driver action function.

Return Value

If TrDeleteFilterOpenAdapter detects that the binding is currently being indicated through NdisIndicateReceive, it returns NDIS_STATUS_CLOSING_INDICATING. If this function calls any of the NIC driver’s action functions, it returns the status code the latter returns. If the action function returns NDIS_STATUS_SUCCESS, TrDeleteFilterOpenAdapter tries to add the filter index to the freelist.

Comments

The NIC driver must be holding the filter library spinlock when it calls this function.

TrDeleteFilterOpenAdapter calls NIC driver action functions associated with clearing filter classes and addresses. The NIC driver should not call this function if the action functions can return a status code that indicates neither pending nor success.

Callers of TrDeleteFilterOpenAdapter run at IRQL DISPATCH_LEVEL.

See Also

NdisIndicateReceive, TrNoteFilterOpenAdapter


TrFilterAdjust

NDIS_STATUS
 TrFilterAdjust(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE NdisFilterHandle,
 IN PNDIS_REQUEST NdisRequest,
 IN UINT FilterClasses,
 IN BOOLEAN Set
);

TrFilterAdjust is called by a NIC driver to adjust a Token Ring filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
NdisFilterHandle
Specifies the handle that the filter library associates with the binding filter.
NdisRequest
Points to the request structure this function passes to a NIC driver action function.
FilterClasses
Specifies a bitmap of filter classes this function adds or deletes.
Set
Specifies TRUE if changes do not result from the closing of the binding. This function does not use Set but passes it to a NIC driver action function.

Return Value

If TrFilterAdjust does not call MacChangeFilter, it returns NDIS_STATUS_SUCCESS to indicate that the filter class changes do not affect the combined packet filter mask.

If TrFilterAdjust calls the NIC driver’s MacChangeFilter function, it returns the status code the latter returns.

Comments

The NIC driver must be holding the filter library spinlock when it calls this function.

If MacChangeFilter returns a code other than NDIS_STATUS_PENDING or NDIS_STATUS_SUCCESS, the call to TrFilterAdjust has no effect on the network interface card or its functional address.

Callers of TrFilterAdjust run at IRQL DISPATCH_LEVEL.

See Also

MacChangeFilter


TrFilterIndicateReceive

VOID
 TrFilterIndicateReceive(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE MacReceiveContext,
 IN PVOID HeaderBuffer,
 IN UINT HeaderBufferSize,
 IN PVOID LookaheadBuffer,
 IN UINT LookaheadBufferSize,
 IN UINT PacketSize
);

TrFilterIndicateReceive is called by a NIC driver to indicate a received packet to all filter library database bindings associated with a Token Ring address.

Parameters

Filter
Points to a previously created and initialized filter library database.
MacReceiveContext
Specifies a context the NIC driver associates with a packet received from the network.
HeaderBuffer
Points to the virtual address of the virtually contiguous buffer containing the packet header. The buffer is valid only within the current call to this function.
HeaderBufferSize
Indicates the length, in bytes, of the packet header.
LookaheadBuffer
Points to the virtual address of the virtually contiguous buffer containing the first bytes of packet data. The packet buffer is valid only for the current call to this function.
LookaheadBufferSize
Specifies the size, in bytes, of the lookahead buffer.
PacketSize
Specifies the size, in bytes, of the received packet data. This number has no relationship with the lookahead buffer. It indicates the size of the received packet data so a protocol driver can call NdisTransferData to transfer the data in the packet, as necessary. The data length does not include the packet header length.

Comments

Instead of calling NdisIndicateReceive without filtering, the NIC driver calls TrFilterIndicateReceive to filter a received packet so that only the appropriate bindings receive it. TrFilterIndicateReceive then calls ProtocolReceive for each protocol driver to which the binding filter applies.

In its calls to ProtocolReceive, this function passes each protocol driver a pointer to the packet header, as well as a pointer to some or all of the packet data. The protocol driver can inspect the header and data in the packet, optionally copy as much of the header or data as is visible, or optionally call NdisTransferData, which calls the NIC driver’s MacTransferData function to copy the data into a protocol-supplied packet.

Callers of TrFilterIndicateReceive run at IRQL <= DISPATCH_LEVEL.

See Also

MacTransferData, NdisIndicateReceive, NdisTransferData, ProtocolReceive, TrFilterIndicateReceiveComplete


TrFilterIndicateReceiveComplete

VOID
 TrFilterIndicateReceiveComplete(
IN PTR_FILTER Filter
);

TrFilterIndicateReceiveComplete is called by the NIC driver to indicate to all bound protocol drivers that TrFilterIndicateReceive has returned.

Parameters

Filter
Points to a previously created and initialized Token Ring filter library database.

Comments

To deliver the necessary indications, TrFilterIndicateReceiveComplete calls the ProtocolReceiveComplete functions of the appropriate protocol drivers once for each indication.

Callers of TrFilterIndicateReceiveComplete run at IRQL <= DISPATCH_LEVEL.

See Also

ProtocolReceiveComplete, TrFilterIndicateReceive


TrNoteFilterOpenAdapter

BOOLEAN
 TrNoteFilterOpenAdapter(
IN PTR_FILTER Filter,
 IN NDIS_HANDLE MacBindingHandle,
 IN NDIS_HANDLE NdisBindingContext,
 OUT PNDIS_HANDLE NdisFilterHandle
);

TrNoteFilterOpenAdapter is called by the NIC driver to add a binding to the Token Ring filter library database.

Parameters

Filter
Points to a previously created and initialized filter library database.
MacBindingHandle
Specifies the handle that the NIC driver associates with the binding.
NdisBindingContext
Specifies the context that the NDIS interface library associates with the binding.
NdisFilterHandle
Points to a caller-supplied variable in which this function writes the handle that the filter library associates with the binding filter.

Return Value

TrNoteFilterOpenAdapter returns TRUE if it adds the binding filter to the filter library database. The function returns FALSE if it cannot add the binding filter because the database already contains the maximum number of filters.

Comments

The NIC driver must be holding the filter library spinlock when it calls this function.

Callers of TrNoteFilterOpenAdapter run at IRQL DISPATCH_LEVEL.

See Also

TrDeleteFilterOpenAdapter


TrShouldAddressLoopBack

BOOLEAN
 TrShouldAddressLoopBack(
IN PTR_FILTER Filter,
IN CHAR DestinationAddress[TR_LENGTH_OF_ADDRESS],
IN CHAR SourceAddress[TR_LENGTH_OF_ADDRESS]
);

TrShouldAddressLoopBack returns FALSE if a packet bound for a given Token Ring address cannot possibly loop back.

Parameters

Filter
Points to a previously created and initialized filter library database.
DestinationAddress[TR_LENGTH_OF_ADDRESS]
Specifies a network destination address that this function uses to determine a need for packet loopback.
SourceAddress[TR_LENGTH_OF_ADDRESS]
Specifies a network source address for the packet.

Return Value

TrShouldAddressLoopBack returns TRUE if the packet is likely to loop back. It returns FALSE if there is no chance the packet will loop back.

Comments

TrShouldAddressLoopBack does not check whether the source address is equal to the destination address.

Callers of TrShouldAddressLoopBack run at IRQL <= DISPATCH_LEVEL.

See Also

TR_COMPARE_NETWORK_ADDRESSES


Virtual Device Drivers for MS-DOS Applications or Special Hardware

Note A VDD need only be written to support special-purpose hardware devices that operate under an MS-DOS application. The provided VDM has built-in support for commonly used hardware such as serial communication ports, video, mouse, and keyboard. Consequently, you should not need a VDD to virtualize access to these common devices.

Microsoft® Windows NT® provides a virtual DOS machine (VDM) within the Microsoft Win32® subsystem to support real-mode and protected-mode MS-DOS applications. The VDM creates an environment where unmodified 16-bit MS-DOS binaries can execute without threatening the security and integrity of the 32-bit Windows NT system. The VDM environment serves programs such as:

  • MS-DOS applications

  • 16-bit drivers

  • 16-bit Windows applications (WOW)

  • Terminate and stay resident (TSR) programs

Windows NT launches the VDM as necessary to support MS-DOS applications launched by the user. There may be several copies of the VDM running at any one time in a Windows NT system. There is one copy of the VDM to support all Windows 3.x applications launched by the user. In addition, each console session (MS-DOS Shell) can initiate a VDM if the user launches an MS-DOS application from that session. Multiple copies of the VDM each set up a self-contained virtual DOS machine. They do not interfere with each other, so there are never resource conflicts among the copies.

When the VDM is launched, it:

  • Allocates memory in which to create a virtual DOS machine.

  • Reads the files \system32\config.nt and \system32\autoexec.nt.

  • Loads drivers and executes batch files as specified.

  • Loads the application and executes it.

The application runs as it would in a native MS-DOS environment because the VDM provides virtual equivalents of all the functionality MS-DOS would supply to the application. All drivers and batch files execute within the virtual DOS environment set up by the VDM.

Given the functionality of the VDM, most MS-DOS applications can run without any modification or additions in the Windows NT environment. However, there are some MS-DOS applications that depend on custom hardware, such as a fax board or a 3270 communications board. Generally, these applications have a plug-in hardware board they manipulate directly through 80386/80486 I/O port IN and OUT instructions and by writing directly to memory on the board. These applications might also use DMA to transfer data to and from the board. In an MS-DOS environment, these operations allow the application or its 16-bit driver to get direct access to the hardware device, as shown in the following illustration.

Normal MS-DOS hardware device access

In an MS-DOS system, an application can perform these operations directly. In some cases, the application uses a 16-bit device driver or a TSR to communicate with the hardware. The application makes calls to the driver or calls the TSR through a software interrupt. For the purposes of this document, the 16-bit driver or TSR is considered to be an extension of the application that requests services. Thus, when this document talks about an MS-DOS application, it refers to either an MS-DOS application that manipulates a hardware device directly, or to a combination of an MS-DOS application and a 16-bit driver or TSR that manipulates the hardware.

In Windows NT, MS-DOS application programs (and 16-bit drivers or TSRs) do not have sufficient processor privilege to use 80386/80486 I/O ports, and the virtual addressing mode of Windows NT does not permit the real-mode addressing techniques of DMA to the board.

To support an MS-DOS application using special hardware in the Windows NT environment, the application developer must write a 32-bit kernel-mode device driver to access the board hardware. The developer must also write a VDD to translate operations performed by the MS-DOS application into the corresponding kernel-mode device driver calls for the particular board. The paired kernel-mode device driver and VDD for the device enable the original MS-DOS application to run in the VDM layer.

In addition to I/O port and memory access arbitration, a VDD can also support DMA, register manipulation, translation of real-mode addresses to 32-bit full pointers, and hardware interrupt simulation. The following sections describe how to write a VDD by using the system services provided by Windows NT.


How to Provide Virtual Device Support

In Windows NT, a 16-bit MS-DOS application runs in the context of the VDM. Likewise, any TSRs or 16-bit drivers also run in the VDM layer, just as they would on a normal MS-DOS machine. Attempts to perform privileged hardware access from the VDM layer must be intercepted and routed to the kernel-mode device driver where those operations can occur without compromising system integrity.

There are two strategies for intercepting an application’s attempts to access its hardware:

  1. Modify your application (or its 16-bit device driver) to call the VDD you have written for Windows NT, instead of directly accessing the hardware.

  2. Instead of modifying your MS-DOS application, rely on the VDM to intercept disallowed hardware accesses and route them to your VDD.

These two strategies and the support routines provided by Windows NT are discussed in the following sections.


Application-Based Intercepts

This strategy involves modifying the MS-DOS application or its 16-bit driver to intercept the device access at a very high level and route it directly to the VDD, which routes the request to the appropriate kernel-mode driver. In addition to modifying the original application or driver, you must also write a VDD and kernel-mode device driver. Control calls in the original driver that access the hardware directly (I/O port or memory mapped accesses) are replaced by control calls to the VDD. In turn, the VDD calls the kernel-mode device driver.

The following illustration shows how application-based intercepts flow through the various levels of the system from the application to the hardware. Shaded boxes show elements that must be written or modified by the developer.

Application-based intercepts of device access


VDM-Based Intercepts

The second strategy allows the VDM to intercept device accesses at a much lower level. The original MS-DOS application and its driver are not modified. The VDM intercepts low-level port I/O or board-memory accesses and attempts to route the operation through the appropriate VDD. The VDD then calls Win32 functions, such as DeviceIoControl, to access the kernel-mode driver. This method involves writing a VDD and kernel-mode device driver, but not a replacement 16-bit driver.

Although this low-level interception is convenient, it is inefficient because the VDM has to trap and dispatch the disallowed operations. If your application needs higher performance in its communication with a hardware device, modify the application (or its 16-bit driver or TSR) so it dispatches directly to the VDD rather than relying on the VDM to intercept disallowed device-access operations.

For example, consider an MS-DOS 3270 terminal program using a 16-bit device driver to communicate with a plug-in 3270 communications board. The driver provides a control call to send a block of text out over the 3270 cable. The application prepares the block of data and then passes the data to the 16-bit driver. The driver then initiates a series of I/O port operations to directly communicate with the 3270 board. In a Windows NT system, the 16-bit device driver does not have sufficient privilege to access the I/O ports. A modified 16-bit driver makes a call to the 3270 VDD instead of trying to access the board’s I/O ports. The VDD then calls the 32-bit kernel-mode device driver to control the board. You can think of the modified 16-bit driver as a stub driver, where most of the functionality has been delegated to the VDD and its corresponding kernel-mode driver.

The following illustration shows how VDM-based intercepts flow through the various levels of the system from the application to the hardware. Shaded boxes show elements that must be written by the developer.

VDM-based intercepts of device access

Note You can actually use a combination of application-based and VDM-based intercepts when moving your MS-DOS program to Windows NT. The application-based intercepts provide the best performance, but using the VDM-based intercepts can save you the effort of rewriting parts of your application or 16-bit driver where performance is not crucial.

The VDM needs a mechanism to determine the specific device an application is trying to access. To accomplish this, each VDD tells the VDM which I/O ports it supports and the range of board memory it accesses. Each VDD/device pair claims a unique range of port addresses and memory ranges it supports. The VDD installs callback functions for ranges of I/O ports and memory areas. The VDM directs application I/O port operations and memory accesses to the appropriate VDD-installed callback functions.

For example, assume a fax board is supporting an MS-DOS application in a Windows NT system. The fax board supports I/O ports in the range 0x230-0x250 and has 32K of memory, starting at address 0xE8000. When the fax board’s VDD is initialized, the VDD tells the VDM which ports it supports and where the board memory is located. When the MS-DOS fax application tries to access these I/O ports or board memory, the VDM intercepts the operations and passes them on to the fax VDD callback functions. In turn, the VDD callback functions call Win32 functions, such as DeviceIoControl, to request services from the kernel-mode device driver for the fax board.


Modifying an MS-DOS Application or 16-bit Driver

Windows NT provides the functions RegisterModule, UnregisterModule, and DispatchCall for a 16-bit driver or MS-DOS application to call a 32-bit VDD. These functions can be included in a 16-bit application program or driver to bridge the gap between the 16- and 32-bit environments.

During its initialization, the 16-bit driver or MS-DOS application calls RegisterModule, specifying the name of the VDD, the name of the VDD initialization routine, and the name of the VDD dispatch routine to be called each time the 16-bit application or driver calls DispatchCall. The RegisterModule function returns a handle to the VDD. If the VDD has not yet been loaded, RegisterModule loads and initializes it before returning. The application or driver can then use the VDD handle when using DispatchCall, which calls the VDD dispatch function specified in RegisterModule. When the application or driver is finished with the VDD, it can call UnregisterModule to unload the VDD and release its resources.

It is not necessary to modify an MS-DOS application or 16-bit device driver unless you want to directly call your VDD. An unmodified MS-DOS application’s attempts to directly access the hardware are intercepted by the VDM and routed to the appropriate VDD. Modify your application only if you need the additional performance provided by direct high-level interceptions of device access requests.

If the VDD needs to use VDDSimulate16, the 16-bit device driver or its stub will need to change as well. For VDDSimulate16 to complete, the 16-bit driver needs to call VDDUnsimulate16.

The following functions (macros) are provided for 16-bit drivers or MS-DOS applications:
DispatchCall | Passes a request to a DLL.
RegisterModule | Registers a third-party DLL with the VDM.
UnRegisterModule | Unregisters a third-party DLL with the VDM.


Writing a VDD

The following sections describe how to write a virtual device driver. The VDD is a 32-bit DLL that runs in user mode in Windows NT. Its purpose is to translate requests from MS-DOS programs into calls to a 32-bit kernel-mode device driver. The VDD serves as a bridge between the 16-bit MS-DOS application and the 32-bit kernel-mode driver. The VDD can be called directly from a modified MS-DOS application, as explained in Application-Based Intercepts or it can rely on the VDM to intercept device access requests from the application and route them to callback functions the VDD has installed.


Registering the Driver (Not Required for Application-Based Intercepts)

If you are writing application-based intercepts, RegisterModule loads and installs the 32-bit VDD. In this case, you do not need to follow the installation procedures described in this section.

The registry contains information for loading and configuring your driver. Each VDD for VDM-based intercepts must be identified under the key HKEY_LOCAL_MACHINE\SYSTEM \CurrentControlSet \Control \VirtualDeviceDrivers. The value entry of the key is an array of zero-terminated strings terminated by two null characters ( REG_MULTI_SZ format). Each substring specifies a full path name to a VDD the system loads when the VDM process is initialized. The format of the registry entry is shown as follows:

[VirtualDeviceDrivers]

VDD = <full-path of the VDD1.DLL>\0<full-path of the VDD2.DLL>\0\0

Your application installation program can use the registry API functions, such as RegSetValue, to modify the required registry entry to enable your VDD, or you can require the user or system administrator to modify the entry with regedt32.exe.

The RegSetValue function and REG_MULTI_SZ format are described in the Win32 SDK.


VDD Initialization

A VDD is implemented as a 32-bit DLL. Accordingly, it follows all the rules described for Win32 DLLs in the Win32 SDK. The system calls the DLL’s main entry point function when initializing the DLL and also when terminating it.

At initialization, a VDD allocates and initializes any memory it needs. The VDD should also get a handle to the 32-bit kernel-mode driver for its device. The kernel-mode driver for the device registers a Win32 name for itself when the driver is initialized. The VDD can use this virtual name to get a handle for the driver by calling CreateFile. For example, the kernel-mode driver for a fax board might register itself under the name “fax1.” The VDD can obtain a handle to the fax board driver by supplying the name “\\ .\fax1” to the CreateFile function. When the VDD has a handle to the driver, it can make ReadFile, WriteFile, or DeviceIoControl calls to communicate with the driver. If the VDD can’t open the kernel-mode driver for its device, it should return FALSE to indicate an error.

If the VDD is relying on the VDM to intercept device access by the MS-DOS application, the VDD initialization must install its callback functions for handling I/O port and board-memory accesses, as explained in I/O Ports and 8.3.4. After these callback functions (also called hooks) are installed, they do the real work of the VDD. The VDM calls your callback functions to handle I/O port operations as well as accesses to board memory from the application.

If the VDD is relying on a modified MS-DOS application to directly call the VDD, there is no need to install I/O or memory access hooks.

A skeleton for the VDD initialization function is shown as follows:

BOOL VDDInitialize(IN PVOID DllHandle, IN ULONG Reason,
IN PCONTEXT Context OPTIONAL)

/*++

Routine Description:

Arguments:
    DllHandle - Not Used
    Reason - Attach or Detach
    Context - Not Used

Return Value:
    SUCCESS - TRUE
    FAILURE - FALSE

--*/

{

    switch (Reason) {

    case DLL_PROCESS_ATTACH:
    // Allocate VDD's local heap if needed. Check that NT FAX driver
    // is available by opening that device.
        break;
    case DLL_PROCESS_DETACH:
    // Deallocate VDD's local heap if needed.
    // Communicate to appropriate Device driver about your departure.
        break;
    default:
        break;
    }

    return TRUE;
}


I/O Ports

In the MS-DOS environment, a peripheral board typically has a range of I/O ports to which it can respond. These ports are often configured by jumpers on the board or through software setup. Special-purpose application programs control and communicate with the board by performing 80386/80486 assembly IN and OUT instructions to the ports supported by the board.

In Windows NT, application programs have insufficient processor privilege to access I/O ports. Therefore, the VDM intercepts IN and OUT instructions from MS-DOS application programs and attempts to find an I/O handler registered by a VDD for the specified port. Each VDD installs I/O handlers (also known as I/O hooks) for one or more ranges of port addresses. For example, a VDD could install I/O handlers for the port range 0x220-0x227 and 0x2F0-0x2F7. The VDM keeps track of all I/O port handlers installed by VDDs in that VDM. When the VDM intercepts an IN or OUT instruction from an MS-DOS application, it looks at all the installed I/O handlers to see if the current port being accessed falls within one of the installed port ranges.

A VDD must install I/O port handlers for the ports used by its application. The handlers are called each time there is an IN or OUT instruction on one of the designated ports. A VDD OUT handler typically gathers several OUT calls into a single complete command and then translates that command into a Win32 DeviceIoControl call to the kernel-mode device driver for the board. Likewise, a single driver call might require a VDD IN handler to be called several times to completely transfer the information back to the application.

A VDD installs its I/O port handlers within the VDD initialization routine, explained in Registering the Driver. Initialize a VDD_IO_HANDLERS structure with pointers to the VDD’s I/O port handlers, specifying one or more ranges of I/O ports the VDD responds to, and call the VDDInstallIOHook function. The VDD_IO_HANDLERS structure contains slots for IN and OUT handlers for byte, word, and string operations. The VDD must supply at least byte-in and byte-out handlers. If you supply a NULL function pointer for the word and string operations, the VDM automatically emulates them by calling the byte operation functions the VDD provides, although it is often more efficient to implement word and string operations yourself.

A particular VDD can only have one set of I/O handler functions for all the I/O ports it uses. That is, a VDD cannot install one set of functions for one I/O port range and another set of functions for another port range. Of course, other VDDs can install their own functions for other ranges, although two VDDs in the same VDM cannot use overlapping port ranges.

A VDD’s main entry point function is also called when the VDD is about to be terminated. At that time, the VDD can deinstall its I/O hooks by calling VDDDeinstallIOHook.

Note A VDD should never directly hook the I/O ports for DMA or Video. Video is handled by the VDM. See DMA for a discussion of how to facilitate DMA transfers in the VDM environment.

The following functions are used with I/O hooks:
VDDInstallIOHook | Hooks the I/O ports the VDD is responsible for.
VDDDeinstallIOHook | Unhoooks the I/O ports previously hooked by a VDD.


Memory-Mapped I/O

In the MS-DOS environment, applications often find it advantageous to write directly to the memory on a particular board. In Windows NT, applications cannot write directly to physical addresses. Thus, while an application may use the virtual address 0xE8000, the actual physical address of the memory will not necessarily be the same. In fact, the memory space in the range allowed for boards is initially marked as invalid by the Windows NT memory manager. Any attempt to access memory in this range by unmodified MS-DOS applications results in a page fault. A VDD can control access to the memory range used by its device by using the functions described in the following sections.


Memory Hooks

A VDD can install a memory hook function called by the system when an MS-DOS application tries to access a specified range of memory on the device. The VDD installs a memory hook function by calling VDDInstallMemoryHook in the VDD initialization routine, specifying a pointer to the callback function and a range of memory it supports. The VDD should install a memory hook for all ranges of board memory the application will attempt to access.

The VDM sizes the memory ranges you specify for your memory hooks to page boundaries. Two VDDs in the same VDM cannot hook overlapping ranges of memory.

Note During VDM initialization, valid areas of ROM on plug-in boards are automatically mapped into the VDM address space. Thus, a VDD does not have to install a memory hook for the ROM on its board.

A VDD memory hook is called if the application accesses an invalid memory range on which a VDD has placed a memory hook. The chain of events looks like this:

  1. The application tries to access memory in the board’s address range.

  2. Because the Windows NT memory manager has marked that range as not present, the page fault handler is called and finds that the memory has not been swapped out, but it is invalid.

  3. The page fault handler then asks the VDM to verify if the memory is hooked by one of its registered VDD memory handlers.

  4. The VDM calls the memory hook the VDD installed for that range of memory. The memory hook can either:

    • Validate the range of memory by calling VDDAllocMem. When the memory hook returns, the system retries the instruction that caused the original page fault.

    • Modify the CS:IP so the offending access instruction is not retried.

Note Windows NT assumes you have solved the page-fault problem when your memory hook returns. The VDD callback function must either allocate some memory to validate the invalid range or modify the CS:IP so that the offending instruction is not retried. Failure to do either can cause an infinite loop in the page fault handler.

After the VDD has allocated a buffer for the range covered by its memory hook, the hook is not called again unless you deallocate some or all of that range, causing another page fault in that range. Use VDDFreeMem to deallocate memory allocated with VDDAllocMem. These two functions ensure the memory operations are performed in a platform-independent manner.

In a typical scenario, the VDD memory hook is called the first time the application tries to access the VDD’s memory-mapped range. The memory hook calls VDDAllocMem to map that range onto the virtual address space. Subsequent accesses to that range proceed normally. The VDD eventually transfers the data in its buffer to the actual device hardware by calling the device driver. How often that transfer takes place depends on the operating characteristics of the application and the hardware. The need to transfer data is often signaled by a command sequence coming in through the I/O ports, as described in I/O Ports. Alternatively, a VDD can create its own worker thread responsible for flushing the data to the Windows NT driver. This worker thread can then use Sleep or WaitForSingleObject with a timeout to get timer functionality.

Likewise, the VDD can periodically request services from the kernel-mode device driver to offload data from the board memory to the VDD buffer so it can be consumed by the application. If the memory hook encounters an unrecoverable error, such as a failure to allocate memory, it should put up a message to the user by calling MessageBox and then call VDDTerminateVDM.

The memory hook function is called by using the address on which the page fault occurred and an argument that tells whether it was a read or a write operation that caused the fault. There is no information given to identify the operand value. If the VDD needs to know this information, it can use GetCS and GetIP to get the segmented address of the offending instruction, use GetVDMPointer to convert it to a 32-bit address, and then use that address to decode the instruction causing the page fault.

The installation process for memory hooks is very similar to that described for I/O hooks I/O Ports. Specify a pointer to the VDD memory hook callback function and a range of memory the device supports, and then call VDDInstallMemoryHook in the initialization routine of the VDD.

The VDD must call VDDDeinstallMemoryHook to remove the memory hook when the VDD is terminated. Before removing its memory hook, a VDD should call VDDFreeMem to free any memory it allocated with VDDAllocMem. When the VDD deinstalls a memory hook, the range of memory supported by that hook is marked as invalid by the Windows NT memory manager. The following code shows a template of a memory hook and its installation.

/** Global variables **/

HANDLE  hVDD;			/* VDD module handle */
HANDLE  hVddHeap;		/* VDD local heap */
PBYTE   IOBuffer;		/* buffer to simulate I/O Read and Write */
ULONG   MIOAddress; 		/* memory mapped I/O linear address */
PVOID   BaseAddress;		/* memory mapped I/O virtual address */

BOOL VDDInitialize(HANDLE hVdd, DWORD dwReason, LPVOID lpReserved)

/*++

Routine Description:

    The DllEntryPoint for the VDD that handles initialization and termination.

Arguments:
    hVdd   - The handle to the VDD
    Reason - flag word that indicates why the Dll Entry Point was called
    lpReserved - Unused

Return Value:
    BOOL bRet - if (dwReason == DLL_PROCESS_ATTACH)
                   TRUE    - Dll Initialization successful
                   FALSE   - Dll Initialization failed
                else
                   always returns TRUE
--*/

{
    int     i;
    static BOOLEAN IOHook;             /* True, if we installed an I/O
                                          hooked. */
    static BOOLEAN MIOHook;            /* True, if we installed a memory
                                          hook. */
    static VDD_IO_PORTRANGE PortRange;
    VDD_IO_HANDLERS  IOHandlers;

/**
    Keep a copy of VDD handle in a global variable so the other
    functions can see it.
**/
    hVDD = hVdd;

    switch (dwReason) {

    case DLL_PROCESS_ATTACH:

        // Allocate the VDD's local heap.
        hVddHeap = HeapCreate(0, 0x1000, 0x10000);

    if (!hVddHeap) {
        OutputDebugString("VDD: Can't create local heap");
            return FALSE;
    }

        IOBuffer = (PBYTE)HeapAlloc(hVddHeap,0,IO_PORT_RANGE);

    if (!IOBuffer)	{
        OutputDebugString("VDD: Can't allocate IO buffer from heap");
            HeapDestroy(hVddHeap);
            return FALSE;
    }

        // Communicate your departure to the appropriate driver.

    // Set emulated I/O to floating.
        for (i = 0 ; i < IO_PORT_RANGE; i++)
        IOBuffer[i] = FLOATING_IO;

    IOHandlers.inb_handler = MyInB;
    IOHandlers.inw_handler = NULL;
    IOHandlers.insb_handler = NULL;
    IOHandlers.insw_handler = NULL;
    IOHandlers.outb_handler = MyOutB;
    IOHandlers.outw_handler = NULL;
    IOHandlers.outsb_handler = NULL;
    IOHandlers.outsw_handler = NULL;
    PortRange.First = IO_PORT_FIRST;
    PortRange.Last = IO_PORT_LAST;

    // Hook I/O mapped I/O.
    IOHook = VDDInstallIOHook(hVDD, (WORD) 1, &PortRange, &IOHandlers);
    // Get 32 bits linear address of memory-mapped I/O.
    MIOAddress = (ULONG) GetVDMPointer(MIO_ADDRESS, MIO_PORT_RANGE, 0);
    // Hook memory-mapped I/O.
    MIOHook = VDDInstallMemoryHook(hVDD, (PVOID) MIOAddress,
                        MIO_PORT_RANGE,
                        (PVDD_MEMORY_HANDLER)MyMIOHandler);
    break;

    case DLL_PROCESS_DETACH:

        // Communicate your departure to the appropriate driver.
    if (IOHook)
        VDDDeInstallIOHook(hVDD, 1, &PortRange);
    if (MIOHook) {
        VDDDeInstallMemoryHook(hVDD, (PVOID) MIOAddress, MIO_PORT_RANGE);
        if (BaseAddress) {
                VDDFreeMem(BaseAddress, PAGE_SIZE, MEM_DECOMMIT);
        }
    }

        // Deallocate VDD's local heap, if needed.
        HeapDestroy(hVddHeap);
        break;

    default:
        break;
    }

    return TRUE;
}

VOID MyInB(WORD	Port, PBYTE Buffer)
{
// Provide the data from our buffer.
    *Buffer = IOBuffer[Port - IO_PORT_FIRST];
}

VOID MyOutB(WORD Port, BYTE Data)
{
    // Update our local buffer.
    // In a real application, the VDD might want to call its associated
    // device driver to update the change.

    IOBuffer[Port - IO_PORT_FIRST] = (BYTE)Data;
    // If the I/O port is the one to trigger DMA operation, do it.
    // To demonstrate the two options in handling a DMA operation, we
    // use two ports here to trigger different DMA operation schemes.

    if (Port == IO_PORT_FIRE_DMA_FAST) {
    FastDMA( );
    }
    else {
    if(Port == IO_PORT_FIRE_DMA_SLOW) {
        SlowDMA( );
     }
    }
}

VOID MyMIOHandler(
ULONG	Address,			// faulting linear address
ULONG	RWFlags 			// 1 if write operation, 0 if read
)
{

    // Map the memory for the memory-mapped I/O so we won't
    // get a page fault on our memory-mapped I/O after this.
    // We may reserve the memory during  DLL_PROCESS_ATTACH by using
    // MEM_RESERVE rather than MEM_COMMIT, as we did here.
    // The solution applied here is not the best, although it
    // is the simplest. A better way to handle memory-mapped
    // I/O is to hook the page fault as we did here and decode the
    // faulting instruction, simulate its operation, and advance 16 bits
    // application program counter(getIP and setIP).

    BaseAddress = VDDAllocMem ((LPVOID) MIOAddress, PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE);
    if (!BaseAddress) {
    OutputDebugString("VDD: Can't allocate virtual memory");
    }
}


Including Upper Memory Blocks

Many devices have a ROM that occupies a particular range in the device’s memory space. If a VDD knows it will not be accessing the ROM, it can call VDDIncludeMem to allow the VDM to use the address range of the ROM as an MS-DOS UMB. A VDD most likely calls VDDIncludeMem if it has chosen to implement application-based intercepts, as described in Application-Based Intercepts, where the VDD replaces most of the functionality originally in the device ROM. Since the ROM is no longer needed, its memory space can be reused by the VDM.


Memory Access

MS-DOS applications are written to use the x86 segmented memory architecture. They use pointers composed of a 16-bit segment or selector and a 16-bit offset into the segment. Windows NT, on the other hand, uses a flat memory model, in which pointers are simply 32-bit linear addresses. Because a VDD can receive a segmented address pointer from an MS-DOS application, Windows NT provides the function GetVDMPointer to translate MS-DOS segment:offset or selector:offset style pointers to 32-bit linear address pointers. The VDD can use the 32-bit pointer to access memory by using Win32 functions, such as VDDAllocMem.

Do not manually convert segmented addresses (by shifting the segment by four and adding the offset). On a non-x86 machine, the VDM might not be based at physical address zero. The GetVDMPointer function converts a segmented address to a 32-bit address in a platform-independent manner.

If the VDD is running on a RISC-based Windows NT machine, the x86 emulator precompiles instructions in memory to increase performance. When a VDD uses a pointer returned by GetVDMPointer to modify memory in a particular range, it is important to tell the emulator that a change has occurred so the emulator can recompile the instructions in that range. Calling FlushVDMPointer or FreeVDMPointer tells the emulator the memory is no longer subject to modification and the instructions in that range can be precompiled again. These functions simply return to the caller on an x86 machine.


DMA

In the MS-DOS environment, applications can use DMA to directly transfer data between application memory and the memory on a device. In Windows NT, applications cannot use DMA to directly access physical memory. If they could, they would subvert the memory protection afforded by the Windows NT memory manager. Therefore, the VDM controls all the DMA ports used by MS-DOS applications. Requests for DMA service from MS-DOS applications do not actually result in the DMA controller being programmed. Instead, the VDM traps the parameters to the request and does not actually transfer the data. It is up to the VDD to pick up the DMA transfer request and complete the operation. Because the VDM keeps track of DMA requests on a per-channel basis, more than one VDD in the same VDM can use DMA as long as they use different channels.

Unlike the callback mechanisms for I/O port access and memory-mapped accesses, the VDD is not notified directly that a DMA transfer has been requested. Instead, the VDD determines the application has requested a DMA operation when the VDD receives a command from the application through the normal I/O port channels on its board. The application and the device must use some sort of synchronization technique to coordinate DMA transfers. Because all possible synchronization messages between an MS-DOS application and its device must pass through the VDD in a Windows NT system, the VDD is notified whenever a DMA transfer is requested. At that point, the VDD must do something to actually transfer the data.

There are two strategies for handling DMA from a VDD. The first strategy involves the least programming work, but it requires two buffer-copy operations and is inherently inefficient. The second strategy is harder to implement but more efficient because it only requires a single buffer-copy operation. These two strategies are described in the following sections.


Double-Buffer DMA Transfer

In the double-buffer strategy, the VDD calls VDDRequestDMA to move the data as described by the last DMA request. The VDDRequestDMA function looks at the last DMA command trapped by the VDM and executes a data transfer that matches those DMA parameters, although the transfer does not actually use DMA. When the DMA transfer request specifies that data be moved from an application buffer to the device, the data actually goes from the application buffer to a block of memory the VDD has mapped to the virtual address space occupied by the board. When the data has been transferred to the VDD’s buffer, the VDD can then transfer it to the device by passing the data to the kernel-mode device driver. Likewise, when the DMA transfer is requesting data be moved from the device to an application buffer, the VDD first calls the kernel-mode driver to copy the data from the device to the VDD’s buffer. The VDD can then transfer the data from its buffer to the application’s buffer by calling VDDRequestDMA, as shown in the following illustration.

Two-stage virtual DMA transfer


Single Buffer DMA Transfer

In the single-buffer strategy, the VDD again determines a DMA request is pending by receiving a command from the application. To avoid copying the data to its own memory space, the VDD calls VDDQueryDMA to determine the source, destination, and other DMA parameters. It then sends the DMA information to the kernel-mode device driver and allows that driver to transfer the data directly from the application’s buffer to the device, as shown in the following illustration. The kernel-mode device driver can program the DMA controller through the platform-specific HAL to carry out the transfer or it can use some other means to transfer the data. In the other direction, the VDD can request that the driver transfer data directly from the device to the application’s buffer. In either direction, VDDs using this second strategy must call VDDSetDMA to update the DMA state after the transfer is complete.

Direct virtual DMA transfer

Because DMA registers contain physical addresses, a VDD that uses VDDQueryDMA must correctly convert these addresses to Win32 linear addresses in the VDM process address space.

The following illustration shows how the virtual DOS machine can be laid out in memory relative to the VDM process. On an x86 machine, linear zero of the VDM process is the same as 0:0 for DOS. On a nonx86 machine these addresses might be different. The addresses returned by VDDQueryDMA are relative to the DOS 0:0.

Memory Layout for DMA addresses

On an x86 machine, there is no difference between a DMA address, as programmed by the MS-DOS application to the DMA chip, and a Win32 address. On a nonx86 machine, you must add the offset from VDM 0 to DOS 0:0 to convert a DMA address to a Win32 address. To get the offset from VDM to DOS, call GetVDMPointer(0,0,0). On an x86 machine, this call returns zero. On a RISC machine, it returns the difference between VDM 0 and DOS 0:0. Thus, adding GetVDMPointer(0,0,0) to a DMA address always gives the correct Win32 linear pointer, regardless of machine type.


Example DMA Transfers

To illustrate these two strategies, consider the example of a fax board in an MS-DOS environment. The controlling application creates an image of the fax in application memory. It then programs the DMA controller to move the fax image to the memory on the fax board. The application then sends a command to the fax board (through an I/O port operation or other means) telling it to receive the DMA transfer and start sending the fax. In Windows NT, the unmodified MS-DOS application running under VDM requests the DMA and then signals the fax board to receive the DMA and begin sending the fax. However, there are two differences:

  1. The DMA controller has not really been programmed because the VDM intercepted the DMA request, although the application has no way of knowing this.

  2. The command to the fax board has been intercepted by the VDD. So now, the VDD must move the data specified by the DMA request to the board.

Using the double-buffer strategy described in Double-Buffer DMA Transfer, the VDD calls VDDRequestDMA to move the data from the application’s buffer to the VDD buffer. This call does not actually use the DMA controller, but performs the equivalent data transfer by other means. The VDD then calls the kernel-mode driver to move the data to the device. Finally, the VDD relays the send fax command to the kernel-mode driver.

Using the single-buffer strategy described in Single Buffer DMA Transfer, the VDD calls VDDQueryDMA to determine the parameters for the DMA request. The VDD then sends these parameters to the kernel-mode driver and enables the driver to control the data transfer directly from the application’s buffer to the device. When the driver has transferred the data, the VDD calls VDDSetDMA to update the DMA state. Finally, the VDD relays the send fax command to the kernel-mode driver.

The following functions are used for memory accesses:
FlushVDMPointer | Flushes data associated with a memory range.
FreeVDMPointer | Releases a pointer returned by GetVDMPointer.
GetVDMPointer | Returns a linear address for an x86 address.
VDDAllocMem | Allocates memory at a given virtual address.
VDDFreeMem | Releases previously allocated memory.
VDDDeInstallMemoryHook | Releases a range of memory-mapped I/O addresses.
VDDIncludeMem | Includes a particular memory range for use as a UMB.
VDDInstallMemoryHook | Releases memory-mapped addresses.
VDDQueryDMA | Gathers all DMA data.
VDDRequestDMA | Requests a DMA transfer from a VDD.
VDDSetDMA | Resets the state of the virtual DMA controller.


Register Manipulation

The VDM provides a complete set of API functions to manipulate the registers of an x86 processor. The VDD can use these functions to get and set registers as needed to facilitate its communication with its device. These functions can be used to get CS and IP during a memory hook, as described in Memory Hooks.The register functions are useful in a VDD if your modified application calls the VDD directly, because the direct interface between an MS-DOS application and a VDD requires certain arguments be passed in registers.

On a RISC-based Windows NT machine, these register manipulation functions use the virtual register set maintained by the x86 emulator.

Table 8.1 Register Manipulation Functions
GET Register Functions | SET Register Functions
getEAX( ) | setEAX(value)
getAX( ) | setAX(value)
getAH( ) | setAH(value)
getAL( ) | setAL(value)
getEBX( ) | setEBX(value)
getBX( ) | setBX(value)
getBH( ) | setBH(value)
getBL( ) | setBL(value)
getECX( ) | setECX(value)
getCX( ) | setCX(value)
getCH( ) | setCH(value)
getCL( ) | setCL(value)
getEDX( ) | setEDX(value)
getDX( ) | setDX(value)
getDH( ) | setDH(value)
getDL( ) | setDL(value)
getESP( ) | setESP(value)
getSP( ) | setSP(value)
getEBP( ) | setEBP(value)
getBP( ) | setBP(value)
getESI( ) | setESI(value)
getSI( ) | setSI(value)
getEDI( ) | setEDI(value)
getDI( ) | setDI(value)
getEIP( ) | setEIP(value)
getIP( ) | setIP(value)
getCS( ) | setCS(value)
getSS( ) | setSS(value)
getDS( ) | setDS(value)
getES( ) | setES(value)
getCF( ) | setCF(value)
getPF( ) | setPF(value)
getAF( ) | setAF(value)
getZF( ) | setZF(value)
getSF( ) | setSF(value)
getIF( ) | setIF(value)
getDF( ) | setDF(value)
getOF( ) | setOF(value)
getMSW( ) | setMSW(value)


Interrupt Simulation

The VDDSimulateInterrupt function allows a VDD to simulate a hardware interrupt to the VDM. Call this function if the application depends on an interrupt to tell it that some particular operation has been completed. For example, the application using a fax board might expect to get a particular interrupt when the board is done sending a fax. Because the VDD is notified when the fax board is done, the VDD can simulate the interrupt to the application.

When the VDD calls VDDSimulateInterrupt, the address pointed to by the interrupt vector starts running in 16-bit mode. For an asynchronous interrupt, the VDD should create another thread and call VDDSimulateInterrupt from that thread.

The VDDSimulate16 function is similar to VDDSimulateInterrupt, except it does not require a hardware interrupt to be supported by the 16-bit stub driver. VDDSimulate16 allows the VDD to execute a routine in its 16-bit driver and return when it has finished. Before calling VDDSimulate16, a VDD should preserve all the 16-bit registers its routine might destroy; minimally, it should preserve CS and IP. Then, set the CS and IP for the 16-bit routine. A VDD can also use registers such as AX or BX to pass parameters to its 16-bit routines. At the end of the 16-bit routine, VDDUnSimulate16 sends control back to the VDD. Because this simulation is synchronous, the VDD is blocked in VDDSimulate16 and returns only when the stub driver calls VDDUnSimulate16. The following code shows an example of how these calls are used:

vdd (Win32 Code):
SaveCS = getCS( );
SaveIP = getIP( );
SaveAX = getAX( );
setCS (16BitRoutineCS);
setIP (16BitRoutineIP);
setAX (DO_X_OPERATION);
VDDSimulate16( );
setCS (SaveCS);
setIP (SaveIP);
setAX (SaveAX);
. .
. .
Stub Driver (Initialization):
RegisterModule				;Loads VDD
push cs
pop ax
mov bx,offset Simulate16
DispatchCall				;passes the address of worker
                            ;routine to VDD in ax:bx
Stub Driver (Run Time)

Simulate16:
. .
. .							;do the operation index passed in ax
VDDUnSimulate16


Hooking Callback Events

A VDD can hook a callback event if it polls DOS events for which there is no explicit close operation, such as printing using int17h.The VDDInstallUserHook and VDDDeInstallUserHook functions allow a VDD to control events such as:

  • PDB (DOS process) creation and termination

  • VDM block

  • VDM resume

A VDM in which a DOS application runs is attached to the console window in which the DOS application is running. When the first DOS binary runs in that console, the VDM is created, and the DOS binary is terminated. The VDM remains with the console window and waits for the next DOS binary to be launched. While the VDM waits, it should block all its components, including VDDs. A VDD can hook VDM Block and Resume events for this purpose. On block events, a VDD should block all worker threads and clean up any other operation it may have started. On Resume events, the VDD can restart a worker thread. VDDDeInstallUserHook de-installs all events hooked earlier.


Summary

VDDs provide a translation layer between 16-bit MS-DOS applications and special-purpose hardware devices in the Windows NT system. A VDD turns an application’s attempt to write directly to a device into a Win32 API call that makes a request to a Windows NT kernel-mode device driver. The key characteristic of every VDD is the original MS-DOS application does not need to be modified to work on Windows NT machines. By adding a VDD and a kernel-mode device driver, an application developer can move an existing MS-DOS application and its dedicated hardware device to Windows NT.

For improved performance, the MS-DOS application can be modified to directly call its companion VDD. Otherwise, the unmodified application can rely on the VDM to intercept disallowed device accesses and route them to the VDD.

The three main areas in which the VDM intercepts an application’s attempts to interact directly with device hardware are:

  1. I/O ports

  2. Memory mapped I/O

  3. DMA

The VDD can install callback functions to handle I/O port access and memory-mapped I/O. The VDD can also make calls to functions provided by Windows NT to support DMA transfers between an application and a hardware device. In addition, Windows NT supplies functions that allow a VDD to query and set processor registers and simulate interrupts. These services allow the VDD to fully support the functionality of the original MS-DOS application and its accompanying hardware device.

For information about writing 32-bit device drivers, see the Kernel-mode Driver Design Guide.


VDD Functions

The topics that follow describe the functions associated with Virtual Device Drivers.


DispatchCall

DispatchCall( )

DispatchCall is a BIOS operation call made from the 16-bit module to pass a request to its DLL.

Parameters

register AX
VDD handle returned by RegisterModule. Other registers may contain the parameters for VDD from 16-bit device driver.

Return Value

If AX is invalid, VDM is terminated with a debug message. VDD can set any register to actually return the value for a 16-bit device driver.

Comments

This service is for application-based intercepts only, to pass a request to a 32-bit DLL.

This function is a 16-bit assembly interface.

See Also

RegisterModule


FlushVDMPointer

BOOL FlushVDMPointer(Addr, Size, Buffer,
ProtectedMode)
IN ULONG  Addr;
IN USHORT  Size;
PBYTE  Buffer;
IN BOOL  ProtectedMode;

FlushVDMPointer flushes any data associated with the memory range. This is required due to the CPU emulator.

Parameters

Addr
The high word is segment or selector and the low word is offset.
Size
Specifies the range of the pointer.
Buffer
Specifies the address returned by GetVDMPointer.
ProtectedMode
If set to TRUE then the address given is selector: offset. Otherwise it is segment: offset.

Return Value

FlushVDMPointer returns TRUE if successful. Otherwise an error message is logged.

Comments

VDDs should use this function to ensure that a nonx86 machine CPU emulator gets a chance to flush any data associated with a memory range.

See Also

GetVDMPointer


FreeVDMPointer

BOOL FreeVDMPointer(Address, Size, Buffer,
ProtectedMode)
IN ULONG  Address;
IN USHORT  Size;
PBYTE  Buffer;
IN BOOL  ProtectedMode;

FreeVDMPointer releases a pointer returned by GetVDMPointer.

Parameters

Address
The high word is a segment or selector, and the low word is an offset.
Size
Specifies the range in bytes of the pointer.
Buffer
Specifies the address returned by GetVDMPointer.
ProtectedMode
If the ProtectMode is TRUE then the address given is selector: offset. Otherwise, it is segment: offset.

Return Value

FreeVDMPointer returns TRUE if the buffer was freed.

Comments

FreeVDMPointer flushes the pointer before freeing the buffer.

See Also

GetVDMPointer, FlushVDMPointer


GetVDMPointer

PBYTE GetVDMPointer(Address, Size, ProtectedMode)
IN ULONG Address;
IN ULONG Size;
IN BOOL ProtectedMode;

GetVDMPointer returns the linear address for a given x86 address.

Parameters

Address
The high word is a segment or selector, and the low word is the offset.
Size
Specifies the range of the pointer.
ProtectedMode
If set to TRUE, then the high word is a selector. Otherwise, the highword is a segment.

Return Value

GetVDMPointer returns the linear address.

Comments

The VDDs should use this function to convert an x86-type address rather than shifting the segment by four and adding the offset. This makes linear addresses platform-independent.

See Also

FlushVdmPointer, FreeVdmPointer


Register Manipulation Services

Register manipulation services are provided by the following functions. VDD can use these functions to get and set registers as needed to communicate with the 16-bit modules.

The register functions are very useful in a VDD if your modified 16-bit module calls the VDD directly (through DispatchCall), since the direct interface between an MS-DOS application and a VDD require certain arguments be passed in registers.
ULONG getEAX( ); | VOID setEAX(ULONG);
USHORT getAX( ); | VOID setAX(USHORT);
UCHAR getAL( ); | VOID setAH(CHAR);
UCHAR getAH( ); | VOID setAL(UCHAR);
ULONG getEBX( ); | VOID setEBX(ULONG);
USHORT getBX( ); | VOID setBX(USHORT);
UCHAR getBL( ); | VOID setBH(UCHAR);
UCHAR getBH( ); | VOID setBL(UCHAR);
ULONG getECX( ); | VOID setECX(ULONG);
USHORT getCX( ); | VOID setCX(USHORT);
UCHAR getCL( ); | VOID setCH(UCHAR);
UCHAR getCH( ); | VOID setCL(UCHAR);
ULONG getEDX( ); | VOID setEDX(ULONG);
UCHAR getDH( ); | VOID setDX(USHORT);
ULONG getESP( ); | VOID setDH(UCHAR);
USHORT getSP( ); | VOID setDL(UCHAR);
ULONG getEBP( ); | VOID setESP(ULONG);
USHORT getBP( ); | VOID setSP(USHORT);
ULONG getESI( ); | VOID setEBP(ULONG);
USHORT getSI( ); | VOID setBP(USHORT);
ULONG getEDI( ); | VOID setESI(ULONG);
USHORT getDI( ); | VOID setSI(USHORT);
ULONG getEIP( ); | VOID setEDI(ULONG);
USHORT getIP( ); | VOID setDI(USHORT);
USHORT getCS( ); | VOID setEIP(ULONG);
USHORT getSS( ); | VOID setIP(USHORT);
USHORT getDS( ); | VOID setCS(USHORT);
USHORT getES( ); | VOID setSS(USHORT);
USHORT getFS( ); | VOID setDS(USHORT);
USHORT getGS( ); | VOID setES(USHORT);
ULONG getCF( ); | VOID setFS(USHORT);
ULONG getPF( ); | VOID setGS(USHORT);
ULONG getAF( ); | VOID setCF(ULONG);
ULONG getZF( ); | VOID setPF(ULONG);
ULONG getSF( ); | VOID setPF(ULONG);
ULONG getIF( ); | VOID setAF(ULONG);
ULONG getDF( ); | VOID setZF(ULONG);
ULONG getOF( ); | VOID setSF(ULONG);
USHORT getMSW( ); | VOID setIF(ULONG);
VOID setDF(ULONG); | VOID setOF(ULONG);
VOID setMSW(USHORT); |


RegisterModule

RegisterModule( )

This BIOS operation call is made from the 16-bit module to register a third party DLL with the BIOS operation manager. This call returns a handle to the 16-bit caller which is used later to dispatch a call to the DLL.

Parameters

Client DS:SI
ASCIIZ string of DLL name
Client DS:DI
ASCIIZ string of Initialization Routine in the DLL. (Optional if DS = 0 and DI = 0).
Client DS:BX
ASCIIZ string to Dispatch routine in the DLL.

Return Value

If successful the client’s carry is cleared and the client’s handle in AX will equal (non Zero). Otherwise, the client carry will be set, and an error code will be returned in AX.
Client AX | Error Code
AX = 1 | DLL not found.
AX = 2 | Dispatch routine not found.
AX = 3 | Initialization routine not found.
AX = 4 | Insufficient memory available.

Comments

RegisterModule will load the DLL specified in DS:SI. The DLL initialization routine is called and the DLL dispatch routine is stored. All calls made from the DispatchCall are sent to this routine.

See Also

UnRegisterModule, DispatchCall


UnRegisterModule

UnRegisterModule( )

UnRegisterModule is a BIOS operation call from the 16-bit module to unregister a third party DLL with the BIOS manager.

Parameters

Client AX
VDD handle returned by RegisterModule.

Return Value

None.

Comments

VDM is terminated with a debug message if the handle is invalid.

Use UnRegisterModule if initialization of a 16-bit application fails after registering the BIOS operation.

See Also

RegisterModule


VDDAllocMem

BOOL VDDAllocMem(hVdd, Address, Size)
IN HANDLE hVdd;
IN PVOID Address;
IN DWORD Size;

VDDAllocMem allocates memory at a given virtual address.

Parameters

hVdd
Identifies the VDD.
Address
Specifies the address between 640K and 1MB where memory will be allocated.
Size
Specifies the number of bytes of memory to allocate.

Return Value

VDDAllocMem returns TRUE if successful. If the function fails then FALSE is returned and an error is logged.

The extended error codes are:
Value | Meaning
ERROR_OUTOFMEMORY | Indicates insufficient memory error.
ERROR_INVALID_ADDRESS | Identifies invalid address error.

Comments

VDDs must use this function instead of VirtualAlloc (see the Win32 SDK) to allocate memory.

Address is page-aligned downwards, and Size is stretched upward to be page-aligned.

Address is relative to DOS 0:0. GetVDMPointer should not be called on this address before calling VDDAllocMem.

This address is the same as that provided by the hook handler for memory-mapped I/O.

See Also

GetVDMPointer


VDDFreeMem

BOOL VDDFreeMem(hVdd, Address, Size)
IN HANDLE hVdd;
IN PVOID Address;
IN DWORD Size;

VDDFreeMem releases previously allocated memory at a given virtual address.

Parameters

hVdd
Identifies the VDD.
Address
Specifies the address where memory will be freed.
Size
Specifies the number of bytes of memory to free.

Return Value

VDDFreeMem returns TRUE if successful. If the function fails then FALSE is returned and an error is logged. The extended error code available is ERROR_INVALID_ADDRESS, indicating an invalid address error.

Comments

The freed Address is page-aligned downwards, and Size is stretched upwards to be page-aligned.

Address is relative to DOS 0:0. That means GetVDMPointer should not be called on this address before calling VDDAllocMem.

This address is the same as that provided by the hook handler for memory-mapped I/O.

See Also

VDDAllocMem, VDDInstallMemoryHook


VDDDeInstallMemoryHook

BOOL VDDDeInstallMemoryHook(hVdd, pStart, count)
IN HANDLE hVdd;
IN PVOID pStart;
IN DWORD count;

VDDDeInstallMemoryHook releases a range of memory-mapped I/O addresses previously allocated by a VDD.

Parameters

hVdd
Identifies the VDD.
pStart
Points to the starting linear address of the range.
count
Identifies the number of bytes of mapped memory that are being used.

Return Value

VDDDeInstallMemoryHook returns TRUE if successful. Otherwise, it returns FALSE and logs an error.

Comments

The memory range becomes invalid once a hook is removed. The only extended error code is ERROR_INVALID_PARAMETER.

Address is relative to DOS 0:0. That means GetVDMPointer should not be called on this address before calling VDDAllocMem.

See Also

VDDInstallMemoryHook


VDDDeInstalllOHook

VOID VDDDeInstalllOHook(hVdd, cPortRange, pPortRange)
IN HANDLE hVdd;
IN WORD cPortRange;
IN PVDD_IO_PORTRANGE pPortRange;

VDDDeinstalllOHook unhooks the I/O ports previously hooked by a VDD.

Parameters

hVdd
Identifies the VDD.
cPortRange
Specifies the number of VDD_IO_PORTRANGE structures.
pPortRange
Points to the VDD_IO_PORTRANGE array.

Comments

When VDDDeinstalllOHook cancels a hook, the default hook on those ports subsequently returns 0xFF when reading the port and ignores write operations.

See Also

VDDInstalllOHook


VDDDeInstallUserHook

BOOL VDDDeInstallUserHook(hVDD)
IN HANDLE hVDD;

VDDDeInstallUserHook unhooks callback events previously hooked by this function.

Parameters

hVDD
Identifies the VDD.

Return Value

TRUE if successful, FALSE otherwise. GetLastError has extended error information.

See Also

VDDInstallUserHook


VDDIncludeMem

BOOL VDDIncludeMem(hVDD, Address, nBytes)
IN HANDLE hVDD;
IN PVOID Address;
DWORD Size;

VDDIncludeMem includes a specified memory range so that it can be used as UMB.

Parameters

hVDD
Identifies the VDD handle.
Address
Points to the address where memory is to be included.
Size
Specifies the number of bytes to be included.

Return Value

VDDIncludeMem returns TRUE if successful. Otherwise, FALSE is logged and an error is returned. The available extended error code is ERROR_INVALID_ADDRESS¾Invalid address.

Comments

If there is a ROM block in the attached adapter and the corresponding VDD knows that it will never use that ROM, the VDD may put the ROM memory space back to the UMB list so the memory is available to the DOS arena.

The address and the size must exactly match the ROM of the attached adapter. It is not allowed to include a portion of the ROM to be used as a UMB.


VDDInstallMemoryHook

BOOL VDDInstallMemoryHook(hVdd, pStart, count,
MemoryHandler)
IN HANDLE  hVdd;
IN PVOID  pStart;
IN DWORD  count;
IN PVDD_MEMORY_HANDLER  MemoryHandler;

VDDInstallMemoryHook releases the memory-mapped I/O addresses a VDD controls.

Parameters

hVdd
Identifies the VDD.
pStart
Points to the starting linear address.
count
Identifies the number of bytes of mapped memory.
MemoryHandler
Points to the VDD handler for the memory address range.

Return Value

VDDInstallMemoryHook returns TRUE if successful. Otherwise, it returns FALSE and logs one of the following errors:
Value | Meaning
ERROR_ACCESS_DENIED | One of the requested ports is already hooked.
ERROR_OUTOFMEMORY | There are insufficient resources.

Comments

The first VDD to hook an address will get control of the memory. There is no chaining of hooks. A VDD should grab the memory during its initialization routine. After all VDDs are loaded, the EMM component will use all remaining memory ranges for UMB support.

Installing a hook on a memory range results in the consumption of memory based on page boundaries. The pStart is rounded down. count is rounded up to the next page boundary. The VDD’s memory hook handler is called for all addresses within the page(s) used. The page(s) are set aside as mapped reserved sections, and will no longer be available for use by the Win32 VDM component or other VDDs. The VDD is permitted to manipulate the memory (commit, free, etc.). Only one VDD is allowed to have memory hooks in a particular page.

The VDD’s memory handler will be called with the address of any page fault and with a flag identifying whether the fault was caused by a read or write operation.

On return from the VDD hook handler, Windows NT will assume that the page fault was handled and returns control to the VDM component.

After calling the VDD’s MemoryHandler, VDM returns to the faulting cs:ip in the 16-bit application. If the VDD doesn’t need this, it can adjust the cs:ip using the setCS and setIP functions.

See Also

VDDDeinstallMemoryHook


VDDInstallIOHook

BOOL VDDInstallIOHook(hVDD, cPortRange, pPortRange,
10handler)
IN HANDLE  hVDD;
IN WORD  cPortRange;
IN PVDD_IO_PORTRANGE  pPortRange;
IN PVDD_IO_HANDLERS  10handler;

VDDInstallIOHook hooks the I/O Ports for the responsible VDD.

Parameters

hVdd
Identifies the VDD.
cPortRange
Specifies the number of VDD_IO_PORTRANGE structures.
pPortRange
Points to the VDD_IO_PORTRANGE array.
10handler
Points to the handler array pointer.

Return Value

VDDInstallIOHook returns TRUE if successful. Otherwise, this function returns FALSE and logs one of the following errors:
Value | Meaning
ERROR_ACCESS_EXISTS | The VDD already has active I/O port handlers.
ERROR_OUTOFMEMORY | There are insufficient resources for additional VDD port handler sets.
ERROR_INVALID_ADDRESS | One of the I/O port handlers has an invalid address.

Comments

The last VDD to hook a port will always get control of the port. There is no concept of chaining hooks.

plOFn must provide the byte read and write handlers. Others can be NULL.

If word or string handlers are not provided, their effect will be emulated using byte handlers.

VDDs must not hook DMA ports. The Win32 VDM component manages this for all the clients and services provided to perform DMA operations. This includes any attempt to access and modify DMA data.

VDDs should not hook video ports. A video port can be successfully hooked but there is no guarantee that the I/O handler will get called.

Each VDD is allowed to install only one set of I/O hooks at a time.

See Also

VDDDeInstalllOHook


VDDInstallUserHook

BOOL VDDInstallUserHook(hVDD, Ucr_Handler, Uterm_Handler,
Ublock_handler,
Uresume_handler)
IN HANDLE  hVDD;
IN PFNVDD_UCREATE  Ucr_Handler;
IN PFNVDD_UTERMINATE  Uterm_Handler;
IN PFNVDD_UBLOCK  Ublock_handler;
IN PFNVDD_URESUME  Uresume_handler;

VDDInstallUserHook hooks callback events. These callback events include PDB (DOS process) creation, PDB termination, VDM block and VDM resume.

Parameters

hVDD
Identifies the VDD.
Ucr_Handler
Function which will be called on PDB creation (optional). Entry: 16-bit DOS PDB; exit: none.
Uterm_handler
Function which will be called on PDB termination (optional). Entry: 16-bit DOS PDB; exit:none.
Ublock_handler
Function which will be called when NTVDM is blocked (optional).
Uresume_handler
Function which will be called with NTVDM resumes (optional).

Return Value

VDDInstallUserHook returns TRUE if successful. GetLastError (see the Win32 SDK) has extended error information.

Comments

The VDD can provide whatever event hook it may choose. Not providing a handler has no effect.

See Also

VDDDeInstallUserHook


VDDQueryDMA

BOOL VDDQueryDMA(hVdd, iChannel, pDmaInfo)
IN HANDLE hVdd;
IN WORD iChannel;
IN PVDD_DMA_INFO pDmaInfo;

VDDQueryDMA provides DMA data.

Parameters

hVdd
Identifies the VDD.
iChannel
Specifies the DMA channel to query.
pDmaInfo
Specifies the buffer location where information will be returned.

Return Value

The VDDQueryDMA function returns TRUE if successful. If the function fails then FALSE is returned and an error is logged. The extended error code is ERROR_INVALID_ADDRESS which identifies an invalid channel.

Comments

This function will assist VDDs which are doing performance critical work. Such a VDD does its own DMA transfers and avoids an extra buffer copy, which is the overhead of using VDDRequestDMA

VDDs must call VDDSetDMA to update the state of the virtual DMA controller after completing an operation.

See Also

VDDRequestDMA, VDDSetDMA


VDDRequestDMA

DWORD VDDRequestDMA(hVDD, iChannel, Buffer,
length)
IN HANDLE  hVDD;
WORD  iChannel;
PVOID  Buffer;
DWORD  length;

VDDRequestDMA requests a DMA transfer from a VDD.

Parameters

hVDD
Identifies the VDD.
iChannel
Specifies the DMA channel for the transfer operation.
Buffer
Points to the buffer to or from which the data is transferred.
length
Specifies the size in bytes of the transfer count. If length is 0, the current transfer count is returned.

Return Value

VDDRequestDMA returns the number of bytes transferred. If zero is returned and GetLastError (see the Win32 SDK) is set, then the operation failed. If zero is returned and GetLastError is clear, then the DMA transfer count was zero.

The extended error codes is ERROR_INVALID_ADDRESS which identifies an invalid channel.

Comments

VDDs that need to carry out DMA operations but that do not need to deal with all DMA registers should call this function.

Using this function is slower than using VDDQueryDMA and VDDSetDMA.

See Also

VDDQueryDMA, VDDSetDMA


VDDSetDMA

BOOL VDDSetDMA(hVdd, iChannel, fDMA,
Buffer)
IN HANDLE  hVdd;
IN WORD  iChannel;
IN WORD  fDMA;
IN PVDD_DMA_INFO  Buffer;

VDDSetDMA resets the state of the virtual DMA controller after a DMA operation using VDDQueryDMA.

Parameters

hVdd
Identifies the VDD.
iChannel
Specifies the DMA Channel to set.
fDMA
Provides bitmask indicating what must be set:

VDD_DMA_ADDR
VDD_DMA_COUNT
VDD_DMA_PAGE
VDD_DMA_STATUS
VDD_DMA_ALL (All of the above)

Buffer
Specifies the buffer location of the DMA data.

Return Value

VDDSetDMA returns TRUE if successful. If the function fails then FALSE is returned. The error logged is ERROR_INVALID_ADDRESS to identify an invalid channel.

See Also

VDDRequestDMA, VDDQueryDMA


VDDSimulate16

VOID VDDSimulate16( )

VDDSimulate16 is similar to VDDSimulateInterrupt except that it does not require a hardware interrupt to be supported by the 16-bit stub device driver. VDDSimulate16 allows a VDD to execute a routine in its 16-bit driver and return when it has finished. Before calling VDDSimulate16, a VDD should preserve at least CS and IP, as well as any other 16-bit registers which its 16-bit routine might destroy. This simulation is synchronous.

See Also

VDDUnSimulate16


VDDSimulateInterrupt

VOID VDDSimulateInterrupt(ms, line, count)
IN INT ms;
IN BYTE line;
IN INT count;

VDDSimulateInterrupt simulates an interrupt to the VDM to support applications that expect an interrupt to signal the end of an operation.

Parameters

ms
Is either ICA_MASTER or ICA_SLAVE, as appropriate.
line
Specifies the interrupt line.
count
Allows a set of interrupts to be delivered. Normally there is only one interrupt.


VDDTerminateVDM

VOID VDDTerminateVDM( )

VDDTerminateVDM terminates the VDM in which the DOS application is running.

Comments

The VDD must call this function if it encounters an error that results in an inconsistent VDD state.

The VDD can call MessageBox (see the Win32 SDK) to produce a popup window before terminating the application.


VDDUnSimulate16

VOID VDDUnSimulate16( )

VDDUnSimulate16 causes the simulation in Intel instructions to stop, and control to return to the VDD. This routine is a macro intended for 16-bit stub drivers. A driver should use it at the end of its worker routine.

See Also

VDDSimulate16


WOW32DriverCallback

BOOL WOW32DriverCallback(dwCallback, dwFlags, wDeviceHandle,
wMsg, dwUser,
dwParam1, dwParam2)
DWORD  dwCallback;
DWORD  dwFlags;
WORD  wDeviceHandle;
WORD  wMsg;
DWORD  dwUser;
DWORD  dwParam1;
DWORD  dwParam2;

WOW32DriverCallback allows a 32-bit thunk extension DLL to simulate a Multimedia hardware interrupt callback into 16-bit code. The built-in thunks for wave, midi, and time callbacks make use of this function to callback into 16-bit application code.

Parameters

dwCallback
Specifies either the address of a callback function, a window handle, or a task handle, depending on the flags specified in dwFlags.
dwFlags
Specifies how the 16-bit client application is notified, according to one of the following flags:

Flag

Meaning

DCB_FUNCTION

Indicates that a message should be sent to a callback function. dwCallback must be a 16-bit address of the 16-bit application callback routine.

DCB_WINDOW

Indicates that a message should be sent to the 16-bit window handle specified in dwCallback.

DCB_TASK

Indicates that a message should be sent ot the 16-bit task handle specified in dwCallback.

wDeviceHandle
Specifies a 16-bit handle of the device associated with the notification.
wMsg
Specifies a message to be sent to the application.
dwUser
Specifies the DWORD of user-instance data supplied by the application when the device was opened.
dwParam1
Specifies a message-dependent parameter. If this is a pointer, it must be mapped to a valid 16-bit address. All fields in the structure pointed to must be mapped correctly.
dwParam2
Specifies a message-dependent parameter. If this is a pointer, it must be mapped to a valid 16-bit address. All fields in the structure pointed to must be mapped correctly.

Return Value

WOW32DriverCallback returns TRUE if the operation was successful.

Comments

The 16-bit client specifies how it is notified when the device is opened. If notification is accomplished with a callback function, wDeviceHandle, wMsg, dwUser, dwParam1, and dwParam2 are passed to the callback. If notification is accomplished by means of a window, only wMsg, wDeviceHandle, and dwParam1 are passed.

Users will have to dynamically link to this function.

See Also

WOW32ResolveHandle


WOW32ResolveHandle

BOOL WOW32ResolveHandle(uHandleType, uMappingDirection,
wHandle16_In,
lpwHandle16_Out, dwHandle32_In, lpdwHandle32_Out)
UINT  uHandleType;
UINT  uMappingDirection;
WORD  wHandle16_In;
LPWORD  lpwHandle16_Out;
DWORD  dwHandle32_In;
LPDWORD  lpdwHandle32_Out;

WOW32ResolveHandle allows a 32-bit thunk extension DLL to map 16-bit handles to valid 32-bit handles and vice-versa.

Parameters

uHandleType
Specifies a generic user handle (WOW32_USER_HANDLE), generic GDI handle (WOW32_GDI_HANDLE), or multimedia handles (WAVEIN_HANDLE, WAVEOUT_HANDLE, MIDIOUT_HANDLE, or MIDIIN_HANDLE).
uMappingDirection
Specifies the handle mapping direction. Can be either WOW32_DIR_16IN_32OUT or WOW32_DIR_32IN_16OUT.
wHandle16_In
Specifies a 16-bit input handle which is ignored if uMappingDirection is WOW32_DIR_32IN_16OUT.
lpwHandle16_Out
Specifies a 16-bit output handle which is ignored if uMappingDirection is WOW32_DIR_16IN_32OUT.
dwHandle32_In
Specifies a 32-bit input handle which is ignored if uMappingDirection is WOW32_DIR_16IN_32OUT.
lpdwHandle32_Out
Specifies a 32-bit output handle which is ignored if uMappingDirection is WOW32_DIR_32IN_16OUT.

Return Value

WOW32ResolveHandle returns FALSE if a known error occurred in the mapping. A return value of TRUE indicates that the mapping was successful but does not mean that the mapped handle is valid. For example, it may be possible to map an invalid 16-bit handle to an invalid 32-bit handle.

Comments

Users will have to dynamically link this function.

See Also

WOW32DriverCallback


VDD Structures

The topics that follow describe the structures associated with Virtual Device Drivers.


VDD_DMA_INFO

typedef struct _VDD_DMA_INFO {
   WORD    addr;
   WORD    count;
   WORD    page;
   BYTE    status;
   BYTE    mode;
   BYTE    mask;
} VDD_DMA_INFO, *PVDD_DMA_INFO;

VDD_DMA_INFO contains DMA information.

Members

addr
The DMA physical address.
count
The DMA count.
page
The page identifier.
status
A numeric representation of the status.
mode
A numeric representation of the mode.
mask
The binary value used to screen out certain bits in the data value.


VDD_IO_HANDLERS

typedef struct _VDD_IO_HANDLERS {
    PFNVDD_INB   inb_handler;
    PFNVDD_INW   inw_handler;
    PFNVDD_INSB  insb_handler;
    PFNVDD_INSW  insw_handler;
    PFNVDD_OUTB  outb_handler;
    PFNVDD_OUTW  outw_handler;
    PFNVDD_OUTSB outsb_handler;
    PFNVDD_OUTSW outsw_handler;
} VDD_IO_HANDLERS, *PVDD_IO_HANDLERS

VDD_IO_HANDLERS defines an array of handlers for the VDD I/O hooks.

Members

inb_handler
A pointer to the VDD-provided function to simulate INB instruction. This handler is mandatory.
inw_handler
A pointer to the VDD-provided function to simulate INW instruction. This handler is optional; the system will simulate this function by using the provided INB handler.
insb_handler
A pointer to the VDD-provided function to simulate INSB instruction. This handler is optional; the system will simulate this function by using the provided INB handler.
insw_handler
A pointer to the VDD-provided function to simulate INSW instruction. This handler is optional; the system will simulate this function by using the provided INB handler.
outb_handler
A pointer to the VDD-provided function to simulate OUTB instruction. This handler is mandatory.
outw_handler
A pointer to the VDD-provided function to simulate OUTW instruction. This handler is optional; the system will simulate this function by using the provided OUTB handler.
outsb_handler
A pointer to the VDD-provided function to simulate OUTSB instruction. This handler is optional; the system will simulate this function by using the provided OUTB handler.
outsw_handler
A pointer to the VDD-provided function to simulate OUTSW instruction. This handler is optional; the system will simulate this function by using the provided OUTB handler.

Refer also to the following typedef statements.

/** Basic typedefs of VDD IO hooks **/

typedef VOID (*PFNVDD_INB)   (WORD iport,BYTE * data);
typedef VOID (*PFNVDD_INW)   (WORD iport,WORD * data);
typedef VOID (*PFNVDD_INSB)  (WORD iport,BYTE * data,WORD count);
typedef VOID (*PFNVDD_INSW)  (WORD iport,WORD * data,WORD count);
typedef VOID (*PFNVDD_OUTB)  (WORD iport,BYTE data);
typedef VOID (*PFNVDD_OUTW)  (WORD iport,WORD data);
typedef VOID (*PFNVDD_OUTSB) (WORD iport,BYTE * data,WORD count);
typedef VOID (*PFNVDD_OUTSW) (WORD iport,WORD * data,WORD count);


VDD_IO_PORTRANGE

typedef struct _VDD_IO_PORTRANGE {
   WORD   First;
   WORD   Last;
} VDD_IO_PORTRANGE, *PVDD_IO_PORTRANGE;

VDD_IO_PORTRANGE defines the beginning and ending point of the dedicated port memory.

Members

First
The beginning address of the dedicated port memory range.
Last
The ending address of the dedicate port memory range.


Topic Not Found

No matching topic was found. Some possible reasons and remedies are as follows:

  • The file (.chm) that contains the destination topic is not in the expected location.

    • Remedy: Rerun the MSDN Library Setup.

  • The file (.chm) that contains the current topic or destination topic is out-of-date and the link is no longer valid.

    • Remedy: Install the latest version of the MSDN Library.

  • The MSDN Library CD is not in the CD-ROM drive.

    • Remedy: Insert the MSDN Library CD into the CD-ROM drive.