NDIS Concepts

The following article originally appeared in the Winter 1991 Issue of 3TECH, The 3Com Technical Journal.

3TECH is published quarterly by 3Com Corporation, Santa Clara, CA 95052.

Subscriptions to 3TECH are available at a rate of $35 per calendar year. To order subscriptions to 3TECH write to 3TECH Journal, 3Com, P.O. Box 58145, Santa Clara, CA 95052-9953. All orders must be prepaid and reference 3C2869.


 * Note: This file has been modified from the original article to be printable with ascii characters. It uses PC extended ascii graphics characters to make boxes for figures, so the printer should support the PC character set to print these correctly.

NDIS Concepts

The Network Driver Interface Specification (NDIS) is a standardized interface for OS/2 or DOS network platforms. NDIS provides access to the network services at the Data Link layer and is especially useful if the access must be shared. Software developers who need to employ their own network protocol implementations can program to the NDIS interface and utilize NDIS-compliant drivers provided by network hardware vendors. This frees the protocol developer from programming directly to various network interface cards and solves compatibility problems on machines with multiple protocols.

This article explains why the Network Driver Interface Specification was developed, and describes its organization and operation. Some of the information is general, and will be of interest to a broad group of people involved with networks. A good deal of the information is quite detailed and technical. It is intended primarily to give network programmers an overview of NDIS and what is involved in binding a protocol to an vendor- supplied MAC driver for a network adapter board.

The Old Way
Traditionally, network software vendors for the MS-DOS environment have used ad hoc methods to implement the protocols and drivers that link applications to their resident network hardware. The entity that performs these network functions and provides communication between applications is usually referred to as a protocol stack. In the OSI Reference Model, the stack would correspond to the Data Link, Network, Transport, and Session layers, with some stacks possibly including higher layers. At the stack's top end is a user interface or some type of applications programming interface (API) and, at the bottom end, are the interfacing routines that control the network adapter hardware. In implementation, a stack might consist of one system driver, multiple drivers, a program, or a combination of drivers and programs.

Figure 1 shows three alternative stacks that could be used to perform equivalent network functions. In a typical implementation (for example, NetBIOS over XNS, in figure 1) the Data Link, Network, and Transport layers might be implemented as three separate system drivers, and the Session layer as a TSR program. Usually, the interfacing between the layers would be accomplished through a proprietary interface developed by the vendor and the application would communicate to NetBIOS via software interrupts. This works fine in a homogeneous network environment, but as networks grow more complex, it is becoming desirable to have the flexibility to utilize mixtures of different protocols, application interfaces, and network media.

Figure 1. Typical Protocol Implementations Without NDIS OSI           NetBios         Sockets Model           over            over XNS           TCP/IP ┌──────────────┐ ┌─────────────┐ ┌─────────────┐         │ Application  │ │             │ │             │ ├──────────────┤ │ Application │ │ Application │ -─ Application │ Presentation │ │            │ │             │    Software ├──────────────┤ ├─────────────┤ ├─────────────┤         │   Session    │ │   NetBIOS   │ │    BSD      │ -- API │             │ │             │ │   Socket    │ ├──────────────┤ ├─────────────┤ ├─────────────┤         │  Transport   │ │     SPP     │ │    TCP      │ \ ├──────────────┤ ├─────────────┤ ├─────────────┤ ├ Protocol │  Network    │ │     IDP     │ │     IP      │ / ├──────────────┤ ├─────────────┤ ├─────────────┤         │  Data Link   │ │   Driver    │ │   Driver    │ -- Adapter │             │ ├─────────────┤ ├─────────────┤    Driver ├──────────────┤ ├─── H/W  ───┤ ├───  H/W  ───┤ │  Physical   │ │    Media    │ │    Media    │ └──────────────┘ └─────────────┘ └─────────────┘

The Problem to be Solved
Compatibility issues between various networking implementations can make it difficult or impossible to accomplish some seemingly simple tasks. For example, assume that we have an Ethernet network that has two types of file servers attached. One server runs an XNS protocol with NetBIOS at the Session layer. The other server runs a TCP/IP protocol with a Berkeley Socket Session interface.

We would like to write a program to run on a workstation that can copy a file from the first server to the second. In this example, let's say we have two sets of software from two vendors that are designed to communicate with each of the servers, and that the vendors have defined a programmer's interface that should allow us to write a program which talks to the two stacks. Assuming that we have enough memory to load both stacks at one time, we will probably find that our biggest configuration problem occurs at the bottom of the stacks.

