PERFORCE change 77599 for review

Sam Leffler sam at FreeBSD.org
Fri May 27 17:41:50 PDT 2005


http://perforce.freebsd.org/chv.cgi?CH=77599

Change 77599 by sam at sam_ebb on 2005/05/28 00:41:14

	Checkpoint sta-mode power save support; mostly works but needs
	some tweaking for bg scanning:
	o add a new SLEEP state and use the state machine to hook into
	  the driver to effect pwr save operation of the device
	o add power save polling callback from inactivity timer; use
	  this to drop into power save mode when inactive
	o mark null data frame sent to ap on entering pwr save mode so
	  driver can identify when it should enable pwr save operation
	  for the device (must be deferred until frame goes is acked)
	
	Also:
	o clear tim after flushing ps q for an associated sta that drops
	  out of pwr save mode
	o when marking an ap down send a disassoc _and_ clear the sta
	  from the table to insure the reference count goes to zero

Affected files ...

.. //depot/projects/vap/sys/net80211/ieee80211_freebsd.h#8 edit
.. //depot/projects/vap/sys/net80211/ieee80211_input.c#18 edit
.. //depot/projects/vap/sys/net80211/ieee80211_ioctl.c#14 edit
.. //depot/projects/vap/sys/net80211/ieee80211_node.c#12 edit
.. //depot/projects/vap/sys/net80211/ieee80211_output.c#15 edit
.. //depot/projects/vap/sys/net80211/ieee80211_power.c#3 edit
.. //depot/projects/vap/sys/net80211/ieee80211_power.h#2 edit
.. //depot/projects/vap/sys/net80211/ieee80211_proto.c#12 edit
.. //depot/projects/vap/sys/net80211/ieee80211_proto.h#7 edit

Differences ...

==== //depot/projects/vap/sys/net80211/ieee80211_freebsd.h#8 (text+ko) ====

@@ -150,6 +150,7 @@
 #define	M_LINK0		M_PROTO1		/* WEP requested */
 #define	M_PWR_SAV	M_PROTO4		/* bypass PS handling */
 #define	M_FF		M_PROTO5		/* fast-frame */
+#define	M_PWR_DOWN	0x20000			/* power down null data frame */
 /*
  * Encode WME access control bits in the PROTO flags.
  * This is safe since it's passed directly in to the

==== //depot/projects/vap/sys/net80211/ieee80211_input.c#18 (text+ko) ====

@@ -1082,7 +1082,7 @@
 	}
 	switch (vap->iv_opmode) {
 	case IEEE80211_M_IBSS:
-		if (vap->iv_state != IEEE80211_S_RUN ||
+		if (vap->iv_state < IEEE80211_S_RUN ||
 		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
 			vap->iv_stats.is_rx_bad_auth++;
 			return;
@@ -1097,7 +1097,7 @@
 		break;
 
 	case IEEE80211_M_HOSTAP:
-		if (vap->iv_state != IEEE80211_S_RUN ||
+		if (vap->iv_state < IEEE80211_S_RUN ||
 		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
 			vap->iv_stats.is_rx_bad_auth++;
 			return;
@@ -1277,7 +1277,7 @@
 		    "bad operating mode %u", vap->iv_opmode);
 		return;
 	case IEEE80211_M_HOSTAP:
-		if (vap->iv_state != IEEE80211_S_RUN) {
+		if (vap->iv_state < IEEE80211_S_RUN) {
 			IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_AUTH,
 			    ni->ni_macaddr, "shared key auth",
 			    "bad state %u", vap->iv_state);
@@ -2402,7 +2402,7 @@
 		u_int8_t rate;
 
 		if (vap->iv_opmode == IEEE80211_M_STA ||
-		    vap->iv_state != IEEE80211_S_RUN) {
+		    vap->iv_state < IEEE80211_S_RUN) {
 			vap->iv_stats.is_rx_mgtdiscard++;
 			return;
 		}
@@ -2557,7 +2557,7 @@
 		u_int8_t reason;
 
 		if (vap->iv_opmode != IEEE80211_M_HOSTAP ||
-		    vap->iv_state != IEEE80211_S_RUN) {
+		    vap->iv_state < IEEE80211_S_RUN) {
 			vap->iv_stats.is_rx_mgtdiscard++;
 			return;
 		}
@@ -2904,7 +2904,7 @@
 	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
 		u_int16_t reason;
 
-		if (vap->iv_state != IEEE80211_S_RUN &&
+		if (vap->iv_state < IEEE80211_S_RUN &&
 		    vap->iv_state != IEEE80211_S_ASSOC &&
 		    vap->iv_state != IEEE80211_S_AUTH) {
 			vap->iv_stats.is_rx_mgtdiscard++;

==== //depot/projects/vap/sys/net80211/ieee80211_ioctl.c#14 (text+ko) ====

@@ -1511,6 +1511,7 @@
 		case IEEE80211_POWERSAVE_OFF:
 			if (vap->iv_flags & IEEE80211_F_PMGTON) {
 				vap->iv_flags &= ~IEEE80211_F_PMGTON;
+				ieee80211_syncflag(ic, IEEE80211_F_PMGTON);
 				error = ENETRESET;
 			}
 			break;
@@ -1519,6 +1520,7 @@
 				error = EINVAL;
 			else if ((vap->iv_flags & IEEE80211_F_PMGTON) == 0) {
 				vap->iv_flags |= IEEE80211_F_PMGTON;
+				ieee80211_syncflag(ic, IEEE80211_F_PMGTON);
 				error = ENETRESET;
 			}
 			break;
@@ -1911,7 +1913,7 @@
 			if ((parent->if_flags & IFF_UP) == 0) {
 				parent->if_flags |= IFF_UP;
 				error = parent->if_ioctl(parent, cmd, data);
-			} else if (vap->iv_state != IEEE80211_S_RUN)
+			} else if (vap->iv_state < IEEE80211_S_RUN)
 				ifp->if_init(ifp->if_softc);
 		} else {
 			ieee80211_stop(ifp);		/* stop ourself */

