/* In some cases (notably with static WEP IBSS with multiple keys) *multicastTxbecomesbroken.Bothpairwiseandgroupwisekeysare *installedalready.UsingWMI_KEY_TX_USAGEindifferentcombinations *didn'tseemhelp.Usingdef_keyidvdevparameterseemstobe *effectivesousethat. * *FIXME:Revisit.Perhapsthiscanbedoneinalesshackyway.
*/ if (arvif->vif->type != NL80211_IFTYPE_ADHOC) return0;
if (arvif->def_wep_key_idx == -1) return0;
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;
}
return0;
}
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, *sinceWMIdoesn'tgiveusthatinformation. * *FIXME:multi-bssneedstobehandled.
*/
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,sowetrytoremovethekeysincrementally
*/
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);
}
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 *thepeer_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'thaveanyvifs?
*/
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 *monitorvdevon.
*/
num_ctx = ath10k_mac_num_chanctxs(ar); if (num_ctx == 0) returnfalse;
/* If there's already an existing special monitor interface then don't *bothercreatinganothermonitorvdev.
*/ if (ar->monitor_arvif) returnfalse;
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;
}
/* When originally vdev is started during assign_vif_chanctx() some *informationismissing,notablySSID.Firmwarerevisionswithbeacon *offloadingrequiretheSSIDtobeprovidedduringvdev(re)startto *handlehiddenSSIDproperly. * *Vdevrestartmustbedoneaftervdevhasbeenbothstartedand *upped.Otherwisesomefirmwarerevisions(atleast10.2)failto *delivervdevrestartresponseeventcausingtimeoutsduringvdev *syncinginath10k. * *Note:Thevdevdown/upandtemplatereinstallationcouldbeskipped *sinceonlywmi-tlvfirmwareareknowntohavebeaconoffloadand *wmi-tlvdoesn'tseemtomisbehavelike10.2wrtvdevrestart *responsedelivery.It'sprobablymorerobusttokeepitasis.
*/ if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) return0;
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;
}
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;
}
return0;
}
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);
}
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 (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 *beaconinterval.Currentlywedon'tknow *sta->listen_interval-mac80211patchrequired. *Currentlyuse10seconds
*/
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;
}
}
return0;
}
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;
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)) return0;
/* The following logic is correct. If a remote STA advertises support *forbeingabeamformerthenweshouldenableusbeingabeamformee.
*/
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 *asnoSARlimitationonit.
*/
ar->tx_power_2g_limit = 0;
ar->tx_power_5g_limit = 0;
/* note the power is in 0.25dbm unit, while ath10k uses *0.5dbmunit.
*/ 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 *beforecallingath10k_setup_peer_smps()whichmightsleep.
*/
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_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'tmakemuchsensetoreconfigurethepeercompletely.
*/ 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 *channelandisscanningactivelyusingProbeRequests *on"Radardetection"/DFSchannelswhicharenot *markedas"available"
*/
ch->passive |= ch->chan_radar;
/* FIXME: why use only legacy modes, why not any *HT/VHTmodes?Wouldthatevenmakeany *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;
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 (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 *withtxencapmodesettoRAW.Ex:Multicasttrafficgenerated *foraspecificVLANgroupwillalwaysbeencryptedinsoftware.
*/ 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;
}
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 *callerisconcernedsonofreeingisnecessaryafterwards.
*/ 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 *encapsulatedto802.11bymac80211.Otherwisepassitasis.
*/ 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 *anoffchannelframe.Otherwisetheframewillbestuckand *nevertransmitted.Wedeletethepeerupontxcompletion. *Itisunlikelythatapeerforoffchanneltxwillalreadybe *present.Howeveritmaybeinsomerarecasessoaccountforthat. *Otherwisewemightremovealegitimatepeerandbreakstuff.
*/
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);
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 *firmwarefailstodelivertheevent(forwhateverreason)itis *desiredtocleanupscanstateanyway.Firmwaremayhavejust *droppedthescancompletioneventdeliveryduetotransportpipe *beingoverflownwithdataand/oritcanrecoveronitsownbefore *nextscanrequestissubmitted.
*/
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 *abortionwhilescancompletionwasbeingprocessed.
*/ 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 *thispoint.Thisisprobablyduetosomeissueinthe *firmware,butnoneedtowedgethedriverduetothat...
*/
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 *ishelpful.Probablyitwillnotdowhatuser *ishopingfor,sowarninthatcase.
*/ 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 *streamssupported,assumeitsupportupto4BFSTSandreturn *thevalueforVHTCAP:nsts-1)
*/ if (nsts == 0) return3;
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 *arestrictedNSSfor160or80+80vswhatitcandofor80Mhz.Give *user-spaceaclueifthatisthecase.
*/ 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);
}
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;
}
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 *fullyRESTARTEDbecauseofincomingsuspendevent, *thenath10k_halt()isalreadycalledvia *ath10k_core_restart()andshouldnotbecalledhere.
*/ 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);
}
}
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 *txqueuelocking.Thisshouldn'twrapduetointerfacecombinations *butdoamoduloforcorrectnesssakeandpreventusingoffchanneltx *queuesforregularviftx.
*/
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 *sendinganotherSWBAevent.Thiscouldleadtohardwareusingold *(freed)beacondatainsomecases,e.g.txcreditstarvation *combinedwithmissedTBTT.Thisisveryrare. * *Onnon-IOMMU-enabledhoststhiscouldbeapossiblesecurityissue *becausehwcouldbeaconsomerandomdataontheair.On *IOMMU-enabledhostsDMARfaultswouldoccurinmostcasesandtarget *devicewouldcrash. * *Sincetherearenobeacontxcompletions(implicitnorexplicit) *propagatedtohosttheonlyworkaroundforthisistoallocatea *DMA-coherentbufferforalifetimeofavifanduseitforall *beacontxcommands.Worstcaseforthisapproachissomebeaconsmay *becomecorrupted,e.g.havegarbledIEsorout-of-dateTIMbitmap.
*/ 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 *tokencanleadtoundefinedbehaviorifthat *makesitintocachemanagementfunctions.Usea *known-invalidaddresstokeninstead,which *avoidsthewarningandmakesiteasiertocatch *bugsifitdoesendupgettingused.
*/
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 *takescareofthiswithidleconnectionpolling.
*/
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 *targetassertinqca9888andqca6174.
*/ 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 *untilafterassociatedvdevisdeleted.
*/ 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 *DMAmemorywehavedeleted.
*/
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 *whenassociatingtopreventsomefirmwarerevisions *(e.g.10.1and10.2)fromcrashing.
*/ 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 *isnotsupportedonthishardware.
*/ 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 *keyindexafterinstallingit.OtherwiseFW/HWTxescorrupted *frameswithmulti-vifAPs.Thisisnotrequiredformainfirmware *branch(e.g.636). * *Thisisalsoneededfor636fwforIBSS-RSNtoworkmorereliably. * *FIXME:Itremainsunknownifthisisrequiredformulti-vifSTA *interfaceson10.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) return1;
if (arvif->nohwcrypt) return1;
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 *wealreadyholdconf_mutex.wejustmakesureitstherenow.
*/
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 *stationsassociatedalready(e.g.whenmerging)withoutany *keys.StaticWEPneedsanexplicitper-peerkeyupload.
*/ 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() *callchangesdefaulttxkey. * *StaticWEPsetsdef_wep_key_idxvia.set_default_unicast_key *afterfirstset_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;
}
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);
}
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 *musthavefailedtodothisabove.
*/
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)) { /* *Newassociation.
*/
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) { /* *Tdlsstationauthorized.
*/
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) return0;
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 *triggerframe.Thefollowingeffectivelydisablesu-UAPSD *autotriggerinfirmware(whichisenabledbydefault *providedtheautotriggerserviceisavailable).
*/
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;
}
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 *betterthannothing.
*/
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 *firmwareactuallyimplementsit.Moreoveritisnotpossibletorely *framefragmentationtomac80211becausefirmwareclearsthe"more *fragments"bitinframecontrolmakingitimpossibleforremote *devicestoreassembleframes. * *Henceimplementadummycallbackjusttosayfragmentationisn't *supported.Thiseffectivelypreventsmac80211fromdoingframe *fragmentationinsoftware.
*/ 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'llcollectthoseframeseitherwayifwestop/deletevdevs
*/
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 *toexpressallVHTMCSratemasks.Effectivelyonlythefollowing *rangescanbeused:none,0-7,0-8and0-9.
*/ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
vht_mcs = mask->control[band].vht_mcs[i];
switch (vht_mcs) { case0: 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;
}
}
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.Doweneedtoverifythis?
*/ return0; 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 *Txaggregationrequests.
*/ return -EOPNOTSUPP;
}
/* Both locks are required because ar->rx_channel is modified. This *allowsreaderstoholdeitherlock.
*/
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 *onalinkedlistnow.Doingalookuppeer->vif->chanctxforeach *ppduonRxmayreduceperformanceonlow-endsystems.Itshouldbe *possibletomaketables/hashmapstospeedthelookupup(bevaryof *cpudatacachelinesthoughregardingsizes)buttokeeptheinitial *implementationsimpleandlessintrusivefallbacktotheslowlookup *onlyformulti-channelcases.Single-channelcaseswillremainto *usetheoldchannelderivalandthusperformanceshouldnotbe *affectedmuch.
*/
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);
/* 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 *definitionspervdevinternallyandthere'snohost-sidechannel *contextabstractiontoconfigure,e.g.channelwidth.
*/
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. *Allowsoftwareencryptionforallchips.Don'tforgetto *updaten_cipher_suitesbelow.
*/
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 *andCCMP-256inhardware.
*/
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);
/* vdev_ids are used as hw queue numbers. Make sure offchan tx queue is *somethingthatvdev_idscan'treachsothatwedon'tstopthequeue *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 *andGCMP-256ciphersinhardware.Fetchnumberofcipherssupported *fromchipspecifichw_paramtable.
*/ 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;
}
return0;
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.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.