PERFORCE change 65287 for review

Sam Leffler sam at FreeBSD.org
Tue Nov 16 16:56:49 PST 2004


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

Change 65287 by sam at sam_ebb on 2004/11/17 00:56:44

	ap-side power save support; still needs some tweaking in the
	handling of multicast frames and cleanup for devices that
	aren't capable

Affected files ...

.. //depot/projects/wifi/sys/dev/ath/if_ath.c#23 edit
.. //depot/projects/wifi/sys/dev/ath/if_athvar.h#7 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_freebsd.h#5 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_input.c#14 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_ioctl.h#9 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_node.c#16 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_output.c#9 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_proto.h#6 edit
.. //depot/projects/wifi/sys/net80211/ieee80211_var.h#11 edit

Differences ...

==== //depot/projects/wifi/sys/dev/ath/if_ath.c#23 (text+ko) ====

@@ -132,6 +132,8 @@
 			struct ieee80211_node *ni,
 			int subtype, int rssi, u_int32_t rstamp);
 static void	ath_rx_proc(void *, int);
+static struct ath_txq *ath_txq_setup(struct ath_softc *, int qtype,
+			int subtype, const char *typename);
 static int	ath_tx_setup(struct ath_softc *, int, int);
 static int	ath_tx_start(struct ath_softc *, struct ieee80211_node *,
 			     struct ath_buf *, struct mbuf *);
@@ -375,6 +377,12 @@
 		error = EIO;
 		goto bad2;
 	}
+	sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0, "CAB");
+	if (sc->sc_cabq == NULL) {
+		if_printf(ifp, "unable to setup CAB xmit queue!\n");
+		error = EIO;
+		goto bad2;
+	}
 	(void) ath_hal_getnumtxqueues(ah, &numqs);
 	if (numqs < 5) {
 		int qnum;
@@ -405,9 +413,11 @@
 	}
 
 	/* 
-	 * Special case certain configurations.
+	 * Special case certain configurations.  Note the
+	 * CAB queue is handled by these specially so don't
+	 * include them when checking the txq setup mask.
 	 */
-	switch (sc->sc_txqsetup) {
+	switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) {
 	case 0x01:
 		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc);
 		break;
@@ -997,6 +1007,7 @@
 	struct ath_buf *bf;
 	struct mbuf *m;
 	struct ieee80211_frame *wh;
+	struct ether_header *eh;
 
 	if ((ifp->if_flags & IFF_RUNNING) == 0 || sc->sc_invalid)
 		return;
@@ -1042,12 +1053,38 @@
 				ATH_TXBUF_UNLOCK(sc);
 				break;
 			}
+			/* 
+			 * Find the node for the destination so we can do
+			 * things like power save and fast frames aggregation.
+			 */
+			if (m->m_len < sizeof(struct ether_header) &&
+			   (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
+				ic->ic_stats.is_tx_nobuf++;	/* XXX */
+				ni = NULL;
+				goto bad;
+			}
+			eh = mtod(m, struct ether_header *);
+			ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+			if (ni == NULL) {
+				/* NB: ieee80211_find_txnode does stat+msg */
+				goto bad;
+			}
+			if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+			    (m->m_flags & M_PWR_SAV) == 0) {
+				/*
+				 * Station in power save mode; pass the frame
+				 * to the 802.11 layer and continue.  We'll get
+				 * the frame back when the time is right.
+				 */
+				ieee80211_pwrsave(ic, ni, m);
+				goto reclaim;
+			}
 			ifp->if_opackets++;
 			BPF_MTAP(ifp, m);
 			/*
 			 * Encapsulate the packet in prep for transmission.
 			 */
