blob: bbdd835801939001176a86b71cb8dafb94dc8f18 [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.
// This is to test the fw/notif-wait.c file.
//
// In the design concept of notif-wait.c, there are 2 parties invoked: caller (usually the firmware
// IRQ) and the user (the driver code waiting for the notification from firmware). In this file,
// we will act as those 2 parties in a test case.
#include <iterator>
#include <gtest/gtest.h>
extern "C" {
#include "third_party/iwlwifi/fw/notif-wait.h"
}
namespace wlan {
namespace testing {
namespace {
enum FakeCommandId : uint16_t {
FAKE_CMD_1 = 1,
FAKE_CMD_2,
};
#define FAKE_PKT_1 \
{ \
.hdr = { \
.cmd = FAKE_CMD_1, \
.group_id = 0, \
}, \
}
#define FAKE_PKT_2 \
{ \
.hdr = { \
.cmd = FAKE_CMD_2, \
.group_id = 0, \
}, \
}
class NotifWaitTest : public ::testing::Test {
public:
NotifWaitTest() {}
~NotifWaitTest() {}
};
// Helper function to create a wait_data and a wait_entry to receive the fake cmd.
static void helper_create_fake_cmd(struct iwl_notif_wait_data* wait_data,
struct iwl_notification_wait* wait_entry, uint16_t fake_cmd) {
uint16_t cmds[] = {fake_cmd};
iwl_init_notification_wait(wait_data, wait_entry, cmds, std::size(cmds), nullptr, nullptr);
}
// Helper function for test case init. This would create a wait_entry for fake cmd 1.
static void helper_test_case_init(struct iwl_notif_wait_data* wait_data,
struct iwl_notification_wait* wait_entry) {
iwl_notification_wait_init(wait_data);
helper_create_fake_cmd(wait_data, wait_entry, FAKE_CMD_1);
}
// The normal case. Expect we can get the notification.
TEST_F(NotifWaitTest, NormalCase) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// Here comes a packet. This will mark the notification has been triggered.
struct iwl_rx_packet pkt = FAKE_PKT_1;
iwl_notification_wait_notify(&wait_data, &pkt);
// Wait for the entry and expect success,
EXPECT_EQ(ZX_OK, iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
}
// Without a trigger, the second wait should time out.
TEST_F(NotifWaitTest, SecondWaitWithoutTriggerShouldTimeOut) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// The first wait. Should be successful.
struct iwl_rx_packet pkt = FAKE_PKT_1;
iwl_notification_wait_notify(&wait_data, &pkt);
EXPECT_EQ(ZX_OK, iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
// The second wait should time out because it is not triggered.
helper_create_fake_cmd(&wait_data, &wait_entry, FAKE_CMD_1);
EXPECT_EQ(ZX_ERR_TIMED_OUT,
iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
}
// Test 2 rounds of trigger/wait (same pkt). Expect both should be successful.
TEST_F(NotifWaitTest, CanBeTriggeredAgain) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// The first wait. Should be successful.
struct iwl_rx_packet pkt = FAKE_PKT_1;
iwl_notification_wait_notify(&wait_data, &pkt);
EXPECT_EQ(ZX_OK, iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
// The second wait. Should be successful as well.
helper_create_fake_cmd(&wait_data, &wait_entry, FAKE_CMD_1);
iwl_notification_wait_notify(&wait_data, &pkt);
EXPECT_EQ(ZX_OK, iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
}
// We expect one command, but only other command is notified.
TEST_F(NotifWaitTest, TestNotInterestedCommand) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// Here comes a packet. But not what we are interested. Expect it will be ignored.
struct iwl_rx_packet pkt = FAKE_PKT_2;
iwl_notification_wait_notify(&wait_data, &pkt);
// Wait for the notification, but expect timeout.
EXPECT_EQ(ZX_ERR_TIMED_OUT,
iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
}
// Expect the waiting is aborted.
TEST_F(NotifWaitTest, TestAbortion) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// Abort all.
iwl_abort_notification_waits(&wait_data);
// Expect aborted.
EXPECT_EQ(ZX_ERR_CANCELED, iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
}
// Try to trigger it first, then abort it.
TEST_F(NotifWaitTest, TestTriggeredThenAborted) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// Trigger
struct iwl_rx_packet pkt = FAKE_PKT_1;
iwl_notification_wait_notify(&wait_data, &pkt);
// Abort
iwl_abort_notification_waits(&wait_data);
// Expect aborted.
EXPECT_EQ(ZX_ERR_CANCELED, iwl_wait_notification(&wait_data, &wait_entry, ZX_TIME_INFINITE_PAST));
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Below tests only focus on iwl_notification_wait().
TEST_F(NotifWaitTest, TestEmptyWaitList) {
// A global wait data.
struct iwl_notif_wait_data wait_data;
iwl_notification_wait_init(&wait_data);
// Nothing has been added into the wait list.
struct iwl_rx_packet pkt = FAKE_PKT_1;
EXPECT_FALSE(iwl_notification_wait(&wait_data, &pkt));
}
TEST_F(NotifWaitTest, TestAbortedThenTriggered) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// Abort
iwl_abort_notification_waits(&wait_data);
// Trigger it and expect we cannot find it.
struct iwl_rx_packet pkt = FAKE_PKT_1;
EXPECT_FALSE(iwl_notification_wait(&wait_data, &pkt));
}
TEST_F(NotifWaitTest, TestRemove) {
struct iwl_notif_wait_data wait_data; // A global wait data
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
helper_test_case_init(&wait_data, &wait_entry);
// Remove it
iwl_remove_notification(&wait_data, &wait_entry);
// Trigger it and expect we cannot find it.
struct iwl_rx_packet pkt = FAKE_PKT_1;
EXPECT_FALSE(iwl_notification_wait(&wait_data, &pkt));
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Test 'fn' -- the callback function.
// A callback function that returns:
//
// 1. true when 'data' is not nullptr. Will also mark 'data' to true.
// 2. otherwise, false.
//
static bool fn(struct iwl_notif_wait_data* notif_wait, struct iwl_rx_packet* pkt, void* data) {
if (data) {
*reinterpret_cast<bool*>(data) = true;
return true;
} else {
return false;
}
}
// Helper function to create a wait_data, a wait_entry, and setup a callback function pointer.
static void helper_create_fn(struct iwl_notif_wait_data* wait_data,
struct iwl_notification_wait* wait_entry,
bool (*fn)(struct iwl_notif_wait_data* notif_data,
struct iwl_rx_packet* pkt, void* data),
void* fn_data) {
iwl_notification_wait_init(wait_data);
uint16_t cmds[] = {FAKE_CMD_1};
iwl_init_notification_wait(wait_data, wait_entry, cmds, std::size(cmds), fn, fn_data);
}
TEST_F(NotifWaitTest, FnReturnsTrue) {
struct iwl_notif_wait_data wait_data; // A global wait data.
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
bool fn_data = false;
helper_create_fn(&wait_data, &wait_entry, fn, &fn_data);
// Trigger it. Expect the trigger is valid and 'fn_data' has been marked.
struct iwl_rx_packet pkt = FAKE_PKT_1;
EXPECT_TRUE(iwl_notification_wait(&wait_data, &pkt));
EXPECT_TRUE(fn_data);
}
TEST_F(NotifWaitTest, FnReturnsFalse) {
struct iwl_notif_wait_data wait_data; // A global wait data.
struct iwl_notification_wait wait_entry; // A local wait entry used to wait for a specific event
bool fn_data = false;
helper_create_fn(&wait_data, &wait_entry, fn, nullptr);
// Trigger it. Expect the trigger is invalid and 'fn_data' is unchanged.
struct iwl_rx_packet pkt = FAKE_PKT_1;
EXPECT_FALSE(iwl_notification_wait(&wait_data, &pkt));
EXPECT_FALSE(fn_data);
}
} // namespace
} // namespace testing
} // namespace wlan