| /* |
| * ion.cpp |
| * |
| * Copyright 2018 Samsung Electronics Co., Ltd. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <hardware/exynos/ion.h> |
| #include <ion/ion.h> |
| #include <linux/dma-buf.h> |
| #include <linux/dma-heap.h> |
| #include <log/log.h> |
| #include <stdatomic.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "ion.h" |
| #include "ion_uapi.h" |
| |
| /* |
| * If vendor system heap is registered, all request of system heap |
| * are allocated from vendor system heap instead of built-in system heap. |
| * The vendor system heap is the same purpose allocator as the system heap, |
| * but it is optimized and supports debugging function for exynos. |
| */ |
| #define EXYNOS_ION_HEAP_VENDOR_SYSTEM_MASK (1 << 14) |
| |
| #define ION_MAX_HEAP_COUNT 15 |
| |
| /* |
| * ION heap names |
| * The array index is the legacy heap id |
| */ |
| static const struct { |
| const char *name; |
| const char *dmaheap_name; |
| unsigned int namelen; |
| } ion_heap_name[ION_MAX_HEAP_COUNT] = { |
| {"ion_system_heap", "system", 15}, |
| {"crypto_heap", "crypto", 11}, |
| {"vfw_heap", "reserved", 8 }, /* no use for dma-heap */ |
| {"vstream_heap", "system-secure-vstream", 12}, |
| {"[reserved]", "reserved", 0 }, /* reserved heap id. never use */ |
| {"vframe_heap", "vframe", 11}, |
| {"vscaler_heap", "vscaler", 12}, |
| {"vnfw_heap", "reserved", 9 }, /* no use for dma-heap */ |
| {"gpu_crc", "gpu_crc", 7 }, |
| {"gpu_buffer", "system-secure-gpu_buffer", 10}, |
| {"camera_heap", "camera", 11}, |
| {"secure_camera_heap", "secure_camera", 18}, |
| {"[reserved]", "reserved", 0 }, /* reserved heap id. never use */ |
| {"ext_ui", "ext_ui", 6 }, |
| {"vendor_system_heap", "system", 22 }, |
| }; |
| |
| #define ION_NUM_HEAP_NAMES (unsigned int)(sizeof(ion_heap_name)/sizeof(ion_heap_name[0])) |
| |
| #define ION_HEAP_TYPE_NONE INT_MAX |
| |
| const char *exynos_ion_get_heap_name(unsigned int legacy_heap_id) { |
| if (legacy_heap_id >= ION_NUM_HEAP_NAMES) |
| return NULL; |
| |
| return ion_heap_name[legacy_heap_id].name; |
| } |
| |
| class DefaultSystemInterface : public SystemInterface { |
| public: |
| ~DefaultSystemInterface() { } |
| int Open(const char *path) { |
| return open(path, O_RDONLY | O_CLOEXEC); |
| } |
| int Ioctl(int fd, unsigned int cmd, void *data) { |
| return ioctl(fd, cmd, data); |
| } |
| int Ioctl(int fd, unsigned int cmd) { |
| return ioctl(fd, cmd); |
| } |
| int Close(int fd) { |
| return close(fd); |
| } |
| }; |
| |
| int DmabufExporter::legacy_free_handle(int ion_fd, int handle) { |
| struct ion_handle_data data = { .handle = handle, }; |
| |
| return systemInterface.Ioctl(ion_fd, ION_IOC_FREE, &data); |
| } |
| |
| int DmabufExporter::alloc_legacy(int ion_fd, size_t len, unsigned int heap_mask, unsigned int flags) { |
| int ret; |
| struct ion_fd_data fd_data; |
| struct ion_allocation_data alloc_data = { |
| .len = len, |
| .align = 0, |
| .heap_id_mask = heap_mask, |
| .flags = flags, |
| }; |
| |
| ret = systemInterface.Ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data); |
| if (ret < 0) { |
| ALOGE("%s(%d, %zu, %#x, %#x) ION_IOC_ALLOC failed: %s", __func__, |
| ion_fd, len, heap_mask, flags, strerror(errno)); |
| return -1; |
| } |
| |
| fd_data.handle = alloc_data.handle; |
| |
| ret = systemInterface.Ioctl(ion_fd, ION_IOC_SHARE, &fd_data); |
| legacy_free_handle(ion_fd, alloc_data.handle); |
| if (ret < 0) { |
| ALOGE("%s(%d, %zu, %#x, %#x) ION_IOC_SHARE failed: %s", __func__, |
| ion_fd, len, heap_mask, flags, strerror(errno)); |
| return -1; |
| } |
| |
| return fd_data.fd; |
| } |
| |
| int DmabufExporter::query_heap_id(int ion_fd, unsigned int legacy_heap_mask) { |
| unsigned int i, legacy_heap_id; |
| struct ion_heap_query query; |
| struct ion_exynos_heap_data data[ION_NUM_HEAP_IDS]; |
| |
| memset(&data, 0, sizeof(data)); |
| memset(&query, 0, sizeof(query)); |
| |
| query.cnt = ION_NUM_HEAP_IDS; |
| query.heaps = (__u64)data; |
| |
| if (systemInterface.Ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query) < 0) { |
| ALOGE("%s: failed query heaps with ion_fd %d: %s", |
| __func__, ion_fd, strerror(errno)); |
| return 0; |
| } |
| |
| if (query.cnt > ION_NUM_HEAP_IDS) |
| query.cnt = ION_NUM_HEAP_IDS; |
| |
| for (legacy_heap_id = 0; legacy_heap_id < ION_NUM_HEAP_NAMES; legacy_heap_id++) { |
| if ((1 << legacy_heap_id) & legacy_heap_mask) { |
| for (i = 0; i < ION_NUM_HEAP_IDS; i++) { |
| if (!strcmp(data[i].name, ion_heap_name[legacy_heap_id].name)) |
| return 1 << data[i].heap_id; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| int DmabufExporter::alloc_modern(int ion_fd, size_t len, unsigned int legacy_heap_mask, unsigned int flags) { |
| int ret; |
| struct ion_new_allocation_data data = { |
| .len = len, |
| .flags = flags, |
| .heap_id_mask = 0, |
| }; |
| |
| if (legacy_heap_mask == EXYNOS_ION_HEAP_SYSTEM_MASK) |
| data.heap_id_mask = query_heap_id(ion_fd, EXYNOS_ION_HEAP_VENDOR_SYSTEM_MASK); |
| |
| if (!data.heap_id_mask) |
| data.heap_id_mask = query_heap_id(ion_fd, legacy_heap_mask); |
| |
| if (!data.heap_id_mask) { |
| ALOGE("%s: unable to find heaps of heap_mask %#x", __func__, legacy_heap_mask); |
| return -1; |
| } |
| |
| ret = systemInterface.Ioctl(ion_fd, ION_IOC_NEW_ALLOC, &data); |
| if (ret < 0) { |
| ALOGE("%s(%d, %zu, %#x(%#x), %#x) failed: %s", __func__, |
| ion_fd, len, legacy_heap_mask, data.heap_id_mask, flags, strerror(errno)); |
| return -1; |
| } |
| |
| return (int)data.fd; |
| } |
| |
| int DmabufExporter::alloc_dma_heap(size_t len, unsigned int legacy_heap_mask, unsigned int flags) { |
| char path[MAX_HEAP_PATH]; |
| int id; |
| |
| strcpy(path, DmaHeapRoot); |
| for (id = 0; id < ION_NUM_HEAP_NAMES; id++) { |
| if (legacy_heap_mask & (1 << id)) { |
| strcat(path, ion_heap_name[id].dmaheap_name); |
| break; |
| } |
| } |
| if (id == ION_NUM_HEAP_NAMES) { |
| ALOGE("%s invalid heapmask (%zu, %#x, %#x)", __func__, len, legacy_heap_mask, flags); |
| return -EINVAL; |
| } |
| |
| /* Append heap name for flags */ |
| if (flags & ION_FLAG_PROTECTED) |
| strcat(path, "-secure"); |
| else if (!(flags & ION_FLAG_CACHED)) |
| strcat(path, "-uncached"); |
| |
| int ret, fd = systemInterface.Open(path); |
| if (fd < 0) { |
| ALOGE("%s No device for %s (%zu, %#x, %#x) failed: %s", __func__, |
| path, len, legacy_heap_mask, flags, strerror(errno)); |
| return fd; |
| } |
| struct dma_heap_allocation_data data; |
| |
| data.fd = 0; |
| data.len = len; |
| data.fd_flags = O_RDWR | O_CLOEXEC; |
| data.heap_flags = 0; |
| |
| ret = systemInterface.Ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data); |
| if (ret < 0) |
| ALOGE("%s Allocation failure for %s (%zu, %#x, %#x) failed: %s", __func__, |
| path, len, legacy_heap_mask, flags, strerror(errno)); |
| else |
| ret = data.fd; |
| |
| systemInterface.Close(fd); |
| |
| return ret; |
| } |
| |
| int DmabufExporter::open(void) { |
| if (version == DMAHEAP_VERSION) |
| return 0; |
| |
| int fd = systemInterface.Open("/dev/ion"); |
| if (fd < 0) |
| ALOGE("open /dev/ion failed: %s", strerror(errno)); |
| |
| return fd; |
| } |
| |
| int DmabufExporter::close(int fd) { |
| if (version == DMAHEAP_VERSION) |
| return 0; |
| |
| int ret = systemInterface.Close(fd); |
| if (ret < 0) |
| ALOGE("closing fd %d of /dev/ion failed: %s", fd, strerror(errno)); |
| return ret; |
| } |
| |
| int DmabufExporter::alloc(int ion_fd, size_t len, unsigned int legacy_heap_mask, unsigned int flags) { |
| int fd; |
| |
| if (version == DMAHEAP_VERSION) |
| fd = alloc_dma_heap(len, legacy_heap_mask, flags); |
| else if (version == ION_LEGACY_VERSION) |
| fd = alloc_legacy(ion_fd, len, legacy_heap_mask, flags); |
| else |
| fd = alloc_modern(ion_fd, len, legacy_heap_mask, flags); |
| |
| return fd; |
| } |
| |
| int DmabufExporter::trace_buffer(int fd) |
| { |
| if (!dma_buf_trace_supported) |
| return 0; |
| |
| if (systemInterface.Ioctl(fd, DMA_BUF_IOCTL_TRACK) < 0) { |
| if (errno == ENOTTY) { |
| dma_buf_trace_supported = false; |
| return 0; |
| } |
| ALOGE("%s(%d) failed: %s", __func__, fd, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int DmabufExporter::untrace_buffer(int fd) |
| { |
| if (!dma_buf_trace_supported) |
| return 0; |
| |
| if (systemInterface.Ioctl(fd, DMA_BUF_IOCTL_UNTRACK) < 0) { |
| ALOGE("%s(%d) failed: %s", __func__, fd, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int DmabufExporter::import_handle(int ion_fd, int fd, int* handle) { |
| int ret; |
| struct ion_fd_data data = { |
| .fd = fd, |
| }; |
| |
| assert(handle == NULL); |
| |
| if (version != ION_LEGACY_VERSION) { |
| if (trace_buffer(fd)) |
| return -1; |
| /* |
| * buffer fd is not a handle and they are maintained seperately. |
| * But we provide buffer fd as the buffer handle to keep the libion |
| * api compatible with the legacy users including gralloc. |
| * We should gradually change all the legacy users in the near future. |
| */ |
| *handle = fd; |
| return 0; |
| } |
| |
| ret = systemInterface.Ioctl(ion_fd, ION_IOC_IMPORT, &data); |
| if (ret < 0) { |
| ALOGE("%s(%d, %d) failed: %s", __func__, ion_fd, fd, strerror(errno)); |
| return -1; |
| } |
| *handle = data.handle; |
| |
| return 0; |
| } |
| |
| int DmabufExporter::free_handle(int ion_fd, int handle) { |
| int ret; |
| |
| if (version != ION_LEGACY_VERSION) { |
| if (untrace_buffer(handle)) |
| return -1; |
| return 0; |
| } |
| |
| ret = legacy_free_handle(ion_fd, handle); |
| if (ret < 0) { |
| ALOGE("%s(%d, %d) failed: %s", __func__, ion_fd, handle, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int DmabufExporter::sync_fd(int ion_fd, int fd) { |
| struct ion_fd_data data = { |
| .fd = fd, |
| }; |
| |
| if (version != ION_LEGACY_VERSION) |
| return 0; |
| |
| if (systemInterface.Ioctl(ion_fd, ION_IOC_SYNC, &data) < 0) { |
| ALOGE("%s(%d, %d) failed: %s", __func__, ion_fd, fd, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int DmabufExporter::sync_fd_partial(int ion_fd, int fd, off_t offset, size_t len) { |
| struct ion_fd_partial_data data = { |
| .fd = fd, |
| .offset = offset, |
| .len = len |
| }; |
| |
| if (version != ION_LEGACY_VERSION) |
| return 0; |
| |
| if (systemInterface.Ioctl(ion_fd, ION_IOC_SYNC_PARTIAL, &data) < 0) { |
| ALOGE("%s(%d, %d, %lu, %zu) failed: %s", __func__, ion_fd, fd, offset, len, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int DmabufExporter::sync(int ion_fd, int fd, int direction, int sync) { |
| if (version == ION_LEGACY_VERSION) |
| return sync_fd(ion_fd, fd); |
| |
| struct dma_buf_sync data; |
| |
| direction &= (ION_SYNC_READ | ION_SYNC_WRITE); |
| data.flags = sync | direction; |
| |
| if (systemInterface.Ioctl(fd, DMA_BUF_IOCTL_SYNC, &data) < 0) { |
| ALOGE("%s(%d, %llu) failed: %m", __func__, fd, data.flags); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| DmabufExporter& getDefaultExporter(void) { |
| static DefaultSystemInterface systemInterface; |
| static DmabufExporter exporter(systemInterface); |
| |
| return exporter; |
| } |
| |
| int exynos_ion_open() { |
| return getDefaultExporter().open(); |
| } |
| int exynos_ion_close(int fd) { |
| return getDefaultExporter().close(fd); |
| } |
| int exynos_ion_alloc(int ion_fd, size_t len, unsigned int heap_mask, unsigned int flags) { |
| return getDefaultExporter().alloc(ion_fd, len, heap_mask, flags); |
| } |
| int exynos_ion_import_handle(int ion_fd, int fd, int* handle) { |
| return getDefaultExporter().import_handle(ion_fd, fd, handle); |
| } |
| int exynos_ion_free_handle(int ion_fd, int handle) { |
| return getDefaultExporter().free_handle(ion_fd, handle); |
| } |
| int exynos_ion_sync_fd(int ion_fd, int fd) { |
| return getDefaultExporter().sync_fd(ion_fd, fd); |
| } |
| int exynos_ion_sync_fd_partial(int ion_fd, int fd, off_t offset, size_t len) { |
| return getDefaultExporter().sync_fd_partial(ion_fd, fd, offset, len); |
| } |
| int exynos_ion_sync_start(int ion_fd, int fd, int direction) { |
| return getDefaultExporter().sync(ion_fd, fd, direction, DMA_BUF_SYNC_START); |
| } |
| int exynos_ion_sync_end(int ion_fd, int fd, int direction) { |
| return getDefaultExporter().sync(ion_fd, fd, direction, DMA_BUF_SYNC_END); |
| } |