At the Data Link layer, each of the vendors has supplied us with a driver for use with their protocol stack that can control the EtherLink II adapter board that we have in our station. Most likely, we will find that each of these drivers expects to have exclusive ownership and control of the EtherLink II. As one of the drivers tries to control the board, it interrupts or corrupts the functions being attempted by the other driver. What is needed is one driver that can control the adapter and be shared by the two protocols.

In May of 1988, 3Com and Microsoft released NDIS, which was jointly developed in conjunction with LAN Manager. The NDIS specification is a standard designed to alleviate compatibility issues for both OS/2 and DOS network platforms. The NDIS specification should be beneficial to both the protocol-level network software developer, who now has a standard interface available, and the user, who gains from the flexibility and interoperability advantages of protocols using NDIS.

NDIS Organization
All network software components compliant with NDIS definitions are drivers. These drivers can be classified into two types: protocol drivers, and Media Access Control (MAC) drivers. NDIS allows protocol drivers to be device drivers, TSR's, or DOS applications, however, for this discussion, we are going to assume that all NDIS drivers are device drivers - the simplest and most common implementation.

The MAC driver forms the bottom layer of the stack and is the driver that directly controls the network hardware. The remaining higher layers of the protocol stack are implemented in one or more protocol drivers.

The MAC layer is a sublayer within the OSI Data Link layer that is defined by IEEE 802 specifications. This layer is the appropriate point for a driver that manages the network hardware and implements the transmission and reception of network data packets. NDIS MAC drivers are provided by 3Com and many other network hardware vendors (see Table 1 for a partial list) and can be used with any vendor's NDIS compliant protocol drivers.

Table 1. Companies Supporting NDIS Companies with NDIS MAC Drivers Note: The entire list of companies shipping NDIS MAC drivers is too long for this article, but includes: 3Com                      Interlan AST Research              Proteon AT&T                      Tiara Compaq                    Ungermann-Bass Exelan                    Western Digital IBM Companies with OS or Application Supporting NDIS 3Com                      - LAN Manager AT&T                      - LAN Manager Banyan                    - Vines (workstation) DEC                       - LAN Works FTP                       - PC/TCP IBM                       - LanServer, OS/2 Extended Microsoft                 - LAN Manager Pacer                     - Pacerlink Sun                       - PC-NFS

NDIS Stacks
All NDIS drivers, both MAC and protocol, share a basic common modular structure. Each driver has an upper and lower boundary. The drivers are linked to form a stack by connecting, or binding, the upper boundary of one driver to the lower boundary of another driver during the binding portion of driver initialization. This binding process can be repeated multiple times, linking several drivers, daisychain fashion, to form the stack. The MAC driver, at the bottom of the stack, always has its lower boundary connected to the physical layer--the network hardware.

The simplest configuration of drivers is one MAC driver supporting one network adapter card, bound to a single protocol driver that spans from the MAC layer to the Session layer (see Figure 2). This forms a single protocol stack of two drivers. Optionally, the protocol part of this stack might be made using two or more protocol drivers to form a single stack of three or more drivers. NDIS also allows us to have two completely parallel stacks in one machine, each with its own adapter card and MAC driver, to implement two different protocols.

Figure 2. NDIS - Single Protocol, Single MAC OSI            IEEE                   Using Reference         Model                  NDIS Model                             ┌─────────────┐ │ ABC Protocol│ ■             ■ ■     LLC     ■        │    Driver   │ │             │ ├─────────────┤  NDIS -ã═════════════Á │             │ │             │  Intfc │    NDIS     │ │ Data Link   │ │     MAC     │        │ MAC Driver  │ │             │ │ 802.3/802.5 │        ├─────────────│          │              │ │             │        │  3Com LAN   │ │             │ │             │        │   Adapter   │ ├──────────────┤ ├─────────────┤       ├─────────────│          │   Physical   │ │   Network   │        │   Network   │ │             │ │   Cabling   │        │   Cabling   │ └──────────────┘ └─────────────┘       └─────────────┘

