2.2  Protocol Driver Packet Management

A protocol driver receives one or more buffers of data from a client to transmit over the network. A protocol driver must, at a minimum, allocate and initialize a packet descriptor to which the client’s data buffers are chained. Packet descriptors must be allocated from packet pool, as follows:

1.Call NdisAllocatePacketPool to allocate and initialize a block of nonpaged pool for a caller-specified number of fixed-size packet descriptors during driver initialization or when each binding is first established.

2.Call NdisAllocatePacket to allocate a packet descriptor from the pool allocated by NdisAllocatePacketPool.

Buffer descriptor(s) mapping such buffer(s) are chained to a packet descriptor by calling NdisChainBufferAtBack or NdisChainBufferAtFront. If a protocol driver receives a data buffer from a client that must be sent in several smaller buffers, the protocol driver can copy the data into protocol-allocated buffers, map its buffers with previously allocated buffer descriptors, and chain these buffer descriptors to protocol-allocated packet descriptors. Such buffers can be allocated by calling a kernel-mode support routine or NdisAllocateMemory and can be mapped with protocol-allocated buffer descriptors as follows:

1.Call NdisAllocateBufferPool to obtain a handle with which to allocate buffer descriptors during driver initialization or when each binding is first established.

2.Call NdisAllocateMemory to allocate a buffer to chain to a packet descriptor allocated by calling NdisAllocatePacket.

3.Call NdisAllocateBuffer to allocate and set up a buffer descriptor that maps the buffer allocated by calling NdisAllocateMemory.

The base virtual address and the length returned by NdisAllocateMemory are passed in the call to NdisAllocateBuffer to initialize a buffer descriptor.

Packet descriptors to meet typical transmit needs can be allocated as needed, when the driver initializes, and/or at binding time. A protocol driver developer can allocate a number of packet descriptors with chained buffer descriptors at initialization time to hold receives that can occur as soon as the protocol binds itself to an underlying NIC driver. Then, ProtocolReceive can return control to the underlying driver as quickly as possible. Otherwise, subsequently received data can be lost.

As described later, a protocol driver can either receive incoming data from an underlying NIC driver at its ProtocolReceivePacket function as a packet descriptor specifying a full network packet or have data indicated to its ProtocolReceive function, which must copy the indicated data into a protocol-supplied buffer chained to a protocol-allocated packet descriptor.

Every protocol driver must provide a ProtocolReceive function. When this function is called, the protocol driver must copy the indicated lookahead data into a protocol-allocated buffer chained to a preallocated packet descriptor, which it must pass to NdisTransferData. This call passes the packet descriptor to the underlying NIC driver to be filled if there is additional data received beyond the indicated lookahead data.

If a protocol driver binds itself only to underlying NDIS drivers that indicate arrays of packet(s) with NdisMIndicateReceivePackets, ProtocolReceivePacket need not provide packet descriptors, buffers, and buffer descriptors for incoming data. When the protocol driver has a full network packet indicated to its ProtocolReceivePacket function, it can give TDI client(s) direct read-only access to the buffered data described by an input packet descriptor until the data is consumed and the client(s) release the packet descriptor and all the resources it specifies by calling TdiReturnChainedReceives. When the protocol driver takes ownership of such packet resources, it has no need to copy data into a preallocated packet and call NdisTransferData to obtain the remaining packet data.

If a protocol driver supplies a packet descriptor with more than one chained buffer to NdisTransferData or NdisSend(Packets) and the length of actual data in the last buffer is less than the allocated length of its buffer, the protocol driver should call NdisAdjustBufferLength to set the actual length of the data in the buffer descriptor. When the packet descriptor is returned to the protocol driver, the driver should readjust the buffer descriptor’s specified length to the full length of the buffer.

Reusing Packets

Ownership of protocol-allocated packet resources for sends reverts to the protocol driver when NdisSend returns anything other than NDIS_STATUS_PENDING or when the driver's ProtocolSendComplete function is called. Then, the protocol driver can reclaim the returned packet resources for subsequent sends or for copying received data in ProtocolReceive.

It is more efficient for a protocol driver to reinitialize and reuse the packet descriptor and any chained buffer descriptors and buffers than to deallocate these resources and, then, later reallocate them again for a subsequent send or data-transfer operation. A protocol usually exhibits better performance if it saves unchained buffer descriptors and buffers for reuse, rather than deallocating and reallocating such resources.

A protocol driver reinitializes a packet descriptor by calling NdisReinitializePacket. First, the protocol driver should take care to remove any chained buffers and their buffer descriptors by calling NdisUnchainBufferAtXxx to release the buffer descriptors and the buffers mapped by these descriptors. Otherwise, NdisReinitializePacket sets the member that points to the chained buffer descriptor(s) to NULL, so reinitializing the packet descriptor without first releasing and saving the chained buffer descriptors will cause a memory leak. Similarly, if the protocol and underlying driver use out-of-band information, the resources specified in the OOB data block associated with each packet descriptor must be reclaimed before a call to NdisReinitializePacket.

A protocol driver can choose to individually reinitialize specific driver-accessible members of a packet descriptor if only a subset are read and will be written subsequently, rather than calling NdisReinitializePacket.