static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mac_power_cmd cmd = {}; struct iwl_mvm_vif *mvmvif __maybe_unused = iwl_mvm_vif_from_mac80211(vif); if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); #ifdef CONFIG_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 iwl_mvm_power_log(mvm, &cmd); return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC, sizeof(cmd), &cmd); }
void iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_beacon_filter_cmd *cmd) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) cmd->bf_roaming_energy_delta = cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD) cmd->bf_temp_threshold = cpu_to_le32(dbgfs_bf->bf_temp_threshold); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER) cmd->bf_temp_fast_filter = cpu_to_le32(dbgfs_bf->bf_temp_fast_filter); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER) cmd->bf_temp_slow_filter = cpu_to_le32(dbgfs_bf->bf_temp_slow_filter); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag); if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) cmd->ba_enable_beacon_abort = cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort); }
static void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); mvmvif->pm_enabled = false; }
void iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_beacon_filter_cmd *cmd) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) cmd->bf_roaming_energy_delta = dbgfs_bf->bf_roaming_energy_delta; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA) cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag; if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort; }
static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, struct iwl_time_quota_cmd *cmd) { #ifdef CONFIG_NL80211_TESTMODE struct iwl_mvm_vif *mvmvif; int i, phy_id = -1, beacon_int = 0; if (!mvm->noa_duration || !mvm->noa_vif) return; mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); if (!mvmvif->ap_ibss_active) return; phy_id = mvmvif->phy_ctxt->id; beacon_int = mvm->noa_vif->bss_conf.beacon_int; for (i = 0; i < MAX_BINDINGS; i++) { u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color); u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; u32 quota = le32_to_cpu(cmd->quotas[i].quota); if (id != phy_id) continue; quota *= (beacon_int - mvm->noa_duration); quota /= beacon_int; cmd->quotas[i].quota = cpu_to_le32(quota); } #endif }
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); 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 (iwl_mvm_phy_ctx_count(mvm) >= 2) return false; return true; }
static ssize_t iwl_dbgfs_reduced_txp_write(struct ieee80211_vif *vif, char *buf, size_t count, loff_t *ppos) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; struct iwl_mvm_sta *mvmsta; bool reduced_tx_power; int ret; if (mvmvif->ap_sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) return -ENOTCONN; if (strtobool(buf, &reduced_tx_power) != 0) return -EINVAL; mutex_lock(&mvm->mutex); mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id); if (IS_ERR_OR_NULL(mvmsta)) { mutex_unlock(&mvm->mutex); return -ENOTCONN; } mvmsta->bt_reduced_txpower_dbg = false; ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, reduced_tx_power); if (!ret) mvmsta->bt_reduced_txpower_dbg = true; mutex_unlock(&mvm->mutex); return ret ? : count; }
static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); mutex_lock(&mvm->mutex); iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); if (vif->type == NL80211_IFTYPE_AP) goto out_unlock; iwl_mvm_binding_remove_vif(mvm, vif); switch (vif->type) { case NL80211_IFTYPE_MONITOR: iwl_mvm_update_quotas(mvm, vif); break; default: break; } out_unlock: mvmvif->phy_ctxt = NULL; mutex_unlock(&mvm->mutex); }
int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_powertable_cmd cmd = {}; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (!iwlwifi_mod_params.power_save) { IWL_DEBUG_POWER(mvm, "Power management is not allowed\n"); return 0; } if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); cmd.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY); IWL_DEBUG_POWER(mvm, "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n", cmd.id_and_color, iwlmvm_mod_params.power_scheme, le16_to_cpu(cmd.flags)); return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, sizeof(cmd), &cmd); }
int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; u8 sta_id; if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU)) return -1; if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && (!info->control.vif || info->hw_queue != info->control.vif->cab_queue))) return -1; /* * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel * queue. STATION (HS2.0) uses the auxiliary context of the FW, * and hence needs to be sent on the aux queue */ if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE && info->control.vif->type == NL80211_IFTYPE_STATION) IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue; /* * If the interface on which frame is sent is the P2P_DEVICE * or an AP/GO interface use the broadcast station associated * with it; otherwise use the AUX station. */ if (info->control.vif && (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE || info->control.vif->type == NL80211_IFTYPE_AP)) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(info->control.vif); sta_id = mvmvif->bcast_sta.sta_id; } else { sta_id = mvm->aux_sta.sta_id; } IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue); dev_cmd = iwl_mvm_set_tx_params(mvm, skb, NULL, sta_id); if (!dev_cmd) return -1; /* From now on, we cannot access info->control */ tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) { iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); return -1; } return 0; }
void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, ETH_ALEN)) memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN); }
static ssize_t iwl_dbgfs_mac_params_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; u8 ap_sta_id; struct ieee80211_chanctx_conf *chanctx_conf; char buf[512]; int bufsz = sizeof(buf); int pos = 0; int i; mutex_lock(&mvm->mutex); ap_sta_id = mvmvif->ap_sta_id; pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", mvmvif->id, mvmvif->color); pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", vif->bss_conf.bssid); pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) pos += scnprintf(buf+pos, bufsz-pos, "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", i, mvmvif->queue_params[i].txop, mvmvif->queue_params[i].cw_min, mvmvif->queue_params[i].cw_max, mvmvif->queue_params[i].aifs, mvmvif->queue_params[i].uapsd); if (vif->type == NL80211_IFTYPE_STATION && ap_sta_id != IWL_MVM_STATION_COUNT) { struct ieee80211_sta *sta; struct iwl_mvm_sta *mvm_sta; sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], lockdep_is_held(&mvm->mutex)); mvm_sta = (void *)sta->drv_priv; pos += scnprintf(buf+pos, bufsz-pos, "ap_sta_id %d - reduced Tx power %d\n", ap_sta_id, mvm_sta->bt_reduced_txpower); } rcu_read_lock(); chanctx_conf = rcu_dereference(vif->chanctx_conf); if (chanctx_conf) pos += scnprintf(buf+pos, bufsz-pos, "idle rx chains %d, active rx chains: %d\n", chanctx_conf->rx_chains_static, chanctx_conf->rx_chains_dynamic); rcu_read_unlock(); mutex_unlock(&mvm->mutex); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); }
static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum iwl_dbgfs_pm_mask param, int val) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; dbgfs_pm->mask |= param; switch (param) { case MVM_DEBUGFS_PM_KEEP_ALIVE: { struct ieee80211_hw *hw = mvm->hw; int dtimper = hw->conf.ps_dtim_period ?: 1; int dtimper_msec = dtimper * vif->bss_conf.beacon_int; IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); if (val * MSEC_PER_SEC < 3 * dtimper_msec) IWL_WARN(mvm, "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", val * MSEC_PER_SEC, 3 * dtimper_msec); dbgfs_pm->keep_alive_seconds = val; break; } case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", val ? "enabled" : "disabled"); dbgfs_pm->skip_over_dtim = val; break; case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); dbgfs_pm->skip_dtim_periods = val; break; case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); dbgfs_pm->rx_data_timeout = val; break; case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); dbgfs_pm->tx_data_timeout = val; break; case MVM_DEBUGFS_PM_LPRX_ENA: IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); dbgfs_pm->lprx_ena = val; break; case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); dbgfs_pm->lprx_rssi_threshold = val; break; case MVM_DEBUGFS_PM_SNOOZE_ENABLE: IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); dbgfs_pm->snooze_ena = val; break; case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); dbgfs_pm->uapsd_misbehaving = val; break; } }
static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, size_t count, loff_t *ppos) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm *mvm = mvmvif->mvm; enum iwl_dbgfs_pm_mask param; int val, ret; if (!strncmp("keep_alive=", buf, 11)) { if (sscanf(buf + 11, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_KEEP_ALIVE; } else if (!strncmp("skip_over_dtim=", buf, 15)) { if (sscanf(buf + 15, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; } else if (!strncmp("skip_dtim_periods=", buf, 18)) { if (sscanf(buf + 18, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; } else if (!strncmp("rx_data_timeout=", buf, 16)) { if (sscanf(buf + 16, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; } else if (!strncmp("tx_data_timeout=", buf, 16)) { if (sscanf(buf + 16, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; } else if (!strncmp("lprx=", buf, 5)) { if (sscanf(buf + 5, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_LPRX_ENA; } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { if (sscanf(buf + 20, "%d", &val) != 1) return -EINVAL; if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < POWER_LPRX_RSSI_THRESHOLD_MIN) return -EINVAL; param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; } else if (!strncmp("snooze_enable=", buf, 14)) { if (sscanf(buf + 14, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { if (sscanf(buf + 18, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; } else { return -EINVAL; } mutex_lock(&mvm->mutex); iwl_dbgfs_update_pm(mvm, vif, param, val); ret = iwl_mvm_power_update_mac(mvm, vif); mutex_unlock(&mvm->mutex); return ret ?: count; }
static void iwl_mvm_quota_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_quota_iterator_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u16 id; /* * We'll account for the new interface (if any) below, * skip it here in case we're not called from within * the add_interface callback (otherwise it won't show * up in iteration) */ if (vif == data->new_vif) return; if (!mvmvif->phy_ctxt) return; /* currently, PHY ID == binding ID */ id = mvmvif->phy_ctxt->id; /* need at least one binding per PHY */ BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS); if (WARN_ON_ONCE(id >= MAX_BINDINGS)) return; if (data->colors[id] < 0) data->colors[id] = mvmvif->phy_ctxt->color; else WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); switch (vif->type) { case NL80211_IFTYPE_STATION: if (vif->bss_conf.assoc) data->n_interfaces[id]++; break; case NL80211_IFTYPE_AP: if (mvmvif->ap_active) data->n_interfaces[id]++; break; case NL80211_IFTYPE_MONITOR: if (mvmvif->monitor_active) data->n_interfaces[id]++; break; case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_ADHOC: if (vif->bss_conf.ibss_joined) data->n_interfaces[id]++; break; default: WARN_ON_ONCE(1); break; } }
static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); bool *global_bound = data; if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS) *global_bound = true; }
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); }
static void iwl_mvm_power_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_power_vifs *power_iterator = _data; mvmvif->pm_enabled = false; switch (ieee80211_vif_type_p2p(vif)) { case NL80211_IFTYPE_P2P_DEVICE: break; case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_AP: /* only a single MAC of the same type */ WARN_ON(power_iterator->ap_vif); power_iterator->ap_vif = vif; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->ap_active = true; break; case NL80211_IFTYPE_MONITOR: /* only a single MAC of the same type */ WARN_ON(power_iterator->monitor_vif); power_iterator->monitor_vif = vif; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->monitor_active = true; break; case NL80211_IFTYPE_P2P_CLIENT: /* only a single MAC of the same type */ WARN_ON(power_iterator->p2p_vif); power_iterator->p2p_vif = vif; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->p2p_active = true; break; case NL80211_IFTYPE_STATION: /* only a single MAC of the same type */ WARN_ON(power_iterator->bss_vif); power_iterator->bss_vif = vif; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) power_iterator->bss_active = true; if (mvmvif->bf_data.bf_enabled && !WARN_ON(power_iterator->bf_vif)) power_iterator->bf_vif = vif; break; default: break; } }
static void iwl_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_powertable_cmd *cmd) { struct ieee80211_hw *hw = mvm->hw; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *chan; int dtimper, dtimper_msec; int keep_alive; bool radar_detect = false; cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); cmd->action = cpu_to_le32(FW_CTXT_ACTION_MODIFY); if ((!vif->bss_conf.ps) || (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); dtimper = hw->conf.ps_dtim_period ?: 1; /* 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)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SLEEP_OVER_DTIM_MSK); cmd->num_skip_dtim = 2; } /* 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_LP) { /* TODO: Also for D3 (device sleep / WoWLAN) */ cmd->rx_data_timeout = cpu_to_le32(10); cmd->tx_data_timeout = cpu_to_le32(10); } else { cmd->rx_data_timeout = cpu_to_le32(50); cmd->tx_data_timeout = cpu_to_le32(50); } }
static void iwl_mvm_quota_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_quota_iterator_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); u16 id; /* skip disabled interfaces here immediately */ if (vif == data->disabled_vif) return; if (!mvmvif->phy_ctxt) return; /* currently, PHY ID == binding ID */ id = mvmvif->phy_ctxt->id; /* need at least one binding per PHY */ BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS); if (WARN_ON_ONCE(id >= MAX_BINDINGS)) return; switch (vif->type) { case NL80211_IFTYPE_STATION: if (vif->bss_conf.assoc) break; return; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: if (mvmvif->ap_ibss_active) break; return; case NL80211_IFTYPE_MONITOR: if (mvmvif->monitor_active) break; return; case NL80211_IFTYPE_P2P_DEVICE: return; default: WARN_ON_ONCE(1); return; } if (data->colors[id] < 0) data->colors[id] = mvmvif->phy_ctxt->color; else WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color); data->n_interfaces[id]++; if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { data->n_low_latency_bindings++; data->low_latency[id] = true; } }
void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); debugfs_remove(mvmvif->dbgfs_slink); mvmvif->dbgfs_slink = NULL; debugfs_remove_recursive(mvmvif->dbgfs_dir); mvmvif->dbgfs_dir = NULL; }
static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int *global_cnt = data; if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS) *global_cnt += 1; }
int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (!mvmvif->bf_data.bf_enabled) return 0; return iwl_mvm_enable_beacon_filter(mvm, vif); }
static void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); bool *disable_ps = _data; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) *disable_ps |= mvmvif->ps_disabled; }
int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool enable, u32 flags) { int ret; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_power_cmd cmd = {}; if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; if (!vif->bss_conf.assoc) return 0; iwl_mvm_power_build_cmd(mvm, vif, &cmd); if (enable) { /* configure skip over dtim up to 306TU - 314 msec */ int dtimper = vif->bss_conf.dtim_period ?: 1; int dtimper_tu = dtimper * vif->bss_conf.beacon_int; bool radar_detect = iwl_mvm_power_is_radar(vif); if (WARN_ON(!dtimper_tu)) return 0; /* Check skip over DTIM conditions */ /* TODO: check that multicast wake lock is off */ if (!radar_detect && (dtimper < 10)) { cmd.skip_dtim_periods = 306 / dtimper_tu; if (cmd.skip_dtim_periods) cmd.flags |= cpu_to_le16( POWER_FLAGS_SKIP_OVER_DTIM_MSK); } } iwl_mvm_power_log(mvm, &cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd)); #endif ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags, sizeof(cmd), &cmd); if (ret) return ret; /* configure beacon filtering */ if (mvmvif != mvm->bf_allowed_vif) return 0; if (enable) { struct iwl_beacon_filter_cmd cmd_bf = { IWL_BF_CMD_CONFIG_D0I3, .bf_enable_beacon_filter = cpu_to_le32(1), }; ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, flags, true); } else { if (mvmvif->bf_data.bf_enabled)
static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; mutex_lock(&mvm->mutex); mvmvif->phy_ctxt = phyctx; switch (vif->type) { case NL80211_IFTYPE_AP: /* * The AP binding flow is handled as part of the start_ap flow * (in bss_info_changed). */ ret = 0; goto out_unlock; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MONITOR: break; default: ret = -EINVAL; goto out_unlock; } ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) goto out_unlock; /* * Setting the quota at this stage is only required for monitor * interfaces. For the other types, the bss_info changed flow * will handle quota settings. */ if (vif->type == NL80211_IFTYPE_MONITOR) { ret = iwl_mvm_update_quotas(mvm, vif); if (ret) goto out_remove_binding; } goto out_unlock; out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); if (ret) mvmvif->phy_ctxt = NULL; return ret; }
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n", sta->addr, old_state, new_state); /* this would be a mac80211 bug ... but don't crash */ if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; /* if a STA is being removed, reuse its ID */ flush_work(&mvm->sta_drained_wk); mutex_lock(&mvm->mutex); if (old_state == IEEE80211_STA_NOTEXIST && new_state == IEEE80211_STA_NONE) { ret = iwl_mvm_add_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { ret = iwl_mvm_update_sta(mvm, vif, sta); if (ret == 0) iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_NONE) { ret = 0; } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST) { ret = iwl_mvm_rm_sta(mvm, vif, sta); } else { ret = -EIO; } mutex_unlock(&mvm->mutex); return ret; }
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; ret = iwl_mvm_mac_ctxt_changed(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); if (changes & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { /* add quota for this interface */ ret = iwl_mvm_update_quotas(mvm, vif); if (ret) { IWL_ERR(mvm, "failed to update quotas\n"); return; } } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { /* remove AP station now that the MAC is unassoc */ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); if (ret) IWL_ERR(mvm, "failed to remove AP station\n"); mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; /* remove quota for this interface */ ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) IWL_ERR(mvm, "failed to update quotas\n"); } } else if (changes & BSS_CHANGED_DTIM_PERIOD) { /* * We received a beacon _after_ association so * remove the session protection. */ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); } else if (changes & BSS_CHANGED_PS) { /* * TODO: remove this temporary code. * Currently MVM FW supports power management only on single * MAC. Avoid power mode update if more than one interface * is active. */ IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", mvm->vif_count); if (mvm->vif_count == 1) { ret = iwl_mvm_power_update_mode(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } } }
static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); if (mvmvif->features & NETIF_F_RXCSUM && desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_IP_HDR_CSUM_OK) && desc->l3l4_flags & cpu_to_le16(IWL_RX_L3L4_TCP_UDP_CSUM_OK)) skb->ip_summed = CHECKSUM_UNNECESSARY; }
static void iwl_mvm_rx_csum(struct ieee80211_sta *sta, struct sk_buff *skb, u32 status) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif); if (mvmvif->features & NETIF_F_RXCSUM && status & RX_MPDU_RES_STATUS_CSUM_DONE && status & RX_MPDU_RES_STATUS_CSUM_OK) skb->ip_summed = CHECKSUM_UNNECESSARY; }