Figure 3. NDIS - Multiple Protocols, Single MAC OSI            IEEE                   Using Reference         Model                  NDIS Model                         ┌──────────┬──────────┐ │ABC Protoc│XYZ Protoc│ │ Drvr ┌───┴───┐ Drvr │ ■             ■ ■     LLC     ■    │      │ Vector│      │ │             │ ├─────────────┤    È═══Ð══¤═══════¤══Ð═══¥ │             │ │             │     /  │    NDIS     │ │ Data Link   │ │     MAC     │  NDIS  │ MAC Driver  │ │             │ │ 802.3/802.5 │  Intfc ├─────────────│ │             │ │             │        │  3Com LAN   │ │             │ │             │        │   Adapter   │ ├──────────────┤ ├─────────────┤       ├─────────────│          │   Physical   │ │   Network   │        │   Network   │ │             │ │   Cabling   │        │   Cabling   │ └──────────────┘ └─────────────┘       └─────────────┘

More importantly, NDIS lets us have just one adapter card and a single MAC driver with the MAC driver bound to two separate protocol drivers (see Figure 3). Therefore, two protocols (XNS and TCP/IP, for instance) can share the same MAC driver and adapter card. This configuration solves the problem discussed earlier in which files needed to be copied from two servers running different protocols.

To complete the picture, we can also have two adapter cards and MAC drivers, with both the MAC drivers bound to one protocol driver (Figure 4). This configuration could be used to create a network bridge with one protocol connected to two networks.

Figure 4. NDIS - Single Protocol, Multiple MACs OSI            IEEE                   Using Reference         Model                  NDIS Model                       NDIS  ┌─────────────┐ Intfc │ JKL Protocol│ ■             ■ ■     LLC     ■     \  │    Driver   │ │             │ ├─────────────┤ €══════¤══════Ð══════¤══════© │             │ │             │ │    NDIS     │    NDIS     │ │ Data Link   │ │     MAC     │ │ MAC Driver  │ MAC Driver  │ │             │ │ 802.3/802.5 │ ├─────────────┼─────────────┤          │              │ │             │ │  3Com LAN   │  3Com LAN   │ │             │ │             │ │   Adapter   │   Adapter   │ ├──────────────┤ ├─────────────┤ ├─────────────┼─────────────┤         │   Physical   │ │   Network   │ │   Network   │   Network   │ │             │ │   Cabling   │ │   Cabling   │   Cabling   │ └──────────────┘ └─────────────┘ └─────────────┴─────────────┘

Driver Structures
The drivers communicate with each other by a defined set of primitives. The NDIS document has clearly specified a set of primitives for the interface between the MAC driver and protocol driver and for managing the NDIS driver binding process. Although it is possible to implement a protocol stack with multiple protocol drivers, currently no primitives are defined by NDIS for these upper layers. This is not a serious limitation, since a stack with multiple protocol drivers would generally have all of the protocol drivers common to one vendor. There is less need for a standardized interface between protocol drivers than at the MAC layer where sharing resources, and multiple vendors, is more likely.

Each driver contains a series of module characteristic data structures that provide information describing the purpose and capabilities of the driver, and that manage the linkage and operation of the driver during and after initialization.

The main structure is called the Common Characteristics Table and contains the name of the driver and version information. This is the highest level table for a driver; other types of characteristics tables are located from pointers in this table. The Common Characteristics table also contains basic information about what type of binding is supported at the upper and lower boundaries of the driver. The binding information is in the form of a byte identifying the OSI layer that is supported for the boundary. This byte can be examined by other drivers to determine if it is appropriate to bind to the driver.

The common characteristics table contains pointers to the other module characteristics tables - the service specific characteristics table, service specific status table, and upper and lower dispatch tables. These tables give specific information related to the service that the driver performs, manage its operation, and record linkage points to other drivers after the driver is bound.

Managing Binding and Initialization

To form the protocol stacks from the individual drivers we need to get the right drivers hooked together in the desired sequence. This is accomplished in the initialization and binding process. Three components are used to manage and control the process - PROTOCOL.INI (an ASCII configuration parameter file), PROTMAN.DOS or PROTMAN.OS2 (the protocol manager -- a special driver), and NETBIND.EXE (a program that initiates the final driver binding process).

The initialization and binding process is essentially the same whether the operating system is DOS or OS/2. Some minor adjustments need to be made (for instance, selecting either PROTMAN.DOS or PROTMAN.OS2) and some different parameters may be required, but the discussion that follows applies to either environment.

