Index: sys/dev/usb/wlan/if_run.c =================================================================== --- sys/dev/usb/wlan/if_run.c +++ sys/dev/usb/wlan/if_run.c @@ -80,7 +80,15 @@ #define RUN_DEBUG #endif +#define IF_RUN_11N +#define IF_RUN_11N_AMPDU +/* #undef IF_RUN_11N */ +/* #undef IF_RUN_11N_AMPDU */ +#undef IF_RUN_11N_5GHZ +#define RUN_DEBUG + #ifdef RUN_DEBUG +/* int run_debug = 0x00000020; */ int run_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB run"); @@ -368,6 +376,7 @@ static usb_callback_t run_bulk_tx_callback4; static usb_callback_t run_bulk_tx_callback5; + static void run_autoinst(void *, struct usb_device *, struct usb_attach_arg *); static int run_driver_loaded(struct module *, int, void *); @@ -495,6 +504,10 @@ static void run_init_locked(struct run_softc *); static void run_stop(void *); static void run_delay(struct run_softc *, u_int); +#ifdef IF_RUN_11N +static int run_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); +static void print_run_txwi(struct rt2860_txwi *txwi); +#endif static eventhandler_tag run_etag; @@ -517,7 +530,34 @@ { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 }, { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 }, { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 }, - { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 } + { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 }, +#ifdef IF_RUN_11N +/* These rate values have been taken from the RT5350 datasheet. + Missing MCS indices in the following list are reserved according + to the datasheet. SGI/BW supported for MCS 0-15, but only SGI + supported for MCS 32. +*/ + /* single stream */ + {6, 0, IEEE80211_T_HT, 4, 60, 60}, + {13, 1, IEEE80211_T_HT, 4, 60, 60}, + {19, 2, IEEE80211_T_HT, 4, 60, 60}, + {26, 3, IEEE80211_T_HT, 4, 60, 60}, + {39, 4, IEEE80211_T_HT, 4, 60, 60}, + {52, 5, IEEE80211_T_HT, 4, 60, 60}, + {58, 6, IEEE80211_T_HT, 4, 60, 60}, + {65, 7, IEEE80211_T_HT, 4, 60, 60}, + /* 2 streams */ + {13, 8, IEEE80211_T_HT, 4, 60, 60}, + {26, 9, IEEE80211_T_HT, 4, 60, 60}, + {39, 10, IEEE80211_T_HT, 4, 60, 60}, + {52, 11, IEEE80211_T_HT, 4, 60, 60}, + {78, 12, IEEE80211_T_HT, 4, 60, 60}, + {104, 13, IEEE80211_T_HT, 4, 60, 60}, + {117, 14, IEEE80211_T_HT, 4, 60, 60}, + {130, 15, IEEE80211_T_HT, 4, 60, 60}, + /* BW = 1 => PHY_RATE *= 2, HT duplicate 6 Mbps */ + {6, 32, IEEE80211_T_HT, 4, 60, 60}, +#endif }; static const struct { @@ -810,6 +850,22 @@ IEEE80211_C_WME | /* WME */ IEEE80211_C_WPA; /* WPA1|WPA2(RSN) */ +#ifdef IF_RUN_11N + /* set device HT capabilities */ + /* HT Capabilities strictly for 11ng (we dont support 11ac yet..) */ + /* XXX: Turn on TX/RX STBC and multiple STBC streams later when + everything else works. Set MCS32 RX/TX bits later after AMPDU RX + is fast enough */ + ic->ic_htcaps = IEEE80211_HTCAP_SMPS_OFF | + IEEE80211_HTCAP_SHORTGI20 | + IEEE80211_HTCAP_GREENFIELD | + IEEE80211_HTC_HT; + + int numstreams = 2; + ic->ic_rxstream = ic->ic_txstream = numstreams; + + printf("run htcaps 0x%x\n", ic->ic_htcaps); +#endif ic->ic_cryptocaps = IEEE80211_CRYPTO_WEP | IEEE80211_CRYPTO_AES_CCM | @@ -839,6 +895,9 @@ ic->ic_vap_delete = run_vap_delete; ic->ic_transmit = run_transmit; ic->ic_parent = run_parent; +#ifdef IF_RUN_11N + ic->ic_ampdu_enable = run_ampdu_enable; +#endif ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), @@ -974,6 +1033,14 @@ vap->iv_update_beacon = run_update_beacon; vap->iv_max_aid = RT2870_WCID_MAX; +#ifdef IF_RUN_11N + vap->iv_debug |= IEEE80211_MSG_11N; +#endif +#ifdef IF_RUN_11N_AMPDU + vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K; + vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_2; /* = 4 */ + /* For density, reference driver just sets it to 4 at hw probe time */ +#endif /* * To delete the right key from h/w, we need wcid. * Luckily, there is unused space in ieee80211_key{}, wk_pad, @@ -2067,8 +2134,8 @@ ni = ieee80211_ref_node(vap->iv_bss); rn = RUN_NODE(ni); rn->fix_ridx = ridx; - RUN_DPRINTF(sc, RUN_DEBUG_RATE, "rate=%d, fix_ridx=%d\n", - rate, rn->fix_ridx); + RUN_DPRINTF(sc, RUN_DEBUG_RATE, " %s: rate=%d, fix_ridx=%d\n", + __func__, rate, rn->fix_ridx); ieee80211_free_node(ni); } @@ -2636,7 +2703,7 @@ le16toh(sta[0].error.fail); RUN_DPRINTF(sc, RUN_DEBUG_RATE, - "retrycnt=%d success=%d failcnt=%d\n", + "amrr retrycnt=%d success=%d failcnt=%d\n", txs->nretries, txs->nsuccess, le16toh(sta[0].error.fail)); } else { wstat = &(sc->wcid_stats[RUN_AID2WCID(ni->ni_associd)]); @@ -2649,7 +2716,7 @@ txs->nsuccess = (*wstat)[RUN_SUCCESS]; txs->nframes = (*wstat)[RUN_TXCNT]; RUN_DPRINTF(sc, RUN_DEBUG_RATE, - "retrycnt=%d txcnt=%d success=%d\n", + "amrr retrycnt=%d txcnt=%d success=%d\n", txs->nretries, txs->nframes, txs->nsuccess); memset(wstat, 0, sizeof(*wstat)); @@ -2657,11 +2724,13 @@ ieee80211_ratectl_tx_update(vap, txs); rn->amrr_ridx = ieee80211_ratectl_rate(ni, NULL, 0); + RUN_DPRINTF(sc, RUN_DEBUG_RATE, + "amrr ridx set to %d\n", rn->amrr_ridx); fail: RUN_UNLOCK(sc); + /* RUN_DPRINTF(sc, RUN_DEBUG_RATE, "amrr_ridx=%d\n", rn->amrr_ridx); */ - RUN_DPRINTF(sc, RUN_DEBUG_RATE, "ridx=%d\n", rn->amrr_ridx); } static void @@ -2703,7 +2772,6 @@ /* only interested in true associations */ if (isnew && ni->ni_associd != 0) { - /* * This function could is called though timeout function. * Need to defer. @@ -2745,6 +2813,7 @@ "rate=0x%02x ridx=%d ctl_ridx=%d\n", rs->rs_rates[i], rn->ridx[i], rn->ctl_ridx[i]); } + rate = vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)].mgmtrate; for (ridx = 0; ridx < RT2860_RIDX_MAX; ridx++) if (rt2860_rates[ridx].rate == rate) @@ -2786,6 +2855,8 @@ struct run_vap *rvp = RUN_VAP(vap); uint64_t ni_tstamp, rx_tstamp; + printf("Got a mgmt frame!\n"); + rvp->recv_mgmt(ni, m, subtype, rxs, rssi, nf); if (vap->iv_state == IEEE80211_S_RUN && @@ -2876,6 +2947,18 @@ } else ni = NULL; +#ifdef IF_RUN_11N_AMPDU + /* XXX predict_true? */ + /* XXX Should we check whether the RXD has the AMPDU flag set + before we mark this mbuf instead of relying on the fact that + HT operation always implies AMPDU? + */ + + if(ni && ni->ni_flags & IEEE80211_NODE_HT) { + m->m_flags |= M_AMPDU; + } +#endif + if (__predict_false(flags & RT2860_RX_MICERR)) { /* report MIC failures to net80211 for TKIP */ if (ni != NULL) @@ -2969,7 +3052,7 @@ switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - RUN_DPRINTF(sc, RUN_DEBUG_RECV, + RUN_DPRINTF(sc, RUN_DEBUG_RECV, "rx done, actlen=%d\n", xferlen); if (xferlen < (int)(sizeof(uint32_t) + rxwisize + @@ -3029,6 +3112,9 @@ /* inputting all the frames must be last */ + uint32_t cnt; + run_read(sc, RT2860_RX_MAX_CNT, &cnt); + printf("rx_max_cnt = 0x%x\n", cnt); RUN_UNLOCK(sc); m->m_pkthdr.len = m->m_len = xferlen; @@ -3285,10 +3371,9 @@ uint16_t mcs; uint8_t ridx = data->ridx; uint8_t pad; - - /* get MCS code from rate index */ - mcs = rt2860_rates[ridx].mcs; - +#ifdef IF_RUN_11N + struct ieee80211_node *ni = data->ni; +#endif txwisize = (sc->mac_ver == 0x5592) ? sizeof(*txwi) + sizeof(uint32_t) : sizeof(*txwi); xferlen = txwisize + m->m_pkthdr.len; @@ -3313,13 +3398,36 @@ /* setup TX Wireless Information */ txwi = (struct rt2860_txwi *)(txd + 1); txwi->len = htole16(m->m_pkthdr.len - pad); + + /* get MCS code from rate index */ + /* XXX: This needs to be a net80211_to_run_ridx() */ + mcs = rt2860_rates[ridx].mcs; + +#ifdef IF_RUN_11N + /* if (IEEE80211_IS_CHAN_HT20(ic->ic_curchan)) { */ + /* XXX Can we decide solely based on ridx whether this was + an MCS rate or not? It seems unlikely as amrr would consult + two different tables rates and htrates and its hard (impossible?) + to disambiguate. + */ + if(ni->ni_txrate & IEEE80211_RATE_MCS) { + ridx += (RT2860_RIDX_MAX); + mcs = rt2860_rates[ridx].mcs; + } + + if (rt2860_rates[ridx].phy == IEEE80211_T_HT) { + mcs |= RT2860_PHY_HT_MIX; + } + else +#endif if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { - mcs |= RT2860_PHY_CCK; - if (ridx != RT2860_RIDX_CCK1 && - (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) - mcs |= RT2860_PHY_SHPRE; + mcs |= RT2860_PHY_CCK; + if (ridx != RT2860_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_PHY_SHPRE; } else - mcs |= RT2860_PHY_OFDM; + mcs |= RT2860_PHY_OFDM; + txwi->phy = htole16(mcs); /* check if RTS/CTS or CTS-to-self protection is required */ @@ -3333,6 +3441,8 @@ if (vap->iv_opmode != IEEE80211_M_STA && !IEEE80211_QOS_HAS_SEQ(wh)) txwi->xflags |= RT2860_TX_NSEQ; + + /* print_run_txwi(txwi); */ } /* This function must be called locked */ @@ -3394,10 +3504,20 @@ RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; ctl_ridx = rt2860_rates[ridx].ctl_ridx; } else { - if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "set to fix_ridx ridx=%d\n", + rn->fix_ridx); + ridx = rn->fix_ridx; - else + } + else { ridx = rn->amrr_ridx; + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "set to amrr_ridx ridx=%d\n", + rn->amrr_ridx); + if(ridx == 0) + ridx = 2; + } + ctl_ridx = rt2860_rates[ridx].ctl_ridx; } @@ -3616,7 +3736,7 @@ run_set_tx_desc(sc, data); - RUN_DPRINTF(sc, RUN_DEBUG_XMIT, "sending prot len=%u rate=%u\n", + RUN_DPRINTF(sc, RUN_DEBUG_RATE, "sending prot len=%u rate=%u\n", m->m_pkthdr.len, rate); STAILQ_INSERT_TAIL(&sc->sc_epq[0].tx_qh, data, next); @@ -4850,21 +4970,34 @@ run_getradiocaps(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { + +#ifdef IF_RUN_11N_5GHZ // disable 5ghz for now struct run_softc *sc = ic->ic_softc; +#endif uint8_t bands[IEEE80211_MODE_BYTES]; + int ht40 = 0; memset(bands, 0, sizeof(bands)); + +#ifdef IF_RUN_11N + setbit(bands, IEEE80211_MODE_11NG); + setbit(bands, IEEE80211_MODE_11G); + ht40 = 0; +#else setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); - ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, 0); +#endif + ieee80211_add_channels_default_2ghz(chans, maxchans, nchans, bands, ht40); +#ifdef IF_RUN_11N_5GHZ //disable 5ghz for now if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850 || sc->rf_rev == RT3070_RF_3052 || sc->rf_rev == RT3593_RF_3053 || sc->rf_rev == RT5592_RF_5592) { setbit(bands, IEEE80211_MODE_11A); ieee80211_add_channel_list_5ghz(chans, maxchans, nchans, - run_chan_5ghz, nitems(run_chan_5ghz), bands, 0); + run_chan_5ghz, nitems(run_chan_5ghz), bands, ht40); } +#endif } static void @@ -5223,6 +5356,9 @@ { struct ieee80211com *ic = &sc->sc_ic; + RUN_DPRINTF(sc, RUN_DEBUG_ANY, + "Mode in %s is 0x%x\n", __func__, ic->ic_curmode); + /* set basic rates mask */ if (ic->ic_curmode == IEEE80211_MODE_11B) run_write(sc, RT2860_LEGACY_BASIC_RATE, 0x003); @@ -6205,6 +6341,11 @@ /* turn radio LED on */ run_set_leds(sc, RT2860_LED_RADIO); +#ifdef IF_RUN_11N + /* Set up AUTO_RSP_CFG register for auto response */ + run_write(sc, RT2860_AUTO_RSP_CFG, RT2860_AUTO_RSP_EN | RT2860_BAC_ACKPOLICY_EN | RT2860_CTS_40M_MODE_EN); +#endif + sc->sc_flags |= RUN_RUNNING; sc->cmdq_run = RUN_CMDQ_GO; @@ -6311,6 +6452,24 @@ &sc->sc_mtx : NULL, USB_MS_TO_TICKS(ms)); } +#ifdef IF_RUN_11N +static int +run_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) +{ + /* For now, no A-MPDU TX support in the driver */ + return 0; +} + +void print_run_txwi(struct rt2860_txwi *txwi) { + printf("word0: flags 0x%08x txop 0x%08x phy 0x%016x\n", txwi->flags, txwi->txop, txwi->phy); + printf("word1: xflags 0x%08x wcid 0x%08x len 0x%016x\n", txwi->xflags, txwi->wcid, txwi->len); + printf("word1 ba winsize 0x%x\n", (txwi->xflags & 0xfc) >> 2); + printf("iv: 0x%x\n", txwi->iv); + printf("eiv: 0x%x\n", txwi->eiv); + printf("-------------\n"); +} +#endif + static device_method_t run_methods[] = { /* Device interface */ DEVMETHOD(device_probe, run_match), @@ -6329,6 +6488,12 @@ DRIVER_MODULE(run, uhub, run_driver, run_devclass, run_driver_loaded, NULL); MODULE_DEPEND(run, wlan, 1, 1, 1); +#ifdef IF_RUN_11N +MODULE_DEPEND(run, wlan_amrr, 1, 1, 1); +MODULE_DEPEND(run, wlan_tkip, 1, 1, 1); +MODULE_DEPEND(run, wlan_ccmp, 1, 1, 1); +MODULE_DEPEND(run, wlan_wep, 1, 1, 1); +#endif MODULE_DEPEND(run, usb, 1, 1, 1); MODULE_DEPEND(run, firmware, 1, 1, 1); MODULE_VERSION(run, 1); Index: sys/dev/usb/wlan/if_runreg.h =================================================================== --- sys/dev/usb/wlan/if_runreg.h +++ sys/dev/usb/wlan/if_runreg.h @@ -761,6 +761,13 @@ uint8_t flags; } __packed; +/* The TXWI and TXD have stayed exactly the same for several chips by the same +manufacturer, see a datasheet for more details, for example pg. 209 in this +datasheet: https://cdn.sparkfun.com/datasheets/Wireless/WiFi/RT5350.pdf +The meaning of most hardware registers is also intact, and the datasheet +can be consulted when developing this for better understanding. +*/ + /* TX Wireless Information */ struct rt2860_txwi { uint8_t flags; @@ -781,7 +788,7 @@ #define RT2860_PHY_MODE 0xc000 #define RT2860_PHY_CCK (0 << 14) #define RT2860_PHY_OFDM (1 << 14) -#define RT2860_PHY_HT (2 << 14) +#define RT2860_PHY_HT_MIX (2 << 14) #define RT2860_PHY_HT_GF (3 << 14) #define RT2860_PHY_SGI (1 << 8) #define RT2860_PHY_BW40 (1 << 7) Index: sys/kern/kern_linker.c =================================================================== --- sys/kern/kern_linker.c +++ sys/kern/kern_linker.c @@ -1870,7 +1870,7 @@ * XXX: we need to limit this number to some reasonable value */ if (vattr.va_size > LINKER_HINTS_MAX) { - printf("hints file too large %ld\n", (long)vattr.va_size); + printf("linker.hints file too large %ld\n", (long)vattr.va_size); goto bad; } hints = malloc(vattr.va_size, M_TEMP, M_WAITOK); @@ -1888,7 +1888,7 @@ intp = (int *)hints; ival = *intp++; if (ival != LINKER_HINTS_VERSION) { - printf("hints file version mismatch %d\n", ival); + printf("linker.hints file version mismatch %d\n", ival); goto bad; } bufend = hints + vattr.va_size; Index: sys/net80211/ieee80211.h =================================================================== --- sys/net80211/ieee80211.h +++ sys/net80211/ieee80211.h @@ -649,6 +649,9 @@ "\13DELBA\14AMSDU(7935)\15DSSSCCK40\16PSMP\1740INTOLERANT" \ "\20LSIGTXOPPROT" +/* XXX: These should really be renamed to HTPARAMs instead of HTCAPs as + they do not correspond to actual HTCAPs in HT IEs +*/ /* HT parameters (hc_param) */ #define IEEE80211_HTCAP_MAXRXAMPDU 0x03 /* max rx A-MPDU factor */ #define IEEE80211_HTCAP_MAXRXAMPDU_S 0 @@ -656,6 +659,9 @@ #define IEEE80211_HTCAP_MAXRXAMPDU_16K 1 #define IEEE80211_HTCAP_MAXRXAMPDU_32K 2 #define IEEE80211_HTCAP_MAXRXAMPDU_64K 3 + +/* XXX: These should really have the unit at the end, like IEEE80211_HTCAP_MPDUDENSITY_2 + should be renamed to IEEE80211_HTCAP_MPDUDENSITY_2US */ #define IEEE80211_HTCAP_MPDUDENSITY 0x1c /* min MPDU start spacing */ #define IEEE80211_HTCAP_MPDUDENSITY_S 2 #define IEEE80211_HTCAP_MPDUDENSITY_NA 0 /* no time restriction */ Index: sys/net80211/ieee80211.c =================================================================== --- sys/net80211/ieee80211.c +++ sys/net80211/ieee80211.c @@ -120,6 +120,7 @@ /* NB: OFDM rates are handled specially based on mode */ static const struct ieee80211_rateset ieee80211_rateset_11g = { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; + /* { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; */ #undef B static int set_vht_extchan(struct ieee80211_channel *c); @@ -206,6 +207,7 @@ if (IEEE80211_IS_CHAN_VHTG(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_VHT_2GHZ); } + /* initialize candidate channels to all available */ memcpy(ic->ic_chan_active, ic->ic_chan_avail, sizeof(ic->ic_chan_avail)); @@ -1238,7 +1240,7 @@ if (*nchans >= maxchans) return (ENOBUFS); -#if 0 +#if 1 printf("%s: %d: ieee=%d, freq=%d, flags=0x%08x\n", __func__, *nchans, @@ -1272,7 +1274,7 @@ if (*nchans >= maxchans) return (ENOBUFS); -#if 0 +#if 1 printf("%s: %d: flags=0x%08x\n", __func__, *nchans, @@ -1630,6 +1632,14 @@ getflags_2ghz(bands, flags, ht40); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); +#if 1 + for(int i = 0; flags[i] != 0; i++) + printf("%s: flags[%d] = 0x%x\n", + __func__, + i, + flags[i]); +#endif + return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); } Index: sys/net80211/ieee80211_ht.c =================================================================== --- sys/net80211/ieee80211_ht.c +++ sys/net80211/ieee80211_ht.c @@ -258,7 +258,6 @@ void ieee80211_ht_vattach(struct ieee80211vap *vap) { - /* driver can override defaults */ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; @@ -2706,7 +2705,6 @@ uint8_t *frm; int tid, ret; - IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: called", @@ -2997,8 +2995,14 @@ txparams |= (ic->ic_txstream - 1) << 2; /* num TX streams */ if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) txparams |= 0x16; /* TX unequal modulation sup */ - } else - txparams = 0; + } else { + /* lrx337 XXX: This was 0, but changed to 1 in order to mimic rt2x00 behavior. + Shouldn't this if else block take into consideration other htcaps like + IEEE80211_HTC_TXMCS32 for setting txparams = 0x1 and then check if the + num streams are equal for TX RX MCS not equal (is it enough to check the + num rx / tx streams to conclude that MCS sets are unequal?). */ + txparams = 0x0; + } frm[12] = txparams; } Index: sys/net80211/ieee80211_node.c =================================================================== --- sys/net80211/ieee80211_node.c +++ sys/net80211/ieee80211_node.c @@ -761,6 +761,11 @@ ieee80211_sync_curchan(struct ieee80211com *ic) { struct ieee80211_channel *c; + printf("%s: curchan freq %d flags 0x%x\nhtadjflags 0x%x\n", + __func__, + ic->ic_curchan->ic_freq, + ic->ic_curchan->ic_flags, + gethtadjustflags(ic)); c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic)); c = ieee80211_vht_adjust_channel(ic, c, getvhtadjustflags(ic)); @@ -773,6 +778,8 @@ ic->ic_set_channel(ic); ieee80211_radiotap_chan_change(ic); IEEE80211_LOCK(ic); + } else { + printf("%s: did not change channel\n", __func__); } } Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c +++ sys/net80211/ieee80211_output.c @@ -2434,6 +2434,12 @@ frm = ieee80211_add_htcap_ch(frm, vap, c); } + /* If we are in STA mode and have HT caps, advertise them. Don't upgrade to an + HT channel just yet. */ + if ((vap->iv_opmode == IEEE80211_M_STA) && (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + frm = ieee80211_add_htcap_ch(frm, vap, ic->ic_curchan); + } + /* * XXX TODO: need to figure out what/how to update the * VHT channel.