diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-01-22 07:02:09 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-02-12 10:52:25 -0500 |
commit | 9b26b5002937850846046a99de8c6065ff86fa6d (patch) | |
tree | 068c55f80755b4c2e7a75fc0ffe89f5309f3acb0 | |
parent | 5718d27fc9a076ca397ea8c3d70e29969be24b9b (diff) |
iwlwifi: mvm: report wakeup reasons
Query the wakeup reasons properly and then
report them to mac80211.
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/d3.c | 168 |
1 files changed, 141 insertions, 27 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 5b2c675a8752..a00267f6bc05 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c | |||
@@ -763,6 +763,146 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | |||
763 | return ret; | 763 | return ret; |
764 | } | 764 | } |
765 | 765 | ||
766 | static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | ||
767 | struct ieee80211_vif *vif) | ||
768 | { | ||
769 | u32 base = mvm->error_event_table; | ||
770 | struct error_table_start { | ||
771 | /* cf. struct iwl_error_event_table */ | ||
772 | u32 valid; | ||
773 | u32 error_id; | ||
774 | } err_info; | ||
775 | struct cfg80211_wowlan_wakeup wakeup = { | ||
776 | .pattern_idx = -1, | ||
777 | }; | ||
778 | struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; | ||
779 | struct iwl_host_cmd cmd = { | ||
780 | .id = WOWLAN_GET_STATUSES, | ||
781 | .flags = CMD_SYNC | CMD_WANT_SKB, | ||
782 | }; | ||
783 | struct iwl_wowlan_status *status; | ||
784 | u32 reasons; | ||
785 | int ret, len; | ||
786 | bool pkt8023 = false; | ||
787 | struct sk_buff *pkt = NULL; | ||
788 | |||
789 | iwl_trans_read_mem_bytes(mvm->trans, base, | ||
790 | &err_info, sizeof(err_info)); | ||
791 | |||
792 | if (err_info.valid) { | ||
793 | IWL_INFO(mvm, "error table is valid (%d)\n", | ||
794 | err_info.valid); | ||
795 | if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) { | ||
796 | wakeup.rfkill_release = true; | ||
797 | ieee80211_report_wowlan_wakeup(vif, &wakeup, | ||
798 | GFP_KERNEL); | ||
799 | } | ||
800 | return; | ||
801 | } | ||
802 | |||
803 | /* only for tracing for now */ | ||
804 | ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL); | ||
805 | if (ret) | ||
806 | IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); | ||
807 | |||
808 | ret = iwl_mvm_send_cmd(mvm, &cmd); | ||
809 | if (ret) { | ||
810 | IWL_ERR(mvm, "failed to query status (%d)\n", ret); | ||
811 | return; | ||
812 | } | ||
813 | |||
814 | /* RF-kill already asserted again... */ | ||
815 | if (!cmd.resp_pkt) | ||
816 | return; | ||
817 | |||
818 | len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; | ||
819 | if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { | ||
820 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); | ||
821 | goto out; | ||
822 | } | ||
823 | |||
824 | status = (void *)cmd.resp_pkt->data; | ||
825 | |||
826 | if (len - sizeof(struct iwl_cmd_header) != | ||
827 | sizeof(*status) + le32_to_cpu(status->wake_packet_bufsize)) { | ||
828 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); | ||
829 | goto out; | ||
830 | } | ||
831 | |||
832 | reasons = le32_to_cpu(status->wakeup_reasons); | ||
833 | |||
834 | if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { | ||
835 | wakeup_report = NULL; | ||
836 | goto report; | ||
837 | } | ||
838 | |||
839 | if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) { | ||
840 | wakeup.magic_pkt = true; | ||
841 | pkt8023 = true; | ||
842 | } | ||
843 | |||
844 | if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) { | ||
845 | wakeup.pattern_idx = | ||
846 | le16_to_cpu(status->pattern_number); | ||
847 | pkt8023 = true; | ||
848 | } | ||
849 | |||
850 | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | | ||
851 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) | ||
852 | wakeup.disconnect = true; | ||
853 | |||
854 | if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) { | ||
855 | wakeup.gtk_rekey_failure = true; | ||
856 | pkt8023 = true; | ||
857 | } | ||
858 | |||
859 | if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) { | ||
860 | wakeup.rfkill_release = true; | ||
861 | pkt8023 = true; | ||
862 | } | ||
863 | |||
864 | if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) { | ||
865 | wakeup.eap_identity_req = true; | ||
866 | pkt8023 = true; | ||
867 | } | ||
868 | |||
869 | if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) { | ||
870 | wakeup.four_way_handshake = true; | ||
871 | pkt8023 = true; | ||
872 | } | ||
873 | |||
874 | if (status->wake_packet_bufsize) { | ||
875 | u32 pktsize = le32_to_cpu(status->wake_packet_bufsize); | ||
876 | u32 pktlen = le32_to_cpu(status->wake_packet_length); | ||
877 | |||
878 | if (pkt8023) { | ||
879 | pkt = alloc_skb(pktsize, GFP_KERNEL); | ||
880 | if (!pkt) | ||
881 | goto report; | ||
882 | memcpy(skb_put(pkt, pktsize), status->wake_packet, | ||
883 | pktsize); | ||
884 | if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) | ||
885 | goto report; | ||
886 | wakeup.packet = pkt->data; | ||
887 | wakeup.packet_present_len = pkt->len; | ||
888 | wakeup.packet_len = pkt->len - (pktlen - pktsize); | ||
889 | wakeup.packet_80211 = false; | ||
890 | } else { | ||
891 | wakeup.packet = status->wake_packet; | ||
892 | wakeup.packet_present_len = pktsize; | ||
893 | wakeup.packet_len = pktlen; | ||
894 | wakeup.packet_80211 = true; | ||
895 | } | ||
896 | } | ||
897 | |||
898 | report: | ||
899 | ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); | ||
900 | kfree_skb(pkt); | ||
901 | |||
902 | out: | ||
903 | iwl_free_resp(&cmd); | ||
904 | } | ||
905 | |||
766 | int iwl_mvm_resume(struct ieee80211_hw *hw) | 906 | int iwl_mvm_resume(struct ieee80211_hw *hw) |
767 | { | 907 | { |
768 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); | 908 | struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); |
@@ -770,14 +910,8 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) | |||
770 | .mvm = mvm, | 910 | .mvm = mvm, |
771 | }; | 911 | }; |
772 | struct ieee80211_vif *vif = NULL; | 912 | struct ieee80211_vif *vif = NULL; |
773 | u32 base; | ||
774 | int ret; | 913 | int ret; |
775 | enum iwl_d3_status d3_status; | 914 | enum iwl_d3_status d3_status; |
776 | struct error_table_start { | ||
777 | /* cf. struct iwl_error_event_table */ | ||
778 | u32 valid; | ||
779 | u32 error_id; | ||
780 | } err_info; | ||
781 | 915 | ||
782 | mutex_lock(&mvm->mutex); | 916 | mutex_lock(&mvm->mutex); |
783 | 917 | ||
@@ -800,27 +934,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) | |||
800 | goto out_unlock; | 934 | goto out_unlock; |
801 | } | 935 | } |
802 | 936 | ||
803 | base = mvm->error_event_table; | 937 | iwl_mvm_query_wakeup_reasons(mvm, vif); |
804 | |||
805 | iwl_trans_read_mem_bytes(mvm->trans, base, | ||
806 | &err_info, sizeof(err_info)); | ||
807 | |||
808 | if (err_info.valid) { | ||
809 | IWL_INFO(mvm, "error table is valid (%d)\n", | ||
810 | err_info.valid); | ||
811 | if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) | ||
812 | IWL_ERR(mvm, "this was due to RF-kill\n"); | ||
813 | goto out_unlock; | ||
814 | } | ||
815 | |||
816 | /* TODO: get status and whatever else ... */ | ||
817 | ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_GET_STATUSES, CMD_SYNC, 0, NULL); | ||
818 | if (ret) | ||
819 | IWL_ERR(mvm, "failed to query status (%d)\n", ret); | ||
820 | |||
821 | ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL); | ||
822 | if (ret) | ||
823 | IWL_ERR(mvm, "failed to query offloads (%d)\n", ret); | ||
824 | 938 | ||
825 | out_unlock: | 939 | out_unlock: |
826 | mutex_unlock(&mvm->mutex); | 940 | mutex_unlock(&mvm->mutex); |