1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <ddktl/device.h> 6#include <ddktl/protocol/ethernet.h> 7#include <fbl/alloc_checker.h> 8#include <fbl/unique_ptr.h> 9#include <unittest/unittest.h> 10 11namespace { 12 13// These tests are testing interfaces that get included via multiple inheritance, and thus we must 14// make sure we get all the casts correct. We record the value of the "this" pointer in the 15// constructor, and then verify in each call the "this" pointer was the same as the original. (The 16// typical way for this to go wrong is to take a EthmacIfc<D>* instead of a D* in a function 17// signature.) 18#define get_this() reinterpret_cast<uintptr_t>(this) 19 20class TestEthmacIfc : public ddk::Device<TestEthmacIfc>, 21 public ddk::EthmacIfc<TestEthmacIfc> { 22 public: 23 TestEthmacIfc() : ddk::Device<TestEthmacIfc>(nullptr) { 24 this_ = get_this(); 25 } 26 27 void DdkRelease() {} 28 29 void EthmacStatus(uint32_t status) { 30 status_this_ = get_this(); 31 status_called_ = true; 32 } 33 34 void EthmacRecv(void* data, size_t length, uint32_t flags) { 35 recv_this_ = get_this(); 36 recv_called_ = true; 37 } 38 39 void EthmacCompleteTx(ethmac_netbuf_t* netbuf, zx_status_t status) { 40 complete_tx_this_ = get_this(); 41 complete_tx_called_ = true; 42 } 43 44 bool VerifyCalls() const { 45 BEGIN_HELPER; 46 EXPECT_EQ(this_, status_this_, ""); 47 EXPECT_EQ(this_, recv_this_, ""); 48 EXPECT_EQ(this_, complete_tx_this_, ""); 49 EXPECT_TRUE(status_called_, ""); 50 EXPECT_TRUE(recv_called_, ""); 51 EXPECT_TRUE(complete_tx_called_, ""); 52 END_HELPER; 53 } 54 55 zx_status_t StartProtocol(ddk::EthmacProtocolProxy* proxy) { 56 return proxy->Start(this); 57 } 58 59 private: 60 uintptr_t this_ = 0u; 61 uintptr_t status_this_ = 0u; 62 uintptr_t recv_this_ = 0u; 63 uintptr_t complete_tx_this_ = 0u; 64 bool status_called_ = false; 65 bool recv_called_ = false; 66 bool complete_tx_called_ = false; 67}; 68 69class TestEthmacProtocol : public ddk::Device<TestEthmacProtocol, ddk::GetProtocolable>, 70 public ddk::EthmacProtocol<TestEthmacProtocol> { 71 public: 72 TestEthmacProtocol() 73 : ddk::Device<TestEthmacProtocol, ddk::GetProtocolable>(nullptr) { 74 this_ = get_this(); 75 } 76 77 zx_status_t DdkGetProtocol(uint32_t proto_id, void* out) { 78 if (proto_id != ZX_PROTOCOL_ETHERNET_IMPL) return ZX_ERR_INVALID_ARGS; 79 ddk::AnyProtocol* proto = static_cast<ddk::AnyProtocol*>(out); 80 proto->ops = ddk_proto_ops_; 81 proto->ctx = this; 82 return ZX_OK; 83 } 84 85 void DdkRelease() {} 86 87 zx_status_t EthmacQuery(uint32_t options, ethmac_info_t* info) { 88 query_this_ = get_this(); 89 query_called_ = true; 90 return ZX_OK; 91 } 92 93 void EthmacStop() { 94 stop_this_ = get_this(); 95 stop_called_ = true; 96 } 97 98 zx_status_t EthmacStart(fbl::unique_ptr<ddk::EthmacIfcProxy> proxy) { 99 start_this_ = get_this(); 100 proxy_.swap(proxy); 101 start_called_ = true; 102 return ZX_OK; 103 } 104 105 zx_status_t EthmacQueueTx(uint32_t options, ethmac_netbuf_t* netbuf) { 106 queue_tx_this_ = get_this(); 107 queue_tx_called_ = true; 108 return ZX_OK; 109 } 110 111 zx_status_t EthmacSetParam(uint32_t param, int32_t value, void* data) { 112 set_param_this_ = get_this(); 113 set_param_called_ = true; 114 return ZX_OK; 115 } 116 zx_handle_t EthmacGetBti() { return ZX_HANDLE_INVALID;} 117 118 119 bool VerifyCalls() const { 120 BEGIN_HELPER; 121 EXPECT_EQ(this_, query_this_, ""); 122 EXPECT_EQ(this_, start_this_, ""); 123 EXPECT_EQ(this_, stop_this_, ""); 124 EXPECT_EQ(this_, queue_tx_this_, ""); 125 EXPECT_EQ(this_, set_param_this_, ""); 126 EXPECT_TRUE(query_called_, ""); 127 EXPECT_TRUE(start_called_, ""); 128 EXPECT_TRUE(stop_called_, ""); 129 EXPECT_TRUE(queue_tx_called_, ""); 130 EXPECT_TRUE(set_param_called_, ""); 131 END_HELPER; 132 } 133 134 bool TestIfc() { 135 if (!proxy_) return false; 136 // Use the provided proxy to test the ifc proxy. 137 proxy_->Status(0); 138 proxy_->Recv(nullptr, 0, 0); 139 proxy_->CompleteTx(nullptr, ZX_OK); 140 return true; 141 } 142 143 private: 144 uintptr_t this_ = 0u; 145 uintptr_t query_this_ = 0u; 146 uintptr_t stop_this_ = 0u; 147 uintptr_t start_this_ = 0u; 148 uintptr_t queue_tx_this_ = 0u; 149 uintptr_t set_param_this_ = 0u; 150 bool query_called_ = false; 151 bool stop_called_ = false; 152 bool start_called_ = false; 153 bool queue_tx_called_ = false; 154 bool set_param_called_ = false; 155 156 fbl::unique_ptr<ddk::EthmacIfcProxy> proxy_; 157}; 158 159static bool test_ethmac_ifc() { 160 BEGIN_TEST; 161 162 TestEthmacIfc dev; 163 164 auto ifc = dev.ethmac_ifc(); 165 ifc->status(&dev, 0); 166 ifc->recv(&dev, nullptr, 0, 0); 167 ifc->complete_tx(&dev, nullptr, ZX_OK); 168 169 EXPECT_TRUE(dev.VerifyCalls(), ""); 170 171 END_TEST; 172} 173 174static bool test_ethmac_ifc_proxy() { 175 BEGIN_TEST; 176 177 TestEthmacIfc dev; 178 ddk::EthmacIfcProxy proxy(dev.ethmac_ifc(), &dev); 179 180 proxy.Status(0); 181 proxy.Recv(nullptr, 0, 0); 182 proxy.CompleteTx(nullptr, ZX_OK); 183 184 EXPECT_TRUE(dev.VerifyCalls(), ""); 185 186 END_TEST; 187} 188 189static bool test_ethmac_protocol() { 190 BEGIN_TEST; 191 192 TestEthmacProtocol dev; 193 194 // Normally we would use device_op_get_protocol, but we haven't added the device to devmgr so 195 // its ops table is currently invalid. 196 ethmac_protocol_t proto; 197 auto status = dev.DdkGetProtocol(0, reinterpret_cast<void*>(&proto)); 198 EXPECT_EQ(ZX_ERR_INVALID_ARGS, status, ""); 199 200 status = dev.DdkGetProtocol(ZX_PROTOCOL_ETHERNET_IMPL, reinterpret_cast<void*>(&proto)); 201 EXPECT_EQ(ZX_OK, status, ""); 202 203 EXPECT_EQ(ZX_OK, proto.ops->query(proto.ctx, 0, nullptr), ""); 204 proto.ops->stop(proto.ctx); 205 EXPECT_EQ(ZX_OK, proto.ops->start(proto.ctx, nullptr, nullptr), ""); 206 ethmac_netbuf_t netbuf = {}; 207 EXPECT_EQ(ZX_OK, proto.ops->queue_tx(proto.ctx, 0, &netbuf), ""); 208 EXPECT_EQ(ZX_OK, proto.ops->set_param(proto.ctx, 0, 0, nullptr), ""); 209 210 EXPECT_TRUE(dev.VerifyCalls(), ""); 211 212 END_TEST; 213} 214 215static bool test_ethmac_protocol_proxy() { 216 BEGIN_TEST; 217 218 // The EthmacProtocol device to wrap. This would live in the parent device 219 // our driver was binding to. 220 TestEthmacProtocol protocol_dev; 221 222 ethmac_protocol_t proto; 223 auto status = protocol_dev.DdkGetProtocol(ZX_PROTOCOL_ETHERNET_IMPL, reinterpret_cast<void*>(&proto)); 224 EXPECT_EQ(ZX_OK, status, ""); 225 // The proxy device to wrap the ops + device that represent the parent 226 // device. 227 ddk::EthmacProtocolProxy proxy(&proto); 228 // The EthmacIfc to hand to the parent device. 229 TestEthmacIfc ifc_dev; 230 231 EXPECT_EQ(ZX_OK, proxy.Query(0, nullptr), ""); 232 proxy.Stop(); 233 EXPECT_EQ(ZX_OK, proxy.Start(&ifc_dev), ""); 234 ethmac_netbuf_t netbuf = {}; 235 EXPECT_EQ(ZX_OK, proxy.QueueTx(0, &netbuf), ""); 236 EXPECT_EQ(ZX_OK, proxy.SetParam(0, 0, nullptr)); 237 238 EXPECT_TRUE(protocol_dev.VerifyCalls(), ""); 239 240 END_TEST; 241} 242 243static bool test_ethmac_protocol_ifc_proxy() { 244 BEGIN_TEST; 245 246 // We create a protocol device that we will start from an ifc device. The protocol device will 247 // then use the pointer passed to it to call methods on the ifc device. This ensures the void* 248 // casting is correct. 249 TestEthmacProtocol protocol_dev; 250 251 ethmac_protocol_t proto; 252 auto status = protocol_dev.DdkGetProtocol(ZX_PROTOCOL_ETHERNET_IMPL, reinterpret_cast<void*>(&proto)); 253 EXPECT_EQ(ZX_OK, status, ""); 254 255 ddk::EthmacProtocolProxy proxy(&proto); 256 TestEthmacIfc ifc_dev; 257 EXPECT_EQ(ZX_OK, ifc_dev.StartProtocol(&proxy), ""); 258 259 // Execute the EthmacIfc methods 260 ASSERT_TRUE(protocol_dev.TestIfc(), ""); 261 // Verify that they were called 262 EXPECT_TRUE(ifc_dev.VerifyCalls(), ""); 263 264 END_TEST; 265} 266 267 268} // namespace 269 270BEGIN_TEST_CASE(ddktl_ethernet_device) 271RUN_NAMED_TEST("ddk::EthmacIfc", test_ethmac_ifc); 272RUN_NAMED_TEST("ddk::EthmacIfcProxy", test_ethmac_ifc_proxy); 273RUN_NAMED_TEST("ddk::EthmacProtocol", test_ethmac_protocol); 274RUN_NAMED_TEST("ddk::EthmacProtocolProxy", test_ethmac_protocol_proxy); 275RUN_NAMED_TEST("EthmacProtocol using EthmacIfcProxy", test_ethmac_protocol_ifc_proxy); 276END_TEST_CASE(ddktl_ethernet_device) 277 278test_case_element* test_case_ddktl_ethernet_device = TEST_CASE_ELEMENT(ddktl_ethernet_device); 279