diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/ops.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 481 |
1 files changed, 444 insertions, 37 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index a3d43de342d7..9545d7fdd4bf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c | |||
@@ -61,6 +61,7 @@ | |||
61 | * | 61 | * |
62 | *****************************************************************************/ | 62 | *****************************************************************************/ |
63 | #include <linux/module.h> | 63 | #include <linux/module.h> |
64 | #include <linux/vmalloc.h> | ||
64 | #include <net/mac80211.h> | 65 | #include <net/mac80211.h> |
65 | 66 | ||
66 | #include "iwl-notif-wait.h" | 67 | #include "iwl-notif-wait.h" |
@@ -78,6 +79,7 @@ | |||
78 | #include "iwl-prph.h" | 79 | #include "iwl-prph.h" |
79 | #include "rs.h" | 80 | #include "rs.h" |
80 | #include "fw-api-scan.h" | 81 | #include "fw-api-scan.h" |
82 | #include "fw-error-dump.h" | ||
81 | #include "time-event.h" | 83 | #include "time-event.h" |
82 | 84 | ||
83 | /* | 85 | /* |
@@ -185,9 +187,10 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) | |||
185 | * (PCIe power is lost before PERST# is asserted), causing ME FW | 187 | * (PCIe power is lost before PERST# is asserted), causing ME FW |
186 | * to lose ownership and not being able to obtain it back. | 188 | * to lose ownership and not being able to obtain it back. |
187 | */ | 189 | */ |
188 | iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, | 190 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) |
189 | APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, | 191 | iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, |
190 | ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); | 192 | APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, |
193 | ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); | ||
191 | } | 194 | } |
192 | 195 | ||
193 | struct iwl_rx_handlers { | 196 | struct iwl_rx_handlers { |
@@ -219,13 +222,17 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { | |||
219 | RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), | 222 | RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), |
220 | RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), | 223 | RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), |
221 | RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), | 224 | RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), |
225 | RX_HANDLER(ANTENNA_COUPLING_NOTIFICATION, | ||
226 | iwl_mvm_rx_ant_coupling_notif, true), | ||
222 | 227 | ||
223 | RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), | 228 | RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false), |
224 | 229 | ||
230 | RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), | ||
231 | |||
225 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), | 232 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), |
226 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), | 233 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true), |
227 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, | 234 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, |
228 | iwl_mvm_rx_scan_offload_complete_notif, false), | 235 | iwl_mvm_rx_scan_offload_complete_notif, true), |
229 | RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results, | 236 | RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results, |
230 | false), | 237 | false), |
231 | 238 | ||
@@ -242,7 +249,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { | |||
242 | #undef RX_HANDLER | 249 | #undef RX_HANDLER |
243 | #define CMD(x) [x] = #x | 250 | #define CMD(x) [x] = #x |
244 | 251 | ||
245 | static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { | 252 | static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { |
246 | CMD(MVM_ALIVE), | 253 | CMD(MVM_ALIVE), |
247 | CMD(REPLY_ERROR), | 254 | CMD(REPLY_ERROR), |
248 | CMD(INIT_COMPLETE_NOTIF), | 255 | CMD(INIT_COMPLETE_NOTIF), |
@@ -284,9 +291,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { | |||
284 | CMD(BEACON_NOTIFICATION), | 291 | CMD(BEACON_NOTIFICATION), |
285 | CMD(BEACON_TEMPLATE_CMD), | 292 | CMD(BEACON_TEMPLATE_CMD), |
286 | CMD(STATISTICS_NOTIFICATION), | 293 | CMD(STATISTICS_NOTIFICATION), |
294 | CMD(EOSP_NOTIFICATION), | ||
287 | CMD(REDUCE_TX_POWER_CMD), | 295 | CMD(REDUCE_TX_POWER_CMD), |
288 | CMD(TX_ANT_CONFIGURATION_CMD), | 296 | CMD(TX_ANT_CONFIGURATION_CMD), |
289 | CMD(D3_CONFIG_CMD), | 297 | CMD(D3_CONFIG_CMD), |
298 | CMD(D0I3_END_CMD), | ||
290 | CMD(PROT_OFFLOAD_CONFIG_CMD), | 299 | CMD(PROT_OFFLOAD_CONFIG_CMD), |
291 | CMD(OFFLOADS_QUERY_CMD), | 300 | CMD(OFFLOADS_QUERY_CMD), |
292 | CMD(REMOTE_WAKE_CONFIG_CMD), | 301 | CMD(REMOTE_WAKE_CONFIG_CMD), |
@@ -309,17 +318,37 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { | |||
309 | CMD(BT_PROFILE_NOTIFICATION), | 318 | CMD(BT_PROFILE_NOTIFICATION), |
310 | CMD(BT_CONFIG), | 319 | CMD(BT_CONFIG), |
311 | CMD(MCAST_FILTER_CMD), | 320 | CMD(MCAST_FILTER_CMD), |
321 | CMD(BCAST_FILTER_CMD), | ||
312 | CMD(REPLY_SF_CFG_CMD), | 322 | CMD(REPLY_SF_CFG_CMD), |
313 | CMD(REPLY_BEACON_FILTERING_CMD), | 323 | CMD(REPLY_BEACON_FILTERING_CMD), |
314 | CMD(REPLY_THERMAL_MNG_BACKOFF), | 324 | CMD(REPLY_THERMAL_MNG_BACKOFF), |
315 | CMD(MAC_PM_POWER_TABLE), | 325 | CMD(MAC_PM_POWER_TABLE), |
316 | CMD(BT_COEX_CI), | 326 | CMD(BT_COEX_CI), |
317 | CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), | 327 | CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), |
328 | CMD(ANTENNA_COUPLING_NOTIFICATION), | ||
318 | }; | 329 | }; |
319 | #undef CMD | 330 | #undef CMD |
320 | 331 | ||
321 | /* this forward declaration can avoid to export the function */ | 332 | /* this forward declaration can avoid to export the function */ |
322 | static void iwl_mvm_async_handlers_wk(struct work_struct *wk); | 333 | static void iwl_mvm_async_handlers_wk(struct work_struct *wk); |
334 | static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); | ||
335 | |||
336 | static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) | ||
337 | { | ||
338 | const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs; | ||
339 | |||
340 | if (!pwr_tx_backoff) | ||
341 | return 0; | ||
342 | |||
343 | while (pwr_tx_backoff->pwr) { | ||
344 | if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr) | ||
345 | return pwr_tx_backoff->backoff; | ||
346 | |||
347 | pwr_tx_backoff++; | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
323 | 352 | ||
324 | static struct iwl_op_mode * | 353 | static struct iwl_op_mode * |
325 | iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | 354 | iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, |
@@ -333,6 +362,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
333 | TX_CMD, | 362 | TX_CMD, |
334 | }; | 363 | }; |
335 | int err, scan_size; | 364 | int err, scan_size; |
365 | u32 min_backoff; | ||
366 | |||
367 | /* | ||
368 | * We use IWL_MVM_STATION_COUNT to check the validity of the station | ||
369 | * index all over the driver - check that its value corresponds to the | ||
370 | * array size. | ||
371 | */ | ||
372 | BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); | ||
336 | 373 | ||
337 | /******************************** | 374 | /******************************** |
338 | * 1. Allocating and configuring HW data | 375 | * 1. Allocating and configuring HW data |
@@ -373,6 +410,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
373 | INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); | 410 | INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); |
374 | INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); | 411 | INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); |
375 | INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); | 412 | INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); |
413 | INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); | ||
414 | |||
415 | spin_lock_init(&mvm->d0i3_tx_lock); | ||
416 | skb_queue_head_init(&mvm->d0i3_tx); | ||
417 | init_waitqueue_head(&mvm->d0i3_exit_waitq); | ||
376 | 418 | ||
377 | SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); | 419 | SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); |
378 | 420 | ||
@@ -421,7 +463,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
421 | IWL_INFO(mvm, "Detected %s, REV=0x%X\n", | 463 | IWL_INFO(mvm, "Detected %s, REV=0x%X\n", |
422 | mvm->cfg->name, mvm->trans->hw_rev); | 464 | mvm->cfg->name, mvm->trans->hw_rev); |
423 | 465 | ||
424 | iwl_mvm_tt_initialize(mvm); | 466 | min_backoff = calc_min_backoff(trans, cfg); |
467 | iwl_mvm_tt_initialize(mvm, min_backoff); | ||
425 | 468 | ||
426 | /* | 469 | /* |
427 | * If the NVM exists in an external file, | 470 | * If the NVM exists in an external file, |
@@ -462,13 +505,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
462 | if (err) | 505 | if (err) |
463 | goto out_unregister; | 506 | goto out_unregister; |
464 | 507 | ||
465 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) | ||
466 | mvm->pm_ops = &pm_mac_ops; | ||
467 | else | ||
468 | mvm->pm_ops = &pm_legacy_ops; | ||
469 | |||
470 | memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); | 508 | memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); |
471 | 509 | ||
510 | /* rpm starts with a taken ref. only set the appropriate bit here. */ | ||
511 | set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap); | ||
512 | |||
472 | return op_mode; | 513 | return op_mode; |
473 | 514 | ||
474 | out_unregister: | 515 | out_unregister: |
@@ -495,6 +536,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) | |||
495 | ieee80211_unregister_hw(mvm->hw); | 536 | ieee80211_unregister_hw(mvm->hw); |
496 | 537 | ||
497 | kfree(mvm->scan_cmd); | 538 | kfree(mvm->scan_cmd); |
539 | vfree(mvm->fw_error_dump); | ||
540 | kfree(mvm->fw_error_sram); | ||
498 | kfree(mvm->mcast_filter_cmd); | 541 | kfree(mvm->mcast_filter_cmd); |
499 | mvm->mcast_filter_cmd = NULL; | 542 | mvm->mcast_filter_cmd = NULL; |
500 | 543 | ||
@@ -508,7 +551,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) | |||
508 | mvm->phy_db = NULL; | 551 | mvm->phy_db = NULL; |
509 | 552 | ||
510 | iwl_free_nvm_data(mvm->nvm_data); | 553 | iwl_free_nvm_data(mvm->nvm_data); |
511 | for (i = 0; i < NVM_NUM_OF_SECTIONS; i++) | 554 | for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) |
512 | kfree(mvm->nvm_sections[i].data); | 555 | kfree(mvm->nvm_sections[i].data); |
513 | 556 | ||
514 | ieee80211_free_hw(mvm->hw); | 557 | ieee80211_free_hw(mvm->hw); |
@@ -658,7 +701,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) | |||
658 | wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); | 701 | wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); |
659 | } | 702 | } |
660 | 703 | ||
661 | static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) | 704 | static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) |
662 | { | 705 | { |
663 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | 706 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); |
664 | 707 | ||
@@ -667,9 +710,9 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) | |||
667 | else | 710 | else |
668 | clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); | 711 | clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); |
669 | 712 | ||
670 | if (state && mvm->cur_ucode != IWL_UCODE_INIT) | ||
671 | iwl_trans_stop_device(mvm->trans); | ||
672 | wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); | 713 | wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); |
714 | |||
715 | return state && mvm->cur_ucode != IWL_UCODE_INIT; | ||
673 | } | 716 | } |
674 | 717 | ||
675 | static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) | 718 | static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) |
@@ -703,6 +746,29 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) | |||
703 | iwl_abort_notification_waits(&mvm->notif_wait); | 746 | iwl_abort_notification_waits(&mvm->notif_wait); |
704 | 747 | ||
705 | /* | 748 | /* |
749 | * This is a bit racy, but worst case we tell mac80211 about | ||
750 | * a stopped/aborted scan when that was already done which | ||
751 | * is not a problem. It is necessary to abort any os scan | ||
752 | * here because mac80211 requires having the scan cleared | ||
753 | * before restarting. | ||
754 | * We'll reset the scan_status to NONE in restart cleanup in | ||
755 | * the next start() call from mac80211. If restart isn't called | ||
756 | * (no fw restart) scan status will stay busy. | ||
757 | */ | ||
758 | switch (mvm->scan_status) { | ||
759 | case IWL_MVM_SCAN_NONE: | ||
760 | break; | ||
761 | case IWL_MVM_SCAN_OS: | ||
762 | ieee80211_scan_completed(mvm->hw, true); | ||
763 | break; | ||
764 | case IWL_MVM_SCAN_SCHED: | ||
765 | /* Sched scan will be restarted by mac80211 in restart_hw. */ | ||
766 | if (!mvm->restart_fw) | ||
767 | ieee80211_sched_scan_stopped(mvm->hw); | ||
768 | break; | ||
769 | } | ||
770 | |||
771 | /* | ||
706 | * If we're restarting already, don't cycle restarts. | 772 | * If we're restarting already, don't cycle restarts. |
707 | * If INIT fw asserted, it will likely fail again. | 773 | * If INIT fw asserted, it will likely fail again. |
708 | * If WoWLAN fw asserted, don't restart either, mac80211 | 774 | * If WoWLAN fw asserted, don't restart either, mac80211 |
@@ -733,25 +799,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) | |||
733 | INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); | 799 | INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); |
734 | schedule_work(&reprobe->work); | 800 | schedule_work(&reprobe->work); |
735 | } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) { | 801 | } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) { |
736 | /* | 802 | /* don't let the transport/FW power down */ |
737 | * This is a bit racy, but worst case we tell mac80211 about | 803 | iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); |
738 | * a stopped/aborted (sched) scan when that was already done | ||
739 | * which is not a problem. It is necessary to abort any scan | ||
740 | * here because mac80211 requires having the scan cleared | ||
741 | * before restarting. | ||
742 | * We'll reset the scan_status to NONE in restart cleanup in | ||
743 | * the next start() call from mac80211. | ||
744 | */ | ||
745 | switch (mvm->scan_status) { | ||
746 | case IWL_MVM_SCAN_NONE: | ||
747 | break; | ||
748 | case IWL_MVM_SCAN_OS: | ||
749 | ieee80211_scan_completed(mvm->hw, true); | ||
750 | break; | ||
751 | case IWL_MVM_SCAN_SCHED: | ||
752 | ieee80211_sched_scan_stopped(mvm->hw); | ||
753 | break; | ||
754 | } | ||
755 | 804 | ||
756 | if (mvm->restart_fw > 0) | 805 | if (mvm->restart_fw > 0) |
757 | mvm->restart_fw--; | 806 | mvm->restart_fw--; |
@@ -759,13 +808,52 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) | |||
759 | } | 808 | } |
760 | } | 809 | } |
761 | 810 | ||
811 | #ifdef CONFIG_IWLWIFI_DEBUGFS | ||
812 | void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) | ||
813 | { | ||
814 | struct iwl_fw_error_dump_file *dump_file; | ||
815 | struct iwl_fw_error_dump_data *dump_data; | ||
816 | u32 file_len; | ||
817 | |||
818 | lockdep_assert_held(&mvm->mutex); | ||
819 | |||
820 | if (mvm->fw_error_dump) | ||
821 | return; | ||
822 | |||
823 | file_len = mvm->fw_error_sram_len + | ||
824 | sizeof(*dump_file) + | ||
825 | sizeof(*dump_data); | ||
826 | |||
827 | dump_file = vmalloc(file_len); | ||
828 | if (!dump_file) | ||
829 | return; | ||
830 | |||
831 | mvm->fw_error_dump = dump_file; | ||
832 | |||
833 | dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); | ||
834 | dump_file->file_len = cpu_to_le32(file_len); | ||
835 | dump_data = (void *)dump_file->data; | ||
836 | dump_data->type = IWL_FW_ERROR_DUMP_SRAM; | ||
837 | dump_data->len = cpu_to_le32(mvm->fw_error_sram_len); | ||
838 | |||
839 | /* | ||
840 | * No need for lock since at the stage the FW isn't loaded. So it | ||
841 | * can't assert - we are the only one who can possibly be accessing | ||
842 | * mvm->fw_error_sram right now. | ||
843 | */ | ||
844 | memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len); | ||
845 | } | ||
846 | #endif | ||
847 | |||
762 | static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) | 848 | static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode) |
763 | { | 849 | { |
764 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | 850 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); |
765 | 851 | ||
766 | iwl_mvm_dump_nic_error_log(mvm); | 852 | iwl_mvm_dump_nic_error_log(mvm); |
767 | if (!mvm->restart_fw) | 853 | |
768 | iwl_mvm_dump_sram(mvm); | 854 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
855 | iwl_mvm_fw_error_sram_dump(mvm); | ||
856 | #endif | ||
769 | 857 | ||
770 | iwl_mvm_nic_restart(mvm); | 858 | iwl_mvm_nic_restart(mvm); |
771 | } | 859 | } |
@@ -778,6 +866,323 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) | |||
778 | iwl_mvm_nic_restart(mvm); | 866 | iwl_mvm_nic_restart(mvm); |
779 | } | 867 | } |
780 | 868 | ||
869 | struct iwl_d0i3_iter_data { | ||
870 | struct iwl_mvm *mvm; | ||
871 | u8 ap_sta_id; | ||
872 | u8 vif_count; | ||
873 | u8 offloading_tid; | ||
874 | bool disable_offloading; | ||
875 | }; | ||
876 | |||
877 | static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, | ||
878 | struct ieee80211_vif *vif, | ||
879 | struct iwl_d0i3_iter_data *iter_data) | ||
880 | { | ||
881 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
882 | struct ieee80211_sta *ap_sta; | ||
883 | struct iwl_mvm_sta *mvmsta; | ||
884 | u32 available_tids = 0; | ||
885 | u8 tid; | ||
886 | |||
887 | if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || | ||
888 | mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) | ||
889 | return false; | ||
890 | |||
891 | ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id]); | ||
892 | if (IS_ERR_OR_NULL(ap_sta)) | ||
893 | return false; | ||
894 | |||
895 | mvmsta = iwl_mvm_sta_from_mac80211(ap_sta); | ||
896 | spin_lock_bh(&mvmsta->lock); | ||
897 | for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { | ||
898 | struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; | ||
899 | |||
900 | /* | ||
901 | * in case of pending tx packets, don't use this tid | ||
902 | * for offloading in order to prevent reuse of the same | ||
903 | * qos seq counters. | ||
904 | */ | ||
905 | if (iwl_mvm_tid_queued(tid_data)) | ||
906 | continue; | ||
907 | |||
908 | if (tid_data->state != IWL_AGG_OFF) | ||
909 | continue; | ||
910 | |||
911 | available_tids |= BIT(tid); | ||
912 | } | ||
913 | spin_unlock_bh(&mvmsta->lock); | ||
914 | |||
915 | /* | ||
916 | * disallow protocol offloading if we have no available tid | ||
917 | * (with no pending frames and no active aggregation, | ||
918 | * as we don't handle "holes" properly - the scheduler needs the | ||
919 | * frame's seq number and TFD index to match) | ||
920 | */ | ||
921 | if (!available_tids) | ||
922 | return true; | ||
923 | |||
924 | /* for simplicity, just use the first available tid */ | ||
925 | iter_data->offloading_tid = ffs(available_tids) - 1; | ||
926 | return false; | ||
927 | } | ||
928 | |||
929 | static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, | ||
930 | struct ieee80211_vif *vif) | ||
931 | { | ||
932 | struct iwl_d0i3_iter_data *data = _data; | ||
933 | struct iwl_mvm *mvm = data->mvm; | ||
934 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
935 | u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; | ||
936 | |||
937 | IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr); | ||
938 | if (vif->type != NL80211_IFTYPE_STATION || | ||
939 | !vif->bss_conf.assoc) | ||
940 | return; | ||
941 | |||
942 | /* | ||
943 | * in case of pending tx packets or active aggregations, | ||
944 | * avoid offloading features in order to prevent reuse of | ||
945 | * the same qos seq counters. | ||
946 | */ | ||
947 | if (iwl_mvm_disallow_offloading(mvm, vif, data)) | ||
948 | data->disable_offloading = true; | ||
949 | |||
950 | iwl_mvm_update_d0i3_power_mode(mvm, vif, true, flags); | ||
951 | iwl_mvm_send_proto_offload(mvm, vif, data->disable_offloading, flags); | ||
952 | |||
953 | /* | ||
954 | * on init/association, mvm already configures POWER_TABLE_CMD | ||
955 | * and REPLY_MCAST_FILTER_CMD, so currently don't | ||
956 | * reconfigure them (we might want to use different | ||
957 | * params later on, though). | ||
958 | */ | ||
959 | data->ap_sta_id = mvmvif->ap_sta_id; | ||
960 | data->vif_count++; | ||
961 | } | ||
962 | |||
963 | static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, | ||
964 | struct iwl_wowlan_config_cmd_v3 *cmd, | ||
965 | struct iwl_d0i3_iter_data *iter_data) | ||
966 | { | ||
967 | struct ieee80211_sta *ap_sta; | ||
968 | struct iwl_mvm_sta *mvm_ap_sta; | ||
969 | |||
970 | if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) | ||
971 | return; | ||
972 | |||
973 | rcu_read_lock(); | ||
974 | |||
975 | ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]); | ||
976 | if (IS_ERR_OR_NULL(ap_sta)) | ||
977 | goto out; | ||
978 | |||
979 | mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); | ||
980 | cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported; | ||
981 | cmd->offloading_tid = iter_data->offloading_tid; | ||
982 | |||
983 | /* | ||
984 | * The d0i3 uCode takes care of the nonqos counters, | ||
985 | * so configure only the qos seq ones. | ||
986 | */ | ||
987 | iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common); | ||
988 | out: | ||
989 | rcu_read_unlock(); | ||
990 | } | ||
991 | static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | ||
992 | { | ||
993 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | ||
994 | u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; | ||
995 | int ret; | ||
996 | struct iwl_d0i3_iter_data d0i3_iter_data = { | ||
997 | .mvm = mvm, | ||
998 | }; | ||
999 | struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = { | ||
1000 | .common = { | ||
1001 | .wakeup_filter = | ||
1002 | cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | | ||
1003 | IWL_WOWLAN_WAKEUP_BEACON_MISS | | ||
1004 | IWL_WOWLAN_WAKEUP_LINK_CHANGE | | ||
1005 | IWL_WOWLAN_WAKEUP_BCN_FILTERING), | ||
1006 | }, | ||
1007 | }; | ||
1008 | struct iwl_d3_manager_config d3_cfg_cmd = { | ||
1009 | .min_sleep_time = cpu_to_le32(1000), | ||
1010 | }; | ||
1011 | |||
1012 | IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); | ||
1013 | |||
1014 | /* make sure we have no running tx while configuring the qos */ | ||
1015 | set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); | ||
1016 | synchronize_net(); | ||
1017 | |||
1018 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, | ||
1019 | IEEE80211_IFACE_ITER_NORMAL, | ||
1020 | iwl_mvm_enter_d0i3_iterator, | ||
1021 | &d0i3_iter_data); | ||
1022 | if (d0i3_iter_data.vif_count == 1) { | ||
1023 | mvm->d0i3_ap_sta_id = d0i3_iter_data.ap_sta_id; | ||
1024 | mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading; | ||
1025 | } else { | ||
1026 | WARN_ON_ONCE(d0i3_iter_data.vif_count > 1); | ||
1027 | mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; | ||
1028 | mvm->d0i3_offloading = false; | ||
1029 | } | ||
1030 | |||
1031 | iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data); | ||
1032 | ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, | ||
1033 | sizeof(wowlan_config_cmd), | ||
1034 | &wowlan_config_cmd); | ||
1035 | if (ret) | ||
1036 | return ret; | ||
1037 | |||
1038 | return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, | ||
1039 | flags | CMD_MAKE_TRANS_IDLE, | ||
1040 | sizeof(d3_cfg_cmd), &d3_cfg_cmd); | ||
1041 | } | ||
1042 | |||
1043 | static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac, | ||
1044 | struct ieee80211_vif *vif) | ||
1045 | { | ||
1046 | struct iwl_mvm *mvm = _data; | ||
1047 | u32 flags = CMD_ASYNC | CMD_HIGH_PRIO; | ||
1048 | |||
1049 | IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr); | ||
1050 | if (vif->type != NL80211_IFTYPE_STATION || | ||
1051 | !vif->bss_conf.assoc) | ||
1052 | return; | ||
1053 | |||
1054 | iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); | ||
1055 | } | ||
1056 | |||
1057 | static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac, | ||
1058 | struct ieee80211_vif *vif) | ||
1059 | { | ||
1060 | struct iwl_mvm *mvm = data; | ||
1061 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
1062 | |||
1063 | if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && | ||
1064 | mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) | ||
1065 | ieee80211_connection_loss(vif); | ||
1066 | } | ||
1067 | |||
1068 | void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) | ||
1069 | { | ||
1070 | struct ieee80211_sta *sta = NULL; | ||
1071 | struct iwl_mvm_sta *mvm_ap_sta; | ||
1072 | int i; | ||
1073 | bool wake_queues = false; | ||
1074 | |||
1075 | lockdep_assert_held(&mvm->mutex); | ||
1076 | |||
1077 | spin_lock_bh(&mvm->d0i3_tx_lock); | ||
1078 | |||
1079 | if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT) | ||
1080 | goto out; | ||
1081 | |||
1082 | IWL_DEBUG_RPM(mvm, "re-enqueue packets\n"); | ||
1083 | |||
1084 | /* get the sta in order to update seq numbers and re-enqueue skbs */ | ||
1085 | sta = rcu_dereference_protected( | ||
1086 | mvm->fw_id_to_mac_id[mvm->d0i3_ap_sta_id], | ||
1087 | lockdep_is_held(&mvm->mutex)); | ||
1088 | |||
1089 | if (IS_ERR_OR_NULL(sta)) { | ||
1090 | sta = NULL; | ||
1091 | goto out; | ||
1092 | } | ||
1093 | |||
1094 | if (mvm->d0i3_offloading && qos_seq) { | ||
1095 | /* update qos seq numbers if offloading was enabled */ | ||
1096 | mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv; | ||
1097 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { | ||
1098 | u16 seq = le16_to_cpu(qos_seq[i]); | ||
1099 | /* firmware stores last-used one, we store next one */ | ||
1100 | seq += 0x10; | ||
1101 | mvm_ap_sta->tid_data[i].seq_number = seq; | ||
1102 | } | ||
1103 | } | ||
1104 | out: | ||
1105 | /* re-enqueue (or drop) all packets */ | ||
1106 | while (!skb_queue_empty(&mvm->d0i3_tx)) { | ||
1107 | struct sk_buff *skb = __skb_dequeue(&mvm->d0i3_tx); | ||
1108 | |||
1109 | if (!sta || iwl_mvm_tx_skb(mvm, skb, sta)) | ||
1110 | ieee80211_free_txskb(mvm->hw, skb); | ||
1111 | |||
1112 | /* if the skb_queue is not empty, we need to wake queues */ | ||
1113 | wake_queues = true; | ||
1114 | } | ||
1115 | clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); | ||
1116 | wake_up(&mvm->d0i3_exit_waitq); | ||
1117 | mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; | ||
1118 | if (wake_queues) | ||
1119 | ieee80211_wake_queues(mvm->hw); | ||
1120 | |||
1121 | spin_unlock_bh(&mvm->d0i3_tx_lock); | ||
1122 | } | ||
1123 | |||
1124 | static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) | ||
1125 | { | ||
1126 | struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, d0i3_exit_work); | ||
1127 | struct iwl_host_cmd get_status_cmd = { | ||
1128 | .id = WOWLAN_GET_STATUSES, | ||
1129 | .flags = CMD_SYNC | CMD_HIGH_PRIO | CMD_WANT_SKB, | ||
1130 | }; | ||
1131 | struct iwl_wowlan_status_v6 *status; | ||
1132 | int ret; | ||
1133 | u32 disconnection_reasons, wakeup_reasons; | ||
1134 | __le16 *qos_seq = NULL; | ||
1135 | |||
1136 | mutex_lock(&mvm->mutex); | ||
1137 | ret = iwl_mvm_send_cmd(mvm, &get_status_cmd); | ||
1138 | if (ret) | ||
1139 | goto out; | ||
1140 | |||
1141 | if (!get_status_cmd.resp_pkt) | ||
1142 | goto out; | ||
1143 | |||
1144 | status = (void *)get_status_cmd.resp_pkt->data; | ||
1145 | wakeup_reasons = le32_to_cpu(status->wakeup_reasons); | ||
1146 | qos_seq = status->qos_seq_ctr; | ||
1147 | |||
1148 | IWL_DEBUG_RPM(mvm, "wakeup reasons: 0x%x\n", wakeup_reasons); | ||
1149 | |||
1150 | disconnection_reasons = | ||
1151 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | | ||
1152 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; | ||
1153 | if (wakeup_reasons & disconnection_reasons) | ||
1154 | ieee80211_iterate_active_interfaces( | ||
1155 | mvm->hw, IEEE80211_IFACE_ITER_NORMAL, | ||
1156 | iwl_mvm_d0i3_disconnect_iter, mvm); | ||
1157 | |||
1158 | iwl_free_resp(&get_status_cmd); | ||
1159 | out: | ||
1160 | iwl_mvm_d0i3_enable_tx(mvm, qos_seq); | ||
1161 | mutex_unlock(&mvm->mutex); | ||
1162 | } | ||
1163 | |||
1164 | static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) | ||
1165 | { | ||
1166 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | ||
1167 | u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | | ||
1168 | CMD_WAKE_UP_TRANS; | ||
1169 | int ret; | ||
1170 | |||
1171 | IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); | ||
1172 | |||
1173 | ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); | ||
1174 | if (ret) | ||
1175 | goto out; | ||
1176 | |||
1177 | ieee80211_iterate_active_interfaces_atomic(mvm->hw, | ||
1178 | IEEE80211_IFACE_ITER_NORMAL, | ||
1179 | iwl_mvm_exit_d0i3_iterator, | ||
1180 | mvm); | ||
1181 | out: | ||
1182 | schedule_work(&mvm->d0i3_exit_work); | ||
1183 | return ret; | ||
1184 | } | ||
1185 | |||
781 | static const struct iwl_op_mode_ops iwl_mvm_ops = { | 1186 | static const struct iwl_op_mode_ops iwl_mvm_ops = { |
782 | .start = iwl_op_mode_mvm_start, | 1187 | .start = iwl_op_mode_mvm_start, |
783 | .stop = iwl_op_mode_mvm_stop, | 1188 | .stop = iwl_op_mode_mvm_stop, |
@@ -789,4 +1194,6 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = { | |||
789 | .nic_error = iwl_mvm_nic_error, | 1194 | .nic_error = iwl_mvm_nic_error, |
790 | .cmd_queue_full = iwl_mvm_cmd_queue_full, | 1195 | .cmd_queue_full = iwl_mvm_cmd_queue_full, |
791 | .nic_config = iwl_mvm_nic_config, | 1196 | .nic_config = iwl_mvm_nic_config, |
1197 | .enter_d0i3 = iwl_mvm_enter_d0i3, | ||
1198 | .exit_d0i3 = iwl_mvm_exit_d0i3, | ||
792 | }; | 1199 | }; |