Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 2 Jul 2009 22:34:43 +0000 (UTC)
From:      Sam Leffler <sam@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r195298 - in projects/mesh11s/sys: conf dev/mwl net80211
Message-ID:  <200907022234.n62MYhiM058710@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sam
Date: Thu Jul  2 22:34:42 2009
New Revision: 195298
URL: http://svn.freebsd.org/changeset/base/195298

Log:
  Overhaul send/recv action frame handling:
  o handlers are now registered; ieee80211_send_action_register for a
    send handler and ieee80211_recv_action_register for a recv handler
  o ieee80211_send_action and ieee80211_recv_action call any registered
    handler; otherwise return EINVAL
  o ic_send_action and ic_recv_action point to ieee80211_send_action
    and ieee80211_recv_action, respectively; drivers can still hook them
    to control operation
  o convert IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_CAT_HT,
    IEEE80211_ACTION_CAT_MESHPATH, IEEE80211_ACTION_CAT_MESHPEERING, and
    IEEE80211_ACTION_CAT_MESHLMETRIC handlers
  o change ic_recv_action api to return an error code
  o change ic_send_action api to pass a void * instead of the
    ieee80211_send_action_args union ref
  o expose getcapinfo as ieee80211_getcapinfo for mesh use
  o expose ieee80211_add_rates and ieee80211_add_xrates
  o fix various issues in mesh code related to parsing and constructing
    action frames (e.g. forcibly casting away const and then modifying the
    frame contents)
  o fixup mwl after api changes
  
  This compiles but has almost certainly broken mesh code.  ADDBA support
  tested with mwl.
  
  Discussed with:	rpaulo (who promises to cleanup after me)

Added:
  projects/mesh11s/sys/net80211/ieee80211_action.c   (contents, props changed)
  projects/mesh11s/sys/net80211/ieee80211_action.h   (contents, props changed)
Modified:
  projects/mesh11s/sys/conf/files
  projects/mesh11s/sys/dev/mwl/if_mwl.c
  projects/mesh11s/sys/dev/mwl/if_mwlvar.h
  projects/mesh11s/sys/net80211/ieee80211_ht.c
  projects/mesh11s/sys/net80211/ieee80211_hwmp.c
  projects/mesh11s/sys/net80211/ieee80211_input.c
  projects/mesh11s/sys/net80211/ieee80211_input.h
  projects/mesh11s/sys/net80211/ieee80211_mesh.c
  projects/mesh11s/sys/net80211/ieee80211_output.c
  projects/mesh11s/sys/net80211/ieee80211_proto.h
  projects/mesh11s/sys/net80211/ieee80211_var.h

Modified: projects/mesh11s/sys/conf/files
==============================================================================
--- projects/mesh11s/sys/conf/files	Thu Jul  2 20:52:23 2009	(r195297)
+++ projects/mesh11s/sys/conf/files	Thu Jul  2 22:34:42 2009	(r195298)
@@ -2230,6 +2230,7 @@ net/zlib.c			optional crypto | geom_uzip
 					 ddb_ctf
 net80211/ieee80211.c		optional wlan
 net80211/ieee80211_acl.c	optional wlan wlan_acl
+net80211/ieee80211_action.c	optional wlan
 net80211/ieee80211_adhoc.c	optional wlan
 net80211/ieee80211_ageq.c	optional wlan
 net80211/ieee80211_amrr.c	optional wlan wlan_amrr

Modified: projects/mesh11s/sys/dev/mwl/if_mwl.c
==============================================================================
--- projects/mesh11s/sys/dev/mwl/if_mwl.c	Thu Jul  2 20:52:23 2009	(r195297)
+++ projects/mesh11s/sys/dev/mwl/if_mwl.c	Thu Jul  2 22:34:42 2009	(r195298)
@@ -144,7 +144,7 @@ static void	mwl_tx_proc(void *, int);
 static int	mwl_chan_set(struct mwl_softc *, struct ieee80211_channel *);
 static void	mwl_draintxq(struct mwl_softc *);
 static void	mwl_cleartxq(struct mwl_softc *, struct ieee80211vap *);
-static void	mwl_recv_action(struct ieee80211_node *,
+static int	mwl_recv_action(struct ieee80211_node *,
 			const uint8_t *, const uint8_t *);
 static int	mwl_addba_request(struct ieee80211_node *,
 			struct ieee80211_tx_ampdu *, int dialogtoken,
@@ -3651,7 +3651,7 @@ mwl_cleartxq(struct mwl_softc *sc, struc
 	}
 }
 
-static void
+static int
 mwl_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t *efrm)
 {
 	struct mwl_softc *sc = ni->ni_ic->ic_ifp->if_softc;
@@ -3666,8 +3666,9 @@ mwl_recv_action(struct ieee80211_node *n
 		mwl_hal_setmimops(sc->sc_mh, ni->ni_macaddr,
 		    mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA,
 		    MS(mps->am_control, IEEE80211_A_HT_MIMOPWRSAVE_MODE));
+		return 0;
 	} else
-		sc->sc_recv_action(ni, frm, efrm);
+		return sc->sc_recv_action(ni, frm, efrm);
 }
 
 static int

