blob: fe30aae43ea58f1b7b0f28bb383a055c887a3b99 [file] [log] [blame]
// Copyright 2018 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 <fuchsia/wlan/device/llcpp/fidl.h>
#include <fuchsia/wlan/tap/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/dispatcher.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/device.h>
#include <zircon/fidl.h>
#include <zircon/status.h>
#include <memory>
#include <mutex>
#include <ddktl/fidl.h>
#include "src/connectivity/wlan/testing/wlantap-driver/wlantapctl_bind.h"
#include "wlantap-phy.h"
namespace {
namespace wlantap = fuchsia_wlan_tap;
class WlantapDriver {
public:
zx_status_t GetOrStartLoop(async_dispatcher_t** out) {
std::lock_guard<std::mutex> guard(mutex_);
if (!loop_) {
auto l = std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
zx_status_t status = l->StartThread("wlantap-loop");
if (status != ZX_OK) {
return status;
}
loop_ = std::move(l);
}
*out = loop_->dispatcher();
return ZX_OK;
}
private:
std::mutex mutex_;
std::unique_ptr<async::Loop> loop_;
};
struct WlantapCtl : fidl::WireRawChannelInterface<wlantap::WlantapCtl> {
WlantapCtl(WlantapDriver* driver) : driver_(driver) {}
static void DdkRelease(void* ctx) { delete static_cast<WlantapCtl*>(ctx); }
void CreatePhy(wlantap::wire::WlantapPhyConfig config, ::zx::channel proxy,
CreatePhyCompleter::Sync& completer) override {
zx_status_t status;
async_dispatcher_t* loop;
if ((status = driver_->GetOrStartLoop(&loop)) != ZX_OK) {
completer.Reply(status);
return;
}
// Convert to HLCPP by transiting through fidl bytes.
auto phy_config = ::fuchsia::wlan::tap::WlantapPhyConfig::New();
{
fidl::OwnedEncodedMessage<wlantap::wire::WlantapPhyConfig> encoded(&config);
if (!encoded.ok()) {
completer.Reply(encoded.status());
return;
}
auto converted = fidl::OutgoingToIncomingMessage(encoded.GetOutgoingMessage());
ZX_ASSERT(converted.ok());
fidl::DecodedMessage<wlantap::wire::WlantapPhyConfig> decoded(converted.incoming_message());
if (!decoded.ok()) {
completer.Reply(status);
return;
}
fidl::Decoder dec(fidl::HLCPPIncomingMessage(
::fidl::BytePart(decoded.bytes(), decoded.byte_actual(), decoded.byte_actual()),
fidl::HandleInfoPart()));
::fuchsia::wlan::tap::WlantapPhyConfig::Decode(&dec, phy_config.get(), /* offset = */ 0);
}
if ((status = wlan::CreatePhy(device_, std::move(proxy), std::move(phy_config), loop)) !=
ZX_OK) {
completer.Reply(status);
} else {
completer.Reply(ZX_OK);
}
};
static zx_status_t DdkMessage(void* ctx, fidl_incoming_msg_t* msg, fidl_txn_t* txn) {
auto self = static_cast<WlantapCtl*>(ctx);
DdkTransaction transaction(txn);
fidl::WireDispatch<wlantap::WlantapCtl>(self, msg, &transaction);
return transaction.Status();
}
zx_device_t* device_ = nullptr;
WlantapDriver* driver_;
};
} // namespace
zx_status_t wlantapctl_init(void** out_ctx) {
*out_ctx = new WlantapDriver();
return ZX_OK;
}
zx_status_t wlantapctl_bind(void* ctx, zx_device_t* parent) {
auto driver = static_cast<WlantapDriver*>(ctx);
auto wlantapctl = std::make_unique<WlantapCtl>(driver);
static zx_protocol_device_t device_ops = {
.version = DEVICE_OPS_VERSION,
.release = &WlantapCtl::DdkRelease,
.message = &WlantapCtl::DdkMessage,
};
device_add_args_t args = {.version = DEVICE_ADD_ARGS_VERSION,
.name = "wlantapctl",
.ctx = wlantapctl.get(),
.ops = &device_ops};
zx_status_t status = device_add(parent, &args, &wlantapctl->device_);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not add device: %d", __func__, status);
return status;
}
// Transfer ownership to devmgr
wlantapctl.release();
return ZX_OK;
}
void wlantapctl_release(void* ctx) { delete static_cast<WlantapDriver*>(ctx); }
static constexpr zx_driver_ops_t wlantapctl_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.init = wlantapctl_init;
ops.bind = wlantapctl_bind;
ops.release = wlantapctl_release;
return ops;
}();
ZIRCON_DRIVER(wlantapctl, wlantapctl_driver_ops, "fuchsia", "0.1");