ps.c 25.7 KB
Newer Older
L
Larry Finger 已提交
1 2
/******************************************************************************
 *
L
Larry Finger 已提交
3
 * Copyright(c) 2009-2012  Realtek Corporation.
L
Larry Finger 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 * Contact Information:
 * wlanfae <wlanfae@realtek.com>
 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
 * Hsinchu 300, Taiwan.
 *
 * Larry Finger <Larry.Finger@lwfinger.net>
 *
 *****************************************************************************/

#include "wifi.h"
#include "base.h"
#include "ps.h"
29 30
#include <linux/export.h>
#include "btcoexist/rtl_btc.h"
31

L
Larry Finger 已提交
32 33 34 35 36 37 38 39 40 41 42 43
bool rtl_ps_enable_nic(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));

	/*<1> reset trx ring */
	if (rtlhal->interface == INTF_PCI)
		rtlpriv->intf_ops->reset_trx_ring(hw);

	if (is_hal_stop(rtlhal))
		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
44
			 "Driver is already down!\n");
L
Larry Finger 已提交
45 46

	/*<2> Enable Adapter */
47
	if (rtlpriv->cfg->ops->hw_init(hw))
48
		return false;
L
Larry Finger 已提交
49 50 51 52 53 54 55 56
	RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);

	/*<3> Enable Interrupt */
	rtlpriv->cfg->ops->enable_interrupt(hw);

	/*<enable timer> */
	rtl_watch_dog_timer_callback((unsigned long)hw);

57
	return true;
L
Larry Finger 已提交
58 59 60 61 62 63 64 65 66 67 68 69
}
EXPORT_SYMBOL(rtl_ps_enable_nic);

bool rtl_ps_disable_nic(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);

	/*<1> Stop all timer */
	rtl_deinit_deferred_work(hw);

	/*<2> Disable Interrupt */
	rtlpriv->cfg->ops->disable_interrupt(hw);
70
	tasklet_kill(&rtlpriv->works.irq_tasklet);
L
Larry Finger 已提交
71 72 73 74

	/*<3> Disable Adapter */
	rtlpriv->cfg->ops->hw_disable(hw);

75
	return true;
L
Larry Finger 已提交
76 77 78 79 80
}
EXPORT_SYMBOL(rtl_ps_disable_nic);

bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
			 enum rf_pwrstate state_toset,
81
			 u32 changesource, bool protect_or_not)
L
Larry Finger 已提交
82 83 84
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
85
	enum rf_pwrstate rtstate;
86
	bool actionallowed = false;
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
	u16 rfwait_cnt = 0;

	if (protect_or_not)
		goto no_protect;

	/*Only one thread can change
	 *the RF state at one time, and others
	 *should wait to be executed.
	 */
	while (true) {
		spin_lock(&rtlpriv->locks.rf_ps_lock);
		if (ppsc->rfchange_inprogress) {
			spin_unlock(&rtlpriv->locks.rf_ps_lock);

			RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
				 "RF Change in progress! Wait to set..state_toset(%d).\n",
				  state_toset);

			/* Set RF after the previous action is done.  */
			while (ppsc->rfchange_inprogress) {
				rfwait_cnt++;
				mdelay(1);
				/*Wait too long, return false to avoid
				 *to be stuck here.
				 */
				if (rfwait_cnt > 100)
					return false;
			}
		} else {
			ppsc->rfchange_inprogress = true;
			spin_unlock(&rtlpriv->locks.rf_ps_lock);
			break;
		}
	}

no_protect:
	rtstate = ppsc->rfpwr_state;
L
Larry Finger 已提交
124 125 126 127 128 129

	switch (state_toset) {
	case ERFON:
		ppsc->rfoff_reason &= (~changesource);

		if ((changesource == RF_CHANGE_BY_HW) &&
130
		    (ppsc->hwradiooff)) {
131
			ppsc->hwradiooff = false;
L
Larry Finger 已提交
132 133 134 135
		}

		if (!ppsc->rfoff_reason) {
			ppsc->rfoff_reason = 0;
136
			actionallowed = true;
L
Larry Finger 已提交
137 138 139 140 141 142
		}

		break;

	case ERFOFF:

143
		if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) {
144
			ppsc->hwradiooff = true;
L
Larry Finger 已提交
145 146 147
		}

		ppsc->rfoff_reason |= changesource;
