blob: 62090ff036e02bef635fc6d7e9dd42794bff3795 [file] [log] [blame]
// 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