| BUS_DMA(9) | Kernel Developer's Manual | BUS_DMA(9) |
bus_dma,
bus_dmamap_create,
bus_dmamap_destroy,
bus_dmamap_load,
bus_dmamap_load_mbuf,
bus_dmamap_load_uio,
bus_dmamap_load_raw,
bus_dmamap_unload,
bus_dmamap_sync,
bus_dmamem_alloc,
bus_dmamem_free,
bus_dmamem_map,
bus_dmamem_unmap,
bus_dmamem_mmap,
bus_dmatag_subregion,
bus_dmatag_destroy — Bus and
Machine Independent DMA Mapping Interface
#include
<sys/bus.h>
int
bus_dmamap_create(bus_dma_tag_t
tag, bus_size_t size, int
nsegments, bus_size_t maxsegsz,
bus_size_t boundary, int flags,
bus_dmamap_t *dmamp);
void
bus_dmamap_destroy(bus_dma_tag_t
tag, bus_dmamap_t dmam);
int
bus_dmamap_load(bus_dma_tag_t
tag, bus_dmamap_t dmam, void
*buf, bus_size_t buflen, struct
proc *p, int flags);
int
bus_dmamap_load_mbuf(bus_dma_tag_t
tag, bus_dmamap_t dmam, struct
mbuf *chain, int flags);
int
bus_dmamap_load_uio(bus_dma_tag_t
tag, bus_dmamap_t dmam, struct
uio *uio, int flags);
int
bus_dmamap_load_raw(bus_dma_tag_t
tag, bus_dmamap_t dmam,
bus_dma_segment_t *segs, int
nsegs, bus_size_t size, int
flags);
void
bus_dmamap_unload(bus_dma_tag_t
tag, bus_dmamap_t dmam);
void
bus_dmamap_sync(bus_dma_tag_t
tag, bus_dmamap_t dmam,
bus_addr_t offset, bus_size_t
len, int ops);
int
bus_dmamem_alloc(bus_dma_tag_t
tag, bus_size_t size, bus_size_t
alignment, bus_size_t boundary,
bus_dma_segment_t *segs, int
nsegs, int *rsegs, int
flags);
void
bus_dmamem_free(bus_dma_tag_t
tag, bus_dma_segment_t *segs,
int nsegs);
int
bus_dmamem_map(bus_dma_tag_t
tag, bus_dma_segment_t *segs,
int nsegs, size_t size,
void **kvap, int flags);
void
bus_dmamem_unmap(bus_dma_tag_t
tag, void *kva, size_t
size);
paddr_t
bus_dmamem_mmap(bus_dma_tag_t
tag, bus_dma_segment_t *segs,
int nsegs, off_t off,
int prot, int flags);
int
bus_dmatag_subregion(bus_dma_tag_t
tag, bus_addr_t min_addr,
bus_addr_t max_addr, bus_dma_tag_t
*newtag, int flags);
void
bus_dmatag_destroy(bus_dma_tag_t
tag);
Provide a bus- and machine-independent “DMA mapping interface”.
All data types and constants will be defined by the port-specific
header <machine/bus_defs.h>.
All functions will be defined by the port-specific header
<machine/bus_funcs.h>. Note
that this document assumes the existence of types already defined by the
current bus_space(9)
interface.
Unless otherwise noted, all function calls in this interface may be defined as cpp(1) macros.
Individual implementations may name these structures whatever they wish, providing that the external representations are:
bus_addr_t ds_addr; bus_size_t ds_len;
The structure may have machine-dependent members and arbitrary layout. The values in ds_addr and ds_len are suitable for programming into DMA controller address and length registers.
bus_size_t dm_maxsegsz; bus_size_t dm_mapsize; int dm_nsegs; bus_dma_segment_t *dm_segs;
The structure may have machine-dependent members and arbitrary layout. The dm_maxsegsz member indicates the maximum number of bytes that may be transferred by any given DMA segment. The dm_mapsize member indicates the size of the mapping. A value of 0 indicates the mapping is invalid. The dm_segs member may be an array of segments or a pointer to an array of segments. The dm_nsegs member indicates the number of segments in dm_segs.
bus_dmamap_create(tag,
size, nsegments,
maxsegsz, boundary,
flags, dmamp)BUS_DMA_WAITOKBUS_DMA_NOWAITBUS_DMA_ALLOCNOWbus_dmamap_load(). If this flag is
specified, bus_dmamap_load() will not
block on resource allocation.BUS_DMA_BUS[1-4]Behavior is not defined if invalid
arguments are passed to
bus_dmamap_create().
Returns 0 on success, or an error code to indicate mode of failure.
bus_dmamap_destroy(tag,
dmam)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().
Behavior is not defined if invalid
arguments are passed to
bus_dmamap_destroy().
If given valid arguments,
bus_dmamap_destroy()
always succeeds.
bus_dmamap_load(tag,
dmam, buf,
buflen, p,
flags)NULL, the buffer is assumed to be in kernel
space. Otherwise, the buffer is assumed to be in proc
p's address space.BUS_DMA_WAITOKBUS_DMA_NOWAITBUS_DMA_STREAMINGbus_dma API assumes that
there is coherency between memory and the device performing the
DMA transaction. Some platforms, however, have special hardware,
such as an “I/O cache”, which may improve
performance of some types of DMA transactions, but which break the
assumption that there is coherency between memory and the device
performing the DMA transaction. This flag allows the use of this
special hardware, provided that the device is doing sequential,
unidirectional transfers which conform to certain alignment and
size constraints defined by the platform. If the platform does not
support the feature, or if the buffer being loaded into the DMA
map does not conform to the constraints required for use of the
feature, then this flag will be silently ignored. Also refer to
the use of this flag with the
bus_dmamem_alloc() function.BUS_DMA_READBUS_DMA_WRITEBUS_DMA_BUS[1-4]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 incomplete 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 failure. Possible error codes include the following:
EFBIGEINVALENOMEMbus_dmamap_load_mbuf(tag,
dmam, chain,
flags)bus_dmamap_load() which
maps mbuf chains for DMA transfers. Mbuf chains are assumed to be in
kernel virtual address space.bus_dmamap_load_uio(tag,
dmam, uio,
flags)bus_dmamap_load() which
maps buffers pointed to by uio for DMA transfers.
Determination if the buffers are in user or kernel virtual address space
is done internally, according to
uio->uio_vmspace.
See uiomove(9) for details
of the uio structure.bus_dmamap_load_raw(tag,
dmam, segs,
nsegs, size,
flags)bus_dmamap_load() which
maps buffers allocated by bus_dmamem_alloc() (see
below). The segs argument is an array of
bus_dma_segment_t's filled in by
bus_dmamem_alloc(). The
nsegs argument is the number of segments in the
array. The size argument is the size of the DMA
transfer.bus_dmamap_unload(tag,
dmam)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 handle was created with
BUS_DMA_ALLOCNOW.
bus_dmamap_unload()
will not perform any implicit synchronization of DMA buffers. This must
be done explicitly by bus_dmamap_sync().
bus_dmamap_unload()
will restore the dm_maxsegsz member to its initial
value assigned by bus_dmamap_create().
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)BUS_DMASYNC_PREREADBUS_DMASYNC_POSTREADBUS_DMASYNC_PREWRITEBUS_DMASYNC_POSTWRITEwhere each operation may involve cache flush/invalidation, bounce buffer copying, and/or memory barriers.
More than one operation may be performed in a given
synchronization 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, i.e., 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 operations, such as flushing of the data cache. See
bus_dmamem_map() for more information on this
subject.
On platforms which implement a weak
memory access ordering model,
bus_dmamap_sync()
will always cause the appropriate memory barriers to be issued.
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_dmamap_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 appropriate
synchronization operations, behavior is undefined.
Behavior is not defined if invalid
arguments are passed to
bus_dmamap_sync().
If given valid arguments,
bus_dmamap_sync()
always succeeds.
bus_dmamem_alloc(tag,
size, alignment,
boundary, segs,
nsegs, rsegs,
flags)The mapping of this memory is
machine-dependent (or “opaque”); machine-independent code
must not 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.
Allocations will always be rounded to the hardware page size. Callers may wish to take advantage of this, and cluster allocation of small data structures.
BUS_DMA_WAITOKBUS_DMA_NOWAITBUS_DMA_STREAMINGBUS_DMA_STREAMING flag with
the
bus_dmamap_load()
function. If the platform does not support the
BUS_DMA_STREAMING feature, or if the size,
alignment, and boundary constraints would already satisfy the
platform's requirements, this flag is silently ignored. The
BUS_DMA_STREAMING flag will never relax
the constraints specified in the call.BUS_DMA_BUS[1-4]All pages allocated by
bus_dmamem_alloc()
will be wired down until they are freed by
bus_dmamem_free().
Behavior is undefined if invalid
arguments are passed to
bus_dmamem_alloc().
Returns 0 on success, or an error code indicating mode of failure.
bus_dmamem_free(tag,
segs, nsegs)bus_dmamem_alloc(). Any mappings will be
invalidated.
bus_dmamem_alloc().Behavior is undefined if invalid
arguments are passed to
bus_dmamem_free().
If given valid arguments,
bus_dmamem_free()
always succeeds.
bus_dmamem_map(tag,
segs, nsegs,
size, kvap,
flags)bus_dmamem_alloc() into
kernel virtual address space.
bus_dmamem_alloc(), representing the memory
regions to map.BUS_DMA_WAITOKBUS_DMA_NOWAITBUS_DMA_BUS[1-4]BUS_DMA_COHERENTLater, when this memory is
loaded into a DMA map, machine-dependent code will take whatever
steps are necessary to determine if the memory was mapped in a
DMA coherent fashion. This may include checking if the kernel
virtual address lies within uncached address space or if the
cache-inhibit bits are set in page table entries. If it is
determined that the mapping is DMA coherent, state may be placed
into the DMA map for use by later calls to
bus_dmamap_sync().
Note that a device driver must
not rely on BUS_DMA_COHERENT for correct
operation. All calls to
bus_dmamap_sync()
must still be made. This flag is provided only as an
optimization hint to machine-dependent code.
Also note that this flag only
applies to coherency between the CPU and memory. Coherency
between memory and the device is controlled with a different
flag. See the description of the
bus_dmamap_load()
function.
BUS_DMA_NOCACHEExclusive with
BUS_DMA_PREFETCHABLE.
BUS_DMA_PREFETCHABLEExclusive with
BUS_DMA_NOCACHE.
Behavior is undefined if invalid arguments
are passed to
bus_dmamem_map().
Returns 0 on success, or an error code indicating mode of failure.
bus_dmamem_unmap(tag,
kva, size)bus_dmamem_map(), freeing the kernel virtual
address space used by the mapping.
Behavior is undefined if invalid
arguments are passed to
bus_dmamem_unmap().
If given valid arguments,
bus_dmamem_unmap()
always succeeds.
bus_dmamem_mmap(tag,
segs, nsegs,
off, prot,
flags)(*d_mmap)()
entry point, which is called by the device pager for each page to be
mapped.
bus_dmamem_alloc(), representing the memory to
be mmap(2)'ed.BUS_DMA_WAITOKBUS_DMA_NOWAITBUS_DMA_BUS[1-4]BUS_DMA_COHERENTbus_dmamem_map() above for a
description of this flag.BUS_DMA_NOCACHEbus_dmamem_map() above for a
description of this flag.Behavior is undefined if invalid
arguments are passed to
bus_dmamem_mmap().
Returns -1 to indicate failure. Otherwise, returns an opaque value to be interpreted by the device pager.
bus_dmatag_subregion(tag,
min_addr, max_addr,
newtag, flags)EOPNOTSUPP is returned.
BUS_DMA_WAITOKBUS_DMA_NOWAITThe address range min_addr to max_addr is inclusive of both addresses.
bus_dmatag_destroy(tag)bus_dmatag_subregion().Jason Thorpe, A Machine-Independent DMA Framework for NetBSD, Proceedings of the FREENIX Track: 1998 USENIX Annual Technical Conference, USENIX Association, http://www.usenix.org/publications/library/proceedings/usenix98/freenix/thorpe_dma.pdf, 1-12, June 15-19, 1998.
The bus_dma interface appeared in
NetBSD 1.3.
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.
| December 29, 2024 | NetBSD 11.0 |