blob: 3b18ddf36142a2e03a3c3a1e5f54fff8d06f1b36 [file] [log] [blame]
// Copyright 2018 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 "device.h"
#include <fuchsia/hardware/wlanif/c/banjo.h>
#include <fuchsia/wlan/mlme/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <net/ethernet.h>
#include <zircon/status.h>
#include <ddk/device.h>
#include <ddk/hw/wlan/wlaninfo/c/banjo.h>
#include <wlan/common/logging.h>
#include "convert.h"
#include "driver.h"
namespace wlanif {
namespace wlan_common = ::fuchsia::wlan::common;
namespace wlan_mlme = ::fuchsia::wlan::mlme;
namespace wlan_stats = ::fuchsia::wlan::stats;
Device::Device(zx_device_t* device, wlanif_impl_protocol_t wlanif_impl_proto)
: parent_(device),
wlanif_impl_(wlanif_impl_proto),
loop_(&kAsyncLoopConfigNoAttachToCurrentThread),
binding_(this) {
debugfn();
}
Device::~Device() { debugfn(); }
#define DEV(c) static_cast<Device*>(c)
static zx_protocol_device_t eth_device_ops = {
.version = DEVICE_OPS_VERSION,
.unbind = [](void* ctx) { DEV(ctx)->EthUnbind(); },
.release = [](void* ctx) { DEV(ctx)->EthRelease(); },
};
static wlanif_impl_ifc_protocol_ops_t wlanif_impl_ifc_ops = {
// MLME operations
.on_scan_result = [](void* cookie,
const wlanif_scan_result_t* result) { DEV(cookie)->OnScanResult(result); },
.on_scan_end = [](void* cookie, const wlanif_scan_end_t* end) { DEV(cookie)->OnScanEnd(end); },
.join_conf = [](void* cookie,
const wlanif_join_confirm_t* resp) { DEV(cookie)->JoinConf(resp); },
.auth_conf = [](void* cookie,
const wlanif_auth_confirm_t* resp) { DEV(cookie)->AuthenticateConf(resp); },
.auth_ind = [](void* cookie,
const wlanif_auth_ind_t* ind) { DEV(cookie)->AuthenticateInd(ind); },
.deauth_conf =
[](void* cookie, const wlanif_deauth_confirm_t* resp) {
DEV(cookie)->DeauthenticateConf(resp);
},
.deauth_ind =
[](void* cookie, const wlanif_deauth_indication_t* ind) {
DEV(cookie)->DeauthenticateInd(ind);
},
.assoc_conf = [](void* cookie,
const wlanif_assoc_confirm_t* resp) { DEV(cookie)->AssociateConf(resp); },
.assoc_ind = [](void* cookie,
const wlanif_assoc_ind_t* ind) { DEV(cookie)->AssociateInd(ind); },
.disassoc_conf =
[](void* cookie, const wlanif_disassoc_confirm_t* resp) {
DEV(cookie)->DisassociateConf(resp);
},
.disassoc_ind =
[](void* cookie, const wlanif_disassoc_indication_t* ind) {
DEV(cookie)->DisassociateInd(ind);
},
.start_conf = [](void* cookie,
const wlanif_start_confirm_t* resp) { DEV(cookie)->StartConf(resp); },
.stop_conf = [](void* cookie,
const wlanif_stop_confirm_t* resp) { DEV(cookie)->StopConf(resp); },
.eapol_conf = [](void* cookie,
const wlanif_eapol_confirm_t* resp) { DEV(cookie)->EapolConf(resp); },
.on_channel_switch =
[](void* cookie, const wlanif_channel_switch_info_t* ind) {
DEV(cookie)->OnChannelSwitched(ind);
},
// MLME extension operations
.signal_report =
[](void* cookie, const wlanif_signal_report_indication_t* ind) {
DEV(cookie)->SignalReport(ind);
},
.eapol_ind = [](void* cookie,
const wlanif_eapol_indication_t* ind) { DEV(cookie)->EapolInd(ind); },
.stats_query_resp =
[](void* cookie, const wlanif_stats_query_response_t* resp) {
DEV(cookie)->StatsQueryResp(resp);
},
.relay_captured_frame =
[](void* cookie, const wlanif_captured_frame_result_t* result) {
DEV(cookie)->RelayCapturedFrame(result);
},
.on_pmk_available = [](void* cookie,
const wlanif_pmk_info_t* ind) { DEV(cookie)->OnPmkAvailable(ind); },
.sae_handshake_ind =
[](void* cookie, const wlanif_sae_handshake_ind_t* ind) {
DEV(cookie)->SaeHandshakeInd(ind);
},
.sae_frame_rx = [](void* cookie,
const wlanif_sae_frame_t* ind) { DEV(cookie)->SaeFrameRx(ind); },
// Ethernet operations
.data_recv = [](void* cookie, const void* data, size_t length,
uint32_t flags) { DEV(cookie)->EthRecv(data, length, flags); },
};
static ethernet_impl_protocol_ops_t ethernet_impl_ops = {
.query = [](void* ctx, uint32_t options, ethernet_info_t* info) -> zx_status_t {
return DEV(ctx)->EthQuery(options, info);
},
.stop = [](void* ctx) { DEV(ctx)->EthStop(); },
.start = [](void* ctx, const ethernet_ifc_protocol_t* ifc) -> zx_status_t {
return DEV(ctx)->EthStart(ifc);
},
.queue_tx =
[](void* ctx, uint32_t options, ethernet_netbuf_t* netbuf,
ethernet_impl_queue_tx_callback completion_cb,
void* cookie) { return DEV(ctx)->EthQueueTx(options, netbuf, completion_cb, cookie); },
.set_param = [](void* ctx, uint32_t param, int32_t value, const void* data, size_t data_size)
-> zx_status_t { return DEV(ctx)->EthSetParam(param, value, data, data_size); },
};
#undef DEV
zx_status_t Device::AddEthDevice() {
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "wlan-ethernet";
args.ctx = this;
args.ops = &eth_device_ops;
args.proto_id = ZX_PROTOCOL_ETHERNET_IMPL;
args.proto_ops = &ethernet_impl_ops;
return device_add(parent_, &args, &ethdev_);
}
#define VERIFY_PROTO_OP(fn) \
do { \
if (wlanif_impl_.ops->fn == nullptr) { \
errorf("wlanif: required protocol function %s missing\n", #fn); \
return ZX_ERR_INVALID_ARGS; \
} \
} while (0)
zx_status_t Device::Bind() {
debugfn();
// Assert minimum required functionality from the wlanif_impl driver
if (wlanif_impl_.ops == nullptr) {
errorf("wlanif: no wlanif_impl protocol ops provided\n");
return ZX_ERR_INVALID_ARGS;
}
VERIFY_PROTO_OP(start);
VERIFY_PROTO_OP(query);
VERIFY_PROTO_OP(start_scan);
VERIFY_PROTO_OP(join_req);
VERIFY_PROTO_OP(auth_req);
VERIFY_PROTO_OP(auth_resp);
VERIFY_PROTO_OP(deauth_req);
VERIFY_PROTO_OP(assoc_req);
VERIFY_PROTO_OP(assoc_resp);
VERIFY_PROTO_OP(disassoc_req);
VERIFY_PROTO_OP(reset_req);
VERIFY_PROTO_OP(start_req);
VERIFY_PROTO_OP(stop_req);
VERIFY_PROTO_OP(set_keys_req);
VERIFY_PROTO_OP(del_keys_req);
VERIFY_PROTO_OP(eapol_req);
// The MLME interface has no start/stop commands, so we will start the wlanif_impl
// device immediately
zx_handle_t sme_channel = ZX_HANDLE_INVALID;
zx_status_t status = wlanif_impl_start(&wlanif_impl_, this, &wlanif_impl_ifc_ops, &sme_channel);
ZX_DEBUG_ASSERT(sme_channel != ZX_HANDLE_INVALID);
if (status != ZX_OK) {
errorf("wlanif: call to wlanif-impl start() failed: %s\n", zx_status_get_string(status));
return status;
}
// Query the device.
wlanif_impl_query(&wlanif_impl_, &query_info_);
status = loop_.StartThread("wlanif-loop");
if (status != ZX_OK) {
errorf("wlanif: unable to start async loop: %s\n", zx_status_get_string(status));
return status;
}
ZX_DEBUG_ASSERT(ethdev_ == nullptr);
status = AddEthDevice();
if (status != ZX_OK) {
errorf("wlanif: could not add ethernet_impl device: %s\n", zx_status_get_string(status));
} else {
status = Connect(zx::channel(sme_channel));
if (status != ZX_OK) {
errorf("wlanif: unable to wait on SME channel: %s\n", zx_status_get_string(status));
device_async_remove(ethdev_);
return status;
}
}
if (status != ZX_OK) {
loop_.Shutdown();
}
return status;
}
#undef VERIFY_PROTO_OP
void Device::EthUnbind() {
debugfn();
// Stop accepting new FIDL requests.
std::lock_guard<std::mutex> lock(lock_);
if (binding_.is_bound()) {
binding_.Unbind();
}
// Ensure that all FIDL messages have been processed before removing the device
auto dispatcher = loop_.dispatcher();
::async::PostTask(dispatcher, [this] { device_unbind_reply(ethdev_); });
}
void Device::EthRelease() {
debugfn();
delete this;
}
zx_status_t Device::Connect(zx::channel request) {
debugfn();
std::lock_guard<std::mutex> lock(lock_);
if (binding_.is_bound()) {
return ZX_ERR_ALREADY_BOUND;
}
return binding_.Bind(std::move(request), loop_.dispatcher());
}
void Device::StartScan(wlan_mlme::ScanRequest req) {
wlanif_scan_req_t impl_req = {};
// txn_id
impl_req.txn_id = req.txn_id;
// bss_type
impl_req.bss_type = ConvertBssType(req.bss_type);
// bssid
std::memcpy(impl_req.bssid, req.bssid.data(), ETH_ALEN);
// ssid
CopySSID(req.ssid, &impl_req.ssid);
// scan_type
impl_req.scan_type = ConvertScanType(req.scan_type);
// probe_delay
impl_req.probe_delay = req.probe_delay;
// channel_list
std::vector<uint8_t> channel_list;
if (req.channel_list.has_value()) {
channel_list = std::move(req.channel_list.value());
}
if (channel_list.size() > WLAN_INFO_CHANNEL_LIST_MAX_CHANNELS) {
warnf("wlanif: truncating channel list from %lu to %d\n", channel_list.size(),
WLAN_INFO_CHANNEL_LIST_MAX_CHANNELS);
impl_req.num_channels = WLAN_INFO_CHANNEL_LIST_MAX_CHANNELS;
} else {
impl_req.num_channels = channel_list.size();
}
std::memcpy(impl_req.channel_list, channel_list.data(), impl_req.num_channels);
// min_channel_time
impl_req.min_channel_time = req.min_channel_time;
// max_channel_time
impl_req.max_channel_time = req.max_channel_time;
// ssid_list
std::vector<std::vector<uint8_t>> ssid_list;
if (req.ssid_list.has_value()) {
ssid_list = std::move(req.ssid_list.value());
}
size_t num_ssids = ssid_list.size();
if (num_ssids > WLAN_SCAN_MAX_SSIDS) {
warnf("wlanif: truncating SSID list from %zu to %d\n", num_ssids, WLAN_SCAN_MAX_SSIDS);
num_ssids = WLAN_SCAN_MAX_SSIDS;
}
for (size_t ndx = 0; ndx < num_ssids; ndx++) {
CopySSID(ssid_list[ndx], &impl_req.ssid_list[ndx]);
}
impl_req.num_ssids = num_ssids;
wlanif_impl_start_scan(&wlanif_impl_, &impl_req);
}
void Device::JoinReq(wlan_mlme::JoinRequest req) {
SetEthernetStatusUnlocked(false);
wlanif_join_req_t impl_req = {};
// selected_bss
ConvertBssDescription(&impl_req.selected_bss, req.selected_bss);
// join_failure_timeout
impl_req.join_failure_timeout = req.join_failure_timeout;
// nav_sync_delay
impl_req.nav_sync_delay = req.nav_sync_delay;
// op_rates
if (req.op_rates.size() > WLAN_MAX_OP_RATES) {
warnf("wlanif: truncating operational rates set from %zu to %d members\n", req.op_rates.size(),
WLAN_MAX_OP_RATES);
impl_req.num_op_rates = WLAN_MAX_OP_RATES;
} else {
impl_req.num_op_rates = req.op_rates.size();
}
std::memcpy(impl_req.op_rates, req.op_rates.data(), impl_req.num_op_rates);
wlanif_impl_join_req(&wlanif_impl_, &impl_req);
}
void Device::AuthenticateReq(wlan_mlme::AuthenticateRequest req) {
SetEthernetStatusUnlocked(false);
wlanif_auth_req_t impl_req = {};
// peer_sta_address
std::memcpy(impl_req.peer_sta_address, req.peer_sta_address.data(), ETH_ALEN);
// auth_type
impl_req.auth_type = ConvertAuthType(req.auth_type);
// auth_failure_timeout
impl_req.auth_failure_timeout = req.auth_failure_timeout;
// sae_password
if (req.sae_password) {
impl_req.sae_password_count = req.sae_password->size();
impl_req.sae_password_list = &(*req.sae_password)[0];
}
wlanif_impl_auth_req(&wlanif_impl_, &impl_req);
}
void Device::AuthenticateResp(wlan_mlme::AuthenticateResponse resp) {
wlanif_auth_resp_t impl_resp = {};
// peer_sta_address
std::memcpy(impl_resp.peer_sta_address, resp.peer_sta_address.data(), ETH_ALEN);
// result_code
impl_resp.result_code = ConvertAuthResultCode(resp.result_code);
wlanif_impl_auth_resp(&wlanif_impl_, &impl_resp);
}
void Device::DeauthenticateReq(wlan_mlme::DeauthenticateRequest req) {
SetEthernetStatusUnlocked(false);
wlanif_deauth_req_t impl_req = {};
// peer_sta_address
std::memcpy(impl_req.peer_sta_address, req.peer_sta_address.data(), ETH_ALEN);
// reason_code
impl_req.reason_code = ConvertReasonCode(req.reason_code);
wlanif_impl_deauth_req(&wlanif_impl_, &impl_req);
}
void Device::AssociateReq(wlan_mlme::AssociateRequest req) {
wlanif_assoc_req_t impl_req = {};
// peer_sta_address
std::memcpy(impl_req.peer_sta_address, req.peer_sta_address.data(), ETH_ALEN);
{
std::lock_guard<std::mutex> lock(lock_);
protected_bss_ = req.rsne.has_value() || req.vendor_ies.has_value();
// rsne
if (protected_bss_) {
if (req.rsne.has_value()) {
CopyRSNE(req.rsne.value(), impl_req.rsne, &impl_req.rsne_len);
}
if (req.vendor_ies.has_value()) {
CopyVendorSpecificIE(req.vendor_ies.value(), impl_req.vendor_ie, &impl_req.vendor_ie_len);
}
} else {
impl_req.rsne_len = 0;
impl_req.vendor_ie_len = 0;
}
}
wlanif_impl_assoc_req(&wlanif_impl_, &impl_req);
}
void Device::AssociateResp(wlan_mlme::AssociateResponse resp) {
wlanif_assoc_resp_t impl_resp = {};
// peer_sta_address
std::memcpy(impl_resp.peer_sta_address, resp.peer_sta_address.data(), ETH_ALEN);
// result_code
impl_resp.result_code = ConvertAssocResultCode(resp.result_code);
// association_id
impl_resp.association_id = resp.association_id;
wlanif_impl_assoc_resp(&wlanif_impl_, &impl_resp);
}
void Device::DisassociateReq(wlan_mlme::DisassociateRequest req) {
SetEthernetStatusUnlocked(false);
wlanif_disassoc_req_t impl_req = {};
// peer_sta_address
std::memcpy(impl_req.peer_sta_address, req.peer_sta_address.data(), ETH_ALEN);
// reason_code
impl_req.reason_code = req.reason_code;
wlanif_impl_disassoc_req(&wlanif_impl_, &impl_req);
}
void Device::ResetReq(wlan_mlme::ResetRequest req) {
SetEthernetStatusUnlocked(false);
wlanif_reset_req_t impl_req = {};
// sta_address
std::memcpy(impl_req.sta_address, req.sta_address.data(), ETH_ALEN);
// set_default_mib
impl_req.set_default_mib = req.set_default_mib;
wlanif_impl_reset_req(&wlanif_impl_, &impl_req);
}
void Device::StartReq(wlan_mlme::StartRequest req) {
SetEthernetStatusUnlocked(true);
wlanif_start_req_t impl_req = {};
// ssid
CopySSID(req.ssid, &impl_req.ssid);
// bss_type
impl_req.bss_type = ConvertBssType(req.bss_type);
// beacon_period
impl_req.beacon_period = req.beacon_period;
// dtim_period
impl_req.dtim_period = req.dtim_period;
// channel
impl_req.channel = req.channel;
// rsne
CopyRSNE(req.rsne.value_or(std::vector<uint8_t>{}), impl_req.rsne, &impl_req.rsne_len);
wlanif_impl_start_req(&wlanif_impl_, &impl_req);
}
void Device::StopReq(wlan_mlme::StopRequest req) {
SetEthernetStatusUnlocked(false);
wlanif_stop_req_t impl_req = {};
// ssid
CopySSID(req.ssid, &impl_req.ssid);
wlanif_impl_stop_req(&wlanif_impl_, &impl_req);
}
void Device::SetKeysReq(wlan_mlme::SetKeysRequest req) {
wlanif_set_keys_req_t impl_req = {};
// keylist
size_t num_keys = req.keylist.size();
if (num_keys > WLAN_MAX_KEYLIST_SIZE) {
warnf("wlanif: truncating key list from %zu to %d members\n", num_keys, WLAN_MAX_KEYLIST_SIZE);
impl_req.num_keys = WLAN_MAX_KEYLIST_SIZE;
} else {
impl_req.num_keys = num_keys;
}
for (size_t desc_ndx = 0; desc_ndx < num_keys; desc_ndx++) {
ConvertSetKeyDescriptor(&impl_req.keylist[desc_ndx], req.keylist[desc_ndx]);
}
wlanif_impl_set_keys_req(&wlanif_impl_, &impl_req);
}
void Device::DeleteKeysReq(wlan_mlme::DeleteKeysRequest req) {
wlanif_del_keys_req_t impl_req = {};
// keylist
size_t num_keys = req.keylist.size();
if (num_keys > WLAN_MAX_KEYLIST_SIZE) {
warnf("wlanif: truncating key list from %zu to %d members\n", num_keys, WLAN_MAX_KEYLIST_SIZE);
impl_req.num_keys = WLAN_MAX_KEYLIST_SIZE;
} else {
impl_req.num_keys = num_keys;
}
for (size_t desc_ndx = 0; desc_ndx < num_keys; desc_ndx++) {
ConvertDeleteKeyDescriptor(&impl_req.keylist[desc_ndx], req.keylist[desc_ndx]);
}
wlanif_impl_del_keys_req(&wlanif_impl_, &impl_req);
}
void Device::EapolReq(wlan_mlme::EapolRequest req) {
wlanif_eapol_req_t impl_req = {};
// src_addr
std::memcpy(impl_req.src_addr, req.src_addr.data(), ETH_ALEN);
// dst_addr
std::memcpy(impl_req.dst_addr, req.dst_addr.data(), ETH_ALEN);
// data
impl_req.data_count = req.data.size();
impl_req.data_list = req.data.data();
wlanif_impl_eapol_req(&wlanif_impl_, &impl_req);
}
void Device::QueryDeviceInfo(QueryDeviceInfoCallback cb) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::DeviceInfo fidl_resp;
// mac_addr
std::memcpy(fidl_resp.mac_addr.data(), query_info_.mac_addr, ETH_ALEN);
// role
fidl_resp.role = ConvertMacRole(query_info_.role);
// bands
fidl_resp.bands.resize(query_info_.num_bands);
for (size_t ndx = 0; ndx < query_info_.num_bands; ndx++) {
ConvertBandCapabilities(&fidl_resp.bands[ndx], query_info_.bands[ndx]);
}
// driver features flag
fidl_resp.driver_features.resize(0);
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_SCAN_OFFLOAD) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::SCAN_OFFLOAD);
}
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_RATE_SELECTION) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::RATE_SELECTION);
}
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_SYNTH) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::SYNTH);
}
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_TX_STATUS_REPORT) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::TX_STATUS_REPORT);
}
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_DFS) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::DFS);
}
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_SAE_SME_AUTH) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::SAE_SME_AUTH);
}
if (query_info_.driver_features & WLAN_INFO_DRIVER_FEATURE_PROBE_RESP_OFFLOAD) {
fidl_resp.driver_features.push_back(wlan_common::DriverFeature::PROBE_RESP_OFFLOAD);
}
cb(std::move(fidl_resp));
}
void Device::StatsQueryReq() {
if (wlanif_impl_.ops->stats_query_req != nullptr) {
wlanif_impl_stats_query_req(&wlanif_impl_);
}
}
void Device::ListMinstrelPeers(ListMinstrelPeersCallback cb) {
errorf("Minstrel peer list not available: FullMAC driver not supported.\n");
ZX_DEBUG_ASSERT(false);
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
cb(wlan_mlme::MinstrelListResponse{});
}
void Device::GetMinstrelStats(wlan_mlme::MinstrelStatsRequest req, GetMinstrelStatsCallback cb) {
errorf("Minstrel stats not available: FullMAC driver not supported.\n");
ZX_DEBUG_ASSERT(false);
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
cb(wlan_mlme::MinstrelStatsResponse{});
}
void Device::SendMpOpenAction(wlan_mlme::MeshPeeringOpenAction req) {
errorf("SendMpConfirmAction is not implemented\n");
}
void Device::SendMpConfirmAction(wlan_mlme::MeshPeeringConfirmAction req) {
errorf("SendMpConfirmAction is not implemented\n");
}
void Device::MeshPeeringEstablished(wlan_mlme::MeshPeeringParams params) {
errorf("MeshPeeringEstablished is not implemented\n");
}
void Device::GetMeshPathTableReq(::fuchsia::wlan::mlme::GetMeshPathTableRequest req,
GetMeshPathTableReqCallback cb) {
errorf("GetMeshPathTable is not implemented\n");
}
void Device::SetControlledPort(wlan_mlme::SetControlledPortRequest req) {
switch (req.state) {
case wlan_mlme::ControlledPortState::OPEN:
SetEthernetStatusUnlocked(true);
break;
case wlan_mlme::ControlledPortState::CLOSED:
SetEthernetStatusUnlocked(false);
break;
}
}
void Device::StartCaptureFrames(::fuchsia::wlan::mlme::StartCaptureFramesRequest req,
StartCaptureFramesCallback cb) {
wlanif_start_capture_frames_req_t impl_req = {};
impl_req.mgmt_frame_flags = ConvertMgmtCaptureFlags(req.mgmt_frame_flags);
wlanif_start_capture_frames_resp_t impl_resp = {};
// forward request to driver
wlanif_impl_start_capture_frames(&wlanif_impl_, &impl_req, &impl_resp);
wlan_mlme::StartCaptureFramesResponse resp;
resp.status = impl_resp.status;
resp.supported_mgmt_frames = ConvertMgmtCaptureFlags(impl_resp.supported_mgmt_frames);
cb(resp);
}
void Device::StopCaptureFrames() { wlanif_impl_stop_capture_frames(&wlanif_impl_); }
void Device::SaeHandshakeResp(::fuchsia::wlan::mlme::SaeHandshakeResponse resp) {
wlanif_sae_handshake_resp_t handshake_resp = {};
memcpy(handshake_resp.peer_sta_address, resp.peer_sta_address.data(), ETH_ALEN);
handshake_resp.status_code = ConvertStatusCode(resp.status_code);
wlanif_impl_sae_handshake_resp(&wlanif_impl_, &handshake_resp);
}
void Device::SaeFrameTx(::fuchsia::wlan::mlme::SaeFrame frame) {
wlanif_sae_frame_t sae_frame = {};
ConvertSaeAuthFrame(frame, &sae_frame);
wlanif_impl_sae_frame_tx(&wlanif_impl_, &sae_frame);
}
void Device::OnScanResult(const wlanif_scan_result_t* result) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::ScanResult fidl_result;
// txn_id
fidl_result.txn_id = result->txn_id;
// bss
ConvertBssDescription(&fidl_result.bss, result->bss);
binding_.events().OnScanResult(std::move(fidl_result));
}
void Device::OnScanEnd(const wlanif_scan_end_t* end) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::ScanEnd fidl_end;
// txn_id
fidl_end.txn_id = end->txn_id;
// code
fidl_end.code = ConvertScanResultCode(end->code);
binding_.events().OnScanEnd(std::move(fidl_end));
}
void Device::JoinConf(const wlanif_join_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
SetEthernetStatusLocked(false);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::JoinConfirm fidl_resp;
// result_code
fidl_resp.result_code = ConvertJoinResultCode(resp->result_code);
binding_.events().JoinConf(std::move(fidl_resp));
}
void Device::AuthenticateConf(const wlanif_auth_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
SetEthernetStatusLocked(false);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::AuthenticateConfirm fidl_resp;
// peer_sta_address
std::memcpy(fidl_resp.peer_sta_address.data(), resp->peer_sta_address, ETH_ALEN);
// auth_type
fidl_resp.auth_type = ConvertAuthType(resp->auth_type);
// result_code
fidl_resp.result_code = ConvertAuthResultCode(resp->result_code);
binding_.events().AuthenticateConf(std::move(fidl_resp));
}
void Device::AuthenticateInd(const wlanif_auth_ind_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::AuthenticateIndication fidl_ind;
// peer_sta_address
std::memcpy(fidl_ind.peer_sta_address.data(), ind->peer_sta_address, ETH_ALEN);
// auth_type
fidl_ind.auth_type = ConvertAuthType(ind->auth_type);
binding_.events().AuthenticateInd(std::move(fidl_ind));
}
void Device::DeauthenticateConf(const wlanif_deauth_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
if (query_info_.role == WLAN_INFO_MAC_ROLE_CLIENT) {
SetEthernetStatusLocked(false);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::DeauthenticateConfirm fidl_resp;
// peer_sta_address
std::memcpy(fidl_resp.peer_sta_address.data(), resp->peer_sta_address, ETH_ALEN);
binding_.events().DeauthenticateConf(std::move(fidl_resp));
}
void Device::DeauthenticateInd(const wlanif_deauth_indication_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (query_info_.role == WLAN_INFO_MAC_ROLE_CLIENT) {
SetEthernetStatusLocked(false);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::DeauthenticateIndication fidl_ind;
// peer_sta_address
std::memcpy(fidl_ind.peer_sta_address.data(), ind->peer_sta_address, ETH_ALEN);
// reason_code
fidl_ind.reason_code = ConvertReasonCode(ind->reason_code);
// locally_initiated
fidl_ind.locally_initiated = ind->locally_initiated;
binding_.events().DeauthenticateInd(std::move(fidl_ind));
}
void Device::AssociateConf(const wlanif_assoc_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
// For unprotected network, set data state to online immediately. For protected network, do
// nothing. Later on upper layer would send message to open controlled port.
if (resp->result_code == WLAN_ASSOC_RESULT_SUCCESS && !protected_bss_) {
SetEthernetStatusLocked(true);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::AssociateConfirm fidl_resp;
// result_code
fidl_resp.result_code = ConvertAssocResultCode(resp->result_code);
// association_id
fidl_resp.association_id = resp->association_id;
if (resp->wmm_param_present) {
// Sanity check that the param length in banjo and FIDL are the same
static_assert(WLAN_WMM_PARAM_LEN == wlan_mlme::WMM_PARAM_LEN);
auto wmm_param = wlan_mlme::WmmParameter::New();
memcpy(wmm_param->bytes.data(), resp->wmm_param, WLAN_WMM_PARAM_LEN);
fidl_resp.wmm_param = std::move(wmm_param);
}
binding_.events().AssociateConf(std::move(fidl_resp));
}
void Device::AssociateInd(const wlanif_assoc_ind_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::AssociateIndication fidl_ind;
ConvertAssocInd(&fidl_ind, *ind);
binding_.events().AssociateInd(std::move(fidl_ind));
}
void Device::DisassociateConf(const wlanif_disassoc_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
if (query_info_.role == WLAN_INFO_MAC_ROLE_CLIENT) {
SetEthernetStatusLocked(false);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::DisassociateConfirm fidl_resp;
// status
fidl_resp.status = resp->status;
binding_.events().DisassociateConf(std::move(fidl_resp));
}
void Device::DisassociateInd(const wlanif_disassoc_indication_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (query_info_.role == WLAN_INFO_MAC_ROLE_CLIENT) {
SetEthernetStatusLocked(false);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::DisassociateIndication fidl_ind;
// peer_sta_address
std::memcpy(fidl_ind.peer_sta_address.data(), ind->peer_sta_address, ETH_ALEN);
// reason_code
fidl_ind.reason_code = ind->reason_code;
// locally_initiated
fidl_ind.locally_initiated = ind->locally_initiated;
binding_.events().DisassociateInd(std::move(fidl_ind));
}
void Device::StartConf(const wlanif_start_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
if (resp->result_code == WLAN_START_RESULT_SUCCESS) {
SetEthernetStatusLocked(true);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::StartConfirm fidl_resp;
// result_code
fidl_resp.result_code = ConvertStartResultCode(resp->result_code);
binding_.events().StartConf(std::move(fidl_resp));
}
void Device::StopConf(const wlanif_stop_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
if (resp->result_code == WLAN_STOP_RESULT_SUCCESS) {
SetEthernetStatusLocked(false);
}
if (!binding_.is_bound()) {
return;
}
wlan_mlme::StopConfirm fidl_resp;
fidl_resp.result_code = ConvertStopResultCode(resp->result_code);
binding_.events().StopConf(fidl_resp);
}
void Device::EapolConf(const wlanif_eapol_confirm_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::EapolConfirm fidl_resp;
// result_code
fidl_resp.result_code = ConvertEapolResultCode(resp->result_code);
binding_.events().EapolConf(std::move(fidl_resp));
}
void Device::OnChannelSwitched(const wlanif_channel_switch_info_t* info) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::ChannelSwitchInfo fidl_info;
fidl_info.new_channel = info->new_channel;
binding_.events().OnChannelSwitched(fidl_info);
}
void Device::SaeHandshakeInd(const wlanif_sae_handshake_ind_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::SaeHandshakeIndication fidl_ind;
memcpy(fidl_ind.peer_sta_address.data(), ind->peer_sta_address, ETH_ALEN);
binding_.events().OnSaeHandshakeInd(fidl_ind);
}
void Device::SaeFrameRx(const wlanif_sae_frame_t* frame) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::SaeFrame fidl_frame;
ConvertSaeAuthFrame(frame, fidl_frame);
binding_.events().OnSaeFrameRx(std::move(fidl_frame));
}
void Device::SignalReport(const wlanif_signal_report_indication_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::SignalReportIndication fidl_ind{
.rssi_dbm = ind->rssi_dbm,
.snr_db = ind->snr_db,
};
binding_.events().SignalReport(std::move(fidl_ind));
}
void Device::EapolInd(const wlanif_eapol_indication_t* ind) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::EapolIndication fidl_ind;
// src_addr
std::memcpy(fidl_ind.src_addr.data(), ind->src_addr, ETH_ALEN);
// dst_addr
std::memcpy(fidl_ind.dst_addr.data(), ind->dst_addr, ETH_ALEN);
// data
fidl_ind.data.resize(ind->data_count);
fidl_ind.data.assign(ind->data_list, ind->data_list + ind->data_count);
binding_.events().EapolInd(std::move(fidl_ind));
}
void Device::StatsQueryResp(const wlanif_stats_query_response_t* resp) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::StatsQueryResponse fidl_resp;
ConvertIfaceStats(&fidl_resp.stats, resp->stats);
binding_.events().StatsQueryResp(std::move(fidl_resp));
}
void Device::RelayCapturedFrame(const wlanif_captured_frame_result* result) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::CapturedFrameResult fidl_result;
fidl_result.frame.resize(result->data_count);
fidl_result.frame.assign(result->data_list, result->data_list + result->data_count);
binding_.events().RelayCapturedFrame(std::move(fidl_result));
}
void Device::OnPmkAvailable(const wlanif_pmk_info_t* info) {
std::lock_guard<std::mutex> lock(lock_);
if (!binding_.is_bound()) {
return;
}
wlan_mlme::PmkInfo fidl_info;
ConvertPmkInfo(&fidl_info, *info);
binding_.events().OnPmkAvailable(fidl_info);
}
zx_status_t Device::EthStart(const ethernet_ifc_protocol_t* ifc) {
std::lock_guard<std::mutex> lock(lock_);
ethernet_ifc_ = *ifc;
eth_started_ = true;
if (eth_online_) {
ethernet_ifc_status(&ethernet_ifc_, ETHERNET_STATUS_ONLINE);
}
// TODO(fxbug.dev/51009): Inform SME that ethernet has started.
return ZX_OK;
}
void Device::EthStop() {
std::lock_guard<std::mutex> lock(lock_);
eth_started_ = false;
std::memset(&ethernet_ifc_, 0, sizeof(ethernet_ifc_));
}
zx_status_t Device::EthQuery(uint32_t options, ethernet_info_t* info) {
std::lock_guard<std::mutex> lock(lock_);
std::memset(info, 0, sizeof(*info));
// features
info->features = ETHERNET_FEATURE_WLAN;
if (query_info_.features & WLANIF_FEATURE_DMA) {
info->features |= ETHERNET_FEATURE_DMA;
}
if (query_info_.features & WLANIF_FEATURE_SYNTH) {
info->features |= ETHERNET_FEATURE_SYNTH;
}
// mtu
info->mtu = 1500;
info->netbuf_size = sizeof(ethernet_netbuf_t);
// mac
std::memcpy(info->mac, query_info_.mac_addr, ETH_ALEN);
return ZX_OK;
}
void Device::EthQueueTx(uint32_t options, ethernet_netbuf_t* netbuf,
ethernet_impl_queue_tx_callback completion_cb, void* cookie) {
if (wlanif_impl_.ops->data_queue_tx != nullptr) {
wlanif_impl_data_queue_tx(&wlanif_impl_, options, netbuf, completion_cb, cookie);
} else {
completion_cb(cookie, ZX_ERR_NOT_SUPPORTED, netbuf);
}
}
zx_status_t Device::EthSetParam(uint32_t param, int32_t value, const void* data, size_t data_size) {
zx_status_t status = ZX_ERR_NOT_SUPPORTED;
switch (param) {
case ETHERNET_SETPARAM_PROMISC:
// See fxbug.dev/28881: In short, the bridge mode doesn't require WLAN promiscuous mode
// enabled.
// So we give a warning and return OK here to continue the bridging.
// TODO(fxbug.dev/29113): To implement the real promiscuous mode.
if (value == 1) { // Only warn when enabling.
warnf("wlanif: WLAN promiscuous not supported yet. see fxbug.dev/29113\n");
}
status = ZX_OK;
break;
case ETHERNET_SETPARAM_MULTICAST_PROMISC:
if (wlanif_impl_.ops->set_multicast_promisc != nullptr) {
return wlanif_impl_set_multicast_promisc(&wlanif_impl_, !!value);
} else {
return ZX_ERR_NOT_SUPPORTED;
}
break;
}
return status;
}
void Device::SetEthernetStatusLocked(bool online) {
// TODO(fxbug.dev/51009): Let SME handle these changes.
if (online != eth_online_) {
eth_online_ = online;
if (eth_started_) {
ethernet_ifc_status(&ethernet_ifc_, online ? ETHERNET_STATUS_ONLINE : 0);
}
}
}
void Device::SetEthernetStatusUnlocked(bool online) {
std::lock_guard<std::mutex> lock(lock_);
SetEthernetStatusLocked(online);
}
void Device::EthRecv(const void* data, size_t length, uint32_t flags) {
std::lock_guard<std::mutex> lock(lock_);
if (eth_started_) {
ethernet_ifc_recv(&ethernet_ifc_, data, length, flags);
}
}
} // namespace wlanif