summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Henderson <sthen@cvs.openbsd.org>2014-09-09 18:55:09 +0000
committerStuart Henderson <sthen@cvs.openbsd.org>2014-09-09 18:55:09 +0000
commit4d394934945cd305012096edc647b52fe65092d0 (patch)
tree11dbc510a054411935f23a774cce1ca79bcb7358
parent5bd25566aa40eba0d1667b60757517b628e00281 (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.c150
-rw-r--r--sys/dev/pci/if_iwnreg.h103
-rw-r--r--sys/dev/pci/if_iwnvar.h4
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;