Author: sam
Date: Sun Jul  5 17:59:19 2009
New Revision: 195377
URL: http://svn.freebsd.org/changeset/base/195377

Log:
  Revamp 802.11 action frame handling:
  o add a new facility for components to register send+recv handlers
  o ieee80211_send_action and ieee80211_recv_action now use the registered
    handlers to dispatch operations
  o rev ieee80211_send_action api to enable passing arbitrary data
  o rev ieee80211_recv_action api to pass the 802.11 frame header as it may
    be difficult to locate
  o update existing IEEE80211_ACTION_CAT_BA and IEEE80211_ACTION_CAT_HT handling
  o update mwl for api rev
  
  Reviewed by:  rpaulo
  Approved by:  re (kensmith)

Added:
  head/sys/net80211/ieee80211_action.c   (contents, props changed)
  head/sys/net80211/ieee80211_action.h   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/mwl/if_mwl.c
  head/sys/dev/mwl/if_mwlvar.h
  head/sys/net80211/ieee80211_adhoc.c
  head/sys/net80211/ieee80211_hostap.c
  head/sys/net80211/ieee80211_ht.c
  head/sys/net80211/ieee80211_ht.h
  head/sys/net80211/ieee80211_sta.c
  head/sys/net80211/ieee80211_var.h
  head/sys/net80211/ieee80211_wds.c

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Sun Jul  5 17:45:48 2009        (r195376)
+++ head/sys/conf/files Sun Jul  5 17:59:19 2009        (r195377)
@@ -2231,6 +2231,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_amrr.c      optional wlan wlan_amrr
 net80211/ieee80211_crypto.c    optional wlan

Modified: head/sys/dev/mwl/if_mwl.c
==============================================================================
--- head/sys/dev/mwl/if_mwl.c   Sun Jul  5 17:45:48 2009        (r195376)
+++ head/sys/dev/mwl/if_mwl.c   Sun Jul  5 17:59:19 2009        (r195377)
@@ -144,7 +144,8 @@ 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 struct ieee80211_frame *,
                        const uint8_t *, const uint8_t *);
 static int     mwl_addba_request(struct ieee80211_node *,
                        struct ieee80211_tx_ampdu *, int dialogtoken,
@@ -3656,8 +3657,9 @@ mwl_cleartxq(struct mwl_softc *sc, struc
        }
 }
 
-static void
-mwl_recv_action(struct ieee80211_node *ni, const uint8_t *frm, const uint8_t 
*efrm)
+static int
+mwl_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+       const uint8_t *frm, const uint8_t *efrm)
 {
        struct mwl_softc *sc = ni->ni_ic->ic_ifp->if_softc;
        const struct ieee80211_action *ia;
@@ -3671,8 +3673,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, wh, frm, efrm);
 }
 
 static int

