Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 24 Apr 2014 01:39:53 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r264855 - head/sys/net80211
Message-ID:  <201404240139.s3O1drcg052381@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Thu Apr 24 01:39:53 2014
New Revision: 264855
URL: http://svnweb.freebsd.org/changeset/base/264855

Log:
  Begin fleshing out support for net80211 provided (legacy) sleep management.
  
  This transitions the VAP in and out of SLEEP state based on:
  
  * whether there's been an active transmission in the last (hardcoded) 500ms;
  * whether the TIM from the AP indicates there is data available.
  
  It uses the beacon reception to trigger the active traffic check.
  This way there's no further timer running to wake up the CPU
  from its own sleep states.
  
  Right now the VAP isn't woken up for multicast traffic - mostly because
  the only NIC I plan on doing this for right will auto wakeup and stay
  awake for multicast traffic indicated in the TIM.  So I don't have
  to manually keep the hardware awake.
  
  This doesn't do anything if the NIC doesn't advertise it implements
  the new SWSLEEP capability AND if the VAP doesn't have powersave
  enabled.
  
  It also doesn't do much with ath(4) as it doesn't currently implement
  the SLEEP state.
  
  Tested:
  
  * AR5416, STA mode (with local ath(4) changes)

Modified:
  head/sys/net80211/ieee80211_power.c
  head/sys/net80211/ieee80211_power.h
  head/sys/net80211/ieee80211_sta.c

Modified: head/sys/net80211/ieee80211_power.c
==============================================================================
--- head/sys/net80211/ieee80211_power.c	Thu Apr 24 01:28:39 2014	(r264854)
+++ head/sys/net80211/ieee80211_power.c	Thu Apr 24 01:39:53 2014	(r264855)
@@ -555,3 +555,89 @@ ieee80211_sta_pwrsave(struct ieee80211va
 		ieee80211_send_nulldata(ieee80211_ref_node(ni));
 	}
 }
+
+/*
+ * Handle being notified that we have data available for us in a TIM/ATIM.
+ *
+ * This may schedule a transition from _SLEEP -> _RUN if it's appropriate.
+ */
+void
+ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set)
+{
+	/*
+	 * Schedule the driver state change.  It'll happen at some point soon.
+	 * Since the hardware shouldn't know that we're running just yet
+	 * (and thus tell the peer that we're awake before we actually wake
+	 * up said hardware), we leave the actual node state transition
+	 * up to the transition to RUN.
+	 *
+	 * XXX TODO: verify that the transition to RUN will wake up the
+	 * BSS node!
+	 */
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s: TIM=%d\n", __func__, set);
+	IEEE80211_LOCK(vap->iv_ic);
+	if (set == 1 && vap->iv_state == IEEE80211_S_SLEEP) {
+		ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0);
+	}
+	IEEE80211_UNLOCK(vap->iv_ic);
+}
+
+/*
+ * Timer check on whether the VAP has had any transmit activity.
+ *
+ * This may schedule a transition from _RUN -> _SLEEP if it's appropriate.
+ */
+void
+ieee80211_sta_ps_timer_check(struct ieee80211vap *vap)
+{
+	struct ieee80211com *ic = vap->iv_ic;
+
+	/* XXX lock assert */
+
+	/* For no, only do this in STA mode */
+	if (! (vap->iv_caps & IEEE80211_C_SWSLEEP))
+		goto out;
+
+	if (vap->iv_opmode != IEEE80211_M_STA)
+		goto out;
+
+	/* If we're not at run state, bail */
+	if (vap->iv_state != IEEE80211_S_RUN)
+		goto out;
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
+	    "%s: lastdata=%llu, ticks=%llu\n",
+	    __func__, (unsigned long long) ic->ic_lastdata,
+	    (unsigned long long) ticks);
+
+	/* If powersave is disabled on the VAP, don't bother */
+	if (! (vap->iv_flags & IEEE80211_F_PMGTON))
+		goto out;
+
+	/* If we've done any data within our idle interval, bail */
+	/* XXX hard-coded to one second for now, ew! */
+	if (time_after(ic->ic_lastdata + 500, ticks))
+		goto out;
+
+	/*
+	 * Signify we're going into power save and transition the
+	 * node to powersave.
+	 */
+	if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+		vap->iv_sta_ps(vap, 1);
+
+	/*
+	 * XXX The driver has to handle the fact that we're going
+	 * to sleep but frames may still be transmitted;
+	 * hopefully it and/or us will do the right thing and mark any
+	 * transmitted frames with PWRMGT set to 1.
+	 */
+	ieee80211_new_state_locked(vap, IEEE80211_S_SLEEP, 0);
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER,
+	    "%s: time delta=%d msec\n", __func__,
+	    (int) ticks_to_msecs(ticks - ic->ic_lastdata));
+
+out:
+	return;
+}

