4.4.1 Steps in Sending

The sequence of calls made by any miniport NIC driver to send a packet or packets is dependent to some degree on the type of NIC the miniport manages. However, there are certain steps that miniports for all types of devices have in common. All miniports do the following:

A miniport is not required to check the packet size; rather the miniport should assume that a protocol driver will never send a packet that is too large. The protocol driver queries the miniport during initialization to determine the maximum packet size it supports and is responsible for passing only packets of a size supported by the miniport.

Whether a miniport exports MiniportSendPackets or MiniportSend, a miniport can be passed a packet for which it has no send resources. The miniport can:

  1. Return the packet with a status of NDIS_STATUS_RESOURCES in which case NDIS internally queues the packet and all subsequent packets in a packet array. Later if the miniport completes a send for which it previously returned a pending status, it must call NdisMSendComplete, or, before it completes the send, call NdisMSendResourcesAvailable when the miniport has available send resources. NdisMSendResourcesAvailable can be called only in the window between the return of NDIS_STATUS_PENDING and a call to NdisMSendComplete.

    If a miniport supports full-duplex operation, neither NdisMSendComplete nor NdisMSendResourcesAvailable can be called from MiniportSend or MiniportSendPackets. They may, for instance, be called from MiniportHandleInterrupt when an interrupt is processed. When the miniport indicates that it has available send resources, NDIS will send the the first packet on its send queue to MiniportSend or an array of queued packets to MiniportSendPackets.

  2. The miniport can queue the send request internally. A miniport can use the miniport-reserved fields in the packet header to link queued packets, perhaps linked to its adapter-specific context structure. If the miniport queues a packet, it must return NDIS_STATUS_PENDING as the status for the queued packet. A miniport might implement packet queuing to avoid the latency incurred when the packet is returned to NDIS and later resubmitted to the miniport, resulting in increased throughput. WAN miniport NIC drivers always queue packets, internally. It is an error for a WAN miniport to return NDIS_STATUS_RESOURCES.

In the case of MiniportSend, if the sending protocol driver and the miniport have agreed upon a set of flags that affect how a packet is sent, the miniport should use the Flags passed to MiniportSend to handle the send appropriately. NDIS does not define the meaning or actions associated with the flags

In the case of MiniportSendPackets, the miniport is responsible for sending packets in the priority order if specified by the protocol driver. NDIS does not order packets it receives from a protocol driver but simply passes them on to the miniport in the order they were received.

Sending a Packet on a Busmaster DMA Device

Generally, a miniport that manages a busmaster DMA device exports MiniportSendPackets to get the best performance from its NIC.

A busmaster DMA miniport calls NdisQueryPacket to read the packet length and to obtain a pointer to the first buffer descriptor, as well as the number of buffer descriptors and the physical segment count.

The miniport may need to see if it has available transmit buffers and available transmit buffer descriptors mapping buffers in the ring. If either is unavailable, the miniport either fails the call with NDIS_STATUS_RESOURCES, returning the packet to NDIS or queues the packet internally and returns NDIS_STATUS_PENDING.

A DMA device can have an upper limit on the number of physical segments that it can map for a single DMA operation. If a miniport is given a packet to send that is too fragmented, it must copy the buffers chained to the packet into a single transmit buffer. A miniport should allocate such a staging buffer and a buffer descriptor to map it in MiniportInitialize. A miniport calls NdisMoveMemory to move data from each protocol-supplied buffer into this transmit staging buffer. A miniport must only copy send data when the packet is too fragmented, since copying to a staging buffer is an expensive operation and would adversely affect performance if done too frequently.

As a general guideline, if the buffer to be sent is < 256 bytes, the miniport can get improved performance by copying the data into a staging buffer and passing a miniport-allocated descriptor for the buffer to NdisMStartBufferPhysicalMapping.