Modified: projects/mesh11s/sys/dev/mwl/if_mwlvar.h
==============================================================================
--- projects/mesh11s/sys/dev/mwl/if_mwlvar.h	Thu Jul  2 20:52:23 2009	(r195297)
+++ projects/mesh11s/sys/dev/mwl/if_mwlvar.h	Thu Jul  2 22:34:42 2009	(r195298)
@@ -285,7 +285,7 @@ struct mwl_softc {
 				    enum ieee80211_state, int);
 	void 			(*sc_node_cleanup)(struct ieee80211_node *);
 	void 			(*sc_node_drain)(struct ieee80211_node *);
-	void			(*sc_recv_action)(struct ieee80211_node *,
+	int			(*sc_recv_action)(struct ieee80211_node *,
 				    const uint8_t *, const uint8_t *);
 	int			(*sc_addba_request)(struct ieee80211_node *,
 				    struct ieee80211_tx_ampdu *,

Added: projects/mesh11s/sys/net80211/ieee80211_action.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/mesh11s/sys/net80211/ieee80211_action.c	Thu Jul  2 22:34:42 2009	(r195298)
@@ -0,0 +1,201 @@
+/*-
+ * Copyright (c) 2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11 send/recv action frame support.
+ */
+
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h> 
+ 
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_action.h>
+
+static int
+send_inval(struct ieee80211_node *ni, int cat, int act, void *sa)
+{
+	return EINVAL;
+}
+
+static ieee80211_send_action_func *ba_send_action[8] = {
+	send_inval, send_inval, send_inval, send_inval,
+	send_inval, send_inval, send_inval, send_inval,
+};
+static ieee80211_send_action_func *ht_send_action[8] = {
+	send_inval, send_inval, send_inval, send_inval,
+	send_inval, send_inval, send_inval, send_inval,
+};
+static ieee80211_send_action_func *vendor_send_action[8] = {
+	send_inval, send_inval, send_inval, send_inval,
+	send_inval, send_inval, send_inval, send_inval,
+};
+
+int
+ieee80211_send_action_register(int cat, int act, ieee80211_send_action_func *f)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	switch (cat) {
+	case IEEE80211_ACTION_CAT_BA:
+		if (act >= N(ba_send_action))
+			break;
+		ba_send_action[act] = f;
+		return 0;
+	case IEEE80211_ACTION_CAT_HT:
+		if (act >= N(ht_send_action))
+			break;
+		ht_send_action[act] = f;
+		return 0;
+	case IEEE80211_ACTION_CAT_VENDOR:
+		if (act >= N(vendor_send_action))
+			break;
+		vendor_send_action[act] = f;
+		return 0;
+	}
+	return EINVAL;
+#undef N
+}
+
+void
+ieee80211_send_action_unregister(int cat, int act)
+{
+	ieee80211_send_action_register(cat, act, send_inval);
+}
+
+int
+ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	ieee80211_send_action_func *f = send_inval;
+
+	switch (cat) {
+	case IEEE80211_ACTION_CAT_BA:
+		if (act < N(ba_send_action))
+			f = ba_send_action[act];
+		break;
+	case IEEE80211_ACTION_CAT_HT:
+		if (act < N(ht_send_action))
+			f = ht_send_action[act];
+		break;
+	case IEEE80211_ACTION_CAT_VENDOR:
+		if (act < N(vendor_send_action))
+			f = vendor_send_action[act];
+		break;
+	}
+	return f(ni, cat, act, sa);
+#undef N
+}
+
+static int
+recv_inval(struct ieee80211_node *ni, const uint8_t *frm,
+	const uint8_t *efrm)
+{
+	return EINVAL;
+}
+
+static ieee80211_recv_action_func *ba_recv_action[8] = {
+	recv_inval, recv_inval, recv_inval, recv_inval,
+	recv_inval, recv_inval, recv_inval, recv_inval,
+};
+static ieee80211_recv_action_func *ht_recv_action[8] = {
+	recv_inval, recv_inval, recv_inval, recv_inval,
+	recv_inval, recv_inval, recv_inval, recv_inval,
+};
+static ieee80211_recv_action_func *vendor_recv_action[8] = {
+	recv_inval, recv_inval, recv_inval, recv_inval,
+	recv_inval, recv_inval, recv_inval, recv_inval,
+};
+
+int
+ieee80211_recv_action_register(int cat, int act, ieee80211_recv_action_func *f)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	switch (cat) {
+	case IEEE80211_ACTION_CAT_BA:
+		if (act >= N(ba_recv_action))
+			break;
+		ba_recv_action[act] = f;
+		return 0;
+	case IEEE80211_ACTION_CAT_HT:
+		if (act >= N(ht_recv_action))
+			break;
+		ht_recv_action[act] = f;
+		return 0;
+	case IEEE80211_ACTION_CAT_VENDOR:
+		if (act >= N(vendor_recv_action))
+			break;
+		vendor_recv_action[act] = f;
+		return 0;
+	}
+	return EINVAL;
+#undef N
+}
+
+void
+ieee80211_recv_action_unregister(int cat, int act)
+{
+	ieee80211_recv_action_register(cat, act, recv_inval);
+}
+
+int
+ieee80211_recv_action(struct ieee80211_node *ni, const uint8_t *frm,
+	const uint8_t *efrm)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	ieee80211_recv_action_func *f = recv_inval;
+	const struct ieee80211_action *ia =
+	    (const struct ieee80211_action *) frm;
+
+	switch (ia->ia_category) {
+	case IEEE80211_ACTION_CAT_BA:
+		if (ia->ia_action < N(ba_recv_action))
+			f = ba_recv_action[ia->ia_action];
+		break;
+	case IEEE80211_ACTION_CAT_HT:
+		if (ia->ia_action < N(ht_recv_action))
+			f = ht_recv_action[ia->ia_action];
+		break;
+	case IEEE80211_ACTION_CAT_VENDOR:
+		if (ia->ia_action < N(vendor_recv_action))
+			f = vendor_recv_action[ia->ia_action];
+		break;
+	}
+	return f(ni, frm, efrm);
+#undef N
+}

