Index: sys/dev/ath/ath_hal/ah.c =================================================================== --- sys/dev/ath/ath_hal/ah.c +++ sys/dev/ath/ath_hal/ah.c @@ -1415,6 +1415,8 @@ /* * Get CCA setting. + * + * XXX TODO: this may not actually be the same on all chips! Grr! */ int ath_hal_getcca(struct ath_hal *ah) Index: sys/dev/ath/if_ath.c =================================================================== --- sys/dev/ath/if_ath.c +++ sys/dev/ath/if_ath.c @@ -911,6 +911,8 @@ | IEEE80211_C_PMGT /* Station side power mgmt */ | IEEE80211_C_SWSLEEP ; + ic->ic_flags_ext = + IEEE80211_FEXT_SEQNO_OFFLOAD; /* driver does sequence number assignment */ /* * Query the hal to figure out h/w crypto support. */ @@ -1010,6 +1012,28 @@ sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah); sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah); + /* + * Some WB335 cards do not support antenna diversity. Since + * we use a hardcoded value for AR9565 instead of using the + * EEPROM/OTP data, remove the combining feature from + * the HW capabilities bitmap. + */ + /* + * XXX TODO: check reference driver and ath9k for what to do + * here for WB335. I think we have to actually disable the + * LNA div processing in the HAL and instead use the hard + * coded values; and then use BT diversity. + * + * .. but also need to setup MCI too for WB335.. + */ +#if 0 + if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { + device_printf(sc->sc_dev, "%s: WB335: disabling LNA mixer diversity\n", + __func__); + sc->sc_dolnadiv = 0; + } +#endif + if (ath_hal_hasfastframes(ah)) ic->ic_caps |= IEEE80211_C_FF; wmodes = ath_hal_getwirelessmodes(ah); @@ -5649,6 +5673,31 @@ */ IEEE80211_LOCK_ASSERT(ic); + /* + * XXX TODO: if nstate is _S_CAC, then we should disable + * ACK processing until CAC is completed. + */ + + /* + * XXX TODO: if we're on a passive channel, then we should + * not allow any ACKs or self-generated frames until we hear + * a beacon. Unfortunately there isn't a notification from + * net80211 so perhaps we could slot that particular check + * into the mgmt receive path and just ensure that we clear + * it on RX of beacons in passive mode (and only clear it + * once, obviously.) + */ + + /* + * XXX TODO: net80211 should be tracking whether channels + * have heard beacons and are thus considered "OK" for + * transmitting - and then inform the driver about this + * state change. That way if we hear an AP go quiet + * (and nothing else is beaconing on a channel) the + * channel can go back to being passive until another + * beacon is heard. + */ + if (nstate == IEEE80211_S_RUN) { /* NB: collect bss node again, it may have changed */ ieee80211_free_node(ni); @@ -5670,6 +5719,14 @@ case IEEE80211_M_HOSTAP: case IEEE80211_M_IBSS: case IEEE80211_M_MBSS: + + /* + * TODO: Enable ACK processing (ie, clear AR_DIAG_ACK_DIS.) + * For channels that are in CAC, we may have disabled + * this during CAC to ensure we don't ACK frames + * sent to us. + */ + /* * Allocate and setup the beacon frame. * @@ -6274,6 +6331,15 @@ */ if (ath_dfs_process_radar_event(sc, sc->sc_curchan)) { /* DFS event found, initiate channel change */ + + /* + * XXX TODO: immediately disable ACK processing + * on the current channel. This would be done + * by setting AR_DIAG_ACK_DIS (AR5212; may be + * different for others) until we are out of + * CAC. + */ + /* * XXX doesn't currently tell us whether the event * XXX was found in the primary or extension Index: sys/dev/ath/if_ath_beacon.c =================================================================== --- sys/dev/ath/if_ath_beacon.c +++ sys/dev/ath/if_ath_beacon.c @@ -758,6 +758,12 @@ } } ath_beacon_setup(sc, bf); + + /* + * Assign sequence number to the beacon frame from the NON-QOS TID. + */ + ieee80211_tx_seqno_assign(bf->bf_node, bf->bf_m); + bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE); /* Index: sys/dev/ath/if_ath_tx.c =================================================================== --- sys/dev/ath/if_ath_tx.c +++ sys/dev/ath/if_ath_tx.c @@ -2016,6 +2016,9 @@ subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL) { bf->bf_state.bfs_dobaw = 1; } + } else { + /* XXX else - do seqno assignment for non-aggregate frames */ + ieee80211_tx_seqno_assign(ni, m0); } /* @@ -2030,7 +2033,7 @@ "%s: tid %d: ampdu pending, seqno %d\n", __func__, tid, M_SEQNO_GET(m0)); - /* This also sets up the DMA map */ + /* This also sets up the DMA map; crypto; frame parameters, etc */ r = ath_tx_normal_setup(sc, ni, bf, m0, txq); if (r != 0) @@ -2146,6 +2149,9 @@ /* XXX If it's an ADDBA, override the correct queue */ do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid); + /* XXX do seqno assignment for frames; we don't do aggregate raw frames yet */ + ieee80211_tx_seqno_assign(ni, m0); + /* Map ADDBA to the correct priority */ if (do_override) { #if 0 Index: sys/net80211/ieee80211_freebsd.c =================================================================== --- sys/net80211/ieee80211_freebsd.c +++ sys/net80211/ieee80211_freebsd.c @@ -592,7 +592,9 @@ * Assert the IC TX lock is held - this enforces the * processing -> queuing order is maintained */ - IEEE80211_TX_LOCK_ASSERT(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK_ASSERT(ic); + error = ic->ic_transmit(ic, m); if (error) { struct ieee80211_node *ni; @@ -619,7 +621,8 @@ * When transmitting via the VAP, we shouldn't hold * any IC TX lock as the VAP TX path will acquire it. */ - IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(vap->iv_ic)) + IEEE80211_TX_UNLOCK_ASSERT(vap->iv_ic); return (ifp->if_transmit(ifp, m)); Index: sys/net80211/ieee80211_ht.c =================================================================== --- sys/net80211/ieee80211_ht.c +++ sys/net80211/ieee80211_ht.c @@ -2430,7 +2430,11 @@ tid = tap->txa_tid; /* - * XXX TODO: This is racy with any other parallel TX going on. :( + * XXX TODO: This is racy with any other parallel TX going on. + * + * Ideally net80211/driver would suspend TX on this TID and then wait + * for it to complete, and then start ADDBA. That way we are + * completely aware of what the txseq should be. */ tap->txa_start = ni->ni_txseqs[tid]; @@ -2706,9 +2710,14 @@ * ic_raw_xmit will free the node reference * regardless of queue/TX success or failure. */ - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); + ret = ieee80211_raw_output(vap, ni, m, NULL); - IEEE80211_TX_UNLOCK(ic); + + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); + if (ret != 0) { IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, ni, "send BAR: failed: (ret = %d)\n", Index: sys/net80211/ieee80211_hwmp.c =================================================================== --- sys/net80211/ieee80211_hwmp.c +++ sys/net80211/ieee80211_hwmp.c @@ -646,7 +646,8 @@ return ENOMEM; } - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, @@ -664,7 +665,8 @@ params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; ret = ieee80211_raw_output(vap, ni, m, ¶ms); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); return (ret); } Index: sys/net80211/ieee80211_mesh.c =================================================================== --- sys/net80211/ieee80211_mesh.c +++ sys/net80211/ieee80211_mesh.c @@ -1049,6 +1049,9 @@ * This consumes the node ref grabbed above and * the mbuf, regardless of whether there's a problem * or not. + * + * XXX TODO: this completely re-encapsulates the frame, which + * we need to check to see if it's the right thing to do. */ (void) ieee80211_vap_pkt_send_dest(vap, m, ni); } @@ -1234,10 +1237,18 @@ * * Doing a direct parent transmit may not be the correct thing * to do here; we'll have to re-think this soon. + * + * XXX TODO: this also means we don't allocate a new seqno to the + * given peer, can't do A-MPDU for 11n, etc. This path should + * really stash the mesh state in a mbuf tag or something and + * then pass it out the normal path to be encap'ed with the + * right header details! */ - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); err = ieee80211_parent_xmitpkt(ic, mcopy); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); if (!err) if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c +++ sys/net80211/ieee80211_output.c @@ -254,7 +254,8 @@ * point (where TX state is being checked/modified) * through to driver queue. */ - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); /* * XXX make the encap and transmit code a separate function @@ -268,7 +269,8 @@ m = ieee80211_encap(vap, ni, m); if (m == NULL) { /* NB: stat+msg handled in ieee80211_encap */ - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); ieee80211_free_node(ni); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENOBUFS); @@ -280,7 +282,8 @@ * Unlock at this point - no need to hold it across * ieee80211_free_node() (ie, the comlock) */ - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); ic->ic_lastdata = ticks; return (0); @@ -657,7 +660,8 @@ /* NB: ieee80211_encap does not include 802.11 header */ IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); /* * NB: DLT_IEEE802_11_RADIO identifies the parameters are @@ -668,7 +672,8 @@ ret = ieee80211_raw_output(vap, ni, m, (const struct ieee80211_bpf_params *)(dst->sa_len ? dst->sa_data : NULL)); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); return (ret); bad: if (m != NULL) @@ -700,7 +705,8 @@ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); ieee80211_seq seqno; - IEEE80211_TX_LOCK_ASSERT(ni->ni_ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ni->ni_ic)) + IEEE80211_TX_LOCK_ASSERT(ni->ni_ic); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { @@ -766,19 +772,14 @@ *(uint16_t *)&wh->i_dur[0] = 0; /* - * XXX TODO: this is what the TX lock is for. - * Here we're incrementing sequence numbers, and they - * need to be in lock-step with what the driver is doing - * both in TX ordering and crypto encap (IV increment.) - * * If the driver does seqno itself, then we can skip * assigning sequence numbers here, and we can avoid * requiring the TX lock. */ tap = &ni->ni_tx_ampdu[tid]; - if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) + if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) { m->m_flags |= M_AMPDU_MPDU; - else { + } else if (! IEEE80211_CONF_SEQNO_OFFLOAD(ni->ni_ic)) { if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK, type & IEEE80211_FC0_SUBTYPE_MASK)) seqno = ni->ni_txseqs[tid]++; @@ -830,7 +831,8 @@ return ENOMEM; } - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); wh = mtod(m, struct ieee80211_frame *); ieee80211_send_setup(ni, m, @@ -859,7 +861,8 @@ IEEE80211_NODE_STAT(ni, tx_mgmt); ret = ieee80211_raw_output(vap, ni, m, params); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); return (ret); } @@ -929,7 +932,8 @@ return ENOMEM; } - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ if (ni->ni_flags & IEEE80211_NODE_QOS) { @@ -979,7 +983,8 @@ wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); ret = ieee80211_raw_output(vap, ni, m, NULL); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); return (ret); } @@ -1245,7 +1250,8 @@ uint8_t *qos; int is_amsdu = 0; - IEEE80211_TX_LOCK_ASSERT(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK_ASSERT(ic); /* * Copy existing Ethernet header to a safe place. The @@ -1558,7 +1564,8 @@ * numbers. If the driver does it, then don't do it here; * and we don't need the TX lock held. */ - if ((m->m_flags & M_AMPDU_MPDU) == 0) { + if (((m->m_flags & M_AMPDU_MPDU) == 0) && + (! IEEE80211_CONF_SEQNO_OFFLOAD(ic))) { /* * NB: don't assign a sequence # to potential * aggregates; we expect this happens at the @@ -1576,7 +1583,7 @@ htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); } - } else { + } else if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) { /* * XXX TODO TX lock is needed for atomic updates of sequence * numbers. If the driver does it, then don't do it here; @@ -2213,7 +2220,8 @@ return ENOMEM; } - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, IEEE80211_NONQOS_TID, sa, da, bssid); @@ -2244,7 +2252,8 @@ params.ibp_try0 = tp->maxretry; params.ibp_power = ni->ni_txpower; ret = ieee80211_raw_output(vap, ni, m, ¶ms); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); ieee80211_free_node(bss); return (ret); } @@ -2864,7 +2873,8 @@ M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); KASSERT(m != NULL, ("no room for header")); - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); ieee80211_send_setup(bss, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); @@ -2880,7 +2890,8 @@ IEEE80211_NODE_STAT(bss, tx_mgmt); ret = ieee80211_raw_output(vap, bss, m, NULL); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); return (ret); } @@ -3360,10 +3371,12 @@ * If the driver identifies it does its own TX seqno management then * we can skip this (and still not do the TX seqno.) */ - seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; - *(uint16_t *)&wh->i_seq[0] = - htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); - M_SEQNO_SET(m, seqno); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) { + seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; + *(uint16_t *)&wh->i_seq[0] = + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); + } /* XXX faster to recalculate entirely or just changes? */ capinfo = ieee80211_getcapinfo(vap, ni->ni_chan); @@ -3682,3 +3695,101 @@ } m_freem(m); } + +/* + * Assign a sequence number to the given frame. + * + * The frame must have: + * + * + the frametype / subtype already setup; + * + the TID must be in the mbuf already / packet header for QoS + * frames so it can be accessed. + * + * This can either be called by the driver in its own serialisation + * path, or in net80211 under the TX serialisation lock. + */ +void +ieee80211_tx_seqno_assign(struct ieee80211_node *ni, struct mbuf *m) +{ + struct ieee80211_frame *wh; + int tid = IEEE80211_NONQOS_TID; + uint16_t seqno; + uint8_t type, subtype; + + wh = mtod(m, struct ieee80211_frame *); + + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: called; type=0x%.2x, subtype=0x%.2x", __func__, + type, subtype); + + /* Figure out if we need a sequence number or not */ + if (! IEEE80211_HAS_SEQ(type, subtype)) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: ! HAS_SEQ", __func__); + return; + } + + /* ctrl - no sequence number */ + if (IEEE80211_IS_CTL(wh)) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: ! IS_CTL", __func__); + return; + } + + /* mgmt - NONQOS_TID */ + if (IEEE80211_IS_MGMT(wh)) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: IS_MGMT; NONQOS-TID", __func__); + tid = IEEE80211_NONQOS_TID; + } + + /* data - QoS - look at TID in frame; only if it needs it */ + if (IEEE80211_IS_DATA(wh) && + (subtype & IEEE80211_FC0_SUBTYPE_QOS)) { + struct ieee80211_qosframe *qwh; + + qwh = mtod(m, struct ieee80211_qosframe *); + tid = qwh->i_qos[0] & IEEE80211_QOS_TID; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: QOS; TID=%d", __func__, tid); + } + + /* data - non-QoS - NONQOS_TID - if it needs it */ + if (IEEE80211_IS_DATA(wh) && + ((subtype & IEEE80211_FC0_SUBTYPE_QOS) == 0)) { + tid = IEEE80211_NONQOS_TID; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: DATA; NON-QOS", __func__); + } + + /* + * data + IEEE80211_FC0_SUBTYPE_QOS_NULL - use NONQOS_TID; + * otherwise A-MPDU BAW tracking gets confused. + */ + if (IEEE80211_IS_DATA(wh) && + (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL)) { + tid = IEEE80211_NONQOS_TID; + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: DATA; QOS-NULL", __func__); + } + + /* + * Finally - assign the sequence number as appropriate. + */ + wh = mtod(m, struct ieee80211_frame *); + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_OUTPUT, ni, + "%s: seqno-assign: type=0x%x,subtype=0x%x, tid=%d, seqno=%d", + __func__, + type, + subtype, + tid, + ni->ni_txseqs[tid]); + seqno = ni->ni_txseqs[tid]++; + *(uint16_t *)wh->i_seq = + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + M_SEQNO_SET(m, seqno); +} Index: sys/net80211/ieee80211_proto.h =================================================================== --- sys/net80211/ieee80211_proto.h +++ sys/net80211/ieee80211_proto.h @@ -121,6 +121,7 @@ const struct ether_header *); void ieee80211_tx_complete(struct ieee80211_node *, struct mbuf *, int); +void ieee80211_tx_seqno_assign(struct ieee80211_node *, struct mbuf *); /* * The formation of ProbeResponse frames requires guidance to Index: sys/net80211/ieee80211_superg.c =================================================================== --- sys/net80211/ieee80211_superg.c +++ sys/net80211/ieee80211_superg.c @@ -580,7 +580,8 @@ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; - IEEE80211_TX_LOCK_ASSERT(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK_ASSERT(ic); /* encap and xmit */ m = ieee80211_encap(vap, ni, m); @@ -653,9 +654,13 @@ M_AGE_SUB(m, quanta); IEEE80211_FF_UNLOCK(ic); - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); + ff_flush(head, m); - IEEE80211_TX_UNLOCK(ic); + + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); } static void @@ -759,7 +764,8 @@ struct mbuf *mstaged; uint32_t txtime, limit; - IEEE80211_TX_UNLOCK_ASSERT(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK_ASSERT(ic); IEEE80211_LOCK(ic); limit = IEEE80211_TXOP_TO_US( @@ -824,12 +830,14 @@ IEEE80211_FF_UNLOCK(ic); if (mstaged != NULL) { - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); IEEE80211_NOTE(vap, IEEE80211_MSG_SUPERG, ni, "%s: flush staged frame", __func__); /* encap and xmit */ ff_transmit(ni, mstaged); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); } return m; /* NB: original frame */ } Index: sys/net80211/ieee80211_wds.c =================================================================== --- sys/net80211/ieee80211_wds.c +++ sys/net80211/ieee80211_wds.c @@ -288,11 +288,13 @@ /* * Encapsulate the packet in prep for transmission. */ - IEEE80211_TX_LOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_LOCK(ic); mcopy = ieee80211_encap(vap, ni, mcopy); if (mcopy == NULL) { /* NB: stat+msg handled in ieee80211_encap */ - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); ieee80211_free_node(ni); continue; } @@ -300,7 +302,8 @@ mcopy->m_pkthdr.rcvif = (void *) ni; err = ieee80211_parent_xmitpkt(ic, mcopy); - IEEE80211_TX_UNLOCK(ic); + if (! IEEE80211_CONF_SEQNO_OFFLOAD(ic)) + IEEE80211_TX_UNLOCK(ic); if (!err) { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1);