blob: dad6b812a9de3a7dd8bc5c2dbbea4c0556c8be80 [file] [log] [blame]
// Copyright 2019 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.
// Used to test mvm/mac80211.c
#include <zircon/compiler.h>
#include <iterator>
#include <gtest/gtest.h>
#include "third_party/iwlwifi/mvm/mvm.h"
#include "third_party/iwlwifi/test/fake-ucode-test.h"
#include "third_party/iwlwifi/test/mock-function.h"
#include "third_party/iwlwifi/test/mock-trans.h"
#include "third_party/iwlwifi/test/single-ap-test.h"
#pragma GCC diagnostic ignored "-Wthread-safety-analysis"
namespace wlan::testing {
namespace {
// A helper class to set up a MAC interface with the given |sim_trans|.
//
// To use this, the test case just create an instance at beginning. This class will set up
// everything and release the resources in destructor.
//
class ClientInterfaceHelper {
public:
ClientInterfaceHelper(SimTransport* sim_trans) : mvmvif_{}, ap_sta_{}, txqs_{} {
mvm_ = iwl_trans_get_mvm(sim_trans->iwl_trans());
// First find a free slot for the interface.
mtx_lock(&mvm_->mutex);
EXPECT_EQ(ZX_OK, iwl_mvm_find_free_mvmvif_slot(mvm_, &mvmvif_idx_));
EXPECT_EQ(ZX_OK, iwl_mvm_bind_mvmvif(mvm_, mvmvif_idx_, &mvmvif_));
mtx_unlock(&mvm_->mutex);
// Initialize the interface data and add it to the mvm.
mvmvif_.mvm = mvm_;
mvmvif_.mac_role = WLAN_MAC_ROLE_CLIENT;
mvmvif_.bss_conf.beacon_int = 16;
iwl_mvm_mac_add_interface(&mvmvif_);
// Assign the phy_ctxt in the mvm to the interface.
struct ieee80211_channel chan = {
.band = NL80211_BAND_2GHZ,
.ch_num = 35, // any arbitrary values
.hw_value = 35, // any arbitrary values
};
struct cfg80211_chan_def chandef = {
.chan = &chan,
.width = NL80211_CHAN_WIDTH_80,
};
uint16_t phy_ctxt_id;
EXPECT_EQ(ZX_OK, iwl_mvm_add_chanctx(mvm_, &chandef, &phy_ctxt_id));
mvmvif_.phy_ctxt = &mvm_->phy_ctxts[phy_ctxt_id];
// Assign the AP sta info.
EXPECT_EQ(fuchsia_wlan_ieee80211_TIDS_MAX + 1, std::size(ap_sta_.txq));
for (size_t i = 0; i < std::size(ap_sta_.txq); i++) {
ap_sta_.txq[i] = &txqs_[i];
}
EXPECT_EQ(ZX_OK, iwl_mvm_mac_sta_state(&mvmvif_, &ap_sta_, IWL_STA_NOTEXIST, IWL_STA_NONE));
// Set it to associated.
mvmvif_.bss_conf.assoc = true;
}
~ClientInterfaceHelper() {
mtx_lock(&mvm_->mutex);
iwl_mvm_unbind_mvmvif(mvm_, mvmvif_idx_);
mtx_unlock(&mvm_->mutex);
}
struct iwl_mvm* mvm() { return mvm_; }
struct iwl_mvm_vif* mvmvif() { return &mvmvif_; }
private:
struct iwl_mvm* mvm_;
// for ClientInterfaceHelper().
struct iwl_mvm_vif mvmvif_;
int mvmvif_idx_;
struct iwl_mvm_sta ap_sta_;
struct iwl_mvm_txq txqs_[fuchsia_wlan_ieee80211_TIDS_MAX + 1];
};
class Mac80211Test : public SingleApTest {
public:
Mac80211Test() : mvm_(iwl_trans_get_mvm(sim_trans_.iwl_trans())) {}
~Mac80211Test() {}
protected:
struct iwl_mvm* mvm_;
};
class Mac80211UcodeTest : public FakeUcodeTest {
public:
Mac80211UcodeTest()
: FakeUcodeTest({IWL_UCODE_TLV_CAPA_LAR_SUPPORT}, {IWL_UCODE_TLV_API_WIFI_MCC_UPDATE}),
mvm_(iwl_trans_get_mvm(sim_trans_.iwl_trans())) {}
~Mac80211UcodeTest() {}
protected:
struct iwl_mvm* mvm_;
};
// Normal case: add an interface, then delete it.
TEST_F(Mac80211Test, AddThenRemove) {
struct iwl_mvm_vif mvmvif = {
.mvm = mvm_,
.mac_role = WLAN_MAC_ROLE_CLIENT,
};
ASSERT_EQ(ZX_OK, iwl_mvm_mac_add_interface(&mvmvif));
// Already existing
ASSERT_EQ(ZX_ERR_IO, iwl_mvm_mac_add_interface(&mvmvif));
// Check internal variables
EXPECT_EQ(1, mvm_->vif_count);
// Expect success.
ASSERT_EQ(ZX_OK, iwl_mvm_mac_remove_interface(&mvmvif));
// Removed so expect error
ASSERT_EQ(ZX_ERR_IO, iwl_mvm_mac_remove_interface(&mvmvif));
// Check internal variables
EXPECT_EQ(0, mvm_->vif_count);
}
// Add multiple interfaces sequentially and expect we can remove them.
TEST_F(Mac80211Test, MultipleAddsRemoves) {
struct iwl_mvm_vif mvmvif[] = {
{
.mvm = mvm_,
.mac_role = WLAN_MAC_ROLE_CLIENT,
},
{
.mvm = mvm_,
.mac_role = WLAN_MAC_ROLE_CLIENT,
},
{
.mvm = mvm_,
.mac_role = WLAN_MAC_ROLE_CLIENT,
},
};
size_t mvmvif_count = std::size(mvmvif);
for (size_t i = 0; i < mvmvif_count; ++i) {
ASSERT_EQ(ZX_OK, iwl_mvm_mac_add_interface(&mvmvif[i]));
// Check internal variables
EXPECT_EQ(i + 1, mvm_->vif_count);
}
for (size_t i = 0; i < mvmvif_count; ++i) {
// Expect success.
ASSERT_EQ(ZX_OK, iwl_mvm_mac_remove_interface(&mvmvif[i]));
// Check internal variables
EXPECT_EQ(mvmvif_count - i - 1, mvm_->vif_count);
}
}
TEST_F(Mac80211Test, ChanCtxSingle) {
struct ieee80211_channel chan = {
.band = NL80211_BAND_2GHZ,
.ch_num = 6, // any arbitrary values
.hw_value = 6, // any arbitrary values
};
struct cfg80211_chan_def chandef = {
.chan = &chan,
.width = NL80211_CHAN_WIDTH_20,
};
uint16_t phy_ctxt_id;
ASSERT_EQ(ZX_OK, iwl_mvm_add_chanctx(mvm_, &chandef, &phy_ctxt_id));
ASSERT_EQ(0, phy_ctxt_id);
struct iwl_mvm_phy_ctxt* phy_ctxt = &mvm_->phy_ctxts[phy_ctxt_id];
ASSERT_NE(0, phy_ctxt->ref);
EXPECT_EQ(6, phy_ctxt->channel->hw_value);
struct ieee80211_channel new_chan = {
.band = NL80211_BAND_2GHZ,
.ch_num = 3,
.hw_value = 3,
};
struct cfg80211_chan_def new_def = {
.chan = &new_chan,
.width = NL80211_CHAN_WIDTH_80,
};
iwl_mvm_change_chanctx(mvm_, phy_ctxt_id, &new_def);
EXPECT_EQ(3, phy_ctxt->channel->hw_value);
iwl_mvm_remove_chanctx(mvm_, phy_ctxt_id);
EXPECT_EQ(0, phy_ctxt->ref);
}
TEST_F(Mac80211Test, ChanCtxMultiple) {
struct ieee80211_channel chan = {
.band = NL80211_BAND_2GHZ,
.ch_num = 44, // any arbitrary values
.hw_value = 44, // any arbitrary values
};
struct cfg80211_chan_def chandef = {
.chan = &chan,
.width = NL80211_CHAN_WIDTH_20,
};
uint16_t phy_ctxt_id_0;
uint16_t phy_ctxt_id_1;
uint16_t phy_ctxt_id_2;
ASSERT_EQ(ZX_OK, iwl_mvm_add_chanctx(mvm_, &chandef, &phy_ctxt_id_0));
ASSERT_EQ(0, phy_ctxt_id_0);
ASSERT_EQ(ZX_OK, iwl_mvm_add_chanctx(mvm_, &chandef, &phy_ctxt_id_1));
ASSERT_EQ(1, phy_ctxt_id_1);
iwl_mvm_remove_chanctx(mvm_, phy_ctxt_id_0);
ASSERT_EQ(ZX_OK, iwl_mvm_add_chanctx(mvm_, &chandef, &phy_ctxt_id_2));
ASSERT_EQ(0, phy_ctxt_id_2);
ASSERT_EQ(ZX_OK, iwl_mvm_remove_chanctx(mvm_, phy_ctxt_id_2));
ASSERT_EQ(ZX_OK, iwl_mvm_remove_chanctx(mvm_, phy_ctxt_id_1));
ASSERT_EQ(ZX_ERR_BAD_STATE, iwl_mvm_remove_chanctx(mvm_, phy_ctxt_id_0)); // removed above
}
// Test the normal usage.
//
TEST_F(Mac80211Test, MvmSlotNormalCase) __TA_NO_THREAD_SAFETY_ANALYSIS {
int index;
struct iwl_mvm_vif mvmvif = {}; // an instance to bind
zx_status_t ret;
// Fill up all mvmvif slots and expect okay
mtx_lock(&mvm_->mutex);
for (size_t i = 0; i < MAX_NUM_MVMVIF; i++) {
ret = iwl_mvm_find_free_mvmvif_slot(mvm_, &index);
ASSERT_EQ(ret, ZX_OK);
ASSERT_EQ(i, index);
// First bind is okay
ret = iwl_mvm_bind_mvmvif(mvm_, index, &mvmvif);
ASSERT_EQ(ret, ZX_OK);
// A second bind is prohibited.
ret = iwl_mvm_bind_mvmvif(mvm_, index, &mvmvif);
ASSERT_EQ(ret, ZX_ERR_ALREADY_EXISTS);
}
// One more is not accepted.
ret = iwl_mvm_find_free_mvmvif_slot(mvm_, &index);
ASSERT_EQ(ret, ZX_ERR_NO_RESOURCES);
mtx_unlock(&mvm_->mutex);
}
// Bind / unbind test.
//
TEST_F(Mac80211Test, MvmSlotBindUnbind) __TA_NO_THREAD_SAFETY_ANALYSIS {
int index;
struct iwl_mvm_vif mvmvif = {}; // an instance to bind
zx_status_t ret;
// re-consider the test case if the slot number is changed.
ASSERT_EQ(MAX_NUM_MVMVIF, 4);
// First occupy the index 0, 1, 3.
mtx_lock(&mvm_->mutex);
ret = iwl_mvm_bind_mvmvif(mvm_, 0, &mvmvif);
ASSERT_EQ(ret, ZX_OK);
ret = iwl_mvm_bind_mvmvif(mvm_, 1, &mvmvif);
ASSERT_EQ(ret, ZX_OK);
ret = iwl_mvm_bind_mvmvif(mvm_, 3, &mvmvif);
ASSERT_EQ(ret, ZX_OK);
// Expect the free one is 2.
ret = iwl_mvm_find_free_mvmvif_slot(mvm_, &index);
ASSERT_EQ(ret, ZX_OK);
ASSERT_EQ(index, 2);
ret = iwl_mvm_bind_mvmvif(mvm_, 2, &mvmvif);
ASSERT_EQ(ret, ZX_OK);
// No more space.
ret = iwl_mvm_find_free_mvmvif_slot(mvm_, &index);
ASSERT_EQ(ret, ZX_ERR_NO_RESOURCES);
// Release the slot 1
iwl_mvm_unbind_mvmvif(mvm_, 1);
// The available slot should be slot 1
ret = iwl_mvm_find_free_mvmvif_slot(mvm_, &index);
ASSERT_EQ(ret, ZX_OK);
ASSERT_EQ(index, 1);
mtx_unlock(&mvm_->mutex);
}
class McastFilterTestWithoutIface : public Mac80211Test, public MockTrans {
public:
McastFilterTestWithoutIface() : mvm_(iwl_trans_get_mvm(sim_trans_.iwl_trans())) {
BIND_TEST(mvm_->trans);
}
~McastFilterTestWithoutIface() { mock_send_cmd_.VerifyAndClear(); }
// The values we expect. We only test few arbitrary bytes in 'addr_list' .
//
mock_function::MockFunction<zx_status_t,
uint32_t, // hcmd->id
uint8_t, // mcast_cmd->port_id
uint8_t, // mcast_cmd->count
uint8_t, // mcast_cmd->pass_all
uint8_t // mcast_cmd->bssid[0]
>
mock_send_cmd_;
static zx_status_t send_cmd_wrapper(struct iwl_trans* trans, struct iwl_host_cmd* hcmd) {
auto mcast_cmd = reinterpret_cast<const struct iwl_mcast_filter_cmd*>(hcmd->data[0]);
auto test = GET_TEST(McastFilterTestWithoutIface, trans);
return test->mock_send_cmd_.Call(hcmd->id, mcast_cmd->port_id, mcast_cmd->count,
mcast_cmd->pass_all, mcast_cmd->bssid[0]);
}
protected:
struct iwl_mvm* mvm_;
};
class McastFilterTest : public McastFilterTestWithoutIface {
public:
McastFilterTest() : helper_(ClientInterfaceHelper(&sim_trans_)) {}
~McastFilterTest() { mock_send_cmd_.VerifyAndClear(); }
protected:
ClientInterfaceHelper helper_;
};
TEST_F(McastFilterTest, McastFilterNormal) {
// mock function after the testing environment had been set.
bindSendCmd(send_cmd_wrapper);
// Test if we can configure the mcast filter.
mock_send_cmd_.ExpectCall(ZX_OK, WIDE_ID(LONG_GROUP, MCAST_FILTER_CMD), // hcmd->id
0, // mcast_cmd->port_id
0, // mcast_cmd->count
1, // mcast_cmd->pass_all
0x00 // mcast_cmd->bssid[0]
);
iwl_mvm_configure_filter(mvm_);
ASSERT_NE(mvm_->mcast_filter_cmd, nullptr);
unbindSendCmd();
}
TEST_F(McastFilterTestWithoutIface, McastFilterNoActiveInterface) {
// mock function after the testing environment had been set.
bindSendCmd(send_cmd_wrapper);
// We shall expect nothing will happen because ClientInterfaceHelper() is not called and
// no interface is created.
iwl_mvm_configure_filter(mvm_);
unbindSendCmd();
}
TEST_F(McastFilterTest, McastFilterAp) {
helper_.mvmvif()->mac_role = WLAN_MAC_ROLE_AP; // overwrite to AP.
// mock function after the testing environment had been set.
bindSendCmd(send_cmd_wrapper);
// We shall expect nothing will happen because the interface is for AP.
iwl_mvm_configure_filter(mvm_);
unbindSendCmd();
}
class RegulatoryTest : public Mac80211UcodeTest {
public:
RegulatoryTest() {}
~RegulatoryTest() {}
};
TEST_F(RegulatoryTest, RegulatoryTestNormal) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
bool changed;
wlan_phy_country_t country;
mtx_lock(&mvm_->mutex);
EXPECT_EQ(ZX_OK, iwl_mvm_get_regdomain(mvm_, "TW", MCC_SOURCE_WIFI, &changed, &country));
mtx_unlock(&mvm_->mutex);
EXPECT_EQ(true, changed);
EXPECT_EQ(0x54, country.alpha2[0]);
EXPECT_EQ(0x57, country.alpha2[1]);
EXPECT_EQ(true, mvm_->lar_regdom_set);
EXPECT_EQ(MCC_SOURCE_WIFI, mvm_->mcc_src);
}
TEST_F(RegulatoryTest, ChannelFilterDefault) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
// compare the channel list and flags.
// check the test/sim-mcc-update.cc:HandleMccUpdate() for the fake data.
EXPECT_EQ(4, mvm_->mcc_info.num_ch);
EXPECT_EQ(1, mvm_->mcc_info.channels[0]); // Ch1
EXPECT_EQ(0x034b, mvm_->mcc_info.ch_flags[0]); // Ch1 flags
EXPECT_EQ(2, mvm_->mcc_info.channels[1]); // Ch2
EXPECT_EQ(0x0f4a, mvm_->mcc_info.ch_flags[1]); // Ch2 flags
EXPECT_EQ(3, mvm_->mcc_info.channels[2]); // Ch3
EXPECT_EQ(0x0f43, mvm_->mcc_info.ch_flags[2]); // Ch3 flags
EXPECT_EQ(4, mvm_->mcc_info.channels[3]); // Ch4
EXPECT_EQ(0x0f4b, mvm_->mcc_info.ch_flags[3]); // Ch4 flags
}
TEST_F(RegulatoryTest, ChannelFilterPassiveList) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
// The passive scan just uses whatever channels that MLME told us to do.
uint8_t ch_list[] = {
14,
36,
144,
181,
};
uint8_t out_ch[std::size(ch_list)] = {}; // The returning list is never larger than the input.
EXPECT_EQ(4, reg_filter_channels(false, &mvm_->mcc_info, std::size(ch_list), ch_list, out_ch));
EXPECT_EQ(14, out_ch[0]);
EXPECT_EQ(181, out_ch[3]);
}
TEST_F(RegulatoryTest, ChannelFilterActiveList) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
uint8_t ch_list[] = {
255, // Ch 255: not supported in any country.
4, // Ch 4: expected to be returned by the API.
36, // Ch 36: not in the return list of wlan::testing::HandleMccUpdate().
3, // Ch 3: in the list but is inactive.
2, // Ch 2: in the list but is invalid.
};
uint8_t out_ch[std::size(ch_list)] = {}; // The returning list is never larger than the input.
EXPECT_EQ(1, reg_filter_channels(true, &mvm_->mcc_info, std::size(ch_list), ch_list, out_ch));
EXPECT_EQ(4, out_ch[0]);
}
// In this test case, we are testing the wild-card query. We expect the function returns all the
// channels allowed in the current regulatory domain.
//
// Check the test/sim-mcc-update.cc:HandleMccUpdate() for the fake data.
//
TEST_F(RegulatoryTest, ChannelFilterActiveWildcard) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
uint8_t out_ch[MAX_MCC_INFO_CH] = {};
EXPECT_EQ(2, reg_filter_channels(true, &mvm_->mcc_info, 0, nullptr, out_ch));
EXPECT_EQ(1, out_ch[0]); // Ch1
// Ch2 is invalid.
// Ch3 is inactive.
EXPECT_EQ(4, out_ch[1]); // Ch4
}
// In this test case, only 1 channel is specified. We would assume the upper layer is pretty sure
// this channel is usable (for example, it learns that fact from a passive scan on a DFS channel).
// Then the function just returns whatever the upper layer specified.
//
TEST_F(RegulatoryTest, ChannelFilterOneChannel) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
uint8_t ch_list[] = {
255, // Ch 255: not supported in any country.
};
uint8_t out_ch[std::size(ch_list)] = {}; // The returning list is never larger than the input.
EXPECT_EQ(1, reg_filter_channels(true, &mvm_->mcc_info, std::size(ch_list), ch_list, out_ch));
EXPECT_EQ(255, out_ch[0]);
}
TEST_F(RegulatoryTest, TestGetCurrentRegDomain) {
ClientInterfaceHelper helper = ClientInterfaceHelper(&sim_trans_);
bool changed;
wlan_phy_country_t country = {};
mtx_lock(&mvm_->mutex);
EXPECT_EQ(ZX_OK, iwl_mvm_get_current_regdomain(mvm_, &changed, &country));
mtx_unlock(&mvm_->mutex);
EXPECT_EQ(true, changed);
EXPECT_EQ(0x5a, country.alpha2[0]); // Becomes 'ZZ' now.
EXPECT_EQ(0x5a, country.alpha2[1]);
EXPECT_EQ(MCC_SOURCE_GET_CURRENT, mvm_->mcc_src);
}
} // namespace
} // namespace wlan::testing