| // 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. |
| |
| #pragma once |
| |
| #include <ddk/protocol/ethernet.h> |
| #include <ddktl/protocol/ethernet-internal.h> |
| #include <zircon/assert.h> |
| #include <fbl/type_support.h> |
| #include <fbl/unique_ptr.h> |
| |
| // DDK ethernet protocol support |
| // |
| // :: Proxies :: |
| // |
| // ddk::EthmacIfcProxy and ddk::EthmacProtocolProxy are simple wrappers around ethmac_ifc_t and |
| // ethmac_protocol_t, respectively. They do not own the pointers passed to them. |
| // |
| // :: Mixins :: |
| // |
| // ddk::EthmacIfc and ddk::EthmacProtocol are mixin classes that simplify writing DDK drivers that |
| // interact with the ethernet protocol. They take care of implementing the function pointer tables |
| // and calling into the object that wraps them. |
| // |
| // :: Examples :: |
| // |
| // // A driver that communicates with a ZX_PROTOCOL_ETHERMAC device as a ethmac_ifc_t |
| // class EthDevice; |
| // using EthDeviceType = ddk::Device<EthDevice, /* ddk mixins */>; |
| // |
| // class EthDevice : public EthDeviceType, |
| // public ddk::EthmacIfc<EthDevice> { |
| // public: |
| // EthDevice(zx_device_t* parent) |
| // : EthDeviceType("my-eth-device"), |
| // parent_(parent) {} |
| // |
| // zx_status_t Bind() { |
| // ethmac_protocol_t* ops; |
| // auto status = get_device_protocol(parent_, ZX_PROTOCOL_ETHERMAC, |
| // reinterpret_cast<void**>(&ops)); |
| // if (status != ZX_OK) { |
| // return status; |
| // } |
| // proxy_.reset(new ddk::EthmacProtocolProxy(ops, parent_)); |
| // status = proxy_->Start(this); |
| // if (status != ZX_OK) { |
| // return status; |
| // } |
| // return device_add(ddk_device(), parent_); |
| // } |
| // |
| // void DdkRelease() { |
| // // Clean up |
| // } |
| // |
| // void EthmacStatus(uint32_t status) { |
| // // Report status |
| // } |
| // |
| // void EthmacRecv(void* buf, size_t length, uint32_t flags) { |
| // // Receive data buffer from ethmac device |
| // } |
| // |
| // private: |
| // zx_device_t* parent_; |
| // fbl::unique_ptr<ddk::EthmacProtocolProxy> proxy_; |
| // }; |
| // |
| // |
| // // A driver that implements a ZX_PROTOCOL_ETHERMAC device |
| // class EthmacDevice; |
| // using EthmacDeviceType = ddk::Device<EthmacDevice, /* ddk mixins */>; |
| // |
| // class EthmacDevice : public EthmacDeviceType, |
| // public ddk::EthmacProtocol<EthmacDevice> { |
| // public: |
| // EthmacDevice(zx_device_t* parent) |
| // : EthmacDeviceType("my-ethmac-device"), |
| // parent_(parent) {} |
| // |
| // zx_status_t Bind() { |
| // return device_add(ddk_device(), parent_); |
| // } |
| // |
| // void DdkRelease() { |
| // // Clean up |
| // } |
| // |
| // zx_status_t EthmacQuery(uint32_t options, ethmac_info_t* info) { |
| // // Fill out the ethmac info |
| // return ZX_OK; |
| // } |
| // |
| // void EthmacStop() { |
| // // Device should stop |
| // } |
| // |
| // zx_status_t EthmacStart(fbl::unique_ptr<ddk::EthmacIfcProxy> proxy) { |
| // // Start ethmac operation |
| // proxy_.swap(proxy); |
| // return ZX_OK; |
| // } |
| // |
| // void EthmacSend(uint32_t options, void* data, size_t length) { |
| // // Send the data |
| // } |
| // |
| // private: |
| // zx_device_t* parent_; |
| // fbl::unique_ptr<ddk::EthmacIfcProxy> proxy_; |
| // }; |
| |
| namespace ddk { |
| |
| template <typename D> |
| class EthmacIfc { |
| public: |
| EthmacIfc() { |
| internal::CheckEthmacIfc<D>(); |
| ifc_.status = Status; |
| ifc_.recv = Recv; |
| } |
| |
| ethmac_ifc_t* ethmac_ifc() { return &ifc_; } |
| |
| private: |
| static void Status(void* cookie, uint32_t status) { |
| static_cast<D*>(cookie)->EthmacStatus(status); |
| } |
| |
| static void Recv(void* cookie, void* data, size_t length, uint32_t flags) { |
| static_cast<D*>(cookie)->EthmacRecv(data, length, flags); |
| } |
| |
| ethmac_ifc_t ifc_ = {}; |
| }; |
| |
| class EthmacIfcProxy { |
| public: |
| EthmacIfcProxy(ethmac_ifc_t* ifc, void* cookie) |
| : ifc_(ifc), cookie_(cookie) {} |
| |
| void Status(uint32_t status) { |
| ifc_->status(cookie_, status); |
| } |
| |
| void Recv(void* data, size_t length, uint32_t flags) { |
| ifc_->recv(cookie_, data, length, flags); |
| } |
| |
| private: |
| ethmac_ifc_t* ifc_; |
| void* cookie_; |
| }; |
| |
| template <typename D> |
| class EthmacProtocol : public internal::base_protocol { |
| public: |
| EthmacProtocol() { |
| internal::CheckEthmacProtocolSubclass<D>(); |
| ops_.query = Query; |
| ops_.stop = Stop; |
| ops_.start = Start; |
| ops_.send = Send; |
| |
| // Can only inherit from one base_protocol implemenation |
| ZX_ASSERT(ddk_proto_ops_ == nullptr); |
| ddk_proto_id_ = ZX_PROTOCOL_ETHERMAC; |
| ddk_proto_ops_ = &ops_; |
| } |
| |
| private: |
| static zx_status_t Query(void* ctx, uint32_t options, ethmac_info_t* info) { |
| return static_cast<D*>(ctx)->EthmacQuery(options, info); |
| } |
| |
| static void Stop(void* ctx) { |
| static_cast<D*>(ctx)->EthmacStop(); |
| } |
| |
| static zx_status_t Start(void* ctx, ethmac_ifc_t* ifc, void* cookie) { |
| auto ifc_proxy = fbl::unique_ptr<EthmacIfcProxy>(new EthmacIfcProxy(ifc, cookie)); |
| return static_cast<D*>(ctx)->EthmacStart(fbl::move(ifc_proxy)); |
| } |
| |
| static void Send(void* ctx, uint32_t options, void* data, size_t length) { |
| static_cast<D*>(ctx)->EthmacSend(options, data, length); |
| } |
| |
| ethmac_protocol_ops_t ops_ = {}; |
| }; |
| |
| class EthmacProtocolProxy { |
| public: |
| EthmacProtocolProxy(ethmac_protocol_t* proto) |
| : ops_(proto->ops), ctx_(proto->ctx) {} |
| |
| zx_status_t Query(uint32_t options, ethmac_info_t* info) { |
| return ops_->query(ctx_, options, info); |
| } |
| |
| template <typename D> |
| zx_status_t Start(D* ifc) { |
| static_assert(fbl::is_base_of<EthmacIfc<D>, D>::value, |
| "Start must be called with a subclass of EthmacIfc"); |
| return ops_->start(ctx_, ifc->ethmac_ifc(), ifc); |
| } |
| |
| void Stop() { |
| ops_->stop(ctx_); |
| } |
| |
| void Send(uint32_t options, void* data, size_t length) { |
| ops_->send(ctx_, options, data, length); |
| } |
| |
| private: |
| ethmac_protocol_ops_t* ops_; |
| void* ctx_; |
| }; |
| |
| } // namespace ddk |