| // Copyright 2019 The Fuchsia 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 "src/camera/drivers/controller/controller_protocol.h" |
| |
| #include <fuchsia/camera2/cpp/fidl.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/fit/defer.h> |
| #include <lib/trace/event.h> |
| |
| #include "src/camera/drivers/controller/configs/product_config.h" |
| |
| namespace camera { |
| |
| ControllerImpl::ControllerImpl(async_dispatcher_t* dispatcher, |
| fuchsia::sysmem2::AllocatorSyncPtr sysmem_allocator, |
| const ddk::IspProtocolClient& isp, const ddk::GdcProtocolClient& gdc, |
| const ddk::Ge2dProtocolClient& ge2d, |
| LoadFirmwareCallback load_firmware) |
| : dispatcher_(dispatcher), |
| binding_(this), |
| pipeline_manager_(dispatcher, std::move(sysmem_allocator), isp, gdc, ge2d, |
| std::move(load_firmware)), |
| product_config_(ProductConfig::Create()) { |
| binding_.set_error_handler( |
| [](zx_status_t status) { zxlogf(INFO, "controller client disconnected"); }); |
| configs_ = product_config_->ExternalConfigs(); |
| internal_configs_ = product_config_->InternalConfigs(); |
| } |
| |
| void ControllerImpl::Connect(fidl::InterfaceRequest<fuchsia::camera2::hal::Controller> request) { |
| if (binding_.is_bound()) { |
| zxlogf(WARNING, "ControllerImpl::Connect(): Camera controller is already bound"); |
| request.Close(ZX_ERR_ALREADY_BOUND); |
| return; |
| } |
| |
| zx_status_t status = binding_.Bind(std::move(request), dispatcher_); |
| zxlogf(INFO, "ControllerImpl::Connect(): Bind() -> %d", status); |
| } |
| |
| void ControllerImpl::GetNextConfig(GetNextConfigCallback callback) { |
| fuchsia::camera2::hal::Config config; |
| |
| if (config_count_ >= configs_.size()) { |
| callback(nullptr, ZX_ERR_STOP); |
| return; |
| } |
| config = fidl::Clone(configs_.at(config_count_)); |
| callback(std::make_unique<fuchsia::camera2::hal::Config>(std::move(config)), ZX_OK); |
| config_count_++; |
| } |
| |
| zx_koid_t GetRelatedKoid(zx_handle_t handle) { |
| zx_info_handle_basic_t info; |
| zx_status_t status = |
| zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr); |
| return status == ZX_OK ? info.related_koid : ZX_KOID_INVALID; |
| } |
| |
| void ControllerImpl::CreateStream(uint32_t config_index, uint32_t stream_index, |
| uint32_t image_format_index, |
| fidl::InterfaceRequest<fuchsia::camera2::Stream> stream) { |
| TRACE_DURATION("camera", "ControllerImpl::CreateStream"); |
| |
| zxlogf(INFO, "new request from remote channel koid %lu for c%us%uf%u", |
| GetRelatedKoid(stream.channel().get()), config_index, stream_index, image_format_index); |
| |
| if (config_index >= configs_.size()) { |
| stream.Close(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| const auto& config = configs_[config_index]; |
| |
| if (stream_index >= config.stream_configs.size()) { |
| stream.Close(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| const auto& stream_config = config.stream_configs[stream_index]; |
| |
| if (image_format_index >= stream_config.image_formats.size()) { |
| stream.Close(ZX_ERR_INVALID_ARGS); |
| return; |
| } |
| |
| // The presence of the requests queue indicates the pipeline manager is in transition. |
| if (requests_) { |
| // If a new config request arrives before the previous shutdown completed, discard any pending |
| // requests received in the interim period and just start tracking requests for the new config. |
| if (config_index != pipeline_config_index_) { |
| pipeline_config_index_ = config_index; |
| requests_ = RequestQueue{}; |
| } |
| requests_->emplace(stream_index, image_format_index, std::move(stream)); |
| return; |
| } |
| |
| // TODO(https://fxbug.dev/42051249): Move config index management into the pipeline manager, then |
| // delete shutdown/queueing in this component. |
| // |
| // If the requested config is different from the current |
| // config, handling it requires shutting down the current pipeline first. |
| if (config_index != pipeline_config_index_) { |
| pipeline_config_index_ = config_index; |
| requests_ = RequestQueue{}; |
| requests_->emplace(stream_index, image_format_index, std::move(stream)); |
| pipeline_manager_.Shutdown([this]() mutable { |
| TRACE_DURATION("camera", "ControllerImpl::CreateStream.shutdown.callback"); |
| pipeline_manager_.SetRoots( |
| internal_configs_.configs_info[pipeline_config_index_].streams_info); |
| auto requests = std::move(*requests_); |
| requests_.reset(); |
| while (!requests.empty()) { |
| auto [stream_index, image_format_index, stream] = std::move(requests.front()); |
| requests.pop(); |
| CreateStream(pipeline_config_index_, stream_index, image_format_index, std::move(stream)); |
| } |
| }); |
| return; |
| } |
| |
| auto& internal_config = internal_configs_.configs_info[config_index]; |
| |
| StreamCreationData info{.roots = internal_config.streams_info}; |
| info.image_format_index = image_format_index; |
| info.stream_config = fidl::Clone(stream_config); |
| info.frame_rate_range = internal_config.frame_rate_range; |
| |
| // We now have the stream_config_node which needs to be configured |
| // Configure the stream pipeline |
| pipeline_manager_.ConfigureStreamPipeline(std::move(info), std::move(stream)); |
| } |
| |
| void ControllerImpl::EnableStreaming() { pipeline_manager_.SetStreamingEnabled(true); } |
| |
| void ControllerImpl::DisableStreaming() { pipeline_manager_.SetStreamingEnabled(false); } |
| |
| void ControllerImpl::GetDeviceInfo(GetDeviceInfoCallback callback) { |
| callback(ProductConfig::DeviceInfo()); |
| } |
| |
| } // namespace camera |