148
		actionallowed = true;
L
Larry Finger 已提交
149 150 151 152
		break;

	case ERFSLEEP:
		ppsc->rfoff_reason |= changesource;
153
		actionallowed = true;
L
Larry Finger 已提交
154 155 156 157
		break;

	default:
		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
158
			 "switch case not processed\n");
L
Larry Finger 已提交
159 160 161
		break;
	}

162
	if (actionallowed)
L
Larry Finger 已提交
163 164
		rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);

165 166 167 168 169 170
	if (!protect_or_not) {
		spin_lock(&rtlpriv->locks.rf_ps_lock);
		ppsc->rfchange_inprogress = false;
		spin_unlock(&rtlpriv->locks.rf_ps_lock);
	}

171
	return actionallowed;
L
Larry Finger 已提交
172 173 174 175 176 177 178 179 180
}
EXPORT_SYMBOL(rtl_ps_set_rf_state);

static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));

181
	ppsc->swrf_processing = true;
L
Larry Finger 已提交
182

183
	if (ppsc->inactive_pwrstate == ERFON &&
184
	    rtlhal->interface == INTF_PCI) {
L
Larry Finger 已提交
185
		if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) &&
186
		    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
L
Larry Finger 已提交
187 188
		    rtlhal->interface == INTF_PCI) {
			rtlpriv->intf_ops->disable_aspm(hw);
189
			RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
L
Larry Finger 已提交
190 191 192
		}
	}

193 194
	rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate,
			    RF_CHANGE_BY_IPS, false);
L
Larry Finger 已提交
195 196 197

	if (ppsc->inactive_pwrstate == ERFOFF &&
	    rtlhal->interface == INTF_PCI) {
198
		if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
199
		    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
L
Larry Finger 已提交
200
			rtlpriv->intf_ops->enable_aspm(hw);
201
			RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
L
Larry Finger 已提交
202 203 204
		}
	}

205
	ppsc->swrf_processing = false;
L
Larry Finger 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
}

void rtl_ips_nic_off_wq_callback(void *data)
{
	struct rtl_works *rtlworks =
	    container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq);
	struct ieee80211_hw *hw = rtlworks->hw;
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	enum rf_pwrstate rtstate;

	if (mac->opmode != NL80211_IFTYPE_STATION) {
		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
221
			 "not station return\n");
L
Larry Finger 已提交
222 223 224
		return;
	}

225 226 227
	if (mac->p2p_in_use)
		return;

228 229 230
	if (mac->link_state > MAC80211_NOLINK)
		return;

L
Larry Finger 已提交
231 232 233 234 235 236
	if (is_hal_stop(rtlhal))
		return;

	if (rtlpriv->sec.being_setkey)
		return;

237 238 239
	if (rtlpriv->cfg->ops->bt_coex_off_before_lps)
		rtlpriv->cfg->ops->bt_coex_off_before_lps(hw);

240
	if (ppsc->inactiveps) {
L
Larry Finger 已提交
241 242 243 244 245
		rtstate = ppsc->rfpwr_state;

		/*
		 *Do not enter IPS in the following conditions:
		 *(1) RF is already OFF or Sleep
246
		 *(2) swrf_processing (indicates the IPS is still under going)
L
Larry Finger 已提交
247 248 249 250 251 252 253
		 *(3) Connectted (only disconnected can trigger IPS)
		 *(4) IBSS (send Beacon)
		 *(5) AP mode (send Beacon)
		 *(6) monitor mode (rcv packet)
		 */

		if (rtstate == ERFON &&
254
		    !ppsc->swrf_processing &&
L
Larry Finger 已提交
255 256 257
		    (mac->link_state == MAC80211_NOLINK) &&
		    !mac->act_scanning) {
			RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
258
				 "IPSEnter(): Turn off RF\n");
L
Larry Finger 已提交
259 260

			ppsc->inactive_pwrstate = ERFOFF;
261
			ppsc->in_powersavemode = true;
L
Larry Finger 已提交
262

263 264 265 266 267
			/* call before RF off */
			if (rtlpriv->cfg->ops->get_btc_status())
				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
									ppsc->inactive_pwrstate);

