blob: c1c143852b7966ed3599979571bbcecb8d9808c6 [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.
#include <ddk/protocol/wlanif.h>
#include "src/connectivity/wlan/drivers/testing/lib/sim-fake-ap/sim-fake-ap.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/sim.h"
#include "src/connectivity/wlan/drivers/third_party/broadcom/brcmfmac/sim/test/sim_test.h"
#include "src/connectivity/wlan/lib/common/cpp/include/wlan/common/status_code.h"
namespace wlan::brcmfmac {
// Some default AP and association request values
constexpr uint16_t kDefaultCh = 149;
constexpr wlan_channel_t kDefaultChannel = {
.primary = kDefaultCh, .cbw = WLAN_CHANNEL_BANDWIDTH__20, .secondary80 = 0};
// Chanspec value corresponding to kDefaultChannel with current d11 encoder.
constexpr uint16_t kDefaultChanspec = 53397;
constexpr uint16_t kTestChanspec = 0xd0a5;
constexpr uint16_t kTest1Chanspec = 0xd095;
constexpr simulation::WlanTxInfo kDefaultTxInfo = {.channel = kDefaultChannel};
constexpr wlan_ssid_t kDefaultSsid = {.len = 15, .ssid = "Fuchsia Fake AP"};
const common::MacAddr kDefaultBssid({0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc});
const common::MacAddr kFakeMac({0xde, 0xad, 0xbe, 0xef, 0x00, 0x02});
class DynamicIfTest : public SimTest {
public:
// How long an individual test will run for. We need an end time because tests run until no more
// events remain and if ap's are beaconing the test will run indefinitely.
static constexpr zx::duration kTestDuration = zx::sec(100);
DynamicIfTest() = default;
// How many devices have been registered by the fake devhost
uint32_t DeviceCount();
// Force fail an attempt to stop the soft AP
void InjectStopAPError();
// Verify SoftAP channel followed client channel
void ChannelCheck();
// Generate an association request to send to the soft AP
void TxAuthAndAssocReq();
void VerifyAssocWithSoftAP();
// Query for wlanphy info
void PhyQuery(wlanphy_impl_info_t* out_info);
// Interfaces to set and get chanspec iovar in sim-fw
void SetChanspec(bool is_ap_iface, uint16_t* chanspec, zx_status_t expect_result);
uint16_t GetChanspec(bool is_ap_iface, zx_status_t expect_result);
// Run a dual mode (apsta) test, verifying AP stop behavior
void TestApStop(bool use_cdown);
protected:
SimInterface client_ifc_;
SimInterface softap_ifc_;
};
void DynamicIfTest::PhyQuery(wlanphy_impl_info_t* out_info) {
zx_status_t status;
status = device_->WlanphyImplQuery(out_info);
ASSERT_EQ(status, ZX_OK);
}
uint32_t DynamicIfTest::DeviceCount() { return (dev_mgr_->DeviceCount()); }
void DynamicIfTest::InjectStopAPError() {
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->err_inj_.AddErrInjIovar("bss", ZX_ERR_IO, softap_ifc_.iface_id_);
}
void DynamicIfTest::ChannelCheck() {
uint16_t softap_chanspec = GetChanspec(true, ZX_OK);
uint16_t client_chanspec = GetChanspec(false, ZX_OK);
EXPECT_EQ(softap_chanspec, client_chanspec);
brcmf_simdev* sim = device_->GetSim();
wlan_channel_t chan;
sim->sim_fw->convert_chanspec_to_channel(softap_chanspec, &chan);
EXPECT_EQ(softap_ifc_.stats_.csa_indications.size(), 1U);
EXPECT_EQ(chan.primary, softap_ifc_.stats_.csa_indications.front().new_channel);
}
void DynamicIfTest::TxAuthAndAssocReq() {
// Get the mac address of the SoftAP
common::MacAddr soft_ap_mac;
softap_ifc_.GetMacAddr(&soft_ap_mac);
wlan_ssid_t ssid = {.len = 6, .ssid = "Sim_AP"};
// Pass the auth stop for softAP iface before assoc.
simulation::SimAuthFrame auth_req_frame(kFakeMac, soft_ap_mac, 1, simulation::AUTH_TYPE_OPEN,
WLAN_STATUS_CODE_SUCCESS);
env_->Tx(auth_req_frame, kDefaultTxInfo, this);
simulation::SimAssocReqFrame assoc_req_frame(kFakeMac, soft_ap_mac, ssid);
env_->Tx(assoc_req_frame, kDefaultTxInfo, this);
}
void DynamicIfTest::VerifyAssocWithSoftAP() {
// Verify the event indications were received and
// the number of clients
ASSERT_EQ(softap_ifc_.stats_.assoc_indications.size(), 1U);
ASSERT_EQ(softap_ifc_.stats_.auth_indications.size(), 1U);
brcmf_simdev* sim = device_->GetSim();
uint16_t num_clients = sim->sim_fw->GetNumClients(softap_ifc_.iface_id_);
ASSERT_EQ(num_clients, 1U);
}
void DynamicIfTest::SetChanspec(bool is_ap_iface, uint16_t* chanspec, zx_status_t expect_result) {
brcmf_simdev* sim = device_->GetSim();
zx_status_t err =
sim->sim_fw->IovarsSet(is_ap_iface ? softap_ifc_.iface_id_ : client_ifc_.iface_id_,
"chanspec", chanspec, sizeof(uint16_t));
EXPECT_EQ(err, expect_result);
}
uint16_t DynamicIfTest::GetChanspec(bool is_ap_iface, zx_status_t expect_result) {
brcmf_simdev* sim = device_->GetSim();
uint16_t chanspec;
zx_status_t err =
sim->sim_fw->IovarsGet(is_ap_iface ? softap_ifc_.iface_id_ : client_ifc_.iface_id_,
"chanspec", &chanspec, sizeof(uint16_t));
EXPECT_EQ(err, expect_result);
return chanspec;
}
TEST_F(DynamicIfTest, CreateDestroy) {
Init();
ASSERT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_), ZX_OK);
DeleteInterface(client_ifc_);
EXPECT_EQ(DeviceCount(), 1U);
ASSERT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_, std::nullopt, kDefaultBssid),
ZX_OK);
// Verify whether the default bssid is correctly set to sim-fw when creating softAP iface.
common::MacAddr soft_ap_mac;
softap_ifc_.GetMacAddr(&soft_ap_mac);
EXPECT_EQ(soft_ap_mac, kDefaultBssid);
DeleteInterface(softap_ifc_);
EXPECT_EQ(DeviceCount(), 1U);
}
// This test case verifies that starting an AP iface using the same MAC address as the existing
// client iface will return an error.
TEST_F(DynamicIfTest, CreateAPwithSameMacAsClient) {
Init();
ASSERT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_), ZX_OK);
// Create AP iface with the same mac addr.
common::MacAddr client_mac;
client_ifc_.GetMacAddr(&client_mac);
EXPECT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_, std::nullopt, client_mac),
ZX_ERR_ALREADY_EXISTS);
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
DeleteInterface(client_ifc_);
EXPECT_EQ(DeviceCount(), static_cast<size_t>(1));
}
// This test verifies that if we want to create an client iface with the same MAC address as the
// pre-set one, no error will be returned.
TEST_F(DynamicIfTest, CreateClientwithPreAllocMac) {
Init();
common::MacAddr pre_set_mac;
brcmf_simdev* sim = device_->GetSim();
sim->sim_fw->IovarsGet(0, "cur_etheraddr", pre_set_mac.byte, ETH_ALEN);
EXPECT_EQ(StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_, std::nullopt, pre_set_mac),
ZX_OK);
EXPECT_EQ(DeviceCount(), static_cast<size_t>(2));
DeleteInterface(client_ifc_);
EXPECT_EQ(DeviceCount(), static_cast<size_t>(1));
}
TEST_F(DynamicIfTest, DualInterfaces) {
Init();
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_);
EXPECT_EQ(DeviceCount(), static_cast<size_t>(3));
DeleteInterface(client_ifc_);
DeleteInterface(softap_ifc_);
EXPECT_EQ(DeviceCount(), static_cast<size_t>(1));
}
TEST_F(DynamicIfTest, PhyQuery) {
Init();
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
// Test brcmfmac supports simutaneous client ap operation
wlanphy_impl_info_t info = {};
PhyQuery(&info);
EXPECT_NE(info.wlan_info.caps & WLAN_INFO_HARDWARE_CAPABILITY_SIMULTANEOUS_CLIENT_AP,
static_cast<size_t>(0));
}
// Start both client and SoftAP interfaces simultaneously and check if
// the client can associate to a FakeAP and a fake client can associate to the
// SoftAP.
TEST_F(DynamicIfTest, ConnectBothInterfaces) {
// Create our device instances
Init();
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_);
// Start our SoftAP
softap_ifc_.StartSoftAp();
// Start up our fake AP
simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel);
// Associate to FakeAp
client_ifc_.AssociateWith(ap, zx::msec(10));
// Associate to SoftAP
SCHEDULE_CALL(zx::msec(100), &DynamicIfTest::TxAuthAndAssocReq, this);
env_->Run(kTestDuration);
// Check if the client's assoc with FakeAP succeeded
EXPECT_EQ(client_ifc_.stats_.assoc_attempts, 1U);
EXPECT_EQ(client_ifc_.stats_.assoc_successes, 1U);
// Verify Assoc with SoftAP succeeded
VerifyAssocWithSoftAP();
// TODO(karthikrish) Will add disassoc once support in SIM FW is available
}
void DynamicIfTest::TestApStop(bool use_cdown) {
// Create our device instances
Init();
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_);
// Start our SoftAP
softap_ifc_.StartSoftAp();
// Start up our fake AP
simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel);
ap.EnableBeacon(zx::msec(100));
// Optionally force the use of a C_DOWN command, which has the side-effect of bringing down the
// client interface.
if (use_cdown) {
InjectStopAPError();
}
// Associate to FakeAp
client_ifc_.AssociateWith(ap, zx::msec(10));
// Associate to SoftAP
SCHEDULE_CALL(zx::msec(100), &DynamicIfTest::TxAuthAndAssocReq, this);
// Verify Assoc with SoftAP succeeded
SCHEDULE_CALL(zx::msec(150), &DynamicIfTest::VerifyAssocWithSoftAP, this);
SCHEDULE_CALL(zx::msec(160), &SimInterface::StopSoftAp, &softap_ifc_);
env_->Run(kTestDuration);
// Check if the client's assoc with FakeAP succeeded
EXPECT_EQ(client_ifc_.stats_.assoc_attempts, 1U);
EXPECT_EQ(client_ifc_.stats_.assoc_successes, 1U);
// Disassoc and other assoc scenarios are covered in assoc_test.cc
}
// Start both client and SoftAP interfaces simultaneously and check if stopping the AP's beacons
// does not affect the client.
TEST_F(DynamicIfTest, StopAPDoesntAffectClientIF) {
TestApStop(false);
// Verify that we didn't shut down our client interface
EXPECT_EQ(client_ifc_.stats_.deauth_indications.size(), 0U);
}
// Start both client and SoftAP interfaces simultaneously and check if stopping the AP with iovar
// bss fail, brings down the client as well because C_DOWN is issued
TEST_F(DynamicIfTest, UsingCdownDisconnectsClient) {
TestApStop(true);
// Verify that the client interface was also shut down
EXPECT_EQ(client_ifc_.stats_.deauth_indications.size(), 1U);
}
TEST_F(DynamicIfTest, SetClientChanspecAfterAPStarted) {
// Create our device instances
Init();
uint16_t chanspec;
// Create softAP iface and start
StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_);
softap_ifc_.StartSoftAp(SimInterface::kDefaultSoftApSsid, kDefaultChannel);
// The chanspec of softAP iface should be set to default one.
chanspec = GetChanspec(true, ZX_OK);
EXPECT_EQ(chanspec, kDefaultChanspec);
// After creating client iface and setting a different chanspec to it, chanspec of softAP will
// change as a result of this operation.
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
chanspec = kTestChanspec;
SetChanspec(false, &chanspec, ZX_OK);
// Confirm chanspec of AP is same as client
chanspec = GetChanspec(true, ZX_OK);
EXPECT_EQ(chanspec, kTestChanspec);
}
TEST_F(DynamicIfTest, SetAPChanspecAfterClientCreated) {
// Create our device instances
Init();
// Create client iface and set chanspec
uint16_t chanspec = kTestChanspec;
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
SetChanspec(false, &chanspec, ZX_OK);
// Create and start softAP iface to and set another chanspec
StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_);
softap_ifc_.StartSoftAp();
// When we call StartSoftAP, the kDefaultCh will be transformed into chanspec(in this case the
// value is 53397) and set to softAP iface, but since there is already a client iface activated,
// that input chanspec will be ignored and set to client's chanspec.
chanspec = GetChanspec(true, ZX_OK);
EXPECT_EQ(chanspec, kTestChanspec);
// Now if we set chanspec again to softAP when it already have a chanspec, this operation is
// silently rejected
chanspec = kTest1Chanspec;
SetChanspec(true, &chanspec, ZX_OK);
}
// Start SoftAP after client assoc. SoftAP's channel should get set to client's channel
TEST_F(DynamicIfTest, CheckSoftAPChannel) {
// Create our device instances
Init();
StartInterface(WLAN_INFO_MAC_ROLE_CLIENT, &client_ifc_);
StartInterface(WLAN_INFO_MAC_ROLE_AP, &softap_ifc_);
// Start up our fake AP
simulation::FakeAp ap(env_.get(), kDefaultBssid, kDefaultSsid, kDefaultChannel);
zx::duration delay = zx::msec(10);
// Associate to FakeAp
client_ifc_.AssociateWith(ap, delay);
// Start our SoftAP
delay += zx::msec(10);
SCHEDULE_CALL(delay, &SimInterface::StartSoftAp, &softap_ifc_, SimInterface::kDefaultSoftApSsid,
kDefaultChannel, 100, 100);
// Wait until SIM FW sends AP Start confirmation. This is set as a
// scheduled event to ensure test runs until AP Start confirmation is
// received.
delay += kStartAPConfDelay + zx::msec(10);
SCHEDULE_CALL(delay, &DynamicIfTest::ChannelCheck, this);
env_->Run(kTestDuration);
EXPECT_EQ(client_ifc_.stats_.assoc_successes, 1U);
}
} // namespace wlan::brcmfmac