NDIS Driver Developer's Tool Kit for OS/2 and DOS - Programmer's Guide

From EDM2
Jump to: navigation, search



How to Use This Manual

How to Use This Manual


The principal audience for this manual is developers of NDIS Media Access Control (MAC) drivers for both DOS and OS/2. It is assumed that the reader is an experienced system programmer, familiar with the operating system, x86 programming, and system internals. Experience with networking and/or device drivers is highly recommended. Familiarity with NDIS, while helpful, is not assumed.


This manual contains guidelines for writing an NDIS MAC driver. It is organized into four chapters. Chapter 1, Overview, contains general background information on device drivers, NDIS, and MAC drivers. Chapter 2, OS/2 Device Drivers, contains OS/2 specific information. Chapter 3, DOS Device Drivers, contains DOS specific information, from the point of view of the differences between DOS and OS/2 drivers. Finally Chapter 4, Writing a Media Access Control Driver, gives guidelines and examples useful for writing your MAC driver.


The intended result of this manual is to aid your driver development.

Notational Conventions

The following notational conventions are used in this manual

  • Hexadecimal (base 16) numbers are given in the form 0xdd, 0xdddd, etc.
    For example, 0x28 is hexadecimal for the decimal number 40.
  • File names are given in all capital letters. Hence CONFIG.SYS stands for the file named CONFIG.SYS.
  • Code examples are given in Courier type face.


NDIS details an internal architecture for network drivers. Drivers are divided into two categories: Media Access Control (MAC) drivers and Protocol drivers. MAC drivers directly control network adapter cards, while protocol drivers interface with the operating system and/or applications. The NDIS details the responsibilities of both types of drivers as well as the interface between them. For more information on NDIS, see the publications: 3Com/Microsoft LAN Manager Network Driver Interface Specification 2.0.1 and NDIS Implementation Information for IBM LAN Systems, NDIS Extensions.

Finding Further Information

Further information can be found in the following document:

If you already have the LAN Technical Reference, you need to order the following document:

Note: Once the Supplement is available, it will ship with orders of the LAN Technical Reference.

Chapter 1. Overview

OS/2 and DOS use specialized software, known as device drivers, to manage input and output. Examples are screen display, keyboard or mouse input, printer output, floppy or hard drive file input/output, and network adapter input/output. Each device driver is responsible for the interface between the device (that is, its controller) and the operating system.

Types of Device Drivers

Each device driver falls into one of two distinct categories, character drivers or block drivers, depending upon its device. Block drivers manage random access devices, such as disc drives, that support logical units and a directory structure. Character drivers manage all other devices, including network adapter cards. We will be concerned with character drivers.

Device drivers are further authorized into base drivers and installable drivers. Base drivers handle the basic system input/output and are usually considered part of the operating system. We will not be concerned with base drivers. Installable drivers handle all other input/output functions.

Installable Device Drivers

Installable device drivers are loaded at system initiation time. The driver initialization thread reads the CONFIG.SYS file and loads and initializes the drivers specified in the "DEVICE=" statements on a first-read-first-loaded basis.

Drivers are loaded into low memory and must consist of a data segment followed by a code segment. Additional data or code segments, if present, will be loaded into high memory.

Parts of a Device Driver

Device drivers consist of from two to five components as follows:

  • Device Driver Header (required). The first item in the driver's initial data segment is the device driver header. The header specifies device attributes and driver entry points.
  • Strategy Routine (required). System communication with device drivers is accomplished by calling the driver's strategy routine. System requests include initialization and, for most device drivers, input/output requests.
  • (Hardware) Interrupt Routine (recommended). OS/2 device drivers usually service hardware requests by means of hardware interrupts.
  • Software Interrupt Handler (optional). DOS device drivers usually service hardware requests by means of software interrupts. OS/2 drivers may contain a software interrupt handle for backwards compatibility with DOS.
  • Timer Handler (optional). OS/2 device drivers may specify a timer entry point which will be called by the system every 31.25 milliseconds.

Real Versus Protected Mode

OS/2 can emulate DOS, running DOS applications in the "DOS Compatibility Box". While in this mode, the processor emulates the behavior of the 8088 and 8086 chips by running in real mode. Otherwise the processor runs in protected mode.

While in real mode, the processor interprets the CS, DS, ES, and SS registers as segment registers, in protected mode they are selector registers. That is to say, in real mode 20-bit physical addresses are split into an high-order, 16-bit segment and a low-order, 4-bit offset. While in protected mode, address are virtual and consist of a 16-bit selector (whose three low order bits are ignored) and a 16-bit offset. The selector references a position in the appropriate (local or global) descriptor table (LDT or GDT).

Privilege Levels

While OS/2 is in protected mode, processes (and their threads) have a privilege level or ring number. The privilege level of a process determines its access to descriptor tables. Applications run at Ring 3, and can only access their own local descriptor table. The operating system runs at Ring 0, or kernel mode and can access both local and global descriptor tables.

Device drivers, however, initialize at Ring 3 (as do most applications) and run at Ring 0, to have greater access to the kernel and to hardware.

Introduction to NDIS

Network device drivers were traditionally large, monolithic drivers, containing code for protocols (the language of the network) as well as network hardware. Traditional drivers were difficult to write, had less flexibility, were harder to upgrade, and didn't support multiple protocols. A fully functional monolithic network driver would have to have the ability to interface with every protocol available.

A modular approach to protocol handling (in which only those protocols required are loaded) is a much more efficient use of system memory. The Network Device Interface Specification (NDIS) was developed to eliminate the need for multiple protocols within a single driver. Under NDIS, protocol handling is completely separated from hardware manipulation. NDIS replaces the traditional network driver with two distinct types of installable character drivers: protocol drivers and Media Access Control (MAC) drivers.

NDIS protocol drivers do not manipulate network hardware - they handle the various network data formats. MAC drivers handle the hardware. Working together, these two driver types replace the large monolithic network driver. As a result, protocol and hardware handling drivers are modular and streamlined. Protocol and MAC drivers, as a group, are called NDIS device drivers

The NDIS describes the functionality that both protocol and MAC drivers must provide for each other. The specification also describes a standard network driver interface for the two types (MAC and protocol) of NDIS device drivers

The interface includes a Protocol Manager to help organize configuration data and to bind the various modules together.

NDIS device drivers may choose to run under both OS/2 and DOS. NDIS drivers provide access to many kinds of complex protocols under multiple hardware configurations.

NDIS and the OSI Reference Model

The International Standards Organization, in an effort to standardize networks created the Open Systems Interconnection (OSI) reference model. In the OSI model, network software is thought of as a system of layers with each layer representing a particular task. The OSI model defines seven layers of networking tasks as follows


The lower portion of an NDIS protocol driver functions in the upper portion of the data link layer, above the network device driver interface. The upper portion of an NDIS protocol driver may reside anywhere from the data link to the application layer of the OSI model.

The device driver interface described in the NDIS specification functions within the data link layer of the OSI reference model.