L
Larry Finger 已提交
268 269 270 271 272 273 274 275 276 277
			/*rtl_pci_reset_trx_ring(hw); */
			_rtl_ps_inactive_ps(hw);
		}
	}
}

void rtl_ips_nic_off(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);

278 279 280
	/* because when link with ap, mac80211 will ask us
	 * to disable nic quickly after scan before linking,
	 * this will cause link failed, so we delay 100ms here
L
Larry Finger 已提交
281 282 283 284 285
	 */
	queue_delayed_work(rtlpriv->works.rtl_wq,
			   &rtlpriv->works.ips_nic_off_wq, MSECS(100));
}

286 287 288
/* NOTICE: any opmode should exc nic_on, or disable without
 * nic_on may something wrong, like adhoc TP
 */
L
Larry Finger 已提交
289 290 291 292 293
void rtl_ips_nic_on(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	enum rf_pwrstate rtstate;
294

295
	cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq);
L
Larry Finger 已提交
296

297
	spin_lock(&rtlpriv->locks.ips_lock);
298
	if (ppsc->inactiveps) {
L
Larry Finger 已提交
299 300 301
		rtstate = ppsc->rfpwr_state;

		if (rtstate != ERFON &&
302
		    !ppsc->swrf_processing &&
L
Larry Finger 已提交
303 304 305
		    ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) {

			ppsc->inactive_pwrstate = ERFON;
306
			ppsc->in_powersavemode = false;
L
Larry Finger 已提交
307
			_rtl_ps_inactive_ps(hw);
308 309 310 311
			/* call after RF on */
			if (rtlpriv->cfg->ops->get_btc_status())
				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
									ppsc->inactive_pwrstate);
L
Larry Finger 已提交
312 313
		}
	}
314
	spin_unlock(&rtlpriv->locks.ips_lock);
L
Larry Finger 已提交
315
}
316
EXPORT_SYMBOL_GPL(rtl_ips_nic_on);
L
Larry Finger 已提交
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336

/*for FW LPS*/

/*
 *Determine if we can set Fw into PS mode
 *in current condition.Return TRUE if it
 *can enter PS mode.
 */
static bool rtl_get_fwlps_doze(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	u32 ps_timediff;

	ps_timediff = jiffies_to_msecs(jiffies -
				       ppsc->last_delaylps_stamp_jiffies);

	if (ps_timediff < 2000) {
		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
337
			 "Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n");
L
Larry Finger 已提交
338 339 340 341 342 343 344 345 346 347 348 349 350
		return false;
	}

	if (mac->link_state != MAC80211_LINKED)
		return false;

	if (mac->opmode == NL80211_IFTYPE_ADHOC)
		return false;

	return true;
}

/* Change current and default preamble mode.*/
351
void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
L
Larry Finger 已提交
352 353 354 355
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
356
	bool enter_fwlps;
L
Larry Finger 已提交
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

	if (mac->opmode == NL80211_IFTYPE_ADHOC)
		return;

	if (mac->link_state != MAC80211_LINKED)
		return;

	if (ppsc->dot11_psmode == rt_psmode)
		return;

	/* Update power save mode configured. */
	ppsc->dot11_psmode = rt_psmode;

	/*
	 *<FW control LPS>
	 *1. Enter PS mode
	 *   Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
	 *   cmd to set Fw into PS mode.
	 *2. Leave PS mode
	 *   Send H2C fw_pwrmode cmd to Fw to set Fw into Active
	 *   mode and set RPWM to turn RF on.
	 */

