/* * bmm_lib.c * * Buffer Management Module * * User level BMM Defines/Globals/Functions * * Li Li (lea.li@marvell.com) *(C) Copyright 2007 Marvell International Ltd. * All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include "bmm_drv.h" #include "bmm_lib.h" #include "rb.h" #undef DEBUG #ifdef DEBUG #define pr_debug(fmt, arg...) do { fprintf(stderr, fmt, ##arg); } while (0) #else #define pr_debug(fmt, arg...) do { if (0) fprintf(stderr, fmt, ##arg); } while (0) #endif static int bmm_fd = -1; static int bmm_use; static pthread_mutex_t bmm_mutex = PTHREAD_MUTEX_INITIALIZER; static Rb_node bmm_virt_rb; struct bmm_virt_buffer { void *vaddr; size_t size; int fd; }; static int cmp_virt(const void *key, const void *val) { const struct bmm_virt_buffer *vbuf = val; void *k = (void *)key; return (k < vbuf->vaddr) ? -1 : (k - vbuf->vaddr < vbuf->size) ? 0 : 1; } static struct bmm_virt_buffer *bmm_buf_find_virt(void *virt) { struct bmm_virt_buffer *vbuf = NULL; Rb_node node; int found = 0; node = rb_find_key_n(bmm_virt_rb, virt, cmp_virt, &found); if (found) { vbuf = rb_val(node); pr_debug("rb: %s(%p) virt=%p size=0x%08zx\n", "find_virt", virt, vbuf->vaddr, vbuf->size); } else { pr_debug("rb: %s(%p): not found\n", "find_virt", virt); } return vbuf; } static void bmm_rb_virt_remove(struct bmm_virt_buffer *vbuf) { Rb_node node; int found = 0; pr_debug("%s: virt=%p size=0x%08zx\n", __FUNCTION__, vbuf->vaddr, vbuf->size); node = rb_find_key_n(bmm_virt_rb, vbuf->vaddr, cmp_virt, &found); assert(found); rb_delete_node(node); } static void bmm_rb_virt_insert(struct bmm_virt_buffer *vbuf) { Rb_node node; int found = 0; pr_debug("%s: virt=%p size=0x%08zx\n", __FUNCTION__, vbuf->vaddr, vbuf->size); node = rb_find_key_n(bmm_virt_rb, vbuf->vaddr, cmp_virt, &found); if (found) { struct bmm_virt_buffer *f = rb_val(node); pr_debug("rb: found: %p\n", f); pr_debug(" v%p s0x%08zx\n", f->vaddr, f->size); } assert(found == 0); rb_insert_b(node, vbuf); } /** * bmm_dmabuf_alloc - allocate a DMA buffer * @size: size of DMA buffer * @attr: attributes of DMA buffer * @align: requested alignment * * Returns: dma_buf file descriptor referring to buffer */ int bmm_dmabuf_alloc(unsigned long size, int attr, unsigned align) { struct bmm_dmabuf_alloc arg; int ret; /* obsolete, only for back-compatible */ if (attr & BMM_ATTR_NONBUFFERABLE && attr & BMM_ATTR_NONCACHEABLE) attr = BMM_ATTR_NONCACHED; if (!(attr & BMM_ATTR_NONBUFFERABLE) && attr & BMM_ATTR_NONCACHEABLE) attr = BMM_ATTR_WRITECOMBINE; arg.size = size; arg.align = align; arg.attr = attr; ret = ioctl(bmm_fd, BMM_DMABUF_ALLOC, &arg); return ret < 0 ? ret : arg.fd; } /** * bmm_dmabuf_map - map an allcoated DMA buffer * @fd: dma_buf file descriptor * @offset: page aligned offset into DMA buffer * @size: size of buffer to map * * Map the requested buffer into userspace with the requested offset and size. * * Returns: address of mapped buffer, or NULL on error */ void *bmm_dmabuf_map(int fd, unsigned offset, unsigned size) { struct bmm_virt_buffer *vbuf; void *vaddr; vaddr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); if (vaddr == (void *)-1) return NULL; vbuf = malloc(sizeof(*vbuf)); if (!vbuf) { munmap(vaddr, size); return NULL; } vbuf->vaddr = vaddr; vbuf->size = size; vbuf->fd = fd; pthread_mutex_lock(&bmm_mutex); bmm_rb_virt_insert(vbuf); pthread_mutex_unlock(&bmm_mutex); return vaddr; } /* * bmm_dmabuf_unmap - unmap a dma_buf mapped previously with bmm_dmabuf_map * @vaddr: virtual address returned from bmm_dmabuf_map() */ void bmm_dmabuf_unmap(void *vaddr) { struct bmm_virt_buffer *vbuf; pthread_mutex_lock(&bmm_mutex); vbuf = bmm_buf_find_virt(vaddr); if (vbuf) bmm_rb_virt_remove(vbuf); pthread_mutex_unlock(&bmm_mutex); if (vbuf) { munmap(vbuf->vaddr, vbuf->size); free(vbuf); } } int bmm_dmabuf_flush(int fd, void *addr, unsigned offset, unsigned size, unsigned direction) { struct bmm_virt_buffer *vbuf; int ret = 0; pthread_mutex_lock(&bmm_mutex); vbuf = bmm_buf_find_virt(addr); pthread_mutex_unlock(&bmm_mutex); if (vbuf) { struct bmm_dmabuf_flush arg; if (fd != vbuf->fd) { errno = EINVAL; return -1; } arg.size = size; arg.offset = offset; arg.ptr = (uint64_t)(uintptr_t)addr; arg.fd = vbuf->fd; arg.direction = direction; ret = ioctl(bmm_fd, BMM_DMABUF_FLUSH, &arg); } return ret; } /** * bmm_dmabuf_fd - return the dma_buf file descriptor for a mapped dma_buf * @vaddr: virtual address returned from bmm_dmabuf_map() * * Returns the dma_buf file descriptor for the dma_buf previously mapped * with bmm_dmabuf_map(), or -1 on error. */ int bmm_dmabuf_fd(void *vaddr) { struct bmm_virt_buffer *vbuf; int fd = -1; pthread_mutex_lock(&bmm_mutex); vbuf = bmm_buf_find_virt(vaddr); if (vbuf) fd = vbuf->fd; pthread_mutex_unlock(&bmm_mutex); return fd; } /** * bmm_dmabuf_free - free the dma_buf file descriptor * @fd: dma_buf file descriptor returned by bmm_dmabuf_alloc() * * Closes the file descriptor associated with the DMA buffer. The actual * DMA buffer will only be freed when all references to it are gone - in * other words, after the buffer has been completely unmapped and any * imported references are also released. * * After this call has completed, the file descriptor returned by a call * to bmm_dmabuf_fd() is invalid. */ void bmm_dmabuf_free(int fd) { close(fd); } int bmm_init(void) { pthread_mutex_lock(&bmm_mutex); if (bmm_use++ == 0) { bmm_virt_rb = make_rb(); if (!bmm_virt_rb) goto err_rb; /* attempt to open the BMM driver */ bmm_fd = open(BMM_DEVICE_FILE, O_RDWR | O_CLOEXEC); pr_debug("BMM device fd: %d\n", bmm_fd); if (bmm_fd < 0) goto err_open; } pthread_mutex_unlock(&bmm_mutex); return bmm_fd; err_open: rb_free_tree(bmm_virt_rb); bmm_virt_rb = NULL; err_rb: bmm_use--; pthread_mutex_unlock(&bmm_mutex); return bmm_fd; } void bmm_exit(void) { pthread_mutex_lock(&bmm_mutex); if (bmm_use && --bmm_use == 0) { Rb_node node; /* Clean up dangling buffers which the app hasn't released. */ rb_traverse(node, bmm_virt_rb) { struct bmm_virt_buffer *vbuf = rb_val(node); munmap(vbuf->vaddr, vbuf->size); close(vbuf->fd); free(vbuf); } rb_free_tree(bmm_virt_rb); bmm_virt_rb = NULL; close(bmm_fd); bmm_fd = -1; } pthread_mutex_unlock(&bmm_mutex); } /* * Destructor for this library: ensure it gets cleaned up when the * library is unloaded. */ static void __attribute__((destructor)) bmm_destruct(void) { if (bmm_use) { bmm_use = 1; bmm_exit(); } }