Added: projects/mesh11s/sys/net80211/ieee80211_action.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/mesh11s/sys/net80211/ieee80211_action.h	Thu Jul  2 22:34:42 2009	(r195298)
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_ACTION_H_
+#define _NET80211_IEEE80211_ACTION_H_
+
+/*
+ * 802.11 send/recv action frame support.
+ */
+
+struct ieee80211_node;
+
+typedef int ieee80211_send_action_func(struct ieee80211_node *,
+    int, int, void *);
+int	ieee80211_send_action_register(int cat, int act,
+		ieee80211_send_action_func *f);
+void	ieee80211_send_action_unregister(int cat, int act);
+int	ieee80211_send_action(struct ieee80211_node *, int, int, void *);
+
+typedef int ieee80211_recv_action_func(struct ieee80211_node *,
+    const uint8_t *, const uint8_t *);
+int	ieee80211_recv_action_register(int cat, int act,
+		ieee80211_recv_action_func *);
+void	ieee80211_recv_action_unregister(int cat, int act);
+int	ieee80211_recv_action(struct ieee80211_node *,
+		const uint8_t *, const uint8_t *);
+#endif /* _NET80211_IEEE80211_ACTION_H_ */

Modified: projects/mesh11s/sys/net80211/ieee80211_ht.c
==============================================================================
--- projects/mesh11s/sys/net80211/ieee80211_ht.c	Thu Jul  2 20:52:23 2009	(r195297)
+++ projects/mesh11s/sys/net80211/ieee80211_ht.c	Thu Jul  2 22:34:42 2009	(r195298)
@@ -47,8 +47,8 @@ __FBSDID("$FreeBSD$");
 #include <net/ethernet.h>
 
 #include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_action.h>
 #include <net80211/ieee80211_input.h>
-#include <net80211/ieee80211_mesh.h>
 
 /* define here, used throughout file */
 #define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
