diff options
author | Stuart Henderson <sthen@cvs.openbsd.org> | 2014-09-09 18:55:09 +0000 |
---|---|---|
committer | Stuart Henderson <sthen@cvs.openbsd.org> | 2014-09-09 18:55:09 +0000 |
commit | 4d394934945cd305012096edc647b52fe65092d0 (patch) | |
tree | 11dbc510a054411935f23a774cce1ca79bcb7358 | |
parent | 5bd25566aa40eba0d1667b60757517b628e00281 (diff) |
Backport https://svnweb.freebsd.org/base?view=revision&revision=258829 to fix
scans with various iwn(4) devices. From Fabian Raetz, testing by Fabian,
Marcin Piotr Pawlowski, Mike Burns, kettenis@ and myself. ok kettenis@ with
minor tweaks for whitespace in #define lines which I've done; also I have
done s/IWL/IWN/ in comments as noticed by dcoppa@.
-rw-r--r-- | sys/dev/pci/if_iwn.c | 150 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnreg.h | 103 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnvar.h | 4 |
3 files changed, 245 insertions, 12 deletions
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c index 1868de2f13d..7d976896172 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.133 2014/07/22 13:12:11 mpi Exp $ */ +/* $OpenBSD: if_iwn.c,v 1.134 2014/09/09 18:55:08 sthen Exp $ */ /*- * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> @@ -220,6 +220,9 @@ int iwn_send_btcoex(struct iwn_softc *); int iwn_send_advanced_btcoex(struct iwn_softc *); int iwn5000_runtime_calib(struct iwn_softc *); int iwn_config(struct iwn_softc *); +uint16_t iwn_get_active_dwell_time(struct iwn_softc *, uint16_t, uint8_t); +uint16_t iwn_limit_dwell(struct iwn_softc *, uint16_t); +uint16_t iwn_get_passive_dwell_time(struct iwn_softc *, uint16_t); int iwn_scan(struct iwn_softc *, uint16_t); int iwn_auth(struct iwn_softc *); int iwn_run(struct iwn_softc *); @@ -4424,6 +4427,66 @@ iwn_config(struct iwn_softc *sc) return 0; } +uint16_t +iwn_get_active_dwell_time(struct iwn_softc *sc, + uint16_t flags, uint8_t n_probes) +{ + /* No channel? Default to 2GHz settings */ + if (flags & IEEE80211_CHAN_2GHZ) { + 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. + */ +uint16_t +iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + int bintval = 0; + + /* bintval is in TU (1.024mS) */ + if (ni != NULL) + bintval = ni->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) { + return (MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); + } + + /* No association context? Default */ + return (IWN_PASSIVE_DWELL_BASE); +} + +uint16_t +iwn_get_passive_dwell_time(struct iwn_softc *sc, uint16_t flags) +{ + uint16_t passive; + if (flags & IEEE80211_CHAN_2GHZ) { + 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)); +} + int iwn_scan(struct iwn_softc *sc, uint16_t flags) { @@ -4436,9 +4499,9 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) struct ieee80211_rateset *rs; struct ieee80211_channel *c; uint8_t *buf, *frm; - uint16_t rxchain; + uint16_t rxchain, dwell_active, dwell_passive; uint8_t txant; - int buflen, error; + int buflen, error, is_active; buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { @@ -4474,7 +4537,6 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) tx->lifetime = htole32(IWN_LIFETIME_INFINITE); if (flags & IEEE80211_CHAN_5GHZ) { - hdr->crc_threshold = 0xffff; /* Send probe requests at 6Mbps. */ tx->plcp = iwn_rates[IWN_RIDX_OFDM6].plcp; rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; @@ -4488,12 +4550,23 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) /* Use the first valid TX antenna. */ txant = IWN_LSB(sc->txchainmask); tx->rflags |= 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 (ic->ic_des_esslen != 0) { essid[0].id = IEEE80211_ELEMID_SSID; essid[0].len = ic->ic_des_esslen; memcpy(essid[0].data, ic->ic_des_essid, ic->ic_des_esslen); + + is_active = 1; } /* * Build a probe request frame. Most of the following code is a @@ -4522,6 +4595,41 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) /* 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; + chan = (struct iwn_scan_chan *)frm; for (c = &ic->ic_channels[1]; c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { @@ -4531,19 +4639,33 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) chan->chan = htole16(ieee80211_chan2ieee(ic, c)); DPRINTFN(2, ("adding channel %d\n", chan->chan)); chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) - chan->flags |= htole32(IWN_CHAN_ACTIVE); if (ic->ic_des_esslen != 0) chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); + + 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, flags, is_active); + dwell_passive = iwn_get_passive_dwell_time(sc, flags); + + /* 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); + chan->dsp_gain = 0x6e; if (IEEE80211_IS_CHAN_5GHZ(c)) { chan->rf_gain = 0x3b; - chan->active = htole16(24); - chan->passive = htole16(110); } else { chan->rf_gain = 0x28; - chan->active = htole16(36); - chan->passive = htole16(120); } hdr->nchan++; chan++; @@ -5581,6 +5703,14 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, sc->noise_gain = letoh32(*ptr) + 1; } break; + case IWN_FW_TLV_FLAGS: + if (len < sizeof(uint32_t)) + break; + if (len % sizeof(uint32_t)) + break; + sc->tlv_feature_flags = letoh32(*ptr); + DPRINTF(("feature: 0x%08x\n", sc->tlv_feature_flags)); + break; default: DPRINTF(("TLV type %d not handled\n", letoh16(tlv->type))); diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h index f9d285d86d1..23d07b78347 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.47 2014/02/11 19:30:10 kettenis Exp $ */ +/* $OpenBSD: if_iwnreg.h,v 1.48 2014/09/09 18:55:08 sthen Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -799,6 +799,7 @@ struct iwn_scan_hdr { struct iwn_scan_chan { uint32_t flags; +#define IWN_CHAN_PASSIVE (0 << 0) #define IWN_CHAN_ACTIVE (1 << 0) #define IWN_CHAN_NPBREQS(x) (((1 << (x)) - 1) << 1) @@ -812,6 +813,51 @@ struct iwn_scan_chan { /* Maximum size of a scan command. */ #define IWN_SCAN_MAXSZ (MCLBYTES - 4) +/* + * 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) + +/* + * 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) + +/* + * 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 IWN_GOOD_CRC_TH_NEVER + * here instead of IWN_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 { @@ -1407,6 +1453,7 @@ struct iwn_fw_tlv { #define IWN_FW_TLV_PBREQ_MAXLEN 6 #define IWN_FW_TLV_ENH_SENS 14 #define IWN_FW_TLV_PHY_CALIB 15 +#define IWN_FW_TLV_FLAGS 18 uint16_t alt; uint32_t len; @@ -1421,6 +1468,60 @@ struct iwn_fw_tlv { #define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ /* + * Microcode flags TLV (18.) + */ + +/** + * enum iwn_ucode_tlv_flag - ucode API flags + * @IWN_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously + * was a separate TLV but moved here to save space. + * @IWN_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID, + * treats good CRC threshold as a boolean + * @IWN_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). + * @IWN_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. + * @IWN_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS + * @IWN_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD + * @IWN_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan + * offload profile config command. + * @IWN_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api + * @IWN_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API. + * @IWN_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six + * (rather than two) IPv6 addresses + * @IWN_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API + * @IWN_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element + * from the probe request template. + * @IWN_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping + * connection when going back to D0 + * @IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) + * @IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) + * @IWN_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. + * @IWN_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API + * @IWN_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command + * containing CAM (Continuous Active Mode) indication. + */ +enum iwn_ucode_tlv_flag { + IWN_UCODE_TLV_FLAGS_PAN = (1 << 0), + IWN_UCODE_TLV_FLAGS_NEWSCAN = (1 << 1), + IWN_UCODE_TLV_FLAGS_MFP = (1 << 2), + IWN_UCODE_TLV_FLAGS_P2P = (1 << 3), + IWN_UCODE_TLV_FLAGS_DW_BC_TABLE = (1 << 4), + IWN_UCODE_TLV_FLAGS_NEWBT_COEX = (1 << 5), + IWN_UCODE_TLV_FLAGS_UAPSD = (1 << 6), + IWN_UCODE_TLV_FLAGS_SHORT_BL = (1 << 7), + IWN_UCODE_TLV_FLAGS_RX_ENERGY_API = (1 << 8), + IWN_UCODE_TLV_FLAGS_TIME_EVENT_API_V2 = (1 << 9), + IWN_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = (1 << 10), + IWN_UCODE_TLV_FLAGS_BF_UPDATED = (1 << 11), + IWN_UCODE_TLV_FLAGS_NO_BASIC_SSID = (1 << 12), + IWN_UCODE_TLV_FLAGS_D3_CONTINUITY_API = (1 << 14), + IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = (1 << 15), + IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = (1 << 16), + IWN_UCODE_TLV_FLAGS_SCHED_SCAN = (1 << 17), + IWN_UCODE_TLV_FLAGS_STA_KEY_CMD = (1 << 19), + IWN_UCODE_TLV_FLAGS_DEVICE_PS_CMD = (1 << 20), +}; + +/* * Offsets into EEPROM. */ #define IWN_EEPROM_MAC 0x015 diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h index 7d9907003d1..a20522cbcda 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.27 2014/02/10 19:08:58 kettenis Exp $ */ +/* $OpenBSD: if_iwnvar.h,v 1.28 2014/09/09 18:55:08 sthen Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -284,6 +284,8 @@ struct iwn_softc { uint8_t reset_noise_gain; uint8_t noise_gain; + uint32_t tlv_feature_flags; + int32_t temp_off; uint32_t int_mask; uint8_t ntxchains; |