Deleted Added
sdiff udiff text old ( 193840 ) new ( 195377 )
full compact
1/*-
2 * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 11 unchanged lines hidden (view full) ---

20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27#ifdef __FreeBSD__
28__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_ht.c 193840 2009-06-09 16:32:07Z sam $");
29#endif
30
31/*
32 * IEEE 802.11n protocol support.
33 */
34
35#include "opt_inet.h"
36#include "opt_wlan.h"

--- 5 unchanged lines hidden (view full) ---

42
43#include <sys/socket.h>
44
45#include <net/if.h>
46#include <net/if_media.h>
47#include <net/ethernet.h>
48
49#include <net80211/ieee80211_var.h>
50#include <net80211/ieee80211_input.h>
51
52/* define here, used throughout file */
53#define MS(_v, _f) (((_v) & _f) >> _f##_S)
54#define SM(_v, _f) (((_v) << _f##_S) & _f)
55
56const struct ieee80211_mcs_rates ieee80211_htrates[16] = {
57 { 13, 14, 27, 30 }, /* MCS 0 */

--- 41 unchanged lines hidden (view full) ---

99 "ADDBA request backoff (ms)");
100static int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */
101SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLTYPE_INT | CTLFLAG_RW,
102 &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff");
103
104static int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */
105static int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */
106
107/*
108 * Setup HT parameters that depends on the clock frequency.
109 */
110static void
111ieee80211_ht_setup(void)
112{
113#ifdef IEEE80211_AMPDU_AGE
114 ieee80211_ampdu_age = msecs_to_ticks(500);
115#endif
116 ieee80211_addba_timeout = msecs_to_ticks(250);
117 ieee80211_addba_backoff = msecs_to_ticks(10*1000);
118 ieee80211_bar_timeout = msecs_to_ticks(250);
119}
120SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL);
121
122static int ieee80211_ampdu_enable(struct ieee80211_node *ni,
123 struct ieee80211_tx_ampdu *tap);
124static int ieee80211_addba_request(struct ieee80211_node *ni,
125 struct ieee80211_tx_ampdu *tap,
126 int dialogtoken, int baparamset, int batimeout);
127static int ieee80211_addba_response(struct ieee80211_node *ni,
128 struct ieee80211_tx_ampdu *tap,
129 int code, int baparamset, int batimeout);
130static void ieee80211_addba_stop(struct ieee80211_node *ni,
131 struct ieee80211_tx_ampdu *tap);
132static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
133 const uint8_t *frm, const uint8_t *efrm);
134static void ieee80211_bar_response(struct ieee80211_node *ni,
135 struct ieee80211_tx_ampdu *tap, int status);
136static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap);
137static void bar_stop_timer(struct ieee80211_tx_ampdu *tap);
138static int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *,
139 int baparamset, int batimeout, int baseqctl);
140static void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *);
141
142void
143ieee80211_ht_attach(struct ieee80211com *ic)
144{
145 /* setup default aggregation policy */
146 ic->ic_recv_action = ieee80211_aggr_recv_action;
147 ic->ic_send_action = ieee80211_send_action;
148 ic->ic_ampdu_enable = ieee80211_ampdu_enable;
149 ic->ic_addba_request = ieee80211_addba_request;
150 ic->ic_addba_response = ieee80211_addba_response;
151 ic->ic_addba_stop = ieee80211_addba_stop;
152 ic->ic_bar_response = ieee80211_bar_response;
153 ic->ic_ampdu_rx_start = ampdu_rx_start;
154 ic->ic_ampdu_rx_stop = ampdu_rx_stop;

--- 1420 unchanged lines hidden (view full) ---

1575}
1576
1577/*
1578 * Process a received action frame using the default aggregation
1579 * policy. We intercept ADDBA-related frames and use them to
1580 * update our aggregation state. All other frames are passed up
1581 * for processing by ieee80211_recv_action.
1582 */
1583static void
1584ieee80211_aggr_recv_action(struct ieee80211_node *ni,
1585 const uint8_t *frm, const uint8_t *efrm)
1586{
1587 struct ieee80211com *ic = ni->ni_ic;
1588 struct ieee80211vap *vap = ni->ni_vap;
1589 const struct ieee80211_action *ia;
1590 struct ieee80211_rx_ampdu *rap;
1591 struct ieee80211_tx_ampdu *tap;
1592 uint8_t dialogtoken, policy;
1593 uint16_t baparamset, batimeout, baseqctl, code;
1594 uint16_t args[4];
1595 int tid, ac, bufsiz;
1596
1597 ia = (const struct ieee80211_action *) frm;
1598 switch (ia->ia_category) {
1599 case IEEE80211_ACTION_CAT_BA:
1600 switch (ia->ia_action) {
1601 case IEEE80211_ACTION_BA_ADDBA_REQUEST:
1602 dialogtoken = frm[2];
1603 baparamset = LE_READ_2(frm+3);
1604 batimeout = LE_READ_2(frm+5);
1605 baseqctl = LE_READ_2(frm+7);
1606
1607 tid = MS(baparamset, IEEE80211_BAPS_TID);
1608
1609 IEEE80211_NOTE(vap,
1610 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1611 "recv ADDBA request: dialogtoken %u "
1612 "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
1613 "baseqctl %d:%d",
1614 dialogtoken, baparamset,
1615 tid, MS(baparamset, IEEE80211_BAPS_BUFSIZ),
1616 batimeout,
1617 MS(baseqctl, IEEE80211_BASEQ_START),
1618 MS(baseqctl, IEEE80211_BASEQ_FRAG));
1619
1620 rap = &ni->ni_rx_ampdu[tid];
1621
1622 /* Send ADDBA response */
1623 args[0] = dialogtoken;
1624 /*
1625 * NB: We ack only if the sta associated with HT and
1626 * the ap is configured to do AMPDU rx (the latter
1627 * violates the 11n spec and is mostly for testing).
1628 */
1629 if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) &&
1630 (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) {
1631 /* XXX handle ampdu_rx_start failure */
1632 ic->ic_ampdu_rx_start(ni, rap,
1633 baparamset, batimeout, baseqctl);
1634
1635 args[1] = IEEE80211_STATUS_SUCCESS;
1636 } else {
1637 IEEE80211_NOTE(vap,
1638 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1639 ni, "reject ADDBA request: %s",
1640 ni->ni_flags & IEEE80211_NODE_AMPDU_RX ?
1641 "administratively disabled" :
1642 "not negotiated for station");
1643 vap->iv_stats.is_addba_reject++;
1644 args[1] = IEEE80211_STATUS_UNSPECIFIED;
1645 }
1646 /* XXX honor rap flags? */
1647 args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
1648 | SM(tid, IEEE80211_BAPS_TID)
1649 | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ)
1650 ;
1651 args[3] = 0;
1652 ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA,
1653 IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
1654 return;
1655
1656 case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
1657 dialogtoken = frm[2];
1658 code = LE_READ_2(frm+3);
1659 baparamset = LE_READ_2(frm+5);
1660 tid = MS(baparamset, IEEE80211_BAPS_TID);
1661 bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
1662 policy = MS(baparamset, IEEE80211_BAPS_POLICY);
1663 batimeout = LE_READ_2(frm+7);
1664
1665 ac = TID_TO_WME_AC(tid);
1666 tap = &ni->ni_tx_ampdu[ac];
1667 if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
1668 IEEE80211_DISCARD_MAC(vap,
1669 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1670 ni->ni_macaddr, "ADDBA response",
1671 "no pending ADDBA, tid %d dialogtoken %u "
1672 "code %d", tid, dialogtoken, code);
1673 vap->iv_stats.is_addba_norequest++;
1674 return;
1675 }
1676 if (dialogtoken != tap->txa_token) {
1677 IEEE80211_DISCARD_MAC(vap,
1678 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1679 ni->ni_macaddr, "ADDBA response",
1680 "dialogtoken mismatch: waiting for %d, "
1681 "received %d, tid %d code %d",
1682 tap->txa_token, dialogtoken, tid, code);
1683 vap->iv_stats.is_addba_badtoken++;
1684 return;
1685 }
1686 /* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */
1687 if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) {
1688 IEEE80211_DISCARD_MAC(vap,
1689 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1690 ni->ni_macaddr, "ADDBA response",
1691 "policy mismatch: expecting %s, "
1692 "received %s, tid %d code %d",
1693 tap->txa_flags & IEEE80211_AGGR_IMMEDIATE,
1694 policy, tid, code);
1695 vap->iv_stats.is_addba_badpolicy++;
1696 return;
1697 }
1698#if 0
1699 /* XXX we take MIN in ieee80211_addba_response */
1700 if (bufsiz > IEEE80211_AGGR_BAWMAX) {
1701 IEEE80211_DISCARD_MAC(vap,
1702 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1703 ni->ni_macaddr, "ADDBA response",
1704 "BA window too large: max %d, "
1705 "received %d, tid %d code %d",
1706 bufsiz, IEEE80211_AGGR_BAWMAX, tid, code);
1707 vap->iv_stats.is_addba_badbawinsize++;
1708 return;
1709 }
1710#endif
1711 IEEE80211_NOTE(vap,
1712 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1713 "recv ADDBA response: dialogtoken %u code %d "
1714 "baparamset 0x%x (tid %d bufsiz %d) batimeout %d",
1715 dialogtoken, code, baparamset, tid, bufsiz,
1716 batimeout);
1717 ic->ic_addba_response(ni, tap,
1718 code, baparamset, batimeout);
1719 return;
1720
1721 case IEEE80211_ACTION_BA_DELBA:
1722 baparamset = LE_READ_2(frm+2);
1723 code = LE_READ_2(frm+4);
1724
1725 tid = MS(baparamset, IEEE80211_DELBAPS_TID);
1726
1727 IEEE80211_NOTE(vap,
1728 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1729 "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
1730 "code %d", baparamset, tid,
1731 MS(baparamset, IEEE80211_DELBAPS_INIT), code);
1732
1733 if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
1734 ac = TID_TO_WME_AC(tid);
1735 tap = &ni->ni_tx_ampdu[ac];
1736 ic->ic_addba_stop(ni, tap);
1737 } else {
1738 rap = &ni->ni_rx_ampdu[tid];
1739 ic->ic_ampdu_rx_stop(ni, rap);
1740 }
1741 return;
1742 }
1743 break;
1744 }
1745 ieee80211_recv_action(ni, frm, efrm);
1746}
1747
1748/*
1749 * Process a received 802.11n action frame.
1750 * Aggregation-related frames are assumed to be handled
1751 * already; we handle any other frames we can, otherwise
1752 * complain about being unsupported (with debugging).
1753 */
1754void
1755ieee80211_recv_action(struct ieee80211_node *ni,
1756 const uint8_t *frm, const uint8_t *efrm)
1757{
1758 struct ieee80211vap *vap = ni->ni_vap;
1759 const struct ieee80211_action *ia;
1760 int chw;
1761
1762 ia = (const struct ieee80211_action *) frm;
1763 switch (ia->ia_category) {
1764 case IEEE80211_ACTION_CAT_BA:
1765 IEEE80211_NOTE(vap,
1766 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1767 "%s: BA action %d not implemented", __func__,
1768 ia->ia_action);
1769 vap->iv_stats.is_rx_mgtdiscard++;
1770 break;
1771 case IEEE80211_ACTION_CAT_HT:
1772 switch (ia->ia_action) {
1773 case IEEE80211_ACTION_HT_TXCHWIDTH:
1774 chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
1775 IEEE80211_NOTE(vap,
1776 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1777 "%s: HT txchwidth, width %d%s",
1778 __func__, chw, ni->ni_chw != chw ? "*" : "");
1779 if (chw != ni->ni_chw) {
1780 ni->ni_chw = chw;
1781 /* XXX notify on change */
1782 }
1783 break;
1784 case IEEE80211_ACTION_HT_MIMOPWRSAVE: {
1785 const struct ieee80211_action_ht_mimopowersave *mps =
1786 (const struct ieee80211_action_ht_mimopowersave *) ia;
1787 /* XXX check iv_htcaps */
1788 if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA)
1789 ni->ni_flags |= IEEE80211_NODE_MIMO_PS;
1790 else
1791 ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS;
1792 if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE)
1793 ni->ni_flags |= IEEE80211_NODE_MIMO_RTS;
1794 else
1795 ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS;
1796 /* XXX notify on change */
1797 IEEE80211_NOTE(vap,
1798 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1799 "%s: HT MIMO PS (%s%s)", __func__,
1800 (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ?
1801 "on" : "off",
1802 (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ?
1803 "+rts" : ""
1804 );
1805 break;
1806 }
1807 default:
1808 IEEE80211_NOTE(vap,
1809 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1810 "%s: HT action %d not implemented", __func__,
1811 ia->ia_action);
1812 vap->iv_stats.is_rx_mgtdiscard++;
1813 break;
1814 }
1815 break;
1816 default:
1817 IEEE80211_NOTE(vap,
1818 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
1819 "%s: category %d not implemented", __func__,
1820 ia->ia_category);
1821 vap->iv_stats.is_rx_mgtdiscard++;
1822 break;
1823 }
1824}
1825
1826/*
1827 * Transmit processing.
1828 */
1829
1830/*
1831 * Check if A-MPDU should be requested/enabled for a stream.
1832 * We require a traffic rate above a per-AC threshold and we
1833 * also handle backoff from previous failed attempts.

--- 98 unchanged lines hidden (view full) ---

1932 ni, "%s: stop BA stream for AC %d (reason %d)",
1933 __func__, tap->txa_ac, reason);
1934 vap->iv_stats.is_ampdu_stop++;
1935
1936 ic->ic_addba_stop(ni, tap);
1937 args[0] = WME_AC_TO_TID(tap->txa_ac);
1938 args[1] = IEEE80211_DELBAPS_INIT;
1939 args[2] = reason; /* XXX reason code */
1940 ieee80211_send_action(ni, IEEE80211_ACTION_CAT_BA,
1941 IEEE80211_ACTION_BA_DELBA, args);
1942 } else {
1943 IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
1944 ni, "%s: BA stream for AC %d not running (reason %d)",
1945 __func__, tap->txa_ac, reason);
1946 vap->iv_stats.is_ampdu_stop_failed++;
1947 }
1948}

