blob: 95f215bd783f1ef3d8678dc709a4fac948f160f6 [file] [log] [blame]
/*
* Copyright 2017 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "common/camera_algorithm_bridge_impl.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <mojo/public/cpp/system/platform_handle.h>
#include "cros-camera/common.h"
#include "cros-camera/constants.h"
namespace cros {
// static
std::unique_ptr<CameraAlgorithmBridge> CameraAlgorithmBridge::CreateInstance(
CameraAlgorithmBackend backend) {
return CameraAlgorithmBridge::CreateInstance(
backend, CameraMojoChannelManager::GetInstance());
}
// static
std::unique_ptr<CameraAlgorithmBridge> CameraAlgorithmBridge::CreateInstance(
CameraAlgorithmBackend backend, CameraMojoChannelManagerToken* token) {
VLOGF_ENTER();
return std::make_unique<CameraAlgorithmBridgeImpl>(
backend, CameraMojoChannelManager::FromToken(token));
}
CameraAlgorithmBridgeImpl::CameraAlgorithmBridgeImpl(
CameraAlgorithmBackend backend, CameraMojoChannelManager* mojo_manager)
: mojo_manager_(mojo_manager),
ipc_bridge_(new IPCBridge(backend, mojo_manager)) {}
CameraAlgorithmBridgeImpl::~CameraAlgorithmBridgeImpl() {
VLOGF_ENTER();
bool result = mojo_manager_->GetIpcTaskRunner()->DeleteSoon(
FROM_HERE, std::move(ipc_bridge_));
DCHECK(result);
VLOGF_EXIT();
}
int32_t CameraAlgorithmBridgeImpl::Initialize(
const camera_algorithm_callback_ops_t* callback_ops) {
VLOGF_ENTER();
const int32_t kInitializationRetryTimeoutMs = 3000;
const int32_t kInitializationWaitConnectionMs = 500;
const int32_t kInitializationRetrySleepUs = 100000;
auto get_elapsed_ms = [](struct timespec& start) {
struct timespec stop = {};
if (clock_gettime(CLOCK_MONOTONIC, &stop)) {
LOG(ERROR) << "Failed to get clock time";
return 0L;
}
return (stop.tv_sec - start.tv_sec) * 1000 +
(stop.tv_nsec - start.tv_nsec) / 1000000;
};
struct timespec start_ts = {};
if (clock_gettime(CLOCK_MONOTONIC, &start_ts)) {
LOG(ERROR) << "Failed to get clock time";
}
int ret = 0;
do {
int32_t elapsed_ms = get_elapsed_ms(start_ts);
if (elapsed_ms >= kInitializationRetryTimeoutMs) {
ret = -ETIMEDOUT;
break;
}
auto future = cros::Future<int32_t>::Create(&relay_);
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE, base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::Initialize,
ipc_bridge_->GetWeakPtr(), callback_ops,
cros::GetFutureCallback(future)));
if (future->Wait(std::min(kInitializationWaitConnectionMs,
kInitializationRetryTimeoutMs - elapsed_ms))) {
ret = future->Get();
if (ret == 0 || ret == -EINVAL) {
break;
}
}
usleep(kInitializationRetrySleepUs);
} while (1);
VLOGF_EXIT();
return ret;
}
int32_t CameraAlgorithmBridgeImpl::RegisterBuffer(int buffer_fd) {
VLOGF_ENTER();
auto future = cros::Future<int32_t>::Create(&relay_);
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::RegisterBuffer,
ipc_bridge_->GetWeakPtr(), buffer_fd,
cros::GetFutureCallback(future)));
future->Wait();
VLOGF_EXIT();
return future->Get();
}
void CameraAlgorithmBridgeImpl::Request(uint32_t req_id,
const std::vector<uint8_t>& req_header,
int32_t buffer_handle) {
VLOGF_ENTER();
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::Request,
ipc_bridge_->GetWeakPtr(), req_id, req_header, buffer_handle));
VLOGF_EXIT();
}
void CameraAlgorithmBridgeImpl::DeregisterBuffers(
const std::vector<int32_t>& buffer_handles) {
VLOGF_ENTER();
mojo_manager_->GetIpcTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CameraAlgorithmBridgeImpl::IPCBridge::DeregisterBuffers,
ipc_bridge_->GetWeakPtr(), buffer_handles));
VLOGF_EXIT();
}
CameraAlgorithmBridgeImpl::IPCBridge::IPCBridge(
CameraAlgorithmBackend backend, CameraMojoChannelManager* mojo_manager)
: algo_backend_(backend),
callback_ops_(nullptr),
mojo_manager_(mojo_manager),
ipc_task_runner_(mojo_manager_->GetIpcTaskRunner()) {}
CameraAlgorithmBridgeImpl::IPCBridge::~IPCBridge() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
Destroy();
}
void CameraAlgorithmBridgeImpl::IPCBridge::Initialize(
const camera_algorithm_callback_ops_t* callback_ops,
base::Callback<void(int32_t)> cb) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
if (!callback_ops || !callback_ops->return_callback) {
cb.Run(-EINVAL);
return;
}
if (cb_impl_) {
LOGF(WARNING)
<< "Camera algorithm bridge is already initialized. Reinitializing...";
Destroy();
}
constexpr char kGpuAlgoJobFilePath[] = "/etc/init/cros-camera-gpu-algo.conf";
switch (algo_backend_) {
case CameraAlgorithmBackend::kVendorCpu:
interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr(
cros::constants::kCrosCameraAlgoSocketPathString, "vendor_cpu");
break;
case CameraAlgorithmBackend::kVendorGpu:
if (!base::PathExists(base::FilePath(kGpuAlgoJobFilePath))) {
cb.Run(-EINVAL);
return;
}
interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr(
cros::constants::kCrosCameraGPUAlgoSocketPathString, "vendor_gpu");
break;
case CameraAlgorithmBackend::kGoogleGpu:
if (!base::PathExists(base::FilePath(kGpuAlgoJobFilePath))) {
cb.Run(-EINVAL);
return;
}
interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr(
cros::constants::kCrosCameraGPUAlgoSocketPathString, "google_gpu");
break;
case CameraAlgorithmBackend::kTest:
interface_ptr_ = mojo_manager_->CreateCameraAlgorithmOpsPtr(
cros::constants::kCrosCameraAlgoSocketPathString, "test");
break;
}
if (!interface_ptr_) {
LOGF(ERROR) << "Failed to connect to the server";
cb.Run(-EAGAIN);
return;
}
interface_ptr_.set_connection_error_handler(base::Bind(
&CameraAlgorithmBridgeImpl::IPCBridge::OnConnectionError, GetWeakPtr()));
cb_impl_.reset(
new CameraAlgorithmCallbackOpsImpl(ipc_task_runner_, callback_ops));
interface_ptr_->Initialize(cb_impl_->CreateInterfacePtr(), cb);
callback_ops_ = callback_ops;
VLOGF_EXIT();
}
void CameraAlgorithmBridgeImpl::IPCBridge::RegisterBuffer(
int buffer_fd, base::Callback<void(int32_t)> cb) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
if (!interface_ptr_.is_bound()) {
LOGF(ERROR) << "Interface is not bound probably because IPC is broken";
cb.Run(-ECONNRESET);
return;
}
int dup_fd = dup(buffer_fd);
if (dup_fd < 0) {
PLOGF(ERROR) << "Failed to dup fd: ";
cb.Run(-errno);
return;
}
interface_ptr_->RegisterBuffer(
mojo::WrapPlatformFile(base::ScopedPlatformFile(dup_fd)), cb);
}
void CameraAlgorithmBridgeImpl::IPCBridge::Request(
uint32_t req_id, std::vector<uint8_t> req_header, int32_t buffer_handle) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
if (!interface_ptr_.is_bound()) {
LOGF(ERROR) << "Interface is not bound probably because IPC is broken";
return;
}
interface_ptr_->Request(req_id, std::move(req_header), buffer_handle);
VLOGF_EXIT();
}
void CameraAlgorithmBridgeImpl::IPCBridge::DeregisterBuffers(
std::vector<int32_t> buffer_handles) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
if (!interface_ptr_.is_bound()) {
LOGF(ERROR) << "Interface is not bound probably because IPC is broken";
return;
}
interface_ptr_->DeregisterBuffers(std::move(buffer_handles));
}
void CameraAlgorithmBridgeImpl::IPCBridge::OnConnectionError() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK(callback_ops_);
VLOGF_ENTER();
Destroy();
if (callback_ops_->notify) {
callback_ops_->notify(callback_ops_, CAMERA_ALGORITHM_MSG_IPC_ERROR);
}
VLOGF_EXIT();
}
void CameraAlgorithmBridgeImpl::IPCBridge::Destroy() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
VLOGF_ENTER();
if (interface_ptr_.is_bound()) {
cb_impl_.reset();
interface_ptr_.reset();
}
VLOGF_EXIT();
}
base::WeakPtr<CameraAlgorithmBridgeImpl::IPCBridge>
CameraAlgorithmBridgeImpl::IPCBridge::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
} // namespace cros