Add VHT capability and operation IE definitions to ieee80211.h.
Introduce channel flags to identify 80MHz and 160MHz capable channels.
Parse VHT IEs in beacons, announce the driver's VHT capabilities in
probe requests and assoc requests, and hop into 11ac mode after
association to the AP if possible.
Enable VHT by default if the driver announces support for it.
ok claudio@
-/* $OpenBSD: ieee80211.c,v 1.86 2021/12/05 11:33:45 stsp Exp $ */
+/* $OpenBSD: ieee80211.c,v 1.87 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211.c,v 1.19 2004/06/06 05:45:29 dyoung Exp $ */
/*-
ADD(ic, IFM_IEEE80211_VHT_MCS0 + i,
mopt | IFM_IEEE80211_MONITOR);
}
-#if 0
ic->ic_flags |= IEEE80211_F_VHTON; /* enable 11ac by default */
+ ic->ic_flags |= IEEE80211_F_HTON; /* 11ac implies 11n */
if (ic->ic_caps & IEEE80211_C_QOS)
ic->ic_flags |= IEEE80211_F_QOS;
-#endif
}
ieee80211_media_status(ifp, &imr);
(newphymode == IEEE80211_MODE_AUTO ||
newphymode == IEEE80211_MODE_11AC)) {
ic->ic_flags |= IEEE80211_F_VHTON;
+ ic->ic_flags |= IEEE80211_F_HTON;
ieee80211_configure_ampdu_tx(ic, 1);
} else if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) &&
(newphymode == IEEE80211_MODE_AUTO ||
-/* $OpenBSD: ieee80211.h,v 1.62 2019/02/19 08:12:30 stsp Exp $ */
+/* $OpenBSD: ieee80211.h,v 1.63 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211.h,v 1.6 2004/04/30 23:51:53 dyoung Exp $ */
/*-
IEEE80211_ELEMID_U_APSD_COEX = 142,
/* 143-174 reserved */
IEEE80211_ELEMID_MCCAOP_ADVERT_OVIEW = 174,
- /* 175-220 reserved */
+ /* 175-190 reserved */
+ IEEE80211_ELEMID_VHTCAPS = 191, /* 11ac */
+ IEEE80211_ELEMID_VHTOP = 192, /* 11ac */
+ IEEE80211_ELEMID_EXT_BSS_LOAD = 193, /* 11ac */
+ IEEE80211_ELEMID_WIDEBAND_CHNL_SWITCH = 194, /* 11ac */
+ IEEE80211_ELEMID_VHT_TXPOWER = 195, /* 11ac */
+ IEEE80211_ELEMID_CHNL_SWITCH_WRAPPER = 196, /* 11ac */
+ IEEE80211_ELEMID_AID = 197, /* 11ac */
+ IEEE80211_ELEMID_QUIET_CHNL = 198, /* 11ac */
+ IEEE80211_ELEMID_OPMODE_NOTIF = 199, /* 11ac */
+ /* 200-220 reserved */
IEEE80211_ELEMID_VENDOR = 221 /* vendor private */
/* 222-255 reserved */
};
#define IEEE80211_HTOP2_PCOPHASE40 0x0800
/* Bits 12-15 are reserved. */
+/*
+ * VHT Capabilities Info (see 802.11ac-2013 8.4.2.160.2).
+ */
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_MASK 0x00000003
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_SHIFT 0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991 1
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_MASK 0x0c
+#define IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT 2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_80 0
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160 1
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160_8080 2
+#define IEEE80211_VHTCAP_RX_LDPC 0x00000010
+#define IEEE80211_VHTCAP_SGI80 0x00000020
+#define IEEE80211_VHTCAP_SGI160 0x00000040
+#define IEEE80211_VHTCAP_TX_STBC 0x00000080
+#define IEEE80211_VHTCAP_RX_STBC_SS_MASK 0x00000700
+#define IEEE80211_VHTCAP_RX_STBC_SS_SHIFT 8
+#define IEEE80211_VHTCAP_SU_BEAMFORMER 0x00000800
+#define IEEE80211_VHTCAP_SU_BEAMFORMEE 0x00001000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK 0x0000e000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT 13
+#define IEEE80211_VHTCAP_NUM_STS_MASK 0x00070000
+#define IEEE80211_VHTCAP_NUM_STS_SHIFT 16
+#define IEEE80211_VHTCAP_MU_BEAMFORMER 0x00080000
+#define IEEE80211_VHTCAP_MU_BEAMFORMEE 0x00100000
+#define IEEE80211_VHTCAP_TXOP_PS 0x00200000
+#define IEEE80211_VHTCAP_HTC_VHT 0x00400000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK 0x03800000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT 23
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_8K 0
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_16K 1
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_32K 2
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K 3
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_128K 4
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_256K 5
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_512K 6
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_1024K 7
+#define IEEE80211_VHTCAP_LINK_ADAPT_MASK 0x0c000000
+#define IEEE80211_VHTCAP_LINK_ADAPT_SHIFT 26
+#define IEEE80211_VHTCAP_LINK_ADAPT_UNSOL_MFB 2
+#define IEEE80211_VHTCAP_LINK_ADAPT_MRQ_MFB 3
+#define IEEE80211_VHTCAP_RX_ANT_PATTERN 0x10000000
+#define IEEE80211_VHTCAP_TX_ANT_PATTERN 0x20000000
+
+/*
+ * VHT-MCS and NSS map (see 802.11ac-2013 8.4.2.160.3, Figure 8-401bs).
+ * Set of VHT MCS supported for a given number of spatial streams, `n'.
+ * Used by the VHT capabilities IE and by the basic VHT MSC set in
+ * the VHT operation IE.
+ */
+#define IEEE80211_VHT_MCS_FOR_SS_MASK(n) (0x3 << (2*((n)-1)))
+#define IEEE80211_VHT_MCS_FOR_SS_SHIFT(n) (2*((n)-1))
+#define IEEE80211_VHT_MCS_0_7 0
+#define IEEE80211_VHT_MCS_0_8 1
+#define IEEE80211_VHT_MCS_0_9 2
+#define IEEE80211_VHT_MCS_SS_NOT_SUPP 3
+
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_MASK 0x1fff
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_SHIFT 0
+
+/* The highest number of spatial streams supported by VHT. */
+#define IEEE80211_VHT_NUM_SS 8
+
+/*
+ * VHT Operation element (see 802.11ac-2013 8.4.2.161).
+ */
+/* Byte 0. */
+#define IEEE80211_VHTOP0_CHAN_WIDTH_MASK 0x03
+#define IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT 0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_HT 0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_80 1
+#define IEEE80211_VHTOP0_CHAN_WIDTH_160 2
+#define IEEE80211_VHTOP0_CHAN_WIDTH_8080 3
+/* Byte 1 contains channel center frequency index 0 for 80, 80+80, 160 MHz. */
+/* Byte 2 contains channel center frequency index 1 for 80+80 MHz only. */
+
/*
* EDCA Access Categories.
*/
-/* $OpenBSD: ieee80211_input.c,v 1.244 2022/01/28 07:11:15 guenther Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.245 2022/03/14 15:07:24 stsp Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
- const u_int8_t *rsnie, *wpaie, *htcaps, *htop;
+ const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop;
u_int16_t capinfo, bintval;
u_int8_t chan, bchan, erp;
int is_new;
capinfo = LE_READ_2(frm); frm += 2;
ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
- htcaps = htop = NULL;
+ htcaps = htop = vhtcaps = vhtop = NULL;
bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
chan = bchan;
erp = 0;
case IEEE80211_ELEMID_HTOP:
htop = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
+ case IEEE80211_ELEMID_VHTOP:
+ vhtop = frm;
+ break;
case IEEE80211_ELEMID_TIM:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
if (htop && !ieee80211_setup_htop(ni, htop + 2, htop[1], 1))
htop = NULL; /* invalid HTOP */
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+ vhtop = NULL; /* invalid VHTOP */
+ }
if (tim) {
ni->ni_dtimcount = tim[2];
{
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates, *htcaps;
+ const u_int8_t *ssid, *rates, *xrates, *htcaps, *vhtcaps;
u_int8_t rate;
if (ic->ic_opmode == IEEE80211_M_STA ||
frm = (const u_int8_t *)&wh[1];
efrm = mtod(m, u_int8_t *) + m->m_len;
- ssid = rates = xrates = htcaps = NULL;
+ ssid = rates = xrates = htcaps = vhtcaps = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
case IEEE80211_ELEMID_HTCAPS:
htcaps = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
}
frm += 2 + frm[1];
}
ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
else
ieee80211_clear_htcaps(ni);
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ else
+ ieee80211_clear_vhtcaps(ni);
IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
}
#endif /* IEEE80211_STA_ONLY */
{
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie, *htcaps;
+ const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie;
+ const u_int8_t *htcaps, *vhtcaps;
u_int16_t capinfo, bintval;
int resp, status = 0;
struct ieee80211_rsnparams rsn;
} else
resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = NULL;
+ ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = vhtcaps = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
case IEEE80211_ELEMID_HTCAPS:
htcaps = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
else
ieee80211_clear_htcaps(ni);
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ else
+ ieee80211_clear_vhtcaps(ni);
end:
if (status != 0) {
IEEE80211_SEND_MGMT(ic, ni, resp, status);
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
+ const u_int8_t *vhtcaps, *vhtop;
u_int16_t capinfo, status, associd;
u_int8_t rate;
associd = LE_READ_2(frm); frm += 2;
rates = xrates = edcaie = wmmie = htcaps = htop = NULL;
+ vhtcaps = vhtop = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
case IEEE80211_ELEMID_HTOP:
htop = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
+ case IEEE80211_ELEMID_VHTOP:
+ vhtop = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
ieee80211_setup_htop(ni, htop + 2, htop[1], 0);
ieee80211_ht_negotiate(ic, ni);
- /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */
- if (ni->ni_flags & IEEE80211_NODE_HT)
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+ vhtop = NULL; /* invalid VHTOP */
+ }
+ ieee80211_vht_negotiate(ic, ni);
+
+ /* Hop into 11n/11ac modes after associating to a HT/VHT AP. */
+ if (ni->ni_flags & IEEE80211_NODE_VHT)
+ ieee80211_setmode(ic, IEEE80211_MODE_11AC);
+ else if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_setmode(ic, IEEE80211_MODE_11N);
else
ieee80211_setmode(ic, ieee80211_chan2mode(ic, ni->ni_chan));
-/* $OpenBSD: ieee80211_ioctl.h,v 1.42 2022/03/07 08:13:13 stsp Exp $ */
+/* $OpenBSD: ieee80211_ioctl.h,v 1.43 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_ioctl.h,v 1.7 2004/04/30 22:51:04 dyoung Exp $ */
/*-
u_int32_t is_ht_rx_ba_window_gap_timeout;
u_int32_t is_ht_rx_ba_timeout;
u_int32_t is_ht_tx_ba_timeout;
+ u_int32_t is_vht_nego_no_mandatory_mcs;
+ u_int32_t is_vht_nego_no_basic_mcs;
};
#define SIOCG80211STATS _IOWR('i', 242, struct ifreq)
-/* $OpenBSD: ieee80211_node.c,v 1.191 2022/01/12 08:29:27 stsp Exp $ */
+/* $OpenBSD: ieee80211_node.c,v 1.192 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */
/*-
void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_ht(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_node_leave_vht(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_pwrsave(struct ieee80211com *,
return 1;
}
+/*
+ * Install received VHT caps information in the node's state block.
+ */
+void
+ieee80211_setup_vhtcaps(struct ieee80211_node *ni, const uint8_t *data,
+ uint8_t len)
+{
+ if (len != 12)
+ return;
+
+ ni->ni_vhtcaps = (data[0] | (data[1] << 8) | data[2] << 16 |
+ data[3] << 24);
+ ni->ni_vht_rxmcs = (data[4] | (data[5] << 8));
+ ni->ni_vht_rx_max_lgi_mbit_s = ((data[6] | (data[7] << 8)) &
+ IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+ ni->ni_vht_txmcs = (data[8] | (data[9] << 8));
+ ni->ni_vht_tx_max_lgi_mbit_s = ((data[10] | (data[11] << 8)) &
+ IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+
+ ni->ni_flags |= IEEE80211_NODE_VHTCAP;
+}
+
+/*
+ * Install received VHT op information in the node's state block.
+ */
+int
+ieee80211_setup_vhtop(struct ieee80211_node *ni, const uint8_t *data,
+ uint8_t len, int isprobe)
+{
+
+ if (len != 5)
+ return 0;
+
+ if (data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_HT &&
+ data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_80 &&
+ data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_160 &&
+ data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_8080)
+ return 0;
+
+ ni->ni_vht_chan_width = data[0];
+ ni->ni_vht_chan_center_freq_idx0 = data[1];
+ ni->ni_vht_chan_center_freq_idx1 = data[2];
+ ni->ni_vht_basic_mcs = (data[3] | data[4] << 8);
+ return 1;
+}
+
+#ifndef IEEE80211_STA_ONLY
+/*
+ * Handle nodes switching from 11ac into legacy modes.
+ */
+void
+ieee80211_clear_vhtcaps(struct ieee80211_node *ni)
+{
+ ni->ni_vhtcaps = 0;
+ ni->ni_vht_rxmcs = 0;
+ ni->ni_vht_rx_max_lgi_mbit_s = 0;
+ ni->ni_vht_txmcs = 0;
+ ni->ni_vht_tx_max_lgi_mbit_s = 0;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+ IEEE80211_NODE_VHT_SGI160 | IEEE80211_NODE_VHTCAP);
+
+}
+#endif
+
/*
* Install received rate set information in the node's state block.
*/
ieee80211_clear_htcaps(ni);
}
+/*
+ * Handle a VHT STA leaving a VHT network.
+ */
+void
+ieee80211_node_leave_vht(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ ieee80211_clear_vhtcaps(ni);
+}
+
/*
* Handle a station leaving an RSN network.
*/
if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_node_leave_ht(ic, ni);
+ if (ni->ni_flags & IEEE80211_NODE_VHT)
+ ieee80211_node_leave_vht(ic, ni);
if (ic->ic_node_leave != NULL)
(*ic->ic_node_leave)(ic, ni);
-/* $OpenBSD: ieee80211_node.h,v 1.91 2022/01/12 08:29:27 stsp Exp $ */
+/* $OpenBSD: ieee80211_node.h,v 1.92 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */
/*-
uint16_t ni_htop2;
uint8_t ni_basic_mcs[howmany(128,NBBY)];
+ /* VHT capabilities */
+ uint32_t ni_vhtcaps;
+ uint16_t ni_vht_rxmcs;
+ uint16_t ni_vht_rx_max_lgi_mbit_s;
+ uint16_t ni_vht_txmcs;
+ uint16_t ni_vht_tx_max_lgi_mbit_s;
+
+ /* VHT operation */
+ uint8_t ni_vht_chan_width;
+ uint8_t ni_vht_chan_center_freq_idx0;
+ uint8_t ni_vht_chan_center_freq_idx1;
+ uint16_t ni_vht_basic_mcs;
+
/* Timeout handlers which trigger Tx Block Ack negotiation. */
struct timeout ni_addba_req_to[IEEE80211_NUM_TID];
int ni_addba_req_intval[IEEE80211_NUM_TID];
#define IEEE80211_NODE_HT_SGI40 0x8000 /* SGI on 40 MHz negotiated */
#define IEEE80211_NODE_VHT 0x10000 /* VHT negotiated */
#define IEEE80211_NODE_HTCAP 0x20000 /* claims to support HT */
+#define IEEE80211_NODE_VHTCAP 0x40000 /* claims to support VHT */
+#define IEEE80211_NODE_VHT_SGI80 0x80000 /* SGI on 80 MHz negotiated */
+#define IEEE80211_NODE_VHT_SGI160 0x100000 /* SGI on 160 MHz negotiated */
/* If not NULL, this function gets called when ni_refcnt hits zero. */
void (*ni_unref_cb)(struct ieee80211com *,
(ni->ni_htop0 & IEEE80211_HTOP0_CHW));
}
+/*
+ * Check if the peer supports VHT.
+ * Require a VHT capabilities IE and support for VHT MCS with a single
+ * spatial stream.
+ */
+static inline int
+ieee80211_node_supports_vht(struct ieee80211_node *ni)
+{
+ uint16_t rx_mcs;
+
+ rx_mcs = (ni->ni_vht_rxmcs & IEEE80211_VHT_MCS_FOR_SS_MASK(1)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(1);
+
+ return ((ni->ni_flags & IEEE80211_NODE_VHTCAP) &&
+ rx_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 80 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi80(struct ieee80211_node *ni)
+{
+ return ieee80211_node_supports_vht(ni) &&
+ (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 160 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi160(struct ieee80211_node *ni)
+{
+ return ieee80211_node_supports_vht(ni) &&
+ (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160);
+}
+
+/* Check if the peer can receive frames sent on an 80 MHz channel. */
+static inline int
+ieee80211_node_supports_vht_chan80(struct ieee80211_node *ni)
+{
+ uint8_t cap_chan_width, op_chan_width;
+
+ if (!ieee80211_node_supports_vht(ni))
+ return 0;
+
+ cap_chan_width = (ni->ni_vhtcaps & IEEE80211_VHTCAP_CHAN_WIDTH_MASK) >>
+ IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT;
+ if (cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_80 &&
+ cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160 &&
+ cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160_8080)
+ return 0;
+
+ op_chan_width = (ni->ni_vht_chan_width &
+ IEEE80211_VHTOP0_CHAN_WIDTH_MASK) >>
+ IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT;
+
+ return (op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80 ||
+ op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160 ||
+ op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_8080);
+}
+
struct ieee80211com;
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_clear_htcaps(struct ieee80211_node *);
int ieee80211_setup_htop(struct ieee80211_node *, const uint8_t *,
uint8_t, int);
+void ieee80211_setup_vhtcaps(struct ieee80211_node *, const uint8_t *,
+ uint8_t);
+void ieee80211_clear_vhtcaps(struct ieee80211_node *);
+int ieee80211_setup_vhtop(struct ieee80211_node *, const uint8_t *,
+ uint8_t, int);
int ieee80211_setup_rates(struct ieee80211com *,
struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int);
void ieee80211_node_trigger_addba_req(struct ieee80211_node *, int);
-/* $OpenBSD: ieee80211_output.c,v 1.136 2022/01/05 05:18:25 dlg Exp $ */
+/* $OpenBSD: ieee80211_output.c,v 1.137 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */
/*-
[EDCA_AC_VI] = { 3, 4, 2, 94 },
[EDCA_AC_VO] = { 2, 3, 2, 47 }
},
+ [IEEE80211_MODE_11AC] = {
+ [EDCA_AC_BK] = { 4, 10, 7, 0 },
+ [EDCA_AC_BE] = { 4, 10, 3, 0 },
+ [EDCA_AC_VI] = { 3, 4, 2, 94 },
+ [EDCA_AC_VO] = { 2, 3, 2, 47 }
+ },
};
#ifndef IEEE80211_STA_ONLY
[EDCA_AC_VI] = { 3, 4, 1, 94 },
[EDCA_AC_VO] = { 2, 3, 1, 47 }
},
+ [IEEE80211_MODE_11AC] = {
+ [EDCA_AC_BK] = { 4, 10, 7, 0 },
+ [EDCA_AC_BE] = { 4, 6, 3, 0 },
+ [EDCA_AC_VI] = { 3, 4, 1, 94 },
+ [EDCA_AC_VO] = { 2, 3, 1, 47 }
+ },
};
#endif /* IEEE80211_STA_ONLY */
}
#endif /* !IEEE80211_STA_ONLY */
+/*
+ * Add a VHT Capabilities element to a frame (see 802.11ac-2013 8.4.2.160.2).
+ */
+u_int8_t *
+ieee80211_add_vhtcaps(u_int8_t *frm, struct ieee80211com *ic)
+{
+ *frm++ = IEEE80211_ELEMID_VHTCAPS;
+ *frm++ = 12;
+ LE_WRITE_4(frm, ic->ic_vhtcaps); frm += 4;
+ LE_WRITE_2(frm, ic->ic_vht_rxmcs); frm += 2;
+ LE_WRITE_2(frm, ic->ic_vht_rx_max_lgi_mbit_s); frm += 2;
+ LE_WRITE_2(frm, ic->ic_vht_txmcs); frm += 2;
+ LE_WRITE_2(frm, ic->ic_vht_tx_max_lgi_mbit_s); frm += 2;
+ return frm;
+}
+
#ifndef IEEE80211_STA_ONLY
/*
* Add a Timeout Interval element to a frame (see 7.3.2.49).
2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
- ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+ ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+ ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
if (m == NULL)
return NULL;
frm = ieee80211_add_htcaps(frm, ic);
frm = ieee80211_add_wme_info(frm, ic);
}
+ if (ic->ic_flags & IEEE80211_F_VHTON)
+ frm = ieee80211_add_htcaps(frm, ic);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
(((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
2 + IEEE80211_WPAIE_MAXLEN : 0) +
- ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+ ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+ ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
if (m == NULL)
return NULL;
frm = ieee80211_add_htcaps(frm, ic);
frm = ieee80211_add_wme_info(frm, ic);
}
+ if (ic->ic_flags & IEEE80211_F_VHTON)
+ frm = ieee80211_add_vhtcaps(frm, ic);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
-/* $OpenBSD: ieee80211_proto.c,v 1.107 2021/12/05 11:33:45 stsp Exp $ */
+/* $OpenBSD: ieee80211_proto.c,v 1.108 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */
/*-
"11b", /* IEEE80211_MODE_11B */
"11g", /* IEEE80211_MODE_11G */
"11n", /* IEEE80211_MODE_11N */
+ "11ac", /* IEEE80211_MODE_11AC */
};
void ieee80211_set_beacon_miss_threshold(struct ieee80211com *);
ni->ni_flags |= IEEE80211_NODE_HT_SGI40;
}
+void
+ieee80211_vht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ int n;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+ IEEE80211_NODE_VHT_SGI160);
+
+ /* Check if we support VHT. */
+ if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11AC)) == 0)
+ return;
+
+ /* Check if VHT support has been explicitly disabled. */
+ if ((ic->ic_flags & IEEE80211_F_VHTON) == 0)
+ return;
+
+ /*
+ * Check if the peer supports VHT.
+ * MCS 0-7 for a single spatial stream are mandatory.
+ */
+ if (!ieee80211_node_supports_vht(ni)) {
+ ic->ic_stats.is_vht_nego_no_mandatory_mcs++;
+ return;
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ /* We must support the AP's basic MCS set. */
+ for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+ uint16_t basic_mcs = (ni->ni_vht_basic_mcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+ uint16_t rx_mcs = (ic->ic_vht_rxmcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+ if (basic_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP &&
+ basic_mcs > rx_mcs) {
+ ic->ic_stats.is_vht_nego_no_basic_mcs++;
+ return;
+ }
+ }
+ }
+
+ ni->ni_flags |= IEEE80211_NODE_VHT;
+
+ if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80) &&
+ (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI80))
+ ni->ni_flags |= IEEE80211_NODE_VHT_SGI80;
+ if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160) &&
+ (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI160))
+ ni->ni_flags |= IEEE80211_NODE_VHT_SGI160;
+}
+
void
ieee80211_tx_ba_timeout(void *arg)
{
else
printf(" start %u%sMb",
rate / 2, (rate & 1) ? ".5" : "");
- printf(" %s preamble %s slot time%s%s\n",
+ printf(" %s preamble %s slot time%s%s%s\n",
(ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
"short" : "long",
(ic->ic_flags & IEEE80211_F_SHSLOT) ?
(ic->ic_flags & IEEE80211_F_USEPROT) ?
" protection enabled" : "",
(ni->ni_flags & IEEE80211_NODE_HT) ?
- " HT enabled" : "");
+ " HT enabled" : "",
+ (ni->ni_flags & IEEE80211_NODE_VHT) ?
+ " VHT enabled" : "");
}
if (!(ic->ic_flags & IEEE80211_F_RSNON)) {
/*
-/* $OpenBSD: ieee80211_proto.h,v 1.47 2021/12/05 11:33:45 stsp Exp $ */
+/* $OpenBSD: ieee80211_proto.h,v 1.48 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */
/*-
const struct ieee80211_rateset *);
extern u_int8_t *ieee80211_add_htcaps(u_int8_t *, struct ieee80211com *);
extern u_int8_t *ieee80211_add_htop(u_int8_t *, struct ieee80211com *);
+extern u_int8_t *ieee80211_add_vhtcaps(u_int8_t *, struct ieee80211com *);
extern u_int8_t *ieee80211_add_tie(u_int8_t *, u_int8_t, u_int32_t);
extern int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *,
struct ieee80211_node *);
extern void ieee80211_ht_negotiate(struct ieee80211com *,
struct ieee80211_node *);
+extern void ieee80211_vht_negotiate(struct ieee80211com *,
+ struct ieee80211_node *);
extern void ieee80211_tx_ba_timeout(void *);
extern void ieee80211_rx_ba_timeout(void *);
extern int ieee80211_addba_request(struct ieee80211com *,
-/* $OpenBSD: ieee80211_var.h,v 1.110 2022/01/21 15:51:03 stsp Exp $ */
+/* $OpenBSD: ieee80211_var.h,v 1.111 2022/03/14 15:07:24 stsp Exp $ */
/* $NetBSD: ieee80211_var.h,v 1.7 2004/05/06 03:07:10 dyoung Exp $ */
/*-
struct ieee80211_channel {
u_int16_t ic_freq; /* setting in MHz */
u_int16_t ic_flags; /* see below */
+ u_int32_t ic_xflags; /* extra flags; see below */
};
/*
#define IEEE80211_CHAN_VHT 0x4000 /* 11ac/VHT channel */
#define IEEE80211_CHAN_40MHZ 0x8000 /* use of 40 MHz is allowed */
+/*
+ * Extra channel flags.
+ */
+#define IEEE80211_CHANX_80MHZ 0x00000001 /* use of 80 MHz is allowed */
+#define IEEE80211_CHANX_160MHZ 0x00000002 /* use of 160 MHz is allowed */
+
/*
* Useful combinations of channel characteristics.
*/
#define IEEE80211_IS_CHAN_XR(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_XR) != 0)
+#define IEEE80211_CHAN_40MHZ_ALLOWED(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_40MHZ) != 0)
+#define IEEE80211_CHAN_80MHZ_ALLOWED(_c) \
+ (((_c)->ic_xflags & IEEE80211_CHANX_80MHZ) != 0)
+#define IEEE80211_CHAN_160MHZ_ALLOWED(_c) \
+ (((_c)->ic_xflags & IEEE80211_CHANX_160MHZ) != 0)
+
/*
* EDCA AC parameters.
*/
u_int8_t ic_aselcaps;
u_int8_t ic_dialog_token;
int ic_fixed_mcs;
+
+ uint32_t ic_vhtcaps;
+ uint16_t ic_vht_rxmcs;
+ uint16_t ic_vht_rx_max_lgi_mbit_s;
+ uint16_t ic_vht_txmcs;
+ uint16_t ic_vht_tx_max_lgi_mbit_s;
+
TAILQ_HEAD(, ieee80211_ess) ic_ess;
};
#define ic_if ic_ac.ac_if