realtekwifi & net80211: Synchronize with FreeBSD.

As of upstream fcb5e8d0c19ac21515ab3047d39a76b32d835cec.
This commit is contained in:
Augustin Cavalier
2024-12-05 23:18:45 -05:00
parent cd730cbd7c
commit 6193a477b8
21 changed files with 321 additions and 78 deletions
@@ -247,6 +247,7 @@ rtwn_attach(struct rtwn_softc *sc)
| IEEE80211_HTCAP_SMPS_OFF /* SM PS mode disabled */ | IEEE80211_HTCAP_SMPS_OFF /* SM PS mode disabled */
/* s/w capabilities */ /* s/w capabilities */
| IEEE80211_HTC_HT /* HT operation */ | IEEE80211_HTC_HT /* HT operation */
| IEEE80211_HTC_RX_AMSDU_AMPDU /* A-MSDU in A-MPDU */
| IEEE80211_HTC_AMPDU /* A-MPDU tx */ | IEEE80211_HTC_AMPDU /* A-MPDU tx */
| IEEE80211_HTC_AMSDU /* A-MSDU tx */ | IEEE80211_HTC_AMSDU /* A-MSDU tx */
; ;
@@ -325,12 +326,15 @@ rtwn_sysctlattach(struct rtwn_softc *sc)
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
#if 1
sc->sc_ht40 = 0; sc->sc_ht40 = 0;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"ht40", CTLFLAG_RDTUN, &sc->sc_ht40, "ht40", CTLFLAG_RDTUN, &sc->sc_ht40,
sc->sc_ht40, "Enable 40 MHz mode support"); sc->sc_ht40, "Enable 40 MHz mode support");
#endif
sc->sc_ena_tsf64 = 0;
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
"ena_tsf64", CTLFLAG_RWTUN, &sc->sc_ena_tsf64,
sc->sc_ena_tsf64, "Enable/disable per-packet TSF64 reporting");
#ifdef RTWN_DEBUG #ifdef RTWN_DEBUG
SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, SYSCTL_ADD_U32(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
@@ -16,7 +16,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <sys/cdefs.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/lock.h> #include <sys/lock.h>
#include <sys/mutex.h> #include <sys/mutex.h>
@@ -285,8 +285,18 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc)
rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC; rxs.c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC;
rxs.r_flags |= IEEE80211_R_TSF_START; /* XXX undocumented */ rxs.r_flags |= IEEE80211_R_TSF_START; /* XXX undocumented */
rxs.r_flags |= IEEE80211_R_TSF64;
rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat); /*
* Doing the TSF64 extension on USB is expensive, especially
* if it's being done on every MPDU in an AMPDU burst.
*/
if (sc->sc_ena_tsf64) {
rxs.r_flags |= IEEE80211_R_TSF64;
rxs.c_rx_tsf = rtwn_extend_rx_tsf(sc, stat);
} else {
rxs.r_flags |= IEEE80211_R_TSF32;
rxs.c_rx_tsf = le32toh(stat->tsf_low);
}
/* Get RSSI from PHY status descriptor. */ /* Get RSSI from PHY status descriptor. */
is_cck = (rxs.c_pktflags & IEEE80211_RX_F_CCK) != 0; is_cck = (rxs.c_pktflags & IEEE80211_RX_F_CCK) != 0;
@@ -318,6 +328,10 @@ rtwn_rx_common(struct rtwn_softc *sc, struct mbuf *m, void *desc)
/* Drop PHY descriptor. */ /* Drop PHY descriptor. */
m_adj(m, infosz + shift); m_adj(m, infosz + shift);
/* If APPFCS, drop FCS */
if (sc->rcr & R92C_RCR_APPFCS)
m_adj(m, -IEEE80211_CRC_LEN);
return (ni); return (ni);
} }
@@ -456,6 +470,15 @@ rtwn_rxfilter_init(struct rtwn_softc *sc)
R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS | R92C_RCR_HTC_LOC_CTRL | R92C_RCR_APP_PHYSTS |
R92C_RCR_APP_ICV | R92C_RCR_APP_MIC; R92C_RCR_APP_ICV | R92C_RCR_APP_MIC;
/*
* Add FCS, to work around occasional 4 byte truncation
* with some frames. This is more problematic on RTL8812/
* RTL8821 because they're also doing L3/L4 checksum offload
* and hardware encryption, so both are tagged as "passed"
* before the frame is truncated.
*/
sc->rcr |= R92C_RCR_APPFCS;
/* Update dynamic Rx filter parts. */ /* Update dynamic Rx filter parts. */
rtwn_rxfilter_update(sc); rtwn_rxfilter_update(sc);
} }
@@ -487,7 +510,7 @@ rtwn_set_promisc(struct rtwn_softc *sc)
RTWN_ASSERT_LOCKED(sc); RTWN_ASSERT_LOCKED(sc);
mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP; mask_all = R92C_RCR_ACF | R92C_RCR_ADF | R92C_RCR_AMF | R92C_RCR_AAP;
mask_min = R92C_RCR_APM; mask_min = R92C_RCR_APM | R92C_RCR_APPFCS;
if (sc->bcn_vaps == 0) if (sc->bcn_vaps == 0)
mask_min |= R92C_RCR_CBSSID_BCN; mask_min |= R92C_RCR_CBSSID_BCN;
@@ -171,9 +171,8 @@ struct rtwn_softc {
struct mbufq sc_snd; struct mbufq sc_snd;
device_t sc_dev; device_t sc_dev;
#if 1
int sc_ht40; int sc_ht40;
#endif int sc_ena_tsf64;
uint32_t sc_debug; uint32_t sc_debug;
int sc_hwcrypto; int sc_hwcrypto;
int sc_ratectl_sysctl; int sc_ratectl_sysctl;
@@ -18,7 +18,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <sys/cdefs.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/lock.h> #include <sys/lock.h>
@@ -18,7 +18,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <sys/cdefs.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/lock.h> #include <sys/lock.h>
#include <sys/mutex.h> #include <sys/mutex.h>
@@ -171,16 +171,21 @@ static void
r92c_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) r92c_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni)
{ {
struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf; struct r92c_tx_desc *txd = (struct r92c_tx_desc *)buf;
struct ieee80211vap *vap = ni->ni_vap;
if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) && /* HT20 */ /*
(ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)) * Only enable short-GI if we're transmitting in that
txd->txdw5 |= htole32(R92C_TXDW5_SGI); * width to that node.
else if (ni->ni_chan != IEEE80211_CHAN_ANYC && /* HT40 */ *
IEEE80211_IS_CHAN_HT40(ni->ni_chan) && * Specifically, do not enable shortgi for 20MHz if
(ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) && * we're attempting to transmit at 40MHz.
(vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)) */
txd->txdw5 |= htole32(R92C_TXDW5_SGI); if (ieee80211_ht_check_tx_ht40(ni)) {
if (ieee80211_ht_check_tx_shortgi_40(ni))
txd->txdw5 |= htole32(R92C_TXDW5_SGI);
} else if (ieee80211_ht_check_tx_ht(ni)) {
if (ieee80211_ht_check_tx_shortgi_20(ni))
txd->txdw5 |= htole32(R92C_TXDW5_SGI);
}
} }
void void
@@ -216,6 +221,28 @@ r92c_tx_setup_macid(void *buf, int id)
txd->txdw4 &= ~htole32(R92C_TXDW4_RTS_SHORT); txd->txdw4 &= ~htole32(R92C_TXDW4_RTS_SHORT);
} }
static int
r92c_calculate_tx_agg_window(struct rtwn_softc *sc,
const struct ieee80211_node *ni, int tid)
{
const struct ieee80211_tx_ampdu *tap;
int wnd;
tap = &ni->ni_tx_ampdu[tid];
/*
* BAW is (MAX_AGG * 2) + 1, hence the /2 here.
* Ensure we don't send 0 or more than 64.
*/
wnd = tap->txa_wnd / 2;
if (wnd == 0)
wnd = 1;
else if (wnd > 0x1f)
wnd = 0x1f;
return (wnd);
}
void void
r92c_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, r92c_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni,
struct mbuf *m, void *buf, uint8_t ridx, int maxretry) struct mbuf *m, void *buf, uint8_t ridx, int maxretry)
@@ -271,9 +298,9 @@ r92c_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni,
(m->m_flags & M_AMPDU_MPDU) != 0); (m->m_flags & M_AMPDU_MPDU) != 0);
if (m->m_flags & M_AMPDU_MPDU) { if (m->m_flags & M_AMPDU_MPDU) {
txd->txdw2 |= htole32(SM(R92C_TXDW2_AMPDU_DEN, txd->txdw2 |= htole32(SM(R92C_TXDW2_AMPDU_DEN,
vap->iv_ampdu_density)); ieee80211_ht_get_node_ampdu_density(ni)));
txd->txdw6 |= htole32(SM(R92C_TXDW6_MAX_AGG, txd->txdw6 |= htole32(SM(R92C_TXDW6_MAX_AGG,
0x1f)); /* XXX */ r92c_calculate_tx_agg_window(sc, ni, tid)));
} }
if (sc->sc_ratectl == RTWN_RATECTL_NET80211) { if (sc->sc_ratectl == RTWN_RATECTL_NET80211) {
txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT); txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT);
@@ -171,8 +171,10 @@ r12a_iq_calib_fw(struct rtwn_softc *sc)
else else
cmd.band_bw = RTWN_CMD_IQ_BAND_2GHZ; cmd.band_bw = RTWN_CMD_IQ_BAND_2GHZ;
/* TODO: 80/160 MHz. */ /* TODO: 160MHz */
if (IEEE80211_IS_CHAN_HT40(c)) if (IEEE80211_IS_CHAN_VHT80(c))
cmd.band_bw |= RTWN_CMD_IQ_CHAN_WIDTH_80;
else if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_VHT40(c))
cmd.band_bw |= RTWN_CMD_IQ_CHAN_WIDTH_40; cmd.band_bw |= RTWN_CMD_IQ_CHAN_WIDTH_40;
else else
cmd.band_bw |= RTWN_CMD_IQ_CHAN_WIDTH_20; cmd.band_bw |= RTWN_CMD_IQ_CHAN_WIDTH_20;
@@ -75,8 +75,8 @@
#define R12A_DATA_SEC_PRIM_DOWN_20 0x02 #define R12A_DATA_SEC_PRIM_DOWN_20 0x02
#define R12A_DATA_SEC_PRIM_UPPER_20 0x03 #define R12A_DATA_SEC_PRIM_UPPER_20 0x03
#define R12A_DATA_SEC_PRIM_LOWER_20 0x04 #define R12A_DATA_SEC_PRIM_LOWER_20 0x04
#define R12A_DATA_SEC_PRIM_UP_40 0x90 #define R12A_DATA_SEC_PRIM_UP_40 0x09
#define R12A_DATA_SEC_PRIM_DOWN_40 0xa0 #define R12A_DATA_SEC_PRIM_DOWN_40 0x0a
/* Bits for R12A_HT_SINGLE_AMPDU. */ /* Bits for R12A_HT_SINGLE_AMPDU. */
#define R12A_HT_SINGLE_AMPDU_PKT_ENA 0x80 #define R12A_HT_SINGLE_AMPDU_PKT_ENA 0x80
@@ -147,6 +147,9 @@ r12a_tx_raid(struct rtwn_softc *sc, struct r12a_tx_desc *txd,
case IEEE80211_MODE_11NG: case IEEE80211_MODE_11NG:
mode = IEEE80211_MODE_11G; mode = IEEE80211_MODE_11G;
break; break;
case IEEE80211_MODE_VHT_5GHZ:
mode = IEEE80211_MODE_VHT_5GHZ;
break;
default: default:
device_printf(sc->sc_dev, "unknown mode(1) %d!\n", device_printf(sc->sc_dev, "unknown mode(1) %d!\n",
ic->ic_curmode); ic->ic_curmode);
@@ -186,8 +189,13 @@ r12a_tx_raid(struct rtwn_softc *sc, struct r12a_tx_desc *txd,
raid = R12A_RAID_11BGN_2; raid = R12A_RAID_11BGN_2;
} }
break; break;
case IEEE80211_MODE_VHT_5GHZ:
if (sc->ntxchains == 1)
raid = R12A_RAID_11AC_1;
else
raid = R12A_RAID_11AC_2;
break;
default: default:
/* TODO: 80 MHz / 11ac */
device_printf(sc->sc_dev, "unknown mode(2) %d!\n", mode); device_printf(sc->sc_dev, "unknown mode(2) %d!\n", mode);
return; return;
} }
@@ -199,16 +207,23 @@ static void
r12a_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni) r12a_tx_set_sgi(struct rtwn_softc *sc, void *buf, struct ieee80211_node *ni)
{ {
struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf; struct r12a_tx_desc *txd = (struct r12a_tx_desc *)buf;
struct ieee80211vap *vap = ni->ni_vap;
if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) && /* HT20 */ /* TODO: VHT 20/40/80 checks */
(ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20))
txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); /*
else if (ni->ni_chan != IEEE80211_CHAN_ANYC && /* HT40 */ * Only enable short-GI if we're transmitting in that
IEEE80211_IS_CHAN_HT40(ni->ni_chan) && * width to that node.
(ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) && *
(vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)) * Specifically, do not enable shortgi for 20MHz if
txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT); * we're attempting to transmit at 40MHz.
*/
if (ieee80211_ht_check_tx_ht40(ni)) {
if (ieee80211_ht_check_tx_shortgi_40(ni))
txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT);
} else if (ieee80211_ht_check_tx_ht(ni)) {
if (ieee80211_ht_check_tx_shortgi_20(ni))
txd->txdw5 |= htole32(R12A_TXDW5_DATA_SHORT);
}
} }
static void static void
@@ -222,6 +237,28 @@ r12a_tx_set_ldpc(struct rtwn_softc *sc, struct r12a_tx_desc *txd,
txd->txdw5 |= htole32(R12A_TXDW5_DATA_LDPC); txd->txdw5 |= htole32(R12A_TXDW5_DATA_LDPC);
} }
static int
r12a_calculate_tx_agg_window(struct rtwn_softc *sc,
const struct ieee80211_node *ni, int tid)
{
const struct ieee80211_tx_ampdu *tap;
int wnd;
tap = &ni->ni_tx_ampdu[tid];
/*
* BAW is (MAX_AGG * 2) + 1, hence the /2 here.
* Ensure we don't send 0 or more than 64.
*/
wnd = tap->txa_wnd / 2;
if (wnd == 0)
wnd = 1;
else if (wnd > 0x1f)
wnd = 0x1f;
return (wnd);
}
void void
r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni, r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni,
struct mbuf *m, void *buf, uint8_t ridx, int maxretry) struct mbuf *m, void *buf, uint8_t ridx, int maxretry)
@@ -273,9 +310,9 @@ r12a_fill_tx_desc(struct rtwn_softc *sc, struct ieee80211_node *ni,
if (m->m_flags & M_AMPDU_MPDU) { if (m->m_flags & M_AMPDU_MPDU) {
txd->txdw2 |= htole32(R12A_TXDW2_AGGEN); txd->txdw2 |= htole32(R12A_TXDW2_AGGEN);
txd->txdw2 |= htole32(SM(R12A_TXDW2_AMPDU_DEN, txd->txdw2 |= htole32(SM(R12A_TXDW2_AMPDU_DEN,
vap->iv_ampdu_density)); ieee80211_ht_get_node_ampdu_density(ni)));
txd->txdw3 |= htole32(SM(R12A_TXDW3_MAX_AGG, txd->txdw3 |= htole32(SM(R12A_TXDW3_MAX_AGG,
0x1f)); /* XXX */ r12a_calculate_tx_agg_window(sc, ni, tid)));
} else } else
txd->txdw2 |= htole32(R12A_TXDW2_AGGBK); txd->txdw2 |= htole32(R12A_TXDW2_AGGBK);
@@ -138,9 +138,7 @@ struct r12a_tx_desc {
#define R12A_RAID_11BG 6 #define R12A_RAID_11BG 6
#define R12A_RAID_11G 7 /* "pure" 11g */ #define R12A_RAID_11G 7 /* "pure" 11g */
#define R12A_RAID_11B 8 #define R12A_RAID_11B 8
#define R12A_RAID_11AC_2_80 9 #define R12A_RAID_11AC_2 9
#define R12A_RAID_11AC_1_80 10 #define R12A_RAID_11AC_1 10
#define R12A_RAID_11AC_1 11
#define R12A_RAID_11AC_2 12
#endif /* R12A_TX_DESC_H */ #endif /* R12A_TX_DESC_H */
@@ -18,7 +18,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <sys/cdefs.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/lock.h> #include <sys/lock.h>
@@ -18,7 +18,6 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <sys/cdefs.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/lock.h> #include <sys/lock.h>
#include <sys/mutex.h> #include <sys/mutex.h>
@@ -752,6 +752,8 @@ ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change,
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_STBC_TX);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_STBC_RX);
IEEE80211_UNLOCK(ic); IEEE80211_UNLOCK(ic);
return 1; return 1;
@@ -809,6 +811,8 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_STBC_TX);
ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_STBC_RX);
/* NB: this handles the bpfdetach done below */ /* NB: this handles the bpfdetach done below */
ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF);
@@ -931,6 +931,7 @@ enum ieee80211_vht_mcs_support {
IEEE80211_VHT_MCS_NOT_SUPPORTED = 3 /* not supported */ IEEE80211_VHT_MCS_NOT_SUPPORTED = 3 /* not supported */
}; };
/* 802.11ac-2013, 8.4.2.160.3 Supported VHT-MCS and NSS Set field */
struct ieee80211_vht_mcs_info { struct ieee80211_vht_mcs_info {
uint16_t rx_mcs_map; uint16_t rx_mcs_map;
uint16_t rx_highest; uint16_t rx_highest;
@@ -973,7 +974,7 @@ struct ieee80211_vht_operation {
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK 0x0000000C #define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK 0x0000000C
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S 2 #define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S 2
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_NONE 0 #define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_NO160 0
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ 1 #define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ 1
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80P80MHZ 2 #define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80P80MHZ 2
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_RESERVED 3 #define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_RESERVED 3
@@ -138,25 +138,6 @@ amrr_deinit(struct ieee80211vap *vap)
nrefs--; /* XXX locking */ nrefs--; /* XXX locking */
} }
/*
* Return whether 11n rates are possible.
*
* Some 11n devices may return HT information but no HT rates.
* Thus, we shouldn't treat them as an 11n node.
*/
static int
amrr_node_is_11n(struct ieee80211_node *ni)
{
if (ni->ni_chan == NULL)
return (0);
if (ni->ni_chan == IEEE80211_CHAN_ANYC)
return (0);
if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
return (0);
return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
}
static void static void
amrr_node_init(struct ieee80211_node *ni) amrr_node_init(struct ieee80211_node *ni)
{ {
@@ -189,7 +170,7 @@ amrr_node_init(struct ieee80211_node *ni)
amn->amn_success_threshold = amrr->amrr_min_success_threshold; amn->amn_success_threshold = amrr->amrr_min_success_threshold;
/* 11n or not? Pick the right rateset */ /* 11n or not? Pick the right rateset */
if (amrr_node_is_11n(ni)) { if (ieee80211_ht_check_tx_ht(ni)) {
/* XXX ew */ /* XXX ew */
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"%s: 11n node", __func__); "%s: 11n node", __func__);
@@ -204,7 +185,7 @@ amrr_node_init(struct ieee80211_node *ni)
rate = rs->rs_rates[0]; rate = rs->rs_rates[0];
/* XXX clear the basic rate flag if it's not 11n */ /* XXX clear the basic rate flag if it's not 11n */
if (! amrr_node_is_11n(ni)) if (! ieee80211_ht_check_tx_ht(ni))
rate &= IEEE80211_RATE_VAL; rate &= IEEE80211_RATE_VAL;
/* pick initial rate from the rateset - HT or otherwise */ /* pick initial rate from the rateset - HT or otherwise */
@@ -213,7 +194,7 @@ amrr_node_init(struct ieee80211_node *ni)
amn->amn_rix--) { amn->amn_rix--) {
/* legacy - anything < 36mbit, stop searching */ /* legacy - anything < 36mbit, stop searching */
/* 11n - stop at MCS4 */ /* 11n - stop at MCS4 */
if (amrr_node_is_11n(ni)) { if (ieee80211_ht_check_tx_ht(ni)) {
if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
break; break;
} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
@@ -222,7 +203,7 @@ amrr_node_init(struct ieee80211_node *ni)
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
/* if the rate is an 11n rate, ensure the MCS bit is set */ /* if the rate is an 11n rate, ensure the MCS bit is set */
if (amrr_node_is_11n(ni)) if (ieee80211_ht_check_tx_ht(ni))
rate |= IEEE80211_RATE_MCS; rate |= IEEE80211_RATE_MCS;
/* Assign initial rate from the rateset */ /* Assign initial rate from the rateset */
@@ -234,7 +215,7 @@ amrr_node_init(struct ieee80211_node *ni)
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni, IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR: nrates=%d, initial rate %s%d", "AMRR: nrates=%d, initial rate %s%d",
rs->rs_nrates, rs->rs_nrates,
amrr_node_is_11n(ni) ? "MCS " : "", ieee80211_ht_check_tx_ht(ni) ? "MCS " : "",
rate & IEEE80211_RATE_VAL); rate & IEEE80211_RATE_VAL);
} }
@@ -254,7 +235,7 @@ amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt)); KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
/* 11n or not? Pick the right rateset */ /* 11n or not? Pick the right rateset */
if (amrr_node_is_11n(ni)) { if (ieee80211_ht_check_tx_ht(ni)) {
/* XXX ew */ /* XXX ew */
rs = (struct ieee80211_rateset *) &ni->ni_htrates; rs = (struct ieee80211_rateset *) &ni->ni_htrates;
} else { } else {
@@ -347,7 +328,7 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
amrr = amn->amn_amrr; amrr = amn->amn_amrr;
/* 11n or not? Pick the right rateset */ /* 11n or not? Pick the right rateset */
if (amrr_node_is_11n(ni)) { if (ieee80211_ht_check_tx_ht(ni)) {
/* XXX ew */ /* XXX ew */
rs = (struct ieee80211_rateset *) &ni->ni_htrates; rs = (struct ieee80211_rateset *) &ni->ni_htrates;
} else { } else {
@@ -360,7 +341,7 @@ amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
/* update public rate */ /* update public rate */
ni->ni_txrate = rs->rs_rates[rix]; ni->ni_txrate = rs->rs_rates[rix];
/* XXX strip basic rate flag from txrate, if non-11n */ /* XXX strip basic rate flag from txrate, if non-11n */
if (amrr_node_is_11n(ni)) if (ieee80211_ht_check_tx_ht(ni))
ni->ni_txrate |= IEEE80211_RATE_MCS; ni->ni_txrate |= IEEE80211_RATE_MCS;
else else
ni->ni_txrate &= IEEE80211_RATE_VAL; ni->ni_txrate &= IEEE80211_RATE_VAL;
@@ -482,7 +463,7 @@ amrr_print_node_rate(struct ieee80211_amrr_node *amn,
int rate; int rate;
struct ieee80211_rateset *rs; struct ieee80211_rateset *rs;
if (amrr_node_is_11n(ni)) { if (ieee80211_ht_check_tx_ht(ni)) {
rs = (struct ieee80211_rateset *) &ni->ni_htrates; rs = (struct ieee80211_rateset *) &ni->ni_htrates;
rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
sbuf_printf(s, "rate: MCS %d\n", rate); sbuf_printf(s, "rate: MCS %d\n", rate);
@@ -3604,3 +3604,143 @@ ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni)
frm[5] = BCM_OUI_HTINFO; frm[5] = BCM_OUI_HTINFO;
return ieee80211_add_htinfo_body(frm + 6, ni); return ieee80211_add_htinfo_body(frm + 6, ni);
} }
/*
* Get the HT density for the given 802.11n node.
*
* Take into account the density advertised from the peer.
* Larger values are longer A-MPDU density spacing values, and
* we want to obey them per station if we get them.
*/
int
ieee80211_ht_get_node_ampdu_density(const struct ieee80211_node *ni)
{
struct ieee80211vap *vap;
int peer_mpdudensity;
vap = ni->ni_vap;
peer_mpdudensity =
_IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
if (vap->iv_ampdu_density > peer_mpdudensity)
peer_mpdudensity = vap->iv_ampdu_density;
return (peer_mpdudensity);
}
/*
* Get the transmit A-MPDU limit for the given 802.11n node.
*
* Take into account the limit advertised from the peer.
* Smaller values indicate smaller maximum A-MPDU sizes, and
* should be used when forming an A-MPDU to the given peer.
*/
int
ieee80211_ht_get_node_ampdu_limit(const struct ieee80211_node *ni)
{
struct ieee80211vap *vap;
int peer_mpdulimit;
vap = ni->ni_vap;
peer_mpdulimit =
_IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
return (MIN(vap->iv_ampdu_limit, peer_mpdulimit));
}
/*
* Return true if short-GI is available when transmitting to
* the given node at 20MHz.
*
* Ensure it's configured and available in the VAP / driver as
* well as the node.
*/
bool
ieee80211_ht_check_tx_shortgi_20(const struct ieee80211_node *ni)
{
const struct ieee80211vap *vap;
const struct ieee80211com *ic;
if (! ieee80211_ht_check_tx_ht(ni))
return (false);
vap = ni->ni_vap;
ic = ni->ni_ic;
return ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) &&
(ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) &&
(vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20));
}
/*
* Return true if short-GI is available when transmitting to
* the given node at 40MHz.
*
* Ensure it's configured and available in the VAP / driver as
* well as the node and BSS.
*/
bool
ieee80211_ht_check_tx_shortgi_40(const struct ieee80211_node *ni)
{
const struct ieee80211vap *vap;
const struct ieee80211com *ic;
if (! ieee80211_ht_check_tx_ht40(ni))
return (false);
vap = ni->ni_vap;
ic = ni->ni_ic;
return ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40) &&
(ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) &&
(vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40));
}
/*
* Return true if HT rates can be used for the given node.
*
* There are some situations seen in the wild, wild past where
* HT APs would announce HT but no HT rates.
*/
bool
ieee80211_ht_check_tx_ht(const struct ieee80211_node *ni)
{
const struct ieee80211vap *vap;
const struct ieee80211_channel *bss_chan;
if (ni == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC ||
ni->ni_vap == NULL || ni->ni_vap->iv_bss == NULL)
return (false);
vap = ni->ni_vap;
bss_chan = vap->iv_bss->ni_chan;
if (bss_chan == IEEE80211_CHAN_ANYC)
return (false);
if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
ni->ni_htrates.rs_nrates == 0)
return (false);
return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
}
/*
* Return true if HT40 rates can be transmitted to the given node.
*
* This verifies that the BSS is HT40 capable and the current
* node channel width is 40MHz.
*/
bool
ieee80211_ht_check_tx_ht40(const struct ieee80211_node *ni)
{
struct ieee80211vap *vap;
struct ieee80211_channel *bss_chan;
if (! ieee80211_ht_check_tx_ht(ni))
return (false);
vap = ni->ni_vap;
bss_chan = vap->iv_bss->ni_chan;
return (IEEE80211_IS_CHAN_HT40(bss_chan) &&
IEEE80211_IS_CHAN_HT40(ni->ni_chan) &&
(ni->ni_chw == 40));
}
@@ -240,5 +240,11 @@ int ieee80211_ampdu_tx_request_ext(struct ieee80211_node *ni, int tid);
int ieee80211_ampdu_tx_request_active_ext(struct ieee80211_node *ni, int ieee80211_ampdu_tx_request_active_ext(struct ieee80211_node *ni,
int tid, int status); int tid, int status);
void ieee80211_htinfo_notify(struct ieee80211vap *vap); void ieee80211_htinfo_notify(struct ieee80211vap *vap);
int ieee80211_ht_get_node_ampdu_density(const struct ieee80211_node *ni);
int ieee80211_ht_get_node_ampdu_limit(const struct ieee80211_node *ni);
bool ieee80211_ht_check_tx_shortgi_20(const struct ieee80211_node *ni);
bool ieee80211_ht_check_tx_shortgi_40(const struct ieee80211_node *ni);
bool ieee80211_ht_check_tx_ht40(const struct ieee80211_node *ni);
bool ieee80211_ht_check_tx_ht(const struct ieee80211_node *ht);
#endif /* _NET80211_IEEE80211_HT_H_ */ #endif /* _NET80211_IEEE80211_HT_H_ */
@@ -3524,6 +3524,26 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
else else
ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_USEVHT80P80); ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_USEVHT80P80);
/* Check if we can do STBC TX/RX before changing the setting. */
if ((ireq->i_val & IEEE80211_FVHT_STBC_TX) &&
((vap->iv_vht_cap.vht_cap_info & IEEE80211_VHTCAP_TXSTBC) == 0))
return EOPNOTSUPP;
if ((ireq->i_val & IEEE80211_FVHT_STBC_RX) &&
((vap->iv_vht_cap.vht_cap_info & IEEE80211_VHTCAP_RXSTBC_MASK) == 0))
return EOPNOTSUPP;
/* TX */
if (ireq->i_val & IEEE80211_FVHT_STBC_TX)
ieee80211_syncflag_vht(vap, IEEE80211_FVHT_STBC_TX);
else
ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_STBC_TX);
/* RX */
if (ireq->i_val & IEEE80211_FVHT_STBC_RX)
ieee80211_syncflag_vht(vap, IEEE80211_FVHT_STBC_RX);
else
ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_STBC_RX);
error = ENETRESET; error = ENETRESET;
break; break;
@@ -739,12 +739,20 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_FVHT_USEVHT80 0x000000004 /* CONF: Use VHT80 */ #define IEEE80211_FVHT_USEVHT80 0x000000004 /* CONF: Use VHT80 */
#define IEEE80211_FVHT_USEVHT160 0x000000008 /* CONF: Use VHT160 */ #define IEEE80211_FVHT_USEVHT160 0x000000008 /* CONF: Use VHT160 */
#define IEEE80211_FVHT_USEVHT80P80 0x000000010 /* CONF: Use VHT 80+80 */ #define IEEE80211_FVHT_USEVHT80P80 0x000000010 /* CONF: Use VHT 80+80 */
#define IEEE80211_FVHT_MASK \ #define IEEE80211_FVHT_STBC_TX 0x00000020 /* CONF: STBC tx enabled */
#define IEEE80211_FVHT_STBC_RX 0x00000040 /* CONF: STBC rx enabled */
#define IEEE80211_FVHT_CHANWIDTH_MASK \
(IEEE80211_FVHT_VHT | IEEE80211_FVHT_USEVHT40 | \ (IEEE80211_FVHT_VHT | IEEE80211_FVHT_USEVHT40 | \
IEEE80211_FVHT_USEVHT80 | IEEE80211_FVHT_USEVHT160 | \ IEEE80211_FVHT_USEVHT80 | IEEE80211_FVHT_USEVHT160 | \
IEEE80211_FVHT_USEVHT80P80) IEEE80211_FVHT_USEVHT80P80)
#define IEEE80211_FVHT_MASK \
(IEEE80211_FVHT_CHANWIDTH_MASK | \
IEEE80211_FVHT_STBC_TX | IEEE80211_FVHT_STBC_RX)
#define IEEE80211_VFHT_BITS \ #define IEEE80211_VFHT_BITS \
"\20\1VHT\2VHT40\3VHT80\4VHT160\5VHT80P80" "\20\1VHT\2VHT40\3VHT80\4VHT160\5VHT80P80\6STBC_TX\7STBC_RX"
#define IEEE80211_COM_DETACHED 0x00000001 /* ieee80211_ifdetach called */ #define IEEE80211_COM_DETACHED 0x00000001 /* ieee80211_ifdetach called */
#define IEEE80211_COM_REF_ADD 0x00000002 /* add / remove reference */ #define IEEE80211_COM_REF_ADD 0x00000002 /* add / remove reference */
@@ -439,8 +439,7 @@ ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni,
IEEE80211_VHTCAP_RXSTBC_MASK); IEEE80211_VHTCAP_RXSTBC_MASK);
} }
val = MIN(val1, val2); val = MIN(val1, val2);
/* XXX For now, use the 11n config flag */ if ((vap->iv_vht_flags & IEEE80211_FVHT_STBC_TX) == 0)
if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_TX) == 0)
val = 0; val = 0;
new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_TXSTBC); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_TXSTBC);
@@ -453,8 +452,7 @@ ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni,
IEEE80211_VHTCAP_TXSTBC); IEEE80211_VHTCAP_TXSTBC);
} }
val = MIN(val1, val2); val = MIN(val1, val2);
/* XXX For now, use the 11n config flag */ if ((vap->iv_vht_flags & IEEE80211_FVHT_STBC_RX) == 0)
if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0)
val = 0; val = 0;
new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_RXSTBC_MASK); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_RXSTBC_MASK);