380
	if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
L
Larry Finger 已提交
381 382
		if (ppsc->dot11_psmode == EACTIVE) {
			RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
383
				 "FW LPS leave ps_mode:%x\n",
384
				  FW_PS_ACTIVE_MODE);
385 386 387
			enter_fwlps = false;
			ppsc->pwr_mode = FW_PS_ACTIVE_MODE;
			ppsc->smart_ps = 0;
388 389
			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_LPS_ACTION,
						      (u8 *)(&enter_fwlps));
390
			if (ppsc->p2p_ps_info.opp_ps)
391
				rtl_p2p_ps_cmd(hw , P2P_PS_ENABLE);
L
Larry Finger 已提交
392

393 394
			if (rtlpriv->cfg->ops->get_btc_status())
				rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
L
Larry Finger 已提交
395 396 397
		} else {
			if (rtl_get_fwlps_doze(hw)) {
				RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
398 399
					 "FW LPS enter ps_mode:%x\n",
					 ppsc->fwctrl_psmode);
400 401
				if (rtlpriv->cfg->ops->get_btc_status())
					rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
402 403 404
				enter_fwlps = true;
				ppsc->pwr_mode = ppsc->fwctrl_psmode;
				ppsc->smart_ps = 2;
L
Larry Finger 已提交
405
				rtlpriv->cfg->ops->set_hw_reg(hw,
406 407
							HW_VAR_FW_LPS_ACTION,
							(u8 *)(&enter_fwlps));
L
Larry Finger 已提交
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422

			} else {
				/* Reset the power save related parameters. */
				ppsc->dot11_psmode = EACTIVE;
			}
		}
	}
}

/*Enter the leisure power save mode.*/
void rtl_lps_enter(struct ieee80211_hw *hw)
{
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	struct rtl_priv *rtlpriv = rtl_priv(hw);
423
	unsigned long flag;
L
Larry Finger 已提交
424

425
	if (!ppsc->fwctrl_lps)
L
Larry Finger 已提交
426 427 428 429 430
		return;

	if (rtlpriv->sec.being_setkey)
		return;

431
	if (rtlpriv->link_info.busytraffic)
L
Larry Finger 已提交
432 433 434 435 436 437 438 439 440 441 442 443
		return;

	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
	if (mac->cnt_after_linked < 5)
		return;

	if (mac->opmode == NL80211_IFTYPE_ADHOC)
		return;

	if (mac->link_state != MAC80211_LINKED)
		return;

444
	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
L
Larry Finger 已提交
445

446 447 448 449
	/* Idle for a while if we connect to AP a while ago. */
	if (mac->cnt_after_linked >= 2) {
		if (ppsc->dot11_psmode == EACTIVE) {
			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
450
				 "Enter 802.11 power save mode...\n");
L
Larry Finger 已提交
451

452
			rtl_lps_set_psmode(hw, EAUTOPS);
L
Larry Finger 已提交
453 454
		}
	}
455

456
	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
L
Larry Finger 已提交
457
}
458
EXPORT_SYMBOL(rtl_lps_enter);
L
Larry Finger 已提交
459 460 461 462 463 464 465

/*Leave the leisure power save mode.*/
void rtl_lps_leave(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
466
	unsigned long flag;
L
Larry Finger 已提交
467

468
	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
L
Larry Finger 已提交
469

470
	if (ppsc->fwctrl_lps) {
L
Larry Finger 已提交
471 472 473
		if (ppsc->dot11_psmode != EACTIVE) {

			/*FIX ME */
474
			/*rtlpriv->cfg->ops->enable_interrupt(hw); */
L
Larry Finger 已提交
475 476

			if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
477
			    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
L
Larry Finger 已提交
478 479
			    rtlhal->interface == INTF_PCI) {
				rtlpriv->intf_ops->disable_aspm(hw);
480
				RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
L
Larry Finger 已提交
481 482 483
			}

			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
484
				 "Busy Traffic,Leave 802.11 power save..\n");
L
Larry Finger 已提交
485 486 487 488

			rtl_lps_set_psmode(hw, EACTIVE);
		}
	}
