Creating DMA Tags

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.

Note

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

BUS_DMA_ALLOCNOW

Preallocates enough resources to handle at least one buffer-load operation; if sufficient resources are unavailable, ENOMEM is returned.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.243.184