// SPDX-License-Identifier: ISC /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP:
arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_CCM];
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_TKIP:
arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_TKIP];
arg.key_txmic_len = 8;
arg.key_rxmic_len = 8; break; case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104:
arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_WEP]; break; case WLAN_CIPHER_SUITE_CCMP_256:
arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_CCM]; break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256:
arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_AES_GCM];
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_BIP_CMAC_256: case WLAN_CIPHER_SUITE_AES_CMAC:
WARN_ON(1); return -EINVAL; default:
ath10k_warn(ar, "cipher %d is not supported\n", key->cipher); return -EOPNOTSUPP;
}
if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (cmd == DISABLE_KEY) { if (flags & WMI_KEY_GROUP) { /* Not all hardware handles group-key deletion operation * correctly. Replace the key with a junk value to invalidate it.
*/
get_random_bytes(key->key, key->keylen);
} else {
arg.key_cipher = ar->wmi_key_cipher[WMI_CIPHER_NONE];
arg.key_data = NULL;
}
}
/* In some cases (notably with static WEP IBSS with multiple keys) * multicast Tx becomes broken. Both pairwise and groupwise keys are * installed already. Using WMI_KEY_TX_USAGE in different combinations * didn't seem help. Using def_keyid vdev parameter seems to be * effective so use that. * * FIXME: Revisit. Perhaps this can be done in a less hacky way.
*/ if (arvif->vif->type != NL80211_IFTYPE_ADHOC) return 0;
if (arvif->def_wep_key_idx == -1) return 0;
ret = ath10k_wmi_vdev_set_param(arvif->ar,
arvif->vdev_id,
arvif->ar->wmi.vdev_param->def_keyid,
arvif->def_wep_key_idx); if (ret) {
ath10k_warn(ar, "failed to re-set def wpa key idxon vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
return 0;
}
staticint ath10k_clear_peer_keys(struct ath10k_vif *arvif, const u8 *addr)
{ struct ath10k *ar = arvif->ar; struct ath10k_peer *peer; int first_errno = 0; int ret; int i;
u32 flags = 0;
for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { if (peer->keys[i] == NULL) continue;
/* key flags are not required to delete the key */
ret = ath10k_install_key(arvif, peer->keys[i],
DISABLE_KEY, addr, flags); if (ret < 0 && first_errno == 0)
first_errno = ret;
if (ret < 0)
ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
i, ret);
/* We don't know which vdev this peer belongs to, * since WMI doesn't give us that information. * * FIXME: multi-bss needs to be handled.
*/
peer = ath10k_peer_find(ar, 0, addr); if (!peer) returnfalse;
for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { if (peer->keys[i] && peer->keys[i]->keyidx == keyidx) returntrue;
}
returnfalse;
}
staticint ath10k_clear_vdev_key(struct ath10k_vif *arvif, struct ieee80211_key_conf *key)
{ struct ath10k *ar = arvif->ar; struct ath10k_peer *peer;
u8 addr[ETH_ALEN]; int first_errno = 0; int ret; int i;
u32 flags = 0;
lockdep_assert_held(&ar->conf_mutex);
for (;;) { /* since ath10k_install_key we can't hold data_lock all the * time, so we try to remove the keys incrementally
*/
spin_lock_bh(&ar->data_lock);
i = 0;
list_for_each_entry(peer, &ar->peers, list) { for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { if (peer->keys[i] == key) {
ether_addr_copy(addr, peer->addr);
peer->keys[i] = NULL; break;
}
}
if (i < ARRAY_SIZE(peer->keys)) break;
}
spin_unlock_bh(&ar->data_lock);
if (i == ARRAY_SIZE(peer->keys)) break; /* key flags are not required to delete the key */
ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, flags); if (ret < 0 && first_errno == 0)
first_errno = ret;
if (ret)
ath10k_warn(ar, "failed to remove key for %pM: %d\n",
addr, ret);
}
static u8 ath10k_parse_mpdudensity(u8 mpdudensity)
{ /* * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": * 0 for no restriction * 1 for 1/4 us * 2 for 1/2 us * 3 for 1 us * 4 for 2 us * 5 for 4 us * 6 for 8 us * 7 for 16 us
*/ switch (mpdudensity) { case 0: return 0; case 1: case 2: case 3: /* Our lower layer calculations limit our precision to * 1 microsecond
*/ return 1; case 4: return 2; case 5: return 4; case 6: return 8; case 7: return 16; default: return 0;
}
}
if (test_bit(WMI_SERVICE_SYNC_DELETE_CMDS, ar->wmi.svc_map)) {
ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr); if (ret) {
ath10k_warn(ar, "failed wait for peer deleted"); return;
}
time_left = wait_for_completion_timeout(&ar->peer_delete_done,
5 * HZ); if (!time_left)
ath10k_warn(ar, "Timeout in receiving peer delete response\n");
}
}
/* Each vdev consumes a peer entry as well. */ if (ar->num_peers + list_count_nodes(&ar->arvifs) >= ar->max_num_peers) return -ENOBUFS;
ret = ath10k_wmi_peer_create(ar, vdev_id, addr, peer_type); if (ret) {
ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
addr, vdev_id, ret); return ret;
}
ret = ath10k_wait_for_peer_created(ar, vdev_id, addr); if (ret) {
ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n",
addr, vdev_id, ret); return ret;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, vdev_id, addr); if (!peer) {
spin_unlock_bh(&ar->data_lock);
ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
addr, vdev_id);
ath10k_wait_for_peer_delete_done(ar, vdev_id, addr); return -ENOENT;
}
param = ar->wmi.pdev_param->sta_kickout_th;
ret = ath10k_wmi_pdev_set_param(ar, param,
ATH10K_KICKOUT_THRESHOLD); if (ret) {
ath10k_warn(ar, "failed to set kickout threshold on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
ATH10K_KEEPALIVE_MIN_IDLE); if (ret) {
ath10k_warn(ar, "failed to set keepalive minimum idle time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
ATH10K_KEEPALIVE_MAX_IDLE); if (ret) {
ath10k_warn(ar, "failed to set keepalive maximum idle time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
ATH10K_KEEPALIVE_MAX_UNRESPONSIVE); if (ret) {
ath10k_warn(ar, "failed to set keepalive maximum unresponsive time on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
/* Double check that peer is properly un-referenced from * the peer_map
*/ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) { if (ar->peer_map[i] == peer) {
ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %p idx %d)\n",
peer->addr, peer, i);
ar->peer_map[i] = NULL;
}
}
/* TODO setup this dynamically, what in case we * don't have any vifs?
*/
arg.channel.mode = chan_to_phymode(chandef);
arg.channel.chan_radar =
!!(channel->flags & IEEE80211_CHAN_RADAR);
ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) {
ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n",
vdev_id, ret); return ret;
}
ret = ath10k_vdev_setup_sync(ar); if (ret) {
ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n",
vdev_id, ret); return ret;
}
ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); if (ret) {
ath10k_warn(ar, "failed to put up monitor vdev %i: %d\n",
vdev_id, ret); goto vdev_stop;
}
vdev_stop:
ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret)
ath10k_warn(ar, "failed to stop monitor vdev %i after start failure: %d\n",
ar->monitor_vdev_id, ret);
return ret;
}
staticint ath10k_monitor_vdev_stop(struct ath10k *ar)
{ int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id); if (ret)
ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n",
ar->monitor_vdev_id, ret);
staticbool ath10k_mac_monitor_vdev_is_needed(struct ath10k *ar)
{ int num_ctx;
/* At least one chanctx is required to derive a channel to start * monitor vdev on.
*/
num_ctx = ath10k_mac_num_chanctxs(ar); if (num_ctx == 0) returnfalse;
/* If there's already an existing special monitor interface then don't * bother creating another monitor vdev.
*/ if (ar->monitor_arvif) returnfalse;
staticbool ath10k_mac_monitor_vdev_is_allowed(struct ath10k *ar)
{ int num_ctx;
num_ctx = ath10k_mac_num_chanctxs(ar);
/* FIXME: Current interface combinations and cfg80211/mac80211 code * shouldn't allow this but make sure to prevent handling the following * case anyway since multi-channel DFS hasn't been tested at all.
*/ if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags) && num_ctx > 1) returnfalse;
staticvoid ath10k_recalc_radar_detection(struct ath10k *ar)
{ int ret;
lockdep_assert_held(&ar->conf_mutex);
ath10k_stop_cac(ar);
if (!ath10k_mac_has_radar_enabled(ar)) return;
if (ar->num_started_vdevs > 0) return;
ret = ath10k_start_cac(ar); if (ret) { /* * Not possible to start CAC on current channel so starting * radiation is not allowed, make this channel DFS_UNAVAILABLE * by indicating that radar was detected.
*/
ath10k_warn(ar, "failed to start CAC: %d\n", ret);
ieee80211_radar_detected(ar->hw, NULL);
}
}
ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie); if (ret) {
ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) return 0;
if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
arvif->vdev_type != WMI_VDEV_TYPE_IBSS) return 0;
bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); if (!bcn) {
ath10k_warn(ar, "failed to get beacon template from mac80211\n"); return -EPERM;
}
ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn); if (ret) {
ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret);
kfree_skb(bcn); return ret;
}
/* P2P IE is inserted by firmware automatically (as configured above) * so remove it from the base beacon template to avoid duplicate P2P * IEs in beacon frames.
*/
ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
offsetof(struct ieee80211_mgmt,
u.beacon.variable));
/* When originally vdev is started during assign_vif_chanctx() some * information is missing, notably SSID. Firmware revisions with beacon * offloading require the SSID to be provided during vdev (re)start to * handle hidden SSID properly. * * Vdev restart must be done after vdev has been both started and * upped. Otherwise some firmware revisions (at least 10.2) fail to * deliver vdev restart response event causing timeouts during vdev * syncing in ath10k. * * Note: The vdev down/up and template reinstallation could be skipped * since only wmi-tlv firmware are known to have beacon offload and * wmi-tlv doesn't seem to misbehave like 10.2 wrt vdev restart * response delivery. It's probably more robust to keep it as is.
*/ if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) return 0;
if (WARN_ON(!arvif->is_started)) return -EINVAL;
if (WARN_ON(!arvif->is_up)) return -EINVAL;
if (WARN_ON(ath10k_mac_vif_chan(arvif->vif, &def))) return -EINVAL;
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); if (ret) {
ath10k_warn(ar, "failed to bring down ap vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
/* Vdev down reset beacon & presp templates. Reinstall them. Otherwise * firmware will crash upon vdev up.
*/
ret = ath10k_mac_setup_bcn_tmpl(arvif); if (ret) {
ath10k_warn(ar, "failed to update beacon template: %d\n", ret); return ret;
}
ret = ath10k_mac_setup_prb_tmpl(arvif); if (ret) {
ath10k_warn(ar, "failed to update presp template: %d\n", ret); return ret;
}
ret = ath10k_vdev_restart(arvif, &def); if (ret) {
ath10k_warn(ar, "failed to restart ap vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
arvif->bssid); if (ret) {
ath10k_warn(ar, "failed to bring up ap vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
return 0;
}
staticvoid ath10k_control_beaconing(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info)
{ struct ath10k *ar = arvif->ar; int ret = 0;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (!info->enable_beacon) {
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); if (ret)
ath10k_warn(ar, "failed to down vdev_id %i: %d\n",
arvif->vdev_id, ret);
staticvoid ath10k_control_ibss(struct ath10k_vif *arvif, struct ieee80211_vif *vif)
{ struct ath10k *ar = arvif->ar;
u32 vdev_param; int ret = 0;
lockdep_assert_held(&arvif->ar->conf_mutex);
if (!vif->cfg.ibss_joined) { if (is_zero_ether_addr(arvif->bssid)) return;
eth_zero_addr(arvif->bssid);
return;
}
vdev_param = arvif->ar->wmi.vdev_param->atim_window;
ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param,
ATH10K_DEFAULT_ATIM); if (ret)
ath10k_warn(ar, "failed to set IBSS ATIM for vdev %d: %d\n",
arvif->vdev_id, ret);
}
if (arvif->vif->type != NL80211_IFTYPE_STATION) return 0;
enable_ps = arvif->ps;
if (enable_ps && ath10k_mac_num_vifs_started(ar) > 1 &&
!test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT,
ar->running_fw->fw_file.fw_features)) {
ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n",
arvif->vdev_id);
enable_ps = false;
}
if (!arvif->is_started) { /* mac80211 can update vif powersave state while disconnected. * Firmware doesn't behave nicely and consumes more power than * necessary if PS is disabled on a non-started vdev. Hence * force-enable PS for non-running vdevs.
*/
psmode = WMI_STA_PS_MODE_ENABLED;
} elseif (enable_ps) {
psmode = WMI_STA_PS_MODE_ENABLED;
param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode); if (ret) {
ath10k_warn(ar, "failed to set PS Mode %d for vdev %d: %d\n",
psmode, arvif->vdev_id, ret); return ret;
}
if (arvif->vdev_type != WMI_VDEV_TYPE_STA) return 0;
if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map)) return 0;
/* Some firmware revisions have a bug and ignore the `enabled` field. * Instead use the interval to disable the keepalive.
*/
arg.vdev_id = arvif->vdev_id;
arg.enabled = 1;
arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME;
arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE;
ret = ath10k_wmi_sta_keepalive(ar, &arg); if (ret) {
ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
/* Firmware doesn't report beacon loss events repeatedly. If AP probe * (done by mac80211) succeeds but beacons do not resume then it * doesn't make sense to continue operation. Queue connection loss work * which can be cancelled when beacon is received.
*/
ieee80211_queue_delayed_work(hw, &arvif->connection_loss_work,
ATH10K_CONNECTION_LOSS_HZ);
}
/**********************/ /* Station management */ /**********************/
static u32 ath10k_peer_assoc_h_listen_intval(struct ath10k *ar, struct ieee80211_vif *vif)
{ /* Some firmware revisions have unstable STA powersave when listen * interval is set too high (e.g. 5). The symptoms are firmware doesn't * generate NullFunc frames properly even if buffered frames have been * indicated in Beacon TIM. Firmware would seldom wake up to pull * buffered frames. Often pinging the device from AP would simply fail. * * As a workaround set it to 1.
*/ if (vif->type == NL80211_IFTYPE_STATION) return 1;
for (i = 0, n = 0, max_nss = 0; i < IEEE80211_HT_MCS_MASK_LEN * 8; i++) if ((ht_cap->mcs.rx_mask[i / 8] & BIT(i % 8)) &&
(ht_mcs_mask[i / 8] & BIT(i % 8))) {
max_nss = (i / 8) + 1;
arg->peer_ht_rates.rates[n++] = i;
}
/* * This is a workaround for HT-enabled STAs which break the spec * and have no HT capabilities RX mask (no HT RX MCS map). * * As per spec, in section 20.3.5 Modulation and coding scheme (MCS), * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs. * * Firmware asserts if such situation occurs.
*/ if (n == 0) {
arg->peer_ht_rates.num_rates = 8; for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
arg->peer_ht_rates.rates[i] = i;
} else {
arg->peer_ht_rates.num_rates = n;
arg->peer_num_spatial_streams = min(sta->deflink.rx_nss,
max_nss);
}
if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN |
WMI_AP_PS_UAPSD_AC2_TRIGGER_EN; if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN |
WMI_AP_PS_UAPSD_AC1_TRIGGER_EN; if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN |
WMI_AP_PS_UAPSD_AC0_TRIGGER_EN;
if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
max_sp = sta->max_sp;
ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
sta->addr,
WMI_AP_PS_PEER_PARAM_UAPSD,
uapsd); if (ret) {
ath10k_warn(ar, "failed to set ap ps peer param uapsd for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
sta->addr,
WMI_AP_PS_PEER_PARAM_MAX_SP,
max_sp); if (ret) {
ath10k_warn(ar, "failed to set ap ps peer param max sp for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
/* TODO setup this based on STA listen interval and * beacon interval. Currently we don't know * sta->listen_interval - mac80211 patch required. * Currently use 10 seconds
*/
ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
10); if (ret) {
ath10k_warn(ar, "failed to set ap ps peer param ageout time for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
}
return 0;
}
static u16
ath10k_peer_assoc_h_vht_limit(u16 tx_mcs_set, const u16 vht_mcs_limit[NL80211_VHT_NSS_MAX])
{ int idx_limit; int nss;
u16 mcs_map;
u16 mcs;
switch (idx_limit) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: default: /* see ath10k_mac_can_set_bitrate_mask() */
WARN_ON(1);
fallthrough; case -1:
mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED; break; case 7:
mcs = IEEE80211_VHT_MCS_SUPPORT_0_7; break; case 8:
mcs = IEEE80211_VHT_MCS_SUPPORT_0_8; break; case 9:
mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; break;
}
/* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to * zero in VHT IE. Using it would result in degraded throughput. * arg->peer_max_mpdu at this point contains HT max_mpdu so keep * it if VHT max_mpdu is smaller.
*/
arg->peer_max_mpdu = max(arg->peer_max_mpdu,
(1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
ampdu_factor)) - 1);
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80)
arg->peer_flags |= ar->wmi.peer_flags->bw80;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
arg->peer_flags |= ar->wmi.peer_flags->bw160;
/* Calculate peer NSS capability from VHT capabilities if STA * supports VHT.
*/ for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) {
vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >>
(2 * i) & 3;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) { switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: return MODE_11AC_VHT160; case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: return MODE_11AC_VHT80_80; default: /* not sure if this is a valid case? */ return MODE_11AC_VHT160;
}
}
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_80) return MODE_11AC_VHT80;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_40) return MODE_11AC_VHT40;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_20) return MODE_11AC_VHT20;
if (WARN_ON(param == WMI_VDEV_PARAM_UNSUPPORTED)) return 0;
/* The following logic is correct. If a remote STA advertises support * for being a beamformer then we should enable us being a beamformee.
*/
if (ar->vht_cap_info &
(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE;
}
if (ar->vht_cap_info &
(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER;
}
if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFEE)
value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE;
if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFER)
value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, value); if (ret) {
ath10k_warn(ar, "failed to submit vdev param txbf 0x%x: %d\n",
value, ret); return ret;
}
if (!ar->hw_params.dynamic_sar_support) {
ret = -EOPNOTSUPP; goto err;
}
if (!sar || sar->type != NL80211_SAR_TYPE_POWER ||
sar->num_sub_specs == 0) {
ret = -EINVAL; goto err;
}
sub_specs = sar->sub_specs;
/* 0dbm is not a practical value for ath10k, so use 0 * as no SAR limitation on it.
*/
ar->tx_power_2g_limit = 0;
ar->tx_power_5g_limit = 0;
/* note the power is in 0.25dbm unit, while ath10k uses * 0.5dbm unit.
*/ for (i = 0; i < sar->num_sub_specs; i++) { if (sub_specs->freq_range_index == 0)
ar->tx_power_2g_limit = sub_specs->power / 2; elseif (sub_specs->freq_range_index == 1)
ar->tx_power_5g_limit = sub_specs->power / 2;
sub_specs++;
}
ret = ath10k_mac_set_sar_power(ar); if (ret) {
ath10k_warn(ar, "failed to set sar power: %d", ret); goto err;
}
err:
mutex_unlock(&ar->conf_mutex); return ret;
}
/* can be called only in mac80211 callbacks due to `key_count` usage */ staticvoid ath10k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf)
{ struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_vht_cap vht_cap; struct wmi_peer_assoc_complete_arg peer_arg; struct ieee80211_sta *ap_sta; int ret;
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!ap_sta) {
ath10k_warn(ar, "failed to find station entry for bss %pM vdev %i\n",
bss_conf->bssid, arvif->vdev_id);
rcu_read_unlock(); return;
}
/* ap_sta must be accessed only within rcu section which must be left * before calling ath10k_setup_peer_smps() which might sleep.
*/
ht_cap = ap_sta->deflink.ht_cap;
vht_cap = ap_sta->deflink.vht_cap;
ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg); if (ret) {
ath10k_warn(ar, "failed to prepare peer assoc for %pM vdev %i: %d\n",
bss_conf->bssid, arvif->vdev_id, ret);
rcu_read_unlock(); return;
}
rcu_read_unlock();
ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) {
ath10k_warn(ar, "failed to run peer assoc for %pM vdev %i: %d\n",
bss_conf->bssid, arvif->vdev_id, ret); return;
}
ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap); if (ret) {
ath10k_warn(ar, "failed to setup peer SMPS for vdev %i: %d\n",
arvif->vdev_id, ret); return;
}
ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap); if (ret) {
ath10k_warn(ar, "failed to recalc txbf for vdev %i on bss %pM: %d\n",
arvif->vdev_id, bss_conf->bssid, ret); return;
}
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n",
arvif->vdev_id, bss_conf->bssid, vif->cfg.aid);
ret = ath10k_wmi_pdev_set_param(ar,
ar->wmi.pdev_param->peer_stats_info_enable, 1); if (ret)
ath10k_warn(ar, "failed to enable peer stats info: %d\n", ret);
ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); if (ret) {
ath10k_warn(ar, "failed to set vdev %d up: %d\n",
arvif->vdev_id, ret); return;
}
arvif->is_up = true;
ath10k_mac_set_sar_power(ar);
/* Workaround: Some firmware revisions (tested with qca6174 * WLAN.RM.2.0-00073) have buggy powersave state machine and must be * poked with peer param command.
*/
ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, arvif->bssid,
ar->wmi.peer_param->dummy_var, 1); if (ret) {
ath10k_warn(ar, "failed to poke peer %pM param for ps workaround on vdev %i: %d\n",
arvif->bssid, arvif->vdev_id, ret); return;
}
}
/* Assign default value(-1) to newly connected station. * This is to identify station specific tid configuration not * configured for the station.
*/
arsta->retry_long[i] = -1;
arsta->noack[i] = -1;
arsta->ampdu[i] = -1;
ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, &arg); if (ret) {
ath10k_warn(ar, "failed to set per tid retry/aggr config for sta %pM: %d\n",
sta->addr, ret); return ret;
}
ret = ath10k_peer_assoc_prepare(ar, vif, sta, &peer_arg); if (ret) {
ath10k_warn(ar, "failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret); return ret;
}
ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) {
ath10k_warn(ar, "failed to run peer assoc for STA %pM vdev %i: %d\n",
sta->addr, arvif->vdev_id, ret); return ret;
}
/* Re-assoc is run only to update supported rates for given station. It * doesn't make much sense to reconfigure the peer completely.
*/ if (!reassoc) {
ret = ath10k_setup_peer_smps(ar, arvif, sta->addr,
&sta->deflink.ht_cap); if (ret) {
ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
arvif->vdev_id, ret); return ret;
}
ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta); if (ret) {
ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
sta->addr, arvif->vdev_id, ret); return ret;
}
if (!sta->wme) {
arvif->num_legacy_stations++;
ret = ath10k_recalc_rtscts_prot(arvif); if (ret) {
ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
arvif->vdev_id, ret); return ret;
}
}
/* Plumb cached keys only for static WEP */ if ((arvif->def_wep_key_idx != -1) && (!sta->tdls)) {
ret = ath10k_install_peer_wep_keys(arvif, sta->addr); if (ret) {
ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
}
}
if (!test_bit(WMI_SERVICE_PEER_TID_CONFIGS_SUPPORT, ar->wmi.svc_map)) return ret;
if (!sta->wme) {
arvif->num_legacy_stations--;
ret = ath10k_recalc_rtscts_prot(arvif); if (ret) {
ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
arvif->vdev_id, ret); return ret;
}
}
ret = ath10k_clear_peer_keys(arvif, sta->addr); if (ret) {
ath10k_warn(ar, "failed to clear all peer wep keys for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
/* the firmware is ignoring the "radar" flag of the * channel and is scanning actively using Probe Requests * on "Radar detection"/DFS channels which are not * marked as "available"
*/
ch->passive |= ch->chan_radar;
/* FIXME: why use only legacy modes, why not any * HT/VHT modes? Would that even make any * difference?
*/ if (channel->band == NL80211_BAND_2GHZ)
ch->mode = MODE_11G; else
ch->mode = MODE_11A;
if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN)) continue;
/* Target allows setting up per-band regdomain but ath_common provides * a combined one only
*/
ret = ath10k_wmi_pdev_set_regdomain(ar,
regpair->reg_domain,
regpair->reg_domain, /* 2ghz */
regpair->reg_domain, /* 5ghz */
regpair->reg_2ghz_ctl,
regpair->reg_5ghz_ctl,
wmi_dfs_reg); if (ret)
ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret);
}
staticvoid ath10k_mac_update_channel_list(struct ath10k *ar, struct ieee80211_supported_band *band)
{ int i;
if (ar->low_5ghz_chan && ar->high_5ghz_chan) { for (i = 0; i < band->n_channels; i++) { if (band->channels[i].center_freq < ar->low_5ghz_chan ||
band->channels[i].center_freq > ar->high_5ghz_chan)
band->channels[i].flags |=
IEEE80211_CHAN_DISABLED;
}
}
}
if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) {
ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs region 0x%x\n",
request->dfs_region);
result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector,
request->dfs_region); if (!result)
ath10k_warn(ar, "DFS region 0x%X not supported, will trigger radar for every pulse\n",
request->dfs_region);
}
mutex_lock(&ar->conf_mutex); if (ar->state == ATH10K_STATE_ON)
ath10k_regd_update(ar);
mutex_unlock(&ar->conf_mutex);
if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY)
ath10k_mac_update_channel_list(ar,
ar->hw->wiphy->bands[NL80211_BAND_5GHZ]);
}
if (IEEE80211_SKB_CB(skb)->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) return ATH10K_HW_TXRX_ETHERNET;
if (!vif || vif->type == NL80211_IFTYPE_MONITOR) return ATH10K_HW_TXRX_RAW;
if (ieee80211_is_mgmt(fc)) return ATH10K_HW_TXRX_MGMT;
/* Workaround: * * NullFunc frames are mostly used to ping if a client or AP are still * reachable and responsive. This implies tx status reports must be * accurate - otherwise either mac80211 or userspace (e.g. hostapd) can * come to a conclusion that the other end disappeared and tear down * BSS connection or it can never disconnect from BSS/client (which is * the case). * * Firmware with HTT older than 3.0 delivers incorrect tx status for * NullFunc frames to driver. However there's a HTT Mgmt Tx command * which seems to deliver correct tx reports for NullFunc frames. The * downside of using it is it ignores client powersave state so it can * end up disconnecting sleeping clients in AP mode. It should fix STA * mode though because AP don't sleep.
*/ if (ar->htt.target_version_major < 3 &&
(ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) &&
!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
ar->running_fw->fw_file.fw_features)) return ATH10K_HW_TXRX_MGMT;
/* Workaround: * * Some wmi-tlv firmwares for qca6174 have broken Tx key selection for * NativeWifi txmode - it selects AP key instead of peer key. It seems * to work with Ethernet txmode so use it. * * FIXME: Check if raw mode works with TDLS.
*/ if (ieee80211_is_data_present(fc) && sta && sta->tdls) return ATH10K_HW_TXRX_ETHERNET;
if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags) ||
skb_cb->flags & ATH10K_SKB_F_RAW_TX) return ATH10K_HW_TXRX_RAW;
/* Some firmware revisions don't handle sending QoS NullFunc well. * These frames are mainly used for CQM purposes so it doesn't really * matter whether QoS NullFunc or NullFunc are sent.
*/
hdr = (void *)skb->data; if (ieee80211_is_qos_nullfunc(hdr->frame_control))
cb->flags &= ~ATH10K_SKB_F_QOS;
if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
cb->flags |= ATH10K_SKB_F_QOS; /* Assume data frames are QoS */ goto finish_cb_fill;
}
if (!ath10k_tx_h_use_hwcrypto(vif, skb))
cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
if (ieee80211_is_mgmt(hdr->frame_control))
cb->flags |= ATH10K_SKB_F_MGMT;
if (ieee80211_is_data_qos(hdr->frame_control)) {
cb->flags |= ATH10K_SKB_F_QOS;
qos_ctl = ieee80211_get_qos_ctl(hdr);
tid = (*qos_ctl) & IEEE80211_QOS_CTL_TID_MASK;
if (arvif->noack[tid] == WMI_PEER_TID_CONFIG_NOACK)
noack = true;
if (sta) {
arsta = (struct ath10k_sta *)sta->drv_priv;
if (arsta->noack[tid] == WMI_PEER_TID_CONFIG_NOACK)
noack = true;
if (arsta->noack[tid] == WMI_PEER_TID_CONFIG_ACK)
noack = false;
}
if (noack)
cb->flags |= ATH10K_SKB_F_NOACK_TID;
}
/* Data frames encrypted in software will be posted to firmware * with tx encap mode set to RAW. Ex: Multicast traffic generated * for a specific VLAN group will always be encrypted in software.
*/ if (is_data && ieee80211_has_protected(hdr->frame_control) &&
!info->control.hw_key) {
cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
cb->flags |= ATH10K_SKB_F_RAW_TX;
}
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
{ /* FIXME: Not really sure since when the behaviour changed. At some * point new firmware stopped requiring creation of peer entries for * offchannel tx (and actually creating them causes issues with wmi-htc * tx credit replenishment and reliability). Assuming it's at least 3.4 * because that's when the `freq` was introduced to TX_FRM HTT command.
*/ return (ar->htt.target_version_major >= 3 &&
ar->htt.target_version_minor >= 4 &&
ar->running_fw->fw_file.htt_op_version == ATH10K_FW_HTT_OP_VERSION_TLV);
}
switch (txpath) { case ATH10K_MAC_TX_HTT:
ret = ath10k_htt_tx(htt, txmode, skb); break; case ATH10K_MAC_TX_HTT_MGMT:
ret = ath10k_htt_mgmt_tx(htt, skb); break; case ATH10K_MAC_TX_WMI_MGMT:
ret = ath10k_mac_tx_wmi_mgmt(ar, skb); break; case ATH10K_MAC_TX_UNKNOWN:
WARN_ON_ONCE(1);
ret = -EINVAL; break;
}
if (ret) {
ath10k_warn(ar, "failed to transmit packet, dropping: %d\n",
ret);
ieee80211_free_txskb(ar->hw, skb);
}
return ret;
}
/* This function consumes the sk_buff regardless of return value as far as * caller is concerned so no freeing is necessary afterwards.
*/ staticint ath10k_mac_tx(struct ath10k *ar, struct ieee80211_vif *vif, enum ath10k_hw_txrx_mode txmode, enum ath10k_mac_tx_path txpath, struct sk_buff *skb, bool noque_offchan)
{ struct ieee80211_hw *hw = ar->hw; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); conststruct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); int ret;
/* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
switch (txmode) { case ATH10K_HW_TXRX_MGMT: case ATH10K_HW_TXRX_NATIVE_WIFI:
ath10k_tx_h_nwifi(hw, skb);
ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
ath10k_tx_h_seq_no(vif, skb); break; case ATH10K_HW_TXRX_ETHERNET: /* Convert 802.11->802.3 header only if the frame was earlier * encapsulated to 802.11 by mac80211. Otherwise pass it as is.
*/ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
ath10k_tx_h_8023(skb); break; case ATH10K_HW_TXRX_RAW: if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags) &&
!(skb_cb->flags & ATH10K_SKB_F_RAW_TX)) {
WARN_ON_ONCE(1);
ieee80211_free_txskb(hw, skb); return -EOPNOTSUPP;
}
}
if (!noque_offchan && info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { if (!ath10k_mac_tx_frm_has_freq(ar)) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %p len %d\n",
skb, skb->len);
/* FW requirement: We must create a peer before FW will send out * an offchannel frame. Otherwise the frame will be stuck and * never transmitted. We delete the peer upon tx completion. * It is unlikely that a peer for offchannel tx will already be * present. However it may be in some rare cases so account for that. * Otherwise we might remove a legitimate peer and break stuff.
*/
for (;;) {
skb = skb_dequeue(&ar->offchan_tx_queue); if (!skb) break;
mutex_lock(&ar->conf_mutex);
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %p len %d\n",
skb, skb->len);
/* It's safe to access vif and sta - conf_mutex guarantees that * sta_state() and remove_interface() are locked exclusively * out wrt to this offchannel worker.
*/
arvif = ath10k_get_arvif(ar, vdev_id); if (arvif) {
vif = arvif->vif;
sta = ieee80211_find_sta(vif, peer_addr);
} else {
vif = NULL;
sta = NULL;
}
/* No need to get locks */ if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) returntrue;
if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed) returntrue;
if (artxq->num_fw_queued < artxq->num_push_allowed) returntrue;
returnfalse;
}
/* Return estimated airtime in microsecond, which is calculated using last * reported TX rate. This is just a rough estimation because host driver has no * knowledge of the actual transmit rate, retries or aggregation. If actual * airtime can be reported by firmware, then delta between estimated and actual * airtime can be adjusted from deficit.
*/ #define IEEE80211_ATF_OVERHEAD 100 /* IFS + some slot time */ #define IEEE80211_ATF_OVERHEAD_IFS 16 /* IFS only */ static u16 ath10k_mac_update_airtime(struct ath10k *ar, struct ieee80211_txq *txq, struct sk_buff *skb)
{ struct ath10k_sta *arsta;
u32 pktlen;
u16 airtime = 0;
if (!txq || !txq->sta) return airtime;
if (test_bit(WMI_SERVICE_REPORT_AIRTIME, ar->wmi.svc_map)) return airtime;
pktlen = skb->len + 38; /* Assume MAC header 30, SNAP 8 for most case */ if (arsta->last_tx_bitrate) { /* airtime in us, last_tx_bitrate in 100kbps */
airtime = (pktlen * 8 * (1000 / 100))
/ arsta->last_tx_bitrate; /* overhead for media access time and IFS */
airtime += IEEE80211_ATF_OVERHEAD_IFS;
} else { /* This is mostly for throttle excessive BC/MC frames, and the * airtime/rate doesn't need be exact. Airtime of BC/MC frames * in 2G get some discount, which helps prevent very low rate * frames from being blocked for too long.
*/
airtime = (pktlen * 8 * (1000 / 100)) / 60; /* 6M */
airtime += IEEE80211_ATF_OVERHEAD;
}
spin_unlock_bh(&ar->data_lock);
ret = ath10k_wmi_stop_scan(ar, &arg); if (ret) {
ath10k_warn(ar, "failed to stop wmi scan: %d\n", ret); goto out;
}
ret = wait_for_completion_timeout(&ar->scan.completed, 3 * HZ); if (ret == 0) {
ath10k_warn(ar, "failed to receive scan abortion completion: timed out\n");
ret = -ETIMEDOUT;
} elseif (ret > 0) {
ret = 0;
}
out: /* Scan state should be updated upon scan completion but in case * firmware fails to deliver the event (for whatever reason) it is * desired to clean up scan state anyway. Firmware may have just * dropped the scan completion event delivery due to transport pipe * being overflown with data and/or it can recover on its own before * next scan request is submitted.
*/
spin_lock_bh(&ar->data_lock); if (ar->scan.state != ATH10K_SCAN_IDLE)
__ath10k_scan_finish(ar);
spin_unlock_bh(&ar->data_lock);
return ret;
}
staticvoid ath10k_scan_abort(struct ath10k *ar)
{ int ret;
lockdep_assert_held(&ar->conf_mutex);
spin_lock_bh(&ar->data_lock);
switch (ar->scan.state) { case ATH10K_SCAN_IDLE: /* This can happen if timeout worker kicked in and called * abortion while scan completion was being processed.
*/ break; case ATH10K_SCAN_STARTING: case ATH10K_SCAN_ABORTING:
ath10k_warn(ar, "refusing scan abortion due to invalid scan state: %s (%d)\n",
ath10k_scan_state_str(ar->scan.state),
ar->scan.state); break; case ATH10K_SCAN_RUNNING:
ar->scan.state = ATH10K_SCAN_ABORTING;
spin_unlock_bh(&ar->data_lock);
ret = ath10k_scan_stop(ar); if (ret)
ath10k_warn(ar, "failed to abort scan: %d\n", ret);
staticint ath10k_start_scan(struct ath10k *ar, conststruct wmi_start_scan_arg *arg)
{ int ret;
lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_wmi_start_scan(ar, arg); if (ret) return ret;
ret = wait_for_completion_timeout(&ar->scan.started, 1 * HZ); if (ret == 0) {
ret = ath10k_scan_stop(ar); if (ret)
ath10k_warn(ar, "failed to stop scan: %d\n", ret);
return -ETIMEDOUT;
}
/* If we failed to start the scan, return error code at * this point. This is probably due to some issue in the * firmware, but no need to wedge the driver due to that...
*/
spin_lock_bh(&ar->data_lock); if (ar->scan.state == ATH10K_SCAN_IDLE) {
spin_unlock_bh(&ar->data_lock); return -EINVAL;
}
spin_unlock_bh(&ar->data_lock);
ret = ath10k_htt_tx_inc_pending(htt); if (ret) {
ath10k_warn(ar, "failed to increase tx pending count: %d, dropping\n",
ret);
spin_unlock_bh(&ar->htt.tx_lock);
ieee80211_free_txskb(ar->hw, skb); return;
}
ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt, is_presp); if (ret) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "failed to increase tx mgmt pending count: %d, dropping\n",
ret);
ath10k_htt_tx_dec_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
ieee80211_free_txskb(ar->hw, skb); return;
}
spin_unlock_bh(&ar->htt.tx_lock);
}
ret = ath10k_mac_tx(ar, vif, txmode, txpath, skb, false); if (ret) {
ath10k_warn(ar, "failed to transmit frame: %d\n", ret); if (is_htt) {
spin_lock_bh(&ar->htt.tx_lock);
ath10k_htt_tx_dec_pending(htt); if (is_mgmt)
ath10k_htt_tx_mgmt_dec_pending(htt);
spin_unlock_bh(&ar->htt.tx_lock);
} return;
}
}
staticvoid ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
{ struct ath10k *ar = hw->priv; int ret;
u8 ac = txq->ac;
ath10k_htt_tx_txq_update(hw, txq); if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH) return;
spin_lock_bh(&ar->queue_lock[ac]);
ieee80211_txq_schedule_start(hw, ac);
txq = ieee80211_next_txq(hw, ac); if (!txq) goto out;
while (ath10k_mac_tx_can_push(hw, txq)) {
ret = ath10k_mac_tx_push_txq(hw, txq); if (ret < 0) break;
}
ieee80211_return_txq(hw, txq, false);
ath10k_htt_tx_txq_update(hw, txq);
out:
ieee80211_txq_schedule_end(hw, ac);
spin_unlock_bh(&ar->queue_lock[ac]);
}
/* Must not be called with conf_mutex held as workers can use that also. */ void ath10k_drain_tx(struct ath10k *ar)
{
lockdep_assert_not_held(&ar->conf_mutex);
/* make sure rcu-protected mac80211 tx path itself is drained */
synchronize_net();
staticbool ath10k_check_chain_mask(struct ath10k *ar, u32 cm, constchar *dbg)
{ /* It is not clear that allowing gaps in chainmask * is helpful. Probably it will not do what user * is hoping for, so warn in that case.
*/ if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0) returntrue;
ath10k_warn(ar, "mac %s antenna chainmask is invalid: 0x%x. Suggested values: 15, 7, 3, 1 or 0.\n",
dbg, cm); returnfalse;
}
staticint ath10k_mac_get_vht_cap_bf_sts(struct ath10k *ar)
{ int nsts = ar->vht_cap_info;
/* If firmware does not deliver to host number of space-time * streams supported, assume it support up to 4 BF STS and return * the value for VHT CAP: nsts-1)
*/ if (nsts == 0) return 3;
return nsts;
}
staticint ath10k_mac_get_vht_cap_bf_sound_dim(struct ath10k *ar)
{ int sound_dim = ar->vht_cap_info;
if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
val = ath10k_mac_get_vht_cap_bf_sts(ar);
val <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
val &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
vht_cap.cap |= val;
}
if (ar->vht_cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) {
val = ath10k_mac_get_vht_cap_bf_sound_dim(ar);
val <<= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
val &= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK;
vht_cap.cap |= val;
}
mcs_map = 0; for (i = 0; i < 8; i++) { if ((i < ar->num_rf_chains) && (ar->cfg_tx_chainmask & BIT(i)))
mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); else
mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
}
if (ar->cfg_tx_chainmask <= 1)
vht_cap.cap &= ~IEEE80211_VHT_CAP_TXSTBC;
/* If we are supporting 160Mhz or 80+80, then the NIC may be able to do * a restricted NSS for 160 or 80+80 vs what it can do for 80Mhz. Give * user-space a clue if that is the case.
*/ if ((vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) &&
(hw->vht160_mcs_rx_highest != 0 ||
hw->vht160_mcs_tx_highest != 0)) {
vht_cap.vht_mcs.rx_highest = cpu_to_le16(hw->vht160_mcs_rx_highest);
vht_cap.vht_mcs.tx_highest = cpu_to_le16(hw->vht160_mcs_tx_highest);
}
node = ar->dev->of_node; if (!node) return -ENOENT;
ret = of_property_read_string_index(node, "ext-fem-name", 0, &fem_name); if (ret) return -ENOENT;
/* * If external Front End module used in hardware, then default base band timing * parameter cannot be used since they were fine tuned for reference hardware, * so choosing different value suitable for that external FEM.
*/ if (!strcmp("microsemi-lx5586", fem_name)) {
bb_timing->bb_tx_timing = 0x00;
bb_timing->bb_xpa_timing = 0x0101;
} else { return -ENOENT;
}
/* * This makes sense only when restarting hw. It is harmless to call * unconditionally. This is necessary to make sure no HTT/WMI tx * commands will be submitted while restarting.
*/
ath10k_drain_tx(ar);
mutex_lock(&ar->conf_mutex);
switch (ar->state) { case ATH10K_STATE_OFF:
ar->state = ATH10K_STATE_ON; break; case ATH10K_STATE_RESTARTING:
ar->state = ATH10K_STATE_RESTARTED; break; case ATH10K_STATE_ON: case ATH10K_STATE_RESTARTED: case ATH10K_STATE_WEDGED:
WARN_ON(1);
ret = -EINVAL; goto err; case ATH10K_STATE_UTF:
ret = -EBUSY; goto err;
}
spin_lock_bh(&ar->data_lock);
if (ar->hw_rfkill_on) {
ar->hw_rfkill_on = false;
spin_unlock_bh(&ar->data_lock); goto err;
}
spin_unlock_bh(&ar->data_lock);
ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL); if (ret) {
ath10k_err(ar, "Could not init hif: %d\n", ret); goto err_off;
}
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL,
&ar->normal_mode_fw); if (ret) {
ath10k_err(ar, "Could not init core: %d\n", ret); goto err_power_down;
}
if (ar->sys_cap_info & WMI_TLV_SYS_CAP_INFO_RFKILL) {
ret = ath10k_mac_rfkill_config(ar); if (ret && ret != -EOPNOTSUPP) {
ath10k_warn(ar, "failed to configure rfkill: %d", ret); goto err_core_stop;
}
}
param = ar->wmi.pdev_param->pmf_qos;
ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret) {
ath10k_warn(ar, "failed to enable PMF QOS: %d\n", ret); goto err_core_stop;
}
param = ar->wmi.pdev_param->dynamic_bw;
ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret) {
ath10k_warn(ar, "failed to enable dynamic BW: %d\n", ret); goto err_core_stop;
}
if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
ret = ath10k_wmi_scan_prob_req_oui(ar, ar->mac_addr); if (ret) {
ath10k_err(ar, "failed to set prob req oui: %i\n", ret); goto err_core_stop;
}
}
if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) {
ret = ath10k_wmi_adaptive_qcs(ar, true); if (ret) {
ath10k_warn(ar, "failed to enable adaptive qcs: %d\n",
ret); goto err_core_stop;
}
}
if (test_bit(WMI_SERVICE_BURST, ar->wmi.svc_map)) {
param = ar->wmi.pdev_param->burst_enable;
ret = ath10k_wmi_pdev_set_param(ar, param, 0); if (ret) {
ath10k_warn(ar, "failed to disable burst: %d\n", ret); goto err_core_stop;
}
}
param = ar->wmi.pdev_param->idle_ps_config;
ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret && ret != -EOPNOTSUPP) {
ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret); goto err_core_stop;
}
/* * By default FW set ARP frames ac to voice (6). In that case ARP * exchange is not working properly for UAPSD enabled AP. ARP requests * which arrives with access category 0 are processed by network stack * and send back with access category 0, but FW changes access category * to 6. Set ARP frames access category to best effort (0) solves * this problem.
*/
param = ar->wmi.pdev_param->arp_ac_override;
ret = ath10k_wmi_pdev_set_param(ar, param, 0); if (ret) {
ath10k_warn(ar, "failed to set arp ac override parameter: %d\n",
ret); goto err_core_stop;
}
if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_ADAPTIVE_CCA,
ar->running_fw->fw_file.fw_features)) {
ret = ath10k_wmi_pdev_enable_adaptive_cca(ar, 1,
WMI_CCA_DETECT_LEVEL_AUTO,
WMI_CCA_DETECT_MARGIN_AUTO); if (ret) {
ath10k_warn(ar, "failed to enable adaptive cca: %d\n",
ret); goto err_core_stop;
}
}
param = ar->wmi.pdev_param->ani_enable;
ret = ath10k_wmi_pdev_set_param(ar, param, 1); if (ret) {
ath10k_warn(ar, "failed to enable ani by default: %d\n",
ret); goto err_core_stop;
}
ar->ani_enabled = true;
if (ath10k_peer_stats_enabled(ar)) {
param = ar->wmi.pdev_param->peer_stats_update_period;
ret = ath10k_wmi_pdev_set_param(ar, param,
PEER_DEFAULT_STATS_UPDATE_PERIOD); if (ret) {
ath10k_warn(ar, "failed to set peer stats period : %d\n",
ret); goto err_core_stop;
}
}
param = ar->wmi.pdev_param->enable_btcoex; if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map) &&
test_bit(ATH10K_FW_FEATURE_BTCOEX_PARAM,
ar->running_fw->fw_file.fw_features) &&
ar->coex_support) {
ret = ath10k_wmi_pdev_set_param(ar, param, 0); if (ret) {
ath10k_warn(ar, "failed to set btcoex param: %d\n", ret); goto err_core_stop;
}
clear_bit(ATH10K_FLAG_BTCOEX, &ar->dev_flags);
}
if (test_bit(WMI_SERVICE_BB_TIMING_CONFIG_SUPPORT, ar->wmi.svc_map)) {
ret = __ath10k_fetch_bb_timing_dt(ar, &bb_timing); if (!ret) {
ret = ath10k_wmi_pdev_bb_timing(ar, &bb_timing); if (ret) {
ath10k_warn(ar, "failed to set bb timings: %d\n",
ret); goto err_core_stop;
}
}
}
mutex_lock(&ar->conf_mutex); if (ar->state != ATH10K_STATE_OFF) { if (!ar->hw_rfkill_on) { /* If the current driver state is RESTARTING but not yet * fully RESTARTED because of incoming suspend event, * then ath10k_halt() is already called via * ath10k_core_restart() and should not be called here.
*/ if (ar->state != ATH10K_STATE_RESTARTING) {
ath10k_halt(ar);
} else { /* Suspending here, because when in RESTARTING * state, ath10k_core_stop() skips * ath10k_wait_for_suspend().
*/
opt = WMI_PDEV_SUSPEND_AND_DISABLE_INTR;
ath10k_wait_for_suspend(ar, opt);
}
}
ar->state = ATH10K_STATE_OFF;
}
mutex_unlock(&ar->conf_mutex);
vdev_param = ar->wmi.vdev_param->tx_encap_type;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
ATH10K_HW_TXRX_NATIVE_WIFI); /* 10.X firmware does not support this VDEV parameter. Do not warn */ if (ret && ret != -EOPNOTSUPP) {
ath10k_warn(ar, "failed to set vdev %i TX encapsulation: %d\n",
arvif->vdev_id, ret);
}
}
/* * TODO: * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE, * because we will send mgmt frames without CCK. This requirement * for P2P_FIND/GO_NEG should be handled by checking CCK flag * in the TX packet.
*/ staticint ath10k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{ struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = (void *)vif->drv_priv; struct ath10k_peer *peer; enum wmi_sta_powersave_param param; int ret = 0;
u32 value; int bit; int i;
u32 vdev_param;
for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
arvif->bitrate_mask.control[i].legacy = 0xffffffff;
memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, sizeof(arvif->bitrate_mask.control[i].ht_mcs));
memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, sizeof(arvif->bitrate_mask.control[i].vht_mcs));
}
if (ar->num_peers >= ar->max_num_peers) {
ath10k_warn(ar, "refusing vdev creation due to insufficient peer entry resources in firmware\n");
ret = -ENOBUFS; goto err;
}
if (ar->free_vdev_map == 0) {
ath10k_warn(ar, "Free vdev map is empty, no more interfaces allowed.\n");
ret = -EBUSY; goto err;
}
bit = __ffs64(ar->free_vdev_map);
switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE:
arvif->vdev_type = WMI_VDEV_TYPE_STA;
arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
(ar, WMI_VDEV_SUBTYPE_P2P_DEVICE); break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION:
arvif->vdev_type = WMI_VDEV_TYPE_STA; if (vif->p2p)
arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
(ar, WMI_VDEV_SUBTYPE_P2P_CLIENT); break; case NL80211_IFTYPE_ADHOC:
arvif->vdev_type = WMI_VDEV_TYPE_IBSS; break; case NL80211_IFTYPE_MESH_POINT: if (test_bit(WMI_SERVICE_MESH_11S, ar->wmi.svc_map)) {
arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
(ar, WMI_VDEV_SUBTYPE_MESH_11S);
} elseif (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
ret = -EINVAL;
ath10k_warn(ar, "must load driver with rawmode=1 to add mesh interfaces\n"); goto err;
}
arvif->vdev_type = WMI_VDEV_TYPE_AP; break; case NL80211_IFTYPE_AP:
arvif->vdev_type = WMI_VDEV_TYPE_AP;
if (vif->p2p)
arvif->vdev_subtype = ath10k_wmi_get_vdev_subtype
(ar, WMI_VDEV_SUBTYPE_P2P_GO); break; case NL80211_IFTYPE_MONITOR:
arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; break; default:
WARN_ON(1); break;
}
/* Using vdev_id as queue number will make it very easy to do per-vif * tx queue locking. This shouldn't wrap due to interface combinations * but do a modulo for correctness sake and prevent using offchannel tx * queues for regular vif tx.
*/
vif->cab_queue = arvif->vdev_id % (IEEE80211_MAX_QUEUES - 1); for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++)
vif->hw_queue[i] = arvif->vdev_id % (IEEE80211_MAX_QUEUES - 1);
/* Some firmware revisions don't wait for beacon tx completion before * sending another SWBA event. This could lead to hardware using old * (freed) beacon data in some cases, e.g. tx credit starvation * combined with missed TBTT. This is very rare. * * On non-IOMMU-enabled hosts this could be a possible security issue * because hw could beacon some random data on the air. On * IOMMU-enabled hosts DMAR faults would occur in most cases and target * device would crash. * * Since there are no beacon tx completions (implicit nor explicit) * propagated to host the only workaround for this is to allocate a * DMA-coherent buffer for a lifetime of a vif and use it for all * beacon tx commands. Worst case for this approach is some beacons may * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap.
*/ if (vif->type == NL80211_IFTYPE_ADHOC ||
vif->type == NL80211_IFTYPE_MESH_POINT ||
vif->type == NL80211_IFTYPE_AP) { if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) {
arvif->beacon_buf = kmalloc(IEEE80211_MAX_FRAME_LEN,
GFP_KERNEL);
/* Using a kernel pointer in place of a dma_addr_t * token can lead to undefined behavior if that * makes it into cache management functions. Use a * known-invalid address token instead, which * avoids the warning and makes it easier to catch * bugs if it does end up getting used.
*/
arvif->beacon_paddr = DMA_MAPPING_ERROR;
} else {
arvif->beacon_buf =
dma_alloc_coherent(ar->dev,
IEEE80211_MAX_FRAME_LEN,
&arvif->beacon_paddr,
GFP_ATOMIC);
} if (!arvif->beacon_buf) {
ret = -ENOMEM;
ath10k_warn(ar, "failed to allocate beacon buffer: %d\n",
ret); goto err;
}
} if (test_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags))
arvif->nohwcrypt = true;
if (arvif->nohwcrypt &&
!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
ret = -EINVAL;
ath10k_warn(ar, "cryptmode module param needed for sw crypto\n"); goto err;
}
/* It makes no sense to have firmware do keepalives. mac80211 already * takes care of this with idle connection polling.
*/
ret = ath10k_mac_vif_disable_keepalive(arvif); if (ret) {
ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n",
arvif->vdev_id, ret); goto err_vdev_delete;
}
arvif->def_wep_key_idx = -1;
ath10k_update_vif_offload(hw, vif);
/* Configuring number of spatial stream for monitor interface is causing * target assert in qca9888 and qca6174.
*/ if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) {
u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
vdev_param = ar->wmi.vdev_param->nss;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
nss); if (ret) {
ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
arvif->vdev_id, ar->cfg_tx_chainmask, nss,
ret); goto err_vdev_delete;
}
}
if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
ret = ath10k_peer_create(ar, vif, NULL, arvif->vdev_id,
vif->addr, WMI_PEER_TYPE_DEFAULT); if (ret) {
ath10k_warn(ar, "failed to create vdev %i peer for AP/IBSS: %d\n",
arvif->vdev_id, ret); goto err_vdev_delete;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, arvif->vdev_id, vif->addr); if (!peer) {
ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
vif->addr, arvif->vdev_id);
spin_unlock_bh(&ar->data_lock);
ret = -ENOENT; goto err_peer_delete;
}
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath10k_mac_set_kickout(arvif); if (ret) {
ath10k_warn(ar, "failed to set vdev %i kickout parameters: %d\n",
arvif->vdev_id, ret); goto err_peer_delete;
}
}
if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
param = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
param, value); if (ret) {
ath10k_warn(ar, "failed to set vdev %i RX wake policy: %d\n",
arvif->vdev_id, ret); goto err_peer_delete;
}
ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif); if (ret) {
ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
arvif->vdev_id, ret); goto err_peer_delete;
}
ret = ath10k_mac_vif_recalc_ps_poll_count(arvif); if (ret) {
ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
arvif->vdev_id, ret); goto err_peer_delete;
}
}
ret = ath10k_mac_set_txbf_conf(arvif); if (ret) {
ath10k_warn(ar, "failed to set txbf for vdev %d: %d\n",
arvif->vdev_id, ret); goto err_peer_delete;
}
ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); if (ret) {
ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n",
arvif->vdev_id, ret); goto err_peer_delete;
}
arvif->txpower = vif->bss_conf.txpower;
ret = ath10k_mac_txpower_recalc(ar); if (ret) {
ath10k_warn(ar, "failed to recalc tx power: %d\n", ret); goto err_peer_delete;
}
if (test_bit(WMI_SERVICE_RTT_RESPONDER_ROLE, ar->wmi.svc_map)) {
vdev_param = ar->wmi.vdev_param->rtt_responder_role;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
arvif->ftm_responder);
/* It is harmless to not set FTM role. Do not warn */ if (ret && ret != -EOPNOTSUPP)
ath10k_warn(ar, "failed to set vdev %i FTM Responder: %d\n",
arvif->vdev_id, ret);
}
if (vif->type == NL80211_IFTYPE_MONITOR) {
ar->monitor_arvif = arvif;
ret = ath10k_monitor_recalc(ar); if (ret) {
ath10k_warn(ar, "failed to recalc monitor: %d\n", ret); goto err_peer_delete;
}
}
spin_lock_bh(&ar->htt.tx_lock); if (!ar->tx_paused)
ieee80211_wake_queue(ar->hw, arvif->vdev_id);
spin_unlock_bh(&ar->htt.tx_lock);
ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id); if (ret)
ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
arvif->vdev_id, ret);
ret = ath10k_vdev_delete_sync(ar); if (ret) {
ath10k_warn(ar, "Error in receiving vdev delete response: %d\n", ret); goto out;
}
/* Some firmware revisions don't notify host about self-peer removal * until after associated vdev is deleted.
*/ if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
ret = ath10k_wait_for_peer_deleted(ar, arvif->vdev_id,
vif->addr); if (ret)
ath10k_warn(ar, "failed to remove AP self-peer on vdev %i: %d\n",
arvif->vdev_id, ret);
spin_lock_bh(&ar->data_lock); for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
peer = ar->peer_map[i]; if (!peer) continue;
if (peer->vif == vif) {
ath10k_warn(ar, "found vif peer %pM entry on vdev %i after it was supposedly removed\n",
vif->addr, arvif->vdev_id);
peer->vif = NULL;
}
}
/* Clean this up late, less opportunity for firmware to access * DMA memory we have deleted.
*/
ath10k_mac_vif_beacon_cleanup(arvif);
spin_unlock_bh(&ar->data_lock);
if (ret)
ath10k_warn(ar, "failed to set beacon interval for vdev %d: %i\n",
arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_BEACON) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "vdev %d set beacon tx mode to staggered\n",
arvif->vdev_id);
pdev_param = ar->wmi.pdev_param->beacon_tx_mode;
ret = ath10k_wmi_pdev_set_param(ar, pdev_param,
WMI_BEACON_STAGGERED_MODE); if (ret)
ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n",
arvif->vdev_id, ret);
ret = ath10k_mac_setup_bcn_tmpl(arvif); if (ret)
ath10k_warn(ar, "failed to update beacon template: %d\n",
ret);
if (ieee80211_vif_is_mesh(vif)) { /* mesh doesn't use SSID but firmware needs it */
arvif->u.ap.ssid_len = 4;
memcpy(arvif->u.ap.ssid, "mesh", arvif->u.ap.ssid_len);
}
}
if (changed & BSS_CHANGED_AP_PROBE_RESP) {
ret = ath10k_mac_setup_prb_tmpl(arvif); if (ret)
ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n",
arvif->vdev_id, ret);
}
if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
arvif->dtim_period = info->dtim_period;
vdev_param = ar->wmi.vdev_param->dtim_period;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
arvif->dtim_period); if (ret)
ath10k_warn(ar, "failed to set dtim period for vdev %d: %i\n",
arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_BEACON_ENABLED)
ath10k_control_beaconing(arvif, info);
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
arvif->use_cts_prot = info->use_cts_prot;
ret = ath10k_recalc_rtscts_prot(arvif); if (ret)
ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
arvif->vdev_id, ret);
if (ath10k_mac_can_set_cts_prot(arvif)) {
ret = ath10k_mac_set_cts_prot(arvif); if (ret)
ath10k_warn(ar, "failed to set cts protection for vdev %d: %d\n",
arvif->vdev_id, ret);
}
}
if (changed & BSS_CHANGED_ERP_SLOT) { if (info->use_short_slot)
slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */
vdev_param = ar->wmi.vdev_param->slot_time;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
slottime); if (ret)
ath10k_warn(ar, "failed to set erp slot for vdev %d: %i\n",
arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_ERP_PREAMBLE) { if (info->use_short_preamble)
preamble = WMI_VDEV_PREAMBLE_SHORT; else
preamble = WMI_VDEV_PREAMBLE_LONG;
vdev_param = ar->wmi.vdev_param->preamble;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
preamble); if (ret)
ath10k_warn(ar, "failed to set preamble for vdev %d: %i\n",
arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_ASSOC) { if (vif->cfg.assoc) { /* Workaround: Make sure monitor vdev is not running * when associating to prevent some firmware revisions * (e.g. 10.1 and 10.2) from crashing.
*/ if (ar->monitor_started)
ath10k_monitor_stop(ar);
ath10k_bss_assoc(hw, vif, info);
ath10k_monitor_recalc(ar);
} else {
ath10k_bss_disassoc(hw, vif);
}
}
vdev_param = ar->wmi.vdev_param->mcast_data_rate;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
vdev_param, rate); if (ret)
ath10k_warn(ar, "failed to set mcast rate on vdev %i: %d\n",
arvif->vdev_id, ret);
vdev_param = ar->wmi.vdev_param->bcast_data_rate;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
vdev_param, rate); if (ret)
ath10k_warn(ar, "failed to set bcast rate on vdev %i: %d\n",
arvif->vdev_id, ret);
}
if (changed & BSS_CHANGED_BASIC_RATES &&
!ath10k_mac_vif_chan(arvif->vif, &def))
ath10k_recalculate_mgmt_rate(ar, vif, &def);
/* This function should never be called if setting the coverage class * is not supported on this hardware.
*/ if (!ar->hw_params.hw_ops->set_coverage_class) {
WARN_ON_ONCE(1); return;
}
ar->hw_params.hw_ops->set_coverage_class(ar, -1, value);
}
if (req->n_channels) {
arg->n_channels = req->n_channels; for (i = 0; i < arg->n_channels; i++)
arg->channels[i] = req->channels[i]->center_freq;
}
/* if duration is set, default dwell times will be overwritten */ if (req->duration) {
arg->dwell_time_active = req->duration;
arg->dwell_time_passive = req->duration;
arg->burst_duration_ms = req->duration;
/* 10.1 firmware branch requires default key index to be set to group * key index after installing it. Otherwise FW/HW Txes corrupted * frames with multi-vif APs. This is not required for main firmware * branch (e.g. 636). * * This is also needed for 636 fw for IBSS-RSN to work more reliably. * * FIXME: It remains unknown if this is required for multi-vif STA * interfaces on 10.1.
*/
if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
arvif->vdev_type != WMI_VDEV_TYPE_IBSS) return;
if (key->cipher == WLAN_CIPHER_SUITE_WEP40) return;
if (key->cipher == WLAN_CIPHER_SUITE_WEP104) return;
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) return;
if (cmd != SET_KEY) return;
ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
key->keyidx); if (ret)
ath10k_warn(ar, "failed to set vdev %i group key as default key: %d\n",
arvif->vdev_id, ret);
}
/* this one needs to be done in software */ if (key->cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
key->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
key->cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256) return 1;
if (arvif->nohwcrypt) return 1;
if (key->keyidx > WMI_MAX_KEY_INDEX) return -ENOSPC;
if (is_wep) { if (cmd == SET_KEY)
arvif->wep_keys[key->keyidx] = key; else
arvif->wep_keys[key->keyidx] = NULL;
}
/* the peer should not disappear in mid-way (unless FW goes awry) since * we already hold conf_mutex. we just make sure its there now.
*/
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr);
spin_unlock_bh(&ar->data_lock);
if (!peer) { if (cmd == SET_KEY) {
ath10k_warn(ar, "failed to install key for non-existent peer %pM\n",
peer_addr);
ret = -EOPNOTSUPP; gotoexit;
} else { /* if the peer doesn't exist there is no key to disable anymore */ gotoexit;
}
}
if (is_wep) { if (cmd == DISABLE_KEY)
ath10k_clear_vdev_key(arvif, key);
/* When WEP keys are uploaded it's possible that there are * stations associated already (e.g. when merging) without any * keys. Static WEP needs an explicit per-peer key upload.
*/ if (vif->type == NL80211_IFTYPE_ADHOC &&
cmd == SET_KEY)
ath10k_mac_vif_update_wep_key(arvif, key);
/* 802.1x never sets the def_wep_key_idx so each set_key() * call changes default tx key. * * Static WEP sets def_wep_key_idx via .set_default_unicast_key * after first set_key().
*/ if (cmd == SET_KEY && arvif->def_wep_key_idx == -1)
flags |= WMI_KEY_TX_USAGE;
}
ret = ath10k_install_key(arvif, key, cmd, peer_addr, flags); if (ret) {
WARN_ON(ret > 0);
ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
arvif->vdev_id, peer_addr, ret); gotoexit;
}
/* mac80211 sets static WEP keys as groupwise while firmware requires * them to be installed twice as both pairwise and groupwise.
*/ if (is_wep && !sta && vif->type == NL80211_IFTYPE_STATION) {
flags2 = flags;
flags2 &= ~WMI_KEY_GROUP;
flags2 |= WMI_KEY_PAIRWISE;
ret = ath10k_install_key(arvif, key, cmd, peer_addr, flags2); if (ret) {
WARN_ON(ret > 0);
ath10k_warn(ar, "failed to install (ucast) key for vdev %i peer %pM: %d\n",
arvif->vdev_id, peer_addr, ret);
ret2 = ath10k_install_key(arvif, key, DISABLE_KEY,
peer_addr, flags); if (ret2) {
WARN_ON(ret2 > 0);
ath10k_warn(ar, "failed to disable (mcast) key for vdev %i peer %pM: %d\n",
arvif->vdev_id, peer_addr, ret2);
} gotoexit;
}
}
if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) {
txpwr = 0;
} else {
txpwr = sta->deflink.txpwr.power; if (!txpwr) return -EINVAL;
}
if (txpwr > ATH10K_TX_POWER_MAX_VAL || txpwr < ATH10K_TX_POWER_MIN_VAL) return -EINVAL;
mutex_lock(&ar->conf_mutex);
ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
ar->wmi.peer_param->use_fixed_power, txpwr); if (ret) {
ath10k_warn(ar, "failed to set tx power for station ret: %d\n",
ret); goto out;
}
staticbool
ath10k_mac_bitrate_mask_has_single_rate(struct ath10k *ar, enum nl80211_band band, conststruct cfg80211_bitrate_mask *mask, int *vht_num_rates)
{ int num_rates = 0; int i, tmp;
if (!ath10k_mac_bitrate_mask_has_single_rate(ar, band, mask,
&vht_num_rates)) { return -EINVAL;
}
ret = ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask,
&rate, &nss, false); if (ret) {
ath10k_warn(ar, "failed to get single rate: %d\n",
ret); return ret;
}
*rate_ctrl_flag = rate;
if (sta && ath10k_mac_validate_rate_mask(ar, sta, *rate_ctrl_flag, nss)) return -EINVAL;
if (config_apply) {
ret = ath10k_wmi_set_per_peer_per_tid_cfg(ar, &arg); if (ret)
ath10k_warn(ar, "failed to set per tid config for sta %pM: %d\n",
sta->addr, ret);
}
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
ath10k_mac_txq_init(sta->txq[i]);
}
/* cancel must be done outside the mutex to avoid deadlock */ if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST)) {
cancel_work_sync(&arsta->update_wk);
cancel_work_sync(&arsta->tid_config_wk);
}
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) { /* * New station addition.
*/ enum wmi_peer_type peer_type = WMI_PEER_TYPE_DEFAULT;
u32 num_tdls_stations;
if (sta->tdls) { if (num_tdls_stations >= ar->max_num_tdls_vdevs) {
ath10k_warn(ar, "vdev %i exceeded maximum number of tdls vdevs %i\n",
arvif->vdev_id,
ar->max_num_tdls_vdevs);
ret = -ELNRNG; gotoexit;
}
peer_type = WMI_PEER_TYPE_TDLS;
}
ret = ath10k_mac_inc_num_stations(arvif, sta); if (ret) {
ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
ar->max_num_stations); gotoexit;
}
if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats),
GFP_KERNEL); if (!arsta->tx_stats) {
ath10k_mac_dec_num_stations(arvif, sta);
ret = -ENOMEM; gotoexit;
}
}
ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
sta->addr, peer_type); if (ret) {
ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
ath10k_mac_dec_num_stations(arvif, sta);
kfree(arsta->tx_stats); gotoexit;
}
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr); if (!peer) {
ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
vif->addr, arvif->vdev_id);
spin_unlock_bh(&ar->data_lock);
ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
ath10k_mac_dec_num_stations(arvif, sta);
kfree(arsta->tx_stats);
ret = -ENOENT; gotoexit;
}
ret = ath10k_wmi_update_fw_tdls_state(ar, arvif->vdev_id,
WMI_TDLS_ENABLE_ACTIVE); if (ret) {
ath10k_warn(ar, "failed to update fw tdls state on vdev %i: %i\n",
arvif->vdev_id, ret);
ath10k_peer_delete(ar, arvif->vdev_id,
sta->addr);
ath10k_mac_dec_num_stations(arvif, sta);
kfree(arsta->tx_stats); gotoexit;
}
ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id, sta,
WMI_TDLS_PEER_STATE_PEERING); if (ret) {
ath10k_warn(ar, "failed to update tdls peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
ath10k_mac_dec_num_stations(arvif, sta);
kfree(arsta->tx_stats);
if (sta->tdls) {
ret = ath10k_mac_tdls_peer_update(ar, arvif->vdev_id,
sta,
WMI_TDLS_PEER_STATE_TEARDOWN); if (ret)
ath10k_warn(ar, "failed to update tdls peer state for %pM state %d: %i\n",
sta->addr,
WMI_TDLS_PEER_STATE_TEARDOWN, ret);
}
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr); if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
sta->addr, arvif->vdev_id, ret);
ath10k_mac_dec_num_stations(arvif, sta);
spin_lock_bh(&ar->data_lock); for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
peer = ar->peer_map[i]; if (!peer) continue;
if (peer->sta == sta) {
ath10k_warn(ar, "found sta peer %pM (ptr %p id %d) entry on vdev %i after it was supposedly removed\n",
sta->addr, peer, i, arvif->vdev_id);
peer->sta = NULL;
/* Clean up the peer object as well since we * must have failed to do this above.
*/
ath10k_peer_map_cleanup(ar, peer);
}
}
spin_unlock_bh(&ar->data_lock);
if (ath10k_debug_is_extd_tx_stats_enabled(ar)) {
kfree(arsta->tx_stats);
arsta->tx_stats = NULL;
}
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
ath10k_mac_txq_unref(ar, sta->txq[i]);
if (!sta->tdls) gotoexit;
if (ath10k_mac_tdls_vif_stations_count(hw, vif)) gotoexit;
/* This was the last tdls peer in current vif */
ret = ath10k_wmi_update_fw_tdls_state(ar, arvif->vdev_id,
WMI_TDLS_DISABLE); if (ret) {
ath10k_warn(ar, "failed to update fw tdls state on vdev %i: %i\n",
arvif->vdev_id, ret);
}
} elseif (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC &&
(vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_MESH_POINT ||
vif->type == NL80211_IFTYPE_ADHOC)) { /* * New association.
*/
ath10k_dbg(ar, ATH10K_DBG_STA, "mac sta %pM associated\n",
sta->addr);
ret = ath10k_station_assoc(ar, vif, sta, false); if (ret)
ath10k_warn(ar, "failed to associate station %pM for vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
} elseif (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED &&
sta->tdls) { /* * Tdls station authorized.
*/
ath10k_dbg(ar, ATH10K_DBG_STA, "mac tdls sta %pM authorized\n",
sta->addr);
ret = ath10k_station_assoc(ar, vif, sta, false); if (ret) {
ath10k_warn(ar, "failed to associate tdls station %pM for vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret); gotoexit;
}
if (arvif->vdev_type != WMI_VDEV_TYPE_STA) return 0;
switch (ac) { case IEEE80211_AC_VO:
value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC3_TRIGGER_EN;
prio = 7;
acc = 3; break; case IEEE80211_AC_VI:
value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC2_TRIGGER_EN;
prio = 5;
acc = 2; break; case IEEE80211_AC_BE:
value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC1_TRIGGER_EN;
prio = 2;
acc = 1; break; case IEEE80211_AC_BK:
value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN |
WMI_STA_PS_UAPSD_AC0_TRIGGER_EN;
prio = 0;
acc = 0; break;
}
if (enable)
arvif->u.sta.uapsd |= value; else
arvif->u.sta.uapsd &= ~value;
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
WMI_STA_PS_PARAM_UAPSD,
arvif->u.sta.uapsd); if (ret) {
ath10k_warn(ar, "failed to set uapsd params: %d\n", ret); gotoexit;
}
if (arvif->u.sta.uapsd)
value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; else
value = WMI_STA_PS_RX_WAKE_POLICY_WAKE;
ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id,
WMI_STA_PS_PARAM_RX_WAKE_POLICY,
value); if (ret)
ath10k_warn(ar, "failed to set rx wake param: %d\n", ret);
ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif); if (ret) {
ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
ret = ath10k_mac_vif_recalc_ps_poll_count(arvif); if (ret) {
ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) ||
test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) { /* Only userspace can make an educated decision when to send * trigger frame. The following effectively disables u-UAPSD * autotrigger in firmware (which is enabled by default * provided the autotrigger service is available).
*/
ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id,
arvif->bssid, &arg, 1); if (ret) {
ath10k_warn(ar, "failed to set uapsd auto trigger %d\n",
ret); return ret;
}
}
switch (ac) { case IEEE80211_AC_VO:
p = &arvif->wmm_params.ac_vo; break; case IEEE80211_AC_VI:
p = &arvif->wmm_params.ac_vi; break; case IEEE80211_AC_BE:
p = &arvif->wmm_params.ac_be; break; case IEEE80211_AC_BK:
p = &arvif->wmm_params.ac_bk; break;
}
/* * The channel time duration programmed in the HW is in absolute * microseconds, while mac80211 gives the txop in units of * 32 microseconds.
*/
p->txop = params->txop * 32;
if (ar->wmi.ops->gen_vdev_wmm_conf) {
ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id,
&arvif->wmm_params); if (ret) {
ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n",
arvif->vdev_id, ret); gotoexit;
}
} else { /* This won't work well with multi-interface cases but it's * better than nothing.
*/
ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params); if (ret) {
ath10k_warn(ar, "failed to set wmm params: %d\n", ret); gotoexit;
}
}
ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd); if (ret)
ath10k_warn(ar, "failed to set sta uapsd: %d\n", ret);
ret = ath10k_mac_set_rts(arvif, value); if (ret) {
ath10k_warn(ar, "failed to set rts threshold for vdev %d: %d\n",
arvif->vdev_id, ret); break;
}
}
mutex_unlock(&ar->conf_mutex);
return ret;
}
staticint ath10k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, int radio_idx, u32 value)
{ /* Even though there's a WMI enum for fragmentation threshold no known * firmware actually implements it. Moreover it is not possible to rely * frame fragmentation to mac80211 because firmware clears the "more * fragments" bit in frame control making it impossible for remote * devices to reassemble frames. * * Hence implement a dummy callback just to say fragmentation isn't * supported. This effectively prevents mac80211 from doing frame * fragmentation in software.
*/ return -EOPNOTSUPP;
}
void ath10k_mac_wait_tx_complete(struct ath10k *ar)
{ bool skip; long time_left;
/* mac80211 doesn't care if we really xmit queued frames or not * we'll collect those frames either way if we stop/delete vdevs
*/
/* TODO: Implement this function properly * For now it is needed to reply to Probe Requests in IBSS mode. * Probably we need this information from FW.
*/ staticint ath10k_tx_last_beacon(struct ieee80211_hw *hw)
{ return 1;
}
if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return;
mutex_lock(&ar->conf_mutex);
/* If device failed to restart it will be in a different state, e.g. * ATH10K_STATE_WEDGED
*/ if (ar->state == ATH10K_STATE_RESTARTED) {
ath10k_info(ar, "device successfully recovered\n");
ar->state = ATH10K_STATE_ON;
ieee80211_wake_queues(ar->hw);
/* Due to firmware limitation in WMI_PEER_ASSOC_CMDID it is impossible * to express all VHT MCS rate masks. Effectively only the following * ranges can be used: none, 0-7, 0-8 and 0-9.
*/ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
vht_mcs = mask->control[band].vht_mcs[i];
switch (vht_mcs) { case 0: case BIT(8) - 1: case BIT(9) - 1: case BIT(10) - 1: break; default: if (!allow_pfr)
ath10k_warn(ar, "refusing bitrate mask with missing 0-7 VHT MCS rates\n"); returnfalse;
}
}
if (ath10k_mac_bitrate_mask_has_single_rate(ar, band, mask,
&vht_num_rates)) {
ret = ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask,
&rate, &nss, false); if (ret) {
ath10k_warn(ar, "failed to get single rate for vdev %i: %d\n",
arvif->vdev_id, ret); return ret;
}
} elseif (ath10k_mac_bitrate_mask_get_single_nss(ar, band, mask,
&single_nss)) {
rate = WMI_FIXED_RATE_NONE;
nss = single_nss;
} else {
rate = WMI_FIXED_RATE_NONE;
nss = min(ar->num_rf_chains,
max(ath10k_mac_max_ht_nss(ht_mcs_mask),
ath10k_mac_max_vht_nss(vht_mcs_mask)));
if (!ath10k_mac_can_set_bitrate_mask(ar, band, mask,
allow_pfr)) {
u8 vht_nss;
if (!allow_pfr || vht_num_rates != 1) return -EINVAL;
/* Reach here, firmware supports peer fixed rate and has * single vht rate, and don't update vif birate_mask, as * the rate only for specific peer.
*/
ath10k_mac_bitrate_mask_get_single_rate(ar, band, mask,
&vht_pfr,
&vht_nss, true);
update_bitrate_mask = false;
} else {
vht_pfr = 0;
}
ret = ath10k_mac_set_fixed_rate_params(arvif, rate, nss, sgi, ldpc); if (ret) {
ath10k_warn(ar, "failed to set fixed rate params on vdev %i: %d\n",
arvif->vdev_id, ret); gotoexit;
}
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac ampdu vdev_id %i sta %pM tid %u action %d\n",
arvif->vdev_id, sta->addr, tid, action);
switch (action) { case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: /* HTT AddBa/DelBa events trigger mac80211 Rx BA session * creation/removal. Do we need to verify this?
*/ return 0; case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_OPERATIONAL: /* Firmware offloads Tx aggregation entirely so deny mac80211 * Tx aggregation requests.
*/ return -EOPNOTSUPP;
}
/* Both locks are required because ar->rx_channel is modified. This * allows readers to hold either lock.
*/
lockdep_assert_held(&ar->conf_mutex);
lockdep_assert_held(&ar->data_lock);
WARN_ON(ctx && vifs);
WARN_ON(vifs && !n_vifs);
/* FIXME: Sort of an optimization and a workaround. Peers and vifs are * on a linked list now. Doing a lookup peer -> vif -> chanctx for each * ppdu on Rx may reduce performance on low-end systems. It should be * possible to make tables/hashmaps to speed the lookup up (be vary of * cpu data cache lines though regarding sizes) but to keep the initial * implementation simple and less intrusive fallback to the slow lookup * only for multi-channel cases. Single-channel cases will remain to * use the old channel derival and thus performance should not be * affected much.
*/
rcu_read_lock(); if (!ctx && ath10k_mac_num_chanctxs(ar) == 1) {
ieee80211_iter_chan_contexts_atomic(ar->hw,
ath10k_mac_get_any_chandef_iter,
&def);
if (vifs)
def = &vifs[0].new_ctx->def;
ar->rx_channel = def->chan;
} elseif ((ctx && ath10k_mac_num_chanctxs(ar) == 0) ||
(ctx && (ar->state == ATH10K_STATE_RESTARTED))) { /* During driver restart due to firmware assert, since mac80211 * already has valid channel context for given radio, channel * context iteration return num_chanctx > 0. So fix rx_channel * when restart is in progress.
*/
ar->rx_channel = ctx->def.chan;
} else {
ar->rx_channel = NULL;
}
rcu_read_unlock();
}
staticvoid
ath10k_mac_update_vif_chan(struct ath10k *ar, struct ieee80211_vif_chanctx_switch *vifs, int n_vifs)
{ struct ath10k_vif *arvif; int ret; int i;
lockdep_assert_held(&ar->conf_mutex);
/* First stop monitor interface. Some FW versions crash if there's a * lone monitor interface.
*/ if (ar->monitor_started)
ath10k_monitor_stop(ar);
for (i = 0; i < n_vifs; i++) {
arvif = (void *)vifs[i].vif->drv_priv;
/* This shouldn't really happen because channel switching should use * switch_vif_chanctx().
*/ if (WARN_ON(changed & IEEE80211_CHANCTX_CHANGE_CHANNEL)) goto unlock;
if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
ieee80211_iterate_active_interfaces_atomic(
hw,
ATH10K_ITER_NORMAL_FLAGS,
ath10k_mac_change_chanctx_cnt_iter,
&arg); if (arg.n_vifs == 0) goto radar;
arg.vifs = kcalloc(arg.n_vifs, sizeof(arg.vifs[0]),
GFP_KERNEL); if (!arg.vifs) goto radar;
/* No other actions are actually necessary. Firmware maintains channel * definitions per vdev internally and there's no host-side channel * context abstraction to configure, e.g. channel width.
*/
if (WARN_ON(arvif->is_started)) {
mutex_unlock(&ar->conf_mutex); return -EBUSY;
}
ret = ath10k_vdev_start(arvif, &ctx->def); if (ret) {
ath10k_warn(ar, "failed to start vdev %i addr %pM on freq %d: %d\n",
arvif->vdev_id, vif->addr,
ctx->def.chan->center_freq, ret); goto err;
}
arvif->is_started = true;
ret = ath10k_mac_vif_setup_ps(arvif); if (ret) {
ath10k_warn(ar, "failed to update vdev %i ps: %d\n",
arvif->vdev_id, ret); goto err_stop;
}
if (vif->type == NL80211_IFTYPE_MONITOR) {
ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, 0, vif->addr); if (ret) {
ath10k_warn(ar, "failed to up monitor vdev %i: %d\n",
arvif->vdev_id, ret); goto err_stop;
}
arvif->is_up = true;
}
if (ath10k_mac_can_set_cts_prot(arvif)) {
ret = ath10k_mac_set_cts_prot(arvif); if (ret)
ath10k_warn(ar, "failed to set cts protection for vdev %d: %d\n",
arvif->vdev_id, ret);
}
if (ath10k_peer_stats_enabled(ar) &&
ar->hw_params.tx_stats_over_pktlog) {
ar->pktlog_filter |= ATH10K_PKTLOG_PEER_STATS;
ret = ath10k_wmi_pdev_pktlog_enable(ar,
ar->pktlog_filter); if (ret) {
ath10k_warn(ar, "failed to enable pktlog %d\n", ret); goto err_stop;
}
}
ieee80211_iterate_active_interfaces_atomic(ar->hw,
ATH10K_ITER_RESUME_FLAGS,
ath10k_get_arvif_iter,
&arvif_iter); if (!arvif_iter.arvif) {
ath10k_warn(ar, "No VIF found for vdev %d\n", vdev_id); return NULL;
}
static u32 ath10k_mac_wrdd_get_mcc(struct ath10k *ar, union acpi_object *wrdd)
{ union acpi_object *mcc_pkg; union acpi_object *domain_type; union acpi_object *mcc_value;
u32 i;
root_handle = ACPI_HANDLE(ar->dev); if (!root_handle) return -EOPNOTSUPP;
status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); if (ACPI_FAILURE(status)) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "failed to get wrd method %d\n", status); return -EIO;
}
status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); if (ACPI_FAILURE(status)) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "failed to call wrdc %d\n", status); return -EIO;
}
alpha2_code = ath10k_mac_wrdd_get_mcc(ar, wrdd.pointer);
kfree(wrdd.pointer); if (!alpha2_code) return -EIO;
/* Do not add hardware supported ciphers before this line. * Allow software encryption for all chips. Don't forget to * update n_cipher_suites below.
*/
WLAN_CIPHER_SUITE_AES_CMAC,
WLAN_CIPHER_SUITE_BIP_CMAC_256,
WLAN_CIPHER_SUITE_BIP_GMAC_128,
WLAN_CIPHER_SUITE_BIP_GMAC_256,
/* Only QCA99x0 and QCA4019 variants support GCMP-128, GCMP-256 * and CCMP-256 in hardware.
*/
WLAN_CIPHER_SUITE_GCMP,
WLAN_CIPHER_SUITE_GCMP_256,
WLAN_CIPHER_SUITE_CCMP_256,
}; struct ieee80211_supported_band *band; void *channels; int ret;
if (!is_valid_ether_addr(ar->mac_addr)) {
ath10k_warn(ar, "invalid MAC address; choosing random\n");
eth_random_addr(ar->mac_addr);
}
SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr);
if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) {
ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
/* Firmware delivers WPS/P2P Probe Requests frames to driver so * that userspace (e.g. wpa_supplicant/hostapd) can generate * correct Probe Responses. This is more of a hack advert..
*/
ar->hw->wiphy->probe_resp_offload |=
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
}
if (test_bit(WMI_SERVICE_TDLS, ar->wmi.svc_map) ||
test_bit(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY, ar->wmi.svc_map)) {
ar->hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; if (test_bit(WMI_SERVICE_TDLS_WIDER_BANDWIDTH, ar->wmi.svc_map))
ieee80211_hw_set(ar->hw, TDLS_WIDER_BW);
}
if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, ar->wmi.svc_map))
ieee80211_hw_set(ar->hw, SUPPORTS_TDLS_BUFFER_STA);
if (ath10k_frame_mode == ATH10K_HW_TXRX_ETHERNET) { if (ar->wmi.vdev_param->tx_encap_type !=
WMI_VDEV_PARAM_UNSUPPORTED)
ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD);
}
if (test_bit(WMI_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
ar->wmi.svc_map)) {
ar->hw->wiphy->tid_config_support.vif |=
BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL);
}
ar->hw->wiphy->tid_config_support.peer =
ar->hw->wiphy->tid_config_support.vif;
ar->hw->wiphy->max_data_retry_count = ATH10K_MAX_RETRY_COUNT;
} else {
ar->ops->set_tid_config = NULL;
} /* * on LL hardware queues are managed entirely by the FW * so we only advertise to mac we can do the queues thing
*/
ar->hw->queues = IEEE80211_MAX_QUEUES;
/* vdev_ids are used as hw queue numbers. Make sure offchan tx queue is * something that vdev_ids can't reach so that we don't stop the queue * accidentally.
*/
ar->hw->offchannel_tx_hw_queue = IEEE80211_MAX_QUEUES - 1;
switch (ar->running_fw->fw_file.wmi_op_version) { case ATH10K_FW_WMI_OP_VERSION_MAIN:
ar->hw->wiphy->iface_combinations = ath10k_if_comb;
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_if_comb);
ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); break; case ATH10K_FW_WMI_OP_VERSION_TLV: if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) {
ar->hw->wiphy->iface_combinations =
ath10k_tlv_qcs_if_comb;
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_tlv_qcs_if_comb);
} else {
ar->hw->wiphy->iface_combinations = ath10k_tlv_if_comb;
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_tlv_if_comb);
}
ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: case ATH10K_FW_WMI_OP_VERSION_10_2_4:
ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_10x_if_comb); break; case ATH10K_FW_WMI_OP_VERSION_10_4:
ar->hw->wiphy->iface_combinations = ath10k_10_4_if_comb;
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_10_4_if_comb); if (test_bit(WMI_SERVICE_VDEV_DIFFERENT_BEACON_INTERVAL_SUPPORT,
ar->wmi.svc_map)) {
ar->hw->wiphy->iface_combinations =
ath10k_10_4_bcn_int_if_comb;
ar->hw->wiphy->n_iface_combinations =
ARRAY_SIZE(ath10k_10_4_bcn_int_if_comb);
} break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX:
WARN_ON(1);
ret = -EINVAL; goto err_free;
}
if (ar->hw_params.dynamic_sar_support)
ar->hw->wiphy->sar_capa = &ath10k_sar_capa;
if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
ar->hw->netdev_features = NETIF_F_HW_CSUM;
if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) { /* Init ath dfs pattern detector */
ar->ath_common.debug_mask = ATH_DBG_DFS;
ar->dfs_detector = dfs_pattern_detector_init(&ar->ath_common,
NL80211_DFS_UNSET);
if (!ar->dfs_detector)
ath10k_warn(ar, "failed to initialise DFS pattern detector\n");
}
ret = ath10k_mac_init_rd(ar); if (ret) {
ath10k_err(ar, "failed to derive regdom: %d\n", ret); goto err_dfs_detector_exit;
}
/* Disable set_coverage_class for chipsets that do not support it. */ if (!ar->hw_params.hw_ops->set_coverage_class)
ar->ops->set_coverage_class = NULL;
ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
ath10k_reg_notifier); if (ret) {
ath10k_err(ar, "failed to initialise regulatory: %i\n", ret); goto err_dfs_detector_exit;
}
if (test_bit(WMI_SERVICE_SPOOF_MAC_SUPPORT, ar->wmi.svc_map)) {
ar->hw->wiphy->features |=
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
}
ar->hw->wiphy->cipher_suites = cipher_suites;
/* QCA988x and QCA6174 family chips do not support CCMP-256, GCMP-128 * and GCMP-256 ciphers in hardware. Fetch number of ciphers supported * from chip specific hw_param table.
*/ if (!ar->hw_params.n_cipher_suites ||
ar->hw_params.n_cipher_suites > ARRAY_SIZE(cipher_suites)) {
ath10k_err(ar, "invalid hw_params.n_cipher_suites %d\n",
ar->hw_params.n_cipher_suites);
ar->hw_params.n_cipher_suites = 8;
}
ar->hw->wiphy->n_cipher_suites = ar->hw_params.n_cipher_suites;
if (!ath_is_world_regd(&ar->ath_common.reg_world_copy) &&
!ath_is_world_regd(&ar->ath_common.regulatory)) {
ret = regulatory_hint(ar->hw->wiphy,
ar->ath_common.regulatory.alpha2); if (ret) goto err_unregister;
}
return 0;
err_unregister:
ieee80211_unregister_hw(ar->hw);
err_dfs_detector_exit: if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector)
ar->dfs_detector->exit(ar->dfs_detector);
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.350Bemerkung:
(vorverarbeitet am 2026-04-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.