diff --git a/sys/conf/files b/sys/conf/files index 3108570a569f..fa3f29c23a43 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2231,6 +2231,7 @@ net/zlib.c optional crypto | geom_uzip | ipsec | \ 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 diff --git a/sys/dev/mwl/if_mwl.c b/sys/dev/mwl/if_mwl.c index ca3c96c16cdb..dbbdca728352 100644 --- a/sys/dev/mwl/if_mwl.c +++ b/sys/dev/mwl/if_mwl.c @@ -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, struct ieee80211vap *vap) } } -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 *ni, const uint8_t *frm, const uint8_t *ef 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 diff --git a/sys/dev/mwl/if_mwlvar.h b/sys/dev/mwl/if_mwlvar.h index 182321ac1dd7..d69285d17de1 100644 --- a/sys/dev/mwl/if_mwlvar.h +++ b/sys/dev/mwl/if_mwlvar.h @@ -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 *, diff --git a/sys/net80211/ieee80211_action.c b/sys/net80211/ieee80211_action.c new file mode 100644 index 000000000000..a694d257df25 --- /dev/null +++ b/sys/net80211/ieee80211_action.c @@ -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 +#ifdef __FreeBSD__ +__FBSDID("$FreeBSD$"); +#endif + +/* + * IEEE 802.11 send/recv action frame support. + */ + +#include "opt_inet.h" +#include "opt_wlan.h" + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +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 +} diff --git a/sys/net80211/ieee80211_action.h b/sys/net80211/ieee80211_action.h new file mode 100644 index 000000000000..4ee2aff4b1ec --- /dev/null +++ b/sys/net80211/ieee80211_action.h @@ -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_ */ diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c index ed3f6f8fa4c3..805589016d50 100644 --- a/sys/net80211/ieee80211_adhoc.c +++ b/sys/net80211/ieee80211_adhoc.c @@ -879,7 +879,7 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } break; } - ic->ic_recv_action(ni, frm, efrm); + ic->ic_recv_action(ni, wh, frm, efrm); break; } diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 3c5bb783c914..240ea7cb30f7 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -2189,7 +2189,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 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; diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 182d052bc7d4..2ce1b92abb46 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include /* define here, used throughout file */ @@ -104,20 +105,52 @@ SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLTYPE_INT | CTLFLAG_RW, 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(struct ieee80211_node *ni, 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_node *ni, struct ieee80211_tx_ampdu *tap) * 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); + 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)); + 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]; + 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); + /* 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; - - 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); - - tid = MS(baparamset, IEEE80211_DELBAPS_TID); - - 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; + 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; } - ieee80211_recv_action(ni, frm, efrm); + /* 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; } -/* - * 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_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; - const struct ieee80211_action *ia; + struct ieee80211_tx_ampdu *tap; + uint8_t dialogtoken, policy; + uint16_t baparamset, batimeout, code; + int tid, ac, bufsiz; + + 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 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; +} + +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; + + 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); + } + return 0; +} + +static int +ht_recv_action_ht_txchwidth(struct ieee80211_node *ni, + const struct ieee80211_frame *wh, + const uint8_t *frm, const uint8_t *efrm) +{ 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_node *ni, struct ieee80211_tx_ampdu *tap, 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,126 +2120,10 @@ ieee80211_send_bar(struct ieee80211_node *ni, #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) -#define ADDSHORT(frm, v) do { \ - frm[0] = (v) & 0xff; \ - frm[1] = (v) >> 8; \ - frm += 2; \ -} while (0) - struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ni->ni_ic; struct ieee80211_bpf_params params; - struct mbuf *m; - uint8_t *frm; - uint16_t baparamset; - int ret; - - KASSERT(ni != NULL, ("null node")); - - /* - * Hold a reference on the node so it doesn't go away until after - * the xmit is complete all the way in the driver. On error we - * will remove our reference. - */ - 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) - senderr(ENOMEM, is_tx_nobuf); - - *frm++ = category; - *frm++ = action; - switch (category) { - case IEEE80211_ACTION_CAT_BA: - switch (action) { - case IEEE80211_ACTION_BA_ADDBA_REQUEST: - IEEE80211_NOTE(vap, - IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, - "send ADDBA request: dialogtoken %d " - "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x", - args[0], args[1], MS(args[1], IEEE80211_BAPS_TID), - args[2], args[3]); - - *frm++ = args[0]; /* dialog token */ - ADDSHORT(frm, args[1]); /* baparamset */ - ADDSHORT(frm, args[2]); /* batimeout */ - ADDSHORT(frm, args[3]); /* baseqctl */ - break; - case IEEE80211_ACTION_BA_ADDBA_RESPONSE: - IEEE80211_NOTE(vap, - IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, - "send ADDBA response: dialogtoken %d status %d " - "baparamset 0x%x (tid %d) batimeout %d", - args[0], args[1], args[2], - MS(args[2], IEEE80211_BAPS_TID), args[3]); - - *frm++ = args[0]; /* dialog token */ - ADDSHORT(frm, args[1]); /* statuscode */ - ADDSHORT(frm, args[2]); /* baparamset */ - ADDSHORT(frm, args[3]); /* batimeout */ - break; - case IEEE80211_ACTION_BA_DELBA: - /* XXX */ - baparamset = SM(args[0], IEEE80211_DELBAPS_TID) - | args[1] - ; - ADDSHORT(frm, baparamset); - ADDSHORT(frm, args[2]); /* reason code */ - - 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]); - break; - default: - goto badaction; - } - break; - case IEEE80211_ACTION_CAT_HT: - switch (action) { - case IEEE80211_ACTION_HT_TXCHWIDTH: - IEEE80211_NOTE(vap, - IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, - ni, "send HT txchwidth: width %d", - IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20 - ); - *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? - IEEE80211_A_HT_TXCHWIDTH_2040 : - IEEE80211_A_HT_TXCHWIDTH_20; - break; - default: - goto badaction; - } - break; - default: - badaction: - IEEE80211_NOTE(vap, - IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, - "%s: unsupported category %d action %d", __func__, - category, action); - senderr(EINVAL, is_tx_unknownmgt); - /* NOTREACHED */ - } - m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); memset(¶ms, 0, sizeof(params)); params.ibp_pri = WME_AC_VO; @@ -2244,15 +2133,148 @@ ieee80211_send_action(struct ieee80211_node *ni, params.ibp_power = ni->ni_txpower; return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, ¶ms); -bad: - ieee80211_free_node(ni); - if (m != NULL) - m_freem(m); - return ret; -#undef ADDSHORT -#undef senderr } +#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. diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h index f1ed36bc21f9..7b0eab7ace18 100644 --- a/sys/net80211/ieee80211_ht.h +++ b/sys/net80211/ieee80211_ht.h @@ -186,16 +186,12 @@ void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *); void ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *, const uint8_t *); void ieee80211_ht_updatehtcap(struct ieee80211_node *, const uint8_t *); -void ieee80211_recv_action(struct ieee80211_node *, - const uint8_t *, const uint8_t *); int ieee80211_ampdu_request(struct ieee80211_node *, struct ieee80211_tx_ampdu *); void ieee80211_ampdu_stop(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int); int ieee80211_send_bar(struct ieee80211_node *, struct ieee80211_tx_ampdu *, ieee80211_seq); -int ieee80211_send_action(struct ieee80211_node *, - int, int, uint16_t [4]); uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *); uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *); uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *); diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 3b0c13bdbb40..fe8c9fa10eb0 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -1717,7 +1717,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, 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; diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index d0cfe9fa0e70..8859c2fe0795 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -112,6 +112,7 @@ struct ieee80211_rate_table; struct ieee80211_tx_ampdu; struct ieee80211_rx_ampdu; struct ieee80211_superg; +struct ieee80211_frame; struct ieee80211com { struct ifnet *ic_ifp; /* associated device */ @@ -282,11 +283,11 @@ struct ieee80211com { * driver passes out-of-order frames to ieee80211_input * from an assocated HT station. */ - void (*ic_recv_action)(struct ieee80211_node *, + int (*ic_recv_action)(struct ieee80211_node *, + const struct ieee80211_frame *, const uint8_t *frm, const uint8_t *efrm); int (*ic_send_action)(struct ieee80211_node *, - int category, int action, - uint16_t args[4]); + int category, int action, void *); /* check if A-MPDU should be enabled this station+ac */ int (*ic_ampdu_enable)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c index 78529ed72ca5..18c4544e0889 100644 --- a/sys/net80211/ieee80211_wds.c +++ b/sys/net80211/ieee80211_wds.c @@ -838,7 +838,7 @@ wds_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } ni->ni_inact = ni->ni_inact_reload; if (ieee80211_parse_action(ni, m0) == 0) - ic->ic_recv_action(ni, frm, efrm); + ic->ic_recv_action(ni, wh, frm, efrm); break; default: IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,