blob: 7ab188da825f25973fc2858f14d5da97d4a2dc3e [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/debug.h>
#include <lib/ddk/driver.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
#include "src/connectivity/network/drivers/network-device/network_device_bind.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;
std::unique_ptr netdev = fbl::make_unique_checked<NetworkDevice>(&ac, parent);
if (!ac.check()) {
zxlogf(ERROR, "no memory");
return ZX_ERR_NO_MEMORY;
}
thrd_t thread;
if (zx_status_t status = netdev->loop_.StartThread("network-device-handler", &thread);
status != ZX_OK) {
zxlogf(ERROR, "failed to create handler thread");
return status;
}
netdev->loop_thread_ = thread;
ddk::NetworkDeviceImplProtocolClient netdevice_impl(parent);
if (!netdevice_impl.is_valid()) {
zxlogf(ERROR, "bind failed, protocol not available");
return ZX_ERR_NOT_FOUND;
}
zx::status device = NetworkDeviceInterface::Create(netdev->loop_.dispatcher(), netdevice_impl);
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 %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.
auto __UNUSED temp_ref = netdev.release();
return ZX_OK;
}
void NetworkDevice::DdkUnbind(ddk::UnbindTxn unbindTxn) {
zxlogf(DEBUG, "DdkUnbind");
device_->Teardown([txn = std::move(unbindTxn)]() mutable { txn.Reply(); });
}
void NetworkDevice::DdkRelease() {
zxlogf(DEBUG, "DdkRelease");
delete this;
}
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 = NetworkDevice::Create,
};
} // namespace network
ZIRCON_DRIVER(network, network::network_driver_ops, "zircon", "0.1");