Modified: head/sys/net80211/ieee80211_power.h
==============================================================================
--- head/sys/net80211/ieee80211_power.h	Thu Apr 24 01:28:39 2014	(r264854)
+++ head/sys/net80211/ieee80211_power.h	Thu Apr 24 01:39:53 2014	(r264855)
@@ -79,6 +79,9 @@ int	ieee80211_node_psq_age(struct ieee80
 int	ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *);
 void	ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
 void	ieee80211_sta_pwrsave(struct ieee80211vap *, int enable);
+void	ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set);
+void	ieee80211_sta_ps_timer_check(struct ieee80211vap *vap);
 
+/* XXX what's this? */
 void	ieee80211_power_poll(struct ieee80211com *);
 #endif /* _NET80211_IEEE80211_POWER_H_ */

Modified: head/sys/net80211/ieee80211_sta.c
==============================================================================
--- head/sys/net80211/ieee80211_sta.c	Thu Apr 24 01:28:39 2014	(r264854)
+++ head/sys/net80211/ieee80211_sta.c	Thu Apr 24 01:39:53 2014	(r264855)
@@ -234,6 +234,7 @@ sta_newstate(struct ieee80211vap *vap, e
 		switch (ostate) {
 		case IEEE80211_S_SLEEP:
 			/* XXX wakeup */
+			/* XXX driver hook to wakeup the hardware? */
 		case IEEE80211_S_RUN:
 			IEEE80211_SEND_MGMT(ni,
 			    IEEE80211_FC0_SUBTYPE_DISASSOC,
@@ -403,6 +404,7 @@ sta_newstate(struct ieee80211vap *vap, e
 			    arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
 			break;
 		case IEEE80211_S_SLEEP:
+			/* Wake up from sleep */
 			vap->iv_sta_ps(vap, 0);
 			break;
 		default:
@@ -430,9 +432,11 @@ sta_newstate(struct ieee80211vap *vap, e
 			ieee80211_node_authorize(ni);
 		/*
 		 * Fake association when joining an existing bss.
+		 *
+		 * Don't do this if we're doing SLEEP->RUN.
 		 */
-		if (ic->ic_newassoc != NULL)
-			ic->ic_newassoc(vap->iv_bss, ostate != IEEE80211_S_RUN);
+		if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP)
+			ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN));
 		break;
 	case IEEE80211_S_CSA:
 		if (ostate != IEEE80211_S_RUN)
@@ -1312,6 +1316,7 @@ sta_recv_mgmt(struct ieee80211_node *ni,
 				vap->iv_stats.is_beacon_bad++;
 			return;
 		}
+
 		/*
 		 * Count frame now that we know it's to be processed.
 		 */
@@ -1381,23 +1386,48 @@ sta_recv_mgmt(struct ieee80211_node *ni,
 			}
 			if (scan.quiet)
 				ic->ic_set_quiet(ni, scan.quiet);
+
 			if (scan.tim != NULL) {
 				struct ieee80211_tim_ie *tim =
 				    (struct ieee80211_tim_ie *) scan.tim;
-#if 0
+				/*
+				 * XXX Check/debug this code; see if it's about
+				 * the right time to force the VAP awake if we
+				 * receive a frame destined for us?
+				 */
 				int aid = IEEE80211_AID(ni->ni_associd);
 				int ix = aid / NBBY;
 				int min = tim->tim_bitctl &~ 1;
 				int max = tim->tim_len + min - 4;
-				if ((tim->tim_bitctl&1) ||
+
+				/*
+				 * Only do this for unicast traffic in the TIM
+				 * The multicast traffic notification for
+				 * the scan notification stuff should occur
+				 * differently.
+				 */
+				if (min <= ix && ix <= max &&
+				     isset(tim->tim_bitmap - min, aid)) {
+					ieee80211_sta_tim_notify(vap, 1);
+					ic->ic_lastdata = ticks;
+				}
+
+				/*
+				 * XXX TODO: do a separate notification
+				 * for the multicast bit being set.
+				 */
+#if 0
+				if ((tim->tim_bitctl & 1) ||
 				    (min <= ix && ix <= max &&
 				     isset(tim->tim_bitmap - min, aid))) {
 					/* 
 					 * XXX Do not let bg scan kick off
 					 * we are expecting data.
 					 */
+					ieee80211_sta_tim_notify(vap, 1);
 					ic->ic_lastdata = ticks;
-					vap->iv_sta_ps(vap, 0);
+					// XXX not yet?
+//					vap->iv_sta_ps(vap, 0);
 				}
 #endif
 				ni->ni_dtim_count = tim->tim_count;
@@ -1446,6 +1476,14 @@ sta_recv_mgmt(struct ieee80211_node *ni,
 			}
 
 			/*
+			 * Put the station to sleep if we haven't seen
+			 * traffic in a while.
+			 */
+			IEEE80211_LOCK(ic);
+			ieee80211_sta_ps_timer_check(vap);
+			IEEE80211_UNLOCK(ic);
+
+			/*
 			 * If we've had a channel width change (eg HT20<->HT40)
 			 * then schedule a delayed driver notification.
 			 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201404240139.s3O1drcg052381>