| // Copyright 2021 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. |
| |
| #include "wlantap-mac.h" |
| |
| #include <fuchsia/hardware/wlan/softmac/cpp/banjo.h> |
| #include <fuchsia/wlan/common/c/banjo.h> |
| #include <fuchsia/wlan/common/cpp/fidl.h> |
| #include <fuchsia/wlan/device/cpp/fidl.h> |
| #include <fuchsia/wlan/ieee80211/c/banjo.h> |
| #include <fuchsia/wlan/internal/cpp/banjo.h> |
| #include <lib/ddk/debug.h> |
| #include <lib/ddk/driver.h> |
| |
| #include <mutex> |
| |
| #include <wlan/common/channel.h> |
| #include <wlan/common/features.h> |
| #include <wlan/common/phy.h> |
| |
| #include "fidl/fuchsia.wlan.common/cpp/wire_types.h" |
| #include "utils.h" |
| |
| namespace wlan { |
| |
| namespace wlantap = ::fuchsia::wlan::tap; |
| namespace wlan_common = ::fuchsia::wlan::common; |
| |
| namespace { |
| |
| // TODO(fxbug.dev/93459) Prune unnecessary fields from phy_config |
| struct WlantapMacImpl : WlantapMac { |
| WlantapMacImpl(zx_device_t* phy_device, uint16_t id, wlan_common::WlanMacRole role, |
| const wlantap::WlantapPhyConfig* phy_config, Listener* listener, |
| zx::channel sme_channel) |
| : id_(id), |
| role_(role), |
| phy_config_(phy_config), |
| listener_(listener), |
| sme_channel_(std::move(sme_channel)) {} |
| |
| static void DdkUnbind(void* ctx) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| self.Unbind(); |
| } |
| |
| static void DdkRelease(void* ctx) { delete static_cast<WlantapMacImpl*>(ctx); } |
| |
| // WlanSoftmac protocol impl |
| |
| static zx_status_t WlanSoftmacQuery(void* ctx, wlan_softmac_info_t* mac_info) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| ConvertTapPhyConfig(mac_info, *self.phy_config_); |
| return ZX_OK; |
| } |
| |
| static void WlanSoftmacQueryDiscoverySupport(void* ctx, discovery_support_t* support) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| wlan::common::ConvertDiscoverySupportToDdk(self.phy_config_->discovery_support, support); |
| } |
| |
| static void WlanSoftmacQueryMacSublayerSupport(void* ctx, mac_sublayer_support_t* support) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| wlan::common::ConvertMacSublayerSupportToDdk(self.phy_config_->mac_sublayer_support, support); |
| } |
| |
| static void WlanSoftmacQuerySecuritySupport(void* ctx, security_support_t* support) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| wlan::common::ConvertSecuritySupportToDdk(self.phy_config_->security_support, support); |
| } |
| |
| static void WlanSoftmacQuerySpectrumManagementSupport(void* ctx, |
| spectrum_management_support_t* support) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| wlan::common::ConvertSpectrumManagementSupportToDdk( |
| self.phy_config_->spectrum_management_support, support); |
| } |
| |
| static zx_status_t WlanSoftmacStart(void* ctx, const wlan_softmac_ifc_protocol_t* ifc, |
| zx_handle_t* out_sme_channel) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| { |
| std::lock_guard<std::mutex> guard(self.lock_); |
| if (self.ifc_.is_valid()) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| if (!self.sme_channel_.is_valid()) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| self.ifc_ = ddk::WlanSoftmacIfcProtocolClient(ifc); |
| } |
| self.listener_->WlantapMacStart(self.id_); |
| *out_sme_channel = self.sme_channel_.release(); |
| return ZX_OK; |
| } |
| |
| static void WlanSoftmacStop(void* ctx) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| { |
| std::lock_guard<std::mutex> guard(self.lock_); |
| self.ifc_.clear(); |
| } |
| self.listener_->WlantapMacStop(self.id_); |
| } |
| |
| static zx_status_t WlanSoftmacQueueTx(void* ctx, const wlan_tx_packet_t* packet, |
| bool* out_enqueue_pending) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| self.listener_->WlantapMacQueueTx(self.id_, packet); |
| *out_enqueue_pending = false; |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacSetChannel(void* ctx, const wlan_channel_t* channel) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| fuchsia_wlan_common::wire::WlanChannel chan = { |
| .primary = channel->primary, |
| .cbw = static_cast<fuchsia_wlan_common::wire::ChannelBandwidth>(channel->cbw), |
| .secondary80 = channel->secondary80, |
| }; |
| |
| if (!wlan::common::IsValidChan(chan)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| self.listener_->WlantapMacSetChannel(self.id_, channel); |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacConfigureBss(void* ctx, const bss_config_t* config) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| bool expected_remote = self.role_ == wlan_common::WlanMacRole::CLIENT; |
| if (config->remote != expected_remote) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| self.listener_->WlantapMacConfigureBss(self.id_, config); |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacEnableBeaconing(void* ctx, const wlan_bcn_config_t* bcn_cfg) { |
| // This is the test driver, so we can just pretend beaconing was enabled. |
| (void)bcn_cfg; |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacConfigureBeacon(void* ctx, const wlan_tx_packet_t* pkt) { |
| // This is the test driver, so we can just pretend the beacon was configured. |
| (void)pkt; |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacSetKey(void* ctx, const wlan_key_config_t* key_config) { |
| auto& self = *static_cast<WlantapMacImpl*>(ctx); |
| self.listener_->WlantapMacSetKey(self.id_, key_config); |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacConfigureAssoc(void* ctx, const wlan_assoc_ctx* assoc_ctx) { |
| // This is the test driver, so we can just pretend the association was configured. |
| (void)assoc_ctx; |
| // TODO(fxbug.dev/28907): Evalute the use and implement |
| return ZX_OK; |
| } |
| |
| static zx_status_t WlanSoftmacClearAssoc( |
| void* ctx, const uint8_t peer_addr[fuchsia_wlan_ieee80211_MAC_ADDR_LEN]) { |
| if (!peer_addr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // TODO(fxbug.dev/28907): Evalute the use and implement |
| return ZX_OK; |
| } |
| |
| // WlantapMac impl |
| |
| virtual void Rx(const std::vector<uint8_t>& data, const wlantap::WlanRxInfo& rx_info) override { |
| std::lock_guard<std::mutex> guard(lock_); |
| if (ifc_.is_valid()) { |
| wlan_rx_info_t converted_info = {.rx_flags = rx_info.rx_flags, |
| .valid_fields = rx_info.valid_fields, |
| .phy = common::FromFidl(rx_info.phy), |
| .data_rate = rx_info.data_rate, |
| .channel = {.primary = rx_info.channel.primary, |
| .cbw = static_cast<uint8_t>(rx_info.channel.cbw), |
| .secondary80 = rx_info.channel.secondary80}, |
| .mcs = rx_info.mcs, |
| .rssi_dbm = rx_info.rssi_dbm, |
| .snr_dbh = rx_info.snr_dbh}; |
| wlan_rx_packet_t rx_packet = { |
| .mac_frame_buffer = data.data(), .mac_frame_size = data.size(), .info = converted_info}; |
| ifc_.Recv(&rx_packet); |
| } |
| } |
| |
| virtual void Status(uint32_t status) override { |
| std::lock_guard<std::mutex> guard(lock_); |
| if (ifc_.is_valid()) { |
| ifc_.Status(status); |
| } |
| } |
| |
| virtual void ReportTxStatus(const wlan_common::WlanTxStatus& ts) override { |
| std::lock_guard<std::mutex> guard(lock_); |
| if (ifc_.is_valid()) { |
| wlan_tx_status_t converted_tx_status = ConvertTxStatus(ts); |
| ifc_.ReportTxStatus(&converted_tx_status); |
| } |
| } |
| |
| void Unbind() { |
| { |
| std::lock_guard<std::mutex> guard(lock_); |
| ifc_.clear(); |
| } |
| device_unbind_reply(device_); |
| } |
| |
| virtual void RemoveDevice() override { device_async_remove(device_); } |
| |
| zx_device_t* device_ = nullptr; |
| uint16_t id_; |
| wlan_common::WlanMacRole role_; |
| std::mutex lock_; |
| ddk::WlanSoftmacIfcProtocolClient ifc_ __TA_GUARDED(lock_); |
| const wlantap::WlantapPhyConfig* phy_config_; |
| Listener* listener_; |
| zx::channel sme_channel_; |
| }; |
| |
| } // namespace |
| |
| zx_status_t CreateWlantapMac(zx_device_t* parent_phy, const wlan_common::WlanMacRole role, |
| const wlantap::WlantapPhyConfig* phy_config, uint16_t id, |
| WlantapMac::Listener* listener, zx::channel sme_channel, |
| WlantapMac** ret) { |
| char name[ZX_MAX_NAME_LEN + 1]; |
| snprintf(name, sizeof(name), "mac%u", id); |
| std::unique_ptr<WlantapMacImpl> wlan_softmac( |
| new WlantapMacImpl(parent_phy, id, role, phy_config, listener, std::move(sme_channel))); |
| static zx_protocol_device_t device_ops = {.version = DEVICE_OPS_VERSION, |
| .unbind = &WlantapMacImpl::DdkUnbind, |
| .release = &WlantapMacImpl::DdkRelease}; |
| static wlan_softmac_protocol_ops_t proto_ops = { |
| .query = &WlantapMacImpl::WlanSoftmacQuery, |
| .query_discovery_support = &WlantapMacImpl::WlanSoftmacQueryDiscoverySupport, |
| .query_mac_sublayer_support = &WlantapMacImpl::WlanSoftmacQueryMacSublayerSupport, |
| .query_security_support = &WlantapMacImpl::WlanSoftmacQuerySecuritySupport, |
| .query_spectrum_management_support = |
| &WlantapMacImpl::WlanSoftmacQuerySpectrumManagementSupport, |
| .start = &WlantapMacImpl::WlanSoftmacStart, |
| .stop = &WlantapMacImpl::WlanSoftmacStop, |
| .queue_tx = &WlantapMacImpl::WlanSoftmacQueueTx, |
| .set_channel = &WlantapMacImpl::WlanSoftmacSetChannel, |
| .configure_bss = &WlantapMacImpl::WlanSoftmacConfigureBss, |
| .enable_beaconing = &WlantapMacImpl::WlanSoftmacEnableBeaconing, |
| .configure_beacon = &WlantapMacImpl::WlanSoftmacConfigureBeacon, |
| .set_key = &WlantapMacImpl::WlanSoftmacSetKey, |
| .configure_assoc = &WlantapMacImpl::WlanSoftmacConfigureAssoc, |
| .clear_assoc = &WlantapMacImpl::WlanSoftmacClearAssoc, |
| }; |
| device_add_args_t args = {.version = DEVICE_ADD_ARGS_VERSION, |
| .name = name, |
| .ctx = wlan_softmac.get(), |
| .ops = &device_ops, |
| .proto_id = ZX_PROTOCOL_WLAN_SOFTMAC, |
| .proto_ops = &proto_ops}; |
| zx_status_t status = device_add(parent_phy, &args, &wlan_softmac->device_); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "%s: could not add device: %d", __func__, status); |
| return status; |
| } |
| // Transfer ownership to devmgr |
| *ret = wlan_softmac.release(); |
| return ZX_OK; |
| } |
| |
| } // namespace wlan |