We currently do not support 11n mode on devices which do not have all antenna ports connected. So if e.g. an athn(4) card is installed into an APU or Alix, we require that users plug pigtails into all antenna connectors on the card, and mount a corresponding number of antennas on the case. Given hardware restrictions this may not always be possible.
I happen to have an Alix case with only one hole for an antenna, and I am now running a MIMO-capable AR9280 card on this board. Without this patch I can either run the card in 11a/g mode, or I could screw a hole for another antenna into the case. But 11n mode isn't a viable option; if either side decides to send a MIMO data frame this frame will be lost. There is no technical requirement for this restriction since 11n mode can operate with just MCS 0-7. There are 11n devices which do not even support MIMO and 11n mode already works fine with such devices. However, on devices which support MIMO we would need a way to tell how many antennas are physically connected in order to make a decision. Automatically detecting dead antennas requires tedious driver-specific heuristics, and in some cases isn't even a possibility since firmware may restrict what the driver can see. E.g. I've been told by an iwlwifi developer that current Intel firmware doesn't bother with detecting missing antennas. What we can easily offer is another nwflag to disable MIMO at run-time. With this set, drivers will operate their device as if it didn't support MIMO, net80211 can stop announcing support for MIMO rates, and 11n mode works. Tested on iwm(4) and athn(4), and works as expected for me. ok? (Apply this patch, run 'make includes', and recompile ifconfig and the kernel.) diff 6608380519f922f07dba3821961f4d7330327041 ad4a4e1aff0295006baf16de294666e4b1293b93 blob - f6efc3bc13e84ddb4e7821b23637e3404668caef blob + c954658d613a805b0e003654429ff8ae68c3c6a7 --- sbin/ifconfig/ifconfig.8 +++ sbin/ifconfig/ifconfig.8 @@ -1053,6 +1053,13 @@ flag will disable the direct bridging of frames betwee nodes when operating in Host AP mode. Setting this flag will block and filter direct inter-station communications. +.It nomimo +The +.Ql nomimo +flag will disable MIMO reception and transmission even if the driver +and wireless network device support MIMO. +This flag can be used to work around packet loss in 11n mode if the +wireless network device has unpopulated extra antenna connectors. .It stayauth The .Ql stayauth blob - 8153923216917c1f8fdb77c3ac4de21feba10e1d blob + 7d2d13f168c1d6eb7e50756c8956e0b05a3c8270 --- sys/dev/ic/athn.c +++ sys/dev/ic/athn.c @@ -180,6 +180,53 @@ struct cfdriver athn_cd = { NULL, "athn", DV_IFNET }; +void +athn_config_ht(struct athn_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int i, ntxstreams, nrxstreams; + + if ((sc->flags & ATHN_FLAG_11N) == 0) + return; + + /* Set HT capabilities. */ + ic->ic_htcaps = (IEEE80211_HTCAP_SMPS_DIS << + IEEE80211_HTCAP_SMPS_SHIFT); +#ifdef notyet + ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40 | + IEEE80211_HTCAP_SGI40 | + IEEE80211_HTCAP_DSSSCCK40; +#endif + ic->ic_htxcaps = 0; +#ifdef notyet + if (AR_SREV_9271(sc) || AR_SREV_9287_10_OR_LATER(sc)) + ic->ic_htcaps |= IEEE80211_HTCAP_SGI20; + if (AR_SREV_9380_10_OR_LATER(sc)) + ic->ic_htcaps |= IEEE80211_HTCAP_LDPC; + if (AR_SREV_9280_10_OR_LATER(sc)) { + ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; + ic->ic_htcaps |= 1 << IEEE80211_HTCAP_RXSTBC_SHIFT; + } +#endif + ntxstreams = sc->ntxchains; + nrxstreams = sc->nrxchains; + if (!AR_SREV_9380_10_OR_LATER(sc)) { + ntxstreams = MIN(ntxstreams, 2); + nrxstreams = MIN(nrxstreams, 2); + } + /* Set supported HT rates. */ + if (ic->ic_userflags & IEEE80211_F_NOMIMO) + ntxstreams = nrxstreams = 1; + memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs)); + for (i = 0; i < nrxstreams; i++) + ic->ic_sup_mcs[i] = 0xff; + ic->ic_tx_mcs_set = IEEE80211_TX_MCS_SET_DEFINED; + if (ntxstreams != nrxstreams) { + ic->ic_tx_mcs_set |= IEEE80211_TX_RX_MCS_NOT_EQUAL; + ic->ic_tx_mcs_set |= (ntxstreams - 1) << 2; + } +} + int athn_attach(struct athn_softc *sc) { @@ -302,44 +349,8 @@ athn_attach(struct athn_softc *sc) IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ IEEE80211_C_PMGT; /* Power saving supported. */ - if (sc->flags & ATHN_FLAG_11N) { - int i, ntxstreams, nrxstreams; + athn_config_ht(sc); - /* Set HT capabilities. */ - ic->ic_htcaps = (IEEE80211_HTCAP_SMPS_DIS << - IEEE80211_HTCAP_SMPS_SHIFT); -#ifdef notyet - ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40 | - IEEE80211_HTCAP_SGI40 | - IEEE80211_HTCAP_DSSSCCK40; -#endif - ic->ic_htxcaps = 0; -#ifdef notyet - if (AR_SREV_9271(sc) || AR_SREV_9287_10_OR_LATER(sc)) - ic->ic_htcaps |= IEEE80211_HTCAP_SGI20; - if (AR_SREV_9380_10_OR_LATER(sc)) - ic->ic_htcaps |= IEEE80211_HTCAP_LDPC; - if (AR_SREV_9280_10_OR_LATER(sc)) { - ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC; - ic->ic_htcaps |= 1 << IEEE80211_HTCAP_RXSTBC_SHIFT; - } -#endif - ntxstreams = sc->ntxchains; - nrxstreams = sc->nrxchains; - if (!AR_SREV_9380_10_OR_LATER(sc)) { - ntxstreams = MIN(ntxstreams, 2); - nrxstreams = MIN(nrxstreams, 2); - } - /* Set supported HT rates. */ - for (i = 0; i < nrxstreams; i++) - ic->ic_sup_mcs[i] = 0xff; - ic->ic_tx_mcs_set |= IEEE80211_TX_MCS_SET_DEFINED; - if (ntxstreams != nrxstreams) { - ic->ic_tx_mcs_set |= IEEE80211_TX_RX_MCS_NOT_EQUAL; - ic->ic_tx_mcs_set |= (ntxstreams - 1) << 2; - } - } - /* Set supported rates. */ if (sc->flags & ATHN_FLAG_11G) { ic->ic_sup_rates[IEEE80211_MODE_11B] = @@ -3063,6 +3074,8 @@ athn_init(struct ifnet *ifp) sc->sc_dev.dv_xname, error); goto fail; } + + athn_config_ht(sc); /* Enable Rx. */ athn_rx_start(sc); blob - 01c059a512b8d83fe9900942e07b4c1b128f660c blob + 22a1b1992f95343786a9a98348e6a725ec8dbb29 --- sys/dev/pci/if_iwm.c +++ sys/dev/pci/if_iwm.c @@ -324,6 +324,7 @@ int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint16_t *, size_t); void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const, const uint8_t *nvm_channels, int nchan); +int iwm_mimo_enabled(struct iwm_softc *); void iwm_setup_ht_rates(struct iwm_softc *); void iwm_htprot_task(void *); void iwm_update_htprot(struct ieee80211com *, struct ieee80211_node *); @@ -2861,6 +2862,15 @@ iwm_init_channel_map(struct iwm_softc *sc, const uint1 } } +int +iwm_mimo_enabled(struct iwm_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + return !sc->sc_nvm.sku_cap_mimo_disable && + (ic->ic_userflags & IEEE80211_F_NOMIMO) == 0; +} + void iwm_setup_ht_rates(struct iwm_softc *sc) { @@ -2870,9 +2880,10 @@ iwm_setup_ht_rates(struct iwm_softc *sc) /* TX is supported with the same MCS as RX. */ ic->ic_tx_mcs_set = IEEE80211_TX_MCS_SET_DEFINED; + memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs)); ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */ - if (sc->sc_nvm.sku_cap_mimo_disable) + if (!iwm_mimo_enabled(sc)) return; rx_ant = iwm_fw_valid_rx_ant(sc); @@ -5245,7 +5256,7 @@ iwm_add_sta_cmd(struct iwm_softc *sc, struct iwm_node |= htole32(IWM_STA_FLG_MAX_AGG_SIZE_MSK | IWM_STA_FLG_AGG_MPDU_DENS_MSK); - if (!sc->sc_nvm.sku_cap_mimo_disable) { + if (iwm_mimo_enabled(sc)) { if (in->in_ni.ni_rxmcs[1] != 0) { add_sta_cmd.station_flags |= htole32(IWM_STA_FLG_MIMO_EN_MIMO2); @@ -6629,7 +6640,7 @@ iwm_run(struct iwm_softc *sc) /* Configure Rx chains for MIMO. */ if ((ic->ic_opmode == IEEE80211_M_MONITOR || (in->in_ni.ni_flags & IEEE80211_NODE_HT)) && - !sc->sc_nvm.sku_cap_mimo_disable) { + iwm_mimo_enabled(sc)) { err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 2, 2, IWM_FW_CTXT_ACTION_MODIFY, 0); if (err) { @@ -6755,7 +6766,7 @@ iwm_run_stop(struct iwm_softc *sc) /* Reset Tx chains in case MIMO was enabled. */ if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) && - !sc->sc_nvm.sku_cap_mimo_disable) { + iwm_mimo_enabled(sc)) { err = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1, IWM_FW_CTXT_ACTION_MODIFY, 0); if (err) { @@ -7726,6 +7737,9 @@ iwm_init(struct ifnet *ifp) iwm_stop(ifp); return err; } + + if (sc->sc_nvm.sku_cap_11n_enable) + iwm_setup_ht_rates(sc); ifq_clr_oactive(&ifp->if_snd); ifp->if_flags |= IFF_RUNNING; blob - 27973d758778cc772be15d5da7733763ae4e9141 blob + a6b7e6258f9ae1dcc3b5d2107b427123c2de8ae4 --- sys/dev/pci/if_iwx.c +++ sys/dev/pci/if_iwx.c @@ -294,6 +294,7 @@ int iwx_nvm_read_section(struct iwx_softc *, uint16_t, void iwx_init_channel_map(struct iwx_softc *, const uint16_t * const, const uint8_t *nvm_channels, int nchan); void iwx_setup_ht_rates(struct iwx_softc *); +int iwx_mimo_enabled(struct iwx_softc *); void iwx_htprot_task(void *); void iwx_update_htprot(struct ieee80211com *, struct ieee80211_node *); int iwx_ampdu_rx_start(struct ieee80211com *, struct ieee80211_node *, @@ -2709,6 +2710,15 @@ iwx_init_channel_map(struct iwx_softc *sc, const uint1 } } +int +iwx_mimo_enabled(struct iwx_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + return !sc->sc_nvm.sku_cap_mimo_disable && + (ic->ic_userflags & IEEE80211_F_NOMIMO) == 0; +} + void iwx_setup_ht_rates(struct iwx_softc *sc) { @@ -2718,9 +2728,10 @@ iwx_setup_ht_rates(struct iwx_softc *sc) /* TX is supported with the same MCS as RX. */ ic->ic_tx_mcs_set = IEEE80211_TX_MCS_SET_DEFINED; + memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs)); ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */ - if (sc->sc_nvm.sku_cap_mimo_disable) + if (!iwx_mimo_enabled(sc)) return; rx_ant = iwx_fw_valid_rx_ant(sc); @@ -5751,7 +5762,7 @@ iwx_run(struct iwx_softc *sc) /* Configure Rx chains for MIMO. */ if ((ic->ic_opmode == IEEE80211_M_MONITOR || (in->in_ni.ni_flags & IEEE80211_NODE_HT)) && - !sc->sc_nvm.sku_cap_mimo_disable) { + iwx_mimo_enabled(sc)) { err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 2, 2, IWX_FW_CTXT_ACTION_MODIFY, 0); if (err) { @@ -5872,7 +5883,7 @@ iwx_run_stop(struct iwx_softc *sc) /* Reset Tx chains in case MIMO was enabled. */ if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) && - !sc->sc_nvm.sku_cap_mimo_disable) { + iwx_mimo_enabled(sc)) { err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1, IWX_FW_CTXT_ACTION_MODIFY, 0); if (err) { @@ -6429,6 +6440,9 @@ iwx_init(struct ifnet *ifp) iwx_stop(ifp); return err; } + + if (sc->sc_nvm.sku_cap_11n_enable) + iwx_setup_ht_rates(sc); ifq_clr_oactive(&ifp->if_snd); ifp->if_flags |= IFF_RUNNING; blob - 94931282fe5196346946000597c4049c12b60fee blob + 84469693c72c712d7bc74764aa0dc926da1f4547 --- sys/net80211/ieee80211_ioctl.h +++ sys/net80211/ieee80211_ioctl.h @@ -412,7 +412,8 @@ struct ieee80211_nodereq_all { #define IEEE80211_F_NOBRIDGE 0x00000002 /* CONF: no internal bridging */ #define IEEE80211_F_HOSTAPMASK 0x00000003 #define IEEE80211_F_STAYAUTH 0x00000004 /* CONF: ignore deauth */ -#define IEEE80211_F_USERBITS "\20\01HIDENWID\02NOBRIDGE\03STAYAUTH" +#define IEEE80211_F_NOMIMO 0x00000008 /* CONF: disable MIMO */ +#define IEEE80211_F_USERBITS "\20\01HIDENWID\02NOBRIDGE\03STAYAUTH\04NOMIMO" struct ieee80211_flags { const char *f_name; @@ -422,7 +423,8 @@ struct ieee80211_flags { #define IEEE80211_FLAGS { \ { "hidenwid", IEEE80211_F_HIDENWID }, \ { "nobridge", IEEE80211_F_NOBRIDGE }, \ - { "stayauth", IEEE80211_F_STAYAUTH } \ + { "stayauth", IEEE80211_F_STAYAUTH }, \ + { "nomimo", IEEE80211_F_NOMIMO } \ } #define SIOCG80211FLAGS _IOWR('i', 216, struct ifreq)