blob: db81fa9aa853a66d701ab4172318b31b531a8535 [file] [log] [blame]
// 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