A miniport performs the following steps to send a packet using DMA.

  1. Calls NdisMStartBufferPhysicalMapping to map the previously allocated shared memory buffer whose contents will be sent. This call takes a Boolean argument, WriteToDevice, that indicates whether data is being moved from host memory to the NIC or from the NIC to host memory. For a send, WriteToDevice is set to TRUE. The miniport must also supply the index of the map register it is using to map the buffer and this index must be one of the registers returned from calling NdisMAllocateMapRegisters at miniport-initialization time.

  2. If the miniport allocated cached memory for its transmit buffers, it calls NdisFlushBuffer, supplying the address of the buffer being sent. NdisFlushBuffer flushes the processor cache line, ensuring that the memory contents seen by the NIC and the miniport are the same. To ensure cache-coherency on all platforms, the miniport should also call NdisMUpdateSharedMemory.

  3. Programs the NIC for the transmission, for instance, by writing to the control registers.

  4. When the send completes, the miniport calls NdisMCompleteDmaTransfer to release the map register. If the NIC interrupts at the completion of the send, this call is made from MiniportHandleInterrupt. If the NIC is polled, this call is made from the MiniportTimer function supplied to NdisMSetPeriodicTimer or NdisMSetTimer (if the miniport does not use a periodic timer), when the timer expires and the send is complete.

Sending a Single Packet on a PIO Device

MiniportSend or MiniportSendPackets is called with a pointer to a packet descriptor or to an array of pointers to packet descriptors specifying the data to be sent. A miniport that controls a NIC that sends data using programmed I/O performs the following steps in its send function:

  1. Calls NdisQueryPacket to get the length of the packet. Checks that there are transmit resources available to send the packet now. If not, returns NDIS_STATUS_RESOURCES or queues the packet internally and returns from its send handler. If a packet is queued in the miniport, the miniport must return NDIS_STATUS_PENDING for that packet. The miniport should assume that it will always receive a packet of the proper size, that is, the protocol driver will not attempt to send a packet that is larger than the miniport supports. The protocol driver determines this size during system initialization. If the miniport supports OOB data, it reads any relevant data from the OOB block using NDIS macros. If send resources are available, the NIC driver continues.

  2. Calls NdisQueryPacket to get a pointer to the first buffer descriptor in the packet.

  3. Calls NdisQueryBuffer to obtain the virtual address of the buffer containing the data to be sent and the length in bytes of that buffer.

  4. Calls NdisRawWritePortUlong to write the length of the buffer to the port.

  5. Calls NdisRawWritePortBufferUlong to write the buffer whose address was returned from NdisQueryBuffer.

  6. Calls NdisGetNextBuffer to get the next buffer in the packet.

  7. Repeats steps three through six until all the buffers in the packet have been successfully sent.

  8. When MiniportSend is done with a packet, it returns the status as the status of MiniportSend. If the status is not NDIS_STATUS_PENDING, the miniport relinquishes the packet descriptor and the resources it specifies back to the caller. MiniportSendPackets returns the status of the send in the Send member of the OOB block for the packet descriptor. If the miniport returned a pending status for any packet, it must call NdisMSendComplete when it is done with the packet resources and is ready to relinquish it to the caller. If the miniport calls NdisMSendResourcesAvailable to indicate it has resources to accept a new send request; it must make this call before making a call to NdisMSendComplete.

Sending a Packet Using On-Board Memory

A miniport that supports such a device calls NdisMMapIoSpace in MiniportInitialize to map the adapter’s on-board memory onto host memory and to give the miniport a virtual address with which to refer to the memory. A miniport sends data on this type of device by moving the buffers to be sent into the mapped-memory and then writing to the port (or registers) it claimed by calling NdisMRegisterIoPortRange in a device-specific way to cause the NIC to put the data on the medium. If the miniport does not have any send resources available, it either queues the packet(s) or returns the packet(s) to NDIS as described earlier.

If the miniport has send resources available, the sequence of calls it makes is:

The miniport should use the length returned from NdisQueryPacket to make sure that there is enough room in the on-board memory to contain all the buffers in the packet before starting the copy. If the adapter memory does not have enough free space for the current packet, the miniport can return a status of NDIS_STATUS_RESOURCES if there is not enough free on-board memory, in which case, NDIS will queue the packet and resend it later when the miniport indicates that it has available resources. Alternatively, the miniport can return NDIS_STATUS_PENDING, queueing the packet internally.

Sending a Packet on a Slave DMA Device

A slave DMA NIC uses the system DMA controller to transfer data from a packet supplied to its send handler, typically MiniportSend, to the NIC for transmission on the network.

For any packet for which the miniport has send resources, a miniport for a slave DMA NIC does the following:

Generally, the miniport will set up the complete buffer for transfer. However, if the size of the buffer is greater than the DMA constraints of the device, the miniport uses the length and offset parameters to send a subrange of the buffer. If the buffer to be sent is < 256 bytes, the miniport can get improved performance by copying the data into a staging buffer and passing a miniport-allocated descriptor for the buffer to NdisMSetupDmaTransfer.