-			m = ieee80211_encap(ic, m, &ni);
+			m = ieee80211_encap(ic, m, ni);
 			if (m == NULL) {
 				DPRINTF(sc, ATH_DEBUG_ANY,
 					"%s: encapsulation failure\n",
@@ -1066,6 +1103,12 @@
 			 * to pass it along.
 			 */
 			ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+			if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+			    (m->m_flags & M_PWR_SAV) == 0) {
+				ieee80211_pwrsave(ic, ni, m);
+				ni = NULL;		/* keep reference */
+				goto reclaim;
+			}
 			m->m_pkthdr.rcvif = NULL;
 
 			wh = mtod(m, struct ieee80211_frame *);
@@ -1087,10 +1130,11 @@
 
 		if (ath_tx_start(sc, ni, bf, m)) {
 	bad:
+			ifp->if_oerrors++;
+	reclaim:
 			ATH_TXBUF_LOCK(sc);
 			STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
 			ATH_TXBUF_UNLOCK(sc);
-			ifp->if_oerrors++;
 			if (ni != NULL)
 				ieee80211_free_node(ni);
 			continue;
@@ -1734,6 +1778,7 @@
 	struct ath_buf *bf = sc->sc_bcbuf;
 	struct ath_hal *ah = sc->sc_ah;
 	struct mbuf *m;
+	int ncabq;
 
 	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: pending %u\n",
 		__func__, pending);
@@ -1747,21 +1792,19 @@
 	}
 
 	/*
-	 * Update dynamic beacon contents.  If this returns non-zero
-	 * then we need to update the descriptor state because the
-	 * beacon frame changed size and/or was re-allocated.
+	 * Update dynamic beacon contents.  If this returns
+	 * non-zero then we need to update the descriptor
+	 * state because the beacon frame changed size
+	 * (probably because of the TIM bitmap).
 	 */
 	m = bf->bf_m;
-	if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, &m)) {
-		/* NB: the old mbuf is free'd */
+	ncabq = sc->sc_cabq->axq_depth;		/* XXX check h/w queue */
+	if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, m, ncabq)) {
 		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-		bf->bf_m = NULL;
 		if (ath_beacon_setup(sc, bf, m) != 0) {
-			m_freem(m);
 			/* XXX statistic */
-			return;		/* XXX??? */
+			return;		/* XXX help??? */
 		}
-		bf->bf_m = m;
 	}
 
 	/*
@@ -1788,10 +1831,16 @@
 	}
 	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
 
+	/*
+	 * Enable the CAB queue before the beacon queue to
+	 * insure cab frames are triggered by this beacon.
+	 */
+	if (ncabq)
+		ath_hal_txstart(ah, sc->sc_cabq->axq_qnum);
 	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
 	ath_hal_txstart(ah, sc->sc_bhalq);
 	DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
-		"%s: TXDP%u = %p (%p)\n", __func__,
+		"%s: TXDP[%u] = %p (%p)\n", __func__,
 		sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc);
 }
 
@@ -2528,29 +2577,18 @@
 }
 
 /*
- * Setup a hardware data transmit queue for the specified
- * access control.  The hal may not support all requested
- * queues in which case it will return a reference to a
- * previously setup queue.  We record the mapping from ac's
- * to h/w queues for use by ath_tx_start and also track
- * the set of h/w queues being used to optimize work in the
- * transmit interrupt handler and related routines.
+ * Setup a h/w transmit queue.
  */
-static int
-ath_tx_setup(struct ath_softc *sc, int ac, int haltype)
+static struct ath_txq *
+ath_txq_setup(struct ath_softc *sc, int qtype, int subtype, const char *typename)
 {
 #define	N(a)	(sizeof(a)/sizeof(a[0]))
 	struct ath_hal *ah = sc->sc_ah;
 	HAL_TXQ_INFO qi;
 	int qnum;
 
-	if (ac >= N(sc->sc_ac2q)) {
-		device_printf(sc->sc_dev, "AC %u out of range, max %u!\n",
-			ac, N(sc->sc_ac2q));
-		return 0;
-	}
 	memset(&qi, 0, sizeof(qi));
-	qi.tqi_subtype = haltype;
+	qi.tqi_subtype = subtype;
 	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
 	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
 	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
@@ -2567,17 +2605,18 @@
 	 * due to a lack of tx descriptors.
 	 */
 	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
-	qnum = ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_DATA, &qi);
+	qnum = ath_hal_setuptxqueue(ah, qtype, &qi);
 	if (qnum == -1) {
 		device_printf(sc->sc_dev,
 			"Unable to setup hardware queue for %s traffic!\n",
-			acnames[ac]);
-		return 0;
+			typename);
+		return NULL;
 	}
 	if (qnum >= N(sc->sc_txq)) {
 		device_printf(sc->sc_dev, "hal qnum %u out of range, max %u!\n",
 			qnum, N(sc->sc_txq));
-		return 0;
+		ath_hal_releasetxqueue(ah, qnum);
+		return NULL;
 	}
 	if (!ATH_TXQ_SETUP(sc, qnum)) {
 		struct ath_txq *txq = &sc->sc_txq[qnum];
@@ -2590,8 +2629,36 @@
 		ATH_TXQ_LOCK_INIT(sc, txq);
 		sc->sc_txqsetup |= 1<<qnum;
 	}
