|  | /* | 
|  | * Copyright (c) 2011-2012, Code Aurora Forum. 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. | 
|  | *   * Neither the name of Code Aurora Forum, Inc. 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 "AS IS" AND ANY EXPRESS OR IMPLIED | 
|  | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | #define DEBUG 0 | 
|  | #include <linux/ioctl.h> | 
|  | #include <sys/mman.h> | 
|  | #include <stdlib.h> | 
|  | #include <fcntl.h> | 
|  | #include <cutils/log.h> | 
|  | #include <errno.h> | 
|  | #include "gralloc_priv.h" | 
|  | #include "ionalloc.h" | 
|  |  | 
|  | using gralloc::IonAlloc; | 
|  |  | 
|  | #define ION_DEVICE "/dev/ion" | 
|  |  | 
|  | int IonAlloc::open_device() | 
|  | { | 
|  | if(mIonFd == FD_INIT) | 
|  | mIonFd = open(ION_DEVICE, O_RDONLY); | 
|  |  | 
|  | if(mIonFd < 0 ) { | 
|  | ALOGE("%s: Failed to open ion device - %s", | 
|  | __FUNCTION__, strerror(errno)); | 
|  | mIonFd = FD_INIT; | 
|  | return -errno; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void IonAlloc::close_device() | 
|  | { | 
|  | if(mIonFd >= 0) | 
|  | close(mIonFd); | 
|  | mIonFd = FD_INIT; | 
|  | } | 
|  |  | 
|  | int IonAlloc::alloc_buffer(alloc_data& data) | 
|  | { | 
|  | Locker::Autolock _l(mLock); | 
|  | int err = 0; | 
|  | int ionSyncFd = FD_INIT; | 
|  | int iFd = FD_INIT; | 
|  | struct ion_handle_data handle_data; | 
|  | struct ion_fd_data fd_data; | 
|  | struct ion_allocation_data ionAllocData; | 
|  |  | 
|  | void *base = 0; | 
|  |  | 
|  | ionAllocData.len = data.size; | 
|  | ionAllocData.align = data.align; | 
|  | ionAllocData.flags = data.flags; | 
|  |  | 
|  | err = open_device(); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | if(data.uncached) { | 
|  | // Use the sync FD to alloc and map | 
|  | // when we need uncached memory | 
|  | ionSyncFd = open(ION_DEVICE, O_RDONLY|O_DSYNC); | 
|  | if(ionSyncFd < 0) { | 
|  | ALOGE("%s: Failed to open ion device - %s", | 
|  | __FUNCTION__, strerror(errno)); | 
|  | return -errno; | 
|  | } | 
|  | iFd = ionSyncFd; | 
|  | } else { | 
|  | iFd = mIonFd; | 
|  | } | 
|  |  | 
|  | if(ioctl(iFd, ION_IOC_ALLOC, &ionAllocData)) { | 
|  | err = -errno; | 
|  | ALOGE("ION_IOC_ALLOC failed with error - %s", strerror(errno)); | 
|  | if(ionSyncFd >= 0) | 
|  | close(ionSyncFd); | 
|  | ionSyncFd = FD_INIT; | 
|  | return err; | 
|  | } | 
|  |  | 
|  | fd_data.handle = ionAllocData.handle; | 
|  | handle_data.handle = ionAllocData.handle; | 
|  | if(ioctl(iFd, ION_IOC_MAP, &fd_data)) { | 
|  | err = -errno; | 
|  | ALOGE("%s: ION_IOC_MAP failed with error - %s", | 
|  | __FUNCTION__, strerror(errno)); | 
|  | ioctl(mIonFd, ION_IOC_FREE, &handle_data); | 
|  | if(ionSyncFd >= 0) | 
|  | close(ionSyncFd); | 
|  | ionSyncFd = FD_INIT; | 
|  | return err; | 
|  | } | 
|  |  | 
|  | if(!(data.flags & ION_SECURE)) { | 
|  | base = mmap(0, ionAllocData.len, PROT_READ|PROT_WRITE, | 
|  | MAP_SHARED, fd_data.fd, 0); | 
|  | if(base == MAP_FAILED) { | 
|  | err = -errno; | 
|  | ALOGE("%s: Failed to map the allocated memory: %s", | 
|  | __FUNCTION__, strerror(errno)); | 
|  | ioctl(mIonFd, ION_IOC_FREE, &handle_data); | 
|  | ionSyncFd = FD_INIT; | 
|  | return err; | 
|  | } | 
|  | memset(base, 0, ionAllocData.len); | 
|  | // Clean cache after memset | 
|  | clean_buffer(base, data.size, data.offset, fd_data.fd); | 
|  | } | 
|  |  | 
|  | //Close the uncached FD since we no longer need it; | 
|  | if(ionSyncFd >= 0) | 
|  | close(ionSyncFd); | 
|  | ionSyncFd = FD_INIT; | 
|  |  | 
|  | data.base = base; | 
|  | data.fd = fd_data.fd; | 
|  | ioctl(mIonFd, ION_IOC_FREE, &handle_data); | 
|  | ALOGD_IF(DEBUG, "ion: Allocated buffer base:%p size:%d fd:%d", | 
|  | data.base, ionAllocData.len, data.fd); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int IonAlloc::free_buffer(void* base, size_t size, int offset, int fd) | 
|  | { | 
|  | Locker::Autolock _l(mLock); | 
|  | ALOGD_IF(DEBUG, "ion: Freeing buffer base:%p size:%d fd:%d", | 
|  | base, size, fd); | 
|  | int err = 0; | 
|  | err = open_device(); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | if(base) | 
|  | err = unmap_buffer(base, size, offset); | 
|  | close(fd); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int IonAlloc::map_buffer(void **pBase, size_t size, int offset, int fd) | 
|  | { | 
|  | int err = 0; | 
|  | void *base = 0; | 
|  | // It is a (quirky) requirement of ION to have opened the | 
|  | // ion fd in the process that is doing the mapping | 
|  | err = open_device(); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | base = mmap(0, size, PROT_READ| PROT_WRITE, | 
|  | MAP_SHARED, fd, 0); | 
|  | *pBase = base; | 
|  | if(base == MAP_FAILED) { | 
|  | err = -errno; | 
|  | ALOGE("ion: Failed to map memory in the client: %s", | 
|  | strerror(errno)); | 
|  | } else { | 
|  | ALOGD_IF(DEBUG, "ion: Mapped buffer base:%p size:%d offset:%d fd:%d", | 
|  | base, size, offset, fd); | 
|  | } | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int IonAlloc::unmap_buffer(void *base, size_t size, int offset) | 
|  | { | 
|  | ALOGD_IF(DEBUG, "ion: Unmapping buffer  base:%p size:%d", base, size); | 
|  | int err = 0; | 
|  | if(munmap(base, size)) { | 
|  | err = -errno; | 
|  | ALOGE("ion: Failed to unmap memory at %p : %s", | 
|  | base, strerror(errno)); | 
|  | } | 
|  | return err; | 
|  |  | 
|  | } | 
|  | int IonAlloc::clean_buffer(void *base, size_t size, int offset, int fd) | 
|  | { | 
|  | struct ion_flush_data flush_data; | 
|  | struct ion_fd_data fd_data; | 
|  | struct ion_handle_data handle_data; | 
|  | struct ion_handle* handle; | 
|  | int err = 0; | 
|  |  | 
|  | err = open_device(); | 
|  | if (err) | 
|  | return err; | 
|  |  | 
|  | fd_data.fd = fd; | 
|  | if (ioctl(mIonFd, ION_IOC_IMPORT, &fd_data)) { | 
|  | err = -errno; | 
|  | ALOGE("%s: ION_IOC_IMPORT failed with error - %s", | 
|  | __FUNCTION__, strerror(errno)); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | handle_data.handle = fd_data.handle; | 
|  | flush_data.handle  = fd_data.handle; | 
|  | flush_data.vaddr   = base; | 
|  | flush_data.offset  = offset; | 
|  | flush_data.length  = size; | 
|  | if(ioctl(mIonFd, ION_IOC_CLEAN_INV_CACHES, &flush_data)) { | 
|  | err = -errno; | 
|  | ALOGE("%s: ION_IOC_CLEAN_INV_CACHES failed with error - %s", | 
|  | __FUNCTION__, strerror(errno)); | 
|  | ioctl(mIonFd, ION_IOC_FREE, &handle_data); | 
|  | return err; | 
|  | } | 
|  | ioctl(mIonFd, ION_IOC_FREE, &handle_data); | 
|  | return 0; | 
|  | } | 
|  |  |