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:
-
Call NdisQueryPacket to get a pointer
to the first buffer descriptor in the packet.
-
Call NdisQueryBuffer to get the base
virtual address for the buffer whose buffer descriptor was returned from NdisQueryPacket.
-
Call NdisGetNextBuffer to get the
next buffer descriptor in a chain of descriptors in a packet.
-
Call NdisRawWritePortXxx to at least program the NIC and
possibly to move the data.
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:
-
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.
-
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.
-
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.
-
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.
-
Programs the NIC for the transmission, for instance, by writing to the control
registers.
-
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:
-
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.
-
Calls NdisQueryPacket to get a pointer
to the first buffer descriptor in the packet.
-
Calls NdisQueryBuffer to obtain the
virtual address of the buffer containing the data to be sent and the length in
bytes of that buffer.
-
Calls NdisRawWritePortUlong to
write the length of the buffer to the port.
-
Calls NdisRawWritePortBufferUlong
to write the buffer whose address was returned from NdisQueryBuffer.
-
Calls NdisGetNextBuffer to get the
next buffer in the packet.
-
Repeats steps three through six until all the buffers in the packet have been
successfully sent.
-
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:
-
NdisQueryPacket to get the start and
length of the packet containing the buffers to be sent.
-
NdisQueryBuffer to get the buffer
descriptor.
-
NdisMoveToMappedMemory to move
a buffer at a time to the mapped adapter memory buffer.
-
NdisGetNextBuffer to retrieve the
next buffer from the packet and again, call NdisMoveToMappedMemory.
Repeat these two calls until all the buffers have been transferred.
-
NdisMRawWritePortXxx or possibly NdisWriteRegisterXxx
to cause the send to occur in a device-specific manner.
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:
-
Calls NdisMSetupDmaTransfer to
set up the system DMA controller for the send. The miniport passes the handle
to a DMA channel it allocated during miniport initialization by calling NdisMRegisterDmaChannel,
a pointer to the host memory buffer containing the data to be sent, an offset
into the buffer, the length of data, and a Boolean that indicates the
direction of the transfer; in this case, from host memory to the device. On
return from NdisMSetupDmaTransfer, the controller is programmed to
transfer the data including ensuring that the buffer is cache-coherent.
-
Performs device-dependent steps to cause the transfer, for instance by calling
NdisRawWritePortXxx.
-
When the transfer is complete, the miniport calls NdisMCompleteDmaTransfer.
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.