BUS_DMA(9) FreeBSD Kernel Developer's Manual BUS_DMA(9) NAME bus_dma, bus_dma_tag_create, bus_dma_tag_destroy, bus_dmamap_create, bus_dmamap_destroy, bus_dmamap_load, bus_dmamap_load_mbuf, bus_dmamap_load_uio, bus_dmamap_unload, bus_dmamap_sync, bus_dmamem_alloc, bus_dmamem_free, - Bus and Machine Independent DMA Map- ping Interface SYNOPSIS #include 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_tag_t *dmat); int bus_dma_tag_destroy(bus_dma_tag_t dmat); int bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp); int bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map); int bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf, bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg, int flags); int bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *mbuf, bus_dmamap_callback2_t *callback, void *callback_arg, int flags); int bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, struct uio *ui, bus_dmamap_callback2_t *callback, void *callback_arg, int flags); void bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map); void bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op); int bus_dmamem_alloc(bus_dma_tag_t dmat, void **vaddr, int flags, bus_dmamap_t *mapp); DESCRIPTION Provide a bus- and machine-independent "DMA mapping interface." INTRODUCTION The bus_dma API was adopted from NetBSD when the FreeBSD/Alpha port was under development. The alterations to the original API were aimed to give the underlying implementation more flexibility on how tp represent DMA maps, and DMA tags -- i.e., if the underlying implementation (MD layer) encounters a "no-op" abstraction, it can implement it without allocating per transaction resources. The following list gives information on important changes between the FreeBSD and NetBSD bus_dma interface. NOTES All data structures, function prototypes, and macros will be defined by the port-specific header ~ . Note that this document assumes the existence of types already defined by the current "bus.h" interface. Unless otherwise noted, all function calls in this interface may be defined as cpp(1) macros. DATA TYPES Structures and type definitions used with the bus_dma interface: bus_dma_tag_t A machine-dependent opaque type describing the implementation of DMA for a given bus. It has the following fields: bus_addr_t lowaddr; /* low address restriction */ bus_addr_t highaddr; /* high address restriction */ bus_size_t boundary; /* boundary constraints */ bus_size_t alignment; /* alignment constraints */ bus_dma_tag_t parent; /* parent (main bus) DMA tag */ bus_dma_filter_t *filter; /* filter function */ void *filterarg; /* filter function's args */ bus_size_t maxsize; /* max size of DMA transfers */ u_int nsegments; /* no. of S/G segments */ bus_size_t maxsegsz; /* max size of DMA segments */ int flags; /* various flags */ int ref_count; /* tag usage ref. count */ int map_count; /* assoc. dmamaps ref. count */ The fields displayed above, are explained with detail in the FUNCTIONS section. bus_dma_segment_t A structure with at least the following members: bus_addr_t ds_addr; /* DMA address */ bus_size_t ds_len; /* DMA transfer size */ The structure may have machine-dependent members and arbitrary layout. The ds_len and ds_addr fields can be directly pro- grammed into DMA controllers. bus_dmamap_t A machine-dependent opaque type describing the implementation of a DMA map. It is a pointer to an opaque struct with at least the following members. Note, not all fields of this opaque type are used by the external interface: bus_size_t buflen; /* unmapped buffer length */ void *buf; /* unmapped buffer pointer */ bus_dma_tag_t dmat; /* associated DMA tag */ The structure may have machine-dependent members and arbitrary layout. bus_dmasync_op_t An enumeration, which contains flags to control behaviour of DMA synchronization operations. It has only be displayed, for the sake of completeness: typedef enum { BUS_DMASYNC_PREREAD, BUS_DMASYNC_POSTREAD, BUS_DMASYNC_PREWRITE, BUS_DMASYNC_POSTWRITE, } bus_dmasync_op_t; The flags are described with detail further down. FUNCTIONS bus_dma_tag_create(parent, alignment, boundary, lowaddr, highaddr, *filtfunc, *filtfuncarg, maxsize, nsegments, maxsegsz, flags, *dmat) Allocates a device specific DMA tag, and initializes it accord- ing to the parameters provided: parent Indicates restrictions between the parent bridge, CPU memory, and the device. If NULL, then it is assumed that parent tag is to be created. alignment Specify alignment constraints in bytes (if any) on the target memory region of a DMA (e.g, if the allocation must be aligned at 32 bits, alignment should contain 4). boundary Boundary constraints (if any) on the target DMA memory region, example if the DMA cannot cross a 64K boundary, you would set this to '64K'. sup- plied to this argument has to be a power of 2, and the boundary must be no larger than 64KB (64 * 1024), i.e. BUS_SPACE_MAXSIZE. highaddr High address of the target memory region restricted to the DMA engine of a device from map- ping. This should be set to BUS_SPACE_MAXADDR, but depending on the platform, the high address can be larger than a 32 bit quantity. lowaddr Low address of the target memory region restricted to the DMA engine of a device from mapping, for example, if the device is on the ISA bus, then this would be BUS_SPACE_MAXADDR_24BIT, and BUS_SPACE_MAXADDR_32BIT if on PCI or similar bus. This value can be different depending on the plat- form. When creating child (descendent) tags, lowaddr should be BUS_SPACE_MAXADDR. bus_dma_tag_destroy(dmat) De-allocates a DMA tag created by bus_dma_tag_create(): dmat DMA tag to de-allocate. Returns EBUSY if there are still DMA maps associated with dmat or 0 on success. bus_dmamap_create(dmat, flags, mapp) Allocates a DMA map and initializes it according to the parame- ters provided: tag This is the bus_dma_tag_t passed down from the parent driver via _attach_args. flags Flags are defined as follows: BUS_DMA_BUS[1-4] These flags are placeholders, and may be used by busses to provide bus-dependent functionality. mapp This is a pointer to a bus_dmamap_t. A DMA map will be allo- cated and pointed to by mapp upon successful completion of this routine. Returns 0 on success, or an error code to indicate mode of fail- ure. bus_dmamap_destroy(dmat, map) Frees all resources associated with a given DMA tag. Arguments are as follows: dmat This is the bus_dma_tag_t passed down from the parent driver via _attach_args. map The DMA handle to destroy. In the event that the DMA handle contains a valid mapping, the mapping will be unloaded via the same mechanism used by bus_dmamap_unload(). If given valid arguments, bus_dmamap_destroy() always succeeds. bus_dmamap_load(dmat, map, buf, buflen, p, flags) Loads a DMA handle with mappings for a DMA transfer. It assumes that all pages involved in a DMA transfer are wired. Arguments are as follows: tag This is the bus_dma_tag_t passed down from the parent driver via _attach_args. dmam The DMA handle with which to map the transfer. buf The buffer to be used for the DMA transfer. buflen The size of the buffer. p Used to indicate the address space in which the buffer is located. If NULL, the buffer is assumed to be in kernel space. Otherwise, the buffer is assumed to be in process p's address space. flags are defined as follows: BUS_DMA_WAITOK It is safe to wait (sleep) for resources during this call. BUS_DMA_NOWAIT It is not safe to wait (sleep) for resources during this call. BUS_DMA_BUS[1-4] These flags are placeholders, and may be used by busses to provide bus-dependent functionality. As noted above, if a DMA handle is created with BUS_DMA_ALLOCNOW, bus_dmamap_load() will never block. If a call to bus_dmamap_load() fails, the mapping in the DMA handle will be invalid. It is the responsibility of the caller to clean up any inconsistent device state resulting from incom- plete iteration through the uio. Behavior is not defined if invalid arguments are passed to bus_dmamap_load(). Returns 0 on success, or an error code to indicate mode of fail- ure. bus_dmamap_load_mbuf(tag, dmam, chain, flags) This is a variation of bus_dmamap_load() which maps mbuf chains for DMA transfers. Mbuf chains are assumed to be in kernel vir- tual address space. bus_dmamap_load_uio(tag, dmam, uio, flags) This is a variation of bus_dmamap_load() which maps buffers pointed to by uio for DMA transfers. The value of uio->uio_segflg will determine if the buffers are in user or kernel virtual address space. If the buffers are in user address space, the buffers are assumed to be in uio->uio_procp's address space. bus_dmamap_unload(tag, dmam) Deletes the mappings for a given DMA handle. Arguments are as follows: tag This is the bus_dma_tag_t passed down from the parent driver via _attach_args. dmam The DMA handle containing the mappings which are to be deleted. If the DMA handle was created with BUS_DMA_ALLOCNOW, bus_dmamap_unload() will not free the corresponding resources which were allocated by bus_dmamap_create(). This is to ensure that bus_dmamap_load() will never block on resources if the han- dle was created with BUS_DMA_ALLOCNOW. bus_dmamap_unload() will not perform any implicit synchroniza- tion of DMA buffers. This must be done explicitly by bus_dmamap_sync(). Behavior is not defined if invalid arguments are passed to bus_dmamap_unload(). If given valid arguments, bus_dmamap_unload() always succeeds. bus_dmamap_sync(tag, dmam, offset, len, ops) Performs pre- and post-DMA operation cache and/or buffer syn- chronization. Arguments are as follows: tag This is the bus_dma_tag_t passed down from the parent driver via _attach_args. dmam The DMA mapping to be synchronized. offset The offset into the DMA mapping to synchronize. len The length of the mapping from offset to synchronize. ops One or more synchronization operation to perform. The following DMA synchronization operations are defined: BUS_DMASYNC_PREREAD Perform any pre-read DMA cache and/or bounce operations. BUS_DMASYNC_POSTREAD Perform any post-read DMA cache and/or bounce operations. BUS_DMASYNC_PREWRITE Perform any pre-write DMA cache and/or bounce operations. BUS_DMASYNC_POSTWRITE Perform any post-write DMA cache and/or bounce operations. More than one operation may performed in a given syn- chronization call. Mixing of PRE and POST operations is not allowed, and behavior is undefined if this is attempted. Synchronization operations are expressed from the perspective of the host RAM, e.g., a device -> memory operation is a READ and a memory -> device operation is a WRITE. bus_dmamap_sync() may consult state kept within the DMA map to determine if the memory is mapped in a DMA coherent fashion. If so, bus_dmamap_sync() may elect to skip certain expensive opera- tions, such as flushing of the data cache. See bus_dmamem_map() for more information on this subject. On platforms which implement reordered stores, bus_dmamap_sync() will always cause the store buffer to be flushed. This function exists to ensure that the host and the device have a consistent view of a range of DMA memory, before and after a DMA operation. An example of using bus_dmamamp_sync(), involving multiple read- write use of a single mapping might look like this: bus_dmamap_load(...); while (not done) { /* invalidate soon-to-be-stale cache blocks */ bus_dmamap_sync(..., BUS_DMASYNC_PREREAD); [ do read DMA ] /* copy from bounce */ bus_dmamap_sync(..., BUS_DMASYNC_POSTREAD); /* read data now in driver-provided buffer */ [ computation ] /* data to be written now in driver-provided buffer */ /* flush write buffers and writeback, copy to bounce */ bus_dmamap_sync(..., BUS_DMASYNC_PREWRITE); [ do write DMA ] /* probably a no-op, but provided for consistency */ bus_dmamap_sync(..., BUS_DMASYNC_POSTWRITE); } bus_dmamap_unload(...); This function must be called to synchronize DMA buffers before and after a DMA operation. Other bus_dma functions can not be relied on to do this synchronization implicitly. If DMA read and write operations are not preceded and followed by the If given valid arguments, bus_dmamap_sync() always succeeds. bus_dmamem_alloc(tag, size, alignment, boundary, segs, ...) Allocates memory that is "DMA safe" for the bus corresponding to the given tag. The mapping of this memory is machine-dependent (or "opaque"); machine-independent code is not to assume that the addresses returned are valid in kernel virtual address space, or that the addresses returned are system physical addresses. The address value returned as part of segs can thus not be used to program DMA controller address registers. Only the values in the dm_segs array of a successfully loaded DMA map (using bus_dmamap_load()) can be used for this purpose. If an allocation exceeds PAGE_SIZE, than contiguous memory is used. Arguments are as follows: tag The is the bus_dma_tag_t passed down from the parent driver via _attach_args. size The amount of memory to allocate. alignment Each segment in the allocated memory will be aligned to this value. If the alignment is less than a hard- ware page size, it will be rounded up to the hardware page size. This value must be a power of two. boundary Each segment in the allocated memory must not cross this boundary (relative to zero). This value must be a power of two. A boundary value less than the size of the allocation is invalid. segs An array of bus_dma_segment_t's, filled in as memory is allocated, representing the opaque addresses of the memory chunks. nsegs Specifies the number of segments in segs, and this is the maximum number of segments that the allocated memory may contain. rsegs Used to return the actual number of segments the mem- ory contains. flags Flags are defined as follows: BUS_DMA_WAITOK It is safe to wait (sleep) for resources during this call. BUS_DMA_NOWAIT It is not safe to wait (sleep) for resources during this call. BUS_DMA_BUS[1-4] These flags are placeholders, and may be used by busses to provide bus- dependent functionality. All pages allocated by bus_dmamem_alloc() will be wired down until they are freed by bus_dmamem_free(). Returns 0 on success, or an error code indicating mode of fail- ure. bus_dmamem_free(tag, segs, nsegs) Frees memory previously allocated by bus_dmamem_alloc(). Any mappings will be invalidated. Arguments are as follows: tag This is the bus_dma_tag_t passed down from the parent driver via _attach_args. segs The array of bus_dma_segment_t's filled in by bus_dmamem_alloc(). nsegs The number of segments in segs. bus_dmamem_free(). If given valid arguments, bus_dmamem_free() always succeeds. RETURN VALUES Behaviour is undefined if invalid arguments are passed to the various bus_dma routines. SEE ALSO mbuf(9) uio(9) HISTORY The bus_dma interface appeared in NetBSD 1.3. It was than ported to FreeBSD 4.0 IMPLEMENTATION NOTES If you are porting something from NetBSD which utilizes the bus_dma API, then please note: - DMA bounce threshold support not available - DMA memory mapping (bus_dmamap_mmap) support not available - bus_dmamap_load_raw() support not available AUTHORS The bus_dma interface was designed and implemented by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, NASA Ames Research Center. Additional input on the bus_dma design was provided by Chris Demetriou, Charles Hannum, Ross Harvey, Matthew Jacob, Jonathan Stone, and Matt Thomas. FreeBSD specific information by Hiten Pandya. FreeBSD 5.0 January 18, 2003 FreeBSD 5.0