489
	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
L
Larry Finger 已提交
490
}
491
EXPORT_SYMBOL(rtl_lps_leave);
492 493 494 495 496 497

/* For sw LPS*/
void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
498
	struct ieee80211_hdr *hdr = data;
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
	struct ieee80211_tim_ie *tim_ie;
	u8 *tim;
	u8 tim_len;
	bool u_buffed;
	bool m_buffed;

	if (mac->opmode != NL80211_IFTYPE_STATION)
		return;

	if (!rtlpriv->psc.swctrl_lps)
		return;

	if (rtlpriv->mac80211.link_state != MAC80211_LINKED)
		return;

	if (!rtlpriv->psc.sw_ps_enabled)
		return;

	if (rtlpriv->psc.fwctrl_lps)
		return;

	if (likely(!(hw->conf.flags & IEEE80211_CONF_PS)))
		return;

	/* check if this really is a beacon */
	if (!ieee80211_is_beacon(hdr->frame_control))
		return;

	/* min. beacon length + FCS_LEN */
	if (len <= 40 + FCS_LEN)
		return;

	/* and only beacons from the associated BSSID, please */
532
	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
		return;

	rtlpriv->psc.last_beacon = jiffies;

	tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
	if (!tim)
		return;

	if (tim[1] < sizeof(*tim_ie))
		return;

	tim_len = tim[1];
	tim_ie = (struct ieee80211_tim_ie *) &tim[2];

	if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period))
		rtlpriv->psc.dtim_counter = tim_ie->dtim_count;

	/* Check whenever the PHY can be turned off again. */

	/* 1. What about buffered unicast traffic for our AID? */
	u_buffed = ieee80211_check_tim(tim_ie, tim_len,
				       rtlpriv->mac80211.assoc_id);

	/* 2. Maybe the AP wants to send multicast/broadcast data? */
	m_buffed = tim_ie->bitmap_ctrl & 0x01;
	rtlpriv->psc.multi_buffered = m_buffed;

	/* unicast will process by mac80211 through
	 * set ~IEEE80211_CONF_PS, So we just check
	 * multicast frames here */
	if (!m_buffed) {
		/* back to low-power land. and delay is
		 * prevent null power save frame tx fail */
		queue_delayed_work(rtlpriv->works.rtl_wq,
567
				   &rtlpriv->works.ps_work, MSECS(5));
568
	} else {
569 570
		RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
			 "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed);
571 572
	}
}
573
EXPORT_SYMBOL_GPL(rtl_swlps_beacon);
574 575 576 577 578 579

void rtl_swlps_rf_awake(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
580
	unsigned long flag;
581 582 583 584 585 586 587

	if (!rtlpriv->psc.swctrl_lps)
		return;
	if (mac->link_state != MAC80211_LINKED)
		return;

	if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
588
	    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
589 590 591 592
		rtlpriv->intf_ops->disable_aspm(hw);
		RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
	}

593 594 595
	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
	rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS, false);
	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
}

void rtl_swlps_rfon_wq_callback(void *data)
{
	struct rtl_works *rtlworks =
	    container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq);
	struct ieee80211_hw *hw = rtlworks->hw;

	rtl_swlps_rf_awake(hw);
}

void rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
612
	unsigned long flag;
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
	u8 sleep_intv;

	if (!rtlpriv->psc.sw_ps_enabled)
		return;

	if ((rtlpriv->sec.being_setkey) ||
	    (mac->opmode == NL80211_IFTYPE_ADHOC))
		return;

	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
	if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5))
		return;

	if (rtlpriv->link_info.busytraffic)
		return;