NDIS MAC drivers are implemented at the bottom portion of the data link layer. MAC drivers may work with any physical media: PC Network, Token-Ring, ArcNet, etc. IBM protocol drivers support 802.3 or DIX (EtherNet) and 802.5 (Token-Ring) networks.

The Protocol Manager

The network device driver interface is implemented through a Protocol Manager used to coordinate connections (bindings) between modules. When an NDIS protocol or MAC driver loads and initializes, it passes to the Protocol Manager a pointer to an internal characteristics table that includes the module's name and entry points. Protocol drivers also pass a list of MAC drivers with which they wish to bind. The Protocol Manager reads the protocol driver's bindings list and helps the protocol driver to exchange information with each MAC driver in the list. The information exchanged consists of pointers to each other's characteristics tables. The exchanged entry point information allows protocol and MAC drivers to exchange characteristics tables and communicate directly with one another.


While a protocol driver may bind to multiple MAC drivers, each MAC driver must bind to one, and only one, protocol driver. To allow a MAC driver to bind to multiple protocol drivers or to allow a protocol driver to be dynamically loaded and unloaded, the Protocol Manager provides an additional module known as VECTOR. The VECTOR module shields a MAC driver from these details, allowing the MAC driver to be written as if there were always a single protocol driver bound to it.

Media Access Control Drivers

MAC drivers provide low-level access to network adapters. MAC drivers are loaded at system initialization time and are static - that is, they may not be removed from memory. The primary function of a MAC driver is to transmit and receive packets. Secondarily, the MAC driver also provides some basic adapter management functions.

Parts of a MAC Device Driver

Like other device drivers. an NDIS MAC driver contains the following components:

  • Header
  • Strategy Routine
    • Initialization Routine
  • Interrupt Routine

Additionally, an NDIS MAC driver must contain entry points for the following seven, NDIS-specific, entry points:

  • System Request Routine
  • General Request Routine
  • TransmitChain Routine
  • TransferData Routine
  • ReceiveRelease Routine
  • IndicationOff Routine
  • IndicationOn Routine

Finally, an NDIS MAC driver must contain the following data structures:

  • Common Characteristics Table
  • Service-Specific Characteristics Table
  • Service-Specific Status Table
  • Upper Dispatch Table

How a MAC Device Driver Works

The sole purpose of the Strategy Routine in an NDIS MAC driver is to handle load-time initialization of the driver. Later the Protocol Manager directs NDIS drivers to exchange common characteristics table addresses with the drivers they bind to. Chained off of the common characteristics table are the other tables as well as entry points to their NDIS-specific routines.

A MAC driver's Interrupt Routine handles notifications from the hardware to the MAC. These notifications will cover such matters as changes in adapter status, reception of frames, and the completion of previous transmissions.

The System Request Routine handles such matters as binding (during which characteristics tables are exchanged) and unbinding. The General Request Routine handles protocol requests of the MAC. These requests include opening and closing the adapter, setting the station address, updating statistics, and many others. Both system and general requests utilize a request packet and return a completion code in a manner similar to that of an OS/2 driver's Strategy Routine.

The TransmitChain, TransferData, and ReceiveRelease routines are used during the transmission and reception of frames in order to pass data buffers between a protocol and a MAC driver.

The IndicationOff and IndicationOn routines are used to control (turn on and off) status and reception indications. Indications are notifications from a MAC driver to a protocol driver of a change in adapter status or of a reception of a frame. A protocol driver reacts to an indication in a manner similar to a MAC driver reacting to an interrupt.

Chapter 2. OS/2 Device Drivers

As mentioned in the first chapter, OS/2 device drivers contain a device driver header and a strategy routine. As these components are common to all device drivers, they will be discussed here. For more detailed information on OS/2 device drivers, see the following publications from the OS/2 2.0 Technical Library: Control Program Programming Reference (S10G-6263-0), and Physical Device Driver Reference (S10G-6266-0). In this section, we provide a summary of that information.

The Device Driver Header

The first item in the driver's initial data segment is the device driver header. This twenty-six byte data structure is organized as follows:

Offset Size (bytes} Description
0x00 4 Pointer to next header
0x04 2 Device Attribute Field
0x06 2 Offset to Strategy Routine
0x08 2 Offset to IDC Routine
0x0A 8 Name
0x12 8 Reserved

In order to support multiple devices within a single driver, the device driver header is a linked list. This list is terminated by a pointer of - 1 (0xFFFFFFFF), so if your driver supports only a single device, you should fill this field with -1.

The Device Attribute Field contains descriptive information used by the system. Its structure is as follows:

Bit Description
0 Keyboard bit: 1 = device is the standard input device, 0 = otherwise
1 Screen bit: 1 = device is the standard output device, 0 = otherwise
2 NULL attribute bit: 1 = device is the NULL device, 0 = otherwise
3 Clock device bit: 1 = device is the system clock device, 0 = otherwise
9-7 Function Level: 001 = 0S/2 device driver (hence will be initialized in protect mode), 000 = compatibility box driver (hence will be initialized in real mode)
11 Open/close bit: 1 = driver requires an OPEN and CLOSE call from the system, 0 = otherwise
12 Shared bit: 1 = system file sharing rules apply, 0 = file ownership contentions will be resolved by the driver
13 IBM bit: set to zero for character devices (for block devices: 1 = supports DOS 2.0 and above file formats, 0 = supports DOS 1.0 or 1.10 formats)
14 Inter-driver communication (IDC) bit: 1 = driver has an IDC entry, 0 = otherwise
15 Device type bit: 1 = character device, 0 = block device

The other bits are reserved, and should be set to zero.

Following the Device Attribute Field offsets into the main code segment to the entry points of the Strategy Routine and the Inter Device Communication Routine.

Character devices then have an eight byte, ASCII, blank-filled name of the device driver. This name must match the "DriverName=" keyword given in the PROTOCOL.INI file. As a device name takes precedence over a file name in a DosOpen function call, device names should contain some unusual character. A terminating "$" is traditional.

The final eight bytes of the header are reserved and should be zero filled.

The Strategy Routine

The Strategy Routine handles the system's requests of the device driver. For an OS/2 device driver, these requests include installation, normal I/O, character device-specific I/O, block device-specific I/O, and generic I/O control.

The Strategy Routine follows the far call/return mode and can be called in either real or protect mode. At entry, a pointer to a Request Packet is contained in the ES:BX register pair. This pointer is bi-modal and references the same address whether the processor is in real or in protect mode.

The Strategy Routine reads the Request Packet and takes the appropriate action. Upon completion of the request, a status field is set in the Request Packet to indicate the completion status of the request.

The Request Packet

The Request Packet consists of a thirteen byte header followed by a command specific data area. The structure of the request packet header is as follows:

Offset Size (bytes) Description
0x00 1 Total length of Request Packet
0x01 1 Unit code (block device)
0x02 1 Command code
0x03 2 Status field
0x05 4 Reserved
0x09 4 Queue linkage field

