summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2016-01-04 13:54:20 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2016-01-04 13:54:20 +0000
commitf8af0383a634bb1024f21acbbd6f55f55daa220d (patch)
treef19f6ec6027f994a3e0c28d05f5a9799760a7722
parent384f887650b934216a7e97e4492109e729232baf (diff)
Add initial support for 11n mode to the iwn(4) driver.
Only MCS 0 to 7 are supported for now. Tested by many. There might still be outstanding issues but they now appear to be rare. I'm putting this in so that additional fixes can be developed and tested more easily if required. This diff is already quite large for one commit. ok deraadt
-rw-r--r--sys/dev/pci/if_iwn.c322
-rw-r--r--sys/dev/pci/if_iwnreg.h61
-rw-r--r--sys/dev/pci/if_iwnvar.h4
3 files changed, 305 insertions, 82 deletions
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c
index 1d295c5104a..91b7195a87f 100644
--- a/sys/dev/pci/if_iwn.c
+++ b/sys/dev/pci/if_iwn.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwn.c,v 1.148 2015/11/25 03:09:59 dlg Exp $ */
+/* $OpenBSD: if_iwn.c,v 1.149 2016/01/04 13:54:19 stsp Exp $ */
/*-
* Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -148,7 +148,7 @@ int iwn_newstate(struct ieee80211com *, enum ieee80211_state, int);
void iwn_iter_func(void *, struct ieee80211_node *);
void iwn_calib_timeout(void *);
int iwn_ccmp_decap(struct iwn_softc *, struct mbuf *,
- struct ieee80211_key *);
+ struct ieee80211_node *);
void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *,
struct iwn_rx_data *);
void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *,
@@ -189,7 +189,7 @@ int iwn5000_add_node(struct iwn_softc *, struct iwn_node_info *,
int);
int iwn_set_link_quality(struct iwn_softc *,
struct ieee80211_node *);
-int iwn_add_broadcast_node(struct iwn_softc *, int);
+int iwn_add_broadcast_node(struct iwn_softc *, int, int);
void iwn_updateedca(struct ieee80211com *);
void iwn_set_led(struct iwn_softc *, uint8_t, uint8_t, uint8_t);
int iwn_set_critical_temp(struct iwn_softc *);
@@ -280,7 +280,7 @@ void iwn_stop(struct ifnet *, int);
#ifdef IWN_DEBUG
#define DPRINTF(x) do { if (iwn_debug > 0) printf x; } while (0)
#define DPRINTFN(n, x) do { if (iwn_debug >= (n)) printf x; } while (0)
-int iwn_debug = 0;
+int iwn_debug = 1;
#else
#define DPRINTF(x)
#define DPRINTFN(n, x)
@@ -458,6 +458,15 @@ iwn_attach(struct device *parent, struct device *self, void *aux)
IEEE80211_C_PMGT; /* power saving supported */
#ifndef IEEE80211_NO_HT
+ /* No optional HT features supported for now, */
+ ic->ic_htcaps = 0;
+ ic->ic_htxcaps = 0;
+ ic->ic_txbfcaps = 0;
+ ic->ic_aselcaps = 0;
+#endif
+
+#ifdef notyet
+#ifndef IEEE80211_NO_HT
if (sc->sc_flags & IWN_FLAG_HAS_11N) {
/* Set HT capabilities. */
ic->ic_htcaps =
@@ -475,6 +484,7 @@ iwn_attach(struct device *parent, struct device *self, void *aux)
ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_DIS;
}
#endif /* !IEEE80211_NO_HT */
+#endif /* notyet */
/* Set supported legacy rates. */
ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
@@ -487,10 +497,12 @@ iwn_attach(struct device *parent, struct device *self, void *aux)
if (sc->sc_flags & IWN_FLAG_HAS_11N) {
/* Set supported HT rates. */
ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */
+#ifdef notyet
if (sc->nrxchains > 1)
- ic->ic_sup_mcs[1] = 0xff; /* MCS 7-15 */
+ ic->ic_sup_mcs[1] = 0xff; /* MCS 8-15 */
if (sc->nrxchains > 2)
ic->ic_sup_mcs[2] = 0xff; /* MCS 16-23 */
+#endif
}
#endif
@@ -515,9 +527,11 @@ iwn_attach(struct device *parent, struct device *self, void *aux)
#ifndef IEEE80211_NO_HT
ic->ic_ampdu_rx_start = iwn_ampdu_rx_start;
ic->ic_ampdu_rx_stop = iwn_ampdu_rx_stop;
+#ifdef notyet
ic->ic_ampdu_tx_start = iwn_ampdu_tx_start;
ic->ic_ampdu_tx_stop = iwn_ampdu_tx_stop;
#endif
+#endif
/* Override 802.11 state transition machine. */
sc->sc_newstate = ic->ic_newstate;
@@ -1635,6 +1649,11 @@ iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr)
/* Save maximum allowed TX power for this channel. */
sc->maxpwr[chan] = channels[i].maxpwr;
+#ifndef IEEE80211_NO_HT
+ if (sc->sc_flags & IWN_FLAG_HAS_11N)
+ ic->ic_channels[chan].ic_flags |= IEEE80211_CHAN_HT;
+#endif
+
DPRINTF(("adding chan %d flags=0x%x maxpwr=%d\n",
chan, channels[i].flags, sc->maxpwr[chan]));
}
@@ -1693,13 +1712,18 @@ iwn_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
ieee80211_amrr_node_init(&sc->amrr, &wn->amn);
/* Start at lowest available bit-rate, AMRR will raise. */
ni->ni_txrate = 0;
+#ifndef IEEE80211_NO_HT
+ ni->ni_txmcs = 0;
+#endif
for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL;
/* Map 802.11 rate to HW rate index. */
- for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++)
- if (iwn_rates[ridx].rate == rate)
+ for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
+ if (iwn_rates[ridx].plcp != IWN_PLCP_INVALID &&
+ iwn_rates[ridx].rate == rate)
break;
+ }
wn->ridx[i] = ridx;
}
}
@@ -1716,12 +1740,17 @@ iwn_media_change(struct ifnet *ifp)
if (error != ENETRESET)
return error;
+#ifndef IEEE80211_NO_HT
+ if (ic->ic_fixed_mcs != -1)
+ sc->fixed_ridx = iwn_mcs2ridx[ic->ic_fixed_mcs];
+#endif
if (ic->ic_fixed_rate != -1) {
rate = ic->ic_sup_rates[ic->ic_curmode].
rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
/* Map 802.11 rate to HW rate index. */
for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++)
- if (iwn_rates[ridx].rate == rate)
+ if (iwn_rates[ridx].plcp != IWN_PLCP_INVALID &&
+ iwn_rates[ridx].rate == rate)
break;
sc->fixed_ridx = ridx;
}
@@ -1816,7 +1845,7 @@ iwn_calib_timeout(void *arg)
if (++sc->calib_cnt >= 120) {
uint32_t flags = 0;
- DPRINTF(("sending request for statistics\n"));
+ DPRINTFN(2, ("sending request for statistics\n"));
(void)iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags,
sizeof flags, 1);
sc->calib_cnt = 0;
@@ -1828,13 +1857,15 @@ iwn_calib_timeout(void *arg)
}
int
-iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_key *k)
+iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_key *k = &ni->ni_pairwise_key;
struct ieee80211_frame *wh;
uint64_t pn, *prsc;
uint8_t *ivp;
uint8_t tid;
- int hdrlen;
+ int hdrlen, hasqos;
wh = mtod(m, struct ieee80211_frame *);
hdrlen = ieee80211_get_hdrlen(wh);
@@ -1845,8 +1876,8 @@ iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_key *k)
DPRINTF(("CCMP decap ExtIV not set\n"));
return 1;
}
- tid = ieee80211_has_qos(wh) ?
- ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
+ hasqos = ieee80211_has_qos(wh);
+ tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
prsc = &k->k_rsc[tid];
/* Extract the 48-bit PN from the CCMP header. */
@@ -1857,12 +1888,38 @@ iwn_ccmp_decap(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_key *k)
(uint64_t)ivp[6] << 32 |
(uint64_t)ivp[7] << 40;
if (pn <= *prsc) {
- /*
- * Not necessarily a replayed frame since we did not check
- * the sequence number of the 802.11 header yet.
- */
- DPRINTF(("CCMP replayed\n"));
- return 1;
+ if (hasqos && (sc->last_rx_valid & IWN_LAST_RX_AMPDU)) {
+ /*
+ * This is an A-MPDU subframe.
+ * Such frames may be received out of order due to
+ * legitimate retransmissions of failed subframes
+ * in previous A-MPDUs. Duplicates will be handled
+ * in ieee80211_input() as part of A-MPDU reordering.
+ */
+ } else if (ieee80211_has_seq(wh)) {
+ /*
+ * Not necessarily a replayed frame since we did not
+ * check the sequence number of the 802.11 header yet.
+ */
+ int nrxseq, orxseq;
+
+ nrxseq = letoh16(*(u_int16_t *)wh->i_seq) >>
+ IEEE80211_SEQ_SEQ_SHIFT;
+ if (hasqos)
+ orxseq = ni->ni_qos_rxseqs[tid];
+ else
+ orxseq = ni->ni_rxseq;
+ if (nrxseq < orxseq) {
+ DPRINTF(("CCMP replayed (n=%d < o=%d)\n",
+ nrxseq, orxseq));
+ ic->ic_stats.is_ccmp_replays++;
+ return 1;
+ }
+ } else {
+ DPRINTF(("CCMP replayed\n"));
+ ic->ic_stats.is_ccmp_replays++;
+ return 1;
+ }
}
/* Update last seen packet number. */
*prsc = pn;
@@ -1892,7 +1949,13 @@ iwn_rx_phy(struct iwn_softc *sc, struct iwn_rx_desc *desc,
/* Save RX statistics, they will be used on MPDU_RX_DONE. */
memcpy(&sc->last_rx_stat, stat, sizeof (*stat));
- sc->last_rx_valid = 1;
+ sc->last_rx_valid = IWN_LAST_RX_VALID;
+ /*
+ * The firmware does not send separate RX_PHY
+ * notifications for A-MPDU subframes.
+ */
+ if (stat->flags & htole16(IWN_STAT_FLAG_AGG))
+ sc->last_rx_valid |= IWN_LAST_RX_AMPDU;
}
/*
@@ -1922,8 +1985,18 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
DPRINTF(("missing RX_PHY\n"));
return;
}
- sc->last_rx_valid = 0;
+ sc->last_rx_valid &= ~IWN_LAST_RX_VALID;
stat = &sc->last_rx_stat;
+ if ((sc->last_rx_valid & IWN_LAST_RX_AMPDU) &&
+ (stat->flags & htole16(IWN_STAT_FLAG_AGG)) == 0) {
+ DPRINTF(("missing RX_PHY (expecting A-MPDU)\n"));
+ return;
+ }
+ if ((sc->last_rx_valid & IWN_LAST_RX_AMPDU) == 0 &&
+ (stat->flags & htole16(IWN_STAT_FLAG_AGG))) {
+ DPRINTF(("missing RX_PHY (unexpected A-MPDU)\n"));
+ return;
+ }
} else
stat = (struct iwn_rx_stat *)(desc + 1);
@@ -2030,7 +2103,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc,
m_freem(m);
return;
}
- if (iwn_ccmp_decap(sc, m, &ni->ni_pairwise_key) != 0) {
+ if (iwn_ccmp_decap(sc, m, ni) != 0) {
ifp->if_ierrors++;
m_freem(m);
return;
@@ -2281,9 +2354,10 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt,
if (ackfailcnt > 0)
wn->amn.amn_retrycnt++;
- if (status != 1 && status != 2)
+ if (status != 1 && status != 2) {
+ DPRINTF(("%s: status=0x%x\n", __func__, status));
ifp->if_oerrors++;
- else
+ } else
ifp->if_opackets++;
/* Unmap and free mbuf. */
@@ -2397,7 +2471,7 @@ iwn_notif_intr(struct iwn_softc *sc)
* If more than 5 consecutive beacons are missed,
* reinitialize the sensitivity state machine.
*/
- DPRINTF(("beacons missed %d/%d\n",
+ DPRINTFN(2, ("beacons missed %d/%d\n",
letoh32(miss->consecutive), letoh32(miss->total)));
if (ic->ic_state == IEEE80211_S_RUN &&
letoh32(miss->consecutive) > 5)
@@ -2778,15 +2852,24 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
/* Choose a TX rate index. */
if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
- type != IEEE80211_FC0_TYPE_DATA) {
- ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ?
+ type != IEEE80211_FC0_TYPE_DATA)
+ ridx = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ?
IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
- } else if (ic->ic_fixed_rate != -1) {
+#ifndef IEEE80211_NO_HT
+ else if (ic->ic_fixed_mcs != -1)
ridx = sc->fixed_ridx;
- } else
- ridx = wn->ridx[ni->ni_txrate];
+#endif
+ else if (ic->ic_fixed_rate != -1)
+ ridx = sc->fixed_ridx;
+ else {
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ ridx = iwn_mcs2ridx[ni->ni_txmcs];
+ else
+#endif
+ ridx = wn->ridx[ni->ni_txrate];
+ }
rinfo = &iwn_rates[ridx];
-
#if NBPFILTER > 0
if (sc->sc_drvbpf != NULL) {
struct mbuf mb;
@@ -2795,7 +2878,15 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags);
- tap->wt_rate = rinfo->rate;
+#ifndef IEEE80211_NO_HT
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ type == IEEE80211_FC0_TYPE_DATA) {
+ /* XXX need a way to pass current MCS in 11n mode */
+ tap->wt_rate = 0;
+ } else
+#endif
+ tap->wt_rate = rinfo->rate;
tap->wt_hwqueue = ac;
if ((ic->ic_flags & IEEE80211_F_WEPON) &&
(wh->i_fc[1] & IEEE80211_FC1_PROTECTED))
@@ -2857,7 +2948,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
- /* NB: Group frames are sent using CCK in 802.11b/g. */
+ /* NB: Group frames are sent using CCK in 802.11b/g/n (2GHz). */
if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) {
flags |= IWN_TX_NEED_RTS;
} else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
@@ -2867,6 +2958,10 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
flags |= IWN_TX_NEED_RTS;
}
+#ifndef IEEE80211_NO_HT
+ else if (ni->ni_flags & IEEE80211_NODE_HT)
+ flags |= IWN_TX_NEED_RTS;
+#endif
if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) {
if (sc->hw_type != IWN_HW_REV_TYPE_4965) {
/* 5000 autoselects RTS/CTS or CTS-to-self. */
@@ -2911,8 +3006,22 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
tx->rts_ntries = 60;
tx->data_ntries = 15;
tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
- tx->plcp = rinfo->plcp;
- tx->rflags = rinfo->flags;
+
+#ifndef IEEE80211_NO_HT
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ tx->id != sc->broadcast_id)
+ tx->plcp = rinfo->ht_plcp;
+ else
+#endif
+ tx->plcp = rinfo->plcp;
+
+#ifndef IEEE80211_NO_HT
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ tx->id != sc->broadcast_id)
+ tx->rflags = rinfo->ht_flags;
+ else
+#endif
+ tx->rflags = rinfo->flags;
if (tx->id == sc->broadcast_id) {
/* Group or management frame. */
tx->linkq = 0;
@@ -2920,7 +3029,12 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
txant = IWN_LSB(sc->txchainmask);
tx->rflags |= IWN_RFLAG_ANT(txant);
} else {
- tx->linkq = ni->ni_rates.rs_nrates - ni->ni_txrate - 1;
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ tx->linkq = 7 - ni->ni_txmcs; /* XXX revisit for MIMO */
+ else
+#endif
+ tx->linkq = ni->ni_rates.rs_nrates - ni->ni_txrate - 1;
flags |= IWN_TX_LINKQ; /* enable MRR */
}
/* Set physical address of "scratch area". */
@@ -3308,17 +3422,45 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
linkq.ampdu_threshold = 3;
linkq.ampdu_limit = htole16(4000); /* 4ms */
- /* Start at highest available bit-rate. */
- txrate = rs->rs_nrates - 1;
- for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
- rinfo = &iwn_rates[wn->ridx[txrate]];
- linkq.retry[i].plcp = rinfo->plcp;
- linkq.retry[i].rflags = rinfo->flags;
- linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
- /* Next retry at immediate lower bit-rate. */
- if (txrate > 0)
- txrate--;
+#ifndef IEEE80211_NO_HT
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ /* Fill LQ table with MCS 7 - 0 (XXX revisit for MIMO) */
+ i = 0;
+ for (txrate = 7; txrate >= 0; txrate--) {
+ rinfo = &iwn_rates[iwn_mcs2ridx[txrate]];
+ linkq.retry[i].plcp = rinfo->ht_plcp;
+ linkq.retry[i].rflags = rinfo->ht_flags;
+
+ /* XXX set correct ant mask for MIMO rates here */
+ linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+
+ if (i++ >= IWN_MAX_TX_RETRIES)
+ break;
+ }
+ /* Fill the rest with MCS 0. */
+ rinfo = &iwn_rates[iwn_mcs2ridx[0]];
+ while (i < IWN_MAX_TX_RETRIES) {
+ linkq.retry[i].plcp = rinfo->ht_plcp;
+ linkq.retry[i].rflags = rinfo->ht_flags;
+ linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+ i++;
+ }
+ } else
+#endif
+ {
+ /* Start at highest available bit-rate. */
+ txrate = rs->rs_nrates - 1;
+ for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
+ rinfo = &iwn_rates[wn->ridx[txrate]];
+ linkq.retry[i].plcp = rinfo->plcp;
+ linkq.retry[i].rflags = rinfo->flags;
+ linkq.retry[i].rflags |= IWN_RFLAG_ANT(txant);
+ /* Next retry at immediate lower bit-rate. */
+ if (txrate > 0)
+ txrate--;
+ }
}
+
return iwn_cmd(sc, IWN_CMD_LINK_QUALITY, &linkq, sizeof linkq, 1);
}
@@ -3326,7 +3468,7 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni)
* Broadcast node is used to send group-addressed and management frames.
*/
int
-iwn_add_broadcast_node(struct iwn_softc *sc, int async)
+iwn_add_broadcast_node(struct iwn_softc *sc, int async, int ridx)
{
struct iwn_ops *ops = &sc->ops;
struct iwn_node_info node;
@@ -3354,8 +3496,7 @@ iwn_add_broadcast_node(struct iwn_softc *sc, int async)
linkq.ampdu_limit = htole16(4000); /* 4ms */
/* Use lowest mandatory bit-rate. */
- rinfo = (sc->sc_ic.ic_curmode != IEEE80211_MODE_11A) ?
- &iwn_rates[IWN_RIDX_CCK1] : &iwn_rates[IWN_RIDX_OFDM6];
+ rinfo = &iwn_rates[ridx];
linkq.retry[0].plcp = rinfo->plcp;
linkq.retry[0].rflags = rinfo->flags;
linkq.retry[0].rflags |= IWN_RFLAG_ANT(txant);
@@ -3564,8 +3705,10 @@ iwn4965_set_txpower(struct iwn_softc *sc, int async)
for (ridx = 0; ridx <= IWN_RIDX_MAX; ridx++) {
/* Convert dBm to half-dBm. */
maxchpwr = sc->maxpwr[chan] * 2;
- if ((ridx / 8) & 1)
+#ifdef notyet
+ if (ridx > iwn_mcs2ridx[7] && ridx < iwn_mcs2ridx[16])
maxchpwr -= 6; /* MIMO 2T: -3dB */
+#endif
pwr = maxpwr;
@@ -3584,7 +3727,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, int async)
pwr = maxchpwr;
idx = gain - (pwr - power) - tdiff - vdiff;
- if ((ridx / 8) & 1) /* MIMO */
+ if (ridx > iwn_mcs2ridx[7]) /* MIMO */
idx += (int32_t)letoh32(uc->atten[grp][c]);
if (cmd.band == 0)
@@ -3766,7 +3909,7 @@ iwn_init_sensitivity(struct iwn_softc *sc)
/* Request statistics at each beacon interval. */
flags = 0;
- DPRINTF(("sending request for statistics\n"));
+ DPRINTFN(2, ("sending request for statistics\n"));
return iwn_cmd(sc, IWN_CMD_GET_STATISTICS, &flags, sizeof flags, 1);
}
@@ -4277,7 +4420,7 @@ iwn_config(struct iwn_softc *sc)
struct ifnet *ifp = &ic->ic_if;
uint32_t txmask;
uint16_t rxchain;
- int error;
+ int error, ridx;
/* Set radio temperature sensor offset. */
if (sc->hw_type == IWN_HW_REV_TYPE_6005) {
@@ -4343,8 +4486,13 @@ iwn_config(struct iwn_softc *sc)
IEEE80211_ADDR_COPY(sc->rxon.wlap, ic->ic_myaddr);
sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan);
sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
- if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan))
+ if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) {
sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
+ if (ic->ic_flags & IEEE80211_F_USEPROT)
+ sc->rxon.flags |= htole32(IWN_RXON_TGG_PROT);
+ DPRINTF(("%s: 2ghz prot 0x%x\n", __func__,
+ le32toh(sc->rxon.flags)));
+ }
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
sc->rxon.mode = IWN_MODE_STA;
@@ -4376,7 +4524,9 @@ iwn_config(struct iwn_softc *sc)
return error;
}
- if ((error = iwn_add_broadcast_node(sc, 0)) != 0) {
+ ridx = (sc->sc_ic.ic_curmode == IEEE80211_MODE_11A) ?
+ IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+ if ((error = iwn_add_broadcast_node(sc, 0, ridx)) != 0) {
printf("%s: could not add broadcast node\n",
sc->sc_dev.dv_xname);
return error;
@@ -4664,14 +4814,19 @@ iwn_auth(struct iwn_softc *sc)
struct iwn_ops *ops = &sc->ops;
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni = ic->ic_bss;
- int error;
+ int error, ridx;
/* Update adapter configuration. */
IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid);
sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan);
sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF);
- if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+ if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) {
sc->rxon.flags |= htole32(IWN_RXON_AUTO | IWN_RXON_24GHZ);
+ if (ic->ic_flags & IEEE80211_F_USEPROT)
+ sc->rxon.flags |= htole32(IWN_RXON_TGG_PROT);
+ DPRINTF(("%s: 2ghz prot 0x%x\n", __func__,
+ le32toh(sc->rxon.flags)));
+ }
if (ic->ic_flags & IEEE80211_F_SHSLOT)
sc->rxon.flags |= htole32(IWN_RXON_SHSLOT);
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
@@ -4685,12 +4840,13 @@ iwn_auth(struct iwn_softc *sc)
sc->rxon.cck_mask = 0x03;
sc->rxon.ofdm_mask = 0;
break;
- default: /* Assume 802.11b/g. */
+ default: /* Assume 802.11b/g/n. */
sc->rxon.cck_mask = 0x0f;
sc->rxon.ofdm_mask = 0x15;
}
- DPRINTF(("rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon.chan,
- sc->rxon.flags, sc->rxon.cck_mask, sc->rxon.ofdm_mask));
+ DPRINTF(("%s: rxon chan %d flags %x cck %x ofdm %x\n", __func__,
+ sc->rxon.chan, le32toh(sc->rxon.flags), sc->rxon.cck_mask,
+ sc->rxon.ofdm_mask));
error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
if (error != 0) {
printf("%s: RXON command failed\n", sc->sc_dev.dv_xname);
@@ -4706,7 +4862,9 @@ iwn_auth(struct iwn_softc *sc)
* Reconfiguring RXON clears the firmware nodes table so we must
* add the broadcast node again.
*/
- if ((error = iwn_add_broadcast_node(sc, 1)) != 0) {
+ ridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
+ IWN_RIDX_OFDM6 : IWN_RIDX_CCK1;
+ if ((error = iwn_add_broadcast_node(sc, 1, ridx)) != 0) {
printf("%s: could not add broadcast node\n",
sc->sc_dev.dv_xname);
return error;
@@ -4742,7 +4900,40 @@ iwn_run(struct iwn_softc *sc)
if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE);
sc->rxon.filter |= htole32(IWN_FILTER_BSS);
- DPRINTF(("rxon chan %d flags %x\n", sc->rxon.chan, sc->rxon.flags));
+#ifndef IEEE80211_NO_HT
+ /* HT is negotiated when associating. */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ enum ieee80211_htprot htprot =
+ (ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK);
+ DPRINTF(("%s: htprot = %d\n", __func__, htprot));
+ sc->rxon.flags |= htole32(IWN_RXON_HT_PROTMODE(htprot));
+ } else
+ sc->rxon.flags &= ~htole32(IWN_RXON_HT_PROTMODE(3));
+#endif
+ if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) {
+ /* 11a or 11n 5GHz */
+ sc->rxon.cck_mask = 0;
+ sc->rxon.ofdm_mask = 0x15;
+#ifndef IEEE80211_NO_HT
+ } else if (ni->ni_flags & IEEE80211_NODE_HT) {
+ /* 11n 2GHz */
+ sc->rxon.cck_mask = 0x0f;
+ sc->rxon.ofdm_mask = 0x15;
+#endif
+ } else {
+ if (ni->ni_rates.rs_nrates == 4) {
+ /* 11b */
+ sc->rxon.cck_mask = 0x03;
+ sc->rxon.ofdm_mask = 0;
+ } else {
+ /* assume 11g */
+ sc->rxon.cck_mask = 0x0f;
+ sc->rxon.ofdm_mask = 0x15;
+ }
+ }
+ DPRINTF(("%s: rxon chan %d flags %x cck %x ofdm %x\n", __func__,
+ sc->rxon.chan, le32toh(sc->rxon.flags), sc->rxon.cck_mask,
+ sc->rxon.ofdm_mask));
error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, sc->rxonsz, 1);
if (error != 0) {
printf("%s: could not update configuration\n",
@@ -4879,8 +5070,9 @@ iwn_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
node.flags = IWN_FLAG_SET_ADDBA;
node.addba_tid = tid;
node.addba_ssn = htole16(ba->ba_winstart);
- DPRINTFN(2, ("ADDBA RA=%d TID=%d SSN=%d\n", wn->id, tid,
+ DPRINTF(("ADDBA RA=%d TID=%d SSN=%d\n", wn->id, tid,
ba->ba_winstart));
+ /* XXX async command, so firmware may still fail to add BA agreement */
return ops->add_node(sc, &node, 1);
}
@@ -4902,7 +5094,7 @@ iwn_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
node.control = IWN_NODE_UPDATE;
node.flags = IWN_FLAG_SET_DELBA;
node.delba_tid = tid;
- DPRINTFN(2, ("DELBA RA=%d TID=%d\n", wn->id, tid));
+ DPRINTF(("DELBA RA=%d TID=%d\n", wn->id, tid));
(void)ops->add_node(sc, &node, 1);
}
@@ -6007,7 +6199,7 @@ iwn_hw_init(struct iwn_softc *sc)
IWN_FH_RX_CONFIG_IGN_RXF_EMPTY | /* HW bug workaround */
IWN_FH_RX_CONFIG_IRQ_DST_HOST |
IWN_FH_RX_CONFIG_SINGLE_FRAME |
- IWN_FH_RX_CONFIG_RB_TIMEOUT(0) |
+ IWN_FH_RX_CONFIG_RB_TIMEOUT(0x11) | /* about 1/2 msec */
IWN_FH_RX_CONFIG_NRBD(IWN_RX_RING_COUNT_LOG));
iwn_nic_unlock(sc);
IWN_WRITE(sc, IWN_FH_RX_WPTR, (IWN_RX_RING_COUNT - 1) & ~7);
diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h
index 14cb12ea42f..cd0018a7fdd 100644
--- a/sys/dev/pci/if_iwnreg.h
+++ b/sys/dev/pci/if_iwnreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwnreg.h,v 1.49 2014/09/09 18:56:24 sthen Exp $ */
+/* $OpenBSD: if_iwnreg.h,v 1.50 2016/01/04 13:54:19 stsp Exp $ */
/*-
* Copyright (c) 2007, 2008
@@ -301,7 +301,7 @@
#define IWN_FH_RX_CONFIG_RB_SIZE_8K (1U << 16)
#define IWN_FH_RX_CONFIG_SINGLE_FRAME (1U << 15)
#define IWN_FH_RX_CONFIG_IRQ_DST_HOST (1U << 12)
-#define IWN_FH_RX_CONFIG_RB_TIMEOUT(x) ((x) << 4)
+#define IWN_FH_RX_CONFIG_RB_TIMEOUT(x) ((x) << 4) /* units 32 usec */
#define IWN_FH_RX_CONFIG_IGN_RXF_EMPTY (1U << 2)
/* Possible flags for register IWN_FH_TX_CONFIG. */
@@ -383,6 +383,12 @@ struct iwn_rx_status {
struct iwn_rx_desc {
uint32_t len;
+#define IWN_RX_DESC_LEN_MASK 0x00003fff
+#define IWN_RX_DESC_LEN_SHIFT 0
+#define IWN_RX_DESC_FLAG_FLUSH_RB_REQ 0x80000000
+#define IWN_RX_DESC_FLAG_IGNORE_TC 0x40000000
+#define IWN_RX_DESC_FLAG_FAST_IRQ 0x20000000
+
uint8_t type;
#define IWN_UC_READY 1
#define IWN_ADD_NODE_DONE 24
@@ -400,7 +406,7 @@ struct iwn_rx_desc {
#define IWN_RX_DONE 195
#define IWN_RX_COMPRESSED_BA 197
- uint8_t flags;
+ uint8_t flags; /* XXX iwlwifi calls this "group_id" */
uint8_t idx;
uint8_t qid;
} __packed;
@@ -488,12 +494,17 @@ struct iwn_rxon {
#define IWN_RXON_24GHZ (1 << 0)
#define IWN_RXON_CCK (1 << 1)
#define IWN_RXON_AUTO (1 << 2)
+#define IWN_RXON_TGG_PROT (1 << 3)
#define IWN_RXON_SHSLOT (1 << 4)
#define IWN_RXON_SHPREAMBLE (1 << 5)
#define IWN_RXON_NODIVERSITY (1 << 7)
#define IWN_RXON_ANTENNA_A (1 << 8)
#define IWN_RXON_ANTENNA_B (1 << 9)
#define IWN_RXON_TSF (1 << 15)
+#define IWN_RXON_HT_HT40MINUS (1 << 22)
+#define IWN_RXON_HT_PROTMODE(x) ((x) << 23) /* 2 bits */
+#define IWN_RXON_HT_CHANMODE_PURE40 (1 << 25)
+#define IWN_RXON_HT_CHANMODE_MIXED2040 (2 << 25)
#define IWN_RXON_CTS_TO_SELF (1 << 30)
uint32_t filter;
@@ -630,7 +641,12 @@ struct iwn4965_node_info {
uint32_t reserved7;
} __packed;
+#define IWN_RFLAG_MCS (1 << 0)
#define IWN_RFLAG_CCK (1 << 1)
+#define IWN_RFLAG_GREENFIELD (1 << 2)
+#define IWN_RFLAG_HT40 (1 << 3)
+#define IWN_RFLAG_DUPLICATE (1 << 4)
+#define IWN_RFLAG_SGI (1 << 5)
#define IWN_RFLAG_ANT(x) ((x) << 6)
/* Structure for command IWN_CMD_TX_DATA. */
@@ -1222,7 +1238,12 @@ struct iwn_rx_stat {
uint64_t tstamp;
uint32_t beacon;
uint16_t flags;
+#define IWN_STAT_FLAG_24GHZ (1 << 0)
+#define IWN_STAT_FLAG_MOD_CCK (1 << 1)
#define IWN_STAT_FLAG_SHPREAMBLE (1 << 2)
+#define IWN_STAT_FLAG_NARROW_BAND (1 << 3)
+#define IWN_STAT_FLAG_ANT(x) ((x) << 4) /* 3 bits */
+#define IWN_STAT_FLAG_AGG (1 << 7)
uint16_t chan;
uint8_t phybuf[32];
@@ -1659,26 +1680,34 @@ static const struct iwn_chan_band {
#define IWN_RIDX_CCK1 0
#define IWN_RIDX_OFDM6 4
+#define IWN_PLCP_INVALID 0xff
+
static const struct iwn_rate {
uint8_t rate;
uint8_t plcp;
uint8_t flags;
+ uint8_t ht_plcp;
+ uint8_t ht_flags;
} iwn_rates[IWN_RIDX_MAX + 1] = {
- { 2, 10, IWN_RFLAG_CCK },
- { 4, 20, IWN_RFLAG_CCK },
- { 11, 55, IWN_RFLAG_CCK },
- { 22, 110, IWN_RFLAG_CCK },
- { 12, 0xd, 0 },
- { 18, 0xf, 0 },
- { 24, 0x5, 0 },
- { 36, 0x7, 0 },
- { 48, 0x9, 0 },
- { 72, 0xb, 0 },
- { 96, 0x1, 0 },
- { 108, 0x3, 0 },
- { 120, 0x3, 0 }
+ /* Legacy */ /* HT */
+ { 2, 10, IWN_RFLAG_CCK, IWN_PLCP_INVALID, 0 },
+ { 4, 20, IWN_RFLAG_CCK, IWN_PLCP_INVALID, 0 },
+ { 11, 55, IWN_RFLAG_CCK, IWN_PLCP_INVALID, 0 },
+ { 22, 110, IWN_RFLAG_CCK, IWN_PLCP_INVALID, 0 },
+ { 12, 0xd, 0, 0, IWN_RFLAG_MCS },
+ { 18, 0xf, 0, IWN_PLCP_INVALID, 0 },
+ { 24, 0x5, 0, 1, IWN_RFLAG_MCS },
+ { 36, 0x7, 0, 2, IWN_RFLAG_MCS, },
+ { 48, 0x9, 0, 3, IWN_RFLAG_MCS, },
+ { 72, 0xb, 0, 4, IWN_RFLAG_MCS, },
+ { 96, 0x1, 0, 5, IWN_RFLAG_MCS, },
+ { 108, 0x3, 0, 6, IWN_RFLAG_MCS, },
+ { 128, IWN_PLCP_INVALID, 0, 7, IWN_RFLAG_MCS, }
};
+/* Convert an MCS index into an iwn_rates[] index. */
+const int iwn_mcs2ridx[] = { 4, 6, 7, 8, 9, 10, 11, 12 };
+
#define IWN4965_MAX_PWR_INDEX 107
/*
diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h
index a20522cbcda..e1370e0c6b7 100644
--- a/sys/dev/pci/if_iwnvar.h
+++ b/sys/dev/pci/if_iwnvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwnvar.h,v 1.28 2014/09/09 18:55:08 sthen Exp $ */
+/* $OpenBSD: if_iwnvar.h,v 1.29 2016/01/04 13:54:19 stsp Exp $ */
/*-
* Copyright (c) 2007, 2008
@@ -259,6 +259,8 @@ struct iwn_softc {
struct iwn_rx_stat last_rx_stat;
int last_rx_valid;
+#define IWN_LAST_RX_VALID 0x01
+#define IWN_LAST_RX_AMPDU 0x02
struct iwn_ucode_info ucode_info;
struct iwn_rxon rxon;
uint32_t rawtemp;