/* * 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 "bmm_lib_priv.h" #include "bmm_lib.h" #include "rb.h" #undef DEBUG #ifdef DEBUG #include #define pr_debug(fmt, arg...) fprintf(stderr, fmt, ##arg) #else #define pr_debug(fmt, arg...) do { } while(0) #endif #define API_FEAT_MIN 0x0000 #define API_COMP_MAX 0x0001 #define API_FEAT(x) ((x) >> 16) #define API_COMP(x) ((x) & 0xffff) #define API_FEAT_GET_DMABUF_FD 0x0001 #define API_FEAT_FREE_PHYS 0x0002 static unsigned bmm_api; static int bmm_fd = -1; static pthread_mutex_t bmm_mutex = PTHREAD_MUTEX_INITIALIZER; static Rb_node virt_rb; static Rb_node phys_rb; struct bmm_buffer { void *vaddr; unsigned long paddr; size_t size; int attr; }; static int cmp_virt(const void *key, const void *val) { const struct bmm_buffer *buf = val; void *k = (void *)key; return (k < buf->vaddr) ? -1 : (k - buf->vaddr < buf->size) ? 0 : 1; } static int cmp_phys(const void *key, const void *val) { const struct bmm_buffer *buf = val; unsigned long k = (unsigned long)key; return (k < buf->paddr) ? -1 : (k - buf->paddr < buf->size) ? 0 : 1; } static struct bmm_buffer *bmm_buf_find_virt(void *virt) { struct bmm_buffer *buf = NULL; Rb_node node; int found = 0; node = rb_find_key_n(virt_rb, virt, cmp_virt, &found); if (found) { buf = rb_val(node); pr_debug("rb: %s(%p) phys=0x%08lx virt=%p size=0x%08lx\n", "find_virt", virt, buf->paddr, buf->vaddr, buf->size); } else { pr_debug("rb: %s(%p): not found\n", "find_virt", virt); } return buf; } static struct bmm_buffer *bmm_buf_find_phys(unsigned long phys) { struct bmm_buffer *buf = NULL; Rb_node node; int found = 0; node = rb_find_key_n(phys_rb, (void *)phys, cmp_phys, &found); if (found) { buf = rb_val(node); pr_debug("rb: %s(0x%08lx) phys=0x%08lx virt=%p size=0x%08lx\n", "find_phys", (unsigned long)phys, buf->paddr, buf->vaddr, buf->size); } else { pr_debug("rb: %s(0x%08lx): not found\n", "find_phys", (unsigned long)phys); } return buf; } static void bmm_buf_remove(struct bmm_buffer *buf) { Rb_node node; int found = 0; pr_debug("rb: %s phys=0x%08lx virt=%p size=0x%08lx\n", "remove", buf->paddr, buf->vaddr, buf->size); node = rb_find_key_n(virt_rb, buf->vaddr, cmp_virt, &found); assert(found); rb_delete_node(node); node = rb_find_key_n(phys_rb, (void *)buf->paddr, cmp_phys, &found); assert(found); rb_delete_node(node); } static void bmm_buf_insert(struct bmm_buffer *buf) { Rb_node node; int found = 0; pr_debug("rb: %s phys=0x%08lx virt=%p size=0x%08lx\n", "insert", buf->paddr, buf->vaddr, buf->size); node = rb_find_key_n(virt_rb, buf->vaddr, cmp_virt, &found); assert(found == 0); rb_insert_b(node, buf); node = rb_find_key_n(phys_rb, (void *)buf->paddr, cmp_phys, &found); assert(found == 0); rb_insert_b(node, buf); } static int bmm_get_api_version(void) { ioctl_arg_t io; /* Get the BMM API version */ io.input = 0; io.arg = 0; io.output = 0; if (ioctl(bmm_fd, BMM_API_VERSION, &io) == 0 && io.input == 0xdeaddead && io.arg == 0xfacebeef) bmm_api = io.output; else bmm_api = 0; pr_debug("BMM API version %08x\n", bmm_api); if (API_FEAT(bmm_api) < API_FEAT_MIN || API_COMP(bmm_api) > API_COMP_MAX) return -1; return 0; } int bmm_init() { if (bmm_fd < 0) { virt_rb = make_rb(); phys_rb = make_rb(); if (!virt_rb || !phys_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; if (bmm_get_api_version() < 0) goto err_api; } return bmm_fd; err_api: close(bmm_fd); bmm_fd = -1; err_open: err_rb: if (phys_rb) rb_free_tree(phys_rb); if (virt_rb) rb_free_tree(virt_rb); phys_rb = virt_rb = NULL; return bmm_fd; } void bmm_exit() { if (bmm_fd >= 0) { close(bmm_fd); rb_free_tree(phys_rb); rb_free_tree(virt_rb); phys_rb = virt_rb = NULL; } bmm_fd = -1; } void *bmm_malloc_aligned_phys(unsigned long size, int attr, unsigned align, unsigned long *paddr) { struct bmm_buffer *buf; int ret; void *vaddr; ioctl_arg_t io; if(size == 0) return NULL; if(bmm_init() < 0) return NULL; buf = malloc(sizeof(*buf)); if (!buf) return NULL; pr_debug("%s(size=%lu,attr=%x,align=%u,paddr=%p)\n", __FUNCTION__, size, attr, align, paddr); /* 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; io.input = align; io.length = size; io.output = 0; io.arg = attr; ret = ioctl(bmm_fd, BMM_MALLOC_ALIGNED, &io); if (ret < 0 || io.output == 0) goto err_free_buf; pr_debug("%s return paddr = 0x%08lx\n", __FUNCTION__, io.output); vaddr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, bmm_fd, io.output); if ((int)vaddr == -1) goto err_free_bmm; if (paddr) *paddr = io.output; buf->vaddr = vaddr; buf->paddr = io.output; buf->size = size; buf->attr = attr; pthread_mutex_lock(&bmm_mutex); bmm_buf_insert(buf); pthread_mutex_unlock(&bmm_mutex); return vaddr; err_free_bmm: if (API_FEAT(bmm_api) >= API_FEAT_FREE_PHYS) { /* Modern APIs allow us to free this failed allocation */ io.input = io.output; io.output = 0; io.arg = 0; ioctl(bmm_fd, BMM_FREE_PHYS, &io); } err_free_buf: free(buf); return NULL; } void *bmm_malloc_aligned(unsigned long size, int attr, unsigned align) { return bmm_malloc_aligned_phys(size, attr, align, NULL); } void *bmm_malloc(unsigned long size, int attr) { return bmm_malloc_aligned_phys(size, attr, sizeof(int), NULL); } void bmm_free(void *vaddr) { struct bmm_buffer *buf; ioctl_arg_t io; if (bmm_init() < 0) return; pthread_mutex_lock(&bmm_mutex); buf = bmm_buf_find_virt(vaddr); if (buf) bmm_buf_remove(buf); pthread_mutex_unlock(&bmm_mutex); assert(buf); munmap(buf->vaddr, buf->size); if (API_FEAT(bmm_api) >= API_FEAT_FREE_PHYS) { io.input = buf->paddr; io.output = 0; io.arg = 0; ioctl(bmm_fd, BMM_FREE_PHYS, &io); } else { io.input = (unsigned long)buf->vaddr; io.output = 0; io.arg = 0; ioctl(bmm_fd, BMM_FREE, &io); } free(buf); } void *bmm_attach(unsigned long paddr, unsigned long len) { void *vaddr; if(len == 0) return NULL; if(bmm_init() < 0) return NULL; vaddr = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, bmm_fd, paddr); return ((int)vaddr == -1) ? NULL : vaddr; } void bmm_detach(void *vaddr, unsigned long len) { if(bmm_init() < 0) return; munmap(vaddr, len); } void *bmm_get_vaddr(unsigned long paddr) { struct bmm_buffer *buf; void *va = NULL; if (bmm_init() < 0) return 0; pthread_mutex_lock(&bmm_mutex); buf = bmm_buf_find_phys(paddr); if (buf) va = buf->vaddr + (paddr - buf->paddr); pthread_mutex_unlock(&bmm_mutex); return va; } unsigned long bmm_get_paddr(void *vaddr) { struct bmm_buffer *buf; unsigned long pa = 0; if (bmm_init() < 0) return 0; pthread_mutex_lock(&bmm_mutex); buf = bmm_buf_find_virt(vaddr); if (buf) pa = buf->paddr + (vaddr - buf->vaddr); pthread_mutex_unlock(&bmm_mutex); return pa; } int bmm_get_dmabuf_fd(void *vaddr) { int ret; ioctl_arg_t io; if (bmm_init() < 0 || API_FEAT(bmm_api) < API_FEAT_GET_DMABUF_FD) return -1; io.input = (unsigned long)vaddr; io.output = 0; io.arg = 0; ret = ioctl(bmm_fd, BMM_GET_DMABUF_FD, &io); return ret < 0 ? -1 : io.output; } unsigned long bmm_get_mem_size(void *vaddr) { struct bmm_buffer *buf; unsigned long size = 0; if (bmm_init() < 0) return 0; pthread_mutex_lock(&bmm_mutex); buf = bmm_buf_find_virt(vaddr); if (buf) size = buf->size; pthread_mutex_unlock(&bmm_mutex); return size; } int bmm_get_mem_attr(void *vaddr) { struct bmm_buffer *buf; int attr = 0; if (bmm_init() < 0) return 0; pthread_mutex_lock(&bmm_mutex); buf = bmm_buf_find_virt(vaddr); if (buf) attr = buf->attr; pthread_mutex_unlock(&bmm_mutex); return attr; } int bmm_set_mem_attr(void *vaddr, int attr) { struct bmm_buffer *buf; int ret; ioctl_arg_t io; if(bmm_init() < 0) return 0; io.input = (unsigned long)vaddr; io.output = 0; io.arg = attr; ret = ioctl(bmm_fd, BMM_SET_MEM_ATTR, &io); if(ret < 0) return 0; attr = io.output; pthread_mutex_lock(&bmm_mutex); buf = bmm_buf_find_virt(vaddr); if (buf) buf->attr = attr; pthread_mutex_unlock(&bmm_mutex); return attr; } unsigned long bmm_get_total_space() { int ret; ioctl_arg_t io; if(bmm_init() < 0) return 0; io.input = 0; io.output = 0; io.arg = 0; ret = ioctl(bmm_fd, BMM_GET_TOTAL_SPACE, &io); if(ret < 0) return 0; return io.output; } unsigned long bmm_get_free_space() { int ret; ioctl_arg_t io; if(bmm_init() < 0) return 0; io.input = 0; io.output = 0; io.arg = 0; ret = ioctl(bmm_fd, BMM_GET_FREE_SPACE, &io); if(ret < 0) return 0; return io.output; } unsigned long bmm_get_allocated_space() { int ret; ioctl_arg_t io; if(bmm_init() < 0) return 0; io.input = 0; io.output = 0; io.arg = 0; ret = ioctl(bmm_fd, BMM_GET_ALLOCATED_SPACE, &io); if(ret < 0) return 0; return io.output; } void bmm_flush_cache(void *vaddr, int dir) { ioctl_arg_t io; if(bmm_init() < 0) return; io.input = (unsigned long)vaddr; io.output = 0; io.arg = dir; ioctl(bmm_fd, BMM_FLUSH_CACHE, &io); } void bmm_flush_cache_range(void *start, size_t size, int direction) { ioctl_arg_t io; if(bmm_init() < 0) return; io.input = (unsigned long)start; io.length = size; io.output = 0; io.arg = direction; ioctl(bmm_fd, BMM_CONSISTENT_SYNC, &io); } void bmm_flush_user(void *start, long size, int direction) { ioctl_arg_t io; if (bmm_init() < 0) return; if (size <= 0) return; io.input = (unsigned long)start; io.length = size; io.output = 0; io.arg = direction; ioctl(bmm_fd, BMM_SYNC_USER, &io); } void bmm_dump() { ioctl_arg_t io; if(bmm_init() < 0) return; io.input = 0; io.output = 0; io.arg = 0; ioctl(bmm_fd, BMM_DUMP, &io); }