The first byte gives the total length of the packet, including the header and command-specific data area. The next byte is ignored for character devices. The third byte gives the command code, indicating which function (for example, 0 = driver initialization) has been requested of the driver.

The next two bytes contain the status field. This field should be set by the driver to indicate the request completion status. Errors are indicated by setting the high order bit of this field. In this case, an error code should be returned in the low order eight bits.

The next four bytes are reserved, and the final four bytes of the header can be used to maintain a linked list of queued request packets. Either DevHlp services or the driver's own queue management may be used.

The remainder of the request packet contains command-specific data. The type and amount of command-specific data varies with each command.

The Initialization Routine


Every device driver's Strategy Routine must accept the INIT initialization request (request code = 0). The driver's code which processes this request is known as the driver's Initialization Routine. The INIT request is generated by the system during driver loading and initialization. At this time, the driver initialization thread reads the CONFIG.SYS file and interprets the "DEVICE=" command lines.

For each driver referenced, the system loads the first two segments into low memory (and any remaining segments into high memory), resolves intersegment memory references, and interprets the device driver header. Then if the device driver is a DOS-compatible driver (function level = 000) that may only run in the compatibility box, the system switches the processor to real mode, otherwise it remains in protect mode. Finally the driver initialization thread formats an INIT request packet and makes a far call to the driver's Strategy Routine to allow the driver to initialize itself.

The INIT Request Packet

The INIT request packet is twenty-three (0x17) bytes long. It consists of the standard thirteen byte header, followed by ten bytes of INIT-specific data in the following format:

Offset Size (bytes) Description
0x0D 1 Data_1
0x0E 4 Pointer_1
0x12 4 Pointer_2
0x16 1 Data_2

On entry, Data_1 is unused. Pointer_1 contains a bi-modal pointer to the device helper (DevHlp) routine. Pointer_2 contains a far pointer to the device driver parameter string as defined in the "DEVICE=" command in the CONFIG.SYS file. This string consists of the driver file name, followed by blank-separated arguments and is null-terminated. Data_2 is set, for block drivers only, to the drive number of the next logical unit the file manager expects to install. Data_2 is unused for character drivers.

On exit, Data_1 will be set, by block drivers only, to the number of logical units supported by the device. Character drivers should set Data_1 to zero. Pointer_1 should be set to the lengths of the code and data segments after initialization time, with the high-order word giving the code segment length and the low-order word the data segment. This allows the system to free memory used only by the initialization routine. Pointer_2 will be set, by block drivers only, to a far pointer top the BIOS parameter block array. Data_2 is not used on exit.

Finally, on exit the driver should set the status field in the Request Packet header. Successful initialization is indicated by a status value of 0x0100 (only bit 8 set, indicating request done). General Failure is signaled by a status value of 0x810C (bit 15 set, indicating error, bit 8 set, indicating request done, and error code 0x0C, indicating General Failure).

OS/2 Functions Available at Initialization

There are over 200 Application Program Interface (API) function calls available under OS/2. These functions perform I/O, control processes, manage interprocess communication, and perform many other tasks. A subset of these API functions are available to a device driver at initialization time.

An OS/2 device driver's initialization routine is called at Ring 3 in protect mode, and a DOS compatibility box driver's initialization routine is called in real mode. Hence neither process management nor memory management API calls are permitted. However, at a device driver's initialization time, the system base drivers have loaded. Hence the following API calls are available at initialization time:

Randomly reposition file pointer
Close a file
Perform generic I/O control functions
Open a file
Read from a file
Write to a file
Directory Management
Delete a file
Terminate a pattern matching search
Look for a file, using pattern matching
Look for next file matching pattern
File System Information
Get system configuration information
Get current working directory
Get current working disk
Get file status
Get file attribute bits
System Message Handling
Retrieve message from system message file
Write message to system message file
Base-Line I/O
Produce system audio alert

DevHlp Service

As device drivers run at Ring 0, kernel mode, API calls are only available during initialization time. Hence an interface to the OS/2 operating services is provided to device drivers through the DevHlp Interface. A pointer to the entry point for DevHlp calls is passed to the driver with the INIT request packet.

There are a total of fifty-seven DevHlp services. The following subsets of these are available at driver initialization time:

  • System Clock Management: SchedClockAddr
  • Character Que Management: QueueInit
  • Memory Management: AllocGDTSelector, AllocPhys, FreePhys, Lock, PhysToGDTSelector, PhysToVirt, Unlock, UnPhysToVirt, VirtToPhys
  • Interrupt Management: EOI, SetIRQ, SetROMVector, UnSetIRQ
  • Timer Services: ResetTimer, SetTimer, TickCount
  • System Services: GetDOSVar
  • Advanced BIOS Services: ABIOSCall, ABIOSCommonEntry, FreeLIDEntry, GetLIDEntry

Chapter 3. DOS Device Drivers

There are several areas of difference between DOS and OS/2, mostly in the area of memory access and management. For real mode DOS, the driver's view of memory is straight-forward. A valid address is always valid whether operating in interrupt, system, or user context. Under OS/2 the validity of an address is entirely dependent upon its context. Extra care and planning is required to ensure that the driver's memory management strategy is correctly defined.

The Device Driver Header

The interfaces and entry points defined by NDIS are the same both for DOS and for OS/2. There are however, differences in the basic device driver structure in the two systems. These discrepancies are illustrated by the format of the driver's device header. The DOS driver's header contains two entry points referred to as Strategy and Interrupt and is eight bytes shorter than the OS/2 header. The eighteen byte DOS device header is organized as follows:

Offset Size (bytes) Description
0x00 4 Pointer to next header
0x04 2 Device Attribute Field
0x06 2 Offset to Strategy Routine
0x08 2 Offset to Software Interrupt Routine
0x0A 8 Name

See the section on the OS/2 device header for a detailed description of the Device Attribute Field.

The Strategy Routine

Under DOS when the system processes a request packet, it initially calls the driver's strategy entry point with the register pair ES:BX containing the address of the packet. The only function the DOS strategy routine performs is saving this address in local memory. Immediately after return from the driver strategy routine, DOS calls the driver at the Software Interrupt entry point. The Software Interrupt routine retrieves the pointer stored by the strategy handler and then proceeds to execute the requested task.

The Request Packet

The DOS Request Packet, like the OS/2 Request Packet, consists of a thirteen byte header followed by a command-specific data area. The structure of the request packet header is as follows:

Offset Size (bytes) Description
0x00 1 Total length of Request Packet
0x01 1 Unit code (block device)
0x02 1 Command code
0x03 2 Status field
0x05 8 Reserved

See the section on the OS/2 Request Packet for a detailed description of these fields.

The Initialization Routine


Under DOS, the Strategy Routine will save the address of the INIT request packet and return. The initialization Routine will be accessed through the Software Interrupt Entry point.

The INIT Request Packet

The DOS INIT request packet is, like the OS/2 packet, twenty-three (Ox 17) bytes long. It consists of the standard thirteen byte header, followed by ten bytes of INIT-specific data in the following format:

