Date: Thu, 28 Aug 2014 03:18:27 +0000 (UTC) From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r270738 - head/sys/dev/iwn Message-ID: <201408280318.s7S3IRKj061407@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: adrian Date: Thu Aug 28 03:18:27 2014 New Revision: 270738 URL: http://svnweb.freebsd.org/changeset/base/270738 Log: Fix antenna configuration, microcode version checks and rate selection in preparation for the 5300 3x3 NIC. During this particular adventure, I did indeed discover that a whole swath of things made little to no sense. Those included, and are fixed here: * A lot of the antenna configuration bits assume the NIC has two receive chains. That's blatantly untrue for NICs that don't. * There was some disconnect between the antenna configuration when forming a PLCP rate DWORD (which includes the transmit antenna configuration), separate to the link quality antenna configuration. So now there's helper functions to return which antenna configurations to use and those are used wherever an antenna config is required. * The 5300 does up to three stream TX/RX (so MCS0->23), however the link quality table has only 16 slots. This means all of the rate entries are .. well, dual-stream rates. If this is the case, the "last MIMO" parameter can't be 16 or it panics the firmware. Set it to 15. * .. and since yes it has 16 slots, it only would try retransmitting from MCS8->MCS23, which can be quite .. terrible. Hard-code the last two retry slots to be the lowest configured rate. * I noticed some transmit configuration command stuff is different based on firmware API version, so I lifted that code from Linux. * Add / augment some more logging to make it easier to capture this stuff. Now, 3x3 is still terrible because the link quality configuration is plainly not good enough. I'll have to think about that. However, the original goal of this - 3x3 operation on the Intel 5300 NIC - actually worked. There are also rate control bugs in the way this driver handles notifying the net80211 rate control code when AMPDU is enabled. It always steps the rate up to the maximum rate possible - and this eventually ends in much sadness. I'll fix that later. As a side note - 2GHz HT40 now works on all the NICs I have tested. As a second side note - this exposed some bad 3x3 behaviour in the ath(4) rate control code where it starts off at a 3-stream rate and doesn't downgrade quickly enough. This makes the initial dhcp exchange take a long time. I'll fix the ath(4) rate code to start at a low fixed 1x1 MCS rate and step up if everything works out. Tested: * Intel 2200 * Intel 2230 * Intel 5300 * Intel 5100 * Intel 6205 * Intel 100 TODO: * Test the other NICs more thoroughly! Thank you to Michael Kosarev <russiane39@gmail.com> for donating the Intel 5300 NIC and pestering me about it since last year to try and make it all work. Modified: head/sys/dev/iwn/if_iwn.c head/sys/dev/iwn/if_iwnreg.h head/sys/dev/iwn/if_iwnvar.h Modified: head/sys/dev/iwn/if_iwn.c ============================================================================== --- head/sys/dev/iwn/if_iwn.c Thu Aug 28 01:15:56 2014 (r270737) +++ head/sys/dev/iwn/if_iwn.c Thu Aug 28 03:18:27 2014 (r270738) @@ -393,6 +393,15 @@ iwn_probe(device_t dev) } static int +iwn_is_3stream_device(struct iwn_softc *sc) +{ + /* XXX for now only 5300, until the 5350 can be tested */ + if (sc->hw_type == IWN_HW_REV_TYPE_5300) + return (1); + return (0); +} + +static int iwn_attach(device_t dev) { struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); @@ -594,21 +603,16 @@ iwn_attach(device_t dev) ic->ic_txstream = sc->ntxchains; /* - * The NICs we currently support cap out at 2x2 support - * separate from the chains being used. - * - * This is a total hack to work around that until some - * per-device method is implemented to return the - * actual stream support. - * - * XXX Note: the 5350 is a 3x3 device; so we shouldn't - * cap this! But, anything that touches rates in the - * driver needs to be audited first before 3x3 is enabled. + * Some of the 3 antenna devices (ie, the 4965) only supports + * 2x2 operation. So correct the number of streams if + * it's not a 3-stream device. */ - if (ic->ic_rxstream > 2) - ic->ic_rxstream = 2; - if (ic->ic_txstream > 2) - ic->ic_txstream = 2; + if (! iwn_is_3stream_device(sc)) { + if (ic->ic_rxstream > 2) + ic->ic_rxstream = 2; + if (ic->ic_txstream > 2) + ic->ic_txstream = 2; + } ic->ic_htcaps = IEEE80211_HTCAP_SMPS_OFF /* SMPS mode disabled */ @@ -2633,6 +2637,52 @@ rate2plcp(int rate) return 0; } +static int +iwn_get_1stream_tx_antmask(struct iwn_softc *sc) +{ + + return IWN_LSB(sc->txchainmask); +} + +static int +iwn_get_2stream_tx_antmask(struct iwn_softc *sc) +{ + int tx; + + /* + * The '2 stream' setup is a bit .. odd. + * + * For NICs that support only 1 antenna, default to IWN_ANT_AB or + * the firmware panics (eg Intel 5100.) + * + * For NICs that support two antennas, we use ANT_AB. + * + * For NICs that support three antennas, we use the two that + * wasn't the default one. + * + * XXX TODO: if bluetooth (full concurrent) is enabled, restrict + * this to only one antenna. + */ + + /* Default - transmit on the other antennas */ + tx = (sc->txchainmask & ~IWN_LSB(sc->txchainmask)); + + /* Now, if it's zero, set it to IWN_ANT_AB, so to not panic firmware */ + if (tx == 0) + tx = IWN_ANT_AB; + + /* + * If the NIC is a two-stream TX NIC, configure the TX mask to + * the default chainmask + */ + else if (sc->ntxchains == 2) + tx = sc->txchainmask; + + return (tx); +} + + + /* * Calculate the required PLCP value from the given rate, * to the given node. @@ -2646,14 +2696,9 @@ iwn_rate_to_plcp(struct iwn_softc *sc, s { #define RV(v) ((v) & IEEE80211_RATE_VAL) struct ieee80211com *ic = ni->ni_ic; - uint8_t txant1, txant2; uint32_t plcp = 0; int ridx; - /* Use the first valid TX antenna. */ - txant1 = IWN_LSB(sc->txchainmask); - txant2 = IWN_LSB(sc->txchainmask & ~txant1); - /* * If it's an MCS rate, let's set the plcp correctly * and set the relevant flags based on the node config. @@ -2685,15 +2730,15 @@ iwn_rate_to_plcp(struct iwn_softc *sc, s } /* - * If it's a two stream rate, enable TX on both - * antennas. - * - * XXX three stream rates? + * Ensure the selected rate matches the link quality + * table entries being used. */ - if (rate > 0x87) - plcp |= IWN_RFLAG_ANT(txant1 | txant2); + if (rate > 0x8f) + plcp |= IWN_RFLAG_ANT(sc->txchainmask); + else if (rate > 0x87) + plcp |= IWN_RFLAG_ANT(iwn_get_2stream_tx_antmask(sc)); else - plcp |= IWN_RFLAG_ANT(txant1); + plcp |= IWN_RFLAG_ANT(iwn_get_1stream_tx_antmask(sc)); } else { /* * Set the initial PLCP - fine for both @@ -2715,7 +2760,8 @@ iwn_rate_to_plcp(struct iwn_softc *sc, s plcp |= IWN_RFLAG_CCK; /* Set antenna configuration */ - plcp |= IWN_RFLAG_ANT(txant1); + /* XXX TODO: is this the right antenna to use for legacy? */ + plcp |= IWN_RFLAG_ANT(iwn_get_1stream_tx_antmask(sc)); } DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n", @@ -3047,8 +3093,9 @@ iwn_rx_compressed_ba(struct iwn_softc *s uint16_t ssn; uint8_t tid; int ackfailcnt = 0, i, lastidx, qid, *res, shift; + int tx_ok = 0, tx_err = 0; - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s begin\n", __func__); bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); @@ -3108,17 +3155,19 @@ iwn_rx_compressed_ba(struct iwn_softc *s for (i = 0; bitmap; i++) { if ((bitmap & 1) == 0) { ifp->if_oerrors++; + tx_err ++; ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); } else { ifp->if_opackets++; + tx_ok ++; ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); } bitmap >>= 1; } - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s: end; %d ok; %d err\n",__func__, tx_ok, tx_err); } @@ -4441,12 +4490,13 @@ iwn_tx_data(struct iwn_softc *sc, struct data->ni = ni; DPRINTF(sc, IWN_DEBUG_XMIT, - "%s: qid %d idx %d len %d nsegs %d rate %04x plcp 0x%08x\n", + "%s: qid %d idx %d len %d nsegs %d flags 0x%08x rate 0x%04x plcp 0x%08x\n", __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs, + flags, rate, tx->rate); @@ -4697,7 +4747,7 @@ iwn_raw_xmit(struct ieee80211_node *ni, struct iwn_softc *sc = ifp->if_softc; int error = 0; - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + DPRINTF(sc, IWN_DEBUG_XMIT | IWN_DEBUG_TRACE, "->%s begin\n", __func__); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ieee80211_free_node(ni); @@ -4728,7 +4778,7 @@ iwn_raw_xmit(struct ieee80211_node *ni, IWN_UNLOCK(sc); - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s: end\n",__func__); return error; } @@ -4752,6 +4802,8 @@ iwn_start_locked(struct ifnet *ifp) IWN_LOCK_ASSERT(sc); + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (ifp->if_drv_flags & IFF_DRV_OACTIVE)) return; @@ -4772,6 +4824,8 @@ iwn_start_locked(struct ifnet *ifp) } sc->sc_tx_timer = 5; } + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: done\n", __func__); } static void @@ -4974,49 +5028,15 @@ iwn_set_link_quality(struct iwn_softc *s struct iwn_node *wn = (void *)ni; struct ieee80211_rateset *rs; struct iwn_cmd_link_quality linkq; - uint8_t txant; int i, rate, txrate; int is_11n; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); - /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txchainmask); - memset(&linkq, 0, sizeof linkq); linkq.id = wn->id; - linkq.antmsk_1stream = txant; - - /* - * The '2 stream' setup is a bit .. odd. - * - * For NICs that support only 1 antenna, default to IWN_ANT_AB or - * the firmware panics (eg Intel 5100.) - * - * For NICs that support two antennas, we use ANT_AB. - * - * For NICs that support three antennas, we use the two that - * wasn't the default one. - * - * XXX TODO: if bluetooth (full concurrent) is enabled, restrict - * this to only one antenna. - */ - - /* So - if there's no secondary antenna, assume IWN_ANT_AB */ - - /* Default - transmit on the other antennas */ - linkq.antmsk_2stream = (sc->txchainmask & ~IWN_LSB(sc->txchainmask)); - - /* Now, if it's zero, set it to IWN_ANT_AB, so to not panic firmware */ - if (linkq.antmsk_2stream == 0) - linkq.antmsk_2stream = IWN_ANT_AB; - - /* - * If the NIC is a two-stream TX NIC, configure the TX mask to - * the default chainmask - */ - else if (sc->ntxchains == 2) - linkq.antmsk_2stream = sc->txchainmask; + linkq.antmsk_1stream = iwn_get_1stream_tx_antmask(sc); + linkq.antmsk_2stream = iwn_get_2stream_tx_antmask(sc); linkq.ampdu_max = 32; /* XXX negotiated? */ linkq.ampdu_threshold = 3; @@ -5053,21 +5073,28 @@ iwn_set_link_quality(struct iwn_softc *s for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { uint32_t plcp; + /* + * XXX TODO: ensure the last two slots are the two lowest + * rate entries, just for now. + */ + if (i == 14 || i == 15) + txrate = 0; + if (is_11n) rate = IEEE80211_RATE_MCS | rs->rs_rates[txrate]; else rate = RV(rs->rs_rates[txrate]); + /* Do rate -> PLCP config mapping */ + plcp = iwn_rate_to_plcp(sc, ni, rate); + linkq.retry[i] = plcp; DPRINTF(sc, IWN_DEBUG_XMIT, - "%s: i=%d, txrate=%d, rate=0x%02x\n", + "%s: i=%d, txrate=%d, rate=0x%02x, plcp=0x%08x\n", __func__, i, txrate, - rate); - - /* Do rate -> PLCP config mapping */ - plcp = iwn_rate_to_plcp(sc, ni, rate); - linkq.retry[i] = plcp; + rate, + le32toh(plcp)); /* * The mimo field is an index into the table which @@ -5088,6 +5115,15 @@ iwn_set_link_quality(struct iwn_softc *s if (txrate > 0) txrate--; } + /* + * If we reached the end of the list and indeed we hit + * all MIMO rates (eg 5300 doing MCS23-15) then yes, + * set mimo to 15. Setting it to 16 panics the firmware. + */ + if (linkq.mimo > 15) + linkq.mimo = 15; + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: mimo = %d\n", __func__, linkq.mimo); DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); @@ -5125,13 +5161,14 @@ iwn_add_broadcast_node(struct iwn_softc memset(&linkq, 0, sizeof linkq); linkq.id = sc->broadcast_id; - linkq.antmsk_1stream = txant; - linkq.antmsk_2stream = IWN_ANT_AB; + linkq.antmsk_1stream = iwn_get_1stream_tx_antmask(sc); + linkq.antmsk_2stream = iwn_get_2stream_tx_antmask(sc); linkq.ampdu_max = 64; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ /* Use lowest mandatory bit-rate. */ + /* XXX rate table lookup? */ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) linkq.retry[0] = htole32(0xd); else @@ -5438,6 +5475,7 @@ iwn5000_set_txpower(struct iwn_softc *sc int async) { struct iwn5000_cmd_txpower cmd; + int cmdid; DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__); @@ -5449,8 +5487,15 @@ iwn5000_set_txpower(struct iwn_softc *sc cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM; /* 16 dBm */ cmd.flags = IWN5000_TXPOWER_NO_CLOSED; cmd.srv_limit = IWN5000_TXPOWER_AUTO; - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__); - return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT, + "%s: setting TX power; rev=%d\n", + __func__, + IWN_UCODE_API(sc->ucode_rev)); + if (IWN_UCODE_API(sc->ucode_rev) == 1) + cmdid = IWN_CMD_TXPOWER_DBM_V1; + else + cmdid = IWN_CMD_TXPOWER_DBM; + return iwn_cmd(sc, cmdid, &cmd, sizeof cmd, async); } /* @@ -5650,7 +5695,7 @@ iwn_collect_noise(struct iwn_softc *sc, for (i = 0; i < 3; i++) if (val - calib->rssi[i] > 15 * 20) sc->chainmask &= ~(1 << i); - DPRINTF(sc, IWN_DEBUG_CALIBRATE, + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT, "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n", __func__, sc->rxchainmask, sc->chainmask); @@ -5775,7 +5820,7 @@ iwn5000_set_gains(struct iwn_softc *sc) cmd.gain[i - 1] |= 1 << 2; /* sign bit */ } } - DPRINTF(sc, IWN_DEBUG_CALIBRATE, + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT, "setting differential gains Ant B/C: %x/%x (%x)\n", cmd.gain[0], cmd.gain[1], sc->chainmask); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); @@ -6309,9 +6354,10 @@ iwn_config(struct iwn_softc *sc) } /* Configure valid TX chains for >=5000 Series. */ - if (sc->hw_type != IWN_HW_REV_TYPE_4965) { + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + IWN_UCODE_API(sc->ucode_rev) > 1) { txmask = htole32(sc->txchainmask); - DPRINTF(sc, IWN_DEBUG_RESET, + DPRINTF(sc, IWN_DEBUG_RESET | IWN_DEBUG_XMIT, "%s: configuring valid TX chains 0x%x\n", __func__, txmask); error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, sizeof txmask, 0); @@ -6367,11 +6413,24 @@ iwn_config(struct iwn_softc *sc) sc->rxon->ht_single_mask = 0xff; sc->rxon->ht_dual_mask = 0xff; sc->rxon->ht_triple_mask = 0xff; + /* + * In active association mode, ensure that + * all the receive chains are enabled. + * + * Since we're not yet doing SMPS, don't allow the + * number of idle RX chains to be less than the active + * number. + */ rxchain = IWN_RXCHAIN_VALID(sc->rxchainmask) | - IWN_RXCHAIN_MIMO_COUNT(2) | - IWN_RXCHAIN_IDLE_COUNT(2); + IWN_RXCHAIN_MIMO_COUNT(sc->nrxchains) | + IWN_RXCHAIN_IDLE_COUNT(sc->nrxchains); sc->rxon->rxchain = htole16(rxchain); + DPRINTF(sc, IWN_DEBUG_RESET | IWN_DEBUG_XMIT, + "%s: rxchainmask=0x%x, nrxchains=%d\n", + __func__, + sc->rxchainmask, + sc->nrxchains); DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); if (sc->sc_is_scanning) device_printf(sc->sc_dev, @@ -7806,6 +7865,8 @@ iwn_read_firmware_leg(struct iwn_softc * ptr = (const uint32_t *)fw->data; rev = le32toh(*ptr++); + sc->ucode_rev = rev; + /* Check firmware API version. */ if (IWN_FW_API(rev) <= 1) { device_printf(sc->sc_dev, @@ -7871,6 +7932,7 @@ iwn_read_firmware_tlv(struct iwn_softc * } DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr, le32toh(hdr->build)); + sc->ucode_rev = le32toh(hdr->rev); /* * Select the closest supported alternative that is less than @@ -8018,6 +8080,8 @@ iwn_read_firmware(struct iwn_softc *sc) return error; } + device_printf(sc->sc_dev, "%s: ucode rev=0x%08x\n", __func__, sc->ucode_rev); + /* Make sure text and data sections fit in hardware memory. */ if (fw->main.textsz > sc->fw_text_maxsz || fw->main.datasz > sc->fw_data_maxsz || Modified: head/sys/dev/iwn/if_iwnreg.h ============================================================================== --- head/sys/dev/iwn/if_iwnreg.h Thu Aug 28 01:15:56 2014 (r270737) +++ head/sys/dev/iwn/if_iwnreg.h Thu Aug 28 03:18:27 2014 (r270738) @@ -489,6 +489,7 @@ struct iwn_tx_cmd { #define IWN_CMD_TXPOWER_DBM 149 #define IWN_CMD_TXPOWER 151 #define IWN5000_CMD_TX_ANT_CONFIG 152 +#define IWN_CMD_TXPOWER_DBM_V1 152 #define IWN_CMD_BT_COEX 155 #define IWN_CMD_GET_STATISTICS 156 #define IWN_CMD_SET_CRITICAL_TEMP 164 Modified: head/sys/dev/iwn/if_iwnvar.h ============================================================================== --- head/sys/dev/iwn/if_iwnvar.h Thu Aug 28 01:15:56 2014 (r270737) +++ head/sys/dev/iwn/if_iwnvar.h Thu Aug 28 03:18:27 2014 (r270738) @@ -414,6 +414,9 @@ struct iwn_softc { /* For specific params */ const struct iwn_base_params *base_params; + +#define IWN_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) + uint32_t ucode_rev; }; #define IWN_LOCK_INIT(_sc) \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201408280318.s7S3IRKj061407>