| // 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 "third_party/iwlwifi/platform/wlan-softmac-device.h" |
| |
| #include <zircon/assert.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| |
| extern "C" { |
| #include "third_party/iwlwifi/mvm/mvm.h" |
| } // extern "C" |
| |
| #include "third_party/iwlwifi/platform/ieee80211.h" |
| #include "third_party/iwlwifi/platform/mvm-mlme.h" |
| #include "third_party/iwlwifi/platform/mvm-sta.h" |
| #include "third_party/iwlwifi/platform/scoped_utils.h" |
| |
| namespace wlan::iwlwifi { |
| |
| WlanSoftmacDevice::WlanSoftmacDevice(zx_device* parent, iwl_trans* drvdata, uint16_t iface_id, |
| struct iwl_mvm_vif* mvmvif) |
| : ddk::Device<WlanSoftmacDevice, ddk::Initializable, ddk::Unbindable>(parent), |
| mvmvif_(mvmvif), |
| drvdata_(drvdata), |
| iface_id_(iface_id) {} |
| |
| WlanSoftmacDevice::~WlanSoftmacDevice() = default; |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacQuery(wlan_softmac_info_t* out_info) { |
| return mac_query(mvmvif_, out_info); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacStart(const wlan_softmac_ifc_protocol_t* ifc, |
| zx::channel* out_mlme_channel) { |
| return mac_start(mvmvif_, ifc, (zx_handle_t*)out_mlme_channel); |
| } |
| |
| void WlanSoftmacDevice::WlanSoftmacStop() { |
| ap_mvm_sta_.reset(); |
| mac_stop(mvmvif_); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacQueueTx(const wlan_tx_packet_t* packet, |
| bool* out_enqueue_pending) { |
| // Delayed transmission is never used right now. |
| *out_enqueue_pending = false; |
| |
| if (ap_mvm_sta_ == nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| if (packet->mac_frame_size > WLAN_MSDU_MAX_LEN) { |
| IWL_ERR(mvmvif_, "Frame size is to large (%lu). expect less than %lu.\n", |
| packet->mac_frame_size, WLAN_MSDU_MAX_LEN); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| ieee80211_mac_packet mac_packet = {}; |
| mac_packet.common_header = |
| reinterpret_cast<const ieee80211_frame_header*>(packet->mac_frame_buffer); |
| mac_packet.header_size = ieee80211_get_header_len(mac_packet.common_header); |
| if (mac_packet.header_size > packet->mac_frame_size) { |
| IWL_ERR(mvmvif_, "TX packet header size %zu too large for data size %zu\n", |
| mac_packet.header_size, packet->mac_frame_size); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| mac_packet.body = packet->mac_frame_buffer + mac_packet.header_size; |
| mac_packet.body_size = packet->mac_frame_size - mac_packet.header_size; |
| if (ieee80211_pkt_is_protected(mac_packet.common_header)) { |
| switch (ieee80211_get_frame_type(mac_packet.common_header)) { |
| case ieee80211_frame_type::IEEE80211_FRAME_TYPE_MGMT: |
| mac_packet.info.control.hw_key = ap_mvm_sta_->GetKey(WLAN_KEY_TYPE_IGTK); |
| break; |
| case ieee80211_frame_type::IEEE80211_FRAME_TYPE_DATA: |
| mac_packet.info.control.hw_key = ap_mvm_sta_->GetKey(WLAN_KEY_TYPE_PAIRWISE); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| auto lock = std::lock_guard(mvmvif_->mvm->mutex); |
| return iwl_mvm_mac_tx(mvmvif_, ap_mvm_sta_->iwl_mvm_sta(), &mac_packet); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacSetChannel(const wlan_channel_t* channel) { |
| zx_status_t status = ZX_OK; |
| |
| // If the AP sta already exists, it probably was left from the previous association attempt. |
| // Remove it first. |
| if (ap_mvm_sta_ != nullptr) { |
| if ((status = mac_unconfigure_bss(mvmvif_)) != ZX_OK) { |
| return status; |
| } |
| ap_mvm_sta_.reset(); |
| } |
| return mac_set_channel(mvmvif_, channel); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacConfigureBss(const bss_config_t* config) { |
| zx_status_t status = ZX_OK; |
| if (ap_mvm_sta_ != nullptr) { |
| return ZX_ERR_ALREADY_BOUND; |
| } |
| if ((status = mac_configure_bss(mvmvif_, config)) != ZX_OK) { |
| return status; |
| } |
| |
| ZX_DEBUG_ASSERT(mvmvif_->mac_role == WLAN_MAC_ROLE_CLIENT); |
| std::unique_ptr<MvmSta> ap_mvm_sta; |
| if ((status = MvmSta::Create(mvmvif_, config->bssid, &ap_mvm_sta)) != ZX_OK) { |
| return status; |
| } |
| |
| ap_mvm_sta_ = std::move(ap_mvm_sta); |
| return ZX_OK; |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacEnableBeaconing(const wlan_bcn_config_t* bcn_cfg) { |
| return mac_enable_beaconing(mvmvif_, bcn_cfg); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacConfigureBeacon(const wlan_tx_packet_t* pkt) { |
| return mac_configure_beacon(mvmvif_, pkt); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacSetKey(const wlan_key_config_t* key_config) { |
| if (ap_mvm_sta_ == nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return ap_mvm_sta_->SetKey(key_config); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacConfigureAssoc(const wlan_assoc_ctx_t* assoc_ctx) { |
| if (ap_mvm_sta_ == nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| return mac_configure_assoc(mvmvif_, assoc_ctx); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacClearAssoc( |
| const uint8_t peer_addr_list[fuchsia_wlan_ieee80211_MAC_ADDR_LEN]) { |
| zx_status_t status = ZX_OK; |
| |
| if (ap_mvm_sta_ == nullptr) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| // Mark the station is no longer associated. This must be set before we start operating on the STA |
| // instance. |
| mvmvif_->bss_conf.assoc = false; |
| ap_mvm_sta_.reset(); |
| |
| if ((status = mac_clear_assoc(mvmvif_, peer_addr_list)) != ZX_OK) { |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacStartPassiveScan( |
| const wlan_softmac_passive_scan_args_t* passive_scan_args, uint64_t* out_scan_id) { |
| return mac_start_passive_scan(mvmvif_, passive_scan_args, out_scan_id); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacStartActiveScan( |
| const wlan_softmac_active_scan_args_t* active_scan_args, uint64_t* out_scan_id) { |
| return mac_start_active_scan(mvmvif_, active_scan_args, out_scan_id); |
| } |
| |
| zx_status_t WlanSoftmacDevice::WlanSoftmacUpdateWmmParams(wlan_ac_t ac, |
| const wlan_wmm_params_t* params) { |
| IWL_ERR(this, "%s() needs porting\n", __func__); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| void WlanSoftmacDevice::DdkInit(ddk::InitTxn txn) { |
| txn.Reply(mac_init(mvmvif_, drvdata_, zxdev(), iface_id_)); |
| } |
| |
| void WlanSoftmacDevice::DdkRelease() { |
| IWL_DEBUG_INFO(this, "Releasing iwlwifi mac-device\n"); |
| mac_release(mvmvif_); |
| |
| delete this; |
| } |
| |
| void WlanSoftmacDevice::DdkUnbind(ddk::UnbindTxn txn) { |
| IWL_DEBUG_INFO(this, "Unbinding iwlwifi mac-device\n"); |
| mac_unbind(mvmvif_); |
| txn.Reply(); |
| } |
| |
| } // namespace wlan::iwlwifi |