The ASCII file PROTOCOL.INI contains the instructions for assembling the protocol stack or stacks from the NDIS network drivers. It also contains parameters that are needed to configure the individual drivers. At CONFIG.SYS initialization time, the Protocol Manager Driver reads this file. The file is created - much as CONFIG.SYS is created - either directly, by the administrator typing the information with an editor, or by some type of installation program.

The PROTOCOL.INI information is grouped into a number of logical sections of the form: [module name] parameter=value The module name is the name of the NDIS driver, as contained in the common characteristics table for the driver. There will be one module section for each of the NDIS drivers that describes the driver's configuration. Each section can have multiple parameters, but must have at least one, the DRIVERNAME.

Figure 5 shows the contents of a simple PROTOCOL.INI file that has entries for three drivers. The first is Protocol Manager, the special driver that controls the binding process -- more about its purpose later. In PROTOCOL.INI the Protocol Manager entry is currently optional, but it may be required in the future, so it's a good idea to include it. The second module section is for the EtherLink II adapter's MAC driver, and the last section is for an arbitrary protocol driver.

Figure 5. PROTOCOL.INI File Contents ;*************************** ; Example PROTOCOL.INI file ;*************************** [PROTMGR] DRIVERNAME=PROTMAN$ [ETHERLINKII] DRIVERNAME=ELNKII$ INTERRUPT=3 TRANSCEIVER=EXTERNAL [PROTOTST] DRIVERNAME=PROTO$ BUFFSIZE=2048 BINDINGS=ETHERLINKII Notice that, in each section, the first parameter is DRIVERNAME=. This parameter must be included and must specify a name that uniquely defines the NDIS module. In most cases it will be the driver name that the driver registers to the operating system during initialization. The driver determines the name that must be used, because the driver uses the DRIVERNAME entries as a key when searching PROTOCOL.INI data for its relevant module section.

Any number of additional, optional parameter entries can be included in a module section. One purpose of these parameters is to allow control of the driver configuration. A set of valid configuration options will be defined for any particular driver. In the case of the ETHERLINKII section in Figure 5, we have selected two of the possible options for this driver. INTERRUPT=3 tells the driver to use hardware interrupt channel 3 and TRANSCEIVER=EXTERNAL tells the driver to configure the adapter for its external transceiver. In the PROTOTST protocol driver, the BUFFSIZE parameter might direct a protocol to use a particular size for its internal buffers.

The BINDINGS= parameter is a special parameter that is valid only for protocol drivers and specifies the module name of the driver with which the protocol should attempt to bind on its lower boundary. This parameter determines which drivers will be bound together to form the stack or stacks. In the PROTOCOL.INI of Figure 5, PROTOTST is bound to the ETHERLINKII driver.

As mentioned earlier, the component of the NDIS environment that manages the binding process is the Protocol Manager, which has the file name PROTMAN.DOS for DOS or PROTMAN.OS2 for OS/2. Protocol Manager has two main functions: it keeps and manages common data for the NDIS drivers, and it controls the binding sequence. Functions of the Protocol Manager are needed by the NDIS drivers during their system level initialization, so the Protocol Manager driver must be loaded in CONFIG.SYS before any of the other NDIS drivers.

The Protocol Manager driver was written by 3Com and is available from both 3Com and Microsoft. Any vendor needing Protocol Manager or NETBIND.EXE for use in the initialization of their network software products can obtain them and include them with their products. Protocol Manager and NETBIND.EXE are also a standard part of LAN Manager as shipped by 3Com and Microsoft.

Driver Initialization

The Protocol Manager gathers NDIS related information during the system CONFIG.SYS initialization of the drivers. During its initialization, the Protocol Manager driver reads the PROTOCOL.INI file and parses the information into a set of structures, called the Configuration Memory Image, that are accessible by the other NDIS drivers. Because the other NDIS drivers use this information, the Protocol Manager must be the first NDIS driver to initialize.

As CONFIG.SYS processing continues, the other drivers are directed to initialize by the operating system. During initialization they must open the Protocol Manager device (PROTMAN$) and then issue a GetProtocolManagerInfo primitive to obtain, from the Protocol Manager, a pointer to the Configuration Memory Image (the PROTOCOL.INI data). The drivers find the section of this data that pertains to them and use any parameters found there to adjust their initialization process. One result of this is that drivers may modify their loaded size, based on parameter requirements, to optimize host memory consumption. If the driver is a protocol and it finds a BINDINGS= parameter, it will assess whether or not this binding is valid. Finally, the driver must issue a RegisterModule primitive to the protocol manager to register itself. During this register, the driver passes a pointer to its common characteristics table, and for a protocol driver, a list of modules to which it wants to bind, based on the BINDINGS= parameter.

