2.5  Sending Protocol Driver-Originated Packets

A protocol driver can transmit a single packet by calling NdisSend, passing in a pointer to a packet descriptor with chained buffer descriptors mapping the buffered data to be sent. Alternatively, a protocol driver can transmit several packets using NdisSendPackets, passing in a pointer to an array of pointer(s) to one or more packet descriptors.

In general, a protocol driver developer should choose whether to call NdisSend or NdisSendPackets based upon the driver’s own requirements and on the characteristics of the underlying NIC driver.

When it has bound itself to an underlying NIC driver, any protocol driver should call NdisRequest with an OID_GEN_MAXIMUM_SEND_PACKETS query to determine the maximum number of send packets that the underlying driver will accept in a packet array. If the NIC driver supports only single-packet sends, either at its MiniportSend or its MiniportSendPackets function, the return value will be one or, possibly NDIS_STATUS_NOT_SUPPORTED. Either of these returns implies the protocol driver is likely to call NdisSend rather than NdisSendPackets. If the underlying driver returns a value greater than one, both drivers' performance will be better if the protocol driver sends an array of packets with NdisSendPackets. If OOB information is passed between the protocol driver and the NIC driver, either NDIS function can be called, since, in either case, the underlying driver can read the OOB data using NDIS-supplied macros

Whenever a protocol driver calls NdisSend, it relinquishes ownership of the given packet resources until the send completes, either synchronously or asynchronously. If the status returned by NdisSend is something other than NDIS_STATUS_PENDING, the send completes synchronously and ownership of the protocol-allocated packet resources reverts to the protocol driver. If the status returned by NdisSend is NDIS_STATUS_PENDING, when the send subsequently completes, the final status of the send and the protocol-allocated packet descriptor will be passed in to the ProtocolSendComplete function.

When a protocol driver transmits one or more packet(s) by calling NdisSendPackets, send operations are always asynchronous. The protocol driver relinquishes ownership of the packet resources that it allocated until each packet descriptor and the final status of the send for that packet is returned to ProtocolSendComplete.

As a consequence, a protocol driver never reads the Status member in the OOB block associated with a packet descriptor on return from NdisSend(Packets). The protocol cannot learn the status of its send request in this manner because this member is in use by NDIS to track the progress of an in-transition send request and is volatile until the packet descriptor is returned to ProtocolSendComplete. A protocol driver always obtains the status of a transmit request either by examining the value returned by NdisSend or from the Status parameter passed to ProtocolSendComplete.

If a protocol driver requests the transmission of an array of packets of different priorities by arranging the packets it receives from clients before transmitting them, the protocol should place the highest priority packets at the beginning of the array. NDIS always preserves the ordering of packets in any array passed to NdisSendPackets, even if NDIS queues some of the packets internally.

NDIS does not attempt to examine and make queueing decisions based on any the OOB data associated with the packet descriptors given to NdisSendPackets (or to NdisSend). Unless a protocol driver has special knowledge of the manner in which the underlying NIC driver handles packet priorities or the TimeToSend timestamps, the protocol should assume that the underlying NIC driver transmits all packets in the order in which it receives them, preserving the as-received order. Consequently, a protocol should order the packet arrays it sends according to the order in which those packets should be transmitted over the network.

Passing Media-Specific Information

Before sending a packet, a protocol driver can call NdisSetPacketFlags to set protocol-defined flags in the NDIS-private portion of the packet descriptor. Such flags are not defined by NDIS, but are defined for use by a cooperating pair of protocol and lower level NDIS drivers. The structure of the Private member of an NDIS_PACKET is opaque to all NDIS drivers and is accessed to read and, in some cases, to write using NDIS-supplied functions or macros.

More media-specific information can be passed by a protocol driver in the OOB block associated with each NDIS_PACKET-type descriptor. The definition of the OOB block is:

typedef struct _NDIS_PACKET_OOB_DATA {
   union {
        ULONGLONG      TimeToSend;
        ULONGLONG      TimeSent;
         };
        ULONGLONG       TimeReceived;
        UINT                      HeaderSize;
        UINT                      SizeMediaSpecificInfo;
        PVOID                   MediaSpecificInformation;
        NDIS_STATUS      Status;
} NDIS_PACKET_OOB_DATA, *PNDIS_PACKET_OOB_DATA;

The structure of individual records within a driver-allocated buffer at MediaSpecificInformation is defined as follows:

typedef struct MediaSpecificInformation {
   UINT                           NextEntryOffset;
   NDIS_CLASS_ID         ClassId;
   UINT                           Size;
   UCHAR                       ClassInformation[1];
} MEDIA_SPECIFIC_INFORMATION;

The ClassId member is an NDIS-defined enumeration that defines the type of information found at ClassInformation[1]. Currently, there are two class IDs in use across Microsoft operating systems that support Win32, NdisClass802_3Priority and NdisClassWirelessWanMbxMailbox. See the NetworkReference for details.

If the protocol driver knows that the underlying NIC driver to which it is sending packets uses OOB data, the protocol can set the following OOB structure members:

·Request that the packet be sent at a specific time by setting the TimeToSend member using the NDIS_SET_PACKET_TIME_TO_SEND macro. This macro passes the requested time in system time units. The protocol can call NdisGetCurrentSystemTime in order to obtain the current system time with which to calculate a requested send time.

·Pass media-specific information in a protocol-allocated buffer at MediaSpecificInformation using the NDIS_PACKET_SET_MEDIA_SPECIFIC_INFO macro to set this member to the address of the buffer. For instance, if a protocol driver is bound to an underlying NIC that requires priority, it will set the ClassId member of the MediaSpecificInformation structure to NdisClass802_3Priority, and pass priority-related information in the ClassInformation member and the size in bytes of this information in Size. The protocol driver is responsible for allocating a buffer to contain any media-specific data record(s) and for setting up the pointer to this buffer at MediaSpecificInformation.