blob: 4a76149cb826d639a1fbf4e9f607dc4c728dfbb7 [file] [log] [blame]
// 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/wlan.h>
#include <ddktl/protocol/wlan-internal.h>
#include <zircon/assert.h>
#include <fbl/type_support.h>
#include <fbl/unique_ptr.h>
// DDK wlan protocol support
//
// :: Proxies ::
//
// ddk::WlanmacIfcProxy and ddk::WlanmacProtocolProxy are simple wrappers around wlanmac_ifc_t and
// wlanmac_protocol_t, respectively. They do not own the pointers passed to them.
//
// :: Mixins ::
//
// ddk::WlanmacIfc and ddk::WlanmacProtocol are mixin classes that simplify writing DDK drivers that
// interact with the wlan 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_WLANMAC device as a wlanmac_ifc_t
// class WlanDevice;
// using WlanDeviceType = ddk::Device<WlanDevice, /* ddk mixins */>;
//
// class WlanDevice : public WlanDeviceType,
// public ddk::WlanmacIfc<WlanDevice> {
// public:
// WlanDevice(zx_device_t* parent)
// : WlanDeviceType(driver, "my-wlan-device"),
// parent_(parent) {}
//
// zx_status_t Bind() {
// wlanmac_protocol_t* ops;
// auto status = get_device_protocol(parent_, ZX_PROTOCOL_WLANMAC,
// reinterpret_cast<void**>(&ops));
// if (status != ZX_OK) {
// return status;
// }
// proxy_.reset(new ddk::WlanmacProtocolProxy(ops, parent_));
// status = proxy_->Start(this);
// if (status != ZX_OK) {
// return status;
// }
// return device_add(ddk_device(), parent_);
// }
//
// void DdkRelease() {
// // Clean up
// }
//
// void WlanmacStatus(uint32_t status) {
// // Report status
// }
//
// void WlanmacRecv(uint32_t flags, const void* buf, size_t length,
// wlan_rx_info_t* info) {
// // Receive data buffer from wlanmac device
// }
//
// private:
// zx_device_t* parent_;
// fbl::unique_ptr<ddk::WlanmacProtocolProxy> proxy_;
// };
//
//
// // A driver that implements a ZX_PROTOCOL_WLANMAC device
// class WlanmacDevice;
// using WlanmacDeviceType = ddk::Device<WlanmacDevice, /* ddk mixins */>;
//
// class WlanmacDevice : public WlanmacDeviceType,
// public ddk::WlanmacProtocol<WlanmacDevice> {
// public:
// WlanmacDevice(zx_device_t* parent)
// : WlanmacDeviceType(driver, "my-wlanmac-device"),
// parent_(parent) {}
//
// zx_status_t Bind() {
// return device_add(ddk_device(), parent_);
// }
//
// void DdkRelease() {
// // Clean up
// }
//
// zx_status_t WlanmacQuery(uint32_t options, ethmac_info_t* info) {
// // Fill out the ethmac info
// return ZX_OK;
// }
//
// void WlanmacStop() {
// // Device should stop
// }
//
// zx_status_t WlanmacStart(fbl::unique_ptr<ddk::WlanmacIfcProxy> proxy) {
// // Start wlanmac operation
// proxy_.swap(proxy);
// return ZX_OK;
// }
//
// void WlanmacTx(uint32_t options, void* data, size_t length) {
// // Send the data
// }
//
// zx_status_t WlanmacSetChannel(uint32_t options, wlan_channel_t* chan) {
// // Set the radio channel
// return ZX_OK;
// }
//
// private:
// zx_device_t* parent_;
// fbl::unique_ptr<ddk::WlanmacIfcProxy> proxy_;
// };
namespace ddk {
template <typename D>
class WlanmacIfc {
public:
WlanmacIfc() {
internal::CheckWlanmacIfc<D>();
ifc_.status = Status;
ifc_.recv = Recv;
}
wlanmac_ifc_t* wlanmac_ifc() { return &ifc_; }
private:
static void Status(void* cookie, uint32_t status) {
static_cast<D*>(cookie)->WlanmacStatus(status);
}
static void Recv(void* cookie, uint32_t flags, const void* data, size_t length,
wlan_rx_info_t* info) {
static_cast<D*>(cookie)->WlanmacRecv(flags, data, length, info);
}
wlanmac_ifc_t ifc_ = {};
};
class WlanmacIfcProxy {
public:
WlanmacIfcProxy(wlanmac_ifc_t* ifc, void* cookie)
: ifc_(ifc), cookie_(cookie) {}
void Status(uint32_t status) {
ifc_->status(cookie_, status);
}
void Recv(uint32_t flags, const void* data, size_t length, wlan_rx_info_t* info) {
ifc_->recv(cookie_, flags, data, length, info);
}
private:
wlanmac_ifc_t* ifc_;
void* cookie_;
};
template <typename D>
class WlanmacProtocol : public internal::base_protocol {
public:
WlanmacProtocol() {
internal::CheckWlanmacProtocolSubclass<D>();
ops_.query = Query;
ops_.stop = Stop;
ops_.start = Start;
ops_.tx = Tx;
ops_.set_channel = SetChannel;
// Can only inherit from one base_protocol implemenation
ZX_ASSERT(this->ddk_proto_ops_ == nullptr);
ddk_proto_id_ = ZX_PROTOCOL_WLANMAC;
ddk_proto_ops_ = &ops_;
}
private:
static zx_status_t Query(void* ctx, uint32_t options, ethmac_info_t* info) {
return static_cast<D*>(ctx)->WlanmacQuery(options, info);
}
static void Stop(void* ctx) {
static_cast<D*>(ctx)->WlanmacStop();
}
static zx_status_t Start(void* ctx, wlanmac_ifc_t* ifc, void* cookie) {
auto ifc_proxy = fbl::unique_ptr<WlanmacIfcProxy>(new WlanmacIfcProxy(ifc, cookie));
return static_cast<D*>(ctx)->WlanmacStart(fbl::move(ifc_proxy));
}
static void Tx(void* ctx, uint32_t options, const void* data, size_t length) {
static_cast<D*>(ctx)->WlanmacTx(options, data, length);
}
static zx_status_t SetChannel(void* ctx, uint32_t options, wlan_channel_t* chan) {
return static_cast<D*>(ctx)->WlanmacSetChannel(options, chan);
}
wlanmac_protocol_ops_t ops_ = {};
};
class WlanmacProtocolProxy {
public:
WlanmacProtocolProxy(wlanmac_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<WlanmacIfc<D>, D>::value,
"Start must be called with a subclass of WlanmacIfc");
return ops_->start(ctx_, ifc->wlanmac_ifc(), ifc);
}
void Stop() {
ops_->stop(ctx_);
}
void Tx(uint32_t options, const void* data, size_t length) {
ops_->tx(ctx_, options, data, length);
}
zx_status_t SetChannel(uint32_t options, wlan_channel_t* chan) {
return ops_->set_channel(ctx_, options, chan);
}
private:
wlanmac_protocol_ops_t* ops_;
void* ctx_;
};
} // namespace ddk