blob: add939ade781bb2be525366fe0c8c18059ecab6d [file] [log] [blame]
// Copyright 2020 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/ui/lib/display/hardware_display_controller_provider_impl.h"
#include <fcntl.h>
#include <fuchsia/hardware/display/c/fidl.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <cstdint>
#include "src/lib/fsl/io/device_watcher.h"
namespace ui_display {
static const std::string kDisplayDir = "/dev/class/display-controller";
HardwareDisplayControllerProviderImpl::HardwareDisplayControllerProviderImpl(
sys::ComponentContext* app_context) {
app_context->outgoing()->AddPublicService(bindings_.GetHandler(this));
}
// |fuchsia::hardware::display::Provider|.
void HardwareDisplayControllerProviderImpl::OpenController(
zx::channel device, ::fidl::InterfaceRequest<fuchsia::hardware::display::Controller> request,
OpenControllerCallback callback) {
// Watcher's lifetime needs to be at most as long as the lifetime of |this|,
// and otherwise as long as the lifetime of |callback|. |this| will own
// the references to outstanding watchers, and each watcher will notify |this|
// when it is done, so that |this| can remove a reference to it.
static uint64_t last_id = 0;
const uint64_t id = ++last_id;
std::unique_ptr<fsl::DeviceWatcher> watcher = fsl::DeviceWatcher::Create(
kDisplayDir,
[id, holders = &holders_, device = std::move(device), request = std::move(request),
callback = std::move(callback)](int dir_fd, std::string filename) mutable {
// Get display info.
std::string path = kDisplayDir + "/" + filename;
FX_LOGS(INFO) << "Found display controller at path: " << path << ".";
fbl::unique_fd fd(open(path.c_str(), O_RDWR));
if (!fd.is_valid()) {
FX_LOGS(ERROR) << "Failed to open display_controller at path: " << path
<< " (errno: " << errno << " " << strerror(errno) << ")";
// We could try to match the value of the C "errno" macro to the closest ZX error, but
// this would give rise to many corner cases. We never expect this to fail anyway, since
// |filename| is given to us by the device watcher.
callback(ZX_ERR_INTERNAL);
return;
}
// TODO(fxbug.dev/57269): it would be nice to simply pass |callback| asynchronously into
// OpenController(), rather than blocking on a synchronous call. However, it is non-trivial
// to do so, so for now we use a blocking call to proxy the request.
fdio_cpp::FdioCaller caller(std::move(fd));
zx_status_t status = ZX_OK;
zx_status_t fidl_status = fuchsia_hardware_display_ProviderOpenController(
caller.borrow_channel(), device.release(), request.TakeChannel().release(), &status);
if (fidl_status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to call service handle: " << zx_status_get_string(fidl_status);
// There's not a clearly-better value to return here. Returning the FIDL error would be
// somewhat unexpected, since the caller wouldn't receive it as a FIDL status, rather as
// the return value of a "successful" method invocation.
callback(ZX_ERR_INTERNAL);
return;
}
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Failed to open display controller : " << zx_status_get_string(status);
callback(status);
return;
}
callback(status);
// We no longer need |this| to store this closure, remove it. Do not do
// any work after this point.
holders->erase(id);
});
holders_[id] = std::move(watcher);
}
} // namespace ui_display