blob: 9ae5b4093f9f2b93d2e781558f937bb79b1693b9 [file] [log] [blame]
// Copyright 2018 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 "driver_ctx.h"
#include <lib/fdio/directory.h>
#include <lib/media/codec_impl/codec_metrics.h>
#include <stdarg.h>
#include <stdio.h>
#include "amlogic-video.h"
#include "device_ctx.h"
#include "macros.h"
namespace amlogic_decoder {
zx_status_t DriverCtx::Init(void** out_ctx) {
amlogic_decoder::DriverCtx* driver_ctx = new amlogic_decoder::DriverCtx();
*out_ctx = reinterpret_cast<void*>(driver_ctx);
return ZX_OK;
}
// ctx is the driver ctx (not device ctx)
zx_status_t DriverCtx::Bind(void* ctx, zx_device_t* parent) {
amlogic_decoder::DriverCtx* driver = reinterpret_cast<amlogic_decoder::DriverCtx*>(ctx);
auto device = std::make_unique<amlogic_decoder::DeviceCtx>(driver, parent);
amlogic_decoder::AmlogicVideo* video = device->video();
zx_status_t status = video->InitRegisters(parent);
if (status != ZX_OK) {
DECODE_ERROR("Failed to initialize registers");
return status;
}
status = video->InitDecoder();
if (status != ZX_OK) {
DECODE_ERROR("Failed to initialize decoder");
return status;
}
status = device->Bind();
if (status != ZX_OK) {
DECODE_ERROR("Failed to bind device");
return status;
}
// The pointer to DeviceCtx is add_device() ctx now, so intentionally don't
// destruct the DeviceCtx instance.
//
// At least for now, the DeviceCtx stays allocated for the life of the
// devhost process.
device.release();
zxlogf(INFO, "[amlogic_video_bind] bound");
return ZX_OK;
}
void DriverCtx::Release(void* ctx) {
auto* driver_ctx = reinterpret_cast<DriverCtx*>(ctx);
delete driver_ctx;
}
DriverCtx::DriverCtx()
: // We use kAsyncLoopConfigNoAttachToCurrentThread here, because we don't really want
// to be setting the default async_t for the thread that creates the
// DriverCtx. We'll plumb async_t(s) explicitly instead.
shared_fidl_loop_{std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread)},
diagnostics_{kDriverName, shared_fidl_loop_->dispatcher()} {
shared_fidl_loop_->StartThread("shared_fidl_thread", &shared_fidl_thread_);
metrics_.emplace();
// This won't actually be logged until codec_factory opens a device and calls
// SetAuxServiceDirectory() on it. Until then we're buffering event counts.
metrics_->LogEvent(
media_metrics::
StreamProcessorEvents2MigratedMetricDimensionImplementation_AmlogicDecoderShared,
media_metrics::StreamProcessorEvents2MigratedMetricDimensionEvent_HostProcessStart);
}
DriverCtx::~DriverCtx() {
if (shared_fidl_loop_) {
shared_fidl_loop_->Quit();
shared_fidl_loop_->JoinThreads();
shared_fidl_loop_->Shutdown();
}
}
// TODO(dustingreen): Do format, printf, log, maybe some epitaphs.
void DriverCtx::FatalError(const char* format, ...) {
// Let's not have a buffer on the stack, not because it couldn't be done
// safely, but because we'd potentially run into stack size vs. message length
// tradeoffs, stack expansion granularity fun, or whatever else.
va_list args;
va_start(args, format);
size_t buffer_bytes = vsnprintf(nullptr, 0, format, args) + 1;
va_end(args);
// ~buffer never actually runs since this method never returns
std::unique_ptr<char[]> buffer(new char[buffer_bytes]);
va_start(args, format);
size_t buffer_bytes_2 = vsnprintf(buffer.get(), buffer_bytes, format, args) + 1;
(void)buffer_bytes_2;
// sanity check; should match so go ahead and assert that it does.
assert(buffer_bytes == buffer_bytes_2);
va_end(args);
DECODE_ERROR("DriverCtx::FatalError(): %s", buffer.get());
// TODO(dustingreen): Send string in buffer via channel epitaphs, when
// possible. The channel activity/failing server-side generally will race with
// trying to send epitaph - probably requires enlisting shared_fidl_thread()
// from here - probably a timeout here would be a good idea if so.
// This should provide more stack dump than exit(-1) would give.
ZX_ASSERT_MSG(false, "DriverCtx::FatalError() is fatal.");
}
// Run to_run on given dispatcher, in order.
void DriverCtx::PostSerial(async_dispatcher_t* dispatcher, fit::closure to_run) {
zx_status_t post_result = async::PostTask(dispatcher, std::move(to_run));
if (post_result != ZX_OK) {
FatalError("async::PostTask() failed - result: %d", post_result);
}
}
// Run to_run_on_shared_fidl_thread on shared_fidl_thread().
void DriverCtx::PostToSharedFidl(fit::closure to_run_on_shared_fidl_thread) {
// Switch the implementation here to fit::function when possible.
PostSerial(shared_fidl_loop()->dispatcher(), std::move(to_run_on_shared_fidl_thread));
}
void DriverCtx::SetAuxServiceDirectory(
fidl::InterfaceHandle<fuchsia::io::Directory> aux_service_directory) {
if (aux_service_directory_) {
// Check if old aux_service_directory_ is PEER_CLOSED / broken.
fidl::InterfaceHandle<fuchsia::io::Directory> tmp_directory;
zx_status_t status = aux_service_directory_->CloneChannel(tmp_directory.NewRequest());
if (status == ZX_OK) {
// Keep the service directory we already had, and that seems to not be PEER_CLOSED. This path
// is to avoid switching to a temporary service directory when running tests that spawn their
// own separate instance of codec_factory.
return;
}
// Toss the PEER_CLOSED / broken service directory.
aux_service_directory_ = nullptr;
}
ZX_DEBUG_ASSERT(!aux_service_directory_);
aux_service_directory_ =
std::make_shared<sys::ServiceDirectory>(std::move(aux_service_directory));
ZX_DEBUG_ASSERT(aux_service_directory_);
metrics_->SetServiceDirectory(aux_service_directory_);
}
CodecMetrics& DriverCtx::metrics() { return *metrics_; }
CodecDiagnostics& DriverCtx::diagnostics() { return diagnostics_; }
} // namespace amlogic_decoder