As mentioned earlier, DMA tags describe the characteristics and restrictions of DMA transactions and are created by using the bus_dma_tag_create
function.
#include <machine/bus.h> int bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment, bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr, bus_dma_filter_t *filtfunc, void *filtfuncarg, bus_size_t maxsize, int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc, void *lockfuncarg, bus_dma_tag_t *dmat);
Here, the parent
argument identifies the parent DMA tag. To create a top-level DMA tag, pass bus_get_dma_tag(device_t dev)
as parent
.
The alignment
argument denotes the physical alignment, in bytes, of each DMA segment. Recall that DMA maps represent memory areas that have been allocated according to the properties of a DMA tag. These memory areas are known as DMA segments. If you return to the foo_callback
function described in Implementing DMA in Implementing DMA, you’ll see that arg
is actually assigned the address of a DMA segment.
The alignment
argument must be 1
, which denotes no specific alignment, or a power of two. As an example, drivers that require DMA buffers to begin on a multiple of 4KB would pass 4096
as alignment
.
The boundary
argument specifies the physical address boundaries that cannot be crossed by each DMA segment; that is, they cannot cross any multiple of boundary
. This argument must be 0
, which indicates no boundary restrictions, or a power of two.
The lowaddr
and highaddr
arguments outline the address range that cannot be employed for DMA. For example, devices incapable of DMA above 4GB would have 0xFFFFFFFF
as lowaddr
and BUS_SPACE_MAXADDR
as highaddr
.
0xFFFFFFFF
equals 4GB, and the constant BUS_SPACE_MAXADDR
signifies the maximum addressable memory for your architecture.
The filtfunc
and filtfuncarg
arguments denote an optional callback function and its first argument, respectively. This function is executed for every attempt to load (or map) a DMA buffer between lowaddr
and highaddr
. If there’s a device-accessible region between lowaddr and highaddr, filtfunc is supposed to tell the system. Here is the function prototype for filtfunc
:
int filtfunc(void *filtfuncarg, bus_addr_t addr)
This function must return 0
if the address addr
is device-accessible or a nonzero value if it’s inaccessible.
If filtfunc
and filtfuncarg
are NULL
, the entire address range from lowaddr
to highaddr
is considered inaccessible.
The maxsize argument denotes the maximum amount of memory, in bytes, that may be allocated for a single DMA map.
The nsegments
argument specifies the number of scatter/gather segments allowed in a single DMA map. A scatter/gather segment is simply a memory page. The name comes from the fact that when you take a set of physically discontinuous pages and virtually assemble them into a single contiguous buffer, you must “scatter” your writes and “gather” your reads. Some devices require blocks of contiguous memory; however sometimes a large enough block is not available. So the kernel “tricks” the device by using a buffer composed of scatter/gather segments. Every DMA segment is a scatter/gather segment.
The nsegments
argument may be BUS_SPACE_UNRESTRICTED
, which indicates no number restriction. DMA tags made with BUS_SPACE_UNRESTRICTED
cannot create DMA maps; they can only be parent tags, because the system cannot support DMA maps composed of an unlimited number of scatter/gather segments.
The maxsegsz
argument denotes the maximum size, in bytes, of an individual DMA segment within a single DMA map.
The flags
argument modifies bus_dma_tag_create
’s behavior. Table 12-1 displays its only valid value.
Table 12-1. bus_dma_tag_create Symbolic Constants
Constant | Description |
---|---|
| Preallocates enough resources to handle at least one buffer-load operation; if sufficient resources are unavailable, |
The lockfunc
and lockfuncarg
arguments denote an optional callback function and its first argument, respectively. Remember how bus_dmamap_load
requires a callback function? Well, lockfunc
executes right before and after that function to acquire and release any necessary synchronization primitives. Here is lockfunc
’s function prototype:
void lockfunc(void *lockfuncarg, bus_dma_lock_op_t op)
When lockfunc executes, op contains either BUS_DMA_LOCK
or BUS_DMA_UNLOCK
. That is, op
dictates what lock operation to perform.
The dmat
argument expects a pointer to bus_dma_tag_t;
assuming bus_dma_tag_create
is successful, this pointer will store the resulting DMA tag.
3.144.243.184