blob: 44c330fdae75186cf282558ec966e6be6108e1ce [file] [log] [blame]
/*
* Copyright (C) 2019-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 "IntelAlgoClient"
#include "modules/sandboxing/client/IntelAlgoClient.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <memory>
#include <string>
#include <vector>
#include "Parameters.h"
#include "PlatformData.h"
#include "iutils/Errors.h"
#include "iutils/Utils.h"
namespace icamera {
IntelAlgoClient* IntelAlgoClient::sInstance = nullptr;
Mutex IntelAlgoClient::sLock;
IntelAlgoClient* IntelAlgoClient::getInstance() {
AutoMutex lock(sLock);
if (!sInstance) {
sInstance = new IntelAlgoClient;
}
return sInstance;
}
void IntelAlgoClient::releaseInstance() {
AutoMutex lock(sLock);
if (sInstance) {
delete sInstance;
sInstance = nullptr;
}
}
IntelAlgoClient::IntelAlgoClient()
: mErrCb(nullptr),
mGpuBridge(nullptr),
mIPCStatus(true),
mMojoManager(nullptr),
mInitialized(false) {
LOGIPC("@%s", __func__);
}
IntelAlgoClient::~IntelAlgoClient() {
LOGIPC("@%s", __func__);
}
int IntelAlgoClient::initialize() {
LOGIPC("@%s, mMojoManager: %p", __func__, mMojoManager);
CheckError(!mMojoManager, UNKNOWN_ERROR, "@%s, mMojoManager is nullptr", __func__);
mCallback = base::Bind(&IntelAlgoClient::callbackHandler, base::Unretained(this));
IntelAlgoClient::return_callback = returnCallback;
mNotifyCallback = base::Bind(&IntelAlgoClient::notifyHandler, base::Unretained(this));
IntelAlgoClient::notify = notifyCallback;
mBridge = cros::CameraAlgorithmBridge::CreateInstance(cros::CameraAlgorithmBackend::kVendorCpu,
mMojoManager);
CheckError(!mBridge, UNKNOWN_ERROR, "@%s, mBridge is nullptr", __func__);
CheckError(mBridge->Initialize(this) != 0, UNKNOWN_ERROR, "@%s, mBridge init fails", __func__);
if (PlatformData::isUsingGpuAlgo()) {
LOGIPC("@%s GPU algo enabled", __func__);
mGpuBridge = cros::CameraAlgorithmBridge::CreateInstance(
cros::CameraAlgorithmBackend::kVendorGpu, mMojoManager);
CheckError(!mGpuBridge, UNKNOWN_ERROR, "@%s, mGpuBridge is nullptr", __func__);
CheckError(mGpuBridge->Initialize(this) != 0, UNKNOWN_ERROR, "@%s, mGpuBridge init fails",
__func__);
}
for (int i = 0; i < IPC_GROUP_NUM; i++) {
if (static_cast<IPC_GROUP>(i) != IPC_GROUP_GPU) {
mRunner[i] =
std::unique_ptr<Runner>(new Runner(static_cast<IPC_GROUP>(i), mBridge.get()));
} else if (mGpuBridge) {
mRunner[i] = std::unique_ptr<Runner>(new Runner(IPC_GROUP_GPU, mGpuBridge.get()));
}
}
mIPCStatus = true;
mInitialized = true;
return OK;
}
bool IntelAlgoClient::isIPCFine() {
std::lock_guard<std::mutex> l(mIPCStatusMutex);
LOGIPC("@%s, mIPCStatus:%d", __func__, mIPCStatus);
return mIPCStatus;
}
void IntelAlgoClient::registerErrorCallback(const camera_callback_ops_t* errCb) {
LOGIPC("@%s, errCb:%p", __func__, errCb);
std::lock_guard<std::mutex> l(mIPCStatusMutex);
mErrCb = errCb;
if (!mIPCStatus && mErrCb) {
camera_msg_data_t data = {CAMERA_IPC_ERROR, {0}};
mErrCb->notify(mErrCb, data);
}
}
int IntelAlgoClient::allocateShmMem(const std::string& name, int size, int* fd, void** addr) {
LOGIPC("@%s, name:%s, size:%d", __func__, name.c_str(), size);
*fd = -1;
*addr = nullptr;
int shmFd = -1;
void* shmAddr = nullptr;
shmFd = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
CheckError((shmFd == -1), UNKNOWN_ERROR, "@%s, call shm_open fail", __func__);
do {
int ret = fcntl(shmFd, F_GETFD);
if (ret == -1) {
LOGE("@%s, call fcntl fail, error %s", __func__, strerror(errno));
break;
}
ret = ftruncate(shmFd, size);
if (ret == -1) {
LOGE("@%s, call ftruncate fail, error %s", __func__, strerror(errno));
break;
}
struct stat sb;
ret = fstat(shmFd, &sb);
if (ret == -1) {
LOGE("@%s, call fstat fail, error %s", __func__, strerror(errno));
break;
}
shmAddr = mmap(0, sb.st_size, PROT_WRITE, MAP_SHARED, shmFd, 0);
if (!shmAddr) {
LOGE("@%s, call mmap fail, error %s", __func__, strerror(errno));
break;
}
*fd = shmFd;
*addr = shmAddr;
return OK;
} while (0);
close(shmFd);
return UNKNOWN_ERROR;
}
void IntelAlgoClient::releaseShmMem(const std::string& name, int size, int fd, void* addr) {
LOGIPC("@%s, name:%s, size:%d, fd:%d, addr:%p", __func__, name.c_str(), size, fd, addr);
munmap(addr, size);
close(fd);
shm_unlink(name.c_str());
}
int IntelAlgoClient::requestSync(IPC_CMD cmd, int32_t bufferHandle) {
LOGIPC("@%s, cmd:%d:%s, bufferHandle:%d, mInitialized:%d", __func__, cmd,
IntelAlgoIpcCmdToString(cmd), bufferHandle, mInitialized);
CheckError(!mInitialized, UNKNOWN_ERROR, "@%s, mInitialized is false", __func__);
CheckError(!isIPCFine(), UNKNOWN_ERROR, "@%s, IPC error happens", __func__);
IPC_GROUP group = IntelAlgoIpcCmdToGroup(cmd);
return mRunner[group]->requestSync(cmd, bufferHandle);
}
int IntelAlgoClient::requestSync(IPC_CMD cmd) {
return requestSync(cmd, -1);
}
int32_t IntelAlgoClient::registerBuffer(int bufferFd, void* addr, ShmMemUsage usage) {
LOGIPC("@%s, bufferFd: %d, mInitialized: %d, addr: %p, usage: %d", __func__, bufferFd,
mInitialized, addr, usage);
CheckError(!mInitialized, -1, "@%s, mInitialized is false", __func__);
CheckError(usage >= MAX_ALGO_SHM, -1, "@%s, usage: %d isn't supported", __func__, usage);
CheckError(!isIPCFine(), -1, "@%s, IPC error happens", __func__);
int32_t handle = -1;
if (usage == CPU_ALGO_SHM) {
handle = mBridge->RegisterBuffer(bufferFd);
} else if (mGpuBridge) {
handle = mGpuBridge->RegisterBuffer(bufferFd);
}
if (handle >= 0) {
std::lock_guard<std::mutex> l(mShmMapMutex);
mShmMap[usage][addr] = handle;
}
return handle;
}
void IntelAlgoClient::deregisterBuffer(int32_t bufferHandle, ShmMemUsage usage) {
LOGIPC("@%s, bufferHandle: %d, mInitialized: %d, usage: %d", __func__, bufferHandle,
mInitialized, usage);
CheckError(!mInitialized, VOID_VALUE, "@%s, mInitialized is false", __func__);
CheckError(usage >= MAX_ALGO_SHM, VOID_VALUE, "@%s, usage: %d isn't supported", __func__,
usage);
CheckError(!isIPCFine(), VOID_VALUE, "@%s, IPC error happens", __func__);
{
std::lock_guard<std::mutex> l(mShmMapMutex);
for (auto& item : mShmMap[usage]) {
if (item.second == bufferHandle) {
mShmMap[usage].erase(item.first);
break;
}
}
}
std::vector<int32_t> handles({bufferHandle});
if (usage == CPU_ALGO_SHM) {
mBridge->DeregisterBuffers(handles);
} else if (mGpuBridge) {
mGpuBridge->DeregisterBuffers(handles);
}
}
int32_t IntelAlgoClient::registerGbmBuffer(int bufferFd, ShmMemUsage usage) {
LOGIPC("@%s, bufferFd:%d, mInitialized:%d", __func__, bufferFd, mInitialized);
CheckError(!mInitialized, -1, "@%s, mInitialized is false", __func__);
CheckError(!isIPCFine(), -1, "@%s, IPC error happens", __func__);
if (usage == CPU_ALGO_SHM) {
return mBridge->RegisterBuffer(bufferFd);
} else if (mGpuBridge) {
return mGpuBridge->RegisterBuffer(bufferFd);
}
return OK;
}
void IntelAlgoClient::deregisterGbmBuffer(int32_t bufferHandle, ShmMemUsage usage) {
LOGIPC("@%s, bufferHandle:%d, mInitialized:%d", __func__, bufferHandle, mInitialized);
CheckError(!mInitialized, VOID_VALUE, "@%s, mInitialized is false", __func__);
CheckError(!isIPCFine(), VOID_VALUE, "@%s, IPC error happens", __func__);
std::vector<int32_t> handles({bufferHandle});
if (usage == CPU_ALGO_SHM) {
mBridge->DeregisterBuffers(handles);
} else if (mGpuBridge) {
mGpuBridge->DeregisterBuffers(handles);
}
}
int32_t IntelAlgoClient::getBufferHandle(void* addr, ShmMemUsage usage) {
CheckError(!mInitialized, -1, "@%s, mInitialized is false", __func__);
CheckError(usage >= MAX_ALGO_SHM, -1, "@%s, usage: %d isn't supported", __func__, usage);
if (!addr) return -1;
LOGIPC("%s, the buffer addr: %p, usage: %d", __func__, addr, usage);
std::lock_guard<std::mutex> l(mShmMapMutex);
CheckError(mShmMap[usage].find(addr) == mShmMap[usage].end(), -1,
"%s, Invalid client addr: %p, usage: %d", __func__, addr, usage);
return mShmMap[usage][addr];
}
void IntelAlgoClient::callbackHandler(uint32_t req_id, uint32_t status, int32_t buffer_handle) {
LOGIPC("@%s, req_id:%d, status:%d, buffer_handle:%d", __func__, req_id, status, buffer_handle);
IPC_GROUP group = IntelAlgoIpcCmdToGroup(static_cast<IPC_CMD>(req_id));
mRunner[group]->callbackHandler(status, buffer_handle);
}
void IntelAlgoClient::notifyHandler(uint32_t msg) {
LOGIPC("@%s, msg:%d", __func__, msg);
if (msg != CAMERA_ALGORITHM_MSG_IPC_ERROR) {
LOGE("@%s, receive msg:%d, not CAMERA_ALGORITHM_MSG_IPC_ERROR", __func__, msg);
return;
}
std::lock_guard<std::mutex> l(mIPCStatusMutex);
mIPCStatus = false;
if (mErrCb) {
camera_msg_data_t data = {CAMERA_IPC_ERROR, {0}};
mErrCb->notify(mErrCb, data);
} else {
LOGE("@%s, mErrCb is nullptr, no device error is sent out", __func__);
}
LOGE("@%s, receive CAMERA_ALGORITHM_MSG_IPC_ERROR", __func__);
}
void IntelAlgoClient::returnCallback(const camera_algorithm_callback_ops_t* callback_ops,
uint32_t req_id, uint32_t status, int32_t buffer_handle) {
LOGIPC("@%s", __func__);
CheckError(!callback_ops, VOID_VALUE, "@%s, callback_ops is nullptr", __func__);
auto s = const_cast<IntelAlgoClient*>(static_cast<const IntelAlgoClient*>(callback_ops));
s->callbackHandler(req_id, status, buffer_handle);
}
void IntelAlgoClient::notifyCallback(const struct camera_algorithm_callback_ops* callback_ops,
camera_algorithm_error_msg_code_t msg) {
LOGIPC("@%s", __func__);
CheckError(!callback_ops, VOID_VALUE, "@%s, callback_ops is nullptr", __func__);
auto s = const_cast<IntelAlgoClient*>(static_cast<const IntelAlgoClient*>(callback_ops));
s->notifyHandler((uint32_t)msg);
}
IntelAlgoClient::Runner::Runner(IPC_GROUP group, cros::CameraAlgorithmBridge* bridge)
: mGroup(group),
mBridge(bridge),
mIsCallbacked(false),
mCbResult(true),
mInitialized(false) {
LOGIPC("@%s, group:%d", __func__, mGroup);
pthread_condattr_t attr;
int ret = pthread_condattr_init(&attr);
if (ret != 0) {
LOGE("@%s, call pthread_condattr_init fails, ret:%d", __func__, ret);
pthread_condattr_destroy(&attr);
return;
}
ret = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
if (ret != 0) {
LOGE("@%s, call pthread_condattr_setclock fails, ret:%d", __func__, ret);
pthread_condattr_destroy(&attr);
return;
}
ret = pthread_cond_init(&mCbCond, &attr);
if (ret != 0) {
LOGE("@%s, call pthread_cond_init fails, ret:%d", __func__, ret);
pthread_condattr_destroy(&attr);
return;
}
pthread_condattr_destroy(&attr);
ret = pthread_mutex_init(&mCbLock, nullptr);
CheckError(ret != 0, VOID_VALUE, "@%s, call pthread_mutex_init fails, ret:%d", __func__, ret);
mInitialized = true;
}
IntelAlgoClient::Runner::~Runner() {
LOGIPC("@%s, group:%d", __func__, mGroup);
int ret = pthread_cond_destroy(&mCbCond);
if (ret != 0) {
LOGE("@%s, call pthread_cond_destroy fails, ret:%d", __func__, ret);
}
ret = pthread_mutex_destroy(&mCbLock);
if (ret != 0) {
LOGE("@%s, call pthread_mutex_destroy fails, ret:%d", __func__, ret);
}
}
int IntelAlgoClient::Runner::requestSync(IPC_CMD cmd, int32_t bufferHandle) {
LOGIPC("@%s, cmd:%d:%s, group:%d, bufferHandle:%d, mInitialized:%d", __func__, cmd,
IntelAlgoIpcCmdToString(cmd), mGroup, bufferHandle, mInitialized);
CheckError(!mInitialized, UNKNOWN_ERROR, "@%s, mInitialized is false, cmd:%d:%s", __func__, cmd,
IntelAlgoIpcCmdToString(cmd));
std::lock_guard<std::mutex> lck(mMutex);
std::vector<uint8_t> reqHeader(IPC_REQUEST_HEADER_USED_NUM);
reqHeader[0] = IPC_MATCHING_KEY;
// cmd is for request id, no duplicate command will be issued at any given time.
mBridge->Request(cmd, reqHeader, bufferHandle);
int ret = waitCallback();
CheckError((ret != OK), UNKNOWN_ERROR, "@%s, waitCallback fails, cmd:%d:%s", __func__, cmd,
IntelAlgoIpcCmdToString(cmd));
LOGIPC("@%s, cmd:%d:%s, group:%d, mCbResult:%d, done!", __func__, cmd,
IntelAlgoIpcCmdToString(cmd), mGroup, mCbResult);
// check callback result
CheckError((mCbResult != true), UNKNOWN_ERROR, "@%s, callback fails, cmd:%d:%s", __func__, cmd,
IntelAlgoIpcCmdToString(cmd));
return OK;
}
void IntelAlgoClient::Runner::callbackHandler(uint32_t status, int32_t buffer_handle) {
LOGIPC("@%s, group:%d, status:%d, buffer_handle:%d", __func__, mGroup, status, buffer_handle);
if (status != 0) {
LOGE("@%s, group:%d, status:%d, buffer_handle:%d", __func__, mGroup, status, buffer_handle);
}
mCbResult = status != 0 ? false : true;
pthread_mutex_lock(&mCbLock);
mIsCallbacked = true;
int ret = pthread_cond_signal(&mCbCond);
pthread_mutex_unlock(&mCbLock);
CheckError(ret != 0, VOID_VALUE, "@%s, group:%d, call pthread_cond_signal fails, ret:%d",
__func__, mGroup, ret);
}
int IntelAlgoClient::Runner::waitCallback() {
LOGIPC("@%s, group:%d", __func__, mGroup);
nsecs_t startTime = CameraUtils::systemTime();
pthread_mutex_lock(&mCbLock);
if (!mIsCallbacked) {
int ret = 0;
struct timespec ts = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += 5; // 5s timeout
while (!mIsCallbacked && !ret) {
ret = pthread_cond_timedwait(&mCbCond, &mCbLock, &ts);
}
if (ret != 0) {
LOGE("@%s, group:%d, call pthread_cond_timedwait fail, ret:%d, it takes %" PRId64 "ms",
__func__, mGroup, ret, (CameraUtils::systemTime() - startTime) / 1000000);
pthread_mutex_unlock(&mCbLock);
return UNKNOWN_ERROR;
}
}
mIsCallbacked = false;
pthread_mutex_unlock(&mCbLock);
LOGIPC("@%s: group:%d, it takes %" PRId64 "ms", __func__, mGroup,
(CameraUtils::systemTime() - startTime) / 1000000);
return OK;
}
} /* namespace icamera */