Example #1
0
static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
                                    struct iwl_mac_power_cmd *cmd)
{
    int dtimper, bi;
    int keep_alive;
    bool radar_detect = false;
    struct iwl_mvm_vif *mvmvif __maybe_unused =
        iwl_mvm_vif_from_mac80211(vif);

    cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
                                    mvmvif->color));
    dtimper = vif->bss_conf.dtim_period;
    bi = vif->bss_conf.beacon_int;

    /*
     * Regardless of power management state the driver must set
     * keep alive period. FW will use it for sending keep alive NDPs
     * immediately after association. Check that keep alive period
     * is at least 3 * DTIM
     */
    keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
                              USEC_PER_SEC);
    keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
    cmd->keep_alive_seconds = cpu_to_le16(keep_alive);

    if (mvm->ps_disabled)
        return;

    cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);

    if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
            !mvmvif->pm_enabled || iwl_mvm_tdls_sta_count(mvm, vif))
        return;

    cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);

    if (vif->bss_conf.beacon_rate &&
            (vif->bss_conf.beacon_rate->bitrate == 10 ||
             vif->bss_conf.beacon_rate->bitrate == 60)) {
        cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
        cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
    }

    /* Check if radar detection is required on current channel */
    radar_detect = iwl_mvm_power_is_radar(vif);

    /* Check skip over DTIM conditions */
    if (!radar_detect && (dtimper < 10) &&
            (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
             mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
        cmd->skip_dtim_periods =
            iwl_mvm_power_get_skip_over_dtim(dtimper, bi);
        if (cmd->skip_dtim_periods)
            cmd->flags |=
                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
    }

    if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
        cmd->rx_data_timeout =
            cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
        cmd->tx_data_timeout =
            cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
    } else {
        cmd->rx_data_timeout =
            cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
        cmd->tx_data_timeout =
            cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
    }

    if (iwl_mvm_power_allow_uapsd(mvm, vif))
        iwl_mvm_power_configure_uapsd(mvm, vif, cmd);

#ifdef CONFIG_IWLWIFI_DEBUGFS
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
        cmd->keep_alive_seconds =
            cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
        if (mvmvif->dbgfs_pm.skip_over_dtim)
            cmd->flags |=
                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
        else
            cmd->flags &=
                cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
    }
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
        cmd->rx_data_timeout =
            cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
        cmd->tx_data_timeout =
            cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
        cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
        if (mvmvif->dbgfs_pm.lprx_ena)
            cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
        else
            cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
    }
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
        cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
        if (mvmvif->dbgfs_pm.snooze_ena)
            cmd->flags |=
                cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
        else
            cmd->flags &=
                cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
    }
    if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
        u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
        if (mvmvif->dbgfs_pm.uapsd_misbehaving)
            cmd->flags |= cpu_to_le16(flag);
        else
            cmd->flags &= cpu_to_le16(flag);
    }
#endif /* CONFIG_IWLWIFI_DEBUGFS */
}
static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
				    struct ieee80211_vif *vif,
				    struct iwl_mac_power_cmd *cmd)
{
	struct ieee80211_hw *hw = mvm->hw;
	struct ieee80211_chanctx_conf *chanctx_conf;
	struct ieee80211_channel *chan;
	int dtimper, dtimper_msec;
	int keep_alive;
	bool radar_detect = false;
	struct iwl_mvm_vif *mvmvif __maybe_unused =
		iwl_mvm_vif_from_mac80211(vif);
	bool allow_uapsd = true;

	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
							    mvmvif->color));
	dtimper = hw->conf.ps_dtim_period ?: 1;

	/*
	 * Regardless of power management state the driver must set
	 * keep alive period. FW will use it for sending keep alive NDPs
	 * immediately after association. Check that keep alive period
	 * is at least 3 * DTIM
	 */
	dtimper_msec = dtimper * vif->bss_conf.beacon_int;
	keep_alive = max_t(int, 3 * dtimper_msec,
			   MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
	keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
	cmd->keep_alive_seconds = cpu_to_le16(keep_alive);

	if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
	    mvm->ps_prevented)
		return;

	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);

#ifdef CPTCFG_IWLWIFI_DEBUGFS
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
	    mvmvif->dbgfs_pm.disable_power_off)
		cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
#endif
	if (!vif->bss_conf.ps || mvmvif->pm_prevented ||
	    iwl_mvm_vif_low_latency(mvmvif))
		return;

	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);

	if (vif->bss_conf.beacon_rate &&
	    (vif->bss_conf.beacon_rate->bitrate == 10 ||
	     vif->bss_conf.beacon_rate->bitrate == 60)) {
		cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
		cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
	}

	/* Check if radar detection is required on current channel */
	rcu_read_lock();
	chanctx_conf = rcu_dereference(vif->chanctx_conf);
	WARN_ON(!chanctx_conf);
	if (chanctx_conf) {
		chan = chanctx_conf->def.chan;
		radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
	}
	rcu_read_unlock();

	/* Check skip over DTIM conditions */
	if (!radar_detect && (dtimper <= 10) &&
	    (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
	     mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
		cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
		cmd->skip_dtim_periods = 3;
	}

	if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
		cmd->rx_data_timeout =
			cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
		cmd->tx_data_timeout =
			cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
	} else {
		cmd->rx_data_timeout =
			cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
		cmd->tx_data_timeout =
			cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
	}

	if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
		    ETH_ALEN))
		allow_uapsd = false;

	if (vif->p2p &&
	    !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
		allow_uapsd = 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)
		allow_uapsd = false;

	if (allow_uapsd)
		iwl_mvm_power_configure_uapsd(mvm, vif, cmd);

#ifdef CPTCFG_IWLWIFI_DEBUGFS
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
		cmd->keep_alive_seconds =
			cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
		if (mvmvif->dbgfs_pm.skip_over_dtim)
			cmd->flags |=
				cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
		else
			cmd->flags &=
				cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
	}
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
		cmd->rx_data_timeout =
			cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
		cmd->tx_data_timeout =
			cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
		cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
		if (mvmvif->dbgfs_pm.lprx_ena)
			cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
		else
			cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
	}
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
		cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
		if (mvmvif->dbgfs_pm.snooze_ena)
			cmd->flags |=
				cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
		else
			cmd->flags &=
				cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
	}
	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
		u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
		if (mvmvif->dbgfs_pm.uapsd_misbehaving)
			cmd->flags |= cpu_to_le16(flag);
		else
			cmd->flags &= cpu_to_le16(flag);
	}
#endif /* CPTCFG_IWLWIFI_DEBUGFS */
}