blob: 1fc3a263f90994f2a995b6b1483c56729a1cd077 [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 <zircon/status.h>
#include <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/driver.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
namespace network {
NetworkDevice::~NetworkDevice() {
if (loop_thread_.has_value()) {
// not allowed to destroy device on the loop thread, will cause deadlock
ZX_ASSERT(loop_thread_.value() != thrd_current());
}
loop_.Shutdown();
}
zx_status_t NetworkDevice::Create(void* ctx, zx_device_t* parent) {
fbl::AllocChecker ac;
auto netdev = fbl::make_unique_checked<NetworkDevice>(&ac, parent);
if (!ac.check()) {
zxlogf(ERROR, "network-device: No memory");
return ZX_ERR_NO_MEMORY;
}
thrd_t thread;
zx_status_t status;
if ((status = netdev->loop_.StartThread("network-device-handler", &thread)) != ZX_OK) {
zxlogf(ERROR, "network-device: Failed to create handler thread");
return status;
}
netdev->loop_thread_ = thread;
ddk::NetworkDeviceImplProtocolClient netdevice_impl(parent);
if (!netdevice_impl.is_valid()) {
zxlogf(ERROR, "network-device: Bind failed, protocol not available");
return ZX_ERR_NOT_FOUND;
}
if ((status = NetworkDeviceInterface::Create(netdev->loop_.dispatcher(), netdevice_impl,
device_get_name(parent), &netdev->device_)) !=
ZX_OK) {
zxlogf(ERROR, "network-device: Failed to create inner device %s",
zx_status_get_string(status));
return status;
}
// If our parent supports the MacAddrImpl protocol, create the handler for it.
ddk::MacAddrImplProtocolClient mac_impl(parent);
if (mac_impl.is_valid()) {
if ((status = MacAddrDeviceInterface::Create(mac_impl, &netdev->mac_)) != ZX_OK) {
zxlogf(ERROR, "network-device: Failed to create inner mac device: %s",
zx_status_get_string(status));
return status;
}
}
if ((status = netdev->DdkAdd("network-device", 0, nullptr, 0, ZX_PROTOCOL_NETWORK_DEVICE)) !=
ZX_OK) {
zxlogf(ERROR, "network-device: Failed to bind %d", 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.
auto __UNUSED temp_ref = netdev.release();
return ZX_OK;
}
zx_status_t NetworkDevice::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
DdkTransaction transaction(txn);
::llcpp::fuchsia::hardware::network::DeviceInstance::Dispatch(this, msg, &transaction);
return transaction.Status();
}
void NetworkDevice::DdkUnbindNew(ddk::UnbindTxn unbindTxn) {
zxlogf(TRACE, "network-device: DdkUnbind");
device_->Teardown([this, txn = std::move(unbindTxn)]() mutable {
if (mac_) {
mac_->Teardown([txn = std::move(txn)]() mutable { txn.Reply(); });
} else {
txn.Reply();
}
});
}
void NetworkDevice::DdkRelease() {
zxlogf(TRACE, "network-device: DdkRelease");
delete this;
}
void NetworkDevice::GetDevice(zx::channel device, GetDeviceCompleter::Sync _completer) {
ZX_ASSERT_MSG(device_, "Can't serve device if not bound to parent implementation");
device_->Bind(std::move(device));
}
void NetworkDevice::GetMacAddressing(zx::channel mac, GetMacAddressingCompleter::Sync _completer) {
if (mac_) {
mac_->Bind(loop_.dispatcher(), std::move(mac));
}
}
static zx_driver_ops_t network_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = NetworkDevice::Create;
return ops;
}();
} // namespace network
// The formatter does not play nice with these macros.
// clang-format off
ZIRCON_DRIVER_BEGIN(network, network::network_driver_ops, "zircon", "0.1", 1)
BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_NETWORK_DEVICE_IMPL),
ZIRCON_DRIVER_END(network)
// clang-format on