blob: b5757e7e099eedc1a027f12db413e838df7cf7e3 [file] [log] [blame]
// Copyright 2020 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/wlanphyimpl-device.h"
#include <lib/sync/completion.h>
#include <zircon/status.h>
#include <memory>
extern "C" {
#include "third_party/iwlwifi/mvm/mvm.h"
} // extern "C"
#include "third_party/iwlwifi/platform/mvm-mlme.h"
#include "third_party/iwlwifi/platform/wlansoftmac-device.h"
namespace wlan::iwlwifi {
namespace phyimpl_fidl = fuchsia_wlan_phyimpl::wire;
WlanPhyImplDevice::WlanPhyImplDevice() = default;
WlanPhyImplDevice::~WlanPhyImplDevice() = default;
void WlanPhyImplDevice::GetSupportedMacRoles(fdf::Arena& arena,
GetSupportedMacRolesCompleter::Sync& completer) {
// The fidl array which will be returned to Wlanphy driver.
fuchsia_wlan_common::WlanMacRole
supported_mac_roles_list[fuchsia_wlan_common::wire::kMaxSupportedMacRoles] = {};
uint8_t supported_mac_roles_count = 0;
zx_status_t status =
phy_get_supported_mac_roles(drvdata(), supported_mac_roles_list, &supported_mac_roles_count);
if (status != ZX_OK) {
IWL_ERR(this, "failed get supported mac roles: %s", zx_status_get_string(status));
completer.buffer(arena).ReplyError(status);
return;
}
if (supported_mac_roles_count > fuchsia_wlan_common::wire::kMaxSupportedMacRoles) {
IWL_ERR(this,
"Too many mac roles returned from iwlwifi driver. Number of supported mac roles got "
"from driver is %u, but the limitation is: %u\n",
supported_mac_roles_count, fuchsia_wlan_common::wire::kMaxSupportedMacRoles);
completer.buffer(arena).ReplyError(ZX_ERR_OUT_OF_RANGE);
return;
}
auto reply_vector = fidl::VectorView<fuchsia_wlan_common::WlanMacRole>::FromExternal(
supported_mac_roles_list, supported_mac_roles_count);
fidl::Arena fidl_arena;
auto builder =
fuchsia_wlan_phyimpl::wire::WlanPhyImplGetSupportedMacRolesResponse::Builder(fidl_arena);
builder.supported_mac_roles(reply_vector);
completer.buffer(arena).ReplySuccess(builder.Build());
}
void WlanPhyImplDevice::CreateIface(CreateIfaceRequestView request, fdf::Arena& arena,
CreateIfaceCompleter::Sync& completer) {
zx_status_t status = ZX_OK;
if (!request->has_role() || !request->has_mlme_channel()) {
IWL_ERR(this, "missing info in request role(%u), channel(%u)", request->has_role(),
request->has_mlme_channel());
completer.buffer(arena).ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
uint16_t out_iface_id;
wlan_phy_impl_create_iface_req_t create_iface_req;
switch (request->role()) {
case fuchsia_wlan_common::WlanMacRole::kClient:
create_iface_req.role = WLAN_MAC_ROLE_CLIENT;
break;
case fuchsia_wlan_common::WlanMacRole::kAp:
create_iface_req.role = WLAN_MAC_ROLE_AP;
break;
case fuchsia_wlan_common::WlanMacRole::kMesh:
create_iface_req.role = WLAN_MAC_ROLE_MESH;
break;
default:
IWL_ERR(this, "Unrecognized WlanMacRole type from the request. Requested role: %u\n",
static_cast<uint32_t>(request->role()));
completer.buffer(arena).ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
create_iface_req.mlme_channel = request->mlme_channel().release();
if ((status = phy_create_iface(drvdata(), &create_iface_req, &out_iface_id)) != ZX_OK) {
IWL_ERR(this, "failed phy create: %s", zx_status_get_string(status));
completer.buffer(arena).ReplyError(status);
return;
}
struct iwl_mvm* mvm = iwl_trans_get_mvm(drvdata());
struct iwl_mvm_vif* mvmvif = mvm->mvmvif[out_iface_id];
if ((status = AddWlansoftmacDevice(out_iface_id, mvmvif)) != ZX_OK) {
IWL_ERR(this, "%s() failed mac device add: %s\n", __func__, zx_status_get_string(status));
phy_create_iface_undo(drvdata(), out_iface_id);
completer.buffer(arena).ReplyError(status);
return;
}
IWL_INFO(this, "%s() created iface %u\n", __func__, out_iface_id);
fidl::Arena fidl_arena;
auto builder = phyimpl_fidl::WlanPhyImplCreateIfaceResponse::Builder(fidl_arena);
builder.iface_id(out_iface_id);
completer.buffer(arena).ReplySuccess(builder.Build());
}
void WlanPhyImplDevice::DestroyIface(DestroyIfaceRequestView request, fdf::Arena& arena,
DestroyIfaceCompleter::Sync& completer) {
if (!request->has_iface_id()) {
IWL_ERR(this, "invoked without valid iface id");
completer.buffer(arena).ReplyError(ZX_ERR_INVALID_ARGS);
return;
}
IWL_INFO(this, "destroying iface %u", request->iface_id());
const int mac_id = 0; // Assume only one interface is created.
const auto iwl_trans = reinterpret_cast<struct iwl_trans*>(drvdata());
struct iwl_mvm* mvm = iwl_trans_get_mvm(iwl_trans);
struct iwl_mvm_vif* mvmvif = mvm->mvmvif[mac_id];
mac_stop(mvmvif);
zx_status_t status = phy_destroy_iface(drvdata(), request->iface_id());
if (status != ZX_OK) {
IWL_ERR(this, "failed destroy iface: %s", zx_status_get_string(status));
completer.buffer(arena).ReplyError(status);
return;
}
if ((status = RemoveWlansoftmacDevice(request->iface_id())) != ZX_OK) {
IWL_ERR(this, "%s() failed mac device remove: %s\n", __func__, zx_status_get_string(status));
completer.buffer(arena).ReplyError(status);
return;
}
completer.buffer(arena).ReplySuccess();
}
void WlanPhyImplDevice::SetCountry(SetCountryRequestView request, fdf::Arena& arena,
SetCountryCompleter::Sync& completer) {
wlan_phy_country_t country;
if (!request->is_alpha2()) {
IWL_ERR(this, "only alpha2 format is supported");
completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED);
return;
}
memcpy(&country.alpha2[0], request->alpha2().data(), WLANPHY_ALPHA2_LEN);
zx_status_t status = phy_set_country(drvdata(), &country);
if (status != ZX_OK) {
IWL_ERR(this, "failed set country: %s", zx_status_get_string(status));
completer.buffer(arena).ReplyError(status);
return;
}
completer.buffer(arena).ReplySuccess();
}
void WlanPhyImplDevice::ClearCountry(fdf::Arena& arena, ClearCountryCompleter::Sync& completer) {
IWL_ERR(this, "%s() not implemented ...\n", __func__);
completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void WlanPhyImplDevice::GetCountry(fdf::Arena& arena, GetCountryCompleter::Sync& completer) {
fidl::Array<uint8_t, WLANPHY_ALPHA2_LEN> alpha2;
// TODO(fxbug.dev/95504): Remove the usage of wlan_phy_country_t inside the iwlwifi driver.
wlan_phy_country_t country;
zx_status_t status = phy_get_country(drvdata(), &country);
if (status != ZX_OK) {
completer.buffer(arena).ReplyError(status);
IWL_ERR(this, "failed get country: %s", zx_status_get_string(status));
return;
}
memcpy(alpha2.begin(), country.alpha2, WLANPHY_ALPHA2_LEN);
auto out_country = fuchsia_wlan_phyimpl::wire::WlanPhyCountry::WithAlpha2(alpha2);
completer.buffer(arena).ReplySuccess(out_country);
}
void WlanPhyImplDevice::SetPowerSaveMode(SetPowerSaveModeRequestView request, fdf::Arena& arena,
SetPowerSaveModeCompleter::Sync& completer) {
IWL_ERR(this, "%s() not implemented ...\n", __func__);
completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void WlanPhyImplDevice::GetPowerSaveMode(fdf::Arena& arena,
GetPowerSaveModeCompleter::Sync& completer) {
IWL_ERR(this, "%s() not implemented ...\n", __func__);
completer.buffer(arena).ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void WlanPhyImplDevice::ServiceConnectHandler(
fdf_dispatcher_t* dispatcher, fdf::ServerEnd<fuchsia_wlan_phyimpl::WlanPhyImpl> server_end) {
fdf::BindServer(dispatcher, std::move(server_end), this);
}
} // namespace wlan::iwlwifi