blob: e49c30aabe0240d99a7f76a265f5c4253b90c059 [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 <wlan/common/channel.h>
#include <wlan/common/element_splitter.h>
#include <wlan/common/parse_element.h>
#include <wlan/mlme/assoc_context.h>
#include <zircon/status.h>
#include <set>
namespace wlan {
PHY AssocContext::DerivePhy() const {
if (ht_cap.has_value() && ht_op.has_value()) {
if (vht_cap.has_value() && vht_op.has_value()) {
return WLAN_PHY_VHT;
} else {
return WLAN_PHY_HT;
}
}
return WLAN_PHY_ERP;
}
const wlan_band_info_t* FindBand(const wlan_info_t& ifc_info, bool is_5ghz) {
ZX_DEBUG_ASSERT(ifc_info.num_bands <= WLAN_MAX_BANDS);
for (uint8_t idx = 0; idx < ifc_info.num_bands; idx++) {
auto bi = &ifc_info.bands[idx];
auto base_freq = bi->supported_channels.base_freq;
if (is_5ghz && base_freq == common::kBaseFreq5Ghz) {
return bi;
} else if (!is_5ghz && base_freq == common::kBaseFreq2Ghz) {
return bi;
}
}
return nullptr;
}
std::optional<std::vector<SupportedRate>> BuildAssocReqSuppRates(
const std::vector<uint8_t>& ap_basic_rate_set,
const std::vector<uint8_t>& ap_op_rate_set,
const std::vector<SupportedRate>& client_rates) {
std::set<uint8_t> basic(ap_basic_rate_set.cbegin(), ap_basic_rate_set.cend());
std::set<uint8_t> op(ap_op_rate_set.cbegin(), ap_op_rate_set.cend());
std::vector<SupportedRate> ap_rates(op.size());
std::transform(op.cbegin(), op.cend(), ap_rates.begin(), [&basic](uint8_t r) {
const bool is_basic = std::binary_search(basic.cbegin(), basic.cend(), r);
return SupportedRate(r, is_basic);
});
auto rates = IntersectRatesAp(ap_rates, client_rates);
size_t num_basic_rates =
std::count_if(rates.cbegin(), rates.cend(), [](auto& r) { return r.is_basic(); });
if (num_basic_rates != basic.size()) {
errorf("Ap demands %zu basic rates. Client supports %zu.\n", basic.size(), num_basic_rates);
return {};
}
return std::move(rates);
}
// TODO(NET-1287): Refactor together with Bss::ParseIE()
std::optional<AssocContext> ParseAssocRespIe(Span<const uint8_t> ie_chains) {
AssocContext ctx{};
for (auto [id, raw_body] : common::ElementSplitter(ie_chains)) {
switch (id) {
case element_id::kSuppRates: {
auto rates = common::ParseSupportedRates(raw_body);
if (!rates) { return {}; }
ctx.rates.insert(ctx.rates.end(), rates->begin(), rates->end());
break;
}
case element_id::kExtSuppRates: {
auto rates = common::ParseExtendedSupportedRates(raw_body);
if (!rates) { return {}; }
ctx.rates.insert(ctx.rates.end(), rates->begin(), rates->end());
break;
}
case element_id::kHtCapabilities: {
auto ht_cap = common::ParseHtCapabilities(raw_body);
if (!ht_cap) { return {}; }
ctx.ht_cap = {*ht_cap};
break;
}
case element_id::kHtOperation: {
auto ht_op = common::ParseHtOperation(raw_body);
if (!ht_op) { return {}; }
ctx.ht_op = {*ht_op};
break;
}
case element_id::kVhtCapabilities: {
auto vht_cap = common::ParseVhtCapabilities(raw_body);
if (!vht_cap) { return {}; }
ctx.vht_cap = {*vht_cap};
break;
}
case element_id::kVhtOperation: {
auto vht_op = common::ParseVhtOperation(raw_body);
if (!vht_op) { return {}; }
ctx.vht_op = {*vht_op};
break;
}
default:
break;
}
}
return ctx;
}
AssocContext MakeClientAssocCtx(const wlan_info_t& ifc_info, const wlan_channel_t join_chan) {
AssocContext assoc_ctx{};
assoc_ctx.cap = CapabilityInfo::FromDdk(ifc_info.caps);
auto band_info = FindBand(ifc_info, common::Is5Ghz(join_chan));
for (uint8_t rate : band_info->basic_rates) {
if (rate == 0) { break; } // basic_rates has fixed-length and is "null-terminated".
// SupportedRates Element can hold only 8 rates.
assoc_ctx.rates.emplace_back(rate);
}
if (ifc_info.supported_phys & WLAN_PHY_HT) {
assoc_ctx.ht_cap = HtCapabilities::FromDdk(band_info->ht_caps);
}
if (band_info->vht_supported) {
assoc_ctx.vht_cap = VhtCapabilities::FromDdk(band_info->vht_caps);
}
return assoc_ctx;
}
std::optional<AssocContext> MakeBssAssocCtx(const AssociationResponse& assoc_resp,
Span<const uint8_t> ie_chains,
const common::MacAddr& peer) {
auto ctx = ParseAssocRespIe(ie_chains);
if (!ctx.has_value()) { return {}; }
ctx->bssid = peer;
ctx->aid = assoc_resp.aid;
ctx->cap = assoc_resp.cap;
return ctx;
}
AssocContext IntersectAssocCtx(const AssocContext& bss, const AssocContext& client) {
auto result = AssocContext{};
result.cap = IntersectCapInfo(bss.cap, client.cap);
result.rates = IntersectRatesAp(bss.rates, client.rates);
if (bss.ht_cap.has_value() && client.ht_cap.has_value()) {
// TODO(porce): Supported MCS Set field from the outcome of the intersection
// requires the conditional treatment depending on the value of the following fields:
// - "Tx MCS Set Defined"
// - "Tx Rx MCS Set Not Equal"
// - "Tx Maximum Number Spatial Streams Supported"
// - "Tx Unequal Modulation Supported"
result.ht_cap = IntersectHtCap(bss.ht_cap.value(), client.ht_cap.value());
// Override the outcome of IntersectHtCap(), which is role agnostic.
// If AP can't rx STBC, then the client shall not tx STBC.
// Otherwise, the client shall do what it can do.
if (bss.ht_cap->ht_cap_info.rx_stbc() == 0) {
result.ht_cap->ht_cap_info.set_tx_stbc(0);
} else {
result.ht_cap->ht_cap_info.set_tx_stbc(client.ht_cap->ht_cap_info.tx_stbc());
}
// If AP can't tx STBC, then the client shall not expect to rx STBC.
// Otherwise, the client shall do what it can do.
if (bss.ht_cap->ht_cap_info.tx_stbc() == 0) {
result.ht_cap->ht_cap_info.set_rx_stbc(0);
} else {
result.ht_cap->ht_cap_info.set_rx_stbc(client.ht_cap->ht_cap_info.rx_stbc());
}
result.ht_op = bss.ht_op;
}
if (bss.vht_cap.has_value() && client.vht_cap.has_value()) {
result.vht_cap = IntersectVhtCap(bss.vht_cap.value(), client.vht_cap.value());
result.vht_op = bss.vht_op;
}
result.is_cbw40_rx =
result.ht_cap &&
bss.ht_cap->ht_cap_info.chan_width_set() == HtCapabilityInfo::TWENTY_FORTY &&
client.ht_cap->ht_cap_info.chan_width_set() == HtCapabilityInfo::TWENTY_FORTY;
// TODO(porce): Test capabilities and configurations of the client and its BSS.
// TODO(porce): Ralink dependency on BlockAck, AMPDU handling
result.is_cbw40_tx = false;
return result;
}
wlan_assoc_ctx_t AssocContext::ToDdk() const {
ZX_DEBUG_ASSERT(rates.size() <= WLAN_MAC_MAX_RATES);
wlan_assoc_ctx_t ddk{};
bssid.CopyTo(ddk.bssid);
ddk.aid = aid;
ddk.listen_interval = listen_interval;
ddk.phy = phy;
ddk.chan = chan;
ddk.rates_cnt = static_cast<uint8_t>(rates.size());
std::copy(rates.cbegin(), rates.cend(), ddk.rates);
ddk.has_ht_cap = ht_cap.has_value();
if (ht_cap.has_value()) { ddk.ht_cap = ht_cap->ToDdk(); }
ddk.has_ht_op = ht_op.has_value();
if (ht_op.has_value()) { ddk.ht_op = ht_op->ToDdk(); }
ddk.has_vht_cap = vht_cap.has_value();
if (vht_cap.has_value()) { ddk.vht_cap = vht_cap->ToDdk(); }
ddk.has_vht_op = vht_op.has_value();
if (vht_op.has_value()) { ddk.vht_op = vht_op->ToDdk(); }
return ddk;
}
} // namespace wlan