Offset Size (bytes) Description
0x0D 1 Data_1
0x0E 4 Pointer_1
0x12 4 Pointer_ 2
0x16 1 Data_ 2

The only difference between the DOS and OS/2 packet is in the usage of Pointer_1. On entry for a DOS device driver, Pointer_1 is unused. On exit for a DOS driver, Pointer_1 should be set to point to the first available byte of memory above the driver.

INT 21 Function Calls at Initialization Time

During system initialization, there are typically several functions which require interface with the underlying operating system. For example, the driver may wish to display a text message identifying itself, or it may wish to query the revision of the system to be sure that some piece of required functionality is present. Particularly, in the case of an NDIS driver, interaction with another driver that has already been loaded is required.

The two operating environments, DOS and OS/2, provide the means to accomplish these tasks. Under DOS, operating system functions are executed by loading parameter values into registers and executing a software interrupt. The system dispatcher fields such requests via vector X'21'. The DOS Technical Reference provides a comprehensive description of the functions available via this method.

The OS/2 driver accesses the system via far calls to API functions linked into the executable file in precisely the same manner as an application program would. This is possible because the OS/2 initialization process executes in Ring 3 just as if it were an ordinary application program.

For both DOS and for OS/2, this functionality is accessible only during system startup. Once the initialization process has returned, neither the INT 21 or the application APIs are available.

Interrupt Handlers

Hardware interrupts and the associated interrupt handlers are managed in a slightly different manner between DOS and OS/2. Please note that interrupt handler as used in this context is distinct from the Software Interrupt routine described in the preceding discussions. In the current context, the interrupt handler is the body of code that executes in response to a hardware event and not as part of system request processing.

In an OS/2 environment, the handler is called indirectly by the system's interrupt manager. At the point at which the handler begins execution, all of the system's registers have been saved and a special interrupt stack is in use. In addition, the system provides helper routines to aid in managing Process Interrupt Control (PIC) related tasks such as delivering the SetIRQ command.

Under DOS, the handler is executed directly when a hardware interrupt occurs. Preserving the processor context, switching stacks, and dismissing the interrupt through direct interaction with the PIC are tasks which the driver must perform on its own behalf.

Chapter 4. Writing a Media Access Control Driver

The Media Access Control driver bridges the gap between the physical components of the network and the logical elements contained in the protocol stack. Every network transaction is processed by the MAC driver. Great care must be taken during implementation to ensure that a highly performant and flexible interface is provided. See the NDIS Driver Developer's Tool Kit for OS/2 and DOS Programmer's Performance Guide for guidance on performance Issues.

Typical drivers contain an entry point for requests from the operating system and usually an interrupt handler as well. NDIS MAC drivers contain additional entry points to process the functions that are defined at the boundary with the protocol driver. The calling sequence for the NDIS functions differs from that used by the operating system. Normal system functions such as read, write, open, and close are passed to the driver via a parameter block. NDIS functions are executed across the boundary between the MAC and the protocol using direct calls to entry points defined by the specification. The calling sequence used is far Pascal. The calling routine pushes its parameters on the stack from left to right, and the called routine releases the stack space occupied by the parameters on exit. Return status is passed via register AX. Called routines are responsible for saving and restoring standard register variables DS, DI, SI, and BP.

The following sections of this chapter will provide detailed descriptions and code samples illustrating the process that must be implemented by a MAC driver. These samples are written in assembly language, although C could be used. These samples are examples intended to illustrate possible code structures. Where differences exist between DOS and OS/2 the code fragments contain conditionals to control the assembly time generation of a driver for one system or the other.

The Strategy and Initialization Routines

Standard operating system type functions are signaled to an OS/2 driver via a far call to the Strategy Routine entry point defined in the driver header. The argument to this call is a pointer to a parameter block containing a code that indicates the function to be executed as well as supplementary information to be used in processing the request. The only function that a MAC driver must implement is initialization. Other functions may be implemented at the developer's discretion. For example, read and write might be used to provide access to the MAC's internal structures during debug. But in all cases, whether a function is implemented or not, every request must return a completed status in order to prevent exhaustion of system resources.

The initialization function is signaled to the driver during the processing of the CONFIG.SYS file at system startup time. During initialization, the driver establishes its operating environment, locates its adapter, and allocates the resources necessary for operation.

The PROTOCOL.INI file contains the definition of the communications subsystem that will be built. The PROTOCOL.INI file contains an entry for each driver that is configured in the NDIS environment. As part of its initialization path, the Protocol Manager reads this file and parses its contents into a tree structure. Each node directly below the root of the tree describes a [module] section from this definitions file.

Opening the Protocol Manager

As each NDIS driver initializes, it interacts with the Protocol Manager to obtain its individual parameters and to register itself. Access to the Protocol Manager is via the file system APIs. This is possible because during startup, the driver initialization code executes in Ring 3, just as if it were a normal application. The first step required is to open the Protocol Manager. The format of the function should be similar to that given below.

;-- Get handle for Protocol Manager
if      OS2
        push    ds                 ;    Far pointer to name (input)
        push    offset ProtocolManagerName
        push    ds                 ;    Far pointer to handle (output)
        push    offset ProtocolManagerHandle
        push    ds                 ;    Far pointer (output)
        push    offset ActionTaken
        push    0                  ;    Dword file size (output)
        push    0
        push    1                  ;    Flag (input)
        push    0C2h               ;    Mode (input)
        push    0                  ;    Dword reserved
        push    0
        call    DOSOPEN
        or      ax, ax             ;    Return status?
        jnz     NoManager          ;    Display error and abort
else                               ;    DOS interface
        mov     dx, offset ProtocolManagerName
        mov     ax, 3d00h          ;    DOS open function
        int     21h
        jc      NoManager

Protocol Manager Requests

After the Protocol Manager open has succeeded and the file handle is obtained, requests are executed using IOCTL functions, passing a pointer to a parameter block which defines the function to be performed. Under OS/2 the generic IOCTL, function code 16, is used to provide access to all of the functionality provided by the Protocol Manager. The Category code used is 0x81 signifying LAN Manager, with a function code of 0x58, indicating Protocol Manager type command. The parameter buffer contains a far pointer to a common request block structure. The structure given below describes the Protocol Manager request block.

PMBlock         struc           ; Function code
PMCode          dw      ?       ; Status returned by
PMStatus        dw      ?
                                ; ProtMan
PMPtrl          dd      ?       ; First parameter
PMPtr2          dd      ?       ; Second parameter
PMWord          dw      ?       ; Third parameter
PMBlock         ends

Getting Configuration Information

Typically, the next step is to gain access to the ConfigMemoryImage parsed from PROTOCOL.INI by the Protocol Manager. The code fragment provided below illustrates the correct procedure for filling in the request block and invoking the Protocol Manager.

;--     Fill in request block.  Only parameter required
;       is function code.
        mov     PMReqBlk.PMCode, GetProtManInfo
