diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/mac80211.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 383 |
1 files changed, 288 insertions, 95 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 2eb6ebee4467..0d6a8b768a68 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c | |||
@@ -211,7 +211,9 @@ void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) | |||
211 | return; | 211 | return; |
212 | 212 | ||
213 | IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); | 213 | IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); |
214 | WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap)); | 214 | spin_lock_bh(&mvm->refs_lock); |
215 | mvm->refs[ref_type]++; | ||
216 | spin_unlock_bh(&mvm->refs_lock); | ||
215 | iwl_trans_ref(mvm->trans); | 217 | iwl_trans_ref(mvm->trans); |
216 | } | 218 | } |
217 | 219 | ||
@@ -221,29 +223,35 @@ void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) | |||
221 | return; | 223 | return; |
222 | 224 | ||
223 | IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); | 225 | IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); |
224 | WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap)); | 226 | spin_lock_bh(&mvm->refs_lock); |
227 | WARN_ON(!mvm->refs[ref_type]--); | ||
228 | spin_unlock_bh(&mvm->refs_lock); | ||
225 | iwl_trans_unref(mvm->trans); | 229 | iwl_trans_unref(mvm->trans); |
226 | } | 230 | } |
227 | 231 | ||
228 | static void | 232 | static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm, |
229 | iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref) | 233 | enum iwl_mvm_ref_type except_ref) |
230 | { | 234 | { |
231 | int i; | 235 | int i, j; |
232 | 236 | ||
233 | if (!iwl_mvm_is_d0i3_supported(mvm)) | 237 | if (!iwl_mvm_is_d0i3_supported(mvm)) |
234 | return; | 238 | return; |
235 | 239 | ||
236 | for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) { | 240 | spin_lock_bh(&mvm->refs_lock); |
237 | if (ref == i) | 241 | for (i = 0; i < IWL_MVM_REF_COUNT; i++) { |
242 | if (except_ref == i || !mvm->refs[i]) | ||
238 | continue; | 243 | continue; |
239 | 244 | ||
240 | IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i); | 245 | IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n", |
241 | clear_bit(i, mvm->ref_bitmap); | 246 | i, mvm->refs[i]); |
242 | iwl_trans_unref(mvm->trans); | 247 | for (j = 0; j < mvm->refs[i]; j++) |
248 | iwl_trans_unref(mvm->trans); | ||
249 | mvm->refs[i] = 0; | ||
243 | } | 250 | } |
251 | spin_unlock_bh(&mvm->refs_lock); | ||
244 | } | 252 | } |
245 | 253 | ||
246 | static int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) | 254 | int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) |
247 | { | 255 | { |
248 | iwl_mvm_ref(mvm, ref_type); | 256 | iwl_mvm_ref(mvm, ref_type); |
249 | 257 | ||
@@ -321,13 +329,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) | |||
321 | hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; | 329 | hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; |
322 | } | 330 | } |
323 | 331 | ||
324 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && | ||
325 | !iwlwifi_mod_params.uapsd_disable) { | ||
326 | hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; | ||
327 | hw->uapsd_queues = IWL_UAPSD_AC_INFO; | ||
328 | hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; | ||
329 | } | ||
330 | |||
331 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) | 332 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) |
332 | hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS; | 333 | hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS; |
333 | 334 | ||
@@ -660,6 +661,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, | |||
660 | spin_unlock_bh(&mvm->time_event_lock); | 661 | spin_unlock_bh(&mvm->time_event_lock); |
661 | 662 | ||
662 | mvmvif->phy_ctxt = NULL; | 663 | mvmvif->phy_ctxt = NULL; |
664 | memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data)); | ||
663 | } | 665 | } |
664 | 666 | ||
665 | #ifdef CONFIG_IWLWIFI_DEBUGFS | 667 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
@@ -668,11 +670,11 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | |||
668 | struct iwl_fw_error_dump_file *dump_file; | 670 | struct iwl_fw_error_dump_file *dump_file; |
669 | struct iwl_fw_error_dump_data *dump_data; | 671 | struct iwl_fw_error_dump_data *dump_data; |
670 | struct iwl_fw_error_dump_info *dump_info; | 672 | struct iwl_fw_error_dump_info *dump_info; |
673 | struct iwl_mvm_dump_ptrs *fw_error_dump; | ||
671 | const struct fw_img *img; | 674 | const struct fw_img *img; |
672 | u32 sram_len, sram_ofs; | 675 | u32 sram_len, sram_ofs; |
673 | u32 file_len, rxf_len; | 676 | u32 file_len, rxf_len; |
674 | unsigned long flags; | 677 | unsigned long flags; |
675 | u32 trans_len; | ||
676 | int reg_val; | 678 | int reg_val; |
677 | 679 | ||
678 | lockdep_assert_held(&mvm->mutex); | 680 | lockdep_assert_held(&mvm->mutex); |
@@ -680,6 +682,10 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | |||
680 | if (mvm->fw_error_dump) | 682 | if (mvm->fw_error_dump) |
681 | return; | 683 | return; |
682 | 684 | ||
685 | fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL); | ||
686 | if (!fw_error_dump) | ||
687 | return; | ||
688 | |||
683 | img = &mvm->fw->img[mvm->cur_ucode]; | 689 | img = &mvm->fw->img[mvm->cur_ucode]; |
684 | sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; | 690 | sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; |
685 | sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; | 691 | sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; |
@@ -697,18 +703,15 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | |||
697 | rxf_len + | 703 | rxf_len + |
698 | sizeof(*dump_info); | 704 | sizeof(*dump_info); |
699 | 705 | ||
700 | trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); | ||
701 | if (trans_len) | ||
702 | file_len += trans_len; | ||
703 | |||
704 | dump_file = vzalloc(file_len); | 706 | dump_file = vzalloc(file_len); |
705 | if (!dump_file) | 707 | if (!dump_file) { |
708 | kfree(fw_error_dump); | ||
706 | return; | 709 | return; |
710 | } | ||
707 | 711 | ||
708 | mvm->fw_error_dump = dump_file; | 712 | fw_error_dump->op_mode_ptr = dump_file; |
709 | 713 | ||
710 | dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); | 714 | dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); |
711 | dump_file->file_len = cpu_to_le32(file_len); | ||
712 | dump_data = (void *)dump_file->data; | 715 | dump_data = (void *)dump_file->data; |
713 | 716 | ||
714 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); | 717 | dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); |
@@ -749,14 +752,12 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | |||
749 | iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, | 752 | iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, |
750 | sram_len); | 753 | sram_len); |
751 | 754 | ||
752 | if (trans_len) { | 755 | fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans); |
753 | void *buf = iwl_fw_error_next_data(dump_data); | 756 | fw_error_dump->op_mode_len = file_len; |
754 | u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, | 757 | if (fw_error_dump->trans_ptr) |
755 | trans_len); | 758 | file_len += fw_error_dump->trans_ptr->len; |
756 | dump_data = (void *)((u8 *)buf + real_trans_len); | 759 | dump_file->file_len = cpu_to_le32(file_len); |
757 | dump_file->file_len = | 760 | mvm->fw_error_dump = fw_error_dump; |
758 | cpu_to_le32(file_len - trans_len + real_trans_len); | ||
759 | } | ||
760 | } | 761 | } |
761 | #endif | 762 | #endif |
762 | 763 | ||
@@ -788,6 +789,12 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) | |||
788 | iwl_mvm_reset_phy_ctxts(mvm); | 789 | iwl_mvm_reset_phy_ctxts(mvm); |
789 | memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); | 790 | memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); |
790 | memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); | 791 | memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); |
792 | memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); | ||
793 | memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old)); | ||
794 | memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); | ||
795 | memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old)); | ||
796 | memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk)); | ||
797 | memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk)); | ||
791 | 798 | ||
792 | ieee80211_wake_queues(mvm->hw); | 799 | ieee80211_wake_queues(mvm->hw); |
793 | 800 | ||
@@ -1399,6 +1406,28 @@ static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, | |||
1399 | } | 1406 | } |
1400 | #endif | 1407 | #endif |
1401 | 1408 | ||
1409 | static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) | ||
1410 | { | ||
1411 | struct ieee80211_sta *sta; | ||
1412 | struct iwl_mvm_sta *mvmsta; | ||
1413 | int i; | ||
1414 | |||
1415 | lockdep_assert_held(&mvm->mutex); | ||
1416 | |||
1417 | for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { | ||
1418 | sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], | ||
1419 | lockdep_is_held(&mvm->mutex)); | ||
1420 | if (!sta || IS_ERR(sta) || !sta->tdls) | ||
1421 | continue; | ||
1422 | |||
1423 | mvmsta = iwl_mvm_sta_from_mac80211(sta); | ||
1424 | ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, | ||
1425 | NL80211_TDLS_TEARDOWN, | ||
1426 | WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, | ||
1427 | GFP_KERNEL); | ||
1428 | } | ||
1429 | } | ||
1430 | |||
1402 | static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, | 1431 | static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, |
1403 | struct ieee80211_vif *vif, | 1432 | struct ieee80211_vif *vif, |
1404 | struct ieee80211_bss_conf *bss_conf, | 1433 | struct ieee80211_bss_conf *bss_conf, |
@@ -1494,14 +1523,18 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, | |||
1494 | */ | 1523 | */ |
1495 | iwl_mvm_remove_time_event(mvm, mvmvif, | 1524 | iwl_mvm_remove_time_event(mvm, mvmvif, |
1496 | &mvmvif->time_event_data); | 1525 | &mvmvif->time_event_data); |
1497 | iwl_mvm_sf_update(mvm, vif, false); | ||
1498 | WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); | ||
1499 | } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | | 1526 | } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | |
1500 | BSS_CHANGED_QOS)) { | 1527 | BSS_CHANGED_QOS)) { |
1501 | ret = iwl_mvm_power_update_mac(mvm); | 1528 | ret = iwl_mvm_power_update_mac(mvm); |
1502 | if (ret) | 1529 | if (ret) |
1503 | IWL_ERR(mvm, "failed to update power mode\n"); | 1530 | IWL_ERR(mvm, "failed to update power mode\n"); |
1504 | } | 1531 | } |
1532 | |||
1533 | if (changes & BSS_CHANGED_BEACON_INFO) { | ||
1534 | iwl_mvm_sf_update(mvm, vif, false); | ||
1535 | WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); | ||
1536 | } | ||
1537 | |||
1505 | if (changes & BSS_CHANGED_TXPOWER) { | 1538 | if (changes & BSS_CHANGED_TXPOWER) { |
1506 | IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", | 1539 | IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n", |
1507 | bss_conf->txpower); | 1540 | bss_conf->txpower); |
@@ -1533,6 +1566,14 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, | |||
1533 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | 1566 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
1534 | int ret; | 1567 | int ret; |
1535 | 1568 | ||
1569 | /* | ||
1570 | * iwl_mvm_mac_ctxt_add() might read directly from the device | ||
1571 | * (the system time), so make sure it is available. | ||
1572 | */ | ||
1573 | ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP); | ||
1574 | if (ret) | ||
1575 | return ret; | ||
1576 | |||
1536 | mutex_lock(&mvm->mutex); | 1577 | mutex_lock(&mvm->mutex); |
1537 | 1578 | ||
1538 | /* Send the beacon template */ | 1579 | /* Send the beacon template */ |
@@ -1581,6 +1622,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, | |||
1581 | 1622 | ||
1582 | iwl_mvm_bt_coex_vif_change(mvm); | 1623 | iwl_mvm_bt_coex_vif_change(mvm); |
1583 | 1624 | ||
1625 | /* we don't support TDLS during DCM */ | ||
1626 | if (iwl_mvm_phy_ctx_count(mvm) > 1) | ||
1627 | iwl_mvm_teardown_tdls_peers(mvm); | ||
1628 | |||
1584 | mutex_unlock(&mvm->mutex); | 1629 | mutex_unlock(&mvm->mutex); |
1585 | return 0; | 1630 | return 0; |
1586 | 1631 | ||
@@ -1594,6 +1639,7 @@ out_remove: | |||
1594 | iwl_mvm_mac_ctxt_remove(mvm, vif); | 1639 | iwl_mvm_mac_ctxt_remove(mvm, vif); |
1595 | out_unlock: | 1640 | out_unlock: |
1596 | mutex_unlock(&mvm->mutex); | 1641 | mutex_unlock(&mvm->mutex); |
1642 | iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP); | ||
1597 | return ret; | 1643 | return ret; |
1598 | } | 1644 | } |
1599 | 1645 | ||
@@ -1671,6 +1717,14 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, | |||
1671 | { | 1717 | { |
1672 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 1718 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
1673 | 1719 | ||
1720 | /* | ||
1721 | * iwl_mvm_bss_info_changed_station() might call | ||
1722 | * iwl_mvm_protect_session(), which reads directly from | ||
1723 | * the device (the system time), so make sure it is available. | ||
1724 | */ | ||
1725 | if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED)) | ||
1726 | return; | ||
1727 | |||
1674 | mutex_lock(&mvm->mutex); | 1728 | mutex_lock(&mvm->mutex); |
1675 | 1729 | ||
1676 | if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) | 1730 | if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) |
@@ -1690,8 +1744,50 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, | |||
1690 | } | 1744 | } |
1691 | 1745 | ||
1692 | mutex_unlock(&mvm->mutex); | 1746 | mutex_unlock(&mvm->mutex); |
1747 | iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED); | ||
1693 | } | 1748 | } |
1694 | 1749 | ||
1750 | static int iwl_mvm_cancel_scan_wait_notif(struct iwl_mvm *mvm, | ||
1751 | enum iwl_scan_status scan_type) | ||
1752 | { | ||
1753 | int ret; | ||
1754 | bool wait_for_handlers = false; | ||
1755 | |||
1756 | mutex_lock(&mvm->mutex); | ||
1757 | |||
1758 | if (mvm->scan_status != scan_type) { | ||
1759 | ret = 0; | ||
1760 | /* make sure there are no pending notifications */ | ||
1761 | wait_for_handlers = true; | ||
1762 | goto out; | ||
1763 | } | ||
1764 | |||
1765 | switch (scan_type) { | ||
1766 | case IWL_MVM_SCAN_SCHED: | ||
1767 | ret = iwl_mvm_scan_offload_stop(mvm, true); | ||
1768 | break; | ||
1769 | case IWL_MVM_SCAN_OS: | ||
1770 | ret = iwl_mvm_cancel_scan(mvm); | ||
1771 | break; | ||
1772 | case IWL_MVM_SCAN_NONE: | ||
1773 | default: | ||
1774 | WARN_ON_ONCE(1); | ||
1775 | ret = -EINVAL; | ||
1776 | break; | ||
1777 | } | ||
1778 | if (ret) | ||
1779 | goto out; | ||
1780 | |||
1781 | wait_for_handlers = true; | ||
1782 | out: | ||
1783 | mutex_unlock(&mvm->mutex); | ||
1784 | |||
1785 | /* make sure we consume the completion notification */ | ||
1786 | if (wait_for_handlers) | ||
1787 | iwl_mvm_wait_for_async_handlers(mvm); | ||
1788 | |||
1789 | return ret; | ||
1790 | } | ||
1695 | static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, | 1791 | static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, |
1696 | struct ieee80211_vif *vif, | 1792 | struct ieee80211_vif *vif, |
1697 | struct ieee80211_scan_request *hw_req) | 1793 | struct ieee80211_scan_request *hw_req) |
@@ -1704,19 +1800,13 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, | |||
1704 | req->n_channels > mvm->fw->ucode_capa.n_scan_channels) | 1800 | req->n_channels > mvm->fw->ucode_capa.n_scan_channels) |
1705 | return -EINVAL; | 1801 | return -EINVAL; |
1706 | 1802 | ||
1803 | ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED); | ||
1804 | if (ret) | ||
1805 | return ret; | ||
1806 | |||
1707 | mutex_lock(&mvm->mutex); | 1807 | mutex_lock(&mvm->mutex); |
1708 | 1808 | ||
1709 | switch (mvm->scan_status) { | 1809 | if (mvm->scan_status != IWL_MVM_SCAN_NONE) { |
1710 | case IWL_MVM_SCAN_SCHED: | ||
1711 | ret = iwl_mvm_scan_offload_stop(mvm, true); | ||
1712 | if (ret) { | ||
1713 | ret = -EBUSY; | ||
1714 | goto out; | ||
1715 | } | ||
1716 | break; | ||
1717 | case IWL_MVM_SCAN_NONE: | ||
1718 | break; | ||
1719 | default: | ||
1720 | ret = -EBUSY; | 1810 | ret = -EBUSY; |
1721 | goto out; | 1811 | goto out; |
1722 | } | 1812 | } |
@@ -1732,8 +1822,6 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, | |||
1732 | iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); | 1822 | iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); |
1733 | out: | 1823 | out: |
1734 | mutex_unlock(&mvm->mutex); | 1824 | mutex_unlock(&mvm->mutex); |
1735 | /* make sure to flush the Rx handler before the next scan arrives */ | ||
1736 | iwl_mvm_wait_for_async_handlers(mvm); | ||
1737 | return ret; | 1825 | return ret; |
1738 | } | 1826 | } |
1739 | 1827 | ||
@@ -1885,28 +1973,6 @@ static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, | |||
1885 | iwl_mvm_power_update_mac(mvm); | 1973 | iwl_mvm_power_update_mac(mvm); |
1886 | } | 1974 | } |
1887 | 1975 | ||
1888 | static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) | ||
1889 | { | ||
1890 | struct ieee80211_sta *sta; | ||
1891 | struct iwl_mvm_sta *mvmsta; | ||
1892 | int i; | ||
1893 | |||
1894 | lockdep_assert_held(&mvm->mutex); | ||
1895 | |||
1896 | for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { | ||
1897 | sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], | ||
1898 | lockdep_is_held(&mvm->mutex)); | ||
1899 | if (!sta || IS_ERR(sta) || !sta->tdls) | ||
1900 | continue; | ||
1901 | |||
1902 | mvmsta = iwl_mvm_sta_from_mac80211(sta); | ||
1903 | ieee80211_tdls_oper_request(mvmsta->vif, sta->addr, | ||
1904 | NL80211_TDLS_TEARDOWN, | ||
1905 | WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, | ||
1906 | GFP_KERNEL); | ||
1907 | } | ||
1908 | } | ||
1909 | |||
1910 | static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, | 1976 | static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, |
1911 | struct ieee80211_vif *vif, | 1977 | struct ieee80211_vif *vif, |
1912 | struct ieee80211_sta *sta, | 1978 | struct ieee80211_sta *sta, |
@@ -2065,10 +2131,19 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, | |||
2065 | if (WARN_ON_ONCE(vif->bss_conf.assoc)) | 2131 | if (WARN_ON_ONCE(vif->bss_conf.assoc)) |
2066 | return; | 2132 | return; |
2067 | 2133 | ||
2134 | /* | ||
2135 | * iwl_mvm_protect_session() reads directly from the device | ||
2136 | * (the system time), so make sure it is available. | ||
2137 | */ | ||
2138 | if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX)) | ||
2139 | return; | ||
2140 | |||
2068 | mutex_lock(&mvm->mutex); | 2141 | mutex_lock(&mvm->mutex); |
2069 | /* Try really hard to protect the session and hear a beacon */ | 2142 | /* Try really hard to protect the session and hear a beacon */ |
2070 | iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500); | 2143 | iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500); |
2071 | mutex_unlock(&mvm->mutex); | 2144 | mutex_unlock(&mvm->mutex); |
2145 | |||
2146 | iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX); | ||
2072 | } | 2147 | } |
2073 | 2148 | ||
2074 | static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, | 2149 | static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, |
@@ -2077,10 +2152,19 @@ static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw, | |||
2077 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 2152 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
2078 | u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; | 2153 | u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int; |
2079 | 2154 | ||
2155 | /* | ||
2156 | * iwl_mvm_protect_session() reads directly from the device | ||
2157 | * (the system time), so make sure it is available. | ||
2158 | */ | ||
2159 | if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS)) | ||
2160 | return; | ||
2161 | |||
2080 | mutex_lock(&mvm->mutex); | 2162 | mutex_lock(&mvm->mutex); |
2081 | /* Protect the session to hear the TDLS setup response on the channel */ | 2163 | /* Protect the session to hear the TDLS setup response on the channel */ |
2082 | iwl_mvm_protect_session(mvm, vif, duration, duration, 100); | 2164 | iwl_mvm_protect_session(mvm, vif, duration, duration, 100); |
2083 | mutex_unlock(&mvm->mutex); | 2165 | mutex_unlock(&mvm->mutex); |
2166 | |||
2167 | iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS); | ||
2084 | } | 2168 | } |
2085 | 2169 | ||
2086 | static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, | 2170 | static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, |
@@ -2091,6 +2175,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, | |||
2091 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 2175 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
2092 | int ret; | 2176 | int ret; |
2093 | 2177 | ||
2178 | ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS); | ||
2179 | if (ret) | ||
2180 | return ret; | ||
2181 | |||
2094 | mutex_lock(&mvm->mutex); | 2182 | mutex_lock(&mvm->mutex); |
2095 | 2183 | ||
2096 | if (!iwl_mvm_is_idle(mvm)) { | 2184 | if (!iwl_mvm_is_idle(mvm)) { |
@@ -2098,26 +2186,7 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, | |||
2098 | goto out; | 2186 | goto out; |
2099 | } | 2187 | } |
2100 | 2188 | ||
2101 | switch (mvm->scan_status) { | 2189 | if (mvm->scan_status != IWL_MVM_SCAN_NONE) { |
2102 | case IWL_MVM_SCAN_OS: | ||
2103 | IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n"); | ||
2104 | ret = iwl_mvm_cancel_scan(mvm); | ||
2105 | if (ret) { | ||
2106 | ret = -EBUSY; | ||
2107 | goto out; | ||
2108 | } | ||
2109 | |||
2110 | /* | ||
2111 | * iwl_mvm_rx_scan_complete() will be called soon but will | ||
2112 | * not reset the scan status as it won't be IWL_MVM_SCAN_OS | ||
2113 | * any more since we queue the next scan immediately (below). | ||
2114 | * We make sure it is called before the next scan starts by | ||
2115 | * flushing the async-handlers work. | ||
2116 | */ | ||
2117 | break; | ||
2118 | case IWL_MVM_SCAN_NONE: | ||
2119 | break; | ||
2120 | default: | ||
2121 | ret = -EBUSY; | 2190 | ret = -EBUSY; |
2122 | goto out; | 2191 | goto out; |
2123 | } | 2192 | } |
@@ -2145,8 +2214,6 @@ err: | |||
2145 | mvm->scan_status = IWL_MVM_SCAN_NONE; | 2214 | mvm->scan_status = IWL_MVM_SCAN_NONE; |
2146 | out: | 2215 | out: |
2147 | mutex_unlock(&mvm->mutex); | 2216 | mutex_unlock(&mvm->mutex); |
2148 | /* make sure to flush the Rx handler before the next scan arrives */ | ||
2149 | iwl_mvm_wait_for_async_handlers(mvm); | ||
2150 | return ret; | 2217 | return ret; |
2151 | } | 2218 | } |
2152 | 2219 | ||
@@ -2266,6 +2333,119 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, | |||
2266 | } | 2333 | } |
2267 | 2334 | ||
2268 | 2335 | ||
2336 | static bool iwl_mvm_rx_aux_roc(struct iwl_notif_wait_data *notif_wait, | ||
2337 | struct iwl_rx_packet *pkt, void *data) | ||
2338 | { | ||
2339 | struct iwl_mvm *mvm = | ||
2340 | container_of(notif_wait, struct iwl_mvm, notif_wait); | ||
2341 | struct iwl_hs20_roc_res *resp; | ||
2342 | int resp_len = iwl_rx_packet_payload_len(pkt); | ||
2343 | struct iwl_mvm_time_event_data *te_data = data; | ||
2344 | |||
2345 | if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD)) | ||
2346 | return true; | ||
2347 | |||
2348 | if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { | ||
2349 | IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n"); | ||
2350 | return true; | ||
2351 | } | ||
2352 | |||
2353 | resp = (void *)pkt->data; | ||
2354 | |||
2355 | IWL_DEBUG_TE(mvm, | ||
2356 | "Aux ROC: Recieved response from ucode: status=%d uid=%d\n", | ||
2357 | resp->status, resp->event_unique_id); | ||
2358 | |||
2359 | te_data->uid = le32_to_cpu(resp->event_unique_id); | ||
2360 | IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n", | ||
2361 | te_data->uid); | ||
2362 | |||
2363 | spin_lock_bh(&mvm->time_event_lock); | ||
2364 | list_add_tail(&te_data->list, &mvm->aux_roc_te_list); | ||
2365 | spin_unlock_bh(&mvm->time_event_lock); | ||
2366 | |||
2367 | return true; | ||
2368 | } | ||
2369 | |||
2370 | #define AUX_ROC_MAX_DELAY_ON_CHANNEL 5000 | ||
2371 | static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm, | ||
2372 | struct ieee80211_channel *channel, | ||
2373 | struct ieee80211_vif *vif, | ||
2374 | int duration) | ||
2375 | { | ||
2376 | int res, time_reg = DEVICE_SYSTEM_TIME_REG; | ||
2377 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
2378 | struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data; | ||
2379 | static const u8 time_event_response[] = { HOT_SPOT_CMD }; | ||
2380 | struct iwl_notification_wait wait_time_event; | ||
2381 | struct iwl_hs20_roc_req aux_roc_req = { | ||
2382 | .action = cpu_to_le32(FW_CTXT_ACTION_ADD), | ||
2383 | .id_and_color = | ||
2384 | cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)), | ||
2385 | .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id), | ||
2386 | /* Set the channel info data */ | ||
2387 | .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ? | ||
2388 | PHY_BAND_24 : PHY_BAND_5, | ||
2389 | .channel_info.channel = channel->hw_value, | ||
2390 | .channel_info.width = PHY_VHT_CHANNEL_MODE20, | ||
2391 | /* Set the time and duration */ | ||
2392 | .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)), | ||
2393 | .apply_time_max_delay = | ||
2394 | cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)), | ||
2395 | .duration = cpu_to_le32(MSEC_TO_TU(duration)), | ||
2396 | }; | ||
2397 | |||
2398 | /* Set the node address */ | ||
2399 | memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN); | ||
2400 | |||
2401 | te_data->vif = vif; | ||
2402 | te_data->duration = duration; | ||
2403 | te_data->id = HOT_SPOT_CMD; | ||
2404 | |||
2405 | lockdep_assert_held(&mvm->mutex); | ||
2406 | |||
2407 | spin_lock_bh(&mvm->time_event_lock); | ||
2408 | list_add_tail(&te_data->list, &mvm->time_event_list); | ||
2409 | spin_unlock_bh(&mvm->time_event_lock); | ||
2410 | |||
2411 | /* | ||
2412 | * Use a notification wait, which really just processes the | ||
2413 | * command response and doesn't wait for anything, in order | ||
2414 | * to be able to process the response and get the UID inside | ||
2415 | * the RX path. Using CMD_WANT_SKB doesn't work because it | ||
2416 | * stores the buffer and then wakes up this thread, by which | ||
2417 | * time another notification (that the time event started) | ||
2418 | * might already be processed unsuccessfully. | ||
2419 | */ | ||
2420 | iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event, | ||
2421 | time_event_response, | ||
2422 | ARRAY_SIZE(time_event_response), | ||
2423 | iwl_mvm_rx_aux_roc, te_data); | ||
2424 | |||
2425 | res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req), | ||
2426 | &aux_roc_req); | ||
2427 | |||
2428 | if (res) { | ||
2429 | IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res); | ||
2430 | iwl_remove_notification(&mvm->notif_wait, &wait_time_event); | ||
2431 | goto out_clear_te; | ||
2432 | } | ||
2433 | |||
2434 | /* No need to wait for anything, so just pass 1 (0 isn't valid) */ | ||
2435 | res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1); | ||
2436 | /* should never fail */ | ||
2437 | WARN_ON_ONCE(res); | ||
2438 | |||
2439 | if (res) { | ||
2440 | out_clear_te: | ||
2441 | spin_lock_bh(&mvm->time_event_lock); | ||
2442 | iwl_mvm_te_clear_data(mvm, te_data); | ||
2443 | spin_unlock_bh(&mvm->time_event_lock); | ||
2444 | } | ||
2445 | |||
2446 | return res; | ||
2447 | } | ||
2448 | |||
2269 | static int iwl_mvm_roc(struct ieee80211_hw *hw, | 2449 | static int iwl_mvm_roc(struct ieee80211_hw *hw, |
2270 | struct ieee80211_vif *vif, | 2450 | struct ieee80211_vif *vif, |
2271 | struct ieee80211_channel *channel, | 2451 | struct ieee80211_channel *channel, |
@@ -2281,8 +2461,17 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, | |||
2281 | IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, | 2461 | IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, |
2282 | duration, type); | 2462 | duration, type); |
2283 | 2463 | ||
2284 | if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { | 2464 | switch (vif->type) { |
2285 | IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type); | 2465 | case NL80211_IFTYPE_STATION: |
2466 | /* Use aux roc framework (HS20) */ | ||
2467 | ret = iwl_mvm_send_aux_roc_cmd(mvm, channel, | ||
2468 | vif, duration); | ||
2469 | return ret; | ||
2470 | case NL80211_IFTYPE_P2P_DEVICE: | ||
2471 | /* handle below */ | ||
2472 | break; | ||
2473 | default: | ||
2474 | IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type); | ||
2286 | return -EINVAL; | 2475 | return -EINVAL; |
2287 | } | 2476 | } |
2288 | 2477 | ||
@@ -2661,6 +2850,10 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, | |||
2661 | goto out_remove; | 2850 | goto out_remove; |
2662 | } | 2851 | } |
2663 | 2852 | ||
2853 | /* we don't support TDLS during DCM - can be caused by channel switch */ | ||
2854 | if (iwl_mvm_phy_ctx_count(mvm) > 1) | ||
2855 | iwl_mvm_teardown_tdls_peers(mvm); | ||
2856 | |||
2664 | goto out; | 2857 | goto out; |
2665 | 2858 | ||
2666 | out_remove: | 2859 | out_remove: |