blob: 69b1362b33532bd16f5f9d2a1029633c3fd8b14a [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-mac.h"
#include <ddk/debug.h>
#include <ddk/driver.h>
#include <fuchsia/wlan/device/cpp/fidl.h>
#include <wlan/common/channel.h>
#include <wlan/wlanmac-ifc-proxy.h>
#include <mutex>
#include "utils.h"
namespace wlan {
namespace wlantap = ::fuchsia::wlan::tap;
namespace wlan_device = ::fuchsia::wlan::device;
namespace {
struct WlantapMacImpl : WlantapMac {
WlantapMacImpl(zx_device_t* phy_device, uint16_t id, wlan_device::MacRole role,
const wlantap::WlantapPhyConfig* phy_config, Listener* listener)
: id_(id), role_(role), phy_config_(phy_config), listener_(listener) {}
static void DdkUnbind(void* ctx) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
self.RemoveDevice();
}
static void DdkRelease(void* ctx) { delete static_cast<WlantapMacImpl*>(ctx); }
// Wlanmac protocol impl
static zx_status_t WlanmacQuery(void* ctx, uint32_t options, wlanmac_info_t* info) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
wlan_info_t* ifc_info = &info->ifc_info;
ConvertPhyInfo(ifc_info, self.phy_config_->phy_info);
ifc_info->mac_role = ConvertMacRole(self.role_);
return ZX_OK;
}
static zx_status_t WlanmacStart(void* ctx, wlanmac_ifc_t* ifc, void* cookie) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
{
std::lock_guard<std::mutex> guard(self.lock_);
if (self.ifc_) { return ZX_ERR_ALREADY_BOUND; }
self.ifc_ = WlanmacIfcClient{ifc, cookie};
}
self.listener_->WlantapMacStart(self.id_);
return ZX_OK;
}
static void WlanmacStop(void* ctx) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
{
std::lock_guard<std::mutex> guard(self.lock_);
self.ifc_ = {};
}
self.listener_->WlantapMacStop(self.id_);
}
static zx_status_t WlanmacQueueTx(void* ctx, uint32_t options, wlan_tx_packet_t* pkt) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
self.listener_->WlantapMacQueueTx(self.id_, pkt);
return ZX_OK;
}
static zx_status_t WlanmacSetChannel(void* ctx, uint32_t options, wlan_channel_t* chan) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
if (!wlan::common::IsValidChan(*chan)) { return ZX_ERR_INVALID_ARGS; }
self.listener_->WlantapMacSetChannel(self.id_, chan);
return ZX_OK;
}
static zx_status_t WlanmacConfigureBss(void* ctx, uint32_t options, wlan_bss_config_t* config) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
bool expected_remote = self.role_ == wlan_device::MacRole::CLIENT;
if (config->remote != expected_remote) { return ZX_ERR_INVALID_ARGS; }
self.listener_->WlantapMacConfigureBss(self.id_, config);
return ZX_OK;
}
static zx_status_t WlanmacEnableBeaconing(void* ctx, uint32_t options,
wlan_bcn_config_t* bcn_cfg) {
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
if (bcn_cfg != nullptr) { return ZX_ERR_NOT_SUPPORTED; }
return ZX_OK;
}
static zx_status_t WlanmacConfigureBeacon(void* ctx, uint32_t options, wlan_tx_packet_t* pkt) {
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
if (pkt != nullptr) { return ZX_ERR_NOT_SUPPORTED; }
return ZX_OK;
}
static zx_status_t WlanmacSetKey(void* ctx, uint32_t options, wlan_key_config_t* key_config) {
auto& self = *static_cast<WlantapMacImpl*>(ctx);
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
self.listener_->WlantapMacSetKey(self.id_, key_config);
return ZX_OK;
}
static zx_status_t WlanmacConfigureAssoc(void* ctx, uint32_t options,
wlan_assoc_ctx* assoc_ctx) {
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
if (assoc_ctx != 0) { return ZX_ERR_INVALID_ARGS; }
// TODO(NET-1270): Evalute the use and implement
return ZX_OK;
}
static zx_status_t WlanmacClearAssoc(void* ctx, uint32_t options, const uint8_t* mac) {
if (options != 0) { return ZX_ERR_INVALID_ARGS; }
if (!mac) { return ZX_ERR_INVALID_ARGS; }
// TODO(NET-1270): Evalute the use and implement
return ZX_OK;
}
// WlantapMac impl
virtual void Rx(const std::vector<uint8_t>& data, const wlantap::WlanRxInfo& rx_info) override {
std::lock_guard<std::mutex> guard(lock_);
if (ifc_) {
wlan_rx_info_t converted_info = {.rx_flags = rx_info.rx_flags,
.valid_fields = rx_info.valid_fields,
.phy = rx_info.phy,
.data_rate = rx_info.data_rate,
.chan = {.primary = rx_info.chan.primary,
.cbw = static_cast<uint8_t>(rx_info.chan.cbw),
.secondary80 = rx_info.chan.secondary80},
.mcs = rx_info.mcs,
.rssi_dbm = rx_info.rssi_dbm,
.rcpi_dbmh = rx_info.rcpi_dbmh,
.snr_dbh = rx_info.snr_dbh};
ifc_.Recv(0, &data[0], data.size(), &converted_info);
}
}
virtual void Status(uint32_t status) override {
std::lock_guard<std::mutex> guard(lock_);
if (ifc_) { ifc_.Status(status); }
}
virtual void ReportTxStatus(const wlantap::WlanTxStatus& ts) override {
std::lock_guard<std::mutex> guard(lock_);
if (ifc_) {
wlan_tx_status_t converted_tx_status = ConvertTxStatus(ts);
ifc_.ReportTxStatus(&converted_tx_status);
}
}
virtual void RemoveDevice() override {
{
std::lock_guard<std::mutex> guard(lock_);
ifc_ = {};
}
device_remove(device_);
}
zx_device_t* device_ = nullptr;
uint16_t id_;
wlan_device::MacRole role_;
std::mutex lock_;
WlanmacIfcClient ifc_ __TA_GUARDED(lock_);
const wlantap::WlantapPhyConfig* phy_config_;
Listener* listener_;
};
} // namespace
zx_status_t CreateWlantapMac(zx_device_t* parent_phy, const wlan_device::MacRole role,
const wlantap::WlantapPhyConfig* phy_config, uint16_t id,
WlantapMac::Listener* listener, WlantapMac** ret) {
char name[ZX_MAX_NAME_LEN + 1];
snprintf(name, sizeof(name), "%s-mac%u", device_get_name(parent_phy), id);
std::unique_ptr<WlantapMacImpl> wlanmac(
new WlantapMacImpl(parent_phy, id, role, phy_config, listener));
static zx_protocol_device_t device_ops = {.version = DEVICE_OPS_VERSION,
.unbind = &WlantapMacImpl::DdkUnbind,
.release = &WlantapMacImpl::DdkRelease};
static wlanmac_protocol_ops_t proto_ops = {
.query = &WlantapMacImpl::WlanmacQuery,
.start = &WlantapMacImpl::WlanmacStart,
.stop = &WlantapMacImpl::WlanmacStop,
.queue_tx = &WlantapMacImpl::WlanmacQueueTx,
.set_channel = &WlantapMacImpl::WlanmacSetChannel,
.configure_bss = &WlantapMacImpl::WlanmacConfigureBss,
.enable_beaconing = &WlantapMacImpl::WlanmacEnableBeaconing,
.configure_beacon = &WlantapMacImpl::WlanmacConfigureBeacon,
.set_key = &WlantapMacImpl::WlanmacSetKey,
.configure_assoc = &WlantapMacImpl::WlanmacConfigureAssoc,
.clear_assoc = &WlantapMacImpl::WlanmacClearAssoc,
};
device_add_args_t args = {.version = DEVICE_ADD_ARGS_VERSION,
.name = name,
.ctx = wlanmac.get(),
.ops = &device_ops,
.proto_id = ZX_PROTOCOL_WLANMAC,
.proto_ops = &proto_ops};
zx_status_t status = device_add(parent_phy, &args, &wlanmac->device_);
if (status != ZX_OK) {
zxlogf(ERROR, "%s: could not add device: %d\n", __func__, status);
return status;
}
// Transfer ownership to devmgr
*ret = wlanmac.release();
return ZX_OK;
}
} // namespace wlan