* GPL LICENSE SUMMARY
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
  * BSD LICENSE
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
        u8 ver;
 } __packed;
 
-#define IWL_MVM_MAX_SIMULTANEOUS_SCANS 8
+/* The maximum of either of these cannot exceed 8, because we use an
+ * 8-bit mask (see IWL_MVM_SCAN_MASK in mvm.h).
+ */
+#define IWL_MVM_MAX_UMAC_SCANS 8
+#define IWL_MVM_MAX_LMAC_SCANS 1
 
 enum scan_config_flags {
        SCAN_CONFIG_FLAG_ACTIVATE                       = BIT(0),
 
 
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
 
+       BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
+                    IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
+
+       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+               mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS;
+       else
+               mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS;
+
        if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
                hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
                        &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
        if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
                int i;
 
-               for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+               for (i = 0; i < mvm->max_scans; i++) {
                        if (WARN_ONCE(mvm->scan_uid[i],
                                      "UMAC scan UID %d was not cleaned\n",
                                      mvm->scan_uid[i]))
        iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED);
 }
 
+static int iwl_mvm_num_scans(struct iwl_mvm *mvm)
+{
+       return hweight32(mvm->scan_status & IWL_MVM_SCAN_MASK);
+}
+
+static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type)
+{
+       /* This looks a bit arbitrary, but the idea is that if we run
+        * out of possible simultaneous scans and the userspace is
+        * trying to run a scan type that is already running, we
+        * return -EBUSY.  But if the userspace wants to start a
+        * different type of scan, we stop the opposite type to make
+        * space for the new request.  The reason is backwards
+        * compatibility with old wpa_supplicant that wouldn't stop a
+        * scheduled scan before starting a normal scan.
+        */
+
+       if (iwl_mvm_num_scans(mvm) < mvm->max_scans)
+               return 0;
+
+       /* Use a switch, even though this is a bitmask, so that more
+        * than one bits set will fall in default and we will warn.
+        */
+       switch (type) {
+       case IWL_MVM_SCAN_REGULAR:
+               if (mvm->scan_status & IWL_MVM_SCAN_REGULAR_MASK)
+                       return -EBUSY;
+               return iwl_mvm_scan_offload_stop(mvm, true);
+       case IWL_MVM_SCAN_SCHED:
+               if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK)
+                       return -EBUSY;
+               return iwl_mvm_cancel_scan(mvm);
+       default:
+               WARN_ON(1);
+               break;
+       }
+
+       return -EIO;
+}
+
 static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct ieee80211_scan_request *hw_req)
                goto out;
        }
 
-       if (mvm->scan_status & IWL_MVM_SCAN_REGULAR) {
-               ret = -EBUSY;
+       ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_REGULAR);
+       if (ret)
                goto out;
-       }
-
-       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) &&
-           (mvm->scan_status & IWL_MVM_SCAN_SCHED)) {
-               ret = iwl_mvm_scan_offload_stop(mvm, true);
-               if (ret)
-                       goto out;
-       }
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
                goto out;
        }
 
-       if (mvm->scan_status & IWL_MVM_SCAN_SCHED) {
-               ret = -EBUSY;
+       ret = iwl_mvm_check_running_scans(mvm, IWL_MVM_SCAN_SCHED);
+       if (ret)
                goto out;
-       }
-
-       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) &&
-           (mvm->scan_status & IWL_MVM_SCAN_REGULAR)) {
-               ret = iwl_mvm_cancel_scan(mvm);
-               if (ret)
-                       goto out;
-       }
 
        ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
        if (ret)
 
        void *scan_cmd;
        struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
+       /* max number of simultaneous scans the FW supports */
+       unsigned int max_scans;
+
        /* UMAC scan tracking */
-       u32 scan_uid[IWL_MVM_MAX_SIMULTANEOUS_SCANS];
+       u32 scan_uid[IWL_MVM_MAX_UMAC_SCANS];
        u8 scan_seq_num, sched_scan_seq_num;
 
        /* rx chain antennas set through debugfs for the scan command */
 
 {
        int i;
 
-       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+       for (i = 0; i < mvm->max_scans; i++)
                if (mvm->scan_uid[i] == uid)
                        return i;
 
 {
        int i;
 
-       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+       for (i = 0; i < mvm->max_scans; i++)
                if (mvm->scan_uid[i] & type)
                        return true;
 
 {
        int i;
 
-       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+       for (i = 0; i < mvm->max_scans; i++)
                if (mvm->scan_uid[i] & type)
                        return i;
 
                uid = type | (mvm->scan_seq_num <<
                              IWL_UMAC_SCAN_UID_SEQ_OFFSET);
                mvm->scan_seq_num++;
-       } while (iwl_mvm_find_scan_uid(mvm, uid) <
-                IWL_MVM_MAX_SIMULTANEOUS_SCANS);
+       } while (iwl_mvm_find_scan_uid(mvm, uid) < mvm->max_scans);
 
        IWL_DEBUG_SCAN(mvm, "Generated scan UID %u\n", uid);
 
        lockdep_assert_held(&mvm->mutex);
 
        uid_idx = iwl_mvm_find_free_scan_uid(mvm);
-       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+       if (uid_idx >= mvm->max_scans)
                return -EBUSY;
 
        /* we should have failed registration if scan_cmd was NULL */
        lockdep_assert_held(&mvm->mutex);
 
        uid_idx = iwl_mvm_find_free_scan_uid(mvm);
-       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+       if (uid_idx >= mvm->max_scans)
                return -EBUSY;
 
        /* we should have failed registration if scan_cmd was NULL */
        /*
         * Scan uid may be set to zero in case of scan abort request from above.
         */
-       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+       if (uid_idx >= mvm->max_scans)
                return 0;
 
        IWL_DEBUG_SCAN(mvm,
        if (WARN_ON(pkt->hdr.cmd != SCAN_COMPLETE_UMAC))
                return false;
 
-       if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+       if (uid_idx >= scan_done->mvm->max_scans)
                return false;
 
        /*
 
        IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type);
 
-       for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+       for (i = 0; i < mvm->max_scans; i++) {
                if (mvm->scan_uid[i] & type) {
                        int err;
 
                u32 uid, i;
 
                uid = iwl_mvm_find_first_scan(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
-               if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS) {
+               if (uid < mvm->max_scans) {
                        ieee80211_scan_completed(mvm->hw, true);
                        mvm->scan_uid[uid] = 0;
                }
                uid = iwl_mvm_find_first_scan(mvm,
                                              IWL_UMAC_SCAN_UID_SCHED_SCAN);
-               if (uid < IWL_MVM_MAX_SIMULTANEOUS_SCANS && !mvm->restart_fw) {
+               if (uid < mvm->max_scans && !mvm->restart_fw) {
                        ieee80211_sched_scan_stopped(mvm->hw);
                        mvm->scan_uid[uid] = 0;
                }
                 * UIDs to make sure there's nothing left there and warn if
                 * any is found.
                 */
-               for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+               for (i = 0; i < mvm->max_scans; i++) {
                        if (WARN_ONCE(mvm->scan_uid[i],
                                      "UMAC scan UID %d was not cleaned\n",
                                      mvm->scan_uid[i]))