@@ -105,20 +105,52 @@ SYSCTL_INT(_net_wlan, OID_AUTO, addba_ma
 static	int ieee80211_bar_timeout = -1;	/* timeout waiting for BAR response */
 static	int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */
 
-/*
- * Setup HT parameters that depends on the clock frequency.
- */
+static	ieee80211_recv_action_func ht_recv_action_ba_addba_request;
+static	ieee80211_recv_action_func ht_recv_action_ba_addba_response;
+static	ieee80211_recv_action_func ht_recv_action_ba_delba;
+static	ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave;
+static	ieee80211_recv_action_func ht_recv_action_ht_txchwidth;
+
+static	ieee80211_send_action_func ht_send_action_ba_addba;
+static	ieee80211_send_action_func ht_send_action_ba_delba;
+static	ieee80211_send_action_func ht_send_action_ht_txchwidth;
+
 static void
-ieee80211_ht_setup(void)
+ieee80211_ht_init(void)
 {
+	/*
+	 * Setup HT parameters that depends on the clock frequency.
+	 */
 #ifdef IEEE80211_AMPDU_AGE
 	ieee80211_ampdu_age = msecs_to_ticks(500);
 #endif
 	ieee80211_addba_timeout = msecs_to_ticks(250);
 	ieee80211_addba_backoff = msecs_to_ticks(10*1000);
 	ieee80211_bar_timeout = msecs_to_ticks(250);
+	/*
+	 * Register action frame handlers.
+	 */
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, 
+	    IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_recv_action_ba_addba_request);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, 
+	    IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_recv_action_ba_addba_response);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, 
+	    IEEE80211_ACTION_BA_DELBA, ht_recv_action_ba_delba);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, 
+	    IEEE80211_ACTION_HT_MIMOPWRSAVE, ht_recv_action_ht_mimopwrsave);
+	ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, 
+	    IEEE80211_ACTION_HT_TXCHWIDTH, ht_recv_action_ht_txchwidth);
+
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, 
+	    IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_send_action_ba_addba);
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, 
+	    IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_send_action_ba_addba);
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, 
+	    IEEE80211_ACTION_BA_DELBA, ht_send_action_ba_delba);
+	ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT, 
+	    IEEE80211_ACTION_HT_TXCHWIDTH, ht_send_action_ht_txchwidth);
 }
-SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL);
+SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_init, NULL);
 
 static int ieee80211_ampdu_enable(struct ieee80211_node *ni,
 	struct ieee80211_tx_ampdu *tap);
@@ -130,8 +162,6 @@ static int ieee80211_addba_response(stru
 	int code, int baparamset, int batimeout);
 static void ieee80211_addba_stop(struct ieee80211_node *ni,
 	struct ieee80211_tx_ampdu *tap);
-static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
-	const uint8_t *frm, const uint8_t *efrm);
 static void ieee80211_bar_response(struct ieee80211_node *ni,
 	struct ieee80211_tx_ampdu *tap, int status);
 static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap);
