| // 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); |