After CONFIG.SYS processing completes, the Protocol Manager has a list of the active NDIS drivers, their characteristics, including entry points, and the desired bindings.

Driver Binding
The actual binding of NDIS drivers starts when some program issues the BindAndStart primitive call to the PROTMAN$ device. For all current Microsoft OS implementations this call will come from the execution of NETBIND.EXE within a BAT or CMD file.

After receiving the BindAndStart directive, Protocol Manager will take the binding information from the module registrations and build a binding hierarchy tree. Starting at the bottom of this tree (the MAC end) the Protocol Manager works up the tree and issues an InitiateBind primitive to each protocol module that needs a driver bound on its lower boundary. As part of the InitiateBind call, the driver is passed a pointer to the common characteristics table of the module to be bound. The protocol driver that was instructed to initiate the bind will then issue a Bind primitive directly to the driver that it wishes to bind. When the bind completes, each driver will have a pointer to the common characteristics of the other, and therefore its entry points.

After the Protocol Manager has processed all of the binding tree, all the appropriate network drivers will be bound to each other. The protocol stack is then fully operational and the drivers can access each other by calling the dispatch entry points for communication. Figure 6, Driver Initialization and Binding, summarizes the binding process.

Figure 6. Initialization and Binding Process A. CONFIG.SYS initialization begins. 1. Protocol Manager driver does its initialization. a. Protocol Manager reads the PROTOCOL.INI file and builds the Configuration Memory Image. 2. Other NDIS drivers do their initialization. a. Open the PROTMAN$ device. b. Issue GetProtocolManagerInfo to gain access to                        ProtMan configuration image. c. Read config parameters from the image and use them to complete initialization. d. Issue RegisterModule to register Characteristics info with Protocol Manager. B. CONFIG.SYS processing ends. C. Binding process starts when the NETBIND.EXE program opens the PROTMAN$ device and issues a BindAndStart to Protocol Manager. 1. Protocol Manager builds a binding tree from RegisterModule info. 2. Protocol Manager starts at the bottom and calls drivers with InitiateBind. a. Each called driver issues Bind to the specified module to complete binding. D. When all modules are bound, Protocol Manager returns from BindAndStart.

One more factor is involved in the binding process if more than one protocol is to be bound to a MAC driver. MAC drivers can only have one binding at their upper boundary. To link one MAC to multiple protocols, the Protocol Manager inserts a component, called Vector, between the MAC and the protocols (see Figure 3). Vector is part of the Protocol Manager and will be bound between the MAC and each of the protocols. To do this, the Protocol Manager first binds the MAC driver to Vector by issuing a Bind call to the MAC driver, then it issues an InitiateBind call to each of the protocols directing them to bind to a Vector entry rather than the MAC entry.

The basic function of Vector is to route incoming packets between the protocols. When a packet is received by a MAC driver, it will issue a notification of the event to its upper boundary. When vector is involved, it will pass this notification, first to one, then to the other protocol, until one protocol accepts the packet or all have rejected it. Other functions can be passed, essentially directly, between protocols and the MAC, but this vectoring of incoming packets is key to implementing multiple protocols on one MAC.

MAC to Protocol Interface and Operation

The main purpose of the NDIS interface is to let the bound drivers communicate with each other. To that end, the NDIS is largely concerned with defining a set of functions that dictate how the MAC driver will communicate with the protocol bound on its upper layer. Table 2, NDIS Primitives, lists the primitives that are defined for this MAC-to-Protocol communication. All communication between the MAC and its bound protocol will be accomplished using these primitives.

In Table 2, the individual primitives are grouped into the main functional categories that they perform. In the first group there are functions for the transmission of network packets from the protocol through the MAC and onto the network, and for the reception of packets in the reverse direction. In the control group are all the functions that the protocol uses to control or modify the operation of the adapter and MAC driver. The asynchronous status group contains functions that the MAC uses to report events to the protocol. Finally, the Binding group has the functions used to accomplish the driver binding process. The most important of these have already been described. The remainder are extensions to allow binding and unbinding of dynamically loadable protocols. More on this subject later.