-	sc->sc_ac2q[ac] = &sc->sc_txq[qnum];
-	return 1;
+	return &sc->sc_txq[qnum];
+#undef N
+}
+
+/*
+ * Setup a hardware data transmit queue for the specified
+ * access control.  The hal may not support all requested
+ * queues in which case it will return a reference to a
+ * previously setup queue.  We record the mapping from ac's
+ * to h/w queues for use by ath_tx_start and also track
+ * the set of h/w queues being used to optimize work in the
+ * transmit interrupt handler and related routines.
+ */
+static int
+ath_tx_setup(struct ath_softc *sc, int ac, int haltype)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ath_txq *txq;
+
+	if (ac >= N(sc->sc_ac2q)) {
+		device_printf(sc->sc_dev, "AC %u out of range, max %u!\n",
+			ac, N(sc->sc_ac2q));
+		return 0;
+	}
+	txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype, acnames[ac]);
+	if (txq != NULL) {
+		sc->sc_ac2q[ac] = txq;
+		return 1;
+	} else
+		return 0;
 #undef N
 }
 
@@ -2802,6 +2869,16 @@
 	}
 
 	/*
+	 * When servicing one or more stations in power-save mode
+	 * multicast frames must be buffered until after the beacon.
+	 * We use the CAB queue for that.
+	 */
+	if (ismcast && ic->ic_ps_sta) {
+		txq = sc->sc_cabq;
+		/* XXX? more bit in 802.11 frame header */
+	}
+
+	/*
 	 * Calculate miscellaneous flags.
 	 */
 	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
@@ -3007,7 +3084,12 @@
 	if (sc->sc_softled)
 		ath_update_led(sc);
 
-	ath_hal_txstart(ah, txq->axq_qnum);
+	/*
+	 * The CAB queue is started from the SWBA handler since
+	 * frames only go out on DTIM and to avoid possible races.
+	 */
+	if (txq != sc->sc_cabq)
+		ath_hal_txstart(ah, txq->axq_qnum);
 	return 0;
 }
 
@@ -3112,6 +3194,7 @@
 	struct ifnet *ifp = &sc->sc_if;
 
 	ath_tx_processq(sc, &sc->sc_txq[0]);
+	ath_tx_processq(sc, sc->sc_cabq);
 	ifp->if_flags &= ~IFF_OACTIVE;
 	sc->sc_tx_timer = 0;
 
@@ -3135,6 +3218,7 @@
 	ath_tx_processq(sc, &sc->sc_txq[1]);
 	ath_tx_processq(sc, &sc->sc_txq[2]);
 	ath_tx_processq(sc, &sc->sc_txq[3]);
+	ath_tx_processq(sc, sc->sc_cabq);
 
 	ifp->if_flags &= ~IFF_OACTIVE;
 	sc->sc_tx_timer = 0;
@@ -4146,6 +4230,9 @@
 			if_printf(ifp, "Use hw queue %u for %s traffic\n",
 				txq->axq_qnum, acnames[i]);
 		}
+		if_printf(ifp, "Use hw queue %u for CAB traffic\n",
+			sc->sc_cabq->axq_qnum);
+		if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq);
 	}
 #undef HAL_MODE_DUALBAND
 }

==== //depot/projects/wifi/sys/dev/ath/if_athvar.h#7 (text+ko) ====

@@ -207,10 +207,11 @@
 	u_int			sc_txqsetup;	/* h/w queues setup */
 	u_int			sc_txintrperiod;/* tx interrupt batching */
 	struct ath_txq		sc_txq[HAL_NUM_TX_QUEUES];
-	struct ath_txq		*sc_ac2q[5];	/* WME AC -> h/w qnum */ 
+	struct ath_txq		*sc_ac2q[5];	/* WME AC -> h/w q map */ 
 	struct task		sc_txtask;	/* tx int processing */
 
 	u_int			sc_bhalq;	/* HAL q for outgoing beacons */
+	struct ath_txq		*sc_cabq;	/* tx q for cab frames */
 	struct ath_buf		*sc_bcbuf;	/* beacon buffer */
 	struct ath_buf		*sc_bufptr;	/* allocated buffer ptr */
 	struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */

