1.5 Receiving Data in an Intermediate Driver

Underlying NIC drivers can indicate packets in two ways:

An intermediate driver always has a ProtocolReceive function, and it can also have a ProtocolReceivePacket function, depending on the environment in which it runs.

Implementing a ProtocolReceivePacket Handler

When an underlying NIC driver indicates an array of one or more packets, possibly with associated OOB data, by calling NdisMIndicateReceivePacket, NDIS will usually call a bound intermediate driver’s ProtocolReceivePacket with each packet descriptor, allowing the intermediate driver to retain the resources specified by the packet descriptor and to consume the data before returning it. Two kinds of NIC drivers typically call NdisMIndicateReceivePacket with an array of packets:

  1. A NIC driver managing a busmaster DMA adapter that is capable of receiving several network packets into a ring of buffers.

  2. A NIC driver that provides out-of-band data containing media-specific information, such as packet priority, to higher level drivers in the NDIS_PACKET_OOB_DATA block associated with the packet descriptor. Such a driver need not be a driver for a busmaster DMA device.

If an intermediate driver is aware that it is (or might be) bound to such a NIC driver, it should, as mentioned earlier, have a ProtocolReceivePacket function. This allows the intermediate driver to all of the following:

  1. To receive a full network packet at every receive indication,

  2. To use NDIS macros to read the OOB data associated with the packet descriptor, rather than calling NdisQueryReceiveInformation to receive and copy the data,

  3. To retain ownership of incoming packet descriptors and direct read-only access to the buffered data specified by these descriptors, possibly making copies of the data for its clients, and then, when done with each packet descriptor,

  4. To return the packet descriptor and the resources it describes, possibly along with other retained packet descriptors, with NdisReturnPackets.

Even when an intermediate driver provides a ProtocolReceivePacket handler, there are cases when a call by a NIC driver to NdisMIndicateReceivePacket results in a call to an intermediate driver’s ProtocolReceive handler. Since a NIC driver temporarily relinquishes ownership of driver-allocated resources when it calls NdisMIndicateReceivePacket, the underlying driver is dependent on the consumers of those packets to return them with NdisReturnPackets in a timely manner. Otherwise, such a NIC driver can run short of receive resources, such as receive buffer space in the NIC. When it does, the NIC driver writes a status of NDIS_STATUS_RESOURCES into the OOB block associated with a packet descriptor in the packet array it passes to NdisMIndicateReceivePacket. Indicating a packet with this status causes NDIS to call the overlying driver's ProtocolReceive function with such a packet and with any subsequent packets in the array, thus forcing the intermediate driver to copy the packet data rather than taking ownership.

If the intermediate driver requires the OOB data associated with a packet descriptor but is called at ProtocolReceive, it must call NdisQueryReceiveInformation to copy the media-specific information into an intermediate-driver-supplied buffer and possibly the TimeSent and the TimeReceived if the underlying NIC driver provides these timestamps.

Implementing a ProtocolReceive Handler

If the NIC driver calls NdisMXxxIndicate, ProtocolReceive is always called, and if the intermediate driver accepts the packet, ProtocolReceive must call NdisTransferData with a packet descriptor into which the lookahead buffer has been copied and into which the rest of the packet is copied. NdisTransferData must be called in the context of ProtocolReceive, and can only be called once. It is the responsibility of the intermediate driver to set up a packet descriptor with chained buffers of a sufficient size to contain all the received data. After NdisTransferData returns, the received data is no longer available from the underlying NIC driver.

If the data passed to ProtocolReceive was indicated by a call to NdisMXxxIndicateReceive, the size of the lookahead buffer passed to ProtocolReceive is <= the size returned by a call to NdisRequest with OID_GEN_CURRENT_LOOKAHEAD. All data in the lookahead buffer is read-only to the intermediate driver. If the call to ProtocolReceive occurred because the underlying NIC driver set the status of one or more packets in a packet array to NDIS_STATUS_RESOURCES before calling NdisMIndicateReceivePacket, the size of the lookahead buffer will always be equal to the size of the full network packet so the intermediate driver need not call NdisTransferData.

ProtocolReceive must return control as quickly as possible. The intermediate driver should insure that it has packet descriptors, buffer descriptors and buffers available before it gets receive indications. If the intermediate driver examines the lookahead data and determines that the packet is not one it will copy, the intermediate driver should return NDIS_STATUS_NOT_ACCEPTED.

ProtocolReceive must not process received data as it is copied since that would adversely impact system performance, as well as the ability of the underlying NIC to accept incoming receives from the network. Instead, the intermediate driver processes the received data later in its ProtocolReceiveComplete function, which is called subsequently when the packets can be postprocessed. Typically, this occurs when the underlying NIC driver has received and indicated a NIC-driver-determined number of packets or before it exits its DPC-level receive handler. The intermediate driver must queue the copied net packets in ProtocolReceive so that they are available to ProtocolReceiveComplete for postprocessing.

Accessing OOB Information

If a received network packet is indicated to ProtocolReceive, it forces the driver to copy the received data into an intermediate-driver-supplied buffer. If the packet contains media-specific and/or timestamp information in the OOB data associated with that packet descriptor, an intermediate driver calls NdisQueryReceiveInformation to retrieve the media-specific information, as well as TimeSent and TimeReceived if such information is provided by the underlying NIC driver.

If a received packet is passed to ProtocolReceivePacket, the intermediate driver must obtain the information from the OOB data associated with the packet using NDIS-supplied macros as follows:

TimeSent is the time a packet was sent by the NIC on the remote node, and is retrieved and stored by the underlying NIC driver if available. TimeReceived is the time that the incoming packet was received on the underlying NIC.

Passing Packets to Higher Level Drivers

After an intermediate driver has processed a received packet, perhaps converted it to the format expected by a higher level driver, and copied relevant data into buffers chained to an intermediate-driver-allocated packet descriptor, the packet is indicated to the next higher driver as if the intermediate driver were a miniport. Certain restrictions are imposed on an intermediate driver when it is acting as a miniport, as already described in Section 1.4.