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)



Reply via email to