Modified: head/sys/dev/mwl/if_mwlvar.h
==============================================================================
--- head/sys/dev/mwl/if_mwlvar.h        Sun Jul  5 17:45:48 2009        
(r195376)
+++ head/sys/dev/mwl/if_mwlvar.h        Sun Jul  5 17:59:19 2009        
(r195377)
@@ -289,7 +289,8 @@ 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 struct ieee80211_frame *,
                                    const uint8_t *, const uint8_t *);
        int                     (*sc_addba_request)(struct ieee80211_node *,
                                    struct ieee80211_tx_ampdu *,

Added: head/sys/net80211/ieee80211_action.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/net80211/ieee80211_action.c        Sun Jul  5 17:59:19 2009        
(r195377)
@@ -0,0 +1,291 @@
+/*-
+ * 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,
+};
+/* NB: temporary until 802.11s support is added */
+#ifdef IEEE80211_ACTION_CAT_MESHPEERING
+static ieee80211_send_action_func *meshpl_send_action[8] = {
+       send_inval, send_inval, send_inval, send_inval,
+       send_inval, send_inval, send_inval, send_inval,
+};
+static ieee80211_send_action_func *meshlm_send_action[4] = {
+       send_inval, send_inval, send_inval, send_inval,
+};
+static ieee80211_send_action_func *hwmp_send_action[8] = {
+       send_inval, send_inval, send_inval, send_inval,
+       send_inval, send_inval, send_inval, send_inval,
+};
+#endif
+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;
+#ifdef IEEE80211_ACTION_CAT_MESHPEERING
+       case IEEE80211_ACTION_CAT_MESHPEERING:
+               if (act >= N(meshpl_send_action))
+                       break;
+               meshpl_send_action[act] = f;
+               return 0;
+       case IEEE80211_ACTION_CAT_MESHLMETRIC:
+               if (act >= N(meshlm_send_action))
+                       break;
+               meshlm_send_action[act] = f;
+               return 0;
+       case IEEE80211_ACTION_CAT_MESHPATH:
+               if (act > N(hwmp_send_action))
+                       break;
+               hwmp_send_action[act] = f;
+               return 0;
+#endif
+       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;
+#ifdef IEEE80211_ACTION_CAT_MESHPEERING
+       case IEEE80211_ACTION_CAT_MESHPEERING:
+               if (act < N(meshpl_send_action))
+                       f = meshpl_send_action[act];
+               break;
+       case IEEE80211_ACTION_CAT_MESHLMETRIC:
+               if (act < N(meshlm_send_action))
+                       f = meshlm_send_action[act];
+               break;
+       case IEEE80211_ACTION_CAT_MESHPATH:
+               if (act < N(hwmp_send_action))
+                       f = hwmp_send_action[act];
+               break;
+#endif
+       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 struct ieee80211_frame *wh,
+       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,
+};
+#ifdef IEEE80211_ACTION_CAT_MESHPEERING
+static ieee80211_recv_action_func *meshpl_recv_action[8] = {
+       recv_inval, recv_inval, recv_inval, recv_inval,
+       recv_inval, recv_inval, recv_inval, recv_inval,
+};
+static ieee80211_recv_action_func *meshlm_recv_action[4] = {
+       recv_inval, recv_inval, recv_inval, recv_inval,
+};
+static ieee80211_recv_action_func *hwmp_recv_action[8] = {
+       recv_inval, recv_inval, recv_inval, recv_inval,
+       recv_inval, recv_inval, recv_inval, recv_inval,
+};
+#endif
+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;
+#ifdef IEEE80211_ACTION_CAT_MESHPEERING
+       case IEEE80211_ACTION_CAT_MESHPEERING:
+               if (act >= N(meshpl_recv_action))
+                       break;
+               meshpl_recv_action[act] = f;
+               return 0;
+       case IEEE80211_ACTION_CAT_MESHLMETRIC:
+               if (act >= N(meshlm_recv_action))
+                       break;
+               meshlm_recv_action[act] = f;
+               return 0;
+       case IEEE80211_ACTION_CAT_MESHPATH:
+               if (act >= N(hwmp_recv_action))
+                       break;
+               hwmp_recv_action[act] = f;
+               return 0;
+#endif
+       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 struct ieee80211_frame *wh,
+       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;
+#ifdef IEEE80211_ACTION_CAT_MESHPEERING
+       case IEEE80211_ACTION_CAT_MESHPEERING:
+               if (ia->ia_action < N(meshpl_recv_action))
+                       f = meshpl_recv_action[ia->ia_action];
+               break;
+       case IEEE80211_ACTION_CAT_MESHLMETRIC:
+               if (ia->ia_action < N(meshlm_recv_action))
+                       f = meshlm_recv_action[ia->ia_action];
+               break;
+       case IEEE80211_ACTION_CAT_MESHPATH:
+               if (ia->ia_action < N(hwmp_recv_action))
+                       f = hwmp_recv_action[ia->ia_action];
+               break;
+#endif
+       case IEEE80211_ACTION_CAT_VENDOR:
+               if (ia->ia_action < N(vendor_recv_action))
+                       f = vendor_recv_action[ia->ia_action];
+               break;
+       }
+       return f(ni, wh, frm, efrm);
+#undef N
+}

Added: head/sys/net80211/ieee80211_action.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/net80211/ieee80211_action.h        Sun Jul  5 17:59:19 2009        
(r195377)
@@ -0,0 +1,52 @@
+/*-
+ * 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;
+struct ieee80211_frame;
+
+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 struct ieee80211_frame *, 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 struct ieee80211_frame *,
+               const uint8_t *, const uint8_t *);
+#endif /* _NET80211_IEEE80211_ACTION_H_ */

Modified: head/sys/net80211/ieee80211_adhoc.c
==============================================================================
--- head/sys/net80211/ieee80211_adhoc.c Sun Jul  5 17:45:48 2009        
(r195376)
+++ head/sys/net80211/ieee80211_adhoc.c Sun Jul  5 17:59:19 2009        
(r195377)
@@ -879,7 +879,7 @@ adhoc_recv_mgmt(struct ieee80211_node *n
                        }
                        break;
                }
-               ic->ic_recv_action(ni, frm, efrm);
+               ic->ic_recv_action(ni, wh, frm, efrm);
                break;
        }
 

Modified: head/sys/net80211/ieee80211_hostap.c
==============================================================================
--- head/sys/net80211/ieee80211_hostap.c        Sun Jul  5 17:45:48 2009        
(r195376)
+++ head/sys/net80211/ieee80211_hostap.c        Sun Jul  5 17:59:19 2009        
(r195377)
@@ -2189,7 +2189,7 @@ hostap_recv_mgmt(struct ieee80211_node *
        case IEEE80211_FC0_SUBTYPE_ACTION:
                if (vap->iv_state == IEEE80211_S_RUN) {
                        if (ieee80211_parse_action(ni, m0) == 0)
-                               ic->ic_recv_action(ni, frm, efrm);
+                               ic->ic_recv_action(ni, wh, frm, efrm);
                } else
                        vap->iv_stats.is_rx_mgtdiscard++;
                break;

Modified: head/sys/net80211/ieee80211_ht.c
==============================================================================
--- head/sys/net80211/ieee80211_ht.c    Sun Jul  5 17:45:48 2009        
(r195376)
+++ head/sys/net80211/ieee80211_ht.c    Sun Jul  5 17:59:19 2009        
(r195377)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 #include <net/ethernet.h>
 
 #include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_action.h>
 #include <net80211/ieee80211_input.h>
 
 /* define here, used throughout file */
@@ -104,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);
@@ -129,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);
@@ -143,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;
@@ -1580,247 +1611,221 @@ 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 struct ieee80211_frame *wh,
        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;