==== //depot/projects/vap/sys/net80211/ieee80211_node.c#12 (text+ko) ====

@@ -1293,6 +1293,7 @@
 
 	ieee80211_scan_timeout(ic);
 	ieee80211_timeout_stations(&ic->ic_sta);
+	ieee80211_power_poll(ic);
 
 	callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT * hz,
 		ieee80211_node_timeout, ic);

==== //depot/projects/vap/sys/net80211/ieee80211_output.c#15 (text+ko) ====

@@ -217,20 +217,26 @@
 	/* NB: parent must be up and running */
 	if ((parent->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP))
 		return;
-	for (;;) {
+	if (vap->iv_state == IEEE80211_S_SLEEP) {
 		/*
-		 * No data frames go out unless we're running.
+		 * Wakeup device for transmit.
 		 */
-		if (vap->iv_state != IEEE80211_S_RUN) {
-			IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
-				"%s: ignore data packet, state %u\n",
-				__func__, vap->iv_state);
+		ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
+	}
+	/*
+	 * No data frames go out unless we're running.
+	 */
+	if (vap->iv_state != IEEE80211_S_RUN) {
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
+			"%s: ignore data packet, state %u\n",
+			__func__, vap->iv_state);
 #if 0
-			vap->iv_stats.ist_tx_discard++;
+		vap->iv_stats.ist_tx_discard++;
 #endif
-			ifp->if_flags |= IFF_OACTIVE;
-			break;
-		}
+		ifp->if_flags |= IFF_OACTIVE;
+		return;
+	}
+	for (;;) {
 		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);	/* XXX: LOCK */
 		if (m == NULL)
 			break;
@@ -467,8 +473,10 @@
 	/* NB: power management bit is never sent by an AP */
 	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
 	    vap->iv_opmode != IEEE80211_M_HOSTAP &&
-	    vap->iv_opmode != IEEE80211_M_WDS)
+	    vap->iv_opmode != IEEE80211_M_WDS) {
 		wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
+		m->m_flags |= M_PWR_DOWN;	/* force special handling */
+	}
 	/* XXX WDS? */
 	m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
 

==== //depot/projects/vap/sys/net80211/ieee80211_power.c#3 (text+ko) ====

@@ -276,33 +276,33 @@
 	/*
 	 * Flush queued unicast frames.
 	 */
