blob: 08881dd31d7c0a74f3cbb80de6752b0f5f3d82d9 [file] [log] [blame]
/*
* 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