==== //depot/projects/wifi/sys/net80211/ieee80211_freebsd.h#5 (text+ko) ====

@@ -77,6 +77,7 @@
 
 extern	struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
 #define	M_LINK0		M_PROTO1		/* WEP requested */
+#define	M_PWR_SAV	M_PROTO4		/* bypass PS handling */
 /*
  * Encode WME access control bits in the PROTO flags.
  * This is safe since it's passed directly in to the

==== //depot/projects/wifi/sys/net80211/ieee80211_input.c#14 (text+ko) ====

@@ -54,6 +54,7 @@
 static struct mbuf *ieee80211_defrag(struct ieee80211com *,
 	struct ieee80211_node *, struct mbuf *);
 static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *);
+static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
 static void ieee80211_recv_pspoll(struct ieee80211com *,
 	struct ieee80211_node *, struct mbuf *);
 
@@ -252,36 +253,6 @@
 		}
 	}
 
-	/*
-	 * Check for ps-poll state change for the station.
-	 * XXX is there a response when pspoll is not supported?
-	 */
-	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
-	    ic->ic_set_tim != NULL &&
-	    ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
-	    (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) {
-		/* XXX statistics? */
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
-			"[%s] power save mode %s\n",
-			ether_sprintf(wh->i_addr2),
-			(wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "on" : "off"));
-		if ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0) {
-			/* turn off power save mode, dequeue stored packets */
-			ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
-			ic->ic_set_tim(ic, ni->ni_associd, 0);
-			while (_IF_QLEN(&ni->ni_savedq) != 0) {
-				struct mbuf *m0;
-				_IF_DEQUEUE(&ni->ni_savedq, m0);
-				/* XXX need different driver interface */
-				/* XXX handoff or enqueue? */
-				IF_ENQUEUE(&ifp->if_snd, m0);
-			}
-		} else {
-			/* turn on power save mode */
-			ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
-		}
-	}
-
 	switch (type) {
 	case IEEE80211_FC0_TYPE_DATA:
 		hdrsize = ieee80211_hdrsize(wh);
@@ -361,6 +332,7 @@
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
+			/* XXX no power-save support */
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (dir != IEEE80211_FC1_DIR_TODS) {
@@ -393,6 +365,14 @@
 				ic->ic_stats.is_rx_notassoc++;
 				goto err;
 			}
+
+			/*
+			 * Check for power save state change.
+			 */
+			if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+			    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+				ieee80211_node_pwrsave(ni,
+					wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
 			break;
 		default:
 			/* XXX here to keep compiler happy */
@@ -461,6 +441,9 @@
 		 */
 		m = ieee80211_decap(ic, m);
 		if (m == NULL) {
+			/* don't count Null data frames as errors */
+			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
+				goto out;
 			IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT,
 				"%s: decapsulation error\n", __func__);
 			ic->ic_stats.is_rx_decap++;
@@ -616,17 +599,19 @@
 	case IEEE80211_FC0_TYPE_CTL:
 		IEEE80211_NODE_STAT(ni, rx_ctrl);
 		ic->ic_stats.is_rx_ctl++;
-		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
-			goto out;
-		switch (subtype) {
-		case IEEE80211_FC0_SUBTYPE_PS_POLL:
-			/* XXX statistic */
-			/* Dump out a single packet from the host */
-			IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
-				"got power save probe from %s\n",
-				ether_sprintf(wh->i_addr2));
-			ieee80211_recv_pspoll(ic, ni, m);
-			break;
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			/*
+			 * Check for power save state change.
+			 */
+			if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+			    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+				ieee80211_node_pwrsave(ni,
+					wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
+			switch (subtype) {
+			case IEEE80211_FC0_SUBTYPE_PS_POLL:
+				ieee80211_recv_pspoll(ic, ni, m);
+				break;
+			}
 		}
 		goto out;
 	default:
@@ -2447,6 +2432,65 @@
 #undef IEEE80211_VERIFY_LENGTH
 #undef IEEE80211_VERIFY_ELEMENT
 
+/*
+ * Handle station power-save state change.
+ */
+static void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+
+	if (enable) {
+		if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+			ic->ic_ps_sta++;
+		ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%s] power save mode on, %u sta's in ps mode\n",
+		    ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
+		return;
+	}
+
+	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
+		ic->ic_ps_sta--;
+	ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+	    "[%s] power save mode off, %u sta's in ps mode\n",
+	    ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
+	/* XXX if no stations in ps mode, flush mc frames */
+
+	/*
+	 * Flush queued unicast frames.
+	 */
+	if (_IF_QLEN(&ni->ni_savedq) == 0) {
+		ic->ic_set_tim(ic, ni, 0);	/* just in case */
+		return;
+	}
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+	    "[%s] flush ps queue, %u packets queued\n",
+	    ether_sprintf(ni->ni_macaddr), _IF_QLEN(&ni->ni_savedq));
+	for (;;) {
+		_IF_DEQUEUE(&ni->ni_savedq, m);
+		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 (_IF_QLEN(&ni->ni_savedq) != 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 */
+		IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+	}
+}
+
+/*
+ * Process a received ps-poll frame.
+ */
 static void
 ieee80211_recv_pspoll(struct ieee80211com *ic,
 	struct ieee80211_node *ni, struct mbuf *m0)
