blob: 09bf746d502622c39a0b9e89fcde9f4fa0210bb0 [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 "wlantap-phy.h"
#include <ddk/debug.h>
#include <fuchsia/wlan/device/c/fidl.h>
#include <fuchsia/wlan/device/cpp/fidl.h>
#include <fuchsia/wlan/tap/c/fidl.h>
#include <wlan/async/dispatcher.h>
#include <wlan/protocol/mac.h>
#include <wlan/protocol/phy-impl.h>
#include <zircon/status.h>
#include <array>
#include "utils.h"
#include "wlantap-mac.h"
namespace wlan {
namespace wlan_common = ::fuchsia::wlan::common;
namespace wlan_device = ::fuchsia::wlan::device;
namespace wlantap = ::fuchsia::wlan::tap;
namespace {
template <typename T>
zx_status_t EncodeFidlMessage(uint32_t ordinal, T* message, fidl::Encoder* encoder) {
encoder->Reset(ordinal);
encoder->Alloc(fidl::CodingTraits<T>::encoded_size);
message->Encode(encoder, sizeof(fidl_message_header_t));
auto encoded = encoder->GetMessage();
const char* err = nullptr;
zx_status_t status =
fidl_validate(T::FidlType, encoded.bytes().data() + sizeof(fidl_message_header_t),
encoded.bytes().actual() - sizeof(fidl_message_header_t), 0, &err);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: FIDL validation failed: %s (%d)\n", __func__, err, status);
return status;
}
return ZX_OK;
}
template <typename T>
zx_status_t SendFidlMessage(uint32_t ordinal, T* message, fidl::Encoder* encoder,
const zx::channel& channel) {
zx_status_t status = EncodeFidlMessage(ordinal, message, encoder);
if (status != ZX_OK) { return status; }
auto m = encoder->GetMessage();
status = channel.write(0, m.bytes().data(), m.bytes().actual(), nullptr, 0);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: write to channel failed: %d\n", __func__, status);
return status;
}
return ZX_OK;
}
template <typename T, size_t N>::fidl::Array<T, N> ToFidlArray(const T (&c_array)[N]) {
::fidl::Array<T, N> ret;
std::copy_n(&c_array[0], N, ret.begin());
return ret;
};
struct EventSender {
explicit EventSender(const zx::channel& channel) : encoder_(0), channel_(channel.get()) {}
void Shutdown() { channel_ = ZX_HANDLE_INVALID; }
void SendTxEvent(uint16_t wlanmac_id, wlan_tx_packet_t* pkt) {
tx_args_.wlanmac_id = wlanmac_id;
ConvertTxInfo(pkt->info, &tx_args_.packet.info);
auto& data = tx_args_.packet.data;
data.clear();
auto head = static_cast<const uint8_t*>(pkt->packet_head.data_buffer);
std::copy_n(head, pkt->packet_head.data_size, std::back_inserter(data));
if (pkt->packet_tail != nullptr) {
auto tail = static_cast<const uint8_t*>(pkt->packet_tail->data_buffer);
std::copy_n(tail + pkt->tail_offset, pkt->packet_tail->data_size - pkt->tail_offset,
std::back_inserter(data));
}
Send(EventOrdinal::Tx, &tx_args_);
}
void SendSetChannelEvent(uint16_t wlanmac_id, wlan_channel_t* channel) {
wlantap::SetChannelArgs args = {
.wlanmac_id = wlanmac_id,
.chan = {.primary = channel->primary,
.cbw = static_cast<wlan_common::CBW>(channel->cbw),
.secondary80 = channel->secondary80}};
Send(EventOrdinal::SetChannel, &args);
}
void SendConfigureBssEvent(uint16_t wlanmac_id, wlan_bss_config_t* config) {
wlantap::ConfigureBssArgs args = {.wlanmac_id = wlanmac_id,
.config = {.bss_type = config->bss_type,
.bssid = ToFidlArray(config->bssid),
.remote = config->remote}};
Send(EventOrdinal::ConfigureBss, &args);
}
void SendSetKeyEvent(uint16_t wlanmac_id, wlan_key_config_t* config) {
set_key_args_.wlanmac_id = wlanmac_id;
set_key_args_.config.protection = config->protection;
set_key_args_.config.cipher_oui = ToFidlArray(config->cipher_oui);
set_key_args_.config.cipher_type = config->cipher_type;
set_key_args_.config.key_type = config->key_type;
set_key_args_.config.peer_addr = ToFidlArray(config->peer_addr);
set_key_args_.config.key_idx = config->key_idx;
auto& key = set_key_args_.config.key;
key.clear();
key.reserve(config->key_len);
std::copy_n(config->key, config->key_len, std::back_inserter(key));
Send(EventOrdinal::SetKey, &set_key_args_);
}
void SendWlanmacStartEvent(uint16_t wlanmac_id) {
wlantap::WlanmacStartArgs args = {.wlanmac_id = wlanmac_id};
Send(EventOrdinal::WlanmacStart, &args);
}
private:
enum class EventOrdinal : uint32_t {
Tx = fuchsia_wlan_tap_WlantapPhyTxOrdinal,
SetChannel = fuchsia_wlan_tap_WlantapPhySetChannelOrdinal,
ConfigureBss = fuchsia_wlan_tap_WlantapPhyConfigureBssOrdinal,
// TODO: ConfigureBeacon
SetKey = fuchsia_wlan_tap_WlantapPhySetKeyOrdinal,
WlanmacStart = fuchsia_wlan_tap_WlantapPhyWlanmacStartOrdinal
};
template <typename T> void Send(EventOrdinal ordinal, T* message) {
if (channel_ == ZX_HANDLE_INVALID) { return; }
zx_status_t status = SendFidlMessage(static_cast<uint32_t>(ordinal), message, &encoder_,
*zx::unowned<zx::channel>(channel_));
if (status != ZX_OK) {
zxlogf(ERROR, "%s: failed to send FIDL message: %d\n", __func__, status);
}
}
static void ConvertTxInfo(const wlan_tx_info_t& in, wlantap::WlanTxInfo* out) {
out->tx_flags = in.tx_flags;
out->valid_fields = in.valid_fields;
out->phy = in.phy;
out->cbw = in.cbw;
out->mcs = in.mcs;
out->tx_vector_idx = in.tx_vector_idx;
}
fidl::Encoder encoder_;
zx_handle_t channel_;
// Messages that require memory allocation
wlantap::TxArgs tx_args_;
wlantap::SetKeyArgs set_key_args_;
};
template <typename T, size_t MAX_COUNT> class DevicePool {
public:
template <class F> zx_status_t TryCreateNew(F factory, uint16_t* out_id) {
for (size_t id = 0; id < MAX_COUNT; ++id) {
if (pool_[id] == nullptr) {
T* dev = nullptr;
zx_status_t status = factory(id, &dev);
if (status != ZX_OK) { return status; }
pool_[id] = dev;
*out_id = id;
return ZX_OK;
}
}
return ZX_ERR_NO_RESOURCES;
}
T* Get(uint16_t id) {
if (id >= MAX_COUNT) { return nullptr; }
return pool_[id];
}
T* Release(uint16_t id) {
if (id >= MAX_COUNT) { return nullptr; }
T* ret = pool_[id];
pool_[id] = nullptr;
return ret;
}
void ReleaseAll() { std::fill(pool_.begin(), pool_.end(), nullptr); }
private:
std::array<T*, MAX_COUNT> pool_{};
};
constexpr size_t kMaxMacDevices = 4;
struct WlantapPhy : wlantap::WlantapPhy, WlantapMac::Listener {
WlantapPhy(zx_device_t* device, zx::channel user_channel,
std::unique_ptr<wlantap::WlantapPhyConfig> phy_config, async_dispatcher_t* loop)
: phy_config_(std::move(phy_config)),
loop_(loop),
user_channel_binding_(this, std::move(user_channel), loop),
event_sender_(user_channel_binding_.channel()) {
user_channel_binding_.set_error_handler([this](zx_status_t status) {
zxlogf(INFO, "wlantap phy: unbinding device because the channel was closed\n");
if (report_tx_status_count_) {
zxlogf(INFO, "Tx Status Reports sent druing device lifetime: %zu\n",
report_tx_status_count_);
}
{
std::lock_guard<std::mutex> guard(user_channel_lock_);
user_channel_binding_.set_error_handler(nullptr);
}
// Remove the device if the client closed the channel
Unbind();
zxlogf(INFO, "wlantap phy: done unbinding\n");
});
}
static void DdkUnbind(void* ctx) {
zxlogf(INFO, "wlantap phy: unbinding device per request from DDK\n");
auto self = static_cast<WlantapPhy*>(ctx);
if (self->report_tx_status_count_) {
zxlogf(INFO, "Tx Status Reports sent druing device lifetime: %zu\n",
self->report_tx_status_count_);
}
self->Unbind();
zxlogf(INFO, "wlantap phy: done unbinding\n");
}
static void DdkRelease(void* ctx) {
zxlogf(INFO, "wlantap phy: DdkRelease\n");
delete static_cast<WlantapPhy*>(ctx);
zxlogf(INFO, "wlantap phy: DdkRelease done\n");
}
void Unbind() {
{
std::lock_guard<std::mutex> guard(user_channel_lock_);
event_sender_.Shutdown();
user_channel_binding_.Unbind();
}
// Flush any remaining tasks in the event loop before destroying the interfaces
::async::PostTask(loop_, [this] {
{
std::lock_guard<std::mutex> guard(wlanmac_lock_);
wlanmac_devices_.ReleaseAll();
}
device_remove(device_);
});
}
// wlanphy-impl DDK interface
zx_status_t Query(wlanphy_info_t* info) {
zxlogf(INFO, "wlantap phy: received a 'Query' DDK request\n");
zx_status_t status = ConvertPhyInfo(&info->wlan_info, phy_config_->phy_info);
zxlogf(INFO, "wlantap phy: responded to 'Query' with status %s\n",
zx_status_get_string(status));
return status;
}
template <typename V, typename T> static bool contains(const V& v, const T& t) {
return std::find(v.cbegin(), v.cend(), t) != v.cend();
}
zx_status_t CreateIface(uint16_t role, uint16_t* id) {
zxlogf(INFO, "wlantap phy: received a 'CreateIface' DDK request\n");
wlan_device::MacRole dev_role = ConvertMacRole(role);
if (!contains(phy_config_->phy_info.mac_roles, dev_role)) {
zxlogf(ERROR, "wlantap phy: CreateIface: role not supported\n");
return ZX_ERR_NOT_SUPPORTED;
}
std::lock_guard<std::mutex> guard(wlanmac_lock_);
zx_status_t status = wlanmac_devices_.TryCreateNew(
[&](uint16_t id, WlantapMac** out_dev) {
return CreateWlantapMac(device_, dev_role, phy_config_.get(), id, this, out_dev);
},
id);
if (status != ZX_OK) {
zxlogf(ERROR,
"wlantap phy: CreateIface: maximum number of interfaces already reached\n");
return ZX_ERR_NO_RESOURCES;
}
zxlogf(INFO, "wlantap phy: CreateIface: success\n");
return ZX_OK;
}
zx_status_t DestroyIface(uint16_t id) {
zxlogf(INFO, "wlantap phy: received a 'DestroyIface' DDK request\n");
std::lock_guard<std::mutex> guard(wlanmac_lock_);
WlantapMac* wlanmac = wlanmac_devices_.Release(id);
if (wlanmac == nullptr) {
zxlogf(ERROR, "wlantap phy: DestroyIface: invalid iface id\n");
return ZX_ERR_INVALID_ARGS;
}
wlanmac->RemoveDevice();
zxlogf(ERROR, "wlantap phy: DestroyIface: done\n");
return ZX_OK;
}
// wlantap::WlantapPhy impl
virtual void Rx(uint16_t wlanmac_id, ::std::vector<uint8_t> data,
wlantap::WlanRxInfo info) override {
zxlogf(INFO, "wlantap phy: Rx(%zu bytes)\n", data.size());
std::lock_guard<std::mutex> guard(wlanmac_lock_);
if (WlantapMac* wlanmac = wlanmac_devices_.Get(wlanmac_id)) { wlanmac->Rx(data, info); }
zxlogf(INFO, "wlantap phy: Rx done\n");
}
virtual void Status(uint16_t wlanmac_id, uint32_t st) override {
zxlogf(INFO, "wlantap phy: Status(%u)\n", st);
std::lock_guard<std::mutex> guard(wlanmac_lock_);
if (WlantapMac* wlanmac = wlanmac_devices_.Get(wlanmac_id)) { wlanmac->Status(st); }
zxlogf(INFO, "wlantap phy: Status done\n");
}
virtual void ReportTxStatus(uint16_t wlanmac_id, wlantap::WlanTxStatus ts) override {
std::lock_guard<std::mutex> guard(wlanmac_lock_);
if (WlantapMac* wlanmac = wlanmac_devices_.Get(wlanmac_id)) {
++report_tx_status_count_;
wlanmac->ReportTxStatus(ts);
}
if (!phy_config_->quiet) { zxlogf(INFO, "wlantap phy: ReportTxStatus done\n"); }
}
// WlantapMac::Listener impl
virtual void WlantapMacStart(uint16_t wlanmac_id) override {
zxlogf(INFO, "wlantap phy: WlantapMacStart id=%u\n", wlanmac_id);
std::lock_guard<std::mutex> guard(user_channel_lock_);
event_sender_.SendWlanmacStartEvent(wlanmac_id);
zxlogf(INFO, "wlantap phy: WlantapMacStart done\n");
}
virtual void WlantapMacStop(uint16_t wlanmac_id) override {
zxlogf(INFO, "wlantap phy: WlantapMacStop\n");
}
virtual void WlantapMacQueueTx(uint16_t wlanmac_id, wlan_tx_packet_t* pkt) override {
if (!phy_config_->quiet) {
zxlogf(INFO, "wlantap phy: WlantapMacQueueTx id=%u\n", wlanmac_id);
}
std::lock_guard<std::mutex> guard(user_channel_lock_);
event_sender_.SendTxEvent(wlanmac_id, pkt);
if (!phy_config_->quiet) { zxlogf(INFO, "wlantap phy: WlantapMacQueueTx done\n"); }
}
virtual void WlantapMacSetChannel(uint16_t wlanmac_id, wlan_channel_t* channel) override {
if (!phy_config_->quiet) {
zxlogf(INFO, "wlantap phy: WlantapMacSetChannel id=%u\n", wlanmac_id);
}
std::lock_guard<std::mutex> guard(user_channel_lock_);
event_sender_.SendSetChannelEvent(wlanmac_id, channel);
if (!phy_config_->quiet) { zxlogf(INFO, "wlantap phy: WlantapMacSetChannel done\n"); }
}
virtual void WlantapMacConfigureBss(uint16_t wlanmac_id, wlan_bss_config_t* config) override {
zxlogf(INFO, "wlantap phy: WlantapMacConfigureBss id=%u\n", wlanmac_id);
std::lock_guard<std::mutex> guard(user_channel_lock_);
event_sender_.SendConfigureBssEvent(wlanmac_id, config);
zxlogf(INFO, "wlantap phy: WlantapMacConfigureBss done\n");
}
virtual void WlantapMacSetKey(uint16_t wlanmac_id, wlan_key_config_t* key_config) override {
zxlogf(INFO, "wlantap phy: WlantapMacSetKey id=%u\n", wlanmac_id);
std::lock_guard<std::mutex> guard(user_channel_lock_);
event_sender_.SendSetKeyEvent(wlanmac_id, key_config);
zxlogf(INFO, "wlantap phy: WlantapMacSetKey done\n");
}
zx_device_t* device_;
const std::unique_ptr<const wlantap::WlantapPhyConfig> phy_config_;
async_dispatcher_t* loop_;
std::mutex wlanmac_lock_;
DevicePool<WlantapMac, kMaxMacDevices> wlanmac_devices_ __TA_GUARDED(wlanmac_lock_);
std::mutex user_channel_lock_;
fidl::Binding<wlantap::WlantapPhy> user_channel_binding_ __TA_GUARDED(user_channel_lock_);
EventSender event_sender_ __TA_GUARDED(user_channel_lock_);
size_t report_tx_status_count_ = 0;
};
} // namespace
#define DEV(c) static_cast<WlantapPhy*>(c)
static wlanphy_impl_protocol_ops_t wlanphy_impl_ops = {
.query = [](void* ctx, wlanphy_info_t* info) -> zx_status_t { return DEV(ctx)->Query(info); },
.create_iface = [](void* ctx, uint16_t mac_role, uint16_t* id) -> zx_status_t {
return DEV(ctx)->CreateIface(mac_role, id);
},
.destroy_iface = [](void* ctx, uint16_t id) -> zx_status_t {
return DEV(ctx)->DestroyIface(id);
},
};
#undef DEV
zx_status_t CreatePhy(zx_device_t* wlantapctl, zx::channel user_channel,
std::unique_ptr<wlantap::WlantapPhyConfig> config, async_dispatcher_t* loop) {
zxlogf(INFO, "wlantap: creating phy\n");
auto phy =
std::make_unique<WlantapPhy>(wlantapctl, std::move(user_channel), std::move(config), loop);
static zx_protocol_device_t device_ops = {.version = DEVICE_OPS_VERSION,
.unbind = &WlantapPhy::DdkUnbind,
.release = &WlantapPhy::DdkRelease};
device_add_args_t args = {.version = DEVICE_ADD_ARGS_VERSION,
.name = phy->phy_config_->name.c_str(),
.ctx = phy.get(),
.ops = &device_ops,
.proto_id = ZX_PROTOCOL_WLANPHY_IMPL,
.proto_ops = &wlanphy_impl_ops};
zx_status_t status = device_add(wlantapctl, &args, &phy->device_);
if (status != ZX_OK) {
zxlogf(ERROR, "wlantap: %s: could not add device: %d\n", __func__, status);
return status;
}
// Transfer ownership to devmgr
phy.release();
zxlogf(INFO, "wlantap: phy successfully created\n");
return ZX_OK;
}
} // namespace wlan