-       struct ieee80211_tx_ampdu *tap;
-       uint8_t dialogtoken, policy;
-       uint16_t baparamset, batimeout, baseqctl, code;
+       uint8_t dialogtoken;
+       uint16_t baparamset, batimeout, baseqctl;
        uint16_t args[4];
-       int tid, ac, bufsiz;
+       int tid;
 
-       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];
+       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));
 
-                       tid = MS(baparamset, IEEE80211_BAPS_TID);
+       rap = &ni->ni_rx_ampdu[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));
+       /* 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);
 
-                       rap = &ni->ni_rx_ampdu[tid];
+               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;
+}
 
-                       /* 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);
+static int
+ht_recv_action_ba_addba_response(struct ieee80211_node *ni,
+       const struct ieee80211_frame *wh,
+       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, code;
+       int tid, ac, bufsiz;
 
-                               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;
-
-               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;
-                       }
+       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 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;
-                       }
+       /* 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;
-
-               case IEEE80211_ACTION_BA_DELBA:
-                       baparamset = LE_READ_2(frm+2);
-                       code = LE_READ_2(frm+4);
+       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;
+}
 
-                       tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+static int
+ht_recv_action_ba_delba(struct ieee80211_node *ni,
+       const struct ieee80211_frame *wh,
+       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;
 
-                       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;
+       baparamset = LE_READ_2(frm+2);
+       code = LE_READ_2(frm+4);
+
+       tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+       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);
+
+       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);
        }
-       ieee80211_recv_action(ni, frm, efrm);
+       return 0;
 }
 
-/*
- * Process a received 802.11n action frame.
- * Aggregation-related frames are assumed to be handled
- * already; we handle any other frames we can, otherwise
- * complain about being unsupported (with debugging).
- */
-void
-ieee80211_recv_action(struct ieee80211_node *ni,
+static int
+ht_recv_action_ht_txchwidth(struct ieee80211_node *ni,
+       const struct ieee80211_frame *wh,
        const uint8_t *frm, const uint8_t *efrm)
 {
-       struct ieee80211vap *vap = ni->ni_vap;
-       const struct ieee80211_action *ia;
        int chw;
 
-       ia = (const struct ieee80211_action *) frm;
-       switch (ia->ia_category) {
-       case IEEE80211_ACTION_CAT_BA:
-               IEEE80211_NOTE(vap,
-                   IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
-                   "%s: BA action %d not implemented", __func__,
-                   ia->ia_action);
-               vap->iv_stats.is_rx_mgtdiscard++;
-               break;
-       case IEEE80211_ACTION_CAT_HT:
-               switch (ia->ia_action) {
-               case IEEE80211_ACTION_HT_TXCHWIDTH:
-                       chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
-                       IEEE80211_NOTE(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 */
-                       }
-                       break;
-               case IEEE80211_ACTION_HT_MIMOPWRSAVE: {
-                       const struct ieee80211_action_ht_mimopowersave *mps =
-                           (const struct ieee80211_action_ht_mimopowersave *) 
ia;
-                       /* 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(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" : ""
-                       );
-                       break;
-               }
-               default:
-                       IEEE80211_NOTE(vap,
-                          IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
-                          "%s: HT action %d not implemented", __func__,
-                          ia->ia_action);
-                       vap->iv_stats.is_rx_mgtdiscard++;
-                       break;
-               }
-               break;
-       default:
-               IEEE80211_NOTE(vap,
-                   IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
-                   "%s: category %d not implemented", __func__,
-                   ia->ia_category);
-               vap->iv_stats.is_rx_mgtdiscard++;
-               break;
+       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 */
        }
+       return 0;
+}
+
+static int
+ht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni,
+       const struct ieee80211_frame *wh,
+       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;
 }
 
 /*
@@ -1937,7 +1942,7 @@ ieee80211_ampdu_stop(struct ieee80211_no
                args[0] = WME_AC_TO_TID(tap->txa_ac);
                args[1] = IEEE80211_DELBAPS_INIT;
                args[2] = reason;                       /* XXX reason code */
-               ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
+               ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
                        IEEE80211_ACTION_BA_DELBA, args);
        } else {
                IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
@@ -2115,143 +2120,160 @@ bad:
 #undef senderr
 }
 
-/*
- * 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).
- */
-int
-ieee80211_send_action(struct ieee80211_node *ni,
-       int category, int action, uint16_t args[4])
+static int
+ht_action_output(struct ieee80211_node *ni, struct mbuf *m)
 {
-#define        senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } 
while (0)
+       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).

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to