-	if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
-		if (vap->iv_set_tim != NULL)
-			vap->iv_set_tim(ni, 0);		/* just in case */
-		return;
-	}
-	IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
-	    "flush ps queue, %u packets queued", IEEE80211_NODE_SAVEQ_QLEN(ni));
-	for (;;) {
-		int qlen;
+	if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
+		    "flush ps queue, %u packets queued",
+		    IEEE80211_NODE_SAVEQ_QLEN(ni));
+		for (;;) {
+			int qlen;
 
-		IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
-		if (m == NULL)
-			break;
-		/* 
-		 * If this is the last packet, turn off the TIM bit.
-		 * If there are more packets, set the more packets bit
-		 * in the packet dispatched to the station.
-		 */
-		if (qlen != 0) {
-			struct ieee80211_frame_min *wh =
-				mtod(m, struct ieee80211_frame_min *);
-			wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+			IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+			if (m == NULL)
+				break;
+			/* 
+			 * If this is the last packet, turn off the TIM bit.
+			 * If there are more packets, set the more packets bit
+			 * in the packet dispatched to the station.
+			 */
+			if (qlen != 0) {
+				struct ieee80211_frame_min *wh =
+					mtod(m, struct ieee80211_frame_min *);
+				wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+			}
+			/* XXX need different driver interface */
+			/* XXX bypasses q max */
+			IF_ENQUEUE(&vap->iv_arp.ac_if.if_snd, m);
 		}
-		/* XXX need different driver interface */
-		/* XXX bypasses q max */
-		IF_ENQUEUE(&vap->iv_arp.ac_if.if_snd, m);
 	}
+	if (vap->iv_set_tim != NULL)
+		vap->iv_set_tim(ni, 0);
 }
 
 /*
@@ -362,3 +362,21 @@
 		ieee80211_send_nulldata(ieee80211_ref_node(ni));
 	}
 }
+
+void
+ieee80211_power_poll(struct ieee80211com *ic)
+{
+	if (ic->ic_opmode == IEEE80211_M_STA &&
+	    (ic->ic_flags & IEEE80211_F_PMGTON) &&
+	    ic->ic_nrunning &&
+	    (ticks - ic->ic_lastdata) > ic->ic_lintval) {
+		struct ieee80211vap *vap;
+
+		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+			if (vap->iv_opmode == IEEE80211_M_STA &&
+			    vap->iv_state == IEEE80211_S_RUN) {
+				ieee80211_new_state(vap, IEEE80211_S_SLEEP, 0);
+				break;
+			}
+	}
+}

==== //depot/projects/vap/sys/net80211/ieee80211_power.h#2 (text+ko) ====

@@ -47,4 +47,6 @@
 void	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_power_poll(struct ieee80211com *);
 #endif /* _NET80211_IEEE80211_POWER_H_ */

==== //depot/projects/vap/sys/net80211/ieee80211_proto.c#12 (text+ko) ====

@@ -76,7 +76,8 @@
 	"SCAN",		/* IEEE80211_S_SCAN */
 	"AUTH",		/* IEEE80211_S_AUTH */
 	"ASSOC",	/* IEEE80211_S_ASSOC */