Table 2. NDIS PRIMITIVES TRANSMIT AND RECEIVE TransmitChain              v  Initiate transmission of a frame TransmitConfirm            ^  Imply completion of frame transmit ReceiveLookahead           ^  Indicate arrival of received frame and offer lookahead data TransferData               v  Request transfer of received frame from MAC to protocol IndicationComplete         ^  Allow protocol to do post-processing on indication ReceiveChain               ^  Indicate reception of a frame in MAC managed buffers ReceiveRelease             v  Return frame buffer to the MAC that owns it CONTROL IndicationOff              v  Disable indications from the MAC IndicationOn               v  Enable indications from the MAC InitiateDiagnostics        v  Start MAC runtime diagnostics ReadErrorLog               v  Get error log info from MAC SetStationAddress          v  Set network address of the station OpenAdapter                v  Issue open request to network adapter CloseAdapter               v  Issue close request to network adapter ResetMAC                   v  Reset MAC software and adapter hardware SetPacketFilter            v  Specify filtering params for received packets AddMulticastAddress        v  Specify multicast address for adapter DeleteMulticastAddress     v  Remove previously added multicast address UpdateStatistics           v  Cause MAC to update statistics counters ClearStatistics            v  Cause MAC to clear statistics counters InterruptRequest           v  Protocol requests later async indication from MAC SetFunctionalAddress       v  Cause adapter to change its functional address SetLookahead               v  Set length of visible data for ReceiveLookahead GeneralRequestConfirmation ^  Confirm completion of previous General Request (see text for                                more explanation) ASYNCHRONOUS STATUS RingStatus                 ^  Indicate a change in ring status AdapterCheck               ^  Indicate error from adapter StartReset                 ^  Indicate adapter has started a reset EndReset                   ^  Indicate adapter has completed reset InterruptIndication        ^  MAC response due to InterruptRequest BINDING InitiateBind              p>m  Instruct a module to bind to another module Bind                      m>m  Exchange Characteristic Table info with another module InitiatePrebind           p>m  In OS/2 dynamic bind mode, instruct a module to restart its prebind initialization InitiateUnbind            p>m  Instruct a module to unbind from another module Unbind                    m>m  Delete linkage info with another module GetProtocolManagerInfo    m>p  Retrieve pointer to Configuration Image RegisterModule            m>p  Register Characteristics and Bindlist with Protocol Manager BindAndStart              e>p  Initiate the binding process GetProtocolManagerLinkage m>p  Get entry point for Protocol Manager GetProtocolIniPath        d>p  Get file path for the PROTOCOL.INI file RegisterProtocolManagerInfo d>p Dynamic mode, register new Configuration Image InitAndRegister           d>p  Dynamic mode OS/2, restart prebind initialization UnbindAndStop             d>p  Dynamic mode, Unbind and terminate a module BindStatus                e>p  Retrieve info on current bindings RegisterStatus            e>p  Query if a specific module is registered KEY: ^   - MAC to protocol v   - protocol to MAC p>m  - Protocol Manager to driver module m>p  - driver module to Protocol Manager e>p  - ? to Protocol Manager; ? is normally an executable program d>p  - dynamic protocol or control program to Protocol Manager The center column of the primitive table has a symbol that indicates the direction in which the primitive calls are passed. To submit a primitive, the caller driver pushes a series of parameters on the system stack and calls an entry point in the called driver. The entry points are known to the calling driver as a result of the binding process. The common characteristics table, whose address was passed during binding, has dispatch tables chained off of it. The defined entry points for the driver are in a dispatch table.

The MAC driver's upper dispatch table and the addresses it contains are shown below: MAC Upper Dispatch Table GeneralRequest TransmitChain TransferData ReceiveRelease IndicationOn IndicationOff These are the entry points that the protocol driver will call to request primitive execution by the MAC. All of these, except GeneralRequest, are called direct primitives because they serve just the one primitive function implied by the entry name. The GeneralRequest entry serves the remainder of the primitive calls, other than binding, that are passed to the MAC. In Table 2, the GeneralRequest primitives are all the primitives in the group labeled CONTROL, except IndicationOn and IndicationOff, which have their own direct entries.

The Direct Primitives have their own entry points because these are performance-critical functions. The primitives employing the GeneralRequest entry, being less critical, can share a common entry. The GeneralRequests identify themselves by passing an opcode as one of their parameters.

