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.
It is important that ProtocolReceive execute as quickly as possible, so the intermediate driver should have packet descriptors, buffers, and buffer descriptors preallocated for this purpose. ProtocolReceive is usually called because the underlying driver called NdisMXxxIndicateReceive. However, ProtocolReceive is also called if the underlying NIC driver indicated the received data with NdisMIndicateReceivePacket but set the status in the OOB block for an indicated packet descriptor to NDIS_STATUS_RESOURCES. For such packets indicated with NdisMIndicateReceivePacket, but passed to ProtocolReceive, the size of the lookahead buffer is always equal to the total size of the packet. Consequently, the intermediate driver will not call NdisTransferData for such an indication to ProtocolReceive. However, if the underlying NIC driver also indicates OOB data, the intermediate driver must retrieve this information in ProtocolReceive by calling NdisQueryReceiveInformation.
ProtocolReceivePacket examines the packet and if it determines that the packet is intended for one or more of its overlying drivers, it can retain ownership of the packet by returning a nonzero value from ProtocolReceivePacket. If it does, the intermediate driver must subsequently call NdisReturnPackets with a pointer to the corresponding packet descriptor. The driver must make this call for a particular packet descriptor as many times as the nonzero value it returned from ProtocolReceivePacket when it received that packet.
When the intermediate driver calls NdisReturnPackets this number of times, it relinquishes ownership of the packet descriptor and associated buffers back to the underlying driver that originally indicated the receive. Therefore, an intermediate driver should call NdisReturnPackets as quickly as possible.
If, on the other hand, an intermediate driver returns zero from ProtocolReceivePacket, it indicates that it is relinquishing the packet immediately. This could occur, for instance, if the intermediate driver copies the indicated data into buffers of its own and processes the data internally before indicating up to still higher level drivers.
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:
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:
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.
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.
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.
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.