4.6.3  Steps in Receiving Data

The way in which received data is processed depends to some extent on the type of NIC. The process for receiving data is described for each such device type.

Some operations are common to all the devices. These include:

·Miniports call NdisRawWritePortXxx functions to program their NIC.

·Miniports that indicate up a complete packet structure must call NdisAdjustBufferLength to adjust the buffer descriptor that describes a buffer to the actual length of the data in that buffer before indicating up the buffer, and must call this function again to reset it to the actual buffer length when the packet is returned.

·Miniports do not enable more receives until the current data has been indicated up and a new set of buffers allocated to the receive ring, or copied (or not, if the protocol driver is not interested) by the protocol driver to which the data was indicated.

Packet Management

Some miniports, such as a miniport for an Ethernet device, always receive a fixed and known size frame. If the frame size is always the same, the miniport can allocate packets with a chained buffer(s) and the same packets can be reused for receives.

If, however, a miniport controls a NIC on which variable length frames are received, the size of a packet needed to handle a receive can vary from one receive to the next.

If a miniport must handle variable-length receives, it must do the following:

1.Chain and unchain buffers from a packet by calling Ndis(Un)ChainBufferAtBack or Ndis(Un)ChainBufferAtFront to build a larger, or make a smaller, packet indication.

2.Call NdisAdjustBufferLength before indicating a packet to set the size in the buffer descriptor for the last buffer in a chain if the full buffer is not used and NdisAdjustBufferLength when the packet is returned to reset the length to the actual full length of the buffer.

Buffers that are unchained from a packet descriptor should be kept in a free list to ensure they don’t get “lost” and so they are available when needed.

Receiving Data on a Busmaster DMA Device

When a packet or packets is received, the miniport does the following:

1.Calls NdisFlushBuffer and NdisMUpdateSharedMemory to ensure that the incoming data is coherent in the memory area to be accessed by the miniport.

2.Calls NdisAdjustBufferLength to adjust the length in the buffer descriptor field that maps the NIC receive/ring buffer to match the actual length of the data in the buffer before indicating up.  When the packet is returned, the miniport must again call NdisAdjustBufferLength to adjust the length back to the full as-allocated length of the receive/ring buffer.

3.If the miniport indicates up an array of packets by calling NdisMIndicateReceivePacket, it must set the Status member of the OOB block associated with each packet descriptor. If the miniport determines that it wants to force the protocol driver to copy the indicated packet(s) because the miniport is running short of receive buffers, it should set the Status member in the OOB block to NDIS_STATUS_RESOURCES. Otherwise, the miniport should set the Status member to NDIS_STATUS_SUCCESS, NDIS_STATUS_FAILURE, NDIS_STATUS_PENDING, or a driver-determined status.

4.If the miniport indicates up a single packet by calling NdisMXxxIndicateReceive, it indicates all the received data, indicating its full size as the lookahead size.

5.After the packet is indicated up (Step 3) or copied (Step 4), the miniport replaces the buffer descriptors on the ring. The buffer descriptor is replaced either with the buffer descriptor(s) from a previously allocated and currently available packet, with the buffer descriptor(s) from a packet that the protocol driver was forced to copy and was returned, or with a buffer descriptor allocated by calling NdisAllocateBuffer for a buffer descriptor to describe a buffer allocated by calling NdisMAllocateSharedMemoryAsync. Miniports for NICs that receive variable-size frames must chain and unchain packets depending on the amount of incoming data to build varying size packets. Some miniports receive data on a medium that uses fixed-size frames. This kind of miniport can allocate fixed -ize buffers once, chain them to packet descriptor(s), and reuse them without being required to chain and unchain buffers.

If the miniport calls NdisMAllocateSharedMemoryAsync to allocate a buffer, it will be called at its MiniportAllocateComplete function when the allocation is complete.

Receiving Data on a PIO Device

A miniport for a PIO device reads data from the NIC ports using NdisRawReadPortXxx using ports previously claimed with NdisMRegisterIoPortRange. Typically such a device indicates the lookahead portion of a received packet to bound protocol drivers by calling a filter-specific NdisXxxIndicateReceive function and transfers the rest of the data to interested protocol drivers in its MiniportTransferData function. After the data is transferred, the data can be overwritten by new data after receive interrupts are re-enabled.

A miniport for a PIO NIC should insure that new data is not missed because of the time delay in transferring current data to an upper layer protocol driver. If a NIC has a relatively small on-board FIFO buffer, the miniport can preallocate a buffer in its MiniportInitialize function and then stage the received data into this buffer to be indicated up. Staging is done to try to keep the FIFO as empty as possible and available for new incoming data.

Alternatively, the miniport can indicate up data copied directly from the NIC port and move the rest of the received data directly from the NIC when its MiniportTransferData is called. This receive technique incurs a delay equal to the time between when the received data was indicated up by calling NdisXxxIndicateReceive and when MiniportTransferData is called and moves the received data into the buffer provided to MiniportTransferData.

If the miniport calls NdisMIndicateReceivePacket, it transfers the data into a buffer(s) chained to a preallocated packet before it makes the call.

Receiving Data on a Memory-Mapped Device

A miniport that manages a NIC with on-board memory typically transfers the received data in MiniportTransferData directly from the NIC to a buffer supplied in the MiniportTransferData call. Like a PIO NIC miniport, the miniport can stage the receive if there is a possibility that new data will be lost because of the latency incurred by the direct transfer from the NIC to the protocol driver-supplied buffer(s).

The miniport calls NdisMXxxIndicateReceive to indicate up an amount of data equal to the lookahead size or, if the buffer is small and the miniport has the complete buffer, it indicates up the complete buffer. MiniportTransferData calls NdisMoveFromMappedMemory to transfer data from the NIC’s on-board memory to the protocol driver’s packet or to a staging buffer. After the data is transferred, the miniport can reenable interrupts so that new data can be received.

If the miniport calls NdisMIndicateReceivePacket, it transfers the data into a buffer(s) chained to a preallocated packet before it makes the call.