@@ -2455,30 +2499,25 @@
 	struct mbuf *m;
 	u_int16_t aid;
 
-	if (ic->ic_set_tim == NULL)	/* No powersaving functionality */
-		return;
-	if (ni == ic->ic_bss) {
-		wh = mtod(m0, struct ieee80211_frame_min *);
+	wh = mtod(m0, struct ieee80211_frame_min *);
+	if (ni->ni_associd == 0) {
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
-			"station %s sent bogus power save poll\n",
+			"[%s] ps-poll for unassociated station\n",
 			ether_sprintf(wh->i_addr2));
+		ic->ic_stats.is_ps_unassoc++;
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_NOT_ASSOCED);
 		return;
 	}
 
-	wh = mtod(m0, struct ieee80211_frame_min *);
-	memcpy(&aid, wh->i_dur, sizeof(wh->i_dur));
-	if ((aid & 0xc000) != 0xc000) {
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
-			"station %s sent bogus aid %x\n",
-			ether_sprintf(wh->i_addr2), aid);
-		/* XXX statistic */
-		return;
-	}
+	aid = le16toh(*(u_int16_t *)wh->i_dur);
 	if (aid != ni->ni_associd) {
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
-			"station %s aid %x doesn't match pspoll aid %x\n",
-			ether_sprintf(wh->i_addr2), ni->ni_associd, aid);
-		/* XXX statistic */
+		    "[%s] sta aid 0x%x does not match ps-poll aid 0x%x\n",
+		    ether_sprintf(wh->i_addr2), ni->ni_associd, aid);
+		ic->ic_stats.is_ps_badaid++;
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_NOT_ASSOCED);
 		return;
 	}
 	ni->ni_inact = ic->ic_inact_run;
@@ -2487,25 +2526,26 @@
 	_IF_DEQUEUE(&ni->ni_savedq, m);
 	if (m == NULL) {
 		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
-			"station %s sent pspoll, but no packets are saved\n",
+			"[%s] got ps-poll, but queue empty\n",
 			ether_sprintf(wh->i_addr2));
-		/* XXX statistic */
+		ieee80211_send_nulldata(ic, ni);
+		ic->ic_stats.is_ps_qempty++;	/* XXX node stat */
+		ic->ic_set_tim(ic, ni, 0);	/* just in case */
 		return;
 	}
 	/* 
-	 * If this is the last packet, turn off the TIM fields.
-	 * If there are more packets, set the more packets bit.
+	 * If there are more packets, set the more packets bit
+	 * in the packet dispatched to the station; otherwise
+	 * turn off the TIM bit.
 	 */
-	if (_IF_QLEN(&ni->ni_savedq) == 0) {
-		if (ic->ic_set_tim) 
-			ic->ic_set_tim(ic, ni->ni_associd, 0);
-	} else {
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		"[%s] got ps-poll, send packet, %u still queued\n",
+		ether_sprintf(ni->ni_macaddr), _IF_QLEN(&ni->ni_savedq));
+	if (_IF_QLEN(&ni->ni_savedq) != 0) {
 		wh = mtod(m, struct ieee80211_frame_min *);
 		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
-	}
-	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
-		"enqueued power saving packet for station %s\n",
-		ether_sprintf(ni->ni_macaddr));
-	/* XXX need different driver interface */
+	} else
+		ic->ic_set_tim(ic, ni, 0);
+	m->m_flags |= M_PWR_SAV;		/* bypass PS handling */
 	IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
 }

