| // 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/graphics/display/bin/coordinator-connector/devfs-factory.h" |
| |
| #include <fidl/fuchsia.hardware.display/cpp/wire.h> |
| #include <lib/component/incoming/cpp/protocol.h> |
| #include <lib/component/outgoing/cpp/outgoing_directory.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/status.h> |
| |
| #include <cstdint> |
| |
| #include "src/lib/fsl/io/device_watcher.h" |
| |
| namespace display { |
| |
| static const std::string kDisplayDir = "/dev/class/display-coordinator"; |
| |
| zx::result<> DevFsCoordinatorFactory::CreateAndPublishService( |
| component::OutgoingDirectory& outgoing, async_dispatcher_t* dispatcher) { |
| return outgoing.AddProtocol<fuchsia_hardware_display::Provider>( |
| std::make_unique<DevFsCoordinatorFactory>(dispatcher)); |
| } |
| |
| DevFsCoordinatorFactory::DevFsCoordinatorFactory(async_dispatcher_t* dispatcher) |
| : dispatcher_(dispatcher) {} |
| |
| void DevFsCoordinatorFactory::OpenCoordinatorForVirtcon( |
| OpenCoordinatorForVirtconRequest& request, |
| OpenCoordinatorForVirtconCompleter::Sync& completer) { |
| completer.Reply({{ |
| .s = ZX_ERR_NOT_SUPPORTED, |
| }}); |
| } |
| |
| zx_status_t DevFsCoordinatorFactory::OpenCoordinatorForPrimaryOnDevice( |
| const fidl::ClientEnd<fuchsia_io::Directory>& dir, const std::string& filename, |
| fidl::ServerEnd<fuchsia_hardware_display::Coordinator> coordinator_server) { |
| zx::result client = component::ConnectAt<fuchsia_hardware_display::Provider>(dir, filename); |
| if (client.is_error()) { |
| FX_PLOGS(ERROR, client.error_value()) |
| << "Failed to open display_controller at path: " << kDisplayDir << '/' << filename; |
| |
| // 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. |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // TODO(https://fxbug.dev/42135096): Pass an async completer asynchronously into |
| // OpenCoordinator(), rather than blocking on a synchronous call. |
| fidl::WireResult result = |
| fidl::WireCall(client.value())->OpenCoordinatorForPrimary(std::move(coordinator_server)); |
| if (!result.ok()) { |
| FX_PLOGS(ERROR, result.status()) << "Failed to call service handle"; |
| |
| // 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. |
| return ZX_ERR_INTERNAL; |
| } |
| if (result->s != ZX_OK) { |
| FX_PLOGS(ERROR, result->s) << "Failed to open display coordinator"; |
| return result->s; |
| } |
| |
| return ZX_OK; |
| } |
| |
| void DevFsCoordinatorFactory::OpenCoordinatorForPrimary( |
| OpenCoordinatorForPrimaryRequest& request, |
| OpenCoordinatorForPrimaryCompleter::Sync& completer) { |
| // 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. |
| const int64_t id = next_display_client_id_++; |
| |
| std::unique_ptr<fsl::DeviceWatcher> watcher = fsl::DeviceWatcher::Create( |
| kDisplayDir, |
| [this, id, request = std::move(request), async_completer = completer.ToAsync()]( |
| const fidl::ClientEnd<fuchsia_io::Directory>& dir, const std::string& filename) mutable { |
| FX_LOGS(INFO) << "Found display controller at path: " << kDisplayDir << '/' << filename |
| << '.'; |
| zx_status_t open_coordinator_status = |
| OpenCoordinatorForPrimaryOnDevice(dir, filename, std::move(request.coordinator())); |
| if (open_coordinator_status != ZX_OK) { |
| async_completer.Reply({{.s = open_coordinator_status}}); |
| return; |
| } |
| async_completer.Reply({{.s = ZX_OK}}); |
| // We no longer need |this| to store this closure, remove it. Do not do |
| // any work after this point. |
| pending_device_watchers_.erase(id); |
| }, |
| dispatcher_); |
| pending_device_watchers_[id] = std::move(watcher); |
| } |
| |
| } // namespace display |