/* * Copyright (c) 2018 Ondrej Hlavaty * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - 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. * - 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. */ /** @addtogroup libusb * @{ */ /** @file * @brief USB host controller library: DMA buffer helpers * * Simplifies handling of buffers accessible to hardware. Defines properties of * such buffer, which can be communicated through IPC to allow higher layers to * allocate a buffer that is ready to be passed to HW right away (after being * shared through IPC). * * Currently, it is possible to allocate either completely contiguous buffers * (with dma_map_anonymous) or arbitrary memory (with as_area_create). Shall the * kernel be updated, this is a subject of major optimization of memory usage. * The other way to do it without the kernel is building an userspace IO vector * in a similar way how QEMU does it. * * The structures themselves are defined in usbhc_iface, because they need to be * passed through IPC. */ #ifndef LIB_USB_DMA_BUFFER #define LIB_USB_DMA_BUFFER #include <as.h> #include <bitops.h> #include <errno.h> #include <stdint.h> #include <stdlib.h> #include <usbhc_iface.h> /** * The DMA policy describes properties of the buffer. It is used in two * different contexts. Either it represents requirements, which shall be * satisfied to avoid copying the buffer to a more strict one. Or, it is the * actual property of the buffer, which can be more strict than requested. It * always holds that more bits set means more restrictive policy, and that by * computing a bitwise OR one gets the restriction that holds for both. * * The high bits of a DMA policy represent a physical contiguity. If bit i is * set, it means that chunks of a size 2^(i+1) are contiguous in memory. It * shall never happen that bit i > j is set when j is not. * * The previous applies for i >= PAGE_WIDTH. Lower bits are used as bit flags. */ #define DMA_POLICY_FLAGS_MASK (PAGE_SIZE - 1) #define DMA_POLICY_CHUNK_SIZE_MASK (~DMA_POLICY_FLAGS_MASK) #define DMA_POLICY_4GiB (1<<0) /**< Must use only 32-bit addresses */ #define DMA_POLICY_STRICT (-1UL) #define DMA_POLICY_DEFAULT DMA_POLICY_STRICT extern dma_policy_t dma_policy_create(unsigned, size_t); /** * Get mask which defines bits of offset in chunk. */ static inline size_t dma_policy_chunk_mask(const dma_policy_t policy) { return policy | DMA_POLICY_FLAGS_MASK; } extern errno_t dma_buffer_alloc(dma_buffer_t *db, size_t size); extern errno_t dma_buffer_alloc_policy(dma_buffer_t *, size_t, dma_policy_t); extern void dma_buffer_free(dma_buffer_t *); extern uintptr_t dma_buffer_phys(const dma_buffer_t *, const void *); static inline uintptr_t dma_buffer_phys_base(const dma_buffer_t *db) { return dma_buffer_phys(db, db->virt); } extern errno_t dma_buffer_lock(dma_buffer_t *, void *, size_t); extern void dma_buffer_unlock(dma_buffer_t *, size_t); extern void dma_buffer_acquire(dma_buffer_t *); extern void dma_buffer_release(dma_buffer_t *); static inline bool dma_buffer_is_set(const dma_buffer_t *db) { return !!db->virt; } #endif /** * @} */