==== //depot/projects/wifi/sys/net80211/ieee80211_ioctl.h#9 (text+ko) ====

@@ -167,6 +167,9 @@
 	u_int32_t	is_crypto_keyfail;	/* driver key alloc failed */
 	u_int32_t	is_ibss_capmismatch;	/* merge failed-cap mismatch */
 	u_int32_t	is_ibss_norate;		/* merge failed-rate mismatch */
+	u_int32_t	is_ps_unassoc;		/* ps-poll for unassoc. sta */
+	u_int32_t	is_ps_badaid;		/* ps-poll w/ incorrect aid */
+	u_int32_t	is_ps_qempty;		/* ps-poll w/ nothing to send */
 };
 
 /*

==== //depot/projects/wifi/sys/net80211/ieee80211_node.c#16 (text+ko) ====

@@ -62,6 +62,9 @@
 static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *);
 static void ieee80211_timeout_stations(struct ieee80211_node_table *);
 
+static void ieee80211_set_tim(struct ieee80211com *,
+		struct ieee80211_node *, int set);
+
 static void ieee80211_node_table_init(struct ieee80211com *ic,
 	struct ieee80211_node_table *nt, const char *name, int inact,
 	void (*timeout)(struct ieee80211_node_table *));
@@ -103,6 +106,16 @@
 		printf("%s: no memory for AID bitmap!\n", __func__);
 		ic->ic_max_aid = 0;
 	}
+
+	/* XXX defer until using hostap/ibss mode */
+	ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(u_int8_t);
+	MALLOC(ic->ic_tim_bitmap, u_int8_t *, ic->ic_tim_len,
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ic->ic_tim_bitmap == NULL) {
+		/* XXX no way to recover */
+		printf("%s: no memory for TIM bitmap!\n", __func__);
+	}
+	ic->ic_set_tim = ieee80211_set_tim;	/* NB: driver should override */
 }
 
 void
@@ -171,8 +184,14 @@
 		ieee80211_node_table_free(ic->ic_sta);
 		ic->ic_sta = NULL;
 	}
-	if (ic->ic_aid_bitmap != NULL)
+	if (ic->ic_aid_bitmap != NULL) {
 		FREE(ic->ic_aid_bitmap, M_DEVBUF);
+		ic->ic_aid_bitmap = NULL;
+	}
+	if (ic->ic_tim_bitmap != NULL) {
+		FREE(ic->ic_tim_bitmap, M_DEVBUF);
+		ic->ic_tim_bitmap = NULL;
+	}
 }
 
 /* 
@@ -789,9 +808,25 @@
 node_cleanup(struct ieee80211_node *ni)
 {
 #define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ieee80211com *ic = ni->ni_ic;
 	int i;
 
 	/* NB: preserve ni_table */
+	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+		ic->ic_ps_sta--;
+		ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%s] power save mode off, %u sta's in ps mode\n",
+		    ether_sprintf(ni->ni_macaddr), ic->ic_ps_sta);
+	}
+	if (_IF_QLEN(&ni->ni_savedq) != 0) {
+		/*
+		 * Drain power save queue.
+		 */
+		_IF_DRAIN(&ni->ni_savedq);
+		if (ic->ic_set_tim != NULL)
+			ic->ic_set_tim(ic, ni, 0);
+	}
 	ni->ni_associd = 0;
 	if (ni->ni_challenge != NULL) {
 		FREE(ni->ni_challenge, M_DEVBUF);
@@ -814,7 +849,7 @@
 			m_freem(ni->ni_rxfrag[i]);
 			ni->ni_rxfrag[i] = NULL;
 		}
-	ieee80211_crypto_delkey(ni->ni_ic, &ni->ni_ucastkey);
+	ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
 #undef N
 }
 
@@ -994,6 +1029,8 @@
 {
 #define	IS_CTL(wh) \
 	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+#define	IS_PSPOLL(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
 	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
 
@@ -1007,13 +1044,14 @@
 	/* XXX check ic_bss first in station mode */
 	/* XXX 4-address frames? */
 	IEEE80211_NODE_LOCK(nt);
-	if (IS_CTL(wh))
+	if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
 		ni = _ieee80211_find_node(nt, wh->i_addr1);
 	else
 		ni = _ieee80211_find_node(nt, wh->i_addr2);
 	IEEE80211_NODE_UNLOCK(nt);
 
 	return (ni != NULL ? ni : ieee80211_ref_node(ic->ic_bss));
+#undef IS_PSPOLL
 #undef IS_CTL
 }
 
