| /* |
| * Copyright (C) 2020 Intel Corporation. |
| * |
| * 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. |
| */ |
| |
| #define LOG_TAG "CIPR_CONTEXT" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "iutils/CameraLog.h" |
| #include "iutils/Utils.h" |
| |
| using icamera::CAMERA_DEBUG_LOG_DBG; |
| using icamera::CAMERA_DEBUG_LOG_ERR; |
| using icamera::CAMERA_DEBUG_LOG_VERBOSE; |
| using icamera::CAMERA_DEBUG_LOG_WARNING; |
| |
| #include "modules/ia_cipr/include/Context.h" |
| #include "modules/ia_cipr/include/ipu-psys.h" |
| |
| const char* DRIVER_NAME = "/dev/ipu-psys0"; |
| |
| namespace icamera { |
| namespace CIPR { |
| Result Context::allocate(MemoryDesc* mem) { |
| CheckError(!mem, Result::InvaildArg, "@%s, mem is nullptr", __func__); |
| CheckError(mem->cpuPtr, Result::InvaildArg, "%s: already has an address!", __func__); |
| |
| mem->cpuPtr = CIPR::mallocAlignedMemory(mem->size, CIPR::getPageSize()); |
| CheckError(!mem->cpuPtr, Result::NoMemory, "@%s, mem is out", __func__); |
| |
| if (!(mem->flags & MemoryFlag::Uninitialized)) { |
| memset(mem->cpuPtr, 0, mem->size); |
| } |
| |
| mem->flags &= ~MemoryFlag::AllocateCpuPtr; |
| mem->flags |= MemoryFlag::CpuPtr | MemoryFlag::Allocated; |
| |
| return Result::OK; |
| } |
| |
| Result Context::migrate(MemoryDesc* mem) { |
| CheckError(!mem, Result::InvaildArg, "@%s, mem is nullptr", __func__); |
| |
| if (mem->flags & MemoryFlag::AllocateCpuPtr) { |
| Result ret = allocate(mem); |
| CheckError(ret != Result::OK, ret, "@%s: allocate failed.", __func__); |
| } |
| |
| if (mem->flags & MemoryFlag::PSysAPI) { |
| return Result::OK; |
| } |
| |
| return registerBuffer(mem); |
| } |
| |
| Result Context::getMemory(MemoryDesc* mem, MemoryDesc* out) { |
| CheckError(!mem, Result::InvaildArg, "@%s, mem is nullptr", __func__); |
| CheckError(!out, Result::InvaildArg, "@%s, out is nullptr", __func__); |
| |
| if (!(mInitFlag == Context::Flags::DEBUG) && (mem->flags & MemoryFlag::HardwareOnly)) { |
| LOG2("%s: host cannot access HW only memory!", __func__); |
| return Result::GeneralError; |
| } |
| |
| if ((mem->flags & MemoryFlag::MemoryHandle) && !(mem->flags & MemoryFlag::CpuPtr)) { |
| LOG2("%s: fallback host address mapping not implemented!", __func__); |
| return Result::GeneralError; |
| } |
| |
| *out = *mem; |
| return Result::OK; |
| } |
| |
| Result Context::destroy(MemoryDesc* mem) { |
| return unregisterBuffer(mem); |
| } |
| |
| Context::Context() { |
| mInitialized = false; |
| mFd = open(DRIVER_NAME, 0, O_RDWR | O_NONBLOCK); |
| int errnoCopy = errno; |
| |
| CheckError(mFd < 0, VOID_VALUE, "%s: Failed to open PSYS device! open returned error: %s", |
| __func__, strerror(errnoCopy)); |
| |
| mInitFlag = Context::Flags::NONE; |
| mInitialized = true; |
| } |
| |
| Context::~Context() { |
| if (!mInitialized) { |
| return; |
| } |
| |
| mInitialized = false; |
| |
| int rv = ::close(mFd); |
| int errnoCopy = errno; |
| |
| CheckError(rv < 0, VOID_VALUE, "Close returned error: %s", strerror(errnoCopy)); |
| } |
| |
| Result Context::getCapabilities(PSYSCapability* cap) { |
| struct ipu_psys_capability psys_capability = {}; |
| |
| CheckError(!mInitialized, Result::InternalError, "@%s, mInitialized is false", __func__); |
| CheckError(!cap, Result::InvaildArg, "@%s, cap is nullptr", __func__); |
| |
| Result ret = doIoctl(static_cast<int>(IPU_IOC_QUERYCAP), &psys_capability); |
| CheckError(ret != Result::OK, ret, "%s: failed to retrieve capabilities", __func__); |
| |
| cap->version = psys_capability.version; |
| CIPR::memoryCopy(cap->driver, sizeof(cap->driver), psys_capability.driver, |
| sizeof(psys_capability.driver)); |
| |
| CIPR::memoryCopy(cap->devModel, sizeof(cap->devModel), psys_capability.dev_model, |
| sizeof(psys_capability.dev_model)); |
| |
| cap->programGroupCount = psys_capability.pg_count; |
| |
| return Result::OK; |
| } |
| |
| Result Context::getManifest(uint32_t index, uint32_t* mainfestSize, void* manifest) { |
| struct ipu_psys_manifest pg_manifest = {}; |
| |
| CheckError(!mInitialized, Result::InternalError, "@%s, mInitialized is false", __func__); |
| CheckError(!mainfestSize, Result::InvaildArg, "@%s, mainfestSize is nullptr", __func__); |
| |
| pg_manifest.index = index; |
| pg_manifest.size = 0; |
| pg_manifest.manifest = manifest; |
| |
| Result ret = doIoctl(static_cast<int>(IPU_IOC_GET_MANIFEST), &pg_manifest); |
| CheckError(ret != Result::OK, ret, "%s: Failed to retrieve manifest with index %d", __func__, |
| index); |
| |
| *mainfestSize = pg_manifest.size; |
| return Result::OK; |
| } |
| |
| Result Context::doIoctl(int request, void* ptr) { |
| CheckError(!mInitialized, Result::InternalError, "@%s, mInitialized is false", __func__); |
| |
| #ifdef ANDROID |
| int res = ::ioctl(mFd, request, ptr); |
| #else |
| int res = ::ioctl(mFd, (unsigned int)request, ptr); |
| #endif |
| int errnoCopy = errno; |
| |
| if (res < 0) { |
| LOG2("Ioctl returned error: %s", strerror(errnoCopy)); |
| switch (errnoCopy) { |
| case ENOMEM: |
| return Result::NoMemory; |
| case EINVAL: |
| return Result::InvaildArg; |
| case EFAULT: |
| return Result::InvaildArg; |
| case ENOENT: |
| return Result::NoEntry; |
| default: |
| return Result::GeneralError; |
| } |
| } |
| return Result::OK; |
| } |
| |
| Result Context::registerBuffer(MemoryDesc* mem) { |
| Result res; |
| |
| CheckError(!mInitialized, Result::InternalError, "@%s, mInitialized is false", __func__); |
| CheckError(!mem, Result::InvaildArg, "@%s, mem is nullptr", __func__); |
| CheckError(mem->flags & MemoryFlag::Migrated, Result::InvaildArg, |
| "Buffer already migrated with device."); |
| |
| struct ipu_psys_buffer* ioc_buffer = |
| reinterpret_cast<ipu_psys_buffer*>(callocMemory(1, sizeof(*ioc_buffer))); |
| CheckError(!ioc_buffer, Result::NoMemory, "Could not create psys buffer"); |
| |
| ioc_buffer->len = mem->size; |
| if (mem->flags & MemoryFlag::CpuPtr) { |
| ioc_buffer->base.userptr = mem->cpuPtr; |
| ioc_buffer->flags |= IPU_BUFFER_FLAG_USERPTR; |
| res = doIoctl(static_cast<int>(IPU_IOC_GETBUF), ioc_buffer); |
| |
| if (res != Result::OK) { |
| CIPR::freeMemory(ioc_buffer); |
| return res; |
| } |
| |
| if (!(ioc_buffer->flags & IPU_BUFFER_FLAG_DMA_HANDLE)) { |
| LOG2("CIPR: IOC_GETBUF succeed but did not return dma handle"); |
| CIPR::freeMemory(ioc_buffer); |
| return Result::InternalError; |
| } else if (ioc_buffer->flags & IPU_BUFFER_FLAG_USERPTR) { |
| LOG2("CIPR: IOC_GETBUF succeed but did not consume the userptr flag"); |
| CIPR::freeMemory(ioc_buffer); |
| return Result::InternalError; |
| } |
| } else if (mem->flags & MemoryFlag::MemoryHandle) { |
| ioc_buffer->base.fd = static_cast<int>(mem->handle); |
| ioc_buffer->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; |
| } |
| |
| #ifdef IPU_SYSVER_ipu6v3 |
| ioc_buffer->flags |= IPU_BUFFER_FLAG_NO_FLUSH; |
| #else |
| if (mem->flags & MemoryFlag::NoFlush) { |
| ioc_buffer->flags |= IPU_BUFFER_FLAG_NO_FLUSH; |
| } |
| #endif |
| |
| res = doIoctl(static_cast<int>(IPU_IOC_MAPBUF), |
| reinterpret_cast<void*>((intptr_t)ioc_buffer->base.fd)); |
| |
| if (res != Result::OK) { |
| CIPR::freeMemory(ioc_buffer); |
| return res; |
| } |
| |
| mem->sysBuff = ioc_buffer; |
| mem->flags |= MemoryFlag::Migrated; |
| |
| LOG2("%s: registered %p -> fd %d size:%u offset:%u bytes_used:%u", __func__, |
| mem->cpuPtr, ioc_buffer->base.fd, ioc_buffer->len, ioc_buffer->data_offset, |
| ioc_buffer->bytes_used); |
| |
| return res; |
| } |
| |
| Result Context::unregisterBuffer(MemoryDesc* mem) { |
| CheckError(!mInitialized, Result::InternalError, "@%s, mInitialized == false", __func__); |
| CheckError(!mem, Result::InvaildArg, "@%s, mem is nullptr", __func__); |
| |
| if (mem->sysBuff == nullptr) { |
| return Result::OK; |
| } |
| |
| struct ipu_psys_buffer* ioc_buffer = mem->sysBuff; |
| CheckError(!(ioc_buffer->flags & IPU_BUFFER_FLAG_DMA_HANDLE), Result::GeneralError, |
| "Wrong flag!", __func__); |
| |
| Result res = doIoctl(static_cast<int>(IPU_IOC_UNMAPBUF), |
| reinterpret_cast<void*>((intptr_t)ioc_buffer->base.fd)); |
| if (res != Result::OK) { |
| LOG2("%s: cannot unmap buffer fd %d, possibly already unmapped", __func__, |
| ioc_buffer->base.fd); |
| } |
| |
| if (mem->flags & MemoryFlag::CpuPtr) { |
| res = psysClose(ioc_buffer->base.fd); |
| CheckError(res != Result::OK, res, "@%s: pSysClose failed", __func__); |
| } |
| |
| CIPR::freeMemory(ioc_buffer); |
| mem->sysBuff = nullptr; |
| |
| return res; |
| } |
| |
| Result Context::psysClose(int fd) { |
| int res = close(fd); |
| int error = errno; |
| |
| if (res < 0) { |
| LOG2("Close returned error: %s", strerror(error)); |
| switch (error) { |
| case EBADF: |
| return Result::InvaildArg; |
| case EIO: |
| case EINTR: |
| default: |
| return Result::GeneralError; |
| } |
| } |
| |
| return Result::OK; |
| } |
| |
| ContextPoller Context::getPoller(int events, int timeout) { |
| ContextPoller poller; |
| poller.mFd = mFd; |
| poller.mEvents = events; |
| poller.mTimeout = timeout; |
| |
| return poller; |
| } |
| |
| int ContextPoller::poll() { |
| struct pollfd fds; |
| |
| fds.fd = mFd; |
| fds.events = mEvents; |
| |
| return ::poll(&fds, 1, mTimeout); |
| } |
| } // namespace CIPR |
| } // namespace icamera |