On Tue, Sep 09, 2014 at 12:38:04PM +0200, Fabian Raetz wrote:
> Hi,
> 
> below is a patch for iwn(4) which hopefully fixes a problem where iwn(4)
> does not return from a scan, if the interface is up. 

here's an updated version which does not 
set hdr->max_svc / hdr->pause_svc.

Cristoph Zimmermann noticed that scan requests return no APs on his
device (thanks for testing).
iwn0 at pci2 dev 0 function 0 "Intel WiFi Link 5100" rev 0x00: msi, MIMO 1T2R, 
MoW, address 00:21:6b:a3:70:7a

As Piotr and Mike tested the patch from Piotr which does not set this
values either and it still works on my card, this should the way to go
for now.


Index: if_iwn.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v
retrieving revision 1.133
diff -u -p -r1.133 if_iwn.c
--- if_iwn.c    22 Jul 2014 13:12:11 -0000      1.133
+++ if_iwn.c    9 Sep 2014 14:57:34 -0000
@@ -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 
        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 
        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 
        /* 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 
        /* 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 
                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++;
@@ -5580,6 +5702,14 @@ iwn_read_firmware_tlv(struct iwn_softc *
                                sc->reset_noise_gain = letoh32(*ptr);
                                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",
Index: if_iwnreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwnreg.h,v
retrieving revision 1.47
diff -u -p -r1.47 if_iwnreg.h
--- if_iwnreg.h 11 Feb 2014 19:30:10 -0000      1.47
+++ if_iwnreg.h 9 Sep 2014 14:57:34 -0000
@@ -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 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 {
@@ -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;
@@ -1419,6 +1466,60 @@ struct iwn_fw_tlv {
 #define IWN_FW_BOOT_TEXT_MAXSZ 1024
 #define IWN4965_FWSZ           (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ)
 #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.
Index: if_iwnvar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwnvar.h,v
retrieving revision 1.27
diff -u -p -r1.27 if_iwnvar.h
--- if_iwnvar.h 10 Feb 2014 19:08:58 -0000      1.27
+++ if_iwnvar.h 9 Sep 2014 14:57:34 -0000
@@ -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;


> 
> This patch was backported from this FreeBSD commit
> https://svnweb.freebsd.org/base?view=revision&revision=258829
> ---------
> Overhaul the iwn(4) scan infrastructure to be slightly more "correct"
> for these chipsets.
> 
> * Correctly set the active/passive flag in the scan request - this is
>   NOT a "is the channel active|passive"; it's to do with whether we
>   have an SSID to actively scan for or not.  The firmware takes care
>   of the active/passive setup of the channel.
> 
> * Calculate the active/passive dwell time based on the beacon interval
>   and the channel mode, rather than using a hard coded value.
> 
> * For now, hardcode the scan service_time.  It's defined as:
> 
>   31:22 - number of beacon intervals to come back onto the home channel
>           for;
>   0:21  - time (microseconds) to come back onto the home channel for.
> 
>   When doing an active scan when the NIC is active (whether we're associated
>   or not - it only matters if we've setup the NIC to a destination or not)
>   this determines how much time to stay on the home channel for when
>   scanning.  We can tune this based on the amount of active traffic.
> 
>   For now it's 4 beacon intervals and 100 microseconds.
> 
> * Fix the "good crc threshold" setting.  It differs based on the NIC
>   firmware.  Some older firmware required a workaround; the later
>   firmware instead treats the field as a flag.
> 
> * Enforce that we are not sending a scan command if one is already
>   pending.  Any time this is done is a bug and it absolutely needs
>   to be fixed - so be very loud.
> 
> * Add the SCAN flag to a few debug messages that are scan related but
>   only occuring under STATE.
> 
> Now, this does get noisy when you're scanning in an actively busy 2GHz
> network as the firmware (for reason I don't quite yet understand) seems
> hell bent on staying on some passive channels longer than it should.
> However, it should eventually recover and complete the scan.
> ----------
> 
> Marcin Piotr Pawlowski came up with the same diff (without comments) and
> send it to me in a private mail. Thanks!
> 
> I kept all comments to reduce the diff to FreeBSD's iwn(4).
> In iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time), the
> dwell_time is unused, so there could probably some code be removed.
> 
> 
> Devices tested so far:
> 
> Inter WiFi Link 5100          (Marcin Piotr Pawlowski)
> Intel Centrino Advanced-N 6205        (Mike Burns, who also reported the
> problem on misc@, thanks!)
> Intel Centrino Wireless-N 2230        (Fabian Raetz)
> 
> 
> More tests are much appreciated :)
> 
> 
> Regards,
> Fabian
> 
> 
> Index: if_iwn.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v
> retrieving revision 1.133
> diff -u -p -r1.133 if_iwn.c
> --- if_iwn.c  22 Jul 2014 13:12:11 -0000      1.133
> +++ if_iwn.c  9 Sep 2014 09:47:48 -0000
> @@ -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,10 @@ iwn_scan(struct iwn_softc *sc, uint16_t 
>       struct ieee80211_rateset *rs;
>       struct ieee80211_channel *c;
>       uint8_t *buf, *frm;
> -     uint16_t rxchain;
> +     uint32_t scan_service_time;
> +     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) {
> @@ -4453,6 +4517,21 @@ iwn_scan(struct iwn_softc *sc, uint16_t 
>        */
>       hdr->quiet_time = htole16(10);          /* timeout in milliseconds */
>       hdr->quiet_threshold = htole16(1);      /* min # of packets */
> +     /*
> +      * Max needs to be greater than active and passive and quiet!
> +      * It's also in microseconds!
> +      */
> +     hdr->max_svc =  htole32(250 * 1024);
> +
> +
> +     /*
> +      * Reset scan: interval=100
> +      * Normal scan: interval=beacon interval
> +      * suspend_time: 100 (TU)
> +      *
> +      */
> +     scan_service_time = (4 << 22) | (100 * 1024);   /* Hardcode for now! */
> +     hdr->pause_svc = htole32(scan_service_time);
>  
>       /* Select antennas for scanning. */
>       rxchain =
> @@ -4474,7 +4553,6 @@ iwn_scan(struct iwn_softc *sc, uint16_t 
>       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 +4566,23 @@ iwn_scan(struct iwn_softc *sc, uint16_t 
>       /* 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 +4611,41 @@ iwn_scan(struct iwn_softc *sc, uint16_t 
>       /* 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 +4655,33 @@ iwn_scan(struct iwn_softc *sc, uint16_t 
>               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++;
> @@ -5580,6 +5718,14 @@ iwn_read_firmware_tlv(struct iwn_softc *
>                               sc->reset_noise_gain = letoh32(*ptr);
>                               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",
> Index: if_iwnreg.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/if_iwnreg.h,v
> retrieving revision 1.47
> diff -u -p -r1.47 if_iwnreg.h
> --- if_iwnreg.h       11 Feb 2014 19:30:10 -0000      1.47
> +++ if_iwnreg.h       9 Sep 2014 09:47:48 -0000
> @@ -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 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 {
> @@ -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;
> @@ -1419,6 +1466,60 @@ struct iwn_fw_tlv {
>  #define IWN_FW_BOOT_TEXT_MAXSZ       1024
>  #define IWN4965_FWSZ         (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ)
>  #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.
> Index: if_iwnvar.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/pci/if_iwnvar.h,v
> retrieving revision 1.27
> diff -u -p -r1.27 if_iwnvar.h
> --- if_iwnvar.h       10 Feb 2014 19:08:58 -0000      1.27
> +++ if_iwnvar.h       9 Sep 2014 09:47:48 -0000
> @@ -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;

Reply via email to