@@ -144,7 +174,7 @@ void
 ieee80211_ht_attach(struct ieee80211com *ic)
 {
 	/* setup default aggregation policy */
-	ic->ic_recv_action = ieee80211_aggr_recv_action;
+	ic->ic_recv_action = ieee80211_recv_action;
 	ic->ic_send_action = ieee80211_send_action;
 	ic->ic_ampdu_enable = ieee80211_ampdu_enable;
 	ic->ic_addba_request = ieee80211_addba_request;
@@ -1581,171 +1611,216 @@ ieee80211_addba_stop(struct ieee80211_no
  * update our aggregation state.  All other frames are passed up
  * for processing by ieee80211_recv_action.
  */
-static void
-ieee80211_aggr_recv_action(struct ieee80211_node *ni,
+static int
+ht_recv_action_ba_addba_request(struct ieee80211_node *ni,
 	const uint8_t *frm, const uint8_t *efrm)
 {
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211vap *vap = ni->ni_vap;
-	const struct ieee80211_action *ia;
 	struct ieee80211_rx_ampdu *rap;
+	uint8_t dialogtoken;
+	uint16_t baparamset, batimeout, baseqctl;
+	uint16_t args[4];
+	int tid;
+
+	dialogtoken = frm[2];
+	baparamset = LE_READ_2(frm+3);
+	batimeout = LE_READ_2(frm+5);
+	baseqctl = LE_READ_2(frm+7);
+
+	tid = MS(baparamset, IEEE80211_BAPS_TID);
+
+	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "recv ADDBA request: dialogtoken %u baparamset 0x%x "
+	    "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d",
+	    dialogtoken, baparamset,
+	    tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ),
+	    batimeout,
+	    MS(baseqctl, IEEE80211_BASEQ_START),
+	    MS(baseqctl, IEEE80211_BASEQ_FRAG));
+
+	rap = &ni->ni_rx_ampdu[tid];
+
+	/* Send ADDBA response */
+	args[0] = dialogtoken;
+	/*
+	 * NB: We ack only if the sta associated with HT and
+	 * the ap is configured to do AMPDU rx (the latter
+	 * violates the 11n spec and is mostly for testing).
+	 */
+	if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
+	    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) {
+		/* XXX handle ampdu_rx_start failure */
+		ic->ic_ampdu_rx_start(ni, rap,
+		    baparamset, batimeout, baseqctl);
+
+		args[1] = IEEE80211_STATUS_SUCCESS;
+	} else {
+		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni, "reject ADDBA request: %s",
+		    ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
+		       "administratively disabled" :
+		       "not negotiated for station");
+		vap->iv_stats.is_addba_reject++;
+		args[1] = IEEE80211_STATUS_UNSPECIFIED;
+	}
+	/* XXX honor rap flags? */
+	args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
+		| SM(tid, IEEE80211_BAPS_TID)
+		| SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
+		;
+	args[3] = 0;
+	ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
+		IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
+	return 0;
+}
+
+static int
+ht_recv_action_ba_addba_response(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_tx_ampdu *tap;
 	uint8_t dialogtoken, policy;
-	uint16_t baparamset, batimeout, baseqctl, code;
-	union ieee80211_send_action_args sargs;
+	uint16_t baparamset, batimeout, code;
 	int tid, ac, bufsiz;
 
-	ia = (const struct ieee80211_action *) frm;
-	switch (ia->ia_category) {
-	case IEEE80211_ACTION_CAT_BA:
-		switch (ia->ia_action) {
-		case IEEE80211_ACTION_BA_ADDBA_REQUEST:
-			dialogtoken = frm[2];
-			baparamset = LE_READ_2(frm+3);
-			batimeout = LE_READ_2(frm+5);
-			baseqctl = LE_READ_2(frm+7);
+	dialogtoken = frm[2];
+	code = LE_READ_2(frm+3);
+	baparamset = LE_READ_2(frm+5);
+	tid = MS(baparamset, IEEE80211_BAPS_TID);
+	bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+	policy = MS(baparamset, IEEE80211_BAPS_POLICY);
+	batimeout = LE_READ_2(frm+7);
 
-			tid = MS(baparamset, IEEE80211_BAPS_TID);
+	ac = TID_TO_WME_AC(tid);
+	tap = &ni->ni_tx_ampdu[ac];
+	if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+		IEEE80211_DISCARD_MAC(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni->ni_macaddr, "ADDBA response",
+		    "no pending ADDBA, tid %d dialogtoken %u "
+		    "code %d", tid, dialogtoken, code);
+		vap->iv_stats.is_addba_norequest++;
+		return 0;
+	}
+	if (dialogtoken != tap->txa_token) {
+		IEEE80211_DISCARD_MAC(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni->ni_macaddr, "ADDBA response",
+		    "dialogtoken mismatch: waiting for %d, "
+		    "received %d, tid %d code %d",
+		    tap->txa_token, dialogtoken, tid, code);
+		vap->iv_stats.is_addba_badtoken++;
+		return 0;
+	}
+	/* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */
+	if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) {
+		IEEE80211_DISCARD_MAC(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni->ni_macaddr, "ADDBA response",
+		    "policy mismatch: expecting %s, "
+		    "received %s, tid %d code %d",
+		    tap->txa_flags & IEEE80211_AGGR_IMMEDIATE,
+		    policy, tid, code);
+		vap->iv_stats.is_addba_badpolicy++;
+		return 0;
+	}
+#if 0
+	/* XXX we take MIN in ieee80211_addba_response */
+	if (bufsiz > IEEE80211_AGGR_BAWMAX) {
+		IEEE80211_DISCARD_MAC(vap,
+		    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
+		    ni->ni_macaddr, "ADDBA response",
+		    "BA window too large: max %d, "
+		    "received %d, tid %d code %d",
+		    bufsiz, IEEE80211_AGGR_BAWMAX, tid, code);
+		vap->iv_stats.is_addba_badbawinsize++;
+		return 0;
+	}
+#endif
+	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "recv ADDBA response: dialogtoken %u code %d "
+	    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
+	    dialogtoken, code, baparamset, tid, bufsiz,
+	    batimeout);
+	ic->ic_addba_response(ni, tap, code, baparamset, batimeout);
+	return 0;
+}
 
