Index: if_iwn.c =================================================================== --- if_iwn.c (revision 258118) +++ if_iwn.c (working copy) @@ -3508,8 +3508,8 @@ desc = mtod(data->m, struct iwn_rx_desc *); DPRINTF(sc, IWN_DEBUG_RECV, - "%s: qid %x idx %d flags %x type %d(%s) len %d\n", - __func__, desc->qid & 0xf, desc->idx, desc->flags, + "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n", + __func__, sc->rxq.cur, desc->qid & 0xf, desc->idx, desc->flags, desc->type, iwn_intr_str(desc->type), le16toh(desc->len)); @@ -3608,7 +3608,8 @@ BUS_DMASYNC_POSTREAD); #ifdef IWN_DEBUG uint32_t *status = (uint32_t *)(desc + 1); - DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", + DPRINTF(sc, IWN_DEBUG_INTR | IWN_DEBUG_STATE, + "state changed to %x\n", le32toh(*status)); #endif break; @@ -3633,11 +3634,11 @@ #ifdef IWN_DEBUG struct iwn_stop_scan *scan = (struct iwn_stop_scan *)(desc + 1); - DPRINTF(sc, IWN_DEBUG_STATE, + DPRINTF(sc, IWN_DEBUG_STATE | IWN_DEBUG_SCAN, "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); #endif - + sc->sc_is_scanning = 0; IWN_UNLOCK(sc); ieee80211_scan_next(vap); IWN_LOCK(sc); @@ -4012,6 +4013,7 @@ if (rate & IEEE80211_RATE_MCS) cmp_rate |= IEEE80211_RATE_MCS; +#if 0 DPRINTF(sc, IWN_DEBUG_XMIT, "%s: idx %d: nr=%d, rate=0x%02x, rateentry=0x%02x\n", __func__, i, @@ -4018,6 +4020,7 @@ nr, rate, cmp_rate); +#endif if (cmp_rate == rate) return (i); @@ -4068,6 +4071,7 @@ } ac = M_WME_GETAC(m); if (m->m_flags & M_AMPDU_MPDU) { + uint16_t seqno; struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; if (!IEEE80211_AMPDU_RUNNING(tap)) { @@ -4075,9 +4079,27 @@ return EINVAL; } + /* + * Queue this frame to the hardware ring that we've + * negotiated AMPDU TX on. + * + * Note that the sequence number must match the TX slot + * being used! + */ ac = *(int *)tap->txa_private; + seqno = ni->ni_txseqs[tid]; *(uint16_t *)wh->i_seq = - htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + ring = &sc->txq[ac]; + if ((seqno % 256) != ring->cur) { + device_printf(sc->sc_dev, + "%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n", + __func__, + m, + seqno, + seqno % 256, + ring->cur); + } ni->ni_txseqs[tid]++; } ring = &sc->txq[ac]; @@ -4409,6 +4431,11 @@ ieee80211_radiotap_tx(vap, m); } + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: rts_ntries=%d, data_ntries=%d\n", + __func__, + (int) params->ibp_try1, + (int) params->ibp_try0); + tx->len = htole16(totlen); tx->tid = 0; tx->id = sc->broadcast_id; @@ -4999,6 +5026,10 @@ DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__); + /* XXX don't set LEDs during scan? */ + if (sc->sc_is_scanning) + return; + /* Clear microcode LED ownership. */ IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL); @@ -5464,6 +5495,9 @@ #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ sc->rxon->rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: is_scanning set, before RXON\n", __func__); + } (void)iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); #endif @@ -6098,6 +6132,9 @@ IWN_RXCHAIN_IDLE_COUNT(2); sc->rxon->rxchain = htole16(rxchain); DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: is_scanning set, before RXON\n", __func__); + } error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: RXON command failed\n", @@ -6148,6 +6185,89 @@ return frm + len; } +/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after + * sending probe req. This should be set long enough to hear probe responses + * from more than one AP. */ +#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ +#define IWL_ACTIVE_DWELL_TIME_52 (20) + +#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) +#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) + +/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. + * Must be set longer than active dwell time. + * For the most reliable scan, set > AP beacon interval (typically 100msec). */ +#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ +#define IWL_PASSIVE_DWELL_TIME_52 (10) +#define IWL_PASSIVE_DWELL_BASE (100) +#define IWL_CHANNEL_TUNE_TIME 5 +#define MAX_SCAN_CHANNEL 50 + +static uint16_t +iwn_get_active_dwell_time(struct iwn_softc *sc, + struct ieee80211_channel *c, uint8_t n_probes) +{ + /* No channel? Default to 2GHz settings */ + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { + return (IWL_ACTIVE_DWELL_TIME_24 + + IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1)); + } + + /* 5GHz dwell time */ + return (IWL_ACTIVE_DWELL_TIME_52 + + IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1)); +} + +/* + * Limit the total dwell time to 85% of the beacon interval. + * + * Returns the dwell time in milliseconds. + */ +static uint16_t +iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211vap *vap = NULL; + int bintval = 0; + + /* bintval is in TU (1.024mS) */ + if (! TAILQ_EMPTY(&ic->ic_vaps)) { + vap = TAILQ_FIRST(&ic->ic_vaps); + bintval = vap->iv_bss->ni_intval; + } + + + /* + * If it's non-zero, we should calculate the minimum of + * it and the DWELL_BASE. + * + * XXX Yes, the math should take into account that bintval + * is 1.024mS, not 1mS.. + */ + if (bintval > 0) { + DPRINTF(sc, IWN_DEBUG_SCAN, "%s: bintval=%d\n", __func__, bintval); + return (MIN(IWL_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); + } + + /* No association context? Default */ + return (IWL_PASSIVE_DWELL_BASE); +} + +static uint16_t +iwn_get_passive_dwell_time(struct iwn_softc *sc, struct ieee80211_channel *c) +{ + uint16_t passive; + + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { + passive = IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24; + } else { + passive = IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; + } + + /* Clamp to the beacon interval if we're associated */ + return (iwn_limit_dwell(sc, passive)); +} + static int iwn_scan(struct iwn_softc *sc) { @@ -6166,9 +6286,18 @@ uint16_t rxchain; uint8_t txant; int buflen, error; + int is_active; + uint16_t dwell_active, dwell_passive; + uint32_t extra, scan_service_time; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: called whilst scanning!\n", + __func__); + return (EAGAIN); + } + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { @@ -6188,8 +6317,19 @@ * Max needs to be greater than active and passive and quiet! * It's also in microseconds! */ - hdr->max_svc = htole32(250 * 1000); + hdr->max_svc = htole32(200 * 1024); + /* + * Reset scan: interval=100 + * Normal scan: interval=becaon interval + * suspend_time: 100 (TU) + * + */ + extra = (100 /* suspend_time */ / 100 /* beacon interval */) << 22; + //scan_service_time = extra | ((100 /* susp */ % 100 /* int */) * 1024); + scan_service_time = (1 << 22) | (100 * 1024); /* Hardcode for now! */ + hdr->pause_svc = htole32(scan_service_time); + /* Select antennas for scanning. */ rxchain = IWN_RXCHAIN_VALID(sc->rxchainmask) | @@ -6228,6 +6368,15 @@ txant = IWN_LSB(sc->txchainmask); tx->rate |= htole32(IWN_RFLAG_ANT(txant)); + /* + * Only do active scanning if we're announcing a probe request + * for a given SSID (or more, if we ever add it to the driver.) + */ + is_active = 0; + + /* + * If we're scanning for a specific SSID, add it to the command. + */ essid = (struct iwn_scan_essid *)(tx + 1); if (ss->ss_ssid[0].len != 0) { essid[0].id = IEEE80211_ELEMID_SSID; @@ -6234,6 +6383,12 @@ essid[0].len = ss->ss_ssid[0].len; memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); } + + DPRINTF(sc, IWN_DEBUG_SCAN, "%s: ssid_len=%d, ssid=%*s\n", __func__, ss->ss_ssid[0].len, ss->ss_ssid[0].len, ss->ss_ssid[0].ssid); + + if (ss->ss_nssid > 0) + is_active = 1; + /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. @@ -6259,6 +6414,52 @@ /* Set length of probe request. */ tx->len = htole16(frm - (uint8_t *)wh); + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + * + * This was fixed in later versions along with some other + * scan changes, and the threshold behaves as a flag in those + * versions. + */ +#if 0 +#define IWL_GOOD_CRC_TH_DISABLED 0 +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) + + if (priv->new_scan_threshold_behaviour) + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_DISABLED; + else + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_NEVER; +#endif + + /* + * If we're doing active scanning, set the crc_threshold + * to a suitable value. This is different to active veruss + * passive scanning depending upon the channel flags; the + * firmware will obey that particular check for us. + */ + if (is_active) + hdr->crc_threshold = htole16(1); + else + hdr->crc_threshold = htole16(0xffff); + c = ic->ic_curchan; chan = (struct iwn_scan_chan *)frm; chan->chan = htole16(ieee80211_chan2ieee(ic, c)); @@ -6266,40 +6467,48 @@ if (ss->ss_nssid > 0) chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); chan->dsp_gain = 0x6e; + + /* + * Set the passive/active flag depending upon the channel mode. + * XXX TODO: take the is_active flag into account as well? + */ + if (c->ic_flags & IEEE80211_CHAN_PASSIVE) + chan->flags |= htole32(IWN_CHAN_PASSIVE); + else + chan->flags |= htole32(IWN_CHAN_ACTIVE); + + /* + * Calculate the active/passive dwell times. + */ + + dwell_active = iwn_get_active_dwell_time(sc, c, ss->ss_nssid); + dwell_passive = iwn_get_passive_dwell_time(sc, c); + + /* Make sure they're valid */ + if (dwell_passive <= dwell_active) + dwell_passive = dwell_active + 1; + + chan->active = htole16(dwell_active); + chan->passive = htole16(dwell_passive); + if (IEEE80211_IS_CHAN_5GHZ(c) && !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { chan->rf_gain = 0x3b; - chan->active = htole16(24); - chan->passive = htole16(110); - chan->flags |= htole32(IWN_CHAN_ACTIVE); } else if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->rf_gain = 0x3b; - chan->active = htole16(24); - if (sc->rxon->associd) - chan->passive = htole16(78); - else - chan->passive = htole16(110); - hdr->crc_threshold = 0xffff; } else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { chan->rf_gain = 0x28; - chan->active = htole16(36); - chan->passive = htole16(120); - chan->flags |= htole32(IWN_CHAN_ACTIVE); } else { chan->rf_gain = 0x28; - chan->active = htole16(36); - if (sc->rxon->associd) - chan->passive = htole16(88); - else - chan->passive = htole16(120); - hdr->crc_threshold = 0xffff; } DPRINTF(sc, IWN_DEBUG_STATE, "%s: chan %u flags 0x%x rf_gain 0x%x " - "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, + "dsp_gain 0x%x active %d passive %d scan_svc_time %d crc 0x%x " + "isactive=%d numssid=%d\n", __func__, chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, - chan->active, chan->passive); + dwell_active, dwell_passive, scan_service_time, + hdr->crc_threshold, is_active, ss->ss_nssid); hdr->nchan++; chan++; @@ -6306,6 +6515,11 @@ buflen = (uint8_t *)chan - buf; hdr->len = htole16(buflen); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: called with is_scanning set!\n", __func__); + } + sc->sc_is_scanning = 1; + DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n", hdr->nchan); error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1); @@ -6352,6 +6566,9 @@ DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon->chan, sc->rxon->flags, sc->rxon->cck_mask, sc->rxon->ofdm_mask); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: is_scanning set, before RXON\n", __func__); + } error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n", @@ -6447,6 +6664,9 @@ sc->rxon->filter |= htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n", sc->rxon->chan, sc->rxon->flags); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: is_scanning set, before RXON\n", __func__); + } error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, @@ -7474,7 +7694,7 @@ sc->sc_flags |= IWN_FLAG_ENH_SENS; break; case IWN_FW_TLV_PHY_CALIB: - tmp = htole32(*ptr); + tmp = le32toh(*ptr); if (tmp < 253) { sc->reset_noise_gain = tmp; sc->noise_gain = tmp + 1; @@ -7486,7 +7706,14 @@ "PAN Support found: %d\n", 1); break; case IWN_FW_TLV_FLAGS : - sc->tlv_feature_flags = htole32(*ptr); + if (len < sizeof(uint32_t)) + break; + if (len % sizeof(uint32_t)) + break; + sc->tlv_feature_flags = le32toh(*ptr); + device_printf(sc->sc_dev, "%s: feature: 0x%08x\n", + __func__, + sc->tlv_feature_flags); break; case IWN_FW_TLV_PBREQ_MAXLEN: case IWN_FW_TLV_RUNT_EVTLOG_PTR: @@ -8080,6 +8307,7 @@ IWN_LOCK_ASSERT(sc); + sc->sc_is_scanning = 0; sc->sc_tx_timer = 0; callout_stop(&sc->watchdog_to); callout_stop(&sc->calib_to);