4.6.1 Multipacket Receives

A miniport can allocate and manage an array of packet descriptors, fill the chained buffers with incoming data and then pass pointers to the filled in packets to interested protocol drivers by calling NdisMIndicateReceivePacket. Support of NdisMIndicateReceivePacket requires that the miniport allocate and set up packet descriptor for a full network packet, rather than a lookahead buffer.

The miniport passes status, possibly the time a packet is received, possibly the time the packet was sent, and possibly other media-specific infomation to upper-layers by setting members in the OOB data block associated with each packet descriptor. The miniport must set the Status member of the OOB block to either NDIS_STATUS_SUCCESS, if the miniport is willing to allow a protocol to keep the packet and return it later to MiniportReturnPacket, or to NDIS_STATUS_RESOURCES if the miniport is running short of free packet descriptors or buffers and wants to force any interested upper layer driver to copy the packet data before NdisMIndicateReceivePacket returns. See Section 4.3 for details.

When NdisMIndicateReceivePacket returns:

A miniport that manages a busmaster DMA adapter will typically indicate packets by calling NdisMIndicateReceivePacket because such a miniport’s NIC usually has sufficient ring buffer space to receive multiple packets. The miniport can get a performance boost processing several packets at a time.

Usually, a miniport for any other type of device, such as a PIO device or an adapter-shared-memory device, calls NdisMIndicateReceivePacket only if it needs to pass priority, media-specific information or receive-time. NonDMA NICs typically only receive one packet at a time and must be reset between single receives. Such a miniport may only indicate up one packet at a time with NdisMIndicateReceivePacket.

Because the miniport passes ownership of the packet(s) up when it calls NdisMIndicateReceivePacket, such a miniport must practice a reasonable buffer-management strategy to insure that it has available buffers when new data arrives on the network. Typically, the miniport preallocates a set of buffers in MiniportInitialize the size of its ring plus perhaps a few extra, and allocates sufficient buffer descriptors to map these buffers plus a few packet descriptors for making subsequent receive indications.

If a miniport determines that it is running low on receive buffer space, it can use either of the following two strategies.

  1. When it calls NdisMIndicateReceivePacket, the miniport should set the Status member of the OOB block for the first packet descriptor it indicates up that it does not want the protocol(s) to keep to NDIS_STATUS_RESOURCES. This tells NDIS that the miniport is not willing to give up ownership of this packet's resources, nor of any subsequent packets in the array, to the protocol driver to whom the packet is indicated. NDIS will ensure that the packet is copied by the protocol driver rather than passed to the protocol driver. That is, NDIS calls ProtocolReceive rather than ProtocolReceivePacket, forcing the protocol driver to transfer the data immediately. Since the miniport does not have to provide a MiniportTransferData function if it supports the multipacket receive paradigm described here, NDIS intercepts the protocol driver’s transfer request and performs the transfer.

  2. Or, the miniport maintains a high water mark and low water mark for its set of free buffers. When the number of buffers approaches the low-water mark, the miniport calls NdisMAllocateSharedMemoryAsync to allocate more buffers and NdisAllocateBuffer to allocate buffer descriptors to map these buffers. This NDIS function can be called at IRQL DISPATCH_LEVEL, even in the MiniportHandleInterrupt function for instance, unlike NdisMAllocateSharedMemory which can be called only at IRQL < DISPATCH_LEVEL, for instance, in the MiniportInitialize function. When the set of free buffers exceeds the high-water mark, the miniport returns excess buffers by calling NdisMFreeSharedMemory to free the buffers and NdisFreeBuffer to free the buffer descriptors that map these buffers.