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);
}
Beispiel #2
0
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);
}
Beispiel #3
0
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);
}
Beispiel #5
0
/* 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;
}
Beispiel #6
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);
}
Beispiel #8
0
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);
	}
}
Beispiel #9
0
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;
}
Beispiel #10
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);
}
Beispiel #11
0
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);
}
Beispiel #12
0
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;
}
Beispiel #13
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);
}
Beispiel #14
0
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;
}
Beispiel #15
0
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;
    }
}
Beispiel #16
0
void mt76_csa_check(struct mt76_dev *dev)
{
	ieee80211_iterate_active_interfaces_atomic(dev->hw,
		IEEE80211_IFACE_ITER_RESUME_ALL,
		__mt76_csa_check, dev);
}
Beispiel #17
0
Datei: sf.c Projekt: 020gzh/linux
/*
 * 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);
}
Beispiel #18
0
Datei: scan.c Projekt: 7799/linux
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);
	}
}
Beispiel #19
0
Datei: scan.c Projekt: 3bsa/linux
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);
	}
}
Beispiel #20
0
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;
}
Beispiel #21
0
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;
}