if      OS2
        push    0               ;       Far null pointer
        push    0
        push    ds              ;       Far pointer to ReqBlk
        push    offset PMReqBlk
        push    ProtManCode     ;       0x58
        push    LanManCat       ;       0x81
        mov     ax,[ProtoclManagerHandle]
        push    ax              ;       Handle returned by open
        call    DOSDEVIOCTL     ;       System API
        or      ax, ax          ;       Return okay?
        jnz     NoConfigInfo    ;       Display error and abort
else                            ;       DOS
        mov     bx, [ProtocolManagerHandle]
        mov     dx, offset PMReqBlk
        mov     cx, 14          ;       size of block
        mov     ax, 4402h       ;       IOCTL function
        int     21h
        jc      NoConfigInfo

;--     Success.  First pointer contains address of
;       ConfigMemoryInfo structure. Word parameter
;       contains revision of Protocol Manager. Check
;       for acceptable values.
        mov     ax, PMReqBlk.PMWord
        cmp     ax, ProtManLevel
        jne     InvalidProtMan      ;Display error and abort
        lds     si,PMReqBlk.PMPtrl  ;Get pointer to config tree

The driver must now traverse the list of ModuleConfig structures, seeking the one that contains its operational parameters. Note that although the nodes are based an the square-bracketed [ModuleName] sections contained in the PROTOCOL.INI file, ModuleName is not the correct key to be used for the search. The value of this field is user definable and may be installation specific. The search should be based on the "DriverName=" keyword. This entry is required within every ModuleName section and must accurately reflect the name used within the driver's device header. Other fields are optional in general, but may be required by specific drivers. Below is an example PROTOCOL.INI entry for the driver for the fictional BITHOSE card. This driver requires two additional parameters, namely the I/O base address of the card and the IRQ to be used.

  DriverName      = bithose$
  Base            = 0x300
  IntLevel        = 3

When the Protocol Manager builds the ConfigMemoryImage, lower case characters are converted to upper case and white space and comment lines are ignored. During initialization, the bithose driver searches for a ModuleName section with the correct DriverName value.

;-- The following structure definitions are used
;    by the parsing routine below.
ModuleConfig            struc
ModuleNext      dd      0               ;       Next in chain or
                                        ;       Null
ModulePrev      dd      0               ;       Previous in chain
ModuleName      db      16 dup (0)      ;       Square bracket name
KeyWordList     dd      0               ;       First keyword
ModuleConfig            ends    
KeyWord         struc
KeyWordNext     dd      0               ;       Next in chain or
                                        ;       NULL
KeyWordPrev     dd      0               ;       Previous in chain
KeyWordName     db      16 dup (0)      ;       Keyword text
NumParams       dw      0               ;       Count of parameters
;--  The next section repeats NumParams times
ParamType       dw      0               ;       Type of parameter
                                        ;       =0 then long
                                        ;       =1 then ASCIIZ
ParamValue      dd      0               ;       Variable depending
                                        ;       on type
Keyword                 ends
;-- Find our ModuleName section.
;  ds:si--> Head of ModuleConfig linked list.
     mov        ax, ds                  ;       Check for end of
     or         ax, si                  ;       the line
     jz         NoDriverName            ;       Display error and
                                        ;       abort
     push       ds                      ;       Save our place
     push       si
     lds        si, [si].KeyWordList
     mov    ax, ds
     or     ax, si                      ;       Null pointer?
     jz     EndKeywords                 ;       Yes - try next module
     mov        bx, si                  ;       Save structure pointer
     mov        si, [si].KeyWordName
     mov        di, offset DriverKeyword
     mov        cx, 10                  ;       Length of "DRIVERNAME"
     rep        cmpsb
     jcxz       FoundDriverKeyword
     lds        si, [bx].NextKeyword
     jmp        CheckEndKeyWords
     cmp        [bx].NumParams, 1
     jne        NextKeyword             ;       Invalid entry
     lea        si, [bx].Param
     cmp        [si].ParamType, ASCIIZ
     jne        NextKeyword
     mov        cx, OurNameLen
     cmp        cx, [si].ParamLen
     jne        NextKeyWord
     lea        si, [si].ParamValue
     mov        di, offset OurName
     rep        cmpsb
     jcxz       FoundOurName
     jmp        NextKeyword
     pop        si
     pop        ds                      ;       Restore
                                        ;       ModuleConfig ptr
     lds        si, [si].NextModule
     jmp        CheckEndModules
     pop        si
     pop        ds                      ;       We have our section
                                        ;       now

Building the Characteristics Tables

After locating the correct ModuleName section, the driver should copy the Name specified by the user into its common characteristics table. The driver should scan the keywords linked to this module for any specific required parameters. In this example, the bithose driver would continue the scan for the two additional required parameters. If they are not present, the driver may choose to display an error message and abort its initialization. For each parameter encountered, the driver should check to ensure that the parameter is of the correct type and that the value given is within range.

After parsing its ModuleConfig section, the driver should have enough information to query its adapter and complete the initialization of its characteristics tables. NDIS defines a set of tables that are maintained by conformant drivers. These tables contain information describing the capabilities of the driver and adapter, as well as several entry points within the driver that are callable by external entities across the NDIS boundary. Most of the fields in the table may be initialized when the driver is coded, with the default values in selected fields overridden by information gained from PROTOCOL.INI parsing.

Several fields within these tables contain far pointers to be used by the protocol driver after binding. The values contained must be Ring 0 GDT selectors. They can not be established at initialization time because the driver is operating in Ring 3. The value for these pointer fields are properly established using the SEG assembler pseudo-op as illustrated in the example below, describing the NDIS System Request dispatch entry point as it might appear within the MAC's Common Characteristics Table.

          dw         offset System
          dw         SEG System

Completing Initialization

After the Characteristics Tables have been completely built, the MAC driver can proceed to the final stages of initialization. Depending on the MAC, this may, under OS/2 require reserving GDT selectors and locking extra segments for use during operation. When the implementation-specific initialization functions have completed, the driver is ready to complete its start-up path by registering with the Protocol Manager. This function is executed as a generic IOCTL as described in the preceding section on obtaining the ConfigMemoryInfo. The format of the call is described below.

;--  Fill in request block. Required parameters are
;    function code and far pointer to the Common
;    Characteristics Table. Pointer 2 (bindings
;    list) must be null for MAC's.
       mov              PMReqBlk.PMCode, RegisterModule
       mov              word ptr PMReqBlk.PMPtrl, offset CCTable
       mov              word ptr PMReqBlk.PMPtrl + 2, SEG CCTable
       mov              word ptr PMReqBlk.PMPtr2, 0
       mov              word ptr PMReqBlk.PMPtr2 + 2, 0
if     OS2
       push     0                       ;       Far null pointer
       push     0
       push     ds                      ;       Far pointer to ReqBlk
       push     offset PMReqBlk
       push     ProtManCode             ;       0x58
       push     LanManCat               ;       0x81
       mov      ax, [ProtoclManagerHandle]
       push     ax                      ;       Handle returned by open
       call     DOSDEVIOCTL             ;       System API
       or       ax, ax                  ;       Return okay?
       jnz      RegisterFail            ;       Display error and abort
