diff options
Diffstat (limited to 'drivers/net/wireless/rtlwifi/ps.c')
-rw-r--r-- | drivers/net/wireless/rtlwifi/ps.c | 330 |
1 files changed, 305 insertions, 25 deletions
diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c index 13ad33e85577..884bceae38a9 100644 --- a/drivers/net/wireless/rtlwifi/ps.c +++ b/drivers/net/wireless/rtlwifi/ps.c | |||
@@ -180,6 +180,9 @@ void rtl_ips_nic_off_wq_callback(void *data) | |||
180 | return; | 180 | return; |
181 | } | 181 | } |
182 | 182 | ||
183 | if (mac->p2p_in_use) | ||
184 | return; | ||
185 | |||
183 | if (mac->link_state > MAC80211_NOLINK) | 186 | if (mac->link_state > MAC80211_NOLINK) |
184 | return; | 187 | return; |
185 | 188 | ||
@@ -189,6 +192,9 @@ void rtl_ips_nic_off_wq_callback(void *data) | |||
189 | if (rtlpriv->sec.being_setkey) | 192 | if (rtlpriv->sec.being_setkey) |
190 | return; | 193 | return; |
191 | 194 | ||
195 | if (rtlpriv->cfg->ops->bt_coex_off_before_lps) | ||
196 | rtlpriv->cfg->ops->bt_coex_off_before_lps(hw); | ||
197 | |||
192 | if (ppsc->inactiveps) { | 198 | if (ppsc->inactiveps) { |
193 | rtstate = ppsc->rfpwr_state; | 199 | rtstate = ppsc->rfpwr_state; |
194 | 200 | ||
@@ -231,6 +237,9 @@ void rtl_ips_nic_off(struct ieee80211_hw *hw) | |||
231 | &rtlpriv->works.ips_nic_off_wq, MSECS(100)); | 237 | &rtlpriv->works.ips_nic_off_wq, MSECS(100)); |
232 | } | 238 | } |
233 | 239 | ||
240 | /* NOTICE: any opmode should exc nic_on, or disable without | ||
241 | * nic_on may something wrong, like adhoc TP | ||
242 | */ | ||
234 | void rtl_ips_nic_on(struct ieee80211_hw *hw) | 243 | void rtl_ips_nic_on(struct ieee80211_hw *hw) |
235 | { | 244 | { |
236 | struct rtl_priv *rtlpriv = rtl_priv(hw); | 245 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
@@ -299,7 +308,7 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) | |||
299 | struct rtl_priv *rtlpriv = rtl_priv(hw); | 308 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
300 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | 309 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); |
301 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); | 310 | struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); |
302 | u8 rpwm_val, fw_pwrmode; | 311 | bool enter_fwlps; |
303 | 312 | ||
304 | if (mac->opmode == NL80211_IFTYPE_ADHOC) | 313 | if (mac->opmode == NL80211_IFTYPE_ADHOC) |
305 | return; | 314 | return; |
@@ -324,43 +333,31 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) | |||
324 | */ | 333 | */ |
325 | 334 | ||
326 | if ((ppsc->fwctrl_lps) && ppsc->report_linked) { | 335 | if ((ppsc->fwctrl_lps) && ppsc->report_linked) { |
327 | bool fw_current_inps; | ||
328 | if (ppsc->dot11_psmode == EACTIVE) { | 336 | if (ppsc->dot11_psmode == EACTIVE) { |
329 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, | 337 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, |
330 | "FW LPS leave ps_mode:%x\n", | 338 | "FW LPS leave ps_mode:%x\n", |
331 | FW_PS_ACTIVE_MODE); | 339 | FW_PS_ACTIVE_MODE); |
332 | 340 | enter_fwlps = false; | |
333 | rpwm_val = 0x0C; /* RF on */ | 341 | ppsc->pwr_mode = FW_PS_ACTIVE_MODE; |
334 | fw_pwrmode = FW_PS_ACTIVE_MODE; | 342 | ppsc->smart_ps = 0; |
335 | rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, | ||
336 | &rpwm_val); | ||
337 | rtlpriv->cfg->ops->set_hw_reg(hw, | 343 | rtlpriv->cfg->ops->set_hw_reg(hw, |
338 | HW_VAR_H2C_FW_PWRMODE, | 344 | HW_VAR_FW_LPS_ACTION, |
339 | &fw_pwrmode); | 345 | (u8 *)(&enter_fwlps)); |
340 | fw_current_inps = false; | 346 | if (ppsc->p2p_ps_info.opp_ps) |
341 | 347 | rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); | |
342 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
343 | HW_VAR_FW_PSMODE_STATUS, | ||
344 | (u8 *) (&fw_current_inps)); | ||
345 | 348 | ||
346 | } else { | 349 | } else { |
347 | if (rtl_get_fwlps_doze(hw)) { | 350 | if (rtl_get_fwlps_doze(hw)) { |
348 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, | 351 | RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, |
349 | "FW LPS enter ps_mode:%x\n", | 352 | "FW LPS enter ps_mode:%x\n", |
350 | ppsc->fwctrl_psmode); | 353 | ppsc->fwctrl_psmode); |
351 | 354 | enter_fwlps = true; | |
352 | rpwm_val = 0x02; /* RF off */ | 355 | ppsc->pwr_mode = ppsc->fwctrl_psmode; |
353 | fw_current_inps = true; | 356 | ppsc->smart_ps = 2; |
354 | rtlpriv->cfg->ops->set_hw_reg(hw, | 357 | rtlpriv->cfg->ops->set_hw_reg(hw, |
355 | HW_VAR_FW_PSMODE_STATUS, | 358 | HW_VAR_FW_LPS_ACTION, |
356 | (u8 *) (&fw_current_inps)); | 359 | (u8 *)(&enter_fwlps)); |
357 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
358 | HW_VAR_H2C_FW_PWRMODE, | ||
359 | &ppsc->fwctrl_psmode); | ||
360 | 360 | ||
361 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
362 | HW_VAR_SET_RPWM, | ||
363 | &rpwm_val); | ||
364 | } else { | 361 | } else { |
365 | /* Reset the power save related parameters. */ | 362 | /* Reset the power save related parameters. */ |
366 | ppsc->dot11_psmode = EACTIVE; | 363 | ppsc->dot11_psmode = EACTIVE; |
@@ -642,3 +639,286 @@ void rtl_swlps_wq_callback(void *data) | |||
642 | rtlpriv->psc.state = ps; | 639 | rtlpriv->psc.state = ps; |
643 | } | 640 | } |
644 | } | 641 | } |
642 | |||
643 | static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, | ||
644 | unsigned int len) | ||
645 | { | ||
646 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
647 | struct ieee80211_mgmt *mgmt = (void *)data; | ||
648 | struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); | ||
649 | u8 *pos, *end, *ie; | ||
650 | u16 noa_len; | ||
651 | static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; | ||
652 | u8 noa_num, index, i, noa_index = 0; | ||
653 | bool find_p2p_ie = false , find_p2p_ps_ie = false; | ||
654 | pos = (u8 *)mgmt->u.beacon.variable; | ||
655 | end = data + len; | ||
656 | ie = NULL; | ||
657 | |||
658 | while (pos + 1 < end) { | ||
659 | if (pos + 2 + pos[1] > end) | ||
660 | return; | ||
661 | |||
662 | if (pos[0] == 221 && pos[1] > 4) { | ||
663 | if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) { | ||
664 | ie = pos + 2+4; | ||
665 | break; | ||
666 | } | ||
667 | } | ||
668 | pos += 2 + pos[1]; | ||
669 | } | ||
670 | |||
671 | if (ie == NULL) | ||
672 | return; | ||
673 | find_p2p_ie = true; | ||
674 | /*to find noa ie*/ | ||
675 | while (ie + 1 < end) { | ||
676 | noa_len = READEF2BYTE(&ie[1]); | ||
677 | if (ie + 3 + ie[1] > end) | ||
678 | return; | ||
679 | |||
680 | if (ie[0] == 12) { | ||
681 | find_p2p_ps_ie = true; | ||
682 | if ((noa_len - 2) % 13 != 0) { | ||
683 | RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, | ||
684 | "P2P notice of absence: invalid length.%d\n", | ||
685 | noa_len); | ||
686 | return; | ||
687 | } else { | ||
688 | noa_num = (noa_len - 2) / 13; | ||
689 | } | ||
690 | noa_index = ie[3]; | ||
691 | if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == | ||
692 | P2P_PS_NONE || noa_index != p2pinfo->noa_index) { | ||
693 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, | ||
694 | "update NOA ie.\n"); | ||
695 | p2pinfo->noa_index = noa_index; | ||
696 | p2pinfo->opp_ps = (ie[4] >> 7); | ||
697 | p2pinfo->ctwindow = ie[4] & 0x7F; | ||
698 | p2pinfo->noa_num = noa_num; | ||
699 | index = 5; | ||
700 | for (i = 0; i < noa_num; i++) { | ||
701 | p2pinfo->noa_count_type[i] = | ||
702 | READEF1BYTE(ie+index); | ||
703 | index += 1; | ||
704 | p2pinfo->noa_duration[i] = | ||
705 | READEF4BYTE(ie+index); | ||
706 | index += 4; | ||
707 | p2pinfo->noa_interval[i] = | ||
708 | READEF4BYTE(ie+index); | ||
709 | index += 4; | ||
710 | p2pinfo->noa_start_time[i] = | ||
711 | READEF4BYTE(ie+index); | ||
712 | index += 4; | ||
713 | } | ||
714 | |||
715 | if (p2pinfo->opp_ps == 1) { | ||
716 | p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; | ||
717 | /* Driver should wait LPS entering | ||
718 | * CTWindow | ||
719 | */ | ||
720 | if (rtlpriv->psc.fw_current_inpsmode) | ||
721 | rtl_p2p_ps_cmd(hw, | ||
722 | P2P_PS_ENABLE); | ||
723 | } else if (p2pinfo->noa_num > 0) { | ||
724 | p2pinfo->p2p_ps_mode = P2P_PS_NOA; | ||
725 | rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); | ||
726 | } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | ||
727 | rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); | ||
728 | } | ||
729 | } | ||
730 | break; | ||
731 | } | ||
732 | ie += 3 + noa_len; | ||
733 | } | ||
734 | |||
735 | if (find_p2p_ie == true) { | ||
736 | if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) && | ||
737 | (find_p2p_ps_ie == false)) | ||
738 | rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, | ||
743 | unsigned int len) | ||
744 | { | ||
745 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
746 | struct ieee80211_mgmt *mgmt = (void *)data; | ||
747 | struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); | ||
748 | u8 noa_num, index, i, noa_index = 0; | ||
749 | u8 *pos, *end, *ie; | ||
750 | u16 noa_len; | ||
751 | static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09}; | ||
752 | |||
753 | pos = (u8 *)&mgmt->u.action.category; | ||
754 | end = data + len; | ||
755 | ie = NULL; | ||
756 | |||
757 | if (pos[0] == 0x7f) { | ||
758 | if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0) | ||
759 | ie = pos + 3+4; | ||
760 | } | ||
761 | |||
762 | if (ie == NULL) | ||
763 | return; | ||
764 | |||
765 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n"); | ||
766 | /*to find noa ie*/ | ||
767 | while (ie + 1 < end) { | ||
768 | noa_len = READEF2BYTE(&ie[1]); | ||
769 | if (ie + 3 + ie[1] > end) | ||
770 | return; | ||
771 | |||
772 | if (ie[0] == 12) { | ||
773 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n"); | ||
774 | RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ", | ||
775 | ie, noa_len); | ||
776 | if ((noa_len - 2) % 13 != 0) { | ||
777 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, | ||
778 | "P2P notice of absence: invalid length.%d\n", | ||
779 | noa_len); | ||
780 | return; | ||
781 | } else { | ||
782 | noa_num = (noa_len - 2) / 13; | ||
783 | } | ||
784 | noa_index = ie[3]; | ||
785 | if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode == | ||
786 | P2P_PS_NONE || noa_index != p2pinfo->noa_index) { | ||
787 | p2pinfo->noa_index = noa_index; | ||
788 | p2pinfo->opp_ps = (ie[4] >> 7); | ||
789 | p2pinfo->ctwindow = ie[4] & 0x7F; | ||
790 | p2pinfo->noa_num = noa_num; | ||
791 | index = 5; | ||
792 | for (i = 0; i < noa_num; i++) { | ||
793 | p2pinfo->noa_count_type[i] = | ||
794 | READEF1BYTE(ie+index); | ||
795 | index += 1; | ||
796 | p2pinfo->noa_duration[i] = | ||
797 | READEF4BYTE(ie+index); | ||
798 | index += 4; | ||
799 | p2pinfo->noa_interval[i] = | ||
800 | READEF4BYTE(ie+index); | ||
801 | index += 4; | ||
802 | p2pinfo->noa_start_time[i] = | ||
803 | READEF4BYTE(ie+index); | ||
804 | index += 4; | ||
805 | } | ||
806 | |||
807 | if (p2pinfo->opp_ps == 1) { | ||
808 | p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW; | ||
809 | /* Driver should wait LPS entering | ||
810 | * CTWindow | ||
811 | */ | ||
812 | if (rtlpriv->psc.fw_current_inpsmode) | ||
813 | rtl_p2p_ps_cmd(hw, | ||
814 | P2P_PS_ENABLE); | ||
815 | } else if (p2pinfo->noa_num > 0) { | ||
816 | p2pinfo->p2p_ps_mode = P2P_PS_NOA; | ||
817 | rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE); | ||
818 | } else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | ||
819 | rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); | ||
820 | } | ||
821 | } | ||
822 | break; | ||
823 | } | ||
824 | ie += 3 + noa_len; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) | ||
829 | { | ||
830 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
831 | struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw)); | ||
832 | struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); | ||
833 | |||
834 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n", p2p_ps_state); | ||
835 | switch (p2p_ps_state) { | ||
836 | case P2P_PS_DISABLE: | ||
837 | p2pinfo->p2p_ps_state = p2p_ps_state; | ||
838 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
839 | HW_VAR_H2C_FW_P2P_PS_OFFLOAD, | ||
840 | (u8 *)(&p2p_ps_state)); | ||
841 | |||
842 | p2pinfo->noa_index = 0; | ||
843 | p2pinfo->ctwindow = 0; | ||
844 | p2pinfo->opp_ps = 0; | ||
845 | p2pinfo->noa_num = 0; | ||
846 | p2pinfo->p2p_ps_mode = P2P_PS_NONE; | ||
847 | if (rtlps->fw_current_inpsmode == true) { | ||
848 | if (rtlps->smart_ps == 0) { | ||
849 | rtlps->smart_ps = 2; | ||
850 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
851 | HW_VAR_H2C_FW_PWRMODE, | ||
852 | (u8 *)(&rtlps->pwr_mode)); | ||
853 | } | ||
854 | } | ||
855 | break; | ||
856 | case P2P_PS_ENABLE: | ||
857 | if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | ||
858 | p2pinfo->p2p_ps_state = p2p_ps_state; | ||
859 | |||
860 | if (p2pinfo->ctwindow > 0) { | ||
861 | if (rtlps->smart_ps != 0) { | ||
862 | rtlps->smart_ps = 0; | ||
863 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
864 | HW_VAR_H2C_FW_PWRMODE, | ||
865 | (u8 *)(&rtlps->pwr_mode)); | ||
866 | } | ||
867 | } | ||
868 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
869 | HW_VAR_H2C_FW_P2P_PS_OFFLOAD, | ||
870 | (u8 *)(&p2p_ps_state)); | ||
871 | } | ||
872 | break; | ||
873 | case P2P_PS_SCAN: | ||
874 | case P2P_PS_SCAN_DONE: | ||
875 | case P2P_PS_ALLSTASLEEP: | ||
876 | if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) { | ||
877 | p2pinfo->p2p_ps_state = p2p_ps_state; | ||
878 | rtlpriv->cfg->ops->set_hw_reg(hw, | ||
879 | HW_VAR_H2C_FW_P2P_PS_OFFLOAD, | ||
880 | (u8 *)(&p2p_ps_state)); | ||
881 | } | ||
882 | break; | ||
883 | default: | ||
884 | break; | ||
885 | } | ||
886 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, | ||
887 | "ctwindow %x oppps %x\n", p2pinfo->ctwindow, p2pinfo->opp_ps); | ||
888 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, | ||
889 | "count %x duration %x index %x interval %x start time %x noa num %x\n", | ||
890 | p2pinfo->noa_count_type[0], p2pinfo->noa_duration[0], | ||
891 | p2pinfo->noa_index, p2pinfo->noa_interval[0], | ||
892 | p2pinfo->noa_start_time[0], p2pinfo->noa_num); | ||
893 | RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n"); | ||
894 | } | ||
895 | |||
896 | void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) | ||
897 | { | ||
898 | struct rtl_priv *rtlpriv = rtl_priv(hw); | ||
899 | struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); | ||
900 | struct ieee80211_hdr *hdr = (void *)data; | ||
901 | |||
902 | if (!mac->p2p) | ||
903 | return; | ||
904 | if (mac->link_state != MAC80211_LINKED) | ||
905 | return; | ||
906 | /* min. beacon length + FCS_LEN */ | ||
907 | if (len <= 40 + FCS_LEN) | ||
908 | return; | ||
909 | |||
910 | /* and only beacons from the associated BSSID, please */ | ||
911 | if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid)) | ||
912 | return; | ||
913 | |||
914 | /* check if this really is a beacon */ | ||
915 | if (!(ieee80211_is_beacon(hdr->frame_control) || | ||
916 | ieee80211_is_probe_resp(hdr->frame_control) || | ||
917 | ieee80211_is_action(hdr->frame_control))) | ||
918 | return; | ||
919 | |||
920 | if (ieee80211_is_action(hdr->frame_control)) | ||
921 | rtl_p2p_action_ie(hw, data, len - FCS_LEN); | ||
922 | else | ||
923 | rtl_p2p_noa_ie(hw, data, len - FCS_LEN); | ||
924 | } | ||