Date: Wed, 17 Nov 2004 00:56:48 GMT From: Sam Leffler <sam@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 65287 for review Message-ID: <200411170056.iAH0umjZ017317@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=65287 Change 65287 by sam@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) <<<
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200411170056.iAH0umjZ017317>