@@ -1045,10 +1083,17 @@
 	ni = _ieee80211_find_node(nt, macaddr);
 	IEEE80211_NODE_UNLOCK(nt);
 
-	if (ni == NULL &&
-	    (ic->ic_opmode == IEEE80211_M_IBSS ||
-	     ic->ic_opmode == IEEE80211_M_AHDEMO))
-		ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+	if (ni == NULL) {
+		if (ic->ic_opmode == IEEE80211_M_IBSS ||
+		    ic->ic_opmode == IEEE80211_M_AHDEMO)
+			ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+		else {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+				"[%s] no node, discard frame (%s)\n",
+				ether_sprintf(macaddr), __func__);
+			ic->ic_stats.is_tx_nonode++;
+		}
+	}
 	return ni;
 }
 
@@ -1144,14 +1189,6 @@
 		TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
 		LIST_REMOVE(ni, ni_hash);
 	}
-	if (_IF_QLEN(&ni->ni_savedq) != 0) {	/* XXX */
-		/*
-		 * Drain power save queue.
-		 */
-		_IF_DRAIN(&ni->ni_savedq);
-		if (ic->ic_set_tim)
-			ic->ic_set_tim(ic, ni->ni_associd, 0);
-	}
 	ic->ic_node_free(ni);
 }
 
@@ -1649,6 +1686,38 @@
 }
 
 /*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static void
+ieee80211_set_tim(struct ieee80211com *ic, struct ieee80211_node *ni, int set)
+{
+	u_int16_t aid;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		ic->ic_opmode == IEEE80211_M_IBSS,
+		("operating mode %u", ic->ic_opmode));
+
+	aid = IEEE80211_AID(ni->ni_associd);
+	KASSERT(aid < ic->ic_max_aid,
+		("bogus aid %u, max %u", aid, ic->ic_max_aid));
+
+	/* XXX locking */
+	if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+		if (set) {
+			setbit(ic->ic_tim_bitmap, aid);
+			ic->ic_ps_pending++;
+		} else {
+			clrbit(ic->ic_tim_bitmap, aid);
+			ic->ic_ps_pending--;
+		}
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+			"[%s] aid %u has %sframes\n",
+			ether_sprintf(ni->ni_macaddr), aid, set ? "" : "no ");
+		ic->ic_flags |= IEEE80211_F_TIMUPDATE;
+	}
+}
+
+/*
  * Node table support.
  */
 

==== //depot/projects/wifi/sys/net80211/ieee80211_output.c#9 (text+ko) ====

@@ -164,6 +164,48 @@
 }
 
 /*
+ * Send a null data frame to the specified node.
+ */
+int
+ieee80211_send_nulldata(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct mbuf *m;
+	struct ieee80211_frame *wh;
+
+	MGETHDR(m, M_NOWAIT, MT_HEADER);
+	if (m == NULL) {
+		/* XXX debug msg */
+		ic->ic_stats.is_tx_nobuf++;
+		return ENOMEM;
+	}
+	m->m_pkthdr.rcvif = (void *) ieee80211_ref_node(ni);
+
+	wh = mtod(m, struct ieee80211_frame *);
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA |
+		IEEE80211_FC0_SUBTYPE_NODATA;
+	*(u_int16_t *)wh->i_dur = 0;
+	*(u_int16_t *)wh->i_seq =
+	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseq++;
+
+	/* XXX WDS */
+	wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_myaddr);
+	m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+
+	ni->ni_inact = ic->ic_inact_auth;
+	IEEE80211_NODE_STAT(ni, tx_data);
+
+	IF_ENQUEUE(&ic->ic_mgtq, m);		/* cheat */
+	if_start(ifp);
+
+	return 0;
+}
+
+/*
  * Insure there is sufficient contiguous space to encapsulate the
  * 802.11 data frame.  If room isn't already there, arrange for it.
  * Drivers and cipher modules assume we have done the necessary work
@@ -262,41 +304,21 @@
 }
 
 /*
- * Encapsulate an outbound data frame.  The mbuf chain is updated and
- * a reference to the destination node is returned.  If an error is
- * encountered NULL is returned and the node reference will also be NULL.
- * 
- * NB: The caller is responsible for free'ing a returned node reference.
- *     The convention is ic_bss is not reference counted; the caller must
- *     maintain that.
+ * Encapsulate an outbound data frame.  The mbuf chain is updated.
+ * If an error is encountered NULL is returned.  The caller is required
+ * to provide a node reference and pullup the ethernet header in the
+ * first mbuf.
  */
 struct mbuf *
 ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