else                                    ;       DOS
       mov      bx, [ProtocolManagerHandle]
       mov      dx, offset PMReqblk
       mov      cx, 14
       mov      ax, 4402h               ;       DOS IOCTL
       int      21h
       jc       RegisterFail

Initialization is now completed and no further interaction with the Protocol Manager is required. The file handle used for the interface may now be closed and the driver may exit back to the OS/2 configuration processor.

The System Request Routine

At some point after the system has completed boot up, an application program may request that the network interface be started. This is signaled to the Protocol Manager via a BindAndStart function. A typical source of this request is the NETBIND.EXE program. During the RegisterModule phase, the Protocol Manager built a binding tree describing the protocol driver to MAC linkages to be established in order to create the communications subsystem. Upon receipt of the BindAndStart command the Protocol Manager traverses this tree, requesting that the higher level entities (protocol drivers) bind to the lower (MAC drivers).

For the MAC driver, the end result of this activity will, if the system is correctly configured, generate one and only one bind request from an upper level routine. This request may be issued by a single protocol driver module or it may be issued by a sub-function of the Protocol Manager called VECTOR acting on behalf of multiple protocol driver modules that have specified a binding to the MAC driver. In either case, the interface to the MAC driver is identical. When the MAC is invoked, the stack contains the parameters passed by the protocol driver in the following format

stack   -->     dword   far return
        -->     word    MAC's data_seg
        -->     word    opcode for Bind (2)
        -->     word    pad (0)
        -->     dword   pointer where to return CCTable address
        -->     dword   callers CCTable address

The Bind System Request

Bind is the only System Request that MAC drivers implement. The MAC should be prepared to bind with no more than one upper lead module. Succeeding Bind requests should return an invalid status. The code fragment provided below demonstrates the Bind procedure.

System proc far
       push     bp                      ;       Save frame pointer
       mov      bp, sp                  ;       And establish
                                        ;       addressability
       push     ds                      ;       Save
       push     di
       push     si
       mov      ds, [bp].SysDs          ;       Our data_seg
       mov      ax, INVALID_FUNCTION
       cmp      [bp].SysFunc, BIND
       jne      SysExit                 ;       Nothing else
       mov      ax, [BindStatus]
       or       ax, ax                  ;       We update this when a
                                        ;       bind has occurred.
                                        ;       If 1=0 then
                                        ;       already bound.
       jnz      SysExit
;-- Take control of the adapter now. It shouldn't be
;   done during bootup in case the adapter is being
;   used for remote boot.
      call GetAdapter                   ;       Do adapter specific stuff
      or        ax, ax                  ;       Make sure all went well
      jnz       SysExit                 ;       Some kind of hardware
                                        ;       problem - fail the bind
;--  Mark ourselves as bound and copy in some of the
;     Protocol's information for fast access.
      mov   [BindStatus], INVALID_FUNCTION
      or      SpecStatusTable.SpecStatus, MACBound
      mov       ax, ds
      mov       es, ax
      mov       di, offset ProtocolTable
      lds       si, [bp].SysProtTable
      mov       ax, [si].CCTableDS      ;       Save Protocol's DS
      mov       es: [ProtcolDS], ax
      lds       si, [si].CCTableLowerDispatch
      mov       cx, SizeLowerDispatch
      rep       movsb
      xor       ax, ax                  ;       Status cleared
      pop       si
      pop       di
      pop       ds
      pop       bp
      ret       SizeSysParms            ;       Clean up stack and exit
System endp

If the MAC driver has chosen not to implement Open and Close then, on exit from the System Request function after a successful Bind, the adapter and the MAC should be fully conditioned and ready to support network traffic.

The Indication Routines

An indication, as generated by the MAC to the protocol driver, is a higher level notification of an adapter-generated event. Indications may occur when a frame is received, when an adapter error condition occurs, or, if supported by the underlying hardware. as the result of an InterruptRequest Gall Request issued at some earlier point in time by the protocol driver MAC drivers implement two entry points, IndicationOn and IndicationOff, that provide the protocol driver with a logical mechanism for enabling and disabling these events.

The MAC driver maintains its indication level as a counter. IndicationOff increments the counter and IndicationOn decrements it. At any point if the value of the counter is non-zero, indications may not be signaled to the protocol drive. This correctly handles unwinding from the case where IndicationOff requests have been nested.

On entry to the indication routines, the stack holds the calling context as follows:

       Stack    -->     dword   far return
                -->     word    MAC's data_seg


The code fragment below provides an example IndicationOff routine. As part of its process path, the routine calls an adapter specific routine to disable its interrupt generation. Depending on the hardware, it may be possible to execute a partial disable, allowing the driver to continue processing non-indication generating functions such as TransmitChain.

IndicationOff                   proc far
      push      bp                      ;       Standard preamble
      mov       bp, sp
      push      ds
      push      di
      push      si
      mov       ax, [bp].IndDS
      cli                               ;       Per NDIS we return disabled
      mov       al, [IndicationLevel]
      inc       [IndicationLevel]
      or        al, al                  ;       Enabled before?
      jnz       IOffExit                ;       Yes - HW already disabled
     call       AdapterDisable          ;       Specific to adapter
      xor       ax, ax                  ;       Return okay
      pop       si
      pop       di
      pop       ds
      pop       bp
      ret       IndParmSize
IndicationOff                   endp


The IndicationOn procedure is the complement of the IndicationOff function. The MACs indication level is decremented and, if the count reaches zero, adapter interrupts are re-enabled.

IndicationOn                                    proc far
     push       bp                      ;       Standard preamble
     mov        bp, sp                  ;       Get stack addressability
     push       ds
     push       di
     push       si
     mov        ds, [bp].IndDS          ;       Our data_seg
     cli                                ;       Per NDIS we return disabled
     dec        [IndicationLevel]
     jnz        IOnExit                 ;       Still off
     call AdapterEnable                 ;       HW specific to restart
                                        ;       Must run disabled because
                                        ;       indications not allowed within
                                        ;       the context of this call
     xor        ax, ax                  ;       Status = okay
     pop        si
     pop        di
     pop        ds
     pop        bp
     ret        IndParmSize
IndicationOn                            endp

In addition to these two routines, the indication level is implicitly manipulated by the MAC driver whenever an indication is about to be signaled to the protocol drive. This will be described more fully in the following sections describing the specific indications.

The TransmitChain Routine

The MAC's TransmitChain entry point is called by the protocol driver when there is a frame ready to be dispatched to the network. MAC's may implement TransmitChain synchronously or asynchronously. If the transmission is completed successfully within the context of this call, a successful return status is provided to the calling protocol. If the adapter is currently unable to accept another frame for transmission, the MAC may queue the request and return immediately to the protocol with a status indicating that the request was pended. At some later point when adapter resources become available, probably within the context of the interrupt handler, the frame can be dequeued and transmitted.

