1.3 Intermediate Driver Packet Management
An intermediate driver receives a packet descriptor with one or more chained buffers of data from a higher level driver to send on the network. The intermediate driver can repackage the data to be transmitted in a fresh packet descriptor and simply pass the packet through to the underlying driver by calling NdisSend or NdisSendPackets, or it can take some actions to modify either the contents of the chained buffer(s) or perhaps the ordering or timing of the incoming packet(s) relative to other transmissions. Even if the intermediate driver does nothing other than simply pass on incoming packet(s), for instance if it simply counts the packets, it must allocate a fresh packet descriptor and manage some or all of a new packet structure.
Every intermediate driver must allocate its own packet descriptors to replace those of the overlying driver. If an intermediate driver converts the packet from one format to another, it also can allocate buffer descriptors to map intermediate-allocated buffers into which the converted data is copied. If there is OOB data associated with the packet descriptor being copied, this data can be copied to the new OOB block associated with the intermediate-allocated packet descriptor, using the macro NDIS_OOB_DATA_FROM_PACKET, to obtain a pointer to the OOB data area and, then, calling NdisMoveMemory to move the contents into the OOB area associated with the new packet descriptor. Alternatively, such an intermediate driver can use the NDIS_GET_PACKET_XXX and NDIS_SET_PACKET_XXX macros to read specific items from the OOB data associated with the old packet descriptor and to write the OOB data for the new packet descriptor.
Packet descriptors must be allocated by calling the following NDIS functions:
1.NdisAllocatePacketPool to allocate and initialize a block of nonpaged pool for a caller-specified number of fixed-size packet descriptors
2.NdisAllocatePacket to allocate a packet descriptor from the pool allocated by NdisAllocatePacketPool
Depending on the purpose of the intermediate driver, such a driver can repackage buffers chained to incoming packet descriptors. For instance, an intermediate driver would allocate buffer pool and repackage incoming packet data in the following circumstances:
·The intermediate driver receives a larger data buffer from an overlying protocol driver than can be sent in a single buffer over the underlying medium. Consequently, the intermediate driver must divide the incoming data into smaller buffers.
·The intermediate driver changes the length of the incoming packet data by compressing or encrypting the data before forwarding each send to the underlying driver.
Such buffers can be allocated by calling the following NDIS routines:
1.NdisAllocateBufferPool to obtain a handle with which to allocate buffer descriptors
2.NdisAllocateMemory to allocate a buffer
3.NdisAllocateBuffer to allocate and set up a buffer descriptor to map the buffer allocated by calling NdisAllocateMemory and to chain to the packet descriptor allocated by calling NdisAllocatePacket
Buffer descriptors are chained to a packet descriptor by calling NdisChainBufferAtBack or NdisChainBufferAtFront. The virtual address and the length of the buffer returned by NdisAllocateMemory are passed in the call to NdisAllocateBuffer to initialize the buffer descriptor that maps the buffer.
Packet descriptors to meet typical needs can be allocated as needed, at driver initialization time, or in the ProtocolBindAdapter function. An intermediate driver developer can, if necessary and for performance reasons, allocate a number of packet descriptors and possibly buffers mapped by buffer descriptors at initialization time so ProtocolReceive has preallocated resources into which to copy incoming data and so MiniportSend or MiniportSendPackets has available descriptors (and possibly buffers) for passing on incoming send packets to the next lower driver.
If an intermediate driver copies send data or received data to a new buffer or buffers, and the length of actual data in the last buffer is less than the allocated length of the buffer, the intermediate driver calls NdisAdjustBufferLength to adjust the buffer descriptor to the actual length of the data. When the packet is returned to the intermediate driver, it should again make this call to readjust the length to the full length of its buffer.
An intermediate driver can either receive incoming data from an underlying NIC driver as a complete packet, specified by a packet descriptor of type NDIS_PACKET, at its ProtocolReceivePacket function or have the data indicated to its ProtocolReceive function and copy it into an intermediate-driver-supplied packet. When the intermediate driver has a complete packet indicated to its ProtocolReceivePacket function, it can retain ownership of the packet descriptor and the resources it describes until the received data is consumed and return these resources to the underlying driver later by calling NdisReturnPackets. If ProtocolReceivePacket passes the resources it received to a higher level driver, it must, at a minimum, replace the input packet descriptor with one that the intermediate driver has allocated.
Depending on an intermediate driver's purpose when it receives a full packet from an underlying driver, several packet-management strategies are possible. For example, it can do either of the following:
·Copy the buffered contents into an intermediate driver-allocated buffer mapped and chained to a fresh packet descriptor, return the input packet descriptor to the underlying driver, and, then, indicate the new packet to the higher level driver.
·Create a new packet descriptor and chain the buffers from the indicated packet descriptor to this new descriptor. This new packet descriptor can then be indicated up to the higher level driver. When this higher level driver returns the packet descriptor, the intermediate driver must unchain the buffers from its packet descriptor, chain them to the packet descriptor it originally received from the underlying driver and return the original packet descriptor and the resources it describes to the underlying driver.
Even if the intermediate driver has a ProtocolReceivePacket function, it must also have a ProtocolReceive function. NDIS calls ProtocolReceive whenever the underlying driver will not relinquish ownership of the resources it indicates with a packet descriptor, and the intermediate driver must copy the received data into its own buffers when such an indication occurs. If the underlying driver also indicates out-of-band data with receives, ProtocolReceive can call NdisQueryReceiveInformation to get the out-of-band data associated with the receive indication.
Reusing Packets
As already mentioned, after NdisSend returns NDIS_STATUS_SUCCESS for a packet descriptor submitted by an intermediate driver, such a packet descriptor is returned to ProtocolSendComplete, or an intermediate-driver-allocated packet is returned to MiniportReturnPacket, ownership of that packet descriptor and all the resources it describes is returned to the intermediate driver. If a higher level driver supplied the buffers chained to such a returned packet descriptor, the intermediate driver should return these resources back to the allocating driver promptly.
If the intermediate driver originally allocated the packet descriptor and/or the chained buffers, it can reclaim its resources and use them in subsequent sends or use them for received data that is indicated to ProtocolReceive. It is more efficient for the intermediate driver to reinitialize and reuse the packet descriptors it allocates, and to reuse any intermediate driver-allocated chained buffer descriptors and buffers, than to deallocate and, then, later reallocate these resources.
An intermediate driver reinitializes a packet descriptor by calling NdisReinitializePacket. However, the intermediate driver should first take care to remove any chained buffers and their buffer descriptors by calling NdisUnchainBufferAtXxx. Otherwise, since NdisReinitializePacket will clear the member that points to the chain of buffers, such a call without previously deallocating or saving the chained buffers will cause a memory leak. Similarly, if there is OOB data at MediaSpecificInformation associated with the packet descriptor, this memory must be reclaimed before reinitializing the packet descriptor.
An intermediate driver can choose to individually reinitialize specific members of a packet descriptor if only a subset of the fields are used, rather than calling NdisReinitializePacket.