Index: sys/dev/iwn/if_iwn.c =================================================================== --- sys/dev/iwn/if_iwn.c (revision 258760) +++ sys/dev/iwn/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); @@ -4070,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)) { @@ -4077,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]; @@ -5001,6 +5021,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); @@ -5466,6 +5490,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 @@ -6100,6 +6127,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", @@ -6150,6 +6180,73 @@ return frm + len; } +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 (IWN_ACTIVE_DWELL_TIME_2GHZ + + IWN_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1)); + } + + /* 5GHz dwell time */ + return (IWN_ACTIVE_DWELL_TIME_5GHZ + + IWN_ACTIVE_DWELL_FACTOR_5GHZ * (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(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); + } + + /* No association context? Default */ + return (IWN_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 = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_2GHZ; + } else { + passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_5GHZ; + } + + /* Clamp to the beacon interval if we're associated */ + return (iwn_limit_dwell(sc, passive)); +} + static int iwn_scan(struct iwn_softc *sc) { @@ -6168,9 +6265,22 @@ 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__); + /* + * We are absolutely not allowed to send a scan command when another + * scan command is pending. + */ + 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) { @@ -6190,8 +6300,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 = (4 << 22) | (100 * 1024); /* Hardcode for now! */ + hdr->pause_svc = htole32(scan_service_time); + /* Select antennas for scanning. */ rxchain = IWN_RXCHAIN_VALID(sc->rxchainmask) | @@ -6230,6 +6351,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; @@ -6236,6 +6366,16 @@ 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. @@ -6261,6 +6401,41 @@ /* 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 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 (sc->tlv_feature_flags & IWN_UCODE_TLV_FLAGS_NEWSCAN) + hdr->crc_threshold = is_active ? + IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_DISABLED; + else + hdr->crc_threshold = is_active ? + IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_NEVER; + c = ic->ic_curchan; chan = (struct iwn_scan_chan *)frm; chan->chan = htole16(ieee80211_chan2ieee(ic, c)); @@ -6268,40 +6443,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++; @@ -6308,6 +6491,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); @@ -6354,6 +6542,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", @@ -6449,6 +6640,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, @@ -7487,8 +7681,16 @@ DPRINTF(sc, IWN_DEBUG_RESET, "PAN Support found: %d\n", 1); break; - case IWN_FW_TLV_FLAGS : - sc->tlv_feature_flags = htole32(*ptr); + case IWN_FW_TLV_FLAGS: + if (len < sizeof(uint32_t)) + break; + if (len % sizeof(uint32_t)) + break; + sc->tlv_feature_flags = le32toh(*ptr); + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: feature: 0x%08x\n", + __func__, + sc->tlv_feature_flags); break; case IWN_FW_TLV_PBREQ_MAXLEN: case IWN_FW_TLV_RUNT_EVTLOG_PTR: @@ -8082,6 +8284,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); Index: sys/dev/iwn/if_iwnreg.h =================================================================== --- sys/dev/iwn/if_iwnreg.h (revision 258760) +++ sys/dev/iwn/if_iwnreg.h (working copy) @@ -922,18 +922,54 @@ /* Maximum size of a scan command. */ #define IWN_SCAN_MAXSZ (MCLBYTES - 4) -#define IWN_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ -#define IWN_ACTIVE_DWELL_TIME_52 (20) -#define IWN_ACTIVE_DWELL_FACTOR_24 (3) -#define IWN_ACTIVE_DWELL_FACTOR_52 (2) +/* + * 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 IWN_ACTIVE_DWELL_TIME_2GHZ (30) /* all times in msec */ +#define IWN_ACTIVE_DWELL_TIME_5GHZ (20) +#define IWN_ACTIVE_DWELL_FACTOR_2GHZ (3) +#define IWN_ACTIVE_DWELL_FACTOR_5GHZ (2) -#define IWN_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ -#define IWN_PASSIVE_DWELL_TIME_52 (10) +/* + * 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 IWN_PASSIVE_DWELL_TIME_2GHZ (20) /* all times in msec */ +#define IWN_PASSIVE_DWELL_TIME_5GHZ (10) #define IWN_PASSIVE_DWELL_BASE (100) #define IWN_CHANNEL_TUNE_TIME (5) #define IWN_SCAN_CHAN_TIMEOUT 2 +#define IWN_MAX_SCAN_CHANNEL 50 +/* + * 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. + */ +#define IWN_GOOD_CRC_TH_DISABLED 0 +#define IWN_GOOD_CRC_TH_DEFAULT htole16(1) +#define IWN_GOOD_CRC_TH_NEVER htole16(0xffff) + /* Structure for command IWN_CMD_TXPOWER (4965AGN only.) */ #define IWN_RIDX_MAX 32 struct iwn4965_cmd_txpower { Index: sys/dev/iwn/if_iwnvar.h =================================================================== --- sys/dev/iwn/if_iwnvar.h (revision 258760) +++ sys/dev/iwn/if_iwnvar.h (working copy) @@ -361,6 +361,9 @@ int sc_tx_timer; int sc_scan_timer; + /* Are we doing a scan? */ + int sc_is_scanning; + struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES]; int (*sc_ampdu_rx_start)(struct ieee80211_node *,