If transmission is deferred, the MAC has two additional responsibilities. First, the TransmitChainBuffer Descriptor and any immediate data it describes are valid only during the execution of the original TransmitChain call. Therefore, these volatile portions of the frame description must be copied into the MAC's memory space before returning. Second, when the deferred frame is finally transmitted, it must be confirmed to the protocol driver via the TransmitConfirm function described below.

On entry to TransmitChain, the stack contains the arguments provided by the protocol driver to be used in processing this request.

       stack    -->     dword   far return
                -->     word    MAC's data_seg
                -->     dword   pointer to buffer descriptor
                -->     word    request handle
                -->     word    Protocol ID
TransmitChain                           proc far
     push       bp                      ;       Standard preamble
     mov        bp, sp
     push       ds
     push       es
     push       di
     push       si
;-- First check if we can start a transmit now.
     call       CheckResources          ;       Adapter specific
     or         ax, ax                  ;       ax=0 then all systems go
     jz         TxOkay
;-- Else we have to queue request.
     call       CopyChain               ;       Copy descriptor, immediate
                                        ;       data, handle, and protocol ID
                                        ;       ss:bp -->TxChain info
     mov        ax, REQUEST_QUEUED
     jmp        TxChainExit             ;       Return - xmit deferred
;--   We can send now.
     Call DoTransmit                    ;       Adapter specific
                                        ;       ss:bp-->Txchain info
                                        ;       Returns status of xmit.
     pop        si
     pop        di
     pop        es
     pop        ds
     pop        bp
     ret        TxChainParmSize
TransmitChain                           endp

The Interrupt Routine

In large part, the design of the interrupt routine is dictated by the functionality of the adapter. There are, however, certain common sense guidelines to which all drivers should adhere.

The Interrupt Routine should be divided into two major sections. The front end is responsible for time-critical functions that execute during initial processing with the adapter disabled. The back end, while still in interrupt context, handles the less critical tasks after the adapter has been reconditioned to process network traffic. It is recommended that nesting within the Interrupt Routine be kept to a minimum. One invocation of the Interrupt Routine can completely clear the adapter's interrupt queue, efficiently using system resources. If multiple invocations of the Interrupt Routine are allowed to occur, then the potential exists to exhaust finite system resources such as stack space.

A simple, workable methodology, that eliminates Interrupt Routine nesting, gates access to the two portions of the Interrupt Routine based on memory resident flags. On entry the front end Interrupt Routine dismisses the interrupt at the PIC and, while interrupts are disabled, the following code sequence guarantees exclusive access to the Interrupt Routine code. A similar mechanism can be used when the front end handler has completed his critical execution path and is ready to continue the back end processing if necessary.

;-- Get and set the exclusion flag.
    mov         ax, 1
    xchg        al, [Level1Flag]
    or          al, al
    jnz         IntlExit                        ;       Already running - bail out
;-- Execute front end code
;-- Front end completed. Clear semaphore and see
;   if tail is running.
     cli                                        ;       Tests must execute disabled
     xor        al, al
     xchg   [Level1Flag],       al              ;       Get/Clear level 1
     xchg   [Level2Flag],       al              ;       Get/Set level 2
     or         al, al
     jz     ExecLevel2                          ;       Not running - go execute
if   DOS                                        ;       Restore registers and swap
                                                ;        back to original stack
     pop        es
     pop        ds
     mov        ss, cs: [savedss]
     mov        sp, cs: [savedsp]
else                                            ;       OS/2

;-- Execute back end code.
if   DOS                                        ;       Swap off of interrupt stack
     pop        es
     pop        ds
     mov        ss, cs: [savedss]
     mov        sp, cs: [savedsp]
     cli                                        ;       Clear until we exit
     mov        [Level2Flag], 0
if   DOS
else                                            ;       OS/2

With this technique, at most one iteration of the front end handler and one iteration of the back end handler we be active at any one time

Time-Critical Tasks

On entry to the Interrupt Routine, there are several house-keeping functions that are required for all drivers. When the Interrupt Routine begins execution, interrupts are disabled. Several critical tasks must be performed before re-enabling. The adapter's interrupt should be masked, either at the PIC or at the adapter itself, whichever is more convenient, and an End-of-Interrupt command must be delivered to the PIC to re-enable normal hardware interrupt processing. If running under DOS, the driver must swap to a private interrupt stack. Finally, with the Interrupt Routine secure from recursion, the processor interrupt flag should be set to allow normal system operation.

Changing Mode

Before continuing further along its processing path, the driver must ensure that it is executing in protected mode. This is a requirement if any upcalls to the protocol are to be executed during interrupt handling. The procedure for ascertaining whether or not the Interrupt Routine is running in protected mode and, if necessary, executing the mode switch is given below.

if    OS2
      smsw      ax                      ;       Get machine status word
      mov   [ModeSwitch],  ax
      shr       ax, 1                   ;       Shift out Protected Mode bit
      jc        InProtMode              ;       Okay
      mov       dl, RealtoProt          ;       0x2F
      call      dword ptr [DevHelp]
      mov       [ModeSwitch], 1

Similarly, during the Interrupt Routine's exit procedure, the processor must be resumed to real mode if a switch has been executed at the driver's request.

;--  Ready to exit. See if we did mode switch.
if    OS2
      xor       al, al
      xchg      [ModeSwitch], al
      or        al, al                  ;       =0 then no switch
      jz        NoSwitch
      mov       dl, ProttoReal          ;       0x30
      call      dword ptr [DevHelp]

Receiving Frames

There are two basic mechanisms provided for passing received frames up to the protocol. The choice is based primarily on the type of interface supported by the adapter. For adapters that support shared memory such that the MAC drive has complete visibility to the entire received frame, the ReceiveChain primitive provides the more efficient mechanism for conveying frames to the protocol driver. If, on the other hand, the adapter interfaces via programmed I/O, ReceiveLookahead offers a facility whereby the driver can input a relatively small leading portion of the received frame and advertise it to the protocol driver. Drivers that generally use ReceiveChain may occasionally revert to ReceiveLookahead in situations where adapter memory resources are running low. Both methods are described in more detail below.

ReceiveLookahead and ReceiveChain are both defined as indications, and may be suppressed based on the value of the indication level variable. The level of indication may be manipulated explicitly by the protocol driver via the IndicationOn and IndicationOff MAC entries. Additionally, the MAC explicitly disables indications before calling the protocol driver at an indication entry point. This ensures that the protocol driver will not be interrupted during indication processing by the arrival of another indication. Beside the other arguments passed to the protocol driver during an indication call, the MAC passes the address of a byte location called the IndicateFlag. Before the upcall, the MAC initializes this location with the value 0xFF. During indication processing the protocol driver may clear this location. On return, the MAC examines the value contained in the flag. If it has been cleared, then indications remain disabled. Otherwise, the MAC decrements the value of indication level potentially re-enabling indications.