629 630 631 632 633 634 635 636 637 638
	spin_lock(&rtlpriv->locks.rf_ps_lock);
	if (rtlpriv->psc.rfchange_inprogress) {
		spin_unlock(&rtlpriv->locks.rf_ps_lock);
		return;
	}
	spin_unlock(&rtlpriv->locks.rf_ps_lock);

	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
	rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS , false);
	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
639 640

	if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
641
	    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
		rtlpriv->intf_ops->enable_aspm(hw);
		RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
	}

	/* here is power save alg, when this beacon is DTIM
	 * we will set sleep time to dtim_period * n;
	 * when this beacon is not DTIM, we will set sleep
	 * time to sleep_intv = rtlpriv->psc.dtim_counter or
	 * MAX_SW_LPS_SLEEP_INTV(default set to 5) */

	if (rtlpriv->psc.dtim_counter == 0) {
		if (hw->conf.ps_dtim_period == 1)
			sleep_intv = hw->conf.ps_dtim_period * 2;
		else
			sleep_intv = hw->conf.ps_dtim_period;
	} else {
		sleep_intv = rtlpriv->psc.dtim_counter;
	}

	if (sleep_intv > MAX_SW_LPS_SLEEP_INTV)
		sleep_intv = MAX_SW_LPS_SLEEP_INTV;

	/* this print should always be dtim_conter = 0 &
	 * sleep  = dtim_period, that meaons, we should
	 * awake before every dtim */
	RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
668
		 "dtim_counter:%x will sleep :%d beacon_intv\n",
669
		  rtlpriv->psc.dtim_counter, sleep_intv);
670 671 672 673 674 675

	/* we tested that 40ms is enough for sw & hw sw delay */
	queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq,
			MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40));
}

676 677 678 679 680 681 682 683 684 685 686 687
void rtl_lps_change_work_callback(struct work_struct *work)
{
	struct rtl_works *rtlworks =
	    container_of(work, struct rtl_works, lps_change_work);
	struct ieee80211_hw *hw = rtlworks->hw;
	struct rtl_priv *rtlpriv = rtl_priv(hw);

	if (rtlpriv->enter_ps)
		rtl_lps_enter(hw);
	else
		rtl_lps_leave(hw);
}
688
EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706

void rtl_swlps_wq_callback(void *data)
{
	struct rtl_works *rtlworks = container_of_dwork_rtl(data,
				     struct rtl_works,
				     ps_work);
	struct ieee80211_hw *hw = rtlworks->hw;
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	bool ps = false;

	ps = (hw->conf.flags & IEEE80211_CONF_PS);

	/* we can sleep after ps null send ok */
	if (rtlpriv->psc.state_inap) {
		rtl_swlps_rf_sleep(hw);

		if (rtlpriv->psc.state && !ps) {
			rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies -
707
						 rtlpriv->psc.last_action);
708 709 710 711 712 713 714 715 716
		}

		if (ps)
			rtlpriv->psc.last_slept = jiffies;

		rtlpriv->psc.last_action = jiffies;
		rtlpriv->psc.state = ps;
	}
}
717 718 719 720 721

static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
			   unsigned int len)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
722
	struct ieee80211_mgmt *mgmt = data;
723 724 725 726
	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
	u8 *pos, *end, *ie;
	u16 noa_len;
	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
