diff options
author | Kalle Valo <kvalo@codeaurora.org> | 2015-04-07 19:55:57 +0300 |
---|---|---|
committer | Kalle Valo <kvalo@codeaurora.org> | 2015-04-07 19:55:57 +0300 |
commit | 6dcaac59d08b77d5730f2ae824bca1c0bf8130dc (patch) | |
tree | 5d01321c7ceedb7a9a294ac20dadee372b301d0b /drivers/net/wireless/iwlwifi/mvm | |
parent | 7abccdba25be45630eede85053496f1f48d36ec8 (diff) | |
parent | 31755207afc5d5a30e3eea9e4f2a518fc5b680c1 (diff) | |
download | linux-6dcaac59d08b77d5730f2ae824bca1c0bf8130dc.tar.xz |
Merge tag 'iwlwifi-next-for-kalle-2015-04-02' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
* some more work on LAR
* fixes for UMAC scan
* more work on debugging framework
* more work for 8000 devices
* cleanups and small bugfixes
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm')
21 files changed, 514 insertions, 157 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 5f8afa5f11a3..a6c48c7b1e16 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1131,6 +1131,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, iwl_trans_d3_suspend(mvm->trans, test); out: if (ret < 0) { + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); ieee80211_restart_hw(mvm->hw); iwl_mvm_free_nd(mvm); } @@ -1725,6 +1726,10 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, results->matched_profiles = le32_to_cpu(query->matched_profiles); memcpy(results->matches, query->matches, sizeof(results->matches)); +#ifdef CPTCFG_IWLWIFI_DEBUGFS + mvm->last_netdetect_scans = le32_to_cpu(query->n_scans_done); +#endif + out_free_resp: iwl_free_resp(&cmd); return ret; @@ -2016,6 +2021,7 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) __iwl_mvm_resume(mvm, true); rtnl_unlock(); iwl_abort_notification_waits(&mvm->notif_wait); + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); ieee80211_restart_hw(mvm->hw); /* wait for restart and disconnect all interfaces */ diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 8c5229892e57..9ac04c1ea706 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -1473,26 +1473,6 @@ out: return count; } -static ssize_t iwl_dbgfs_enable_scan_iteration_notif_write(struct iwl_mvm *mvm, - char *buf, - size_t count, - loff_t *ppos) -{ - int val; - - mutex_lock(&mvm->mutex); - - if (kstrtoint(buf, 10, &val)) { - mutex_unlock(&mvm->mutex); - return -EINVAL; - } - - mvm->scan_iter_notif_enabled = val; - mutex_unlock(&mvm->mutex); - - return count; -} - MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ @@ -1515,7 +1495,6 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); -MVM_DEBUGFS_WRITE_FILE_OPS(enable_scan_iteration_notif, 8); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); @@ -1559,8 +1538,11 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(enable_scan_iteration_notif, mvm->debugfs_dir, - S_IWUSR); + if (!debugfs_create_bool("enable_scan_iteration_notif", + S_IRUSR | S_IWUSR, + mvm->debugfs_dir, + &mvm->scan_iter_notif_enabled)) + goto err; #ifdef CONFIG_IWLWIFI_BCAST_FILTERING if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { @@ -1587,6 +1569,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR, mvm->debugfs_dir, &mvm->d3_wake_sysassert)) goto err; + if (!debugfs_create_u32("last_netdetect_scans", S_IRUSR, + mvm->debugfs_dir, &mvm->last_netdetect_scans)) + goto err; MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR); #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 6d3bea5c59d1..d7658d16e965 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -132,7 +132,7 @@ struct iwl_proto_offload_cmd_common { * @solicited_node_ipv6_addr: broken -- solicited node address exists * for each target address * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor soliciation response MAC address + * @ndp_mac_addr: neighbor solicitation response MAC address */ struct iwl_proto_offload_cmd_v1 { struct iwl_proto_offload_cmd_common common; @@ -150,7 +150,7 @@ struct iwl_proto_offload_cmd_v1 { * @solicited_node_ipv6_addr: broken -- solicited node address exists * for each target address * @target_ipv6_addr: our target addresses - * @ndp_mac_addr: neighbor soliciation response MAC address + * @ndp_mac_addr: neighbor solicitation response MAC address */ struct iwl_proto_offload_cmd_v2 { struct iwl_proto_offload_cmd_common common; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h index aabaedd3b3ee..f3f3ee0a766b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h @@ -255,7 +255,7 @@ struct iwl_mac_data_p2p_dev { /** * enum iwl_mac_filter_flags - MAC context filter flags * @MAC_FILTER_IN_PROMISC: accept all data frames - * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and + * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all management and * control frames to the host * @MAC_FILTER_ACCEPT_GRP: accept multicast frames * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index a5fbbd637070..4f81dcf57a73 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -103,7 +103,7 @@ struct iwl_ssid_ie { * @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax * @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful * (not an error!) - * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver + * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repetition the driver * asked for * @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events */ @@ -187,11 +187,11 @@ enum scan_framework_client { * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6 * @scan_flags: see enum iwl_scan_flags * @channel_count: channels in channel list - * @quiet_time: dwell time, in milisiconds, on quiet channel + * @quiet_time: dwell time, in milliseconds, on quiet channel * @quiet_plcp_th: quiet channel num of packets threshold * @good_CRC_th: passive to active promotion threshold * @rx_chain: RXON rx chain. - * @max_out_time: max TUs to be out of assoceated channel + * @max_out_time: max TUs to be out of associated channel * @suspend_time: pause scan this TUs when returning to service channel * @flags: RXON flags * @filter_flags: RXONfilter @@ -232,7 +232,7 @@ enum iwl_scan_offload_channel_flags { * see enum iwl_scan_offload_channel_flags. * __le16 channel_number: channel number 1-13 etc. * __le16 iter_count: repetition count for the channel. - * __le32 iter_interval: interval between two innteration on one channel. + * __le32 iter_interval: interval between two iterations on one channel. * u8 active_dwell. * u8 passive_dwell. */ @@ -275,8 +275,8 @@ enum iwl_scan_offload_band_selection { /** * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S * @ssid_index: index to ssid list in fixed part - * @unicast_cipher: encryption olgorithm to match - bitmap - * @aut_alg: authentication olgorithm to match - bitmap + * @unicast_cipher: encryption algorithm to match - bitmap + * @aut_alg: authentication algorithm to match - bitmap * @network_type: enum iwl_scan_offload_network_type * @band_selection: enum iwl_scan_offload_band_selection * @client_bitmap: clients waiting for match - enum scan_framework_client @@ -748,7 +748,7 @@ enum iwl_umac_scan_general_flags { * @flags: bitmap - 0-19: directed scan to i'th ssid. * @channel_num: channel number 1-13 etc. * @iter_count: repetition count for the channel. - * @iter_interval: interval between two scan interations on one channel. + * @iter_interval: interval between two scan iterations on one channel. */ struct iwl_scan_channel_cfg_umac { __le32 flags; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 6cf7d9837ca5..bc5eac4960e1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -526,16 +526,33 @@ int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, struct iwl_fw_dbg_trigger_tlv *trigger, - const char *str, size_t len) + const char *fmt, ...) { unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); u16 occurrences = le16_to_cpu(trigger->occurrences); - int ret; + int ret, len = 0; + char buf[64]; if (!occurrences) return 0; - ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), str, + if (fmt) { + va_list ap; + + buf[sizeof(buf) - 1] = '\0'; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + /* check for truncation */ + if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) + buf[sizeof(buf) - 1] = '\0'; + + len = strlen(buf) + 1; + } + + ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), buf, len, delay); if (ret) return ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 581b3b8f29f9..8088c7137f7c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -470,9 +470,8 @@ exit_fail: int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? - mvm->cfg->base_params->wd_timeout : - IWL_WATCHDOG_DISABLED; + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, false, false); u32 ac; int ret; @@ -1413,7 +1412,7 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx || rx_missed_bcon >= stop_trig_missed_bcon) - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL, 0); + iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); } int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 302c8cc50f25..84555170b6f7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -379,11 +379,13 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) { enum iwl_mcc_source used_src; struct ieee80211_regdomain *regd; + int ret; + bool changed; const struct ieee80211_regdomain *r = rtnl_dereference(mvm->hw->wiphy->regd); if (!r) - return 0; + return -ENOENT; /* save the last source in case we overwrite it below */ used_src = mvm->mcc_src; @@ -395,14 +397,19 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm) } /* Now set our last stored MCC and source */ - regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, NULL); + regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, + &changed); if (IS_ERR_OR_NULL(regd)) return -EIO; - regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); - kfree(regd); + /* update cfg80211 if the regdomain was changed */ + if (changed) + ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); + else + ret = 0; - return 0; + kfree(regd); + return ret; } int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) @@ -1007,6 +1014,9 @@ void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) mvm->fw_dump_desc = NULL; } +#define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ +#define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ + void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; @@ -1022,16 +1032,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - /* W/A for 8000 HW family A-step */ - if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) { - if (smem_len) - smem_len = 0x38000; - - if (sram2_len) - sram2_len = 0x10000; - } - fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); if (!fw_error_dump) return; @@ -1083,6 +1083,14 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) fifo_data_len + sizeof(*dump_info); + /* + * In 8000 HW family B-step include the ICCM (which resides separately) + */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + + IWL8260_ICCM_LEN; + if (mvm->fw_dump_desc) file_len += sizeof(*dump_data) + sizeof(*dump_trig) + mvm->fw_dump_desc->len; @@ -1170,6 +1178,19 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_mem->data, sram2_len); } + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(IWL8260_ICCM_LEN + + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(IWL8260_ICCM_OFFSET); + iwl_trans_read_mem_bytes(mvm->trans, IWL8260_ICCM_OFFSET, + dump_mem->data, IWL8260_ICCM_LEN); + } + fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans); fw_error_dump->op_mode_len = file_len; if (fw_error_dump->trans_ptr) @@ -1399,6 +1420,20 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) */ clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + /* We shouldn't have any UIDs still set. Loop over all the UIDs to + * make sure there's nothing left there and warn if any is found. + */ + if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) { + int i; + + for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) { + if (WARN_ONCE(mvm->scan_uid[i], + "UMAC scan UID %d was not cleaned\n", + mvm->scan_uid[i])) + mvm->scan_uid[i] = 0; + } + } + mvm->ucode_loaded = false; } @@ -1595,9 +1630,33 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif); if (tfd_msk) { + /* + * mac80211 first removes all the stations of the vif and + * then removes the vif. When it removes a station it also + * flushes the AMPDU session. So by now, all the AMPDU sessions + * of all the stations of this vif are closed, and the queues + * of these AMPDU sessions are properly closed. + * We still need to take care of the shared queues of the vif. + * Flush them here. + */ mutex_lock(&mvm->mutex); iwl_mvm_flush_tx_path(mvm, tfd_msk, true); mutex_unlock(&mvm->mutex); + + /* + * There are transports that buffer a few frames in the host. + * For these, the flush above isn't enough since while we were + * flushing, the transport might have sent more frames to the + * device. To solve this, wait here until the transport is + * empty. Technically, this could have replaced the flush + * above, but flush is much faster than draining. So flush + * first, and drain to make sure we have no frames in the + * transport anymore. + * If a station still had frames on the shared queues, it is + * already marked as draining, so to complete the draining, we + * just need to wait until the transport is empty. + */ + iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk); } if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { @@ -2167,8 +2226,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (iwl_mvm_phy_ctx_count(mvm) > 1) iwl_mvm_teardown_tdls_peers(mvm); - mutex_unlock(&mvm->mutex); - return 0; + goto out_unlock; out_quota_failed: iwl_mvm_power_update_mac(mvm); @@ -3022,6 +3080,8 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, duration, type); + flush_work(&mvm->roc_done_wk); + mutex_lock(&mvm->mutex); switch (vif->type) { @@ -3646,11 +3706,12 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + mvmvif->csa_failed = false; + IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n", chsw->chandef.center_freq1); - iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH, - NULL, 0); + iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH); switch (vif->type) { case NL80211_IFTYPE_AP: @@ -3721,6 +3782,12 @@ static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (mvmvif->csa_failed) { + mvmvif->csa_failed = false; + ret = -EIO; + goto out_unlock; + } + if (vif->type == NL80211_IFTYPE_STATION) { struct iwl_mvm_sta *mvmsta; @@ -3844,6 +3911,7 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx, mvm->radio_stats.on_time_scan; do_div(survey->time_scan, USEC_PER_MSEC); + ret = 0; out: mutex_unlock(&mvm->mutex); return ret; @@ -3889,6 +3957,64 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } +static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct ieee80211_event *event) +{ +#define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...) \ + do { \ + if ((_cnt) && --(_cnt)) \ + break; \ + iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\ + } while (0) + + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_mlme *trig_mlme; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) + return; + + if (event->u.mlme.status == MLME_SUCCESS) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); + trig_mlme = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + return; + + if (event->u.mlme.data == ASSOC_EVENT) { + if (event->u.mlme.status == MLME_DENIED) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_assoc_denied, + "DENIED ASSOC: reason %d", + event->u.mlme.reason); + else if (event->u.mlme.status == MLME_TIMEOUT) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_assoc_timeout, + "ASSOC TIMEOUT"); + } else if (event->u.mlme.data == AUTH_EVENT) { + if (event->u.mlme.status == MLME_DENIED) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_auth_denied, + "DENIED AUTH: reason %d", + event->u.mlme.reason); + else if (event->u.mlme.status == MLME_TIMEOUT) + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_auth_timeout, + "AUTH TIMEOUT"); + } else if (event->u.mlme.data == DEAUTH_RX_EVENT) { + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_rx_deauth, + "DEAUTH RX %d", event->u.mlme.reason); + } else if (event->u.mlme.data == DEAUTH_TX_EVENT) { + CHECK_MLME_TRIGGER(mvm, trig, buf, + trig_mlme->stop_tx_deauth, + "DEAUTH TX %d", event->u.mlme.reason); + } +#undef CHECK_MLME_TRIGGER +} + const struct ieee80211_ops iwl_mvm_hw_ops = { .tx = iwl_mvm_mac_tx, .ampdu_action = iwl_mvm_mac_ampdu_action, @@ -3942,6 +4068,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch, .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch, + .event_callback = iwl_mvm_mac_event_callback, + CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4b5c8f66df8b..d5522a161242 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -349,11 +349,12 @@ struct iwl_mvm_vif_bf_data { * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. * @beacon_skb: the skb used to hold the AP/GO beacon template - * @smps_requests: the SMPS requests of differents parts of the driver, + * @smps_requests: the SMPS requests of different parts of the driver, * combined on update to yield the overall request to mac80211. * @beacon_stats: beacon statistics, containing the # of received beacons, * # of received beacons accumulated over FW restart, and the current * average signal of beacons retrieved from the firmware + * @csa_failed: CSA failed to schedule time event, report an error later */ struct iwl_mvm_vif { struct iwl_mvm *mvm; @@ -433,6 +434,7 @@ struct iwl_mvm_vif { /* Indicates that CSA countdown may be started */ bool csa_countdown; + bool csa_failed; }; static inline struct iwl_mvm_vif * @@ -686,7 +688,7 @@ struct iwl_mvm { bool disable_power_off; bool disable_power_off_d3; - bool scan_iter_notif_enabled; + u32 scan_iter_notif_enabled; /* must be u32 for debugfs_create_bool */ struct debugfs_blob_wrapper nvm_hw_blob; struct debugfs_blob_wrapper nvm_sw_blob; @@ -746,6 +748,7 @@ struct iwl_mvm { void *d3_resume_sram; u32 d3_test_pme_ptr; struct ieee80211_vif *keep_vif; + u32 last_netdetect_scans; /* no. of scans in the last net-detect wake */ #endif #endif @@ -934,7 +937,8 @@ static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_wifi_mcc_supported(struct iwl_mvm *mvm) { - return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE; + return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WIFI_MCC_UPDATE || + mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC; } static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm) @@ -1146,6 +1150,7 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_upload, int iwl_mvm_scan_size(struct iwl_mvm *mvm); int iwl_mvm_cancel_scan(struct iwl_mvm *mvm); int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan); +void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm); /* Scheduled scan */ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, @@ -1475,8 +1480,12 @@ int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, struct iwl_fw_dbg_trigger_tlv *trigger, - const char *str, size_t len); - + const char *fmt, ...) __printf(3, 4); +unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool tdls, bool cmd_q); +void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + const char *errmsg); static inline bool iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig, struct ieee80211_vif *vif) @@ -1509,8 +1518,7 @@ iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, static inline void iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum iwl_fw_dbg_trigger trig, - const char *str, size_t len) + enum iwl_fw_dbg_trigger trig) { struct iwl_fw_dbg_trigger_tlv *trigger; @@ -1521,7 +1529,7 @@ iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm, if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger)) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trigger, str, len); + iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL); } #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 123e0a16aea8..87b2a30a2308 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -77,8 +77,7 @@ /* Default NVM size to read */ #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) #define IWL_MAX_NVM_SECTION_SIZE 0x1b58 -#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc -#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc +#define IWL_MAX_NVM_8000_SECTION_SIZE 0x1ffc #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 @@ -267,7 +266,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) { struct iwl_nvm_section *sections = mvm->nvm_sections; const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; - bool is_family_8000_a_step = false, lar_enabled; + bool lar_enabled; u32 mac_addr0, mac_addr1; /* Checking for required sections */ @@ -293,12 +292,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return NULL; } - if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) - is_family_8000_a_step = true; - /* PHY_SKU section is mandatory in B0 */ - if (!is_family_8000_a_step && - !mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { + if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { IWL_ERR(mvm, "Can't parse phy_sku in B0, empty sections\n"); return NULL; @@ -327,8 +322,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, regulatory, mac_override, phy_sku, mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, - lar_enabled, is_family_8000_a_step, - mac_addr0, mac_addr1); + lar_enabled, mac_addr0, mac_addr1); } #define MAX_NVM_FILE_LEN 16384 @@ -381,10 +375,8 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) /* Maximal size depends on HW family and step */ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) max_section_size = IWL_MAX_NVM_SECTION_SIZE; - else if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) - max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE; - else /* Family 8000 B-step or C-step */ - max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE; + else + max_section_size = IWL_MAX_NVM_8000_SECTION_SIZE; /* * Obtain NVM image via request_firmware. Since we already used @@ -426,6 +418,15 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2])); IWL_INFO(mvm, "NVM Manufacturing date %08X\n", le32_to_cpu(dword_buff[3])); + + /* nvm file validation, dword_buff[2] holds the file version */ + if ((CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_C_STEP && + le32_to_cpu(dword_buff[2]) < 0xE4A) || + (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_B_STEP && + le32_to_cpu(dword_buff[2]) >= 0xE4A)) { + ret = -EFAULT; + goto out; + } } else { file_sec = (void *)fw_entry->data; } @@ -524,6 +525,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) int ret, section; u32 size_read = 0; u8 *nvm_buffer, *temp; + const char *nvm_file_B = mvm->cfg->default_nvm_file_B_step; + const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step; if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) return -EINVAL; @@ -582,10 +585,27 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) /* load external NVM if configured */ if (mvm->nvm_file_name) { - /* move to External NVM flow */ + /* read External NVM file - take the default */ ret = iwl_mvm_read_external_nvm(mvm); - if (ret) - return ret; + if (ret) { + /* choose the nvm_file name according to the + * HW step + */ + if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == + SILICON_B_STEP) + mvm->nvm_file_name = nvm_file_B; + else + mvm->nvm_file_name = nvm_file_C; + + if (ret == -EFAULT && mvm->nvm_file_name) { + /* in case nvm file was failed try again */ + ret = iwl_mvm_read_external_nvm(mvm); + if (ret) + return ret; + } else { + return ret; + } + } } /* parse the relevant nvm sections */ @@ -786,13 +806,12 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm) return 0; /* - * During HW restart, only replay the last set MCC to FW. Otherwise, + * try to replay the last set MCC to FW. If it doesn't exist, * queue an update to cfg80211 to retrieve the default alpha2 from FW. */ - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - /* This should only be called during vif up and hold RTNL */ - return iwl_mvm_init_fw_regd(mvm); - } + retval = iwl_mvm_init_fw_regd(mvm); + if (retval != -ENOENT) + return retval; /* * Driver regulatory hint for initial update, this also informs the diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 80121e41ca22..a08b03d58d4b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -488,8 +488,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, /* Set a short watchdog for the command queue */ trans_cfg.cmd_q_wdg_timeout = - iwlmvm_mod_params.tfd_q_hang_detect ? IWL_DEF_WD_TIMEOUT : - IWL_WATCHDOG_DISABLED; + iwl_mvm_get_wd_timeout(mvm, NULL, false, true); snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), @@ -524,12 +523,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, /* set the nvm_file_name according to priority */ if (iwlwifi_mod_params.nvm_file) { mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; - } else { - if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) && - (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) - mvm->nvm_file_name = mvm->cfg->default_nvm_file_8000A; + } else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + if (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP) + mvm->nvm_file_name = mvm->cfg->default_nvm_file_B_step; else - mvm->nvm_file_name = mvm->cfg->default_nvm_file; + mvm->nvm_file_name = mvm->cfg->default_nvm_file_C_step; } if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, @@ -691,7 +689,6 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, { struct iwl_fw_dbg_trigger_tlv *trig; struct iwl_fw_dbg_trigger_cmd *cmds_trig; - char buf[32]; int i; if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF)) @@ -711,9 +708,9 @@ static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm, if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd) continue; - memset(buf, 0, sizeof(buf)); - snprintf(buf, sizeof(buf), "CMD 0x%02x received", pkt->hdr.cmd); - iwl_mvm_fw_dbg_collect_trig(mvm, trig, buf, sizeof(buf)); + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "CMD 0x%02x received", + pkt->hdr.cmd); break; } } @@ -894,18 +891,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) * the next start() call from mac80211. If restart isn't called * (no fw restart) scan status will stay busy. */ - switch (mvm->scan_status) { - case IWL_MVM_SCAN_NONE: - break; - case IWL_MVM_SCAN_OS: - ieee80211_scan_completed(mvm->hw, true); - break; - case IWL_MVM_SCAN_SCHED: - /* Sched scan will be restarted by mac80211 in restart_hw. */ - if (!mvm->restart_fw) - ieee80211_sched_scan_stopped(mvm->hw); - break; - } + iwl_mvm_report_scan_aborted(mvm); /* * If we're restarting already, don't cycle restarts. @@ -1175,7 +1161,7 @@ static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac, if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) - ieee80211_connection_loss(vif); + iwl_mvm_connection_loss(mvm, vif, "D0i3"); } void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 192b74bc8cf6..e68a475e3071 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -67,7 +67,7 @@ #include "fw-api.h" #include "mvm.h" -/* Maps the driver specific channel width definition to the the fw values */ +/* Maps the driver specific channel width definition to the fw values */ u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef) { switch (chandef->width) { diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 9140b0b701c7..f9928f2c125f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1277,9 +1277,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, info->status.ampdu_ack_len); } } else { - /* - * For legacy, update frame history with for each Tx retry. - */ + /* For legacy, update frame history with for each Tx retry. */ retries = info->status.rates[0].count - 1; /* HW doesn't send more than 15 retries */ retries = min(retries, 15); @@ -1615,9 +1613,9 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) static void rs_update_rate_tbl(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - struct rs_rate *rate) + struct iwl_scale_tbl_info *tbl) { - rs_fill_lq_cmd(mvm, sta, lq_sta, rate); + rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } @@ -2147,7 +2145,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, rate->type = LQ_NONE; lq_sta->search_better_tbl = 0; tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl); } return; } @@ -2310,7 +2308,7 @@ lq_update: /* Replace uCode's rate table for the destination station. */ if (update_lq) { tbl->rate.index = index; - rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl); } rs_stay_in_table(lq_sta, false); @@ -2357,8 +2355,7 @@ lq_update: rs_dump_rate(mvm, &tbl->rate, "Switch to SEARCH TABLE:"); - rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl); } else { done_search = 1; } @@ -3238,7 +3235,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, lq_cmd->agg_frame_cnt_limit = mvmsta->max_agg_bufsize; /* - * In case of low latency, tell the firwmare to leave a frame in the + * In case of low latency, tell the firmware to leave a frame in the * Tx Fifo so that it can start a transaction in the same TxOP. This * basically allows the firmware to send bursts. */ @@ -3745,7 +3742,7 @@ void iwl_mvm_rate_control_unregister(void) /** * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable - * Tx protection, according to this rquest and previous requests, + * Tx protection, according to this request and previous requests, * and send the LQ command. * @mvmsta: The station * @enable: Enable Tx protection? diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 6177e24f4c01..78ec7db64ba5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -362,7 +362,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif, trig); if (trig_check && rx_status->signal < rssi) - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0); + iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } } @@ -552,7 +552,7 @@ iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold) return; - iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0); + iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index a75bb150ea27..74e1c86289dc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -935,6 +935,8 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, cmd->n_channels = (u8)req->n_channels; + cmd->delay = cpu_to_le32(req->delay); + if (iwl_mvm_scan_pass_all(mvm, req)) flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; else @@ -1177,6 +1179,18 @@ static bool iwl_mvm_find_scan_type(struct iwl_mvm *mvm, return false; } +static int iwl_mvm_find_first_scan(struct iwl_mvm *mvm, + enum iwl_umac_scan_uid_type type) +{ + int i; + + for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) + if (mvm->scan_uid[i] & type) + return i; + + return i; +} + static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm, enum iwl_umac_scan_uid_type type) { @@ -1436,7 +1450,13 @@ int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cpu_to_le16(req->interval / MSEC_PER_SEC); sec_part->schedule[0].iter_count = 0xff; - sec_part->delay = 0; + if (req->delay > U16_MAX) { + IWL_DEBUG_SCAN(mvm, + "delay value is > 16-bits, set to max possible\n"); + sec_part->delay = cpu_to_le16(U16_MAX); + } else { + sec_part->delay = cpu_to_le16(req->delay); + } iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq, req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? @@ -1613,3 +1633,54 @@ int iwl_mvm_scan_size(struct iwl_mvm *mvm) mvm->fw->ucode_capa.n_scan_channels + sizeof(struct iwl_scan_probe_req); } + +/* + * This function is used in nic restart flow, to inform mac80211 about scans + * that was aborted by restart flow or by an assert. + */ +void iwl_mvm_report_scan_aborted(struct iwl_mvm *mvm) +{ + if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) { + u32 uid, i; + + uid = iwl_mvm_find_first_scan(mvm, IWL_UMAC_SCAN_UID_REG_SCAN); + if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS) { + ieee80211_scan_completed(mvm->hw, true); + mvm->scan_uid[uid] = 0; + } + uid = iwl_mvm_find_first_scan(mvm, + IWL_UMAC_SCAN_UID_SCHED_SCAN); + if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS && !mvm->restart_fw) { + ieee80211_sched_scan_stopped(mvm->hw); + mvm->scan_uid[uid] = 0; + } + + /* We shouldn't have any UIDs still set. Loop over all the + * UIDs to make sure there's nothing left there and warn if + * any is found. + */ + for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) { + if (WARN_ONCE(mvm->scan_uid[i], + "UMAC scan UID %d was not cleaned\n", + mvm->scan_uid[i])) + mvm->scan_uid[i] = 0; + } + } else { + switch (mvm->scan_status) { + case IWL_MVM_SCAN_NONE: + break; + case IWL_MVM_SCAN_OS: + ieee80211_scan_completed(mvm->hw, true); + break; + case IWL_MVM_SCAN_SCHED: + /* + * Sched scan will be restarted by mac80211 in + * restart_hw, so do not report if FW is about to be + * restarted. + */ + if (!mvm->restart_fw) + ieee80211_sched_scan_stopped(mvm->hw); + break; + } + } +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 50f9288368af..1845b79487c8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -209,9 +209,8 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, { unsigned long used_hw_queues; struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? - mvm->cfg->base_params->wd_timeout : - IWL_WATCHDOG_DISABLED; + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, NULL, true, false); u32 ac; lockdep_assert_held(&mvm->mutex); @@ -491,8 +490,18 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, if (vif->type == NL80211_IFTYPE_STATION && mvmvif->ap_sta_id == mvm_sta->sta_id) { + ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); + if (ret) + return ret; /* flush its queues here since we are freeing mvm_sta */ ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true); + if (ret) + return ret; + ret = iwl_trans_wait_tx_queue_empty(mvm->trans, + mvm_sta->tfd_queue_msk); + if (ret) + return ret; + ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); /* if we are associated - we can't remove the AP STA now */ if (vif->bss_conf.assoc) @@ -971,9 +980,8 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? - mvm->cfg->base_params->wd_timeout : - IWL_WATCHDOG_DISABLED; + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, sta->tdls, false); int queue, fifo, ret; u16 ssn; @@ -1120,8 +1128,12 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, spin_unlock_bh(&mvmsta->lock); if (old_state >= IWL_AGG_ON) { + iwl_mvm_drain_sta(mvm, mvmsta, true); if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true)) IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); + iwl_trans_wait_tx_queue_empty(mvm->trans, + mvmsta->tfd_queue_msk); + iwl_mvm_drain_sta(mvm, mvmsta, false); iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); @@ -1702,8 +1714,8 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, mvm_sta->disable_tx = disable; /* - * Tell mac80211 to start/stop queueing tx for this station, - * but don't stop queueing if there are still pending frames + * Tell mac80211 to start/stop queuing tx for this station, + * but don't stop queuing if there are still pending frames * for this station. */ if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id])) diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index d8f48975ad08..748f5dc3f9f4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -150,7 +150,7 @@ struct iwl_mvm_vif; * DOC: station table - AP Station in STA mode * * %iwl_mvm_vif includes the index of the AP station in the fw's STA table: - * %ap_sta_id. To get the point to the coresponsding %ieee80211_sta, + * %ap_sta_id. To get the point to the corresponding %ieee80211_sta, * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove * the AP station from the fw before setting the MAC context as unassociated. * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is @@ -209,14 +209,14 @@ struct iwl_mvm_vif; * When a trigger frame is received, mac80211 tells the driver to send frames * from the AMPDU queues or sends frames to non-aggregation queues itself, * depending on which ACs are delivery-enabled and what TID has frames to - * transmit. Note that mac80211 has all the knowledege since all the non-agg + * transmit. Note that mac80211 has all the knowledge since all the non-agg * frames are buffered / filtered, and the driver tells mac80211 about agg * frames). The driver needs to tell the fw to let frames out even if the * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count. * * When we receive a frame from that station with PM bit unset, the driver * needs to let the fw know that this station isn't asleep any more. This is - * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the + * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signaling the * station's wakeup. * * For a GO, the Service Period might be cut short due to an absence period diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 8d179ab67cc2..fd7b0d36f9a6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -119,7 +119,7 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk) /* * Flush the offchannel queue -- this is called when the time - * event finishes or is cancelled, so that frames queued for it + * event finishes or is canceled, so that frames queued for it * won't get stuck on the queue and be transmitted in the next * time event. * We have to send the command asynchronously since this cannot @@ -187,7 +187,8 @@ static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm, return false; if (errmsg) IWL_ERR(mvm, "%s\n", errmsg); - ieee80211_connection_loss(vif); + + iwl_mvm_connection_loss(mvm, vif, errmsg); return true; } @@ -196,19 +197,24 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, struct iwl_mvm_time_event_data *te_data, struct iwl_time_event_notif *notif) { - if (!le32_to_cpu(notif->status)) { - if (te_data->vif->type == NL80211_IFTYPE_STATION) - ieee80211_connection_loss(te_data->vif); + struct ieee80211_vif *vif = te_data->vif; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!notif->status) IWL_DEBUG_TE(mvm, "CSA time event failed to start\n"); - iwl_mvm_te_clear_data(mvm, te_data); - return; - } switch (te_data->vif->type) { case NL80211_IFTYPE_AP: + if (!notif->status) + mvmvif->csa_failed = true; iwl_mvm_csa_noa_start(mvm); break; case NL80211_IFTYPE_STATION: + if (!notif->status) { + iwl_mvm_connection_loss(mvm, vif, + "CSA TE failed to start"); + break; + } iwl_mvm_csa_client_absent(mvm, te_data->vif); ieee80211_chswitch_done(te_data->vif, true); break; @@ -222,6 +228,44 @@ iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm, iwl_mvm_te_clear_data(mvm, te_data); } +static void iwl_mvm_te_check_trigger(struct iwl_mvm *mvm, + struct iwl_time_event_notif *notif, + struct iwl_mvm_time_event_data *te_data) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_time_event *te_trig; + int i; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT)) + return; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TIME_EVENT); + te_trig = (void *)trig->data; + + if (!iwl_fw_dbg_trigger_check_stop(mvm, te_data->vif, trig)) + return; + + for (i = 0; i < ARRAY_SIZE(te_trig->time_events); i++) { + u32 trig_te_id = le32_to_cpu(te_trig->time_events[i].id); + u32 trig_action_bitmap = + le32_to_cpu(te_trig->time_events[i].action_bitmap); + u32 trig_status_bitmap = + le32_to_cpu(te_trig->time_events[i].status_bitmap); + + if (trig_te_id != te_data->id || + !(trig_action_bitmap & le32_to_cpu(notif->action)) || + !(trig_status_bitmap & BIT(le32_to_cpu(notif->status)))) + continue; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, + "Time event %d Action 0x%x received status: %d", + te_data->id, + le32_to_cpu(notif->action), + le32_to_cpu(notif->status)); + break; + } +} + /* * Handles a FW notification for an event that is known to the driver. * @@ -239,6 +283,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, le32_to_cpu(notif->unique_id), le32_to_cpu(notif->action)); + iwl_mvm_te_check_trigger(mvm, notif, te_data); + /* * The FW sends the start/end time event notifications even for events * that it fails to schedule. This is indicated in the status field of @@ -248,11 +294,16 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * events in the system). */ if (!le32_to_cpu(notif->status)) { - bool start = le32_to_cpu(notif->action) & - TE_V2_NOTIF_HOST_EVENT_START; - IWL_WARN(mvm, "Time Event %s notification failure\n", - start ? "start" : "end"); - if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) { + const char *msg; + + if (notif->action & cpu_to_le32(TE_V2_NOTIF_HOST_EVENT_START)) + msg = "Time Event start notification failure"; + else + msg = "Time Event end notification failure"; + + IWL_DEBUG_TE(mvm, "%s\n", msg); + + if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, msg)) { iwl_mvm_te_clear_data(mvm, te_data); return; } @@ -315,6 +366,8 @@ static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm, if (!aux_roc_te) /* Not a Aux ROC time event */ return -EINVAL; + iwl_mvm_te_check_trigger(mvm, notif, te_data); + if (!le32_to_cpu(notif->status)) { IWL_DEBUG_TE(mvm, "ERROR: Aux ROC Time Event %s notification failure\n", @@ -769,7 +822,7 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm) * Iterate over the list of aux roc time events and find the time * event that is associated with a BSS interface. * This assumes that a BSS interface can have only a single time - * event at any given time and this time event coresponds to a ROC + * event at any given time and this time event corresponds to a ROC * request */ list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) { diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h index 6f6b35db3ab8..de4fbc6d57f1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h @@ -147,7 +147,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, * @vif: the virtual interface for which the session is issued * * This functions cancels the session protection which is an act of good - * citizenship. If it is not needed any more it should be cancelled because + * citizenship. If it is not needed any more it should be canceled because * the other bindings wait for the medium during that time. * This funtions doesn't sleep. */ @@ -162,7 +162,7 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd); /** - * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity + * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionality * @mvm: the mvm component * @vif: the virtual interface for which the roc is requested. It is assumed * that the vif type is NL80211_IFTYPE_P2P_DEVICE diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index ba34dda1ae36..ef32e177f662 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -1049,6 +1049,14 @@ out: return 0; } +/* + * Note that there are transports that buffer frames before they reach + * the firmware. This means that after flush_tx_path is called, the + * queue might not be empty. The race-free way to handle this is to: + * 1) set the station as draining + * 2) flush the Tx path + * 3) wait for the transport queues to be empty + */ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) { int ret; diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 435faee0a28e..bc55a8b82db6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -122,7 +122,7 @@ int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id, } /* - * We assume that the caller set the status to the sucess value + * We assume that the caller set the status to the success value */ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, u32 *status) @@ -737,7 +737,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) } /** - * iwl_mvm_update_smps - Get a requst to change the SMPS mode + * iwl_mvm_update_smps - Get a request to change the SMPS mode * @req_type: The part of the driver who call for a change. * @smps_requests: The request to change the SMPS mode. * @@ -921,3 +921,71 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) return bss_iter_data.vif; } + +unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool tdls, bool cmd_q) +{ + struct iwl_fw_dbg_trigger_tlv *trigger; + struct iwl_fw_dbg_trigger_txq_timer *txq_timer; + unsigned int default_timeout = + cmd_q ? IWL_DEF_WD_TIMEOUT : mvm->cfg->base_params->wd_timeout; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS)) + return iwlmvm_mod_params.tfd_q_hang_detect ? + default_timeout : IWL_WATCHDOG_DISABLED; + + trigger = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_TXQ_TIMERS); + txq_timer = (void *)trigger->data; + + if (tdls) + return le32_to_cpu(txq_timer->tdls); + + if (cmd_q) + return le32_to_cpu(txq_timer->command_queue); + + if (WARN_ON(!vif)) + return default_timeout; + + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_ADHOC: + return le32_to_cpu(txq_timer->ibss); + case NL80211_IFTYPE_STATION: + return le32_to_cpu(txq_timer->bss); + case NL80211_IFTYPE_AP: + return le32_to_cpu(txq_timer->softap); + case NL80211_IFTYPE_P2P_CLIENT: + return le32_to_cpu(txq_timer->p2p_client); + case NL80211_IFTYPE_P2P_GO: + return le32_to_cpu(txq_timer->p2p_go); + case NL80211_IFTYPE_P2P_DEVICE: + return le32_to_cpu(txq_timer->p2p_device); + default: + WARN_ON(1); + return mvm->cfg->base_params->wd_timeout; + } +} + +void iwl_mvm_connection_loss(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + const char *errmsg) +{ + struct iwl_fw_dbg_trigger_tlv *trig; + struct iwl_fw_dbg_trigger_mlme *trig_mlme; + + if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME)) + goto out; + + trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME); + trig_mlme = (void *)trig->data; + if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig)) + goto out; + + if (trig_mlme->stop_connection_loss && + --trig_mlme->stop_connection_loss) + goto out; + + iwl_mvm_fw_dbg_collect_trig(mvm, trig, "%s", errmsg); + +out: + ieee80211_connection_loss(vif); +} |