-	"RUN"		/* IEEE80211_S_RUN */
+	"RUN",		/* IEEE80211_S_RUN */
+	"SLEEP"		/* IEEE80211_S_SLEEP */
 };
 const char *ieee80211_wme_acnames[] = {
 	"WME_AC_BE",
@@ -958,7 +959,7 @@
 			 * simply by re-associating.  Otherwise we need to
 			 * re-scan to select an appropriate ap.
 			 */ 
-			if (vap->iv_state != IEEE80211_S_RUN || forcescan)
+			if (vap->iv_state < IEEE80211_S_RUN || forcescan)
 				ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
 			else
 				ieee80211_new_state(vap, IEEE80211_S_ASSOC, 1);
@@ -1109,7 +1110,7 @@
 		 * through different means (e.g. the tx timeout on mgt frames).
 		 */
 		if (vap->iv_opmode != IEEE80211_M_STA ||
-		    vap->iv_state != IEEE80211_S_RUN)
+		    vap->iv_state < IEEE80211_S_RUN)
 			continue;
 		if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
 			/* 
@@ -1136,12 +1137,33 @@
 	}
 }
 
+static void
+sta_disassoc(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211vap *vap = arg;
+
+	if (ni->ni_vap == vap && ni->ni_associd != 0) {
+		IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+			IEEE80211_REASON_ASSOC_LEAVE);
+		ieee80211_node_leave(ni);
+	}
+}
+
+static void
+sta_deauth(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211vap *vap = arg;
+
+	if (ni->ni_vap == vap)
+		IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_ASSOC_LEAVE);
+}
+
 static int
 ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
 {
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ifnet *ifp = &vap->iv_if;
-	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
 	enum ieee80211_state ostate;
 
@@ -1159,6 +1181,7 @@
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 			break;
+		case IEEE80211_S_SLEEP:
 		case IEEE80211_S_RUN:
 			switch (vap->iv_opmode) {
 			case IEEE80211_M_STA:
@@ -1168,17 +1191,8 @@
 				ieee80211_sta_leave(ni);
 				break;
 			case IEEE80211_M_HOSTAP:
-				nt = &ic->ic_sta;
-				IEEE80211_NODE_LOCK(nt);
-				TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
-					if (ni->ni_associd == 0 ||
-					    ni->ni_vap != vap)
-						continue;
-					IEEE80211_SEND_MGMT(ni,
-					    IEEE80211_FC0_SUBTYPE_DISASSOC,
-					    IEEE80211_REASON_ASSOC_LEAVE);
-				}
-				IEEE80211_NODE_UNLOCK(nt);
+				ieee80211_iterate_nodes(&ic->ic_sta,
+					sta_disassoc, vap);
 				break;
 			default:
 				break;
@@ -1192,16 +1206,8 @@
 				    IEEE80211_REASON_AUTH_LEAVE);
 				break;
 			case IEEE80211_M_HOSTAP:
-				nt = &ic->ic_sta;
-				IEEE80211_NODE_LOCK(nt);
-				TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
-					if (ni->ni_vap != vap)
-						continue;
-					IEEE80211_SEND_MGMT(ni,
-					    IEEE80211_FC0_SUBTYPE_DEAUTH,
-					    IEEE80211_REASON_AUTH_LEAVE);
-				}
-				IEEE80211_NODE_UNLOCK(nt);
+				ieee80211_iterate_nodes(&ic->ic_sta,
+					sta_deauth, vap);
 				break;
 			default:
 				break;
@@ -1251,6 +1257,7 @@
 			}
 			break;
 		case IEEE80211_S_RUN:		/* beacon miss */
+		case IEEE80211_S_SLEEP:
 			ieee80211_sta_leave(ni);
 			vap->iv_flags &= ~IEEE80211_F_SIBSS;	/* XXX */
 			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
@@ -1283,6 +1290,7 @@
 			}
 			break;
 		case IEEE80211_S_RUN:
+		case IEEE80211_S_SLEEP:
 			switch (arg) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				IEEE80211_SEND_MGMT(ni,
@@ -1317,6 +1325,7 @@
 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
 			break;
 		case IEEE80211_S_RUN:
+		case IEEE80211_S_SLEEP:
 			ieee80211_sta_leave(ni);
 			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
 				/* NB: caller specifies ASSOC/REASSOC by arg */
@@ -1349,8 +1358,6 @@
 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
 				"%s: invalid transition\n", __func__);
 			break;
-		case IEEE80211_S_RUN:
-			break;
 		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
 		case IEEE80211_S_ASSOC:		/* infra mode */
 			KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
@@ -1373,6 +1380,11 @@
 				ieee80211_notify_node_join(ni, 
 					arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
 			break;
+		case IEEE80211_S_RUN:
+			break;
+		case IEEE80211_S_SLEEP:
+			ieee80211_sta_pwrsave(vap, 0);
+			return 0;
 		}
 		/*
 		 * Start/stop the authenticator when operating as an
@@ -1396,6 +1408,12 @@
 			if_start(ifp);
 		}
 		break;
+	case IEEE80211_S_SLEEP:
+		KASSERT(vap->iv_opmode == IEEE80211_M_STA,
+			("switch to %s state when operating in mode %u",
+			 ieee80211_state_name[nstate], vap->iv_opmode));
+		ieee80211_sta_pwrsave(vap, 1);
+		break;
 	}
 	return 0;
 }

==== //depot/projects/vap/sys/net80211/ieee80211_proto.h#7 (text+ko) ====

@@ -44,8 +44,9 @@
 	IEEE80211_S_AUTH	= 2,	/* try to authenticate */
 	IEEE80211_S_ASSOC	= 3,	/* try to assoc */
 	IEEE80211_S_RUN		= 4,	/* associated */
+	IEEE80211_S_SLEEP	= 5,	/* power save */
 };
-#define	IEEE80211_S_MAX		(IEEE80211_S_RUN+1)
+#define	IEEE80211_S_MAX		(IEEE80211_S_SLEEP+1)
 
 #define	IEEE80211_SEND_MGMT(_ni,_type,_arg) \
 	((*(_ni)->ni_ic->ic_send_mgmt)(_ni, _type, _arg))


More information about the p4-projects mailing list