727
	u8 noa_num, index , i, noa_index = 0;
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
	bool find_p2p_ie = false , find_p2p_ps_ie = false;
	pos = (u8 *)mgmt->u.beacon.variable;
	end = data + len;
	ie = NULL;

	while (pos + 1 < end) {
		if (pos + 2 + pos[1] > end)
			return;

		if (pos[0] == 221 && pos[1] > 4) {
			if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) {
				ie = pos + 2+4;
				break;
			}
		}
		pos += 2 + pos[1];
	}

	if (ie == NULL)
		return;
	find_p2p_ie = true;
	/*to find noa ie*/
	while (ie + 1 < end) {
751
		noa_len = READEF2BYTE((__le16 *)&ie[1]);
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
		if (ie + 3 + ie[1] > end)
			return;

		if (ie[0] == 12) {
			find_p2p_ps_ie = true;
			if ((noa_len - 2) % 13 != 0) {
				RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
					 "P2P notice of absence: invalid length.%d\n",
					 noa_len);
				return;
			} else {
				noa_num = (noa_len - 2) / 13;
			}
			noa_index = ie[3];
			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
					 "update NOA ie.\n");
				p2pinfo->noa_index = noa_index;
				p2pinfo->opp_ps = (ie[4] >> 7);
				p2pinfo->ctwindow = ie[4] & 0x7F;
				p2pinfo->noa_num = noa_num;
				index = 5;
				for (i = 0; i < noa_num; i++) {
					p2pinfo->noa_count_type[i] =
777
							READEF1BYTE(ie+index);
778 779
					index += 1;
					p2pinfo->noa_duration[i] =
780
						 READEF4BYTE((__le32 *)ie+index);
781 782
					index += 4;
					p2pinfo->noa_interval[i] =
783
						 READEF4BYTE((__le32 *)ie+index);
784 785
					index += 4;
					p2pinfo->noa_start_time[i] =
786
						 READEF4BYTE((__le32 *)ie+index);
787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
					index += 4;
				}

				if (p2pinfo->opp_ps == 1) {
					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
					/* Driver should wait LPS entering
					 * CTWindow
					 */
					if (rtlpriv->psc.fw_current_inpsmode)
						rtl_p2p_ps_cmd(hw,
							       P2P_PS_ENABLE);
				} else if (p2pinfo->noa_num > 0) {
					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
				}
			}
805
			break;
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
		}
		ie += 3 + noa_len;
	}

	if (find_p2p_ie == true) {
		if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) &&
		    (find_p2p_ps_ie == false))
			rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
	}
}

static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
			      unsigned int len)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
821
	struct ieee80211_mgmt *mgmt = data;
822
	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
823
	u8 noa_num, index , i , noa_index = 0;
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
	u8 *pos, *end, *ie;
	u16 noa_len;
	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};

	pos = (u8 *)&mgmt->u.action.category;
	end = data + len;
	ie = NULL;

	if (pos[0] == 0x7f) {
		if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0)
			ie = pos + 3+4;
	}

	if (ie == NULL)
		return;

	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
	/*to find noa ie*/
	while (ie + 1 < end) {
843
		noa_len = READEF2BYTE((__le16 *)&ie[1]);
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
		if (ie + 3 + ie[1] > end)
			return;

		if (ie[0] == 12) {
			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n");
			RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ",
				      ie, noa_len);
			if ((noa_len - 2) % 13 != 0) {
				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
					 "P2P notice of absence: invalid length.%d\n",
					 noa_len);
				return;
			} else {
				noa_num = (noa_len - 2) / 13;
			}
			noa_index = ie[3];
			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
				p2pinfo->noa_index = noa_index;
				p2pinfo->opp_ps = (ie[4] >> 7);
				p2pinfo->ctwindow = ie[4] & 0x7F;
				p2pinfo->noa_num = noa_num;
				index = 5;
				for (i = 0; i < noa_num; i++) {
					p2pinfo->noa_count_type[i] =
869
							READEF1BYTE(ie+index);
870 871
					index += 1;
					p2pinfo->noa_duration[i] =
872
							 READEF4BYTE((__le32 *)ie+index);
873 874
					index += 4;
					p2pinfo->noa_interval[i] =
875
							 READEF4BYTE((__le32 *)ie+index);
876 877
					index += 4;
					p2pinfo->noa_start_time[i] =
878
							 READEF4BYTE((__le32 *)ie+index);
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
					index += 4;
				}

				if (p2pinfo->opp_ps == 1) {
					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
					/* Driver should wait LPS entering
					 * CTWindow
					 */
					if (rtlpriv->psc.fw_current_inpsmode)
						rtl_p2p_ps_cmd(hw,
							       P2P_PS_ENABLE);
				} else if (p2pinfo->noa_num > 0) {
					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
				}
			}