-			IEEE80211_NOTE(vap,
-			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
-			    "recv ADDBA request: dialogtoken %u "
-			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
-			    "baseqctl %d:%d",
-			    dialogtoken, baparamset,
-			    tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ),
-			    batimeout,
-			    MS(baseqctl, IEEE80211_BASEQ_START),
-			    MS(baseqctl, IEEE80211_BASEQ_FRAG));
+static int
+ht_recv_action_ba_delba(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_rx_ampdu *rap;
+	struct ieee80211_tx_ampdu *tap;
+	uint16_t baparamset, code;
+	int tid, ac;
 
-			rap = &ni->ni_rx_ampdu[tid];
+	baparamset = LE_READ_2(frm+2);
+	code = LE_READ_2(frm+4);
 
-			/* Send ADDBA response */
-			sargs.arg[0] = dialogtoken;
-			/*
-			 * NB: We ack only if the sta associated with HT and
-			 * the ap is configured to do AMPDU rx (the latter
-			 * violates the 11n spec and is mostly for testing).
-			 */
-			if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
-			    (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) {
-				/* XXX handle ampdu_rx_start failure */
-				ic->ic_ampdu_rx_start(ni, rap,
-				    baparamset, batimeout, baseqctl);
+	tid = MS(baparamset, IEEE80211_DELBAPS_TID);
 
-				sargs.arg[1] = IEEE80211_STATUS_SUCCESS;
-			} else {
-				IEEE80211_NOTE(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-				    ni, "reject ADDBA request: %s",
-				    ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
-				       "administratively disabled" :
-				       "not negotiated for station");
-				vap->iv_stats.is_addba_reject++;
-				sargs.arg[1] =
-				    IEEE80211_STATUS_UNSPECIFIED;
-			}
-			/* XXX honor rap flags? */
-			sargs.arg[2] =
-				  IEEE80211_BAPS_POLICY_IMMEDIATE
-				| SM(tid, IEEE80211_BAPS_TID)
-				| SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
-				;
-			sargs.arg[3] = 0;
-			ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
-				IEEE80211_ACTION_BA_ADDBA_RESPONSE, sargs);
-			return;
-
-		case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
-			dialogtoken = frm[2];
-			code = LE_READ_2(frm+3);
-			baparamset = LE_READ_2(frm+5);
-			tid = MS(baparamset, IEEE80211_BAPS_TID);
-			bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
-			policy = MS(baparamset, IEEE80211_BAPS_POLICY);
-			batimeout = LE_READ_2(frm+7);
-
-			ac = TID_TO_WME_AC(tid);
-			tap = &ni->ni_tx_ampdu[ac];
-			if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
-				IEEE80211_DISCARD_MAC(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-				    ni->ni_macaddr, "ADDBA response",
-				    "no pending ADDBA, tid %d dialogtoken %u "
-				    "code %d", tid, dialogtoken, code);
-				vap->iv_stats.is_addba_norequest++;
-				return;
-			}
-			if (dialogtoken != tap->txa_token) {
-				IEEE80211_DISCARD_MAC(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-				    ni->ni_macaddr, "ADDBA response",
-				    "dialogtoken mismatch: waiting for %d, "
-				    "received %d, tid %d code %d",
-				    tap->txa_token, dialogtoken, tid, code);
-				vap->iv_stats.is_addba_badtoken++;
-				return;
-			}
-			/* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */
-			if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) {
-				IEEE80211_DISCARD_MAC(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-				    ni->ni_macaddr, "ADDBA response",
-				    "policy mismatch: expecting %s, "
-				    "received %s, tid %d code %d",
-				    tap->txa_flags & IEEE80211_AGGR_IMMEDIATE,
-				    policy, tid, code);
-				vap->iv_stats.is_addba_badpolicy++;
-				return;
-			}
-#if 0
-			/* XXX we take MIN in ieee80211_addba_response */
-			if (bufsiz > IEEE80211_AGGR_BAWMAX) {
-				IEEE80211_DISCARD_MAC(vap,
-				    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
-				    ni->ni_macaddr, "ADDBA response",
-				    "BA window too large: max %d, "
-				    "received %d, tid %d code %d",
-				    bufsiz, IEEE80211_AGGR_BAWMAX, tid, code);
-				vap->iv_stats.is_addba_badbawinsize++;
-				return;
-			}
-#endif
-			IEEE80211_NOTE(vap,
-			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
-			    "recv ADDBA response: dialogtoken %u code %d "
-			    "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
-			    dialogtoken, code, baparamset, tid, bufsiz,
-			    batimeout);
-			ic->ic_addba_response(ni, tap,
-				code, baparamset, batimeout);
-			return;
-
-		case IEEE80211_ACTION_BA_DELBA:
-			baparamset = LE_READ_2(frm+2);
-			code = LE_READ_2(frm+4);
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
+	    "code %d", baparamset, tid,
+	    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
 
-			tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+	if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+		ac = TID_TO_WME_AC(tid);
+		tap = &ni->ni_tx_ampdu[ac];
+		ic->ic_addba_stop(ni, tap);
+	} else {
+		rap = &ni->ni_rx_ampdu[tid];
+		ic->ic_ampdu_rx_stop(ni, rap);
+	}
+	return 0;
+}
 