On the protocol driver side, the protocol lower dispatch table defines the entry points from the MAC to the protocol. The table and its contents are as follows: Protocol Lower Dispatch Table GeneralRequestConfirm TransmitConfirm ReceiveLookahead* IndicationComplete ReceiveChain* Status* * denotes Indications All of these entries except Status are direct. Status is the entry for the asynchronous status group in the primitive table, and these primitive calls also have an opcode that identifies the type so they can share one entry. In a sense, GeneralRequestConfirm can also be viewed as a shared entry.

There is only one primitive for this entry, but it is generated by the MAC at the end of any of the GeneralRequests to the MAC and contains the request handle of the original primitive request to the MAC. Therefore, it is shared functionally in response to all of the GeneralRequest primitives.

Some of the entries in this list are marked with an asterisk to show that they are Indications. Indications are a special class of requests that imply some special handling. In implementation, they are usually associated with notifications to the protocol that are made from the MAC while it is in interrupt context. Because of this, the protocol is required to handle Indications as efficiently as possible. (For example, it might put the received frame on a queue.) After the protocol processes the indication, it returns control to the caller, the MAC. The MAC will enable as much interrupt processing as possible and then call IndicationComplete to give the protocol an opportunity to perform more processing on the Indication in a less critical mode (e.g., to decode the previously queued receive frame).

Transmit and Receive

Information in Table 2 identifies the basic purpose of each NDIS Primitive. Of these, transmit and receive are the key functions at the MAC-protocol interface level, so let's examine the primitives serving these functions in a little more detail.

Network packets, or frames, are the data units that are transferred between the MAC and the protocol by the transmit and receive primitives. These packets include all the information, other than hardware related functions such as preamble and checksum, that will be sent out on the network medium. As a result, the protocol, on transmit, must build the entire packet, including data link fields such as source and destination addresses. Likewise, on receive, the protocol must process the packet down to these levels.

The passing of packet data across the interface between the protocol and MAC is accomplished, whenever possible, by exchanging pointers to buffers or to a descriptor that, in turn, points to several data buffers. The objective is to avoid unnecessary, time-consuming copying of data between buffers.

In transmit, the packet data buffers are owned by the host system and managed by the protocol driver. NDIS defines a structure, the transmit data buffer descriptor, that allows the packet data to be contained either in one buffer, or in a series of chained buffers. To initiate a transmit, the protocol assembles the packet data into buffers, puts the buffer addresses in the buffer descriptor structure, and calls the MAC with the TransmitChain primitive. The primitive contains a pointer to the buffer descriptor.

The MAC has two options for processing the transmit. The MAC will choose one or the other at its own discretion; the protocol must be capable of handling either. In the first, called synchronous transmission, the MAC copies all the packet data and returns to the protocol with a code signifying that the data buffers are free and the transmit is complete. Optionally, the MAC can return with a code signifying that the transmit is queued this is called asynchronous transmission). It implies that the buffers are not free and the transmit has not yet completed. Later, after the MAC has copied all the transmit data, it will call the protocol with a TransmitConfirm primitive (the asynchronous response) to inform the protocol that the buffers are now free and the transmit is complete.

An additional feature available in transmit is immediate data. The protocol has the option of beginning the transmit buffers with up to 64 bytes of immediate data. This immediate data, if present, is always the first data to be transmitted. The MAC must fully process or copy this data before returning from the TransmitChain call, even if it will asynchronously process any remaining data buffers for the call. This feature allows the protocol to have a small locally managed buffer that only needs to be valid during the TransmitChain primitive call. A protocol might use this for building packet header information, or for entire small protocol-generated packets, such as acknowledgements.

For receiving packets, the process can work in one of two ways: using ReceiveLookahead and TransferData primitives, or using the ReceiveChain primitive. The method that is used is determined by the MAC, depending on how the MAC and adapter can handle data buffering. It is generally a function of whether the adapter has on-board receive buffers that use I/O or DMA to transfer the data, or whether the adapter buffers are memory-mapped and accessible directly by the host.

For buffers on the adapter that use programmed I/O or DMA to transfer the data, the reception process will use a ReceiveLookahead and TransferData pair of primitives. When the MAC has received a packet that it wants to present to the protocol, it indicates this by calling the ReceiveLookahead primitive of the protocol.

The ReceiveLookahead call contains a pointer to a short portion of the data at the beginning of the packet. This usually means that the MAC must have first DMA'ed this lookahead data to a buffer in the host.

