blob: 00359b92d4079d50bd68a69c08274fa03208b9a4 [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 "network_device.h"
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/driver.h>
#include <lib/fdf/cpp/env.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include "device/network_device_shim.h"
namespace network {
namespace {
// Creates a `NetworkDeviceImplBinder` based on the `parent` device type.
zx::result<std::unique_ptr<NetworkDeviceImplBinder>> CreateImplBinder(
ddk::NetworkDeviceImplProtocolClient netdevice_impl, NetworkDevice* device,
const ShimDispatchers& dispatchers) {
fbl::AllocChecker ac;
// If the `parent` is Banjo based, then we must use "shims" to translate between Banjo and FIDL in
// order to leverage the netdevice core library.
if (netdevice_impl.is_valid()) {
auto shim = fbl::make_unique_checked<NetworkDeviceShim>(&ac, netdevice_impl, dispatchers);
if (!ac.check()) {
zxlogf(ERROR, "no memory");
return zx::error(ZX_ERR_NO_MEMORY);
}
return zx::ok(std::move(shim));
}
// If the `parent` is FIDL based, then return a binder that connects to the device with no extra
// translation layer.
std::unique_ptr fidl = fbl::make_unique_checked<FidlNetworkDeviceImplBinder>(&ac, device);
if (!ac.check()) {
zxlogf(ERROR, "no memory");
return zx::error(ZX_ERR_NO_MEMORY);
}
return zx::ok(std::move(fidl));
}
} // namespace
NetworkDevice::~NetworkDevice() {
if (dispatchers_) {
dispatchers_->ShutdownSync();
}
if (shim_dispatchers_) {
shim_dispatchers_->ShutdownSync();
}
}
zx_status_t NetworkDevice::Create(void* ctx, zx_device_t* parent) {
fbl::AllocChecker ac;
std::unique_ptr netdev = fbl::make_unique_checked<NetworkDevice>(&ac, parent);
if (!ac.check()) {
zxlogf(ERROR, "no memory");
return ZX_ERR_NO_MEMORY;
}
zx::result dispatchers = OwnedDeviceInterfaceDispatchers::Create();
if (dispatchers.is_error()) {
zxlogf(ERROR, "failed to create owned dispatchers: %s", dispatchers.status_string());
return dispatchers.status_value();
}
netdev->dispatchers_ = std::move(dispatchers.value());
ddk::NetworkDeviceImplProtocolClient netdevice_impl(parent);
ShimDispatchers unowned_shim_dispatchers;
if (netdevice_impl.is_valid()) {
// We only need these dispatchers for Banjo parents.
zx::result shim_dispatchers = OwnedShimDispatchers::Create();
if (shim_dispatchers.is_error()) {
zxlogf(ERROR, "failed to create owned shim dispatchers: %s",
shim_dispatchers.status_string());
return shim_dispatchers.status_value();
}
netdev->shim_dispatchers_ = std::move(shim_dispatchers.value());
unowned_shim_dispatchers = netdev->shim_dispatchers_->Unowned();
}
zx::result<std::unique_ptr<NetworkDeviceImplBinder>> binder =
CreateImplBinder(netdevice_impl, netdev.get(), unowned_shim_dispatchers);
if (binder.is_error()) {
zxlogf(ERROR, "failed to create network device binder: %s", binder.status_string());
return binder.status_value();
}
zx::result device =
NetworkDeviceInterface::Create(netdev->dispatchers_->Unowned(), std::move(binder.value()));
if (device.is_error()) {
zxlogf(ERROR, "failed to create inner device %s", device.status_string());
return device.status_value();
}
netdev->device_ = std::move(device.value());
if (zx_status_t status = netdev->DdkAdd(
ddk::DeviceAddArgs("network-device").set_proto_id(ZX_PROTOCOL_NETWORK_DEVICE));
status != ZX_OK) {
zxlogf(ERROR, "failed to bind device: %s", zx_status_get_string(status));
return status;
}
// On successful Add, Devmgr takes ownership (relinquished on DdkRelease),
// so transfer our ownership to a local var, and let it go out of scope.
[[maybe_unused]] auto temp_ref = netdev.release();
return ZX_OK;
}
void NetworkDevice::DdkUnbind(ddk::UnbindTxn unbindTxn) {
zxlogf(INFO, "%p DdkUnbind", zxdev());
device_->Teardown([txn = std::move(unbindTxn), this]() mutable {
zxlogf(INFO, "%p DdkUnbind Done", zxdev());
txn.Reply();
});
}
void NetworkDevice::DdkRelease() {
zxlogf(INFO, "%p DdkRelease", zxdev());
delete this;
}
void NetworkDevice::DdkSuspend(ddk::SuspendTxn suspendTxn) {
// In DFv1 DdkSuspend is only called during shutdown and reboot, resuming is not supported. It's
// therefore safe to tear down the device here as it will never come back from this.
zxlogf(INFO, "%p DdkSuspend", zxdev());
device_->Teardown([txn = std::move(suspendTxn), this]() mutable {
zxlogf(INFO, "%p DdkSuspend Done", zxdev());
txn.Reply(ZX_OK, txn.requested_state());
});
}
void NetworkDevice::GetDevice(GetDeviceRequestView request, GetDeviceCompleter::Sync& _completer) {
ZX_ASSERT_MSG(device_, "can't serve device if not bound to parent implementation");
device_->Bind(std::move(request->device));
}
static constexpr zx_driver_ops_t network_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = [](void* ctx, zx_device_t* parent) -> zx_status_t {
return NetworkDevice::Create(ctx, parent);
},
};
zx::result<fdf::ClientEnd<fuchsia_hardware_network_driver::NetworkDeviceImpl>>
FidlNetworkDeviceImplBinder::Bind() {
zx::result<fdf::ClientEnd<fuchsia_hardware_network_driver::NetworkDeviceImpl>> client_end =
parent_->DdkConnectRuntimeProtocol<
fuchsia_hardware_network_driver::Service::NetworkDeviceImpl>();
if (client_end.is_error()) {
zxlogf(ERROR, "failed to connect to parent device: %s", client_end.status_string());
return client_end;
}
return client_end;
}
} // namespace network
ZIRCON_DRIVER(network, network::network_driver_ops, "zircon", "0.1");