blob: 8a52c84fbebeb60a17b7499b0ecfa6bbc110e8e3 [file] [log] [blame]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2019, Google Inc.
*
* camera_hal_manager.cpp - libcamera Android Camera Manager
*/
#include "camera_hal_manager.h"
#include <libcamera/base/log.h>
#include <libcamera/camera.h>
#include <libcamera/property_ids.h>
#include "camera_device.h"
using namespace libcamera;
LOG_DECLARE_CATEGORY(HAL)
/*
* \class CameraHalManager
*
* The HAL camera manager is initializated at camera_module_t 'hal_init()' time
* and spawns its own thread where libcamera related events are dispatched to.
* It wraps the libcamera CameraManager operations and provides helpers for the
* camera_module_t operations, to retrieve the number of cameras in the system,
* their static information and to open camera devices.
*/
CameraHalManager::CameraHalManager()
: cameraManager_(nullptr), callbacks_(nullptr), numInternalCameras_(0),
nextExternalCameraId_(firstExternalCameraId_)
{
}
/* CameraManager calls stop() in the destructor. */
CameraHalManager::~CameraHalManager() = default;
/* static */
CameraHalManager *CameraHalManager::instance()
{
static CameraHalManager *cameraHalManager = new CameraHalManager;
return cameraHalManager;
}
int CameraHalManager::init()
{
cameraManager_ = std::make_unique<CameraManager>();
/*
* If the configuration file is not available the HAL only supports
* external cameras. If it exists but it's not valid then error out.
*/
if (halConfig_.exists() && !halConfig_.isValid()) {
LOG(HAL, Error) << "HAL configuration file is not valid";
return -EINVAL;
}
/* Support camera hotplug. */
cameraManager_->cameraAdded.connect(this, &CameraHalManager::cameraAdded);
cameraManager_->cameraRemoved.connect(this, &CameraHalManager::cameraRemoved);
int ret = cameraManager_->start();
if (ret) {
LOG(HAL, Error) << "Failed to start camera manager: "
<< strerror(-ret);
cameraManager_.reset();
return ret;
}
return 0;
}
std::tuple<CameraDevice *, int>
CameraHalManager::open(unsigned int id, const hw_module_t *hardwareModule)
{
MutexLocker locker(mutex_);
if (!callbacks_) {
LOG(HAL, Error) << "Can't open camera before callbacks are set";
return { nullptr, -ENODEV };
}
CameraDevice *camera = cameraDeviceFromHalId(id);
if (!camera) {
LOG(HAL, Error) << "Invalid camera id '" << id << "'";
return { nullptr, -ENODEV };
}
int ret = camera->open(hardwareModule);
if (ret)
return { nullptr, ret };
LOG(HAL, Info) << "Open camera '" << id << "'";
return { camera, 0 };
}
void CameraHalManager::cameraAdded(std::shared_ptr<Camera> cam)
{
unsigned int id;
bool isCameraExternal = false;
bool isCameraNew = false;
MutexLocker locker(mutex_);
/*
* Each camera is assigned a unique integer ID when it is seen for the
* first time. If the camera has been seen before, the previous ID is
* re-used.
*
* IDs starts from '0' for internal cameras and '1000' for external
* cameras.
*/
auto iter = cameraIdsMap_.find(cam->id());
if (iter != cameraIdsMap_.end()) {
id = iter->second;
if (id >= firstExternalCameraId_)
isCameraExternal = true;
} else {
isCameraNew = true;
/*
* Now check if this is an external camera and assign
* its id accordingly.
*/
if (cameraLocation(cam.get()) == properties::CameraLocationExternal) {
isCameraExternal = true;
id = nextExternalCameraId_;
} else {
id = numInternalCameras_;
}
}
/*
* The configuration file must be valid, and contain a corresponding
* entry for internal cameras. External cameras can be initialized
* without configuration file.
*/
if (!isCameraExternal && !halConfig_.exists()) {
LOG(HAL, Error)
<< "HAL configuration file is mandatory for internal cameras."
<< " Camera " << cam->id() << " failed to load";
return;
}
const CameraConfigData *cameraConfigData = halConfig_.cameraConfigData(cam->id());
/*
* Some cameras whose location is reported by libcamera as external may
* actually be internal to the device. This is common with UVC cameras
* that are integrated in a laptop. In that case the real location
* should be specified in the configuration file.
*
* If the camera location is external and a configuration entry exists
* for it, override its location.
*/
if (isCameraNew && isCameraExternal) {
if (cameraConfigData && cameraConfigData->facing != -1) {
isCameraExternal = false;
id = numInternalCameras_;
}
}
if (!isCameraExternal && !cameraConfigData) {
LOG(HAL, Error)
<< "HAL configuration entry for internal camera "
<< cam->id() << " is missing";
return;
}
/* Create a CameraDevice instance to wrap the libcamera Camera. */
std::unique_ptr<CameraDevice> camera = CameraDevice::create(id, cam);
int ret = camera->initialize(cameraConfigData);
if (ret) {
LOG(HAL, Error) << "Failed to initialize camera: " << cam->id();
return;
}
if (isCameraNew) {
cameraIdsMap_.emplace(cam->id(), id);
if (isCameraExternal)
nextExternalCameraId_++;
else
numInternalCameras_++;
}
cameras_.emplace_back(std::move(camera));
if (callbacks_)
callbacks_->camera_device_status_change(callbacks_, id,
CAMERA_DEVICE_STATUS_PRESENT);
LOG(HAL, Debug) << "Camera ID: " << id << " added successfully.";
}
void CameraHalManager::cameraRemoved(std::shared_ptr<Camera> cam)
{
MutexLocker locker(mutex_);
auto iter = std::find_if(cameras_.begin(), cameras_.end(),
[&cam](const std::unique_ptr<CameraDevice> &camera) {
return cam == camera->camera();
});
if (iter == cameras_.end())
return;
/*
* CAMERA_DEVICE_STATUS_NOT_PRESENT should be set for external cameras
* only.
*/
unsigned int id = (*iter)->id();
if (id >= firstExternalCameraId_)
callbacks_->camera_device_status_change(callbacks_, id,
CAMERA_DEVICE_STATUS_NOT_PRESENT);
/*
* \todo Check if the camera is already open and running.
* Inform the framework about its absence before deleting its
* reference here.
*/
cameras_.erase(iter);
LOG(HAL, Debug) << "Camera ID: " << id << " removed successfully.";
}
int32_t CameraHalManager::cameraLocation(const Camera *cam)
{
return cam->properties().get(properties::Location).value_or(-1);
}
CameraDevice *CameraHalManager::cameraDeviceFromHalId(unsigned int id)
{
auto iter = std::find_if(cameras_.begin(), cameras_.end(),
[id](const std::unique_ptr<CameraDevice> &camera) {
return camera->id() == id;
});
if (iter == cameras_.end())
return nullptr;
return iter->get();
}
unsigned int CameraHalManager::numCameras() const
{
return numInternalCameras_;
}
int CameraHalManager::getCameraInfo(unsigned int id, struct camera_info *info)
{
if (!info)
return -EINVAL;
MutexLocker locker(mutex_);
CameraDevice *camera = cameraDeviceFromHalId(id);
if (!camera) {
LOG(HAL, Error) << "Invalid camera id '" << id << "'";
return -EINVAL;
}
info->facing = camera->facing();
info->orientation = camera->orientation();
info->device_version = CAMERA_DEVICE_API_VERSION_3_5;
info->resource_cost = 51;
info->static_camera_characteristics = camera->getStaticMetadata();
info->conflicting_devices = nullptr;
info->conflicting_devices_length = 0;
return 0;
}
void CameraHalManager::setCallbacks(const camera_module_callbacks_t *callbacks)
{
callbacks_ = callbacks;
MutexLocker locker(mutex_);
/*
* Some external cameras may have been identified before the callbacks_
* were set. Iterate all existing external cameras and mark them as
* CAMERA_DEVICE_STATUS_PRESENT explicitly.
*
* Internal cameras are already assumed to be present at module load
* time by the Android framework.
*/
for (const std::unique_ptr<CameraDevice> &camera : cameras_) {
unsigned int id = camera->id();
if (id >= firstExternalCameraId_)
callbacks_->camera_device_status_change(callbacks_, id,
CAMERA_DEVICE_STATUS_PRESENT);
}
}