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