void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) { if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00lib_beacondone_iter, rt2x00dev); ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); }
static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_vif *avp = (void *)vif->drv_priv; struct ath9k_htc_target_vif hvif; int ret = 0; u8 cmd_rsp; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); hvif.index = avp->index; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); if (ret) { ath_err(common, "Unable to remove interface at idx: %d\n", avp->index); } priv->nvifs--; priv->vif_slot &= ~(1 << avp->index); ath9k_htc_remove_station(priv, vif, NULL); DEC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || vif->type == NL80211_IFTYPE_MESH_POINT || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_remove_bslot(priv, vif); ath9k_htc_set_opmode(priv); ath9k_htc_set_bssid_mask(priv, vif); /* * Stop ANI only if there are no associated station interfaces. */ if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) { priv->rearm_ani = false; ieee80211_iterate_active_interfaces_atomic( priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, ath9k_htc_vif_iter, priv); if (!priv->rearm_ani) ath9k_htc_stop_ani(priv); } ath_dbg(common, CONFIG, "Detach Interface at idx: %d\n", avp->index); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); }
void mt76_csa_finish(struct mt76_dev *dev) { if (!dev->csa_complete) return; ieee80211_iterate_active_interfaces_atomic(dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, __mt76_csa_finish, dev); dev->csa_complete = 0; }
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) { if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* send buffered bc/mc frames out for every bssid */ ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00lib_bc_buffer_iter, rt2x00dev); /* * Devices with pre tbtt interrupt don't need to update the beacon * here as they will fetch the next beacon directly prior to * transmission. */ if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags)) return; /* fetch next beacon */ ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, rt2x00lib_beaconupdate_iter, rt2x00dev); }
/* caller must hold wiphy_lock */ static int __ath9k_wiphy_pause(struct ath_wiphy *aphy) { ieee80211_stop_queues(aphy->hw); aphy->state = ATH_WIPHY_PAUSING; /* * TODO: handle PAUSING->PAUSED for the case where there are multiple * active vifs (now we do it on the first vif getting ready; should be * on the last) */ ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter, aphy); return 0; }
int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; u8 ap_sta_id = le32_to_cpu(notif->sta_id); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); return 0; }
static void mac80211_hwsim_beacon(unsigned long arg) { struct ieee80211_hw *hw = (struct ieee80211_hw *) arg; struct mac80211_hwsim_data *data = hw->priv; if (!data->started || !data->radio_enabled) return; ieee80211_iterate_active_interfaces_atomic( hw, mac80211_hwsim_beacon_tx, hw); data->beacon_timer.expires = jiffies + data->beacon_int; add_timer(&data->beacon_timer); }
static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) { priv->rearm_ani = false; priv->reconfig_beacon = false; ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_vif_iter, priv); if (priv->rearm_ani) ath9k_htc_start_ani(priv); if (priv->reconfig_beacon) { ath9k_htc_ps_wakeup(priv); ath9k_htc_beacon_reconfig(priv); ath9k_htc_ps_restore(priv); } }
int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { struct rt2x00_dev *rt2x00dev = hw->priv; if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; ieee80211_iterate_active_interfaces_atomic( rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, rt2x00mac_set_tim_iter, rt2x00dev); /* queue work to upodate the beacon template */ ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); return 0; }
int iwl_mvm_power_update_ps(struct iwl_mvm *mvm) { struct iwl_power_vifs vifs = { .mvm = mvm, }; int ret; lockdep_assert_held(&mvm->mutex); /* get vifs info */ ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_power_get_vifs_iterator, &vifs); ret = iwl_mvm_power_set_ps(mvm); if (ret) return ret; return iwl_mvm_power_set_ba(mvm, &vifs); }
static int iwl_mvm_binding_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_phy_ctxt *phyctxt, bool add) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_iface_iterator_data data = { .ignore_vif = vif, .phyctxt = phyctxt, }; u32 action = FW_CTXT_ACTION_MODIFY; lockdep_assert_held(&mvm->mutex); ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_iface_iterator, &data); /* * If there are no other interfaces yet we * need to create a new binding. */ if (data.idx == 0) { if (add) action = FW_CTXT_ACTION_ADD; else action = FW_CTXT_ACTION_REMOVE; } if (add) { if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING)) return -EINVAL; data.ids[data.idx] = mvmvif->id; data.colors[data.idx] = mvmvif->color; data.idx++; } return iwl_mvm_binding_cmd(mvm, action, &data); }
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { iwl_trans_stop_device(mvm->trans); iwl_trans_stop_hw(mvm->trans, false); mvm->scan_status = IWL_MVM_SCAN_NONE; /* just in case one was running */ ieee80211_remain_on_channel_expired(mvm->hw); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_cleanup_iterator, mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); ieee80211_wake_queues(mvm->hw); mvm->vif_count = 0; }
static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_vif_iter_data iter_data; /* * Use the hardware MAC address as reference, the hardware uses it * together with the BSSID mask when matching addresses. */ iter_data.hw_macaddr = common->macaddr; memset(&iter_data.mask, 0xff, ETH_ALEN); if (vif) ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, &iter_data); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); }
static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); unsigned long phy_ctxt_counter = 0; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_binding_iterator, &phy_ctxt_counter); if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, ETH_ALEN)) return false; if (vif->p2p && !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) return false; /* * Avoid using uAPSD if P2P client is associated to GO that uses * opportunistic power save. This is due to current FW limitation. */ if (vif->p2p && (vif->bss_conf.p2p_noa_attr.oppps_ctwindow & IEEE80211_P2P_OPPPS_ENABLE_BIT)) return false; /* * Avoid using uAPSD if client is in DCM - * low latency issue in Miracast */ if (hweight8(phy_ctxt_counter) >= 2) return false; return true; }
static void iwl_mvm_power_set_pm(struct iwl_mvm *mvm, struct iwl_power_vifs *vifs) { struct iwl_mvm_vif *bss_mvmvif = NULL; struct iwl_mvm_vif *p2p_mvmvif = NULL; struct iwl_mvm_vif *ap_mvmvif = NULL; bool client_same_channel = false; bool ap_same_channel = false; lockdep_assert_held(&mvm->mutex); /* set pm_enable to false */ ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_power_disable_pm_iterator, NULL); if (vifs->bss_vif) bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); if (vifs->p2p_vif) p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif); if (vifs->ap_vif) ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); /* enable PM on bss if bss stand alone */ if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { bss_mvmvif->pm_enabled = true; return; } /* enable PM on p2p if p2p stand alone */ if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) p2p_mvmvif->pm_enabled = true; return; } if (vifs->bss_active && vifs->p2p_active) client_same_channel = (bss_mvmvif->phy_ctxt->id == p2p_mvmvif->phy_ctxt->id); if (vifs->bss_active && vifs->ap_active) ap_same_channel = (bss_mvmvif->phy_ctxt->id == ap_mvmvif->phy_ctxt->id); /* clients are not stand alone: enable PM if DCM */ if (!(client_same_channel || ap_same_channel) && (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) { if (vifs->bss_active) bss_mvmvif->pm_enabled = true; if (vifs->p2p_active && (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)) p2p_mvmvif->pm_enabled = true; return; } /* * There is only one channel in the system and there are only * bss and p2p clients that share it */ if (client_same_channel && !vifs->ap_active && (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) { /* share same channel*/ bss_mvmvif->pm_enabled = true; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) p2p_mvmvif->pm_enabled = true; } }
void mt76_csa_check(struct mt76_dev *dev) { ieee80211_iterate_active_interfaces_atomic(dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, __mt76_csa_check, dev); }
/* * Update Smart fifo: * Count bound interfaces that are not to be removed, ignoring p2p devices, * and set new state accordingly. */ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, bool remove_vif) { enum iwl_sf_state new_state; u8 sta_id = IWL_MVM_STATION_COUNT; struct iwl_mvm_vif *mvmvif = NULL; struct iwl_mvm_active_iface_iterator_data data = { .ignore_vif = changed_vif, .sta_vif_state = SF_UNINIT, .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, }; /* * Ignore the call if we are in HW Restart flow, or if the handled * vif is a p2p device. */ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE)) return 0; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bound_iface_iterator, &data); /* If changed_vif exists and is not to be removed, add to the count */ if (changed_vif && !remove_vif) data.num_active_macs++; switch (data.num_active_macs) { case 0: /* If there are no active macs - change state to SF_INIT_OFF */ new_state = SF_INIT_OFF; break; case 1: if (remove_vif) { /* The one active mac left is of type station * and we filled the relevant data during iteration */ new_state = data.sta_vif_state; sta_id = data.sta_vif_ap_sta_id; } else { if (WARN_ON(!changed_vif)) return -EINVAL; if (changed_vif->type != NL80211_IFTYPE_STATION) { new_state = SF_UNINIT; } else if (changed_vif->bss_conf.assoc && changed_vif->bss_conf.dtim_period) { mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); sta_id = mvmvif->ap_sta_id; new_state = SF_FULL_ON; } else { new_state = SF_INIT_OFF; } } break; default: /* If there are multiple active macs - change to SF_UNINIT */ new_state = SF_UNINIT; } return iwl_mvm_sf_config(mvm, sta_id, new_state); }
static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int n_ssids, struct iwl_mvm_scan_params *params) { bool global_bound = false; enum ieee80211_band band; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_scan_condition_iterator, &global_bound); /* * Under low latency traffic passive scan is fragmented meaning * that dwell on a particular channel will be fragmented. Each fragment * dwell time is 20ms and fragments period is 105ms. Skipping to next * channel will be delayed by the same period - 105ms. So suspend_time * parameter describing both fragments and channels skipping periods is * set to 105ms. This value is chosen so that overall passive scan * duration will not be too long. Max_out_time in this case is set to * 70ms, so for active scanning operating channel will be left for 70ms * while for passive still for 20ms (fragment dwell). */ if (global_bound) { if (!iwl_mvm_low_latency(mvm)) { params->suspend_time = ieee80211_tu_to_usec(100); params->max_out_time = ieee80211_tu_to_usec(600); } else { params->suspend_time = ieee80211_tu_to_usec(105); /* P2P doesn't support fragmented passive scan, so * configure max_out_time to be at least longest dwell * time for passive scan. */ if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { params->max_out_time = ieee80211_tu_to_usec(70); params->passive_fragmented = true; } else { u32 passive_dwell; /* * Use band G so that passive channel dwell time * will be assigned with maximum value. */ band = IEEE80211_BAND_2GHZ; passive_dwell = iwl_mvm_get_passive_dwell(band); params->max_out_time = ieee80211_tu_to_usec(passive_dwell); } } } for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { if (params->passive_fragmented) params->dwell[band].passive = 20; else params->dwell[band].passive = iwl_mvm_get_passive_dwell(band); params->dwell[band].active = iwl_mvm_get_active_dwell(band, n_ssids); } }
static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int n_ssids, u32 flags, struct iwl_mvm_scan_params *params) { int global_cnt = 0; enum ieee80211_band band; u8 frag_passive_dwell = 0; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_scan_condition_iterator, &global_cnt); if (!global_cnt) goto not_bound; params->suspend_time = 30; params->max_out_time = 120; if (iwl_mvm_low_latency(mvm)) { if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_FRAGMENTED_SCAN) { params->suspend_time = 105; /* * If there is more than one active interface make * passive scan more fragmented. */ frag_passive_dwell = 40; params->max_out_time = frag_passive_dwell; } else { params->suspend_time = 120; params->max_out_time = 120; } } if (frag_passive_dwell && (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) { /* * P2P device scan should not be fragmented to avoid negative * impact on P2P device discovery. Configure max_out_time to be * equal to dwell time on passive channel. Take a longest * possible value, one that corresponds to 2GHz band */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { u32 passive_dwell = iwl_mvm_get_passive_dwell(mvm, IEEE80211_BAND_2GHZ); params->max_out_time = passive_dwell; } else { params->passive_fragmented = true; } } if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY) params->max_out_time = 200; not_bound: for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { if (params->passive_fragmented) params->dwell[band].fragmented = frag_passive_dwell; params->dwell[band].passive = iwl_mvm_get_passive_dwell(mvm, band); params->dwell[band].active = iwl_mvm_get_active_dwell(mvm, band, n_ssids); } }
static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; /* * Not much to do here. The stack will not allow interface * types or combinations that we didn't advertise, so we * don't really have to check the types. */ mutex_lock(&mvm->mutex); /* Allocate resources for the MAC context, and add it the the fw */ ret = iwl_mvm_mac_ctxt_init(mvm, vif); if (ret) goto out_unlock; /* * The AP binding flow can be done only after the beacon * template is configured (which happens only in the mac80211 * start_ap() flow), and adding the broadcast station can happen * only after the binding. * In addition, since modifying the MAC before adding a bcast * station is not allowed by the FW, delay the adding of MAC context to * the point where we can also add the bcast station. * In short: there's not much we can do at this point, other than * allocating resources :) */ if (vif->type == NL80211_IFTYPE_AP) { u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask); if (ret) { IWL_ERR(mvm, "Failed to allocate bcast sta\n"); goto out_release; } goto out_unlock; } /* * TODO: remove this temporary code. * Currently MVM FW supports power management only on single MAC. * Iterate and disable PM on all active interfaces. * Note: the method below does not count the new interface being added * at this moment. */ mvm->vif_count++; if (mvm->vif_count > 1) { IWL_DEBUG_MAC80211(mvm, "Disable power on existing interfaces\n"); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_pm_disable_iterator, mvm); } ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) goto out_release; /* * Update power state on the new interface. Admittedly, based on * mac80211 logics this power update will disable power management */ iwl_mvm_power_update_mode(mvm, vif); /* * P2P_DEVICE interface does not have a channel context assigned to it, * so a dedicated PHY context is allocated to it and the corresponding * MAC context is bound to it at this stage. */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; mvmvif->phy_ctxt = &mvm->phy_ctxt_roc; /* * The channel used here isn't relevant as it's * going to be overwritten as part of the ROC flow. * For now use the first channel we have. */ chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef, 1, 1); if (ret) goto out_remove_mac; ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) goto out_remove_phy; ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) goto out_unbind; /* Save a pointer to p2p device vif, so it can later be used to * update the p2p device MAC when a GO is started/stopped */ mvm->p2p_device_vif = vif; } goto out_unlock; out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); out_remove_phy: iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); out_remove_mac: mvmvif->phy_ctxt = NULL; iwl_mvm_mac_ctxt_remove(mvm, vif); out_release: /* * TODO: remove this temporary code. * Currently MVM FW supports power management only on single MAC. * Check if only one additional interface remains after rereasing * current one. Update power mode on the remaining interface. */ mvm->vif_count--; IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", mvm->vif_count); if (mvm->vif_count == 1) { ieee80211_iterate_active_interfaces( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_power_update_iterator, mvm); } iwl_mvm_mac_ctxt_release(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); return ret; }
static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) { struct iwl_bt_iterator_data data = { .mvm = mvm, .notif = &mvm->last_bt_notif, }; struct iwl_bt_coex_ci_cmd cmd = {}; u8 ci_bw_idx; /* Ignore updates if we are in force mode */ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) return; rcu_read_lock(); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_notif_iterator, &data); iwl_mvm_bt_coex_tcm_based_ci(mvm, &data); if (data.primary) { struct ieee80211_chanctx_conf *chan = data.primary; if (WARN_ON(!chan->def.chan)) { rcu_read_unlock(); return; } if (chan->def.width < NL80211_CHAN_WIDTH_40) { ci_bw_idx = 0; } else { if (chan->def.center_freq1 > chan->def.chan->center_freq) ci_bw_idx = 2; else ci_bw_idx = 1; } cmd.bt_primary_ci = iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; cmd.primary_ch_phy_id = cpu_to_le32(*((u16 *)data.primary->drv_priv)); } if (data.secondary) { struct ieee80211_chanctx_conf *chan = data.secondary; if (WARN_ON(!data.secondary->def.chan)) { rcu_read_unlock(); return; } if (chan->def.width < NL80211_CHAN_WIDTH_40) { ci_bw_idx = 0; } else { if (chan->def.center_freq1 > chan->def.chan->center_freq) ci_bw_idx = 2; else ci_bw_idx = 1; } cmd.bt_secondary_ci = iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; cmd.secondary_ch_phy_id = cpu_to_le32(*((u16 *)data.secondary->drv_priv)); } rcu_read_unlock(); /* Don't spam the fw with the same command over and over */ if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, sizeof(cmd), &cmd)) IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); } } void iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", le32_to_cpu(notif->primary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", le32_to_cpu(notif->secondary_ch_lut)); IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", le32_to_cpu(notif->bt_activity_grading)); /* remember this notification for future use: rssi fluctuations */ memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); iwl_mvm_bt_coex_notif_handle(mvm); } void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event_data rssi_event) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; lockdep_assert_held(&mvm->mutex); /* Ignore updates if we are in force mode */ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) return; /* * Rssi update while not associated - can happen since the statistics * are handled asynchronously */ if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) return; /* No BT - reports should be disabled */ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) return; IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); /* * Check if rssi is good enough for reduced Tx power, but not in loose * scheme. */ if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); else ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); if (ret) IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); } #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) #define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) return LINK_QUAL_AGG_TIME_LIMIT_DEF; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return LINK_QUAL_AGG_TIME_LIMIT_DEF; lut_type = iwl_get_coex_type(mvm, mvmsta->vif); if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT) return LINK_QUAL_AGG_TIME_LIMIT_DEF; /* tight coex, high bt traffic, reduce AGG time limit */ return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; } bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt; enum iwl_bt_coex_lut_type lut_type; if (mvm->last_bt_notif.ttc_status & BIT(phy_ctxt->id)) return true; if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC) return true; /* * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas * since BT is already killed. * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while * we Tx. * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO. */ lut_type = iwl_get_coex_type(mvm, mvmsta->vif); return lut_type != BT_COEX_LOOSE_LUT; } bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant) { /* there is no other antenna, shared antenna is always available */ if (mvm->cfg->bt_shared_single_ant) return true; if (ant & mvm->cfg->non_shared_ant) return true; return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; } bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm) { /* there is no other antenna, shared antenna is always available */ if (mvm->cfg->bt_shared_single_ant) return true; return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < BT_HIGH_TRAFFIC; } bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, enum nl80211_band band) { u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); if (band != NL80211_BAND_2GHZ) return false; return bt_activity >= BT_LOW_TRAFFIC; }