897
			break;
898 899 900 901 902
		}
		ie += 3 + noa_len;
	}
}

903
void rtl_p2p_ps_cmd(struct ieee80211_hw *hw , u8 p2p_ps_state)
904 905 906 907 908
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
	struct rtl_p2p_ps_info  *p2pinfo = &(rtlpriv->psc.p2p_ps_info);

909
	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n" , p2p_ps_state);
910 911 912
	switch (p2p_ps_state) {
	case P2P_PS_DISABLE:
		p2pinfo->p2p_ps_state = p2p_ps_state;
913 914
		rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
					      &p2p_ps_state);
915 916 917 918 919
		p2pinfo->noa_index = 0;
		p2pinfo->ctwindow = 0;
		p2pinfo->opp_ps = 0;
		p2pinfo->noa_num = 0;
		p2pinfo->p2p_ps_mode = P2P_PS_NONE;
920
		if (rtlps->fw_current_inpsmode) {
921 922 923 924
			if (rtlps->smart_ps == 0) {
				rtlps->smart_ps = 2;
				rtlpriv->cfg->ops->set_hw_reg(hw,
					 HW_VAR_H2C_FW_PWRMODE,
925
					 &rtlps->pwr_mode);
926
			}
927

928 929 930 931 932 933 934 935 936 937 938
		}
		break;
	case P2P_PS_ENABLE:
		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
			p2pinfo->p2p_ps_state = p2p_ps_state;

			if (p2pinfo->ctwindow > 0) {
				if (rtlps->smart_ps != 0) {
					rtlps->smart_ps = 0;
					rtlpriv->cfg->ops->set_hw_reg(hw,
						 HW_VAR_H2C_FW_PWRMODE,
939
						 &rtlps->pwr_mode);
940 941 942 943
				}
			}
			rtlpriv->cfg->ops->set_hw_reg(hw,
				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
944
				 &p2p_ps_state);
945

946 947 948 949 950 951 952 953 954
		}
		break;
	case P2P_PS_SCAN:
	case P2P_PS_SCAN_DONE:
	case P2P_PS_ALLSTASLEEP:
		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
			p2pinfo->p2p_ps_state = p2p_ps_state;
			rtlpriv->cfg->ops->set_hw_reg(hw,
				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
955
				 &p2p_ps_state);
956 957 958 959 960 961
		}
		break;
	default:
		break;
	}
	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
962 963
		 "ctwindow %x oppps %x\n",
		 p2pinfo->ctwindow , p2pinfo->opp_ps);
964 965
	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
		 "count %x duration %x index %x interval %x start time %x noa num %x\n",
966 967 968 969 970 971
		 p2pinfo->noa_count_type[0],
		 p2pinfo->noa_duration[0],
		 p2pinfo->noa_index,
		 p2pinfo->noa_interval[0],
		 p2pinfo->noa_start_time[0],
		 p2pinfo->noa_num);
972 973 974 975 976 977 978
	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n");
}

void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
979
	struct ieee80211_hdr *hdr = data;
980 981 982 983 984 985 986 987 988 989

	if (!mac->p2p)
		return;
	if (mac->link_state != MAC80211_LINKED)
		return;
	/* min. beacon length + FCS_LEN */
	if (len <= 40 + FCS_LEN)
		return;

	/* and only beacons from the associated BSSID, please */
990
	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
991 992 993 994 995 996 997 998 999
		return;

	/* check if this really is a beacon */
	if (!(ieee80211_is_beacon(hdr->frame_control) ||
	      ieee80211_is_probe_resp(hdr->frame_control) ||
	      ieee80211_is_action(hdr->frame_control)))
		return;

	if (ieee80211_is_action(hdr->frame_control))
1000
		rtl_p2p_action_ie(hw , data , len - FCS_LEN);
1001
	else
1002
		rtl_p2p_noa_ie(hw , data , len - FCS_LEN);
1003
}
1004
EXPORT_SYMBOL_GPL(rtl_p2p_info);