3.2.3.1  Allocating Memory

Generally a miniport should allocate any buffers, packet descriptors and buffer descriptors that it requires to send and receive data at initialization time. The type and amount of memory that must be allocated is dependent on the type of NIC as well as dependent on the type of receive paradigm supported by the driver.

Memory can be shared or unshared. Generally, miniports allocate unshared memory. A driver allocates shared memory only when that memory will be accessed by a busmaster DMA NIC. Miniports for busmaster DMA NICs must allocate shared memory for any buffers that will be accessed both by the miniport and by the NIC.

Memory can be cached or noncached, but noncached memory is a scarce system resource and is, therefore, harder to allocate. A miniport must ensure that any cached memory it uses is coherent before reading received data from the buffer or before sending data from the buffer. For example, a busmaster DMA NIC shares cached memory with the miniport for that NIC. The miniport for such a NIC must flush its buffers to ensure cache coherency on sends and receives.

Cache coherency is no problem for a miniport that manages a slave DMA device because the NDIS calls that such a driver makes do the flush on behalf of the caller. Thus, such a miniport for a slave DMA NIC need not make explicit flush requests.

Cached versus noncached memory is not an issue for miniports for non-DMA NIC types.

Generally, a miniport for a DMA NIC should allocate cached memory. There are exceptions that are noted in Chapter 4, Section 4.6.4.

The functions that can be called to allocate memory include:

·NdisAllocateMemory allocates a buffer whose length is specified by the caller. The memory is not shared, but it can be cached or noncached depending on the caller’s request. The memory can be contiguous and within a particular physical address boundary.

·NdisMAllocateSharedMemory allocates a piece of shared memory of a length specified by the caller. The memory can be cached or noncached. This call returns both a physical address pointer that the NIC uses and a virtual address pointer that the miniport uses. NdisMAllocateSharedMemory must be called after NdisMSetAttributes(Ex) and after NdisMAllocateMapRegisters.

·NdisAllocatePacketPool allocates and initializes a block of nonpaged system memory from which packet descriptors are allocated.

·NdisAllocatePacket allocates a packet descriptor from the packet pool allocated by calling NdisAllocatePacketPool.

·NdisAllocateBufferPool returns a handle with which the caller can allocate buffer descriptors with NdisAllocateBuffer.

·NdisAllocateBuffer allocates memory for a single buffer descriptor from the memory allocated by calling NdisAllocateBufferPool.

A miniport that will indicate received data using NdisMIndicateReceivePacket must indicate that received data mapped by buffer descriptors that are chained to a packet descriptor, which is a structure of type NDIS_PACKET. Therefore, such a miniport, in its MiniportInitialize function calls NdisAllocatePacketPool. It calls NdisAllocatePacket to allocate a packet descriptor for each packet it requires to indicate a receive, NdisAllocateBufferPool and then NdisAllocateBuffer to allocate a buffer descriptor mapping each buffer chained to the packet descriptor, and NdisAllocateSharedMemory or NdisAllocateMemory(Async) to allocate the actual buffers. A driver must create packets by explicitly calling NdisAllocatePacketPool, NdisAllocatePacket, NdisAllocateBufferPool, and NdisAllocateBuffer. It is an error for a driver to create packet descriptors outside of NDIS.

Busmaster DMA NIC miniports typically use NdisMIndicateReceivePacket since they can realize increased performance by allocating and filling many packets at one time. After it has called NdisMSetAttributes(Ex), the miniport for such a device calls NdisMAllocateMapRegisters to reserve some number of map registers and, then, calls NdisMAllocateSharedMemory to allocate shared memory for its receive buffers since the driver shares that memory with the NIC.

Miniports for nonDMA NICs generally use the NdisMXxxIndicateReceive functions for indicating up received data one buffer at a time which requires interested protocols to request and copy any additional received data into buffers the protocol driver supplies. If the miniport for a nonDMA NIC supports media-specific information and priority that must be passed to bound protocols, it must create a complete packet descriptor for the buffered received data and indicate it up by calling NdisMIndicateReceivePacket. In either case, such a miniport allocates nonshared memory for buffers with NdisAllocateMemory since the NIC driver does not share the memory with the NIC. If the NIC driver indicates the entire packet up, it must allocate buffer(s) to stage received data and chain them to a packet descriptor before indicating the packet. If the NIC driver forces interested upper layers to copy the data, it may or may not stage the data into a receive buffer.

A miniport for a busmaster DMA NIC allocates shared memory in its MiniportInitialize function. On a send, the miniport puts the data to be sent into the shared memory; the NIC’s on-board DMA controller reads the data from the host memory and sends it out across the network. On a receive, the DMA controller receives the incoming data into the memory and the miniport indicates the received data to interested protocol drivers. Therefore the memory must be shared between the busmaster DMA controller and the miniport since both can access the memory without the intervention of the other. When incoming data is received by the NIC, transfer of the data to this memory is done by the NIC’s DMA controller without the knowledge or control of the miniport, which is usually notified by a receive interrupt when data is available to be indicated up.

A miniport for a PIO NIC either reads received data from the NIC directly into an interested protocol’s buffer or into a staging buffer it allocates by calling NdisAllocateMemory. The miniport calls NdisRawReadPortXxx to read the data from the port to a receive buffer either in its MiniportTransferData function if the buffer belongs to the protocol or in its MiniportHandleInterrupt function into a staging buffer. On a send, the miniport uses NdisRawWritePortXxx to write the data from the packet sent to its MiniportSend or MiniportSendPackets function into the NIC registers it claimed at initialization time.

When a miniport controls a memory-mapped NIC, the NIC receives incoming data into its on-board memory and signals the miniport; or the miniport writes outgoing data to this on-board memory and causes the device to send the data out on the network. The miniport must therefore map the NIC’s on-board memory to system-space memory on the host so that it can transfer incoming data from the board, and outgoing data to the board. Transfer is done using NdisMoveToMappedMemory and NdisMoveFromMappedMemory. Unlike a busmaster DMA miniport, the memory to which and from which the driver transfers data is part of the NIC, but the driver uses mapped virtual system-space addresses. When data is received, the miniport indicates the receive as one or more virtual ranges mapped by buffer descriptors and when/if its MiniportTransfer function is called, it copies the data from the on-board memory to the protocol driver’s supplied buffer. If no protocol driver transfers the data, it is discarded when the miniport returns from its MiniportHandleInterrupt function or its polling MiniportTimerXxx function. If the miniport uses a staging buffer, it is allocated with NdisAllocateMemory.

A miniport that manages a slave DMA NIC calls NdisAllocateMemory to allocate memory from which it transfers data to the system DMA controller on a send, and to which received data is transferred on a receive.