-			IEEE80211_NOTE(vap,
-			    IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
-			    "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
-			    "code %d", baparamset, tid,
-			    MS(baparamset, IEEE80211_DELBAPS_INIT), code);
-
-			if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
-				ac = TID_TO_WME_AC(tid);
-				tap = &ni->ni_tx_ampdu[ac];
-				ic->ic_addba_stop(ni, tap);
-			} else {
-				rap = &ni->ni_rx_ampdu[tid];
-				ic->ic_ampdu_rx_stop(ni, rap);
-			}
-			return;
-		}
-		break;
+static int
+ht_recv_action_ht_txchwidth(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	int chw;
+
+	chw = (frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040) ? 40 : 20;
+
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "%s: HT txchwidth, width %d%s",
+	    __func__, chw, ni->ni_chw != chw ? "*" : "");
+	if (chw != ni->ni_chw) {
+		ni->ni_chw = chw;
+		/* XXX notify on change */
 	}
-	ieee80211_recv_action(ni, frm, efrm);
+	return 0;
+}
+
+static int
+ht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni,
+	const uint8_t *frm, const uint8_t *efrm)
+{
+	const struct ieee80211_action_ht_mimopowersave *mps =
+	    (const struct ieee80211_action_ht_mimopowersave *) frm;
+
+	/* XXX check iv_htcaps */
+	if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA)
+		ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
+	else
+		ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS;
+	if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE)
+		ni->ni_flags |= IEEE80211_NODE_MIMO_RTS;
+	else
+		ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
+	/* XXX notify on change */
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "%s: HT MIMO PS (%s%s)", __func__,
+	    (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ?  "on" : "off",
+	    (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ?  "+rts" : ""
+	);
+	return 0;
 }
 
 /*
@@ -1794,9 +1869,9 @@ ieee80211_ampdu_request(struct ieee80211
 	struct ieee80211_tx_ampdu *tap)
 {
 	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t args[4];
 	int tid, dialogtoken;
 	static int tokens = 0;	/* XXX */
-	union ieee80211_send_action_args sargs;
 
 	/* XXX locking */
 	if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
@@ -1810,15 +1885,14 @@ ieee80211_ampdu_request(struct ieee80211
 	tid = WME_AC_TO_TID(tap->txa_ac);
 	tap->txa_start = ni->ni_txseqs[tid];
 
-	sargs.arg[0] = dialogtoken;
-	sargs.arg[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
+	args[0] = dialogtoken;
+	args[1]	= IEEE80211_BAPS_POLICY_IMMEDIATE
 		| SM(tid, IEEE80211_BAPS_TID)
 		| SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ)
 		;
-	sargs.arg[2] = 0;	/* batimeout */
+	args[2] = 0;	/* batimeout */
 	/* NB: do first so there's no race against reply */
-	if (!ic->ic_addba_request(ni, tap, dialogtoken, sargs.arg[1],
-	    sargs.arg[2])) {
+	if (!ic->ic_addba_request(ni, tap, dialogtoken, args[1], args[2])) {
 		/* unable to setup state, don't make request */
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
 		    ni, "%s: could not setup BA stream for AC %d",
@@ -1832,11 +1906,11 @@ ieee80211_ampdu_request(struct ieee80211
 	}
 	tokens = dialogtoken;			/* allocate token */
 	/* NB: after calling ic_addba_request so driver can set txa_start */
-	sargs.arg[3] = SM(tap->txa_start, IEEE80211_BASEQ_START)
+	args[3] = SM(tap->txa_start, IEEE80211_BASEQ_START)
 		| SM(0, IEEE80211_BASEQ_FRAG)
 		;
 	return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
-		IEEE80211_ACTION_BA_ADDBA_REQUEST, sargs);
+		IEEE80211_ACTION_BA_ADDBA_REQUEST, args);
 }
 
 /*
@@ -1849,7 +1923,7 @@ ieee80211_ampdu_stop(struct ieee80211_no
 {
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211vap *vap = ni->ni_vap;
-	union ieee80211_send_action_args sargs;
+	uint16_t args[4];
 
 	/* XXX locking */
 	tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
@@ -1860,11 +1934,11 @@ ieee80211_ampdu_stop(struct ieee80211_no
 		vap->iv_stats.is_ampdu_stop++;
 
 		ic->ic_addba_stop(ni, tap);
-		sargs.arg[0] = WME_AC_TO_TID(tap->txa_ac);
-		sargs.arg[1] = IEEE80211_DELBAPS_INIT;
-		sargs.arg[2] = reason;		/* XXX reason code */
-		ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
-			IEEE80211_ACTION_BA_DELBA, sargs);
+		args[0] = WME_AC_TO_TID(tap->txa_ac);
+		args[1] = IEEE80211_DELBAPS_INIT;
+		args[2] = reason;			/* XXX reason code */
+		ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
+			IEEE80211_ACTION_BA_DELBA, args);
 	} else {
 		IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
 		    ni, "%s: BA stream for AC %d not running (reason %d)",
@@ -2041,6 +2115,161 @@ bad:
 #undef senderr
 }
 
