| // 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 = ð_device_ops; |
| args.proto_id = ZX_PROTOCOL_ETHERNET_IMPL; |
| args.proto_ops = ðernet_impl_ops; |
| return device_add(parent_, &args, ðdev_); |
| } |
| |
| #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(ðernet_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(ðernet_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(ðernet_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(ðernet_ifc_, data, length, flags); |
| } |
| } |
| |
| } // namespace wlanif |