--- 161 unchanged lines hidden (view full) ---

2110 bar_start_timer(tap);
2111 return 0;
2112bad:
2113 ieee80211_free_node(ni);
2114 return ret;
2115#undef senderr
2116}
2117
2118/*
2119 * Send an action management frame. The arguments are stuff
2120 * into a frame without inspection; the caller is assumed to
2121 * prepare them carefully (e.g. based on the aggregation state).
2122 */
2123int
2124ieee80211_send_action(struct ieee80211_node *ni,
2125 int category, int action, uint16_t args[4])
2126{
2127#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
2128#define ADDSHORT(frm, v) do { \
2129 frm[0] = (v) & 0xff; \
2130 frm[1] = (v) >> 8; \
2131 frm += 2; \
2132} while (0)
2133 struct ieee80211vap *vap = ni->ni_vap;
2134 struct ieee80211com *ic = ni->ni_ic;
2135 struct ieee80211_bpf_params params;
2136 struct mbuf *m;
2137 uint8_t *frm;
2138 uint16_t baparamset;
2139 int ret;
2140
2141 KASSERT(ni != NULL, ("null node"));
2142
2143 /*
2144 * Hold a reference on the node so it doesn't go away until after
2145 * the xmit is complete all the way in the driver. On error we
2146 * will remove our reference.
2147 */
2148 IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
2149 "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
2150 __func__, __LINE__,
2151 ni, ether_sprintf(ni->ni_macaddr),
2152 ieee80211_node_refcnt(ni)+1);
2153 ieee80211_ref_node(ni);
2154
2155 m = ieee80211_getmgtframe(&frm,
2156 ic->ic_headroom + sizeof(struct ieee80211_frame),
2157 sizeof(uint16_t) /* action+category */
2158 /* XXX may action payload */
2159 + sizeof(struct ieee80211_action_ba_addbaresponse)
2160 );
2161 if (m == NULL)
2162 senderr(ENOMEM, is_tx_nobuf);
2163
2164 *frm++ = category;
2165 *frm++ = action;
2166 switch (category) {
2167 case IEEE80211_ACTION_CAT_BA:
2168 switch (action) {
2169 case IEEE80211_ACTION_BA_ADDBA_REQUEST:
2170 IEEE80211_NOTE(vap,
2171 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2172 "send ADDBA request: dialogtoken %d "
2173 "baparamset 0x%x (tid %d) batimeout 0x%x baseqctl 0x%x",
2174 args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
2175 args[2], args[3]);
2176
2177 *frm++ = args[0]; /* dialog token */
2178 ADDSHORT(frm, args[1]); /* baparamset */
2179 ADDSHORT(frm, args[2]); /* batimeout */
2180 ADDSHORT(frm, args[3]); /* baseqctl */
2181 break;
2182 case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
2183 IEEE80211_NOTE(vap,
2184 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2185 "send ADDBA response: dialogtoken %d status %d "
2186 "baparamset 0x%x (tid %d) batimeout %d",
2187 args[0], args[1], args[2],
2188 MS(args[2], IEEE80211_BAPS_TID), args[3]);
2189
2190 *frm++ = args[0]; /* dialog token */
2191 ADDSHORT(frm, args[1]); /* statuscode */
2192 ADDSHORT(frm, args[2]); /* baparamset */
2193 ADDSHORT(frm, args[3]); /* batimeout */
2194 break;
2195 case IEEE80211_ACTION_BA_DELBA:
2196 /* XXX */
2197 baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
2198 | args[1]
2199 ;
2200 ADDSHORT(frm, baparamset);
2201 ADDSHORT(frm, args[2]); /* reason code */
2202
2203 IEEE80211_NOTE(vap,
2204 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2205 "send DELBA action: tid %d, initiator %d reason %d",
2206 args[0], args[1], args[2]);
2207 break;
2208 default:
2209 goto badaction;
2210 }
2211 break;
2212 case IEEE80211_ACTION_CAT_HT:
2213 switch (action) {
2214 case IEEE80211_ACTION_HT_TXCHWIDTH:
2215 IEEE80211_NOTE(vap,
2216 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
2217 ni, "send HT txchwidth: width %d",
2218 IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20
2219 );
2220 *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ?
2221 IEEE80211_A_HT_TXCHWIDTH_2040 :
2222 IEEE80211_A_HT_TXCHWIDTH_20;
2223 break;
2224 default:
2225 goto badaction;
2226 }
2227 break;
2228 default:
2229 badaction:
2230 IEEE80211_NOTE(vap,
2231 IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
2232 "%s: unsupported category %d action %d", __func__,
2233 category, action);
2234 senderr(EINVAL, is_tx_unknownmgt);
2235 /* NOTREACHED */
2236 }
2237 m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
2238
2239 memset(&params, 0, sizeof(params));
2240 params.ibp_pri = WME_AC_VO;
2241 params.ibp_rate0 = ni->ni_txparms->mgmtrate;
2242 /* NB: we know all frames are unicast */
2243 params.ibp_try0 = ni->ni_txparms->maxretry;
2244 params.ibp_power = ni->ni_txpower;
2245 return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION,
2246 &params);
2247bad:
2248 ieee80211_free_node(ni);
2249 if (m != NULL)
2250 m_freem(m);
2251 return ret;
2252#undef ADDSHORT
2253#undef senderr
2254}
2255
2256/*
2257 * Construct the MCS bit mask for inclusion
2258 * in an HT information element.
2259 */
2260static void
2261ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
2262{

--- 237 unchanged lines hidden ---