-	struct ieee80211_node **pni)
+	struct ieee80211_node *ni)
 {
 	struct ether_header eh;
 	struct ieee80211_frame *wh;
-	struct ieee80211_node *ni = NULL;
 	struct ieee80211_key *key;
 	struct llc *llc;
 	int hdrsize, datalen;
 
-	if (m->m_len < sizeof(struct ether_header) &&
-	    (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
-		ic->ic_stats.is_tx_nobuf++;
-		goto bad;
-	}
-	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
-
-	ni = ieee80211_find_txnode(ic, eh.ether_dhost);
-	if (ni == NULL) {
-		IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
-			"%s: no node for dst %s, discard frame\n",
-			__func__, ether_sprintf(eh.ether_dhost));
-		ic->ic_stats.is_tx_nonode++; 
-		goto bad;
-	}
-
 	/* 
 	 * If node has a vlan tag then all traffic
 	 * to it must have a matching tag.
@@ -313,6 +335,9 @@
 		}
 	}
 
+	KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
+	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
+
 	/*
 	 * Insure space for additional headers.  First identify
 	 * transmit key to use in calculating any buffer adjustments
@@ -473,14 +498,10 @@
 	IEEE80211_NODE_STAT(ni, tx_data);
 	IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
 
-	*pni = ni;
 	return m;
 bad:
 	if (m != NULL)
 		m_freem(m);
-	if (ni != NULL)
-		ieee80211_free_node(ni);
-	*pni = NULL;
 	return NULL;
 }
 
@@ -988,6 +1009,15 @@
 		IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
 
 		ieee80211_node_unauthorize(ic, ni);	/* port closed */
+		/*
+		 * If station is in power-save state mark the frame
+		 * so it goes out immediately.  The station is likely
+		 * to ignore it but the alternative is to defer reclaiming
+		 * station state until it wakes up and polls for the
+		 * frame which leads to possible DOS.
+		 */
+		if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
+			m->m_flags |= M_PWR_SAV;
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
@@ -1166,16 +1196,16 @@
 	 *	[tlv] WPA/RSN parameters
 	 * XXX WME, etc.
 	 * XXX Vendor-specific OIDs (e.g. Atheros)
+	 * NB: we allocate the max space required for the TIM bitmap.
 	 */
 	rs = &ni->ni_rates;
-	/* XXX may be better to just allocate a max-sized buffer */
 	pktlen =   8					/* time stamp */
 		 + sizeof(u_int16_t)			/* beacon interval */
 		 + sizeof(u_int16_t)			/* capabilities */
 		 + 2 + ni->ni_esslen			/* ssid */
 	         + 2 + IEEE80211_RATE_SIZE		/* supported rates */
 	         + 2 + 1				/* DS parameters */
-		 + 2 + 4				/* DTIM/IBSSPARMS */
+		 + 2 + 4 + ic->ic_tim_len		/* DTIM/IBSSPARMS */
 		 + 2 + 1				/* ERP */
 	         + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
 		 + 2*sizeof(struct ieee80211_ie_wpa)	/* WPA 1+2 */
@@ -1224,16 +1254,15 @@
 		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
 		*frm++ = 2;
 		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
-		bo->bo_tim_len = 4;
+		bo->bo_tim_len = 0;
 	} else {
-		/* TODO: TIM */
 		*frm++ = IEEE80211_ELEMID_TIM;
 		*frm++ = 4;	/* length */
 		*frm++ = 0;	/* DTIM count */ 
 		*frm++ = 1;	/* DTIM period */
 		*frm++ = 0;	/* bitmap control */
 		*frm++ = 0;	/* Partial Virtual Bitmap (variable length) */
-		bo->bo_tim_len = 6;
+		bo->bo_tim_len = 1;
 	}
 	bo->bo_trailer = frm;

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list