aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rtlwifi/ps.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rtlwifi/ps.c')
-rw-r--r--drivers/net/wireless/rtlwifi/ps.c330
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 */
234void rtl_ips_nic_on(struct ieee80211_hw *hw) 243void 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
643static 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
742static 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
828void 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
896void 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}