blob: 363b2e8a075ac78a0d9d1a010316a98526a81736 [file] [log] [blame]
// Copyright 2022 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.
// To test PHY device callback functions.
#include <lib/async/cpp/task.h>
#include <lib/fdio/directory.h>
#include <lib/sync/cpp/completion.h>
#include <zircon/listnode.h>
#include <zircon/syscalls.h>
#include <iterator>
#include <gtest/gtest.h>
#include "fidl/fuchsia.wlan.phyimpl/cpp/wire_types.h"
#include "third_party/iwlwifi/platform/banjo/common.h"
extern "C" {
#include "third_party/iwlwifi/mvm/mvm.h"
}
#include "third_party/iwlwifi/platform/ieee80211.h"
#include "third_party/iwlwifi/platform/mvm-mlme.h"
#include "third_party/iwlwifi/platform/wlanphyimpl-device.h"
#include "third_party/iwlwifi/test/fake-ucode-test.h"
namespace wlan::testing {
namespace {
constexpr size_t kListenInterval = 100;
constexpr zx_handle_t kDummyMlmeChannel = 73939133; // An arbitrary value not ZX_HANDLE_INVALID
class WlanPhyImplDeviceTest : public FakeUcodeTest {
public:
WlanPhyImplDeviceTest()
: FakeUcodeTest({IWL_UCODE_TLV_CAPA_LAR_SUPPORT}, {IWL_UCODE_TLV_API_WIFI_MCC_UPDATE}),
mvmvif_sta_{
.mvm = iwl_trans_get_mvm(sim_trans_.iwl_trans()),
.mac_role = WLAN_MAC_ROLE_CLIENT,
.bss_conf =
{
.beacon_int = kListenInterval,
},
},
test_arena_(nullptr) {
sim_driver_ = sim_trans_.sim_driver();
auto endpoints = fdf::CreateEndpoints<fuchsia_wlan_phyimpl::WlanPhyImpl>();
EXPECT_FALSE(endpoints.is_error());
client_ = fdf::WireSyncClient<fuchsia_wlan_phyimpl::WlanPhyImpl>(std::move(endpoints->client));
// `DdkServiceConnect` starts a FIDL server that bounds to the dispatcher of
// the caller. The FIDL protocol that is being served uses driver transport
// and so it must be bound to an fdf dispatcher.
libsync::Completion connected;
async::PostTask(sim_trans_.async_driver_dispatcher(), [&]() {
sim_driver_->ServiceConnectHandler(sim_trans_.fdf_driver_dispatcher(),
std::move(endpoints->server));
connected.Signal();
});
connected.Wait();
// Create test arena.
constexpr uint32_t kTag = 'TEST';
test_arena_ = fdf::Arena(kTag);
}
~WlanPhyImplDeviceTest() = default;
zx_status_t CreateIface(fuchsia_wlan_common::WlanMacRole role, uint16_t* iface_id_out) {
auto ch = ::zx::channel(kDummyMlmeChannel);
static fidl::Arena arena;
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplCreateIfaceRequest::Builder(arena);
builder.role(role);
builder.mlme_channel(std::move(ch));
auto result = client_.buffer(test_arena_)->CreateIface(builder.Build());
EXPECT_EQ(result.status(), ZX_OK);
if (result->is_error()) {
// Returns an invalid iface id.
*iface_id_out = MAX_NUM_MVMVIF;
return result->error_value();
}
*iface_id_out = result->value()->iface_id();
return ZX_OK;
}
zx_status_t DestroyIface(uint16_t iface_id) {
static fidl::Arena arena;
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplDestroyIfaceRequest::Builder(arena);
builder.iface_id(iface_id);
auto result = client_.buffer(test_arena_)->DestroyIface(builder.Build());
EXPECT_TRUE(result.ok());
if (result->is_error()) {
return result->error_value();
}
return ZX_OK;
}
protected:
struct iwl_mvm_vif mvmvif_sta_; // The mvm_vif settings for station role.
wlan::iwlwifi::SimTransIwlwifiDriver* sim_driver_;
fdf::WireSyncClient<fuchsia_wlan_phyimpl::WlanPhyImpl> client_;
fdf::Arena test_arena_;
libsync::Completion completion_;
};
///////////////////////////////////// PHY //////////////////////////////////////////////
TEST_F(WlanPhyImplDeviceTest, GetSupportedMacRoles) {
auto result = client_.buffer(test_arena_)->GetSupportedMacRoles();
ASSERT_TRUE(result.ok());
ASSERT_FALSE(result->is_error());
ASSERT_TRUE(result->value()->has_supported_mac_roles());
EXPECT_EQ(result->value()->supported_mac_roles().count(), 1);
EXPECT_EQ(result->value()->supported_mac_roles().data()[0],
fuchsia_wlan_common::WlanMacRole::kClient);
}
TEST_F(WlanPhyImplDeviceTest, PartialCreateCleanup) {
wlan_phy_impl_create_iface_req_t req = {
.role = WLAN_MAC_ROLE_CLIENT,
.mlme_channel = kDummyMlmeChannel,
};
uint16_t iface_id;
struct iwl_trans* iwl_trans = sim_trans_.iwl_trans();
// Test input null pointers
ASSERT_EQ(ZX_OK, phy_create_iface(iwl_trans, &req, &iface_id));
// Ensure mvmvif got created and indexed.
struct iwl_mvm* mvm = iwl_trans_get_mvm(iwl_trans);
ASSERT_NE(nullptr, mvm->mvmvif[iface_id]);
// Ensure partial create failure removes it from the index.
phy_create_iface_undo(iwl_trans, iface_id);
ASSERT_EQ(nullptr, mvm->mvmvif[iface_id]);
}
TEST_F(WlanPhyImplDeviceTest, CreateIfaceNegativeTest) {
static fidl::Arena arena;
// Both role and channel not populated.
{
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplCreateIfaceRequest::Builder(arena);
auto result = client_.buffer(test_arena_)->CreateIface(builder.Build());
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
EXPECT_EQ(ZX_ERR_INVALID_ARGS, result->error_value());
}
// Role is set, but not channel.
{
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplCreateIfaceRequest::Builder(arena);
builder.role(fuchsia_wlan_common::wire::WlanMacRole::kClient);
auto result = client_.buffer(test_arena_)->CreateIface(builder.Build());
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_INVALID_ARGS, result->error_value());
}
// Channel is set, but not the role.
{
auto ch = ::zx::channel(kDummyMlmeChannel);
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplCreateIfaceRequest::Builder(arena);
builder.mlme_channel(std::move(ch));
auto result = client_.buffer(test_arena_)->CreateIface(builder.Build());
EXPECT_TRUE(result.ok());
EXPECT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_INVALID_ARGS, result->error_value());
}
}
TEST_F(WlanPhyImplDeviceTest, DestroyIfaceNegativeTest) {
static fidl::Arena arena;
// iface_id not populated.
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplDestroyIfaceRequest::Builder(arena);
auto result = client_.buffer(test_arena_)->DestroyIface(builder.Build());
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_INVALID_ARGS, result->error_value());
}
TEST_F(WlanPhyImplDeviceTest, CreateDestroySingleInterface) {
uint16_t iface_id;
// Test invalid inputs
EXPECT_EQ(ZX_ERR_INVALID_ARGS, DestroyIface(MAX_NUM_MVMVIF));
EXPECT_EQ(ZX_ERR_NOT_FOUND, DestroyIface(0)); // hasn't been added yet.
// To verify the internal state of MVM driver.
struct iwl_mvm* mvm = iwl_trans_get_mvm(sim_trans_.iwl_trans());
// Add interface
EXPECT_EQ(ZX_OK, CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(iface_id, 0); // the first interface should have id 0.
struct iwl_mvm_vif* mvmvif = mvm->mvmvif[0];
ASSERT_NE(mvmvif, nullptr);
ASSERT_EQ(mvmvif->mac_role,
static_cast<wlan_mac_role_t>(fuchsia_wlan_common::wire::WlanMacRole::kClient));
// Count includes phy device in addition to the newly created mac device.
ASSERT_EQ(sim_driver_->DeviceCount(), 1);
// Remove interface
EXPECT_EQ(ZX_OK, DestroyIface(0));
ASSERT_EQ(mvm->mvmvif[0], nullptr);
ASSERT_EQ(sim_driver_->DeviceCount(), 0);
}
TEST_F(WlanPhyImplDeviceTest, CreateDestroyMultipleInterfaces) {
struct iwl_trans* iwl_trans = sim_trans_.iwl_trans();
struct iwl_mvm* mvm = iwl_trans_get_mvm(iwl_trans); // To verify the internal state of MVM
uint16_t iface_id;
// Add 1st interface
EXPECT_EQ(ZX_OK, CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(iface_id, 0);
ASSERT_NE(mvm->mvmvif[0], nullptr);
ASSERT_EQ(mvm->mvmvif[0]->mac_role, WLAN_MAC_ROLE_CLIENT);
ASSERT_EQ(sim_driver_->DeviceCount(), 1);
// Add 2nd interface
EXPECT_EQ(ZX_OK, CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(iface_id, 1);
ASSERT_NE(mvm->mvmvif[1], nullptr);
ASSERT_EQ(mvm->mvmvif[1]->mac_role, WLAN_MAC_ROLE_CLIENT);
ASSERT_EQ(sim_driver_->DeviceCount(), 2);
// Add 3rd interface
EXPECT_EQ(ZX_OK, CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(iface_id, 2);
ASSERT_NE(mvm->mvmvif[2], nullptr);
ASSERT_EQ(mvm->mvmvif[2]->mac_role, WLAN_MAC_ROLE_CLIENT);
ASSERT_EQ(sim_driver_->DeviceCount(), 3);
// Remove the 2nd interface
EXPECT_EQ(ZX_OK, DestroyIface(1));
ASSERT_EQ(mvm->mvmvif[1], nullptr);
ASSERT_EQ(sim_driver_->DeviceCount(), 2);
// Add a new interface and it should be the 2nd one.
EXPECT_EQ(ZX_OK, CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(iface_id, 1);
ASSERT_NE(mvm->mvmvif[1], nullptr);
ASSERT_EQ(mvm->mvmvif[1]->mac_role, WLAN_MAC_ROLE_CLIENT);
ASSERT_EQ(sim_driver_->DeviceCount(), 3);
// Add 4th interface
EXPECT_EQ(ZX_OK, CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(iface_id, 3);
ASSERT_NE(mvm->mvmvif[3], nullptr);
ASSERT_EQ(mvm->mvmvif[3]->mac_role, WLAN_MAC_ROLE_CLIENT);
ASSERT_EQ(sim_driver_->DeviceCount(), 4);
// Add 5th interface and it should fail
EXPECT_EQ(ZX_ERR_NO_RESOURCES,
CreateIface(fuchsia_wlan_common::wire::WlanMacRole::kClient, &iface_id));
ASSERT_EQ(sim_driver_->DeviceCount(), 4);
// Remove the 2nd interface
EXPECT_EQ(ZX_OK, DestroyIface(1));
ASSERT_EQ(mvm->mvmvif[1], nullptr);
ASSERT_EQ(sim_driver_->DeviceCount(), 3);
// Remove the 3rd interface
EXPECT_EQ(ZX_OK, DestroyIface(2));
ASSERT_EQ(mvm->mvmvif[2], nullptr);
ASSERT_EQ(sim_driver_->DeviceCount(), 2);
// Remove the 4th interface
EXPECT_EQ(ZX_OK, DestroyIface(3));
ASSERT_EQ(mvm->mvmvif[3], nullptr);
ASSERT_EQ(sim_driver_->DeviceCount(), 1);
// Remove the 1st interface
EXPECT_EQ(ZX_OK, DestroyIface(0));
ASSERT_EQ(mvm->mvmvif[0], nullptr);
ASSERT_EQ(sim_driver_->DeviceCount(), 0);
// Remove the 1st interface again and it should fail.
EXPECT_EQ(ZX_ERR_NOT_FOUND, DestroyIface(0));
ASSERT_EQ(sim_driver_->DeviceCount(), 0);
}
TEST_F(WlanPhyImplDeviceTest, GetCountry) {
auto result = client_.buffer(test_arena_)->GetCountry();
ASSERT_TRUE(result.ok());
ASSERT_FALSE(result->is_error());
auto& country = result.value();
EXPECT_EQ('Z', country->alpha2().data()[0]);
EXPECT_EQ('Z', country->alpha2().data()[1]);
}
TEST_F(WlanPhyImplDeviceTest, SetCountry) {
auto country = fuchsia_wlan_phyimpl::wire::WlanPhyCountry::WithAlpha2({'U', 'S'});
auto result = client_.buffer(test_arena_)->SetCountry(country);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, result->error_value());
}
TEST_F(WlanPhyImplDeviceTest, ClearCountry) {
auto result = client_.buffer(test_arena_)->ClearCountry();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, result->error_value());
}
TEST_F(WlanPhyImplDeviceTest, SetPowerSaveMode) {
static fidl::Arena arena;
auto builder = fuchsia_wlan_phyimpl::wire::WlanPhyImplSetPowerSaveModeRequest::Builder(arena);
auto result = client_.buffer(test_arena_)->SetPowerSaveMode(builder.Build());
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, result->error_value());
}
TEST_F(WlanPhyImplDeviceTest, GetPowerSaveMode) {
auto result = client_.buffer(test_arena_)->GetPowerSaveMode();
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
ASSERT_EQ(ZX_ERR_NOT_SUPPORTED, result->error_value());
}
} // namespace
} // namespace wlan::testing