/* * udma_mem.c * This file provides udma memory functionality to user space applications * to use the udma kernel driver. * * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ * * * 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. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * OWNER OR CONTRIBUTORS 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. * */ #include #define align(num, align) (((num) + (align) - 1) & (~((align) - 1))) static int udma_page_size = -1; static void *udma_mem_start = NULL; static void *udma_mem_end = NULL; static struct udma_atom udma_mem_avail; int udma_mem_init(unsigned long mem_size) { void *udma_mem; udma_page_size = getpagesize(); udma_mem = mmap(0, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, udma_fd, 0); if (udma_mem == MAP_FAILED) return -ENOMEM; udma_mem_start = udma_mem; udma_mem_end = udma_mem + mem_size; udma_atom_set_ptr(&udma_mem_avail, udma_mem_start); return 0; } void udma_mem_shutdown(void) { munmap(udma_mem_start, udma_mem_end - udma_mem_start); udma_mem_start = udma_mem_end = NULL; udma_atom_set_ptr(&udma_mem_avail, NULL); } void *udma_mem_alloc(unsigned long size) { void *memstart, *memend; enum udma_atom_status status; size = align(size, udma_page_size); do { memstart = udma_atom_get_ptr(&udma_mem_avail); memend = memstart + size; if (memend > udma_mem_end) return NULL; status = udma_atom_cas_ptr(&udma_mem_avail, memstart, memend); } while (status != UDMA_ATOM_CAS_SUCCESS); return memstart; } int udma_mem_free(void *mem, unsigned long size) { void *memstart = mem, *memend, *avail; enum udma_atom_status status; size = align(size, udma_page_size); memend = memstart + size; do { avail = udma_atom_get_ptr(&udma_mem_avail); if (avail != memend) return -EINVAL; status = udma_atom_cas_ptr(&udma_mem_avail, memend, memstart); } while (status != UDMA_ATOM_CAS_SUCCESS); return 0; } struct udma_mem_part *udma_mem_part_create(unsigned nblocks, unsigned blocksize) { struct udma_mem_part *part; unsigned total; void *mem; part = udma_malloc(sizeof(struct udma_mem_part)); if (!part) return NULL; part->blocksize = align(blocksize, 4); total = part->blocksize * nblocks; nblocks = total / part->blocksize; mem = udma_mem_alloc(total); if (!mem) { udma_free(part); return NULL; } part->memstart = mem; part->memend = mem + total; udma_atom_set(&part->used, 0); udma_lifo_init(&part->free); for (; nblocks; nblocks--, mem += part->blocksize) udma_lifo_put(&part->free, mem); return part; } void udma_mem_part_destroy(struct udma_mem_part *part) { udma_mem_free(part->memstart, part->memend - part->memstart); udma_free(part); } void *udma_mem_block_alloc(struct udma_mem_part *part) { void *block; block = udma_lifo_get(&part->free); if (block) udma_atom_inc(&part->used); return block; } void udma_mem_block_free(struct udma_mem_part *part, void* block) { if (block < part->memstart || block >= part->memend) return; udma_lifo_put(&part->free, block); udma_atom_dec(&part->used); }