blob: 2f34bddedce8c55f45ebcd9f7d8be989c39e168e [file] [log] [blame]
/*
* Copyright 2016 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 "hal_adapter/camera_hal_adapter.h"
#include <algorithm>
#include <string>
#include <tuple>
#include <utility>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/logging.h>
#include <base/threading/thread_task_runner_handle.h>
#include <base/time/time.h>
#include "cros-camera/common.h"
#include "cros-camera/future.h"
#include "hal_adapter/camera_device_adapter.h"
#include "hal_adapter/camera_module_callbacks_delegate.h"
#include "hal_adapter/camera_module_delegate.h"
#include "hal_adapter/cros_camera_mojo_utils.h"
namespace cros {
namespace {
// A special id used in ResetModuleDelegateOnThread and
// ResetCallbacksDelegateOnThread to specify all the entries present in the
// |module_delegates_| and |callbacks_delegates_| maps.
const uint32_t kIdAll = 0xFFFFFFFF;
} // namespace
CameraHalAdapter::CameraHalAdapter(std::vector<camera_module_t*> camera_modules)
: camera_modules_(camera_modules),
camera_module_thread_("CameraModuleThread"),
camera_module_callbacks_thread_("CameraModuleCallbacksThread"),
module_id_(0) {
VLOGF_ENTER();
}
CameraHalAdapter::~CameraHalAdapter() {
VLOGF_ENTER();
camera_module_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraHalAdapter::ResetModuleDelegateOnThread,
base::Unretained(this), kIdAll));
camera_module_callbacks_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraHalAdapter::ResetCallbacksDelegateOnThread,
base::Unretained(this), kIdAll));
camera_module_thread_.Stop();
camera_module_callbacks_thread_.Stop();
}
bool CameraHalAdapter::Start() {
VLOGF_ENTER();
if (!camera_module_thread_.Start()) {
LOGF(ERROR) << "Failed to start CameraModuleThread";
return false;
}
if (!camera_module_callbacks_thread_.Start()) {
LOGF(ERROR) << "Failed to start CameraCallbacksThread";
return false;
}
auto future = cros::Future<bool>::Create(nullptr);
camera_module_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraHalAdapter::StartOnThread, base::Unretained(this),
cros::GetFutureCallback(future)));
return future->Get();
}
void CameraHalAdapter::OpenCameraHal(
mojom::CameraModuleRequest camera_module_request) {
VLOGF_ENTER();
auto module_delegate = base::MakeUnique<CameraModuleDelegate>(
this, camera_module_thread_.task_runner());
uint32_t module_id = module_id_++;
module_delegate->Bind(
camera_module_request.PassMessagePipe(),
base::Bind(&CameraHalAdapter::ResetModuleDelegateOnThread,
base::Unretained(this), module_id));
base::AutoLock l(module_delegates_lock_);
module_delegates_[module_id] = std::move(module_delegate);
VLOGF(1) << "CameraModule " << module_id << " connected";
}
// Callback interface for camera_module_t APIs.
int32_t CameraHalAdapter::OpenDevice(
int32_t camera_id, mojom::Camera3DeviceOpsRequest device_ops_request) {
VLOGF_ENTER();
camera_module_t* camera_module;
int internal_camera_id;
std::tie(camera_module, internal_camera_id) =
GetInternalModuleAndId(camera_id);
if (!camera_module) {
return -EINVAL;
}
if (device_adapters_.find(camera_id) != device_adapters_.end()) {
LOGF(WARNING) << "Multiple calls to OpenDevice on device " << camera_id;
return -EBUSY;
}
hw_module_t* common = &camera_module->common;
camera3_device_t* camera_device;
int ret =
common->methods->open(common, std::to_string(internal_camera_id).c_str(),
reinterpret_cast<hw_device_t**>(&camera_device));
if (ret != 0) {
LOGF(ERROR) << "Failed to open camera device " << camera_id;
return ret;
}
// This method is called by |camera_module_delegate_| on its mojo IPC
// handler thread.
// The CameraHalAdapter (and hence |camera_module_delegate_|) must out-live
// the CameraDeviceAdapters, so it's safe to keep a reference to the task
// runner of the current thread in the callback functor.
base::Callback<void()> close_callback =
base::Bind(&CameraHalAdapter::CloseDeviceCallback, base::Unretained(this),
base::ThreadTaskRunnerHandle::Get(), camera_id);
device_adapters_[camera_id].reset(
new CameraDeviceAdapter(camera_device, close_callback));
if (!device_adapters_[camera_id]->Start()) {
device_adapters_.erase(camera_id);
return -ENODEV;
}
device_adapters_.at(camera_id)->Bind(std::move(device_ops_request));
return 0;
}
int32_t CameraHalAdapter::GetNumberOfCameras() {
VLOGF_ENTER();
return camera_id_map_.size();
}
int32_t CameraHalAdapter::GetCameraInfo(int32_t camera_id,
mojom::CameraInfoPtr* camera_info) {
VLOGF_ENTER();
camera_module_t* camera_module;
int internal_camera_id;
std::tie(camera_module, internal_camera_id) =
GetInternalModuleAndId(camera_id);
if (!camera_module) {
camera_info->reset();
return -EINVAL;
}
camera_info_t info;
int ret = camera_module->get_camera_info(internal_camera_id, &info);
if (ret != 0) {
LOGF(ERROR) << "Failed to get info of camera " << camera_id;
camera_info->reset();
return ret;
}
LOGF(INFO) << "camera_id = " << camera_id << ", facing = " << info.facing;
if (VLOG_IS_ON(1)) {
dump_camera_metadata(info.static_camera_characteristics, 2, 3);
}
mojom::CameraInfoPtr info_ptr = mojom::CameraInfo::New();
info_ptr->facing = static_cast<mojom::CameraFacing>(info.facing);
info_ptr->orientation = info.orientation;
info_ptr->device_version = info.device_version;
info_ptr->static_camera_characteristics =
internal::SerializeCameraMetadata(info.static_camera_characteristics);
*camera_info = std::move(info_ptr);
return 0;
}
int32_t CameraHalAdapter::SetCallbacks(
mojom::CameraModuleCallbacksPtr callbacks) {
VLOGF_ENTER();
auto callbacks_delegate = base::MakeUnique<CameraModuleCallbacksDelegate>(
camera_module_callbacks_thread_.task_runner());
uint32_t callbacks_id = callbacks_id_++;
callbacks_delegate->Bind(
callbacks.PassInterface(),
base::Bind(&CameraHalAdapter::ResetCallbacksDelegateOnThread,
base::Unretained(this), callbacks_id));
base::AutoLock l(callbacks_delegates_lock_);
if (callbacks_delegates_.empty()) {
// To prevent some callbacks being fired too early, the call of
// set_callbacks is delayed until the first client try to do so. Note that
// the other clients that comes later might still miss some callbacks, and
// may cause some problems for external camera support in the future.
for (size_t i = 0; i < camera_modules_.size(); i++) {
auto aux = base::MakeUnique<CameraModuleCallbacksAux>();
aux->camera_device_status_change = CameraDeviceStatusChange;
aux->torch_mode_status_change = TorchModeStatusChange;
aux->module_id = i;
aux->adapter = this;
int ret = camera_modules_[i]->set_callbacks(aux.get());
if (ret != 0) {
LOGF(ERROR) << "Failed to set camera module callbacks";
return ret;
}
callbacks_auxs_.push_back(std::move(aux));
}
}
callbacks_delegates_[callbacks_id] = std::move(callbacks_delegate);
return 0;
}
int32_t CameraHalAdapter::SetTorchMode(int32_t camera_id, bool enabled) {
VLOGF_ENTER();
camera_module_t* camera_module;
int internal_camera_id;
std::tie(camera_module, internal_camera_id) =
GetInternalModuleAndId(camera_id);
if (!camera_module) {
return -EINVAL;
}
if (auto fn = camera_module->set_torch_mode) {
return fn(std::to_string(internal_camera_id).c_str(), enabled);
}
return -ENOSYS;
}
int32_t CameraHalAdapter::Init() {
VLOGF_ENTER();
return 0;
}
void CameraHalAdapter::CloseDeviceCallback(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
int32_t camera_id) {
task_runner->PostTask(FROM_HERE,
base::Bind(&CameraHalAdapter::CloseDevice,
base::Unretained(this), camera_id));
}
// static
void CameraHalAdapter::CameraDeviceStatusChange(
const camera_module_callbacks_t* callbacks,
int internal_camera_id,
int new_status) {
VLOGF_ENTER();
auto* aux = static_cast<const CameraModuleCallbacksAux*>(callbacks);
CameraHalAdapter* self = aux->adapter;
int camera_id =
self->camera_id_inverse_map_[aux->module_id][internal_camera_id];
base::AutoLock l(self->callbacks_delegates_lock_);
for (auto& it : self->callbacks_delegates_) {
it.second->CameraDeviceStatusChange(camera_id, new_status);
}
}
// static
void CameraHalAdapter::TorchModeStatusChange(
const camera_module_callbacks_t* callbacks,
const char* internal_camera_id,
int new_status) {
VLOGF_ENTER();
auto* aux = static_cast<const CameraModuleCallbacksAux*>(callbacks);
CameraHalAdapter* self = aux->adapter;
int camera_id =
self->camera_id_inverse_map_[aux->module_id][atoi(internal_camera_id)];
base::AutoLock l(self->callbacks_delegates_lock_);
for (auto& it : self->callbacks_delegates_) {
it.second->TorchModeStatusChange(camera_id, new_status);
}
}
void CameraHalAdapter::StartOnThread(base::Callback<void(bool)> callback) {
VLOGF_ENTER();
for (const auto& m : camera_modules_) {
if (m->init) {
int ret = m->init();
if (ret != 0) {
LOGF(ERROR) << "Failed to init camera module " << m->common.name;
callback.Run(false);
return;
}
}
}
std::vector<std::tuple<int, int, int>> cameras;
camera_id_inverse_map_.resize(camera_modules_.size());
for (size_t module_id = 0; module_id < camera_modules_.size(); module_id++) {
camera_module_t* m = camera_modules_[module_id];
int n = m->get_number_of_cameras();
LOGF(INFO) << "Camera module " << module_id << " has " << n << " cameras";
camera_id_inverse_map_[module_id].resize(n);
for (int camera_id = 0; camera_id < n; camera_id++) {
camera_info_t info;
int ret = m->get_camera_info(camera_id, &info);
if (ret != 0) {
LOGF(ERROR) << "Failed to get info of camera " << camera_id
<< " from module " << module_id;
callback.Run(false);
return;
}
cameras.emplace_back(info.facing, static_cast<int>(module_id), camera_id);
}
}
sort(cameras.begin(), cameras.end());
for (size_t i = 0; i < cameras.size(); i++) {
int module_id = std::get<1>(cameras[i]);
int camera_id = std::get<2>(cameras[i]);
camera_id_map_.emplace_back(module_id, camera_id);
camera_id_inverse_map_[module_id][camera_id] = i;
}
LOGF(INFO) << "SuperHAL started with " << camera_modules_.size()
<< " modules and " << camera_id_map_.size() << " cameras";
callback.Run(true);
}
std::pair<camera_module_t*, int> CameraHalAdapter::GetInternalModuleAndId(
int camera_id) {
if (camera_id < 0 ||
static_cast<size_t>(camera_id) >= camera_id_map_.size()) {
LOGF(ERROR) << "Invalid camera id: " << camera_id;
return {};
}
std::pair<int, int> idx = camera_id_map_[camera_id];
return {camera_modules_[idx.first], idx.second};
}
void CameraHalAdapter::CloseDevice(int32_t camera_id) {
VLOGF_ENTER();
if (device_adapters_.find(camera_id) == device_adapters_.end()) {
LOGF(ERROR) << "Failed to close camera device " << camera_id
<< ": device is not opened";
return;
}
device_adapters_.erase(camera_id);
}
void CameraHalAdapter::ResetModuleDelegateOnThread(uint32_t module_id) {
VLOGF_ENTER();
DCHECK(camera_module_thread_.task_runner()->BelongsToCurrentThread());
base::AutoLock l(module_delegates_lock_);
if (module_id == kIdAll) {
module_delegates_.clear();
} else {
module_delegates_.erase(module_id);
}
}
void CameraHalAdapter::ResetCallbacksDelegateOnThread(uint32_t callbacks_id) {
VLOGF_ENTER();
DCHECK(
camera_module_callbacks_thread_.task_runner()->BelongsToCurrentThread());
base::AutoLock l(callbacks_delegates_lock_);
if (callbacks_id == kIdAll) {
callbacks_delegates_.clear();
} else {
callbacks_delegates_.erase(callbacks_id);
}
}
} // namespace cros