| /* 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); |
| } |
| } |