The code fragment given below illustrates the sequence required to signal a ReceiveLookahead indication to the protocol driver. If the Protocol accepts the frame, it will call the MAC driver's TransferData entry within the context of the ReceiveLookahead call in order to receive a copy of the frame data.

;-- Execute a ReceiveLookahead upcall. The assumption is
;   that if we have reached this point indications are
;   currently enabled.
      mov       byte ptr [IndicateFlag], -1
      inc   byte ptr [IndicationLevel1]
      push      [MacId]                 ;       Assigned by ProtMan
      push      [FrameSize]             ;       Total size of frame
      push      [LookAheadLen]          ;       Range of LookAhead
                                        ;       This could be entire frame
      push      ds                      ;       Far ptr to LookAhead buffer
      push      offset LookAheadBuff
      push      ds                      ;       Far ptr to action flag
      push      offset IndicateFlag
      push      [ProtocolDS]            ;       Obtained during bind
      push      dword ptr [ReceiveLookahead]
                                        ;       Address copied during bind
;-- If the Protocol wants the frame it will call our
;    TransferData entry from within the context of this call
      mov       dl [IndicateFlag]
                                        ;       On return either 0 or -1
      add       [IndicationLevel, dl
      inc       [NeedIndComplete]
                                        ;       Remember we need a complete


The process path for ReceiveChain is conceptually similar to that used by ReceiveLookahead. ReceiveChain is more appropriately used when the entire frame is available in memory visible to the MAC driver. The indication level is managed similarly for both calls. With ReceiveChain the protocol driver may elect to copy the frame immediately into its space or it may defer the copy and queue the frame for copy and process during back end interrupt processing. If the protocol driver chooses to defer, the MAC driver must maintain the data buffers until they are explicitly released.

;-- ReceiveChain upcall processing. On entry the
;   frame buffers have already been linked to the
;   BufDescr.
    mov         byte ptr [IndicateFlag], -1
    inc         byte ptr [IndicationLevel]
    inc         word ptr [RequestHandle]        ;       Generate a new handle
    push        [MacId]                         ;       Assigned by ProtMan
    push        [FrameSize]                     ;       Total size of frame
    push        [RequestHandle]                 ;       Identify this request
    push        ds                              ;       Far ptr to BufDescr
    push        offset RxBufDescr               ;       These are volatile so we
                                                ;       we only need one
    push        ds                              ;       Far ptr to action flag
    push        offset IndicateFlag
    push        [ProtocolDS]                    ;       Obtained during bind
    call        dword ptr [ReceiveChain]        ;       Address copied
                                                ;       during bind
    mov     dl, [IndicateFlag]                  ;       On return either 0 or -1
    add         [IndicationLevel], dl
    inc         [NeedIndComplete]               ;       Remember that we need a
                                                ;       complete
    cmp         ax, REQUEST_QUEUED              ;       If no --
    jne         ReleaseBuf                      ;       then we can release
    call        QueuedRecv                      ;       Put buffers on a waiting
                                                ;       queue. The unique handle
                                                ;       generated before the
                                                ;       call will be used for ID
                                                ;       during release

Note that a MAC that normally uses ReceiveChain may switch temporarily to ReceiveLookahead if the protocol driver has deferred processing on several frames and the MAC is running low on resources. ReceiveLookahead forces the protocol driver to process or reject the frame synchronously within the context of the call.

Other Tasks

In addition to receive processing, there are several other tasks that the interrupt Interrupt Routine may be called upon to manage. Adapter errors should be monitored and if necessary AdapterCheck status indications may be generated to the protocol driver in response to serious failures. If a transmit completion occurs, the MAC must generate a confirmation to the protocol driver, using the request handle passed with the original TransmitChain. If another frame is queued, a new transmit request can be passed to the adapter.

Only those functions that are time critical should be processed during front end interrupt handling. Whenever possible, work should be deferred until post processing when the adapter is re-enabled. Before exiting its back end interrupt processing, the MAC driver should generate an IndicationComplete upcall to allow the protocol driver to complete any of the processing it has deferred during indication time.

The TransferData Routine

TransferData is used only in conjunction with ReceiveLookahead. It describes a service entry point within the MAC driver that the protocol driver may invoke in order to transfer a received frame into its space. This call is valid only within the context of a ReceiveLookahead upcall from the MAC.

The MAC driver processes the data from the network card into the buffers linked to the TransferData buffer descriptor. The actual method of interface, DMA, programmed I/O, etc., is specific to the adapter. The process completes until either the entire frame has been transferred or until the protocol driver's buffers have been completely filled. In either case, the actual number of bytes copied is returned to the protocol driver in the location pointed to by the BytesCopied argument.

Depending on the hardware, the MAC may be able to support multiple calls to TransferData within the context of a single ReceiveLookahead upcall. If at all possible, the MAC should support this feature to allow for the case where multiple protocols are bound, via Vector, to a single MAC.

The ReceiveRelease Routine

ReceiveRelease is a MAC entry point called by the protocol driver when a ReceiveChain indication has been deferred. The protocol driver must remember the handle passed with the original ReceiveChain upcall and pass it back to the MAC. The handle is used to uniquely identify the buffers that are to be released.

The General Request Routines

The General Requests provide the protocol driver with the ability to perform adapter administration operations. All of the requests are handled via a single entry point. Specific operations are demultiplexed by the MAC via a function code parameter.

The MAC may choose to either handle the requests synchronously or to queue the request for later processing. If the request is deferred, the MAC must remember the handle and protocol ID passed with the call to be used later when the request is confirmed.


January, 1996

Issued by:

IBM Corporation

Personal Software Products

11400 Burnet Road

Austin, Texas 78758

Second Edition (January 1996)

First Edition (May 1993)

The following paragraph does not apply to the United Kingdom or any country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you.

This publication could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or program(s) described in this publication at any time.

It is possible that this publication may contain reference to, or information about, IBM products (machines and programs), programming, or services that are not announced in your country. Such references or information must not be construed to mean that IBM intends to announce such IBM products, programming, or services in your country.

Copyright Notices

© Copyright International Business Machines Corporation 1993. All rights reserved.

Note to U.S. Government Users - Documentation related to restricted rights - Use, duplication or disclosure is subject to restrictions set forth in GSA ADP Schedule Contract with IBM Corp.


References in this products to IBM products, programs or services do not imply that IBM intends to make these available in all countries in which IBM operates. Any reference to an IBM product, program or service is not intended to state or imply that only IBM's product, program, or service may be used. Any functionally equivalent product, program, or service that does not infringe any of the intellectual property rights of IBM may be used instead of the IBM product, program, or service. The evaluation and verification of operation in conjunction with other products except those expressly designated by IBM, are the responsibility of the user.

IBM may have patents or pending patent applications covering subject matter this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries in writing to the IBM Director of Commercial relations IBM Corporation Purchase NY 10577 - USA.


The following terms are trademarks of the International Business Machine Company:


The following terms are trademarks of the indicated companies:

Microsoft - Microsoft Corporation
LAN Manager - Microsoft Corporation
3Com - 3Com Corporation

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation