1.5 Receiving Data in an Intermediate Driver
Underlying NIC drivers can indicate packets in two ways:
·A NIC driver calls the non-filter-specific NdisMIndicateReceivePacket, passing a pointer to an array of pointer(s) to one or more packet descriptors, relinquishing ownership of the indicated packet resources to higher level drivers. When the higher level driver(s) have consumed the data, they return the packet descriptor(s) (and the resources they specify) to the NIC driver.
·A NIC driver calls a filter-specific NdisMXxxIndicateReceive function, passing a pointer to a lookahead buffer and the total size of the packet.
An intermediate driver always has a ProtocolReceive function, and it can also have a ProtocolReceivePacket function, depending on the environment in which it runs.
·ProtocolReceive is a required function that receives a pointer to a lookahead buffer. If, after the intermediate driver examines the lookahead data, it determines that the packet is intended for one or more of its overlying drivers, it must copy the data into a previously allocated packet which it will indicate to its overlying driver(s). If the size of the lookahead buffer is less than the total size of the received packet the intermediate driver must first call NdisTransferData in the context of ProtocolReceive, to copy the rest of the received data.
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 is an optional function that receives a pointer to packet descriptor that always describes a full network packet. If any underlying NIC might be a busmaster DMA device, its driver will most likely indicate received packets by calling the non-filter-specific NdisMIndicateReceivePacket, and the intermediate driver should have a ProtocolReceivePacket function if it might bind itself to such an underlying NID driver. In addition, any underlying NIC driver that supports OOB data will most likely pass a packet descriptor on receives to NdisMIndicateReceivePacket so that the intermediate driver can access the OOB data associated with the packet descriptor.
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.
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:
·Media-specific information is read using NDIS_GET_MEDIA_SPECIFIC_INFO and written using NDIS_SET_MEDIA_SPECIFIC_INFO
·TimeSent is read using NDIS_GET_TIME_SENT and written using NDIS_SET_TIME_TO_SEND
·TimeReceived is read using NDIS_GET_TIME_RECEIVED
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.