blob: 873f737b897b69078286227a11ec43fc4914653f [file] [log] [blame] [edit]
// Copyright 2022 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 <lib/driver2/node_add_args.h>
#include <lib/driver_compat/device_server.h>
#include <lib/fdio/directory.h>
namespace compat {
namespace fcd = fuchsia_component_decl;
zx_status_t DeviceServer::AddMetadata(uint32_t type, const void* data, size_t size) {
Metadata metadata(size);
auto begin = static_cast<const uint8_t*>(data);
std::copy(begin, begin + size, metadata.begin());
auto [_, inserted] = metadata_.emplace(type, std::move(metadata));
if (!inserted) {
return ZX_ERR_ALREADY_EXISTS;
}
return ZX_OK;
}
zx_status_t DeviceServer::GetMetadata(uint32_t type, void* buf, size_t buflen, size_t* actual) {
auto it = metadata_.find(type);
if (it == metadata_.end()) {
return ZX_ERR_NOT_FOUND;
}
auto& [_, metadata] = *it;
auto size = std::min(buflen, metadata.size());
auto begin = metadata.begin();
std::copy(begin, begin + size, static_cast<uint8_t*>(buf));
*actual = metadata.size();
return ZX_OK;
}
zx_status_t DeviceServer::GetMetadataSize(uint32_t type, size_t* out_size) {
auto it = metadata_.find(type);
if (it == metadata_.end()) {
return ZX_ERR_NOT_FOUND;
}
auto& [_, metadata] = *it;
*out_size = metadata.size();
return ZX_OK;
}
zx_status_t DeviceServer::Serve(async_dispatcher_t* dispatcher,
component::OutgoingDirectory* outgoing) {
component::ServiceHandler handler;
fuchsia_driver_compat::Service::Handler compat_service(&handler);
auto device = [this, dispatcher](
fidl::ServerEnd<fuchsia_driver_compat::Device> server_end) mutable -> void {
fidl::BindServer<fidl::WireServer<fuchsia_driver_compat::Device>>(dispatcher,
std::move(server_end), this);
};
zx::status<> status = compat_service.add_device(std::move(device));
if (status.is_error()) {
return status.error_value();
}
status = outgoing->AddService<fuchsia_driver_compat::Service>(std::move(handler), name());
if (status.is_error()) {
return status.error_value();
}
stop_serving_ = [this, outgoing]() {
(void)outgoing->RemoveService<fuchsia_driver_compat::Service>(name_);
};
if (service_offers_) {
return service_offers_->Serve(dispatcher, outgoing);
}
return ZX_OK;
}
zx_status_t DeviceServer::Serve(async_dispatcher_t* dispatcher,
driver::OutgoingDirectory* outgoing) {
driver::ServiceInstanceHandler handler;
fuchsia_driver_compat::Service::Handler compat_service(&handler);
auto device = [this, dispatcher](
fidl::ServerEnd<fuchsia_driver_compat::Device> server_end) mutable -> void {
fidl::BindServer<fidl::WireServer<fuchsia_driver_compat::Device>>(dispatcher,
std::move(server_end), this);
};
zx::status<> status = compat_service.add_device(std::move(device));
if (status.is_error()) {
return status.error_value();
}
status = outgoing->AddService<fuchsia_driver_compat::Service>(std::move(handler), name());
if (status.is_error()) {
return status.error_value();
}
stop_serving_ = [this, outgoing]() {
(void)outgoing->RemoveService<fuchsia_driver_compat::Service>(name_);
};
if (service_offers_) {
return service_offers_->Serve(dispatcher, outgoing);
}
return ZX_OK;
}
void DeviceServer::ExportToDevfs(const driver::DevfsExporter& exporter,
std::string_view service_path,
fit::callback<void(zx_status_t)> callback) const {
exporter.Export(service_path, topological_path_, fuchsia_device_fs::ExportOptions(), proto_id_,
std::move(callback));
}
std::vector<fcd::wire::Offer> DeviceServer::CreateOffers(fidl::ArenaBase& arena) {
std::vector<fcd::wire::Offer> offers;
// Create the main fuchsia.driver.compat.Service offer.
offers.push_back(driver::MakeOffer<fuchsia_driver_compat::Service>(arena, name()));
if (service_offers_) {
auto service_offers = service_offers_->CreateOffers(arena);
offers.reserve(offers.size() + service_offers.size());
offers.insert(offers.end(), service_offers.begin(), service_offers.end());
}
return offers;
}
std::vector<fcd::Offer> DeviceServer::CreateOffers() {
std::vector<fcd::Offer> offers;
// Create the main fuchsia.driver.compat.Service offer.
offers.push_back(driver::MakeOffer<fuchsia_driver_compat::Service>(name()));
if (service_offers_) {
auto service_offers = service_offers_->CreateOffers();
offers.reserve(offers.size() + service_offers.size());
offers.insert(offers.end(), service_offers.begin(), service_offers.end());
}
return offers;
}
void DeviceServer::GetTopologicalPath(GetTopologicalPathCompleter::Sync& completer) {
completer.Reply(fidl::StringView::FromExternal(topological_path_));
}
void DeviceServer::GetMetadata(GetMetadataCompleter::Sync& completer) {
std::vector<fuchsia_driver_compat::wire::Metadata> metadata;
metadata.reserve(metadata_.size());
for (auto& [type, data] : metadata_) {
fuchsia_driver_compat::wire::Metadata new_metadata;
new_metadata.type = type;
zx::vmo vmo;
zx_status_t status = zx::vmo::create(data.size(), 0, &new_metadata.data);
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
status = new_metadata.data.write(data.data(), 0, data.size());
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
size_t size = data.size();
status = new_metadata.data.set_property(ZX_PROP_VMO_CONTENT_SIZE, &size, sizeof(size));
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
metadata.push_back(std::move(new_metadata));
}
completer.ReplySuccess(fidl::VectorView<fuchsia_driver_compat::wire::Metadata>::FromExternal(
metadata.data(), metadata.size()));
}
void DeviceServer::ConnectFidl(ConnectFidlRequestView request,
ConnectFidlCompleter::Sync& completer) {
if (service_offers_) {
auto path = std::string("svc/").append(request->name.data(), request->name.size());
fdio_service_connect_at(service_offers_->dir().channel()->get(), path.data(),
request->server.release());
}
completer.Reply();
}
} // namespace compat