+static int
+ht_action_output(struct ieee80211_node *ni, struct mbuf *m)
+{
+	struct ieee80211_bpf_params params;
+
+	memset(&params, 0, sizeof(params));
+	params.ibp_pri = WME_AC_VO;
+	params.ibp_rate0 = ni->ni_txparms->mgmtrate;
+	/* NB: we know all frames are unicast */
+	params.ibp_try0 = ni->ni_txparms->maxretry;
+	params.ibp_power = ni->ni_txpower;
+	return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
+	     &params);
+}
+
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+
+/*
+ * Send an action management frame.  The arguments are stuff
+ * into a frame without inspection; the caller is assumed to
+ * prepare them carefully (e.g. based on the aggregation state).
+ */
+static int
+ht_send_action_ba_addba(struct ieee80211_node *ni,
+	int category, int action, void *arg0)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t *args = arg0;
+	struct mbuf *m;
+	uint8_t *frm;
+
+	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "send ADDBA %s: dialogtoken %d "
+	    "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
+	    (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ?
+		"request" : "response",
+	    args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
+	    args[2], args[3]);
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
+	m = ieee80211_getmgtframe(&frm,
+	    ic->ic_headroom + sizeof(struct ieee80211_frame),
+	    sizeof(uint16_t)	/* action+category */
+	    /* XXX may action payload */
+	    + sizeof(struct ieee80211_action_ba_addbaresponse)
+	);
+	if (m != NULL) {
+		*frm++ = category;
+		*frm++ = action;
+		*frm++ = args[0];		/* dialog token */
+		ADDSHORT(frm, args[1]);		/* baparamset */
+		ADDSHORT(frm, args[2]);		/* batimeout */
+		if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST)
+			ADDSHORT(frm, args[3]);	/* baseqctl */
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		return ht_action_output(ni, m);
+	} else {
+		vap->iv_stats.is_tx_nobuf++;
+		ieee80211_free_node(ni);
+		return ENOMEM;
+	}
+}
+
+static int
+ht_send_action_ba_delba(struct ieee80211_node *ni,
+	int category, int action, void *arg0)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = ni->ni_ic;
+	uint16_t *args = arg0;
+	struct mbuf *m;
+	uint16_t baparamset;
+	uint8_t *frm;
+
+	baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
+		   | args[1]
+		   ;
+	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "send DELBA action: tid %d, initiator %d reason %d",
+	    args[0], args[1], args[2]);
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
+	m = ieee80211_getmgtframe(&frm,
+	    ic->ic_headroom + sizeof(struct ieee80211_frame),
+	    sizeof(uint16_t)	/* action+category */
+	    /* XXX may action payload */
+	    + sizeof(struct ieee80211_action_ba_addbaresponse)
+	);
+	if (m != NULL) {
+		*frm++ = category;
+		*frm++ = action;
+		ADDSHORT(frm, baparamset);
+		ADDSHORT(frm, args[2]);		/* reason code */
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		return ht_action_output(ni, m);
+	} else {
+		vap->iv_stats.is_tx_nobuf++;
+		ieee80211_free_node(ni);
+		return ENOMEM;
+	}
+}
+
+static int
+ht_send_action_ht_txchwidth(struct ieee80211_node *ni,
+	int category, int action, void *arg0)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+	uint8_t *frm;
+
+	IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
+	    "send HT txchwidth: width %d",
+	    IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20);
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
+	    "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__,
+	    ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
+	m = ieee80211_getmgtframe(&frm,
+	    ic->ic_headroom + sizeof(struct ieee80211_frame),
+	    sizeof(uint16_t)	/* action+category */
+	    /* XXX may action payload */
+	    + sizeof(struct ieee80211_action_ba_addbaresponse)
+	);
+	if (m != NULL) {
+		*frm++ = category;
+		*frm++ = action;
+		*frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 
+			IEEE80211_A_HT_TXCHWIDTH_2040 :
+			IEEE80211_A_HT_TXCHWIDTH_20;
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		return ht_action_output(ni, m);
+	} else {
+		vap->iv_stats.is_tx_nobuf++;
+		ieee80211_free_node(ni);
+		return ENOMEM;
+	}
+}
+#undef ADDSHORT
+
 /*
  * Construct the MCS bit mask for inclusion
  * in an HT information element.

Modified: projects/mesh11s/sys/net80211/ieee80211_hwmp.c
==============================================================================
--- projects/mesh11s/sys/net80211/ieee80211_hwmp.c	Thu Jul  2 20:52:23 2009	(r195297)
+++ projects/mesh11s/sys/net80211/ieee80211_hwmp.c	Thu Jul  2 22:34:42 2009	(r195298)
@@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$");
 #include <net/bpf.h>
 

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200907022234.n62MYhiM058710>