Download raw body.
sys/qwz: add support of 11ac with 5Ghz and 20Mhz window only
tech@,
here minimal implementation of 802.11ac for qwz driver, it works only in
5Ghz network with only 20Mhz window.
Probably I've missed some details, but it works on my honor against unifi.
Tests? Feedback? Ok?
Index: sys/dev/ic/qwz.c
===================================================================
RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v
diff -u -p -r1.34 qwz.c
--- sys/dev/ic/qwz.c 23 May 2026 12:27:41 -0000 1.34
+++ sys/dev/ic/qwz.c 23 May 2026 22:42:08 -0000
@@ -10885,6 +10885,7 @@ qwz_init_channels_world(struct qwz_softc
IEEE80211_CHAN_5GHZ);
chan->ic_flags = IEEE80211_CHAN_A |
IEEE80211_CHAN_HT |
+ IEEE80211_CHAN_VHT |
IEEE80211_CHAN_PASSIVE;
}
}
@@ -10965,7 +10966,8 @@ qwz_init_channels(struct qwz_softc *sc,
} else {
chan->ic_freq = freq;
chan->ic_flags = IEEE80211_CHAN_A |
- IEEE80211_CHAN_HT;
+ IEEE80211_CHAN_HT |
+ IEEE80211_CHAN_VHT;
if (rule->flags & (REGULATORY_CHAN_RADAR |
REGULATORY_CHAN_NO_IR |
REGULATORY_CHAN_INDOOR_ONLY)) {
@@ -21116,7 +21118,8 @@ qwz_reg_update_chan_list(struct qwz_soft
* in a fixed, user-specified phy mode.
*/
if (IFM_MODE(ic->ic_media.ifm_cur->ifm_media) != IFM_AUTO) {
- if (ic->ic_curmode == IEEE80211_MODE_11A)
+ if (ic->ic_curmode == IEEE80211_MODE_11A ||
+ ic->ic_curmode == IEEE80211_MODE_11AC)
scan_2ghz = 0;
if (ic->ic_curmode == IEEE80211_MODE_11B ||
ic->ic_curmode == IEEE80211_MODE_11G)
@@ -21188,6 +21191,9 @@ qwz_reg_update_chan_list(struct qwz_soft
case IFM_IEEE80211_11B:
ch->phy_mode = MODE_11B;
break;
+ case IFM_IEEE80211_11AC:
+ ch->phy_mode = MODE_11AC_VHT20;
+ break;
case IFM_IEEE80211_11N:
default:
if (IEEE80211_IS_CHAN_A(channel))
@@ -21332,7 +21338,7 @@ qwz_mac_op_start(struct qwz_pdev *pdev)
{
struct qwz_softc *sc = pdev->sc;
struct ieee80211com *ic = &sc->sc_ic;
- int ret;
+ int n, ret;
ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_PMF_QOS, 1,
pdev->pdev_id);
@@ -21392,6 +21398,32 @@ qwz_mac_op_start(struct qwz_pdev *pdev)
if (sc->num_rx_chains > 3)
ic->ic_sup_mcs[3] = 0xff; /* MCS 24-31 */
+ ic->ic_vhtcaps = pdev->cap.vht_cap &
+ (IEEE80211_VHTCAP_MAX_MPDU_LENGTH_MASK |
+ IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK);
+
+ ic->ic_vht_rxmcs = 0;
+ for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+ if (n <= sc->num_rx_chains) {
+ ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_0_9 <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+ } else {
+ ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+ }
+ }
+
+ ic->ic_vht_txmcs = 0;
+ for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+ if (n <= sc->num_tx_chains) {
+ ic->ic_vht_txmcs |= (IEEE80211_VHT_MCS_0_9 <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+ } else {
+ ic->ic_vht_txmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+ }
+ }
+
/* TODO: Do we need to enable ANI? */
ret = qwz_reg_update_chan_list(sc, pdev->pdev_id);
@@ -21629,7 +21661,12 @@ qwz_mac_vdev_start_restart(struct qwz_so
arg.channel.band_center_freq1 = chan->ic_freq;
arg.channel.band_center_freq2 = chan->ic_freq;
- if (IEEE80211_IS_CHAN_5GHZ(chan))
+ if (IEEE80211_IS_CHAN_5GHZ(chan) &&
+ IEEE80211_IS_CHAN_AC(chan) &&
+ (ic->ic_flags & IEEE80211_F_VHTON) &&
+ ieee80211_node_supports_vht(ic->ic_bss))
+ arg.channel.mode = MODE_11AC_VHT20;
+ else if (IEEE80211_IS_CHAN_5GHZ(chan))
arg.channel.mode = MODE_11A;
else if (ic->ic_bss->ni_flags & IEEE80211_NODE_ERP)
arg.channel.mode = MODE_11G;
@@ -23836,6 +23873,7 @@ qwz_scan(struct qwz_softc *sc)
struct scan_req_params *arg = NULL;
struct ieee80211_channel *chan, *lastc;
int ret = 0, num_channels, i;
+ int scan_2ghz = 1, scan_5ghz = 1;
uint32_t scan_timeout;
if (arvif == NULL) {
@@ -23904,11 +23942,23 @@ qwz_scan(struct qwz_softc *sc)
} else
arg->scan_flags |= WMI_SCAN_FLAG_PASSIVE;
+ if (IFM_MODE(ic->ic_media.ifm_cur->ifm_media) != IFM_AUTO) {
+ if (ic->ic_curmode == IEEE80211_MODE_11A ||
+ ic->ic_curmode == IEEE80211_MODE_11AC)
+ scan_2ghz = 0;
+ if (ic->ic_curmode == IEEE80211_MODE_11B ||
+ ic->ic_curmode == IEEE80211_MODE_11G)
+ scan_5ghz = 0;
+ }
+
lastc = &ic->ic_channels[IEEE80211_CHAN_MAX];
num_channels = 0;
for (chan = &ic->ic_channels[1]; chan <= lastc; chan++) {
if (chan->ic_flags == 0)
continue;
+ if ((!scan_2ghz && IEEE80211_IS_CHAN_2GHZ(chan)) ||
+ (!scan_5ghz && IEEE80211_IS_CHAN_5GHZ(chan)))
+ continue;
num_channels++;
}
if (num_channels) {
@@ -23925,6 +23975,9 @@ qwz_scan(struct qwz_softc *sc)
for (chan = &ic->ic_channels[1]; chan <= lastc; chan++) {
if (chan->ic_flags == 0)
continue;
+ if ((!scan_2ghz && IEEE80211_IS_CHAN_2GHZ(chan)) ||
+ (!scan_5ghz && IEEE80211_IS_CHAN_5GHZ(chan)))
+ continue;
arg->chan_list[i++] = chan->ic_freq;
}
}
@@ -24272,25 +24325,29 @@ qwz_peer_assoc_h_phymode(struct qwz_soft
struct ieee80211com *ic = &sc->sc_ic;
enum wmi_phy_mode phymode;
- switch (ic->ic_curmode) {
- case IEEE80211_MODE_11A:
- phymode = MODE_11A;
- break;
- case IEEE80211_MODE_11B:
- phymode = MODE_11B;
- break;
- case IEEE80211_MODE_11G:
- phymode = MODE_11G;
- break;
- case IEEE80211_MODE_11N:
- if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
- phymode = MODE_11NA_HT20;
- else
- phymode = MODE_11NG_HT20;
- break;
- default:
- phymode = MODE_UNKNOWN;
- break;
+ if (ni->ni_flags & IEEE80211_NODE_VHT) {
+ phymode = MODE_11AC_VHT20;
+ } else {
+ switch (ic->ic_curmode) {
+ case IEEE80211_MODE_11A:
+ phymode = MODE_11A;
+ break;
+ case IEEE80211_MODE_11B:
+ phymode = MODE_11B;
+ break;
+ case IEEE80211_MODE_11G:
+ phymode = MODE_11G;
+ break;
+ case IEEE80211_MODE_11N:
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
+ phymode = MODE_11NA_HT20;
+ else
+ phymode = MODE_11NG_HT20;
+ break;
+ default:
+ phymode = MODE_UNKNOWN;
+ break;
+ }
}
DNPRINTF(QWZ_D_MAC, "%s: peer %s phymode %s\n", __func__,
@@ -24399,6 +24456,45 @@ qwz_peer_assoc_h_ht(struct qwz_softc *sc
}
void
+qwz_peer_assoc_h_vht(struct qwz_softc *sc, struct ieee80211_node *ni,
+ struct peer_assoc_params *arg)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint16_t ic_mcs, ni_mcs;
+ int max_nss, n;
+
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0)
+ return;
+
+ arg->vht_flag = true;
+ arg->vht_capable = true;
+ arg->peer_vht_caps = ni->ni_vhtcaps;
+ arg->rx_max_rate = ni->ni_vht_rx_max_lgi_mbit_s;
+ arg->rx_mcs_set = ni->ni_vht_rxmcs;
+ arg->tx_max_rate = ni->ni_vht_tx_max_lgi_mbit_s;
+ arg->tx_mcs_set = ni->ni_vht_txmcs;
+
+ max_nss = 0;
+ for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+ ic_mcs = (ic->ic_vht_txmcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+ ni_mcs = (ni->ni_vht_rxmcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+ if (ic_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP &&
+ ni_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP)
+ max_nss = n;
+ }
+ if (max_nss > 0)
+ arg->peer_nss = max_nss;
+
+ DNPRINTF(QWZ_D_MAC, "%s: vht peer %s nss %d rxmcs 0x%x txmcs 0x%x\n",
+ __func__, ether_sprintf(arg->peer_mac), arg->peer_nss,
+ arg->rx_mcs_set, arg->tx_mcs_set);
+}
+
+void
qwz_peer_assoc_h_qos(struct ieee80211_node *ni, struct peer_assoc_params *arg)
{
if (ni->ni_flags & IEEE80211_NODE_QOS) {
@@ -24420,8 +24516,8 @@ qwz_peer_assoc_prepare(struct qwz_softc
qwz_peer_assoc_h_phymode(sc, ni, arg);
qwz_peer_assoc_h_ht(sc, ni, arg);
qwz_peer_assoc_h_qos(ni, arg);
+ qwz_peer_assoc_h_vht(sc, ni, arg);
#if 0
- qwz_peer_assoc_h_vht(sc, arvif, ni, arg);
qwz_peer_assoc_h_he(sc, arvif, ni, arg);
qwz_peer_assoc_h_he_6ghz(sc, arvif, ni, arg);
qwz_peer_assoc_h_smps(ni, arg);
Index: sys/dev/pci/if_qwz_pci.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_qwz_pci.c,v
diff -u -p -r1.11 if_qwz_pci.c
--- sys/dev/pci/if_qwz_pci.c 18 May 2026 13:47:32 -0000 1.11
+++ sys/dev/pci/if_qwz_pci.c 23 May 2026 22:40:49 -0000
@@ -684,7 +684,7 @@ qwz_pci_attach(struct device *parent, st
pci_intr_handle_t ih;
pcireg_t memtype, reg;
const char *intrstr;
- int error;
+ int error, n;
pcireg_t sreg;
sc->sc_dmat = pa->pa_dmat;
@@ -979,6 +979,15 @@ qwz_pci_attach(struct device *parent, st
memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs));
ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */
+
+ ic->ic_vhtcaps = 0;
+ ic->ic_vht_rxmcs = (IEEE80211_VHT_MCS_0_9 <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(1));
+ for (n = 2; n <= IEEE80211_VHT_NUM_SS; n++) {
+ ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+ }
+ ic->ic_vht_txmcs = ic->ic_vht_rxmcs;
/* IBSS channel undefined for now. */
ic->ic_ibss_chan = &ic->ic_channels[1];
--
wbr, Kirill
sys/qwz: add support of 11ac with 5Ghz and 20Mhz window only