blob: 78ec0308302f61900183479d753365b4824efa52 [file] [log] [blame]
// Copyright 2017 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 <ddktl/device.h>
#include <ddktl/protocol/wlan.h>
#include <fbl/alloc_checker.h>
#include <fbl/unique_ptr.h>
#include <unittest/unittest.h>
namespace {
// These tests are testing interfaces that get included via multiple inheritance, and thus we must
// make sure we get all the casts correct. We record the value of the "this" pointer in the
// constructor, and then verify in each call the "this" pointer was the same as the original. (The
// typical way for this to go wrong is to take a WlanmacIfc<D>* instead of a D* in a function
// signature.)
#define get_this() reinterpret_cast<uintptr_t>(this)
class TestWlanmacIfc : public ddk::Device<TestWlanmacIfc>,
public ddk::WlanmacIfc<TestWlanmacIfc> {
public:
TestWlanmacIfc() : ddk::Device<TestWlanmacIfc>(nullptr) {
this_ = get_this();
}
void DdkRelease() {}
void WlanmacStatus(uint32_t status) {
status_this_ = get_this();
status_called_ = true;
}
void WlanmacRecv(uint32_t flags, const void* data, size_t length, wlan_rx_info_t* info) {
recv_this_ = get_this();
recv_called_ = true;
}
bool VerifyCalls() const {
BEGIN_HELPER;
EXPECT_EQ(this_, status_this_, "");
EXPECT_EQ(this_, recv_this_, "");
EXPECT_TRUE(status_called_, "");
EXPECT_TRUE(recv_called_, "");
END_HELPER;
}
zx_status_t StartProtocol(ddk::WlanmacProtocolProxy* proxy) {
return proxy->Start(this);
}
private:
uintptr_t this_ = 0u;
uintptr_t status_this_ = 0u;
uintptr_t recv_this_ = 0u;
bool status_called_ = false;
bool recv_called_ = false;
};
class TestWlanmacProtocol : public ddk::Device<TestWlanmacProtocol, ddk::GetProtocolable>,
public ddk::WlanmacProtocol<TestWlanmacProtocol> {
public:
TestWlanmacProtocol()
: ddk::Device<TestWlanmacProtocol, ddk::GetProtocolable>(nullptr) {
this_ = get_this();
}
zx_status_t DdkGetProtocol(uint32_t proto_id, void* out) {
if (proto_id != ZX_PROTOCOL_WLANMAC) return ZX_ERR_INVALID_ARGS;
ddk::AnyProtocol* proto = static_cast<ddk::AnyProtocol*>(out);
proto->ops = ddk_proto_ops_;
proto->ctx = this;
return ZX_OK;
}
void DdkRelease() {}
zx_status_t WlanmacQuery(uint32_t options, ethmac_info_t* info) {
query_this_ = get_this();
query_called_ = true;
return ZX_OK;
}
void WlanmacStop() {
stop_this_ = get_this();
stop_called_ = true;
}
zx_status_t WlanmacStart(fbl::unique_ptr<ddk::WlanmacIfcProxy> proxy) {
start_this_ = get_this();
proxy_.swap(proxy);
start_called_ = true;
return ZX_OK;
}
void WlanmacTx(uint32_t options, const void* data, size_t length) {
tx_this_ = get_this();
tx_called_ = true;
}
zx_status_t WlanmacSetChannel(uint32_t options, wlan_channel_t* chan) {
set_channel_this_ = get_this();
set_channel_called_ = true;
return ZX_OK;
}
bool VerifyCalls() const {
BEGIN_HELPER;
EXPECT_EQ(this_, query_this_, "");
EXPECT_EQ(this_, start_this_, "");
EXPECT_EQ(this_, stop_this_, "");
EXPECT_EQ(this_, tx_this_, "");
EXPECT_EQ(this_, set_channel_this_, "");
EXPECT_TRUE(query_called_, "");
EXPECT_TRUE(start_called_, "");
EXPECT_TRUE(stop_called_, "");
EXPECT_TRUE(tx_called_, "");
EXPECT_TRUE(set_channel_called_, "");
END_HELPER;
}
bool TestIfc() {
if (!proxy_) return false;
// Use the provided proxy to test the ifc proxy.
proxy_->Status(0);
proxy_->Recv(0, nullptr, 0, nullptr);
return true;
}
private:
uintptr_t this_ = 0u;
uintptr_t query_this_ = 0u;
uintptr_t stop_this_ = 0u;
uintptr_t start_this_ = 0u;
uintptr_t tx_this_ = 0u;
uintptr_t set_channel_this_ = 0u;
bool query_called_ = false;
bool stop_called_ = false;
bool start_called_ = false;
bool tx_called_ = false;
bool set_channel_called_ = false;
fbl::unique_ptr<ddk::WlanmacIfcProxy> proxy_;
};
static bool test_wlanmac_ifc() {
BEGIN_TEST;
TestWlanmacIfc dev;
auto ifc = dev.wlanmac_ifc();
ifc->status(&dev, 0);
ifc->recv(&dev, 0, nullptr, 0, nullptr);
EXPECT_TRUE(dev.VerifyCalls(), "");
END_TEST;
}
static bool test_wlanmac_ifc_proxy() {
BEGIN_TEST;
TestWlanmacIfc dev;
ddk::WlanmacIfcProxy proxy(dev.wlanmac_ifc(), &dev);
proxy.Status(0);
proxy.Recv(0, nullptr, 0, nullptr);
EXPECT_TRUE(dev.VerifyCalls(), "");
END_TEST;
}
static bool test_wlanmac_protocol() {
BEGIN_TEST;
TestWlanmacProtocol dev;
// Normally we would use device_op_get_protocol, but we haven't added the device to devmgr so
// its ops table is currently invalid.
wlanmac_protocol_t proto;
auto status = dev.DdkGetProtocol(0, reinterpret_cast<void*>(&proto));
EXPECT_EQ(ZX_ERR_INVALID_ARGS, status, "");
status = dev.DdkGetProtocol(ZX_PROTOCOL_WLANMAC, reinterpret_cast<void*>(&proto));
EXPECT_EQ(ZX_OK, status, "");
EXPECT_EQ(ZX_OK, proto.ops->query(proto.ctx, 0, nullptr), "");
proto.ops->stop(proto.ctx);
EXPECT_EQ(ZX_OK, proto.ops->start(proto.ctx, nullptr, nullptr), "");
proto.ops->tx(proto.ctx, 0, nullptr, 0);
EXPECT_EQ(ZX_OK, proto.ops->set_channel(proto.ctx, 0, nullptr), "");
EXPECT_TRUE(dev.VerifyCalls(), "");
END_TEST;
}
static bool test_wlanmac_protocol_proxy() {
BEGIN_TEST;
// The WlanmacProtocol device to wrap. This would live in the parent device
// our driver was binding to.
TestWlanmacProtocol protocol_dev;
wlanmac_protocol_t proto;
auto status = protocol_dev.DdkGetProtocol(ZX_PROTOCOL_WLANMAC, reinterpret_cast<void*>(&proto));
EXPECT_EQ(ZX_OK, status, "");
// The proxy device to wrap the ops + device that represent the parent
// device.
ddk::WlanmacProtocolProxy proxy(&proto);
// The WlanmacIfc to hand to the parent device.
TestWlanmacIfc ifc_dev;
EXPECT_EQ(ZX_OK, proxy.Query(0, nullptr), "");
proxy.Stop();
EXPECT_EQ(ZX_OK, proxy.Start(&ifc_dev), "");
proxy.Tx(0, nullptr, 0);
proxy.SetChannel(0, nullptr);
EXPECT_TRUE(protocol_dev.VerifyCalls(), "");
END_TEST;
}
static bool test_wlanmac_protocol_ifc_proxy() {
BEGIN_TEST;
// We create a protocol device that we will start from an ifc device. The protocol device will
// then use the pointer passed to it to call methods on the ifc device. This ensures the void*
// casting is correct.
TestWlanmacProtocol protocol_dev;
wlanmac_protocol_t proto;
auto status = protocol_dev.DdkGetProtocol(ZX_PROTOCOL_WLANMAC, reinterpret_cast<void*>(&proto));
EXPECT_EQ(ZX_OK, status, "");
ddk::WlanmacProtocolProxy proxy(&proto);
TestWlanmacIfc ifc_dev;
EXPECT_EQ(ZX_OK, ifc_dev.StartProtocol(&proxy), "");
// Execute the WlanmacIfc methods
ASSERT_TRUE(protocol_dev.TestIfc(), "");
// Verify that they were called
EXPECT_TRUE(ifc_dev.VerifyCalls(), "");
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(ddktl_wlan_device)
RUN_NAMED_TEST("ddk::WlanmacIfc", test_wlanmac_ifc);
RUN_NAMED_TEST("ddk::WlanmacIfcProxy", test_wlanmac_ifc_proxy);
RUN_NAMED_TEST("ddk::WlanmacProtocol", test_wlanmac_protocol);
RUN_NAMED_TEST("ddk::WlanmacProtocolProxy", test_wlanmac_protocol_proxy);
RUN_NAMED_TEST("WlanmacProtocol using WlanmacIfcProxy", test_wlanmac_protocol_ifc_proxy);
END_TEST_CASE(ddktl_wlan_device)
test_case_element* test_case_ddktl_wlan_device = TEST_CASE_ELEMENT(ddktl_wlan_device);