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#pragma once 6 7#include <ddk/driver.h> 8#include <ddk/protocol/ethernet.h> 9#include <ddktl/protocol/ethernet-internal.h> 10#include <fbl/type_support.h> 11#include <fbl/unique_ptr.h> 12#include <zircon/assert.h> 13 14// DDK ethernet protocol support 15// 16// :: Proxies :: 17// 18// ddk::EthmacIfcProxy and ddk::EthmacProtocolProxy are simple wrappers around ethmac_ifc_t and 19// ethmac_protocol_t, respectively. They do not own the pointers passed to them. 20// 21// :: Mixins :: 22// 23// ddk::EthmacIfc and ddk::EthmacProtocol are mixin classes that simplify writing DDK drivers that 24// interact with the ethernet protocol. They take care of implementing the function pointer tables 25// and calling into the object that wraps them. 26// 27// :: Examples :: 28// 29// // A driver that communicates with a ZX_PROTOCOL_ETHERNET_IMPL device as a ethmac_ifc_t 30// class EthDevice; 31// using EthDeviceType = ddk::Device<EthDevice, /* ddk mixins */>; 32// 33// class EthDevice : public EthDeviceType, 34// public ddk::EthmacIfc<EthDevice> { 35// public: 36// EthDevice(zx_device_t* parent) 37// : EthDeviceType("my-eth-device"), 38// parent_(parent) {} 39// 40// zx_status_t Bind() { 41// ethmac_protocol_t* ops; 42// auto status = get_device_protocol(parent_, ZX_PROTOCOL_ETHERNET_IMPL, 43// reinterpret_cast<void**>(&ops)); 44// if (status != ZX_OK) { 45// return status; 46// } 47// proxy_.reset(new ddk::EthmacProtocolProxy(ops, parent_)); 48// status = proxy_->Start(this); 49// if (status != ZX_OK) { 50// return status; 51// } 52// return device_add(ddk_device(), parent_); 53// } 54// 55// void DdkRelease() { 56// // Clean up 57// } 58// 59// void EthmacStatus(uint32_t status) { 60// // Report status 61// } 62// 63// void EthmacRecv(void* buf, size_t length, uint32_t flags) { 64// // Receive data buffer from ethmac device 65// } 66// 67// private: 68// zx_device_t* parent_; 69// fbl::unique_ptr<ddk::EthmacProtocolProxy> proxy_; 70// }; 71// 72// 73// // A driver that implements a ZX_PROTOCOL_ETHERNET_IMPL device 74// class EthmacDevice; 75// using EthmacDeviceType = ddk::Device<EthmacDevice, /* ddk mixins */>; 76// 77// class EthmacDevice : public EthmacDeviceType, 78// public ddk::EthmacProtocol<EthmacDevice> { 79// public: 80// EthmacDevice(zx_device_t* parent) 81// : EthmacDeviceType("my-ethmac-device"), 82// parent_(parent) {} 83// 84// zx_status_t Bind() { 85// return device_add(ddk_device(), parent_); 86// } 87// 88// void DdkRelease() { 89// // Clean up 90// } 91// 92// zx_status_t EthmacQuery(uint32_t options, ethmac_info_t* info) { 93// // Fill out the ethmac info 94// return ZX_OK; 95// } 96// 97// void EthmacStop() { 98// // Device should stop 99// } 100// 101// zx_status_t EthmacStart(fbl::unique_ptr<ddk::EthmacIfcProxy> proxy) { 102// // Start ethmac operation 103// proxy_.swap(proxy); 104// return ZX_OK; 105// } 106// 107// zx_status_t EthmacQueueTx(uint32_t options, ethmac_netbuf_t* netbuf) { 108// // Send the data 109// return ZX_OK; 110// } 111// 112// zx_status_t EthmacSetParam(uint32_t param, int32_t value, void* data) { 113// // Set the parameter 114// return ZX_OK; 115// } 116// 117// private: 118// zx_device_t* parent_; 119// fbl::unique_ptr<ddk::EthmacIfcProxy> proxy_; 120// }; 121 122namespace ddk { 123 124template <typename D> 125class EthmacIfc { 126public: 127 EthmacIfc() { 128 internal::CheckEthmacIfc<D>(); 129 ifc_.status = Status; 130 ifc_.recv = Recv; 131 ifc_.complete_tx = CompleteTx; 132 } 133 134 ethmac_ifc_t* ethmac_ifc() { return &ifc_; } 135 136private: 137 static void Status(void* cookie, uint32_t status) { 138 static_cast<D*>(cookie)->EthmacStatus(status); 139 } 140 141 static void Recv(void* cookie, void* data, size_t length, uint32_t flags) { 142 static_cast<D*>(cookie)->EthmacRecv(data, length, flags); 143 } 144 145 static void CompleteTx(void* cookie, ethmac_netbuf_t* netbuf, zx_status_t status) { 146 static_cast<D*>(cookie)->EthmacCompleteTx(netbuf, status); 147 } 148 149 ethmac_ifc_t ifc_ = {}; 150}; 151 152class EthmacIfcProxy { 153public: 154 EthmacIfcProxy(ethmac_ifc_t* ifc, void* cookie) 155 : ifc_(ifc), cookie_(cookie) {} 156 157 void Status(uint32_t status) { 158 ifc_->status(cookie_, status); 159 } 160 161 void Recv(void* data, size_t length, uint32_t flags) { 162 ifc_->recv(cookie_, data, length, flags); 163 } 164 165 void CompleteTx(ethmac_netbuf_t* netbuf, zx_status_t status) { 166 ifc_->complete_tx(cookie_, netbuf, status); 167 } 168 169private: 170 ethmac_ifc_t* ifc_; 171 void* cookie_; 172}; 173 174template <typename D> 175class EthmacProtocol : public internal::base_protocol { 176public: 177 EthmacProtocol() { 178 internal::CheckEthmacProtocolSubclass<D>(); 179 ops_.query = Query; 180 ops_.stop = Stop; 181 ops_.start = Start; 182 ops_.queue_tx = QueueTx; 183 ops_.set_param = SetParam; 184 ops_.get_bti = GetBti; 185 186 // Can only inherit from one base_protocol implemenation 187 ZX_ASSERT(ddk_proto_id_ == 0); 188 ddk_proto_id_ = ZX_PROTOCOL_ETHERNET_IMPL; 189 ddk_proto_ops_ = &ops_; 190 } 191 192private: 193 static zx_status_t Query(void* ctx, uint32_t options, ethmac_info_t* info) { 194 return static_cast<D*>(ctx)->EthmacQuery(options, info); 195 } 196 197 static void Stop(void* ctx) { 198 static_cast<D*>(ctx)->EthmacStop(); 199 } 200 201 static zx_status_t Start(void* ctx, ethmac_ifc_t* ifc, void* cookie) { 202 auto ifc_proxy = fbl::unique_ptr<EthmacIfcProxy>(new EthmacIfcProxy(ifc, cookie)); 203 return static_cast<D*>(ctx)->EthmacStart(fbl::move(ifc_proxy)); 204 } 205 206 static zx_status_t QueueTx(void* ctx, uint32_t options, ethmac_netbuf_t* netbuf) { 207 return static_cast<D*>(ctx)->EthmacQueueTx(options, netbuf); 208 } 209 210 static zx_status_t SetParam(void* ctx, uint32_t param, int32_t value, void* data) { 211 return static_cast<D*>(ctx)->EthmacSetParam(param, value, data); 212 } 213 214 static zx_handle_t GetBti(void* ctx) { 215 return static_cast<D*>(ctx)->EthmacGetBti(); 216 } 217 218 ethmac_protocol_ops_t ops_ = {}; 219}; 220 221class EthmacProtocolProxy { 222public: 223 EthmacProtocolProxy(ethmac_protocol_t* proto) 224 : ops_(proto->ops), ctx_(proto->ctx) {} 225 226 zx_status_t Query(uint32_t options, ethmac_info_t* info) { 227 return ops_->query(ctx_, options, info); 228 } 229 230 template <typename D> 231 zx_status_t Start(D* ifc) { 232 static_assert(fbl::is_base_of<EthmacIfc<D>, D>::value, 233 "Start must be called with a subclass of EthmacIfc"); 234 return ops_->start(ctx_, ifc->ethmac_ifc(), ifc); 235 } 236 237 void Stop() { 238 ops_->stop(ctx_); 239 } 240 241 zx_status_t QueueTx(uint32_t options, ethmac_netbuf_t* netbuf) { 242 return ops_->queue_tx(ctx_, options, netbuf); 243 } 244 245 zx_status_t SetParam(uint32_t param, int32_t value, void* data) { 246 return ops_->set_param(ctx_, param, value, data); 247 } 248 249private: 250 ethmac_protocol_ops_t* ops_; 251 void* ctx_; 252}; 253 254} // namespace ddk 255