This is a sample NDIS driver supporting the National Semiconductor Fast Infrared Controllers. It implements the OID_IRDA_* calls required of any NDIS IrDA miniport.
To build NSCIRDA.SYS, select either the checked or free DDK environment, change to the directory NSCIRDA and type BUILD.
This sample is included as part of Windows 2000 for controlling the NSC FIR hardware included in laptop and desktop computers. It supports plug-n-play, and will be installed automatically on systems which have this hardware present and enabled. The installation file, IRNSC.INF, is made for install from the CD and contains differences from an INF file used for floppy installation.
Files Description NSCIRDA.HTM The Sample Tour documentation for this sample (this file). SOURCES The generic file for building the code sample. NSC.C Providing NDIS entry points and the main logic of the driver. NSC.H Header for NSC.C, providing main data structures. NSCTYPES.H Some data types. COMM.C Supports the SIR mode of operation. COMM.H Header for COMM.C. CONVERT.C Converts NDIS packets to SIR packets. DONGLE.C Programming code for various FIR transceivers. DONGLE.H Header for DONGLE.C. NEWDONG.H Another header for DONGLE.C. FCS.C Source to generate SIR FCS (checksum) values. INIT.C Source to initialize the controller. NSCDEMO.H Prototypes for INIT.C. NSCFIR.C Supports the FIR mode of operation. REQUEST.C Implements the NDIS OIDs. RESOURCE.C Primitives to allocate and free memory and structures. RESOURCE.H Header for RESOURCE.C. SETTINGS.C Table of IrDA speeds, and debug code. SETTINGS.H Header for SETTINGS.C, many debug defines. SYNC.C Provides ISR Synchronized List functions. SYNC.H Header for SYNC.C. DEFS.H Included by NEWDONG.H. EXTERNS.H Prototypes. IRNSC.INF Installation File
As this is an NDIS miniport, the documentation of NDIS features is left to other sections. This code tour focuses on IrDA under NDIS, and features unique to this miniport. The OID_IRDA_* calls are described in a .DOC file included with the IrDA samples.
The major topics covered in this tour are:
Management of DMA buffers
In FIR mode, most IrDA controllers use DMA to receive IrDA packets. These packets can range from 2 to 2048 bytes in size, and can be located at any offset inside the DMA buffer. Since the DMA buffer is usually never filled by packets, and there is no way to know exactly how many packets will be received, special efforts must be made to know when to stop the DMA transfer. In NSCIRDA, this is accomplished via a repeating timeout. Once data begins to be received (indicated by an interrupt), NSCIRDA programs the controller to produce an interrupt every few milliseconds. In the interrupt’s DPC routine, it calls NdisMReadDmaCounter to determine if data is still flowing. When the counter stops changing, it stops the DMA and begins to extract the packets. Since more packets may be coming, it is important to restart the DMA as quickly as possible.
NDIS requires that packets indicated to the protocol can be kept indefinitely or returned in any order. These requirements would seem to imply that data in the DMA buffer should be copied out to an NDIS buffer before being indicated to the protocol, but this introduces two problems. First, a second set of buffers must be used to hold the copied packets, increasing the memory footprint of the driver. Second is the overhead of the copy itself. Since most IrDA devices are presently found in laptop and notebook computers, both of these concerns are more significant as memory and computing power are usually lower in these systems than their desktop counterparts.
NSCIRDA avoids both problems by implementing a mechanism whereby the memory used to DMA the packet is the same memory used to indicate the packet, and yet a new DMA can be started immediately to capture more packets.
The heart of this mechanism is a pair of linked lists, rcvBufFull and rcvBufPend, containg rcvBuffer structures. Once a DMA is completed, FIR_DeliverFrames walks the buffer, identifying packets and verifying their integrity. It is the responsibility of the miniport that no corrupt packets be indicated to the protocol. Once a valid packet has been identified, QueueReceivePackets is called to associate the data in the DMA area with a rcvBuffer header, and the header is placed on the end of the rcvBufFull list.
After all packets have been identified in this way, FindLargestSpace is called to locate the largest unused portion of the DMA buffer, and a new DMA is started in that space only. In most cases, the rcvBufPend list will be empty, and the area identified by FindLargestSpace will begin at the end of the last packet and reach to the end of the DMA area, as in Figure 1. After the DMA is restarted, the miniport may proceed to indicate the new packets to the protocol. As each packet is indicated, its rcvBuffer header is moved from rcvBufFull to rcvBufPend.
In the case of packet loss and retransmission, packets may be held by the protocol for some time, even across several transmissions and link turnarounds.
In Figure 2, we see that two packets have been indicated to the protocol and not returned, one new packet has been received, and there are now three non-contiguous areas of unused space in the DMA buffer. FindLargestSpace will walk both lists simultaneously to find the unused area, and in the example would identify the middle space for a new DMA transfer. FindLargestSpace requires that the data indicated by rcvBufFull and rcvBufPend be ordered by address, highest address last.
ISR Accessible linked lists
All packet handling in NSCIRDA is accomplished via linked lists. In the case of SIR mode, it may be required that the lists be accessed inside of an ISR. SYNC.C and SYNC.H provide equivalents to the standard NdisInterlocked*List functions that are protected by SyncWithInterrupt instead of spinlocks. It is important that you understand how this works, and which mechanism is used for a specific list.
Turnaround delays
The half-duplex nature of infrared devices requires a turnaround delay, a delay that occurs before the first packet in a series of transmissions, to insure that the other side is prepared to receive data. Most previous implementations of this delay have used NdisStallExecution. It should be remembered that NdisStallExecution is for delays of no more than 50 microseconds. During NdisStallExecution, the processor can only idle. Using it to implement the turnaround delay that can last several milliseconds can have a significant impact on system performance. NSCIRDA uses NdisTimer to accomplish the delay.