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