At this point, the protocol driver can examine the lookahead data to determine if it wants the packet. In some cases, the packet may not be of interest to the protocol. If the packet is not needed, the protocol can return to the MAC indicating a reject and that the receive is complete. If the packet is needed, the protocol calls the TransferData primitive of the MAC which results in the MAC transferring the remainder of the data to a protocol buffer.

The purpose of the ReceiveLookahead implementation is to avoid unnecessary data transfers between the MAC and the protocol. This technique improves the efficiency of the network stack.

If the adapter has receive buffers that are accessible as host memory, receive will be implemented with the ReceiveChain primitive. For this type of buffering, the MAC will own and manage the receive buffers. For flexibility, this mode has a ReceiveChain buffer descriptor structure, similar to the transmit structure, that lets multiple separate buffers be joined for one packet transfer. When the MAC has a received packet to present to the protocol, it builds a buffer descriptor for the packet and calls the protocol with ReceiveChain.

When the protocol gets the ReceiveChain Indication, it has two options. In the simplest case, the protocol can copy all of the packet data and return to the MAC specifying that the receive is complete and the buffers are free. In the other case, the protocol can defer copying the buffers and return to the MAC specifying that the buffers are still in use. The protocol will later complete the copying of the buffers and then call the MAC with a ReceiveRelease primitive to indicate that the buffers are now free and the receive is done.

In all of these receive scenarios, the primitive calls issued to the protocol are indications. This means that the protocol drivers need to observe certain rules and that the MAC must issue an IndicationComplete call to the protocol as part of the process. See the NDIS document for more information about these indication issues.

Dynamic Binding

As mentioned earlier, some primitives for binding which haven't been discussed are provided to support dynamic binding. Dynamic binding is a new concept that has been added in Version 2.0.1 of the NDIS document. Dynamic binding allows a protocol to be added to, or removed from an existing network configuration after the initialization process has completed. The dynamic protocol driver must be written for this purpose and will normally be implemented as a TSR or transient program module. See the NDIS document for a full explanation of dynamic binding and unbinding.

The main advantage of dynamic binding is freeing system memory until it is actually needed for a particular protocol. This is most useful in the DOS environment where there is a 640K memory limit and it is difficult to have multiple protocol stacks loaded simultaneously.

DOS Versus OS/2

NDIS has all the features needed to allow writing network drivers that will run in either the DOS or the OS/2 environment. A driver can only be used with one of these operating systems (the very same driver can't be used for both), but the structure of the driver can be identical for both environments. We have found that one set of source code can be used to make versions for both DOS and OS/2. Where different techniques are required, a small piece of code can be selected via conditional compile or assembly statements for the two versions. An example of such a difference, is that a certain call might need to use Interrupt 21h in a DOS-based driver, versus an IOCTL call for OS/2. A conditional selection of a few lines of code can implement one or the other. The make process for the driver will select the correct environment.

Network device drivers are not very different in structure from other types of device drivers. The device driver must be written to conform to the architecture in which it will run - DOS or OS/2. All of the normal issues apply in writing NDIS drivers for both of these environments. Several books and articles are available that explain general driver development issues. See the list of references at the end of this article for some titles.

Summary
One of the main goals of the Network Driver Interface Specification is to save network software developers from reinventing the wheel for each new version of network adapter hardware. A protocol that is written with an NDIS interface at its base should be able to function, unchanged, with many different types of adapter hardware. In addition, the manufacturers of the network hardware should be in the best position to write efficient and bug-free MAC drivers for their own boards. 3Com and many other vendors (see Table 1) have NDIS MAC drivers available to support their network hardware.

Using the standardized NDIS interface also allows a new level of sharing of network resources in a machine. Multiple protocols and multiple hardware adapters can coexist - even those from different vendors. Input was sought from many leaders in the network industry to guarantee that the specification is flexible enough to meet most networking needs, however, 3Com carefully defined the functions so that performance would not be sacrificed for this flexibility.

If you are a network software developer, I hope this article has provided enough information for you to evaluate whether NDIS can help in your situation. For the complete description, the NDIS document can be obtained from 3Com at the following address: 3Com - Network Adapter Division Software Product Marketing 5400 Bayfront Plaza P.O. Box 58145 Santa Clara, Ca. 95052-8145

Author Bio

 * Rex Allers is a Systems Engineer in the Technical Services Operation at 3Com, specializing in support for developers. Rex has worked in engineering and support in the computer industry for longer than he cares to admit, and has been at 3Com since 1986.