xref: /netbsd/share/doc/papers/bus_dma/3.me (revision bf9ec67e)
$NetBSD: 3.me,v 1.1 1998/07/15 00:34:54 thorpej Exp $

Copyright (c) 1998 Jason R. Thorpe.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgements:
This product includes software developed for the NetBSD Project
by Jason R. Thorpe.
4. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

.sh 1 "The bus_dma interface" .pp What follows is a description of bus_dma, the DMA portion of the machine-independent bus access interface in NetBSD, commonly referred to as bus.h\**. The DMA portion of the interface is comprised of three DMA-specific data types and thirteen function calls. The bus_dma interface also shares two data types with the bus_space interface. .(f \**The name is derived from the name of the include file that exports the interface. .)f .pp The bus_dma functional interface is split into two categories: mapping calls and memory handling calls. The function calls themselves may be implemented as cpp(1) macros. .sh 2 "Data types" .pp The first of the two data types shared with the bus_space interface is the bus_addr_t type, which represents device bus addresses to be used for CPU access or DMA, and must be large enough to specify the largest possible bus address on the system. The second is the bus_size_t type, which represents sizes of bus address ranges. .pp The implementation of DMA on a given host/bus combination is described by the bus_dma_tag_t. This opaque type is passed to a bus's autoconfiguration machinery by machine-dependent code. The bus layer in turn passes it down to the device drivers. This tag is the first argument to every function in the interface. .pp Individual DMA segments are described by the bus_dma_segment_t. This type is a structure with two publicly accessible members. The first member, ds_addr, is a bus_addr_t containing the address of a DMA segment. The second, ds_len, is a bus_size_t containing the length of the segment. .pp The third, and probably most important, data type is the bus_dmamap_t. This type is a pointer to a structure which describes an individual DMA mapping. The structure has three public members. The first member, dm_mapsize is a bus_size_t describing the length of the mapping, when valid. A dm_mapsize of 0 indicates that the mapping is invalid. The second member, dm_nsegs, is an int which contains the number of DMA segments that comprise the mapping. The third public member, dm_segs, is an array or a pointer to an array of bus_dma_segment_t structures. .pp In addition to data types, the bus_dma interface also defines a set of flags which are passed to some of the interface's functions. Two of these flags, BUS_DMA_WAITOK and BUS_DMA_NOWAIT, indicate to the function that waiting for resources to become available is or is not allowed\**. There are also four placeholder flags, BUS_DMA_BUS1 through BUS_DMA_BUS4. These flags are reserved for the individual bus layers, which may need to define special semantics specific to that bus. An example of this is the ability of VESA local bus devices to utilize 32-bit DMA addresses; while the kernel considers such devices to be logically connected to the ISA bus, they are not limited to the addressing constraints of other ISA devices. The placeholder flags allow such special cases to be handled on a bus-by-bus basis. .(f \**Waiting (also called "blocking") is allowed only if the kernel is running in a process context, as opposed to the interrupt context used when handling device interrupts. .)f .sh 2 "Mapping functions" .pp There are eight functions in the bus_dma interface that operate on DMA maps. These can be sub-categorized into functions that create and destroy maps, functions that load and unload mappings, and functions that synchronize maps. .pp The first two functions fall into the create/destroy sub-category. The bus_dmamap_create() function creates a DMA map and initializes it according to the parameters provided. The parameters include the maximum DMA transfer size the DMA map will map, the maximum number of DMA segments, the maximum size of any given segment, and any DMA boundary limitations. In addition to the standard flags, bus_dmamap_create() also takes the flag BUS_DMA_ALLOCNOW. This flag indicates that all resources necessary to map the maximum size transfer should be allocated when the map is created, and is useful in case the driver must load the DMA map at a time where blocking is not allowed, such as in interrupt context. The bus_dmamap_destroy() function destroys a DMA map, and frees any resources that may be assigned to it. .pp The next five functions fall into the load/unload sub-category. The two basic functions are bus_dmamap_load() and bus_dmamap_unload(). The former maps a DMA transfer to or from a linear buffer. This linear buffer may be mapped into either kernel or a process's virtual address space. The latter unloads the mappings previously loaded into the DMA map. If the BUS_DMA_ALLOCNOW flag was specified when the map was created, bus_dmamap_load() will not block or fail on resource allocation. Similarly, when the map is unloaded, the mapping resources will not be freed. .pp In addition to linear buffers handled by the basic bus_dmamap_load(), there are three alternate data buffer structures handled by the interface. The bus_dmamap_load_mbuf() function operates on mbuf chains. The individual data buffers are assumed to be in kernel virtual address space. The bus_dmamap_load_uio() function operates on uio structures, from which it extracts information about the address space in which the data resides. Finally, the bus_dmamap_load_raw() function operates on raw memory, which is not mapped into any virtual address space. All DMA maps loaded with these functions are unloaded with the bus_dmamap_unload() function. .pp Finally, the map synchronization sub-category includes one function: bus_dmamap_sync(). This function performs the four DMA synchronization operations necessary to handle caches and DMA bouncing. The four operations are: .(b .b BUS_DMASYNC_PREREAD BUS_DMASYNC_POSTREAD BUS_DMASYNC_PREWRITE BUS_DMASYNC_POSTWRITE .r .)b The direction is expressed from the perspective of the host's memory. In other words, a device-to-memory transfer is a read, and a memory-to-device transfer is a write. The synchronization operations are expressed as flags, so it is possible to combine READ and WRITE operations in a single call. This is especially useful for synchronizing mappings of device control descriptors. Mixing of PRE and POST operations is not allowed. .pp In addition to the map and operation arguments, bus_dmamap_sync() also takes offset and length arguments. This is done in order to support partial syncs. In the case where a control descriptor is DMA'd to a device, it may be undesirable to synchronize the entire mapping, as doing so may be inefficient or even destructive to other control descriptors. Synchronizing the entire mapping is supported by passing an offset of 0 and the length specified by the map's dm_mapsize. .sh 2 "Memory handling functions" .pp There are two sub-categories of functions that handle DMA-safe memory in the bus_dma interface: memory allocation and memory mapping. .pp The first function in the memory allocation sub-category, bus_dmamem_alloc(), allocates memory which has the specified attributes. The attributes that may be specified are: the size of the memory region to allocate, the alignment of each segment in the allocation, any boundary limitations, and the maximum number of DMA segments that may make up the allocation. The function fills in a provided array of bus_dma_segment_ts and indicates the number of valid segments in the array. Memory allocated by this interface is raw memory\**; it is not mapped into any virtual address space. Once it is no longer in use, it may be freed with the bus_dmamem_free() function. .(f \**This implies that bus_dmamap_load_raw() is an appropriate interface for mapping a DMA transfer to or from memory allocated by this interface. .)f .pp In order for the kernel or a user process to access the memory, it must be mapped either into the kernel address space or the process's address space. These operations are performed by the memory mapping sub-category of DMA-safe memory handling functions. The bus_dmamem_map() function maps the specified DMA-safe raw memory into the kernel address space. The address of the mapping is returned by filling in a pointer passed by reference. Memory mapped in this manner may be unmapped by calling bus_dmamem_unmap(). .pp DMA-safe raw memory may be mapped into a process's address space via a device driver's mmap() entry point. In order to do this, the VM system's device pager repeatedly calls the driver, once for each page that is to be mapped. The driver translates the user-specified mmap offset into a DMA memory offset, and calls the bus_dmamem_mmap() function to translate the memory offset into an opaque value to be interpreted by the pmap module\**. The device pager invokes the pmap module to translate the mmap cookie into a physical page address which is then mapped into the process's address space. .(f \**The pmap module is the machine-dependent layer of the NetBSD virtual memory system. .)f .pp There are currently no methods for the virtual memory system to specify that an mmap'd area is being unmapped, or for the device driver to specify to the virtual memory system that a mmap'd region must be forcibly unmapped (for example, if a hot-swapable device has been removed from the system). This is widely regarded as a bug, and may be addressed in a future version of the NetBSD virtual memory system. If a change to this effect is made, the bus_dma interface will have to be adjusted accordingly.