static int at76_hw_scan(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
-                       struct cfg80211_scan_request *req)
+                       struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct at76_priv *priv = hw->priv;
        struct at76_req_scan scan;
        u8 *ssid = NULL;
 
 
 static int ath10k_hw_scan(struct ieee80211_hw *hw,
                          struct ieee80211_vif *vif,
-                         struct cfg80211_scan_request *req)
+                         struct ieee80211_scan_request *hw_req)
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wmi_start_scan_arg arg;
        int ret = 0;
        int i;
 
 
 int cw1200_hw_scan(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req)
+                  struct ieee80211_scan_request *hw_req)
 {
        struct cw1200_common *priv = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wsm_template_frame frame = {
                .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
        };
 
 
 int cw1200_hw_scan(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req);
+                  struct ieee80211_scan_request *hw_req);
 void cw1200_scan_work(struct work_struct *work);
 void cw1200_scan_timeout(struct work_struct *work);
 void cw1200_clear_recent_scan_work(struct work_struct *work);
 
 
 int
 il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-              struct cfg80211_scan_request *req)
+              struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct il_priv *il = hw->priv;
        int ret;
 
 
 int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms);
 void il_force_scan_end(struct il_priv *il);
 int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req);
+                  struct ieee80211_scan_request *hw_req);
 void il_internal_short_hw_scan(struct il_priv *il);
 int il_force_reset(struct il_priv *il, bool external);
 u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
 
 
 static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
-                             struct cfg80211_scan_request *req)
+                             struct ieee80211_scan_request *hw_req)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
 
        IWL_DEBUG_MAC80211(priv, "enter\n");
 
 
 static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
-                              struct cfg80211_scan_request *req)
+                              struct ieee80211_scan_request *hw_req)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
 
        if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
 
 
 static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
-                                 struct cfg80211_scan_request *req)
+                                 struct ieee80211_scan_request *hw_req)
 {
        struct mac80211_hwsim_data *hwsim = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
 
        mutex_lock(&hwsim->mutex);
        if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
 
 
 static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
-                            struct cfg80211_scan_request *req)
+                            struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wl1251 *wl = hw->priv;
        struct sk_buff *skb;
        size_t ssid_len = 0;
 
 
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
-                            struct cfg80211_scan_request *req)
+                            struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wl1271 *wl = hw->priv;
        int ret;
        u8 *ssid = NULL;
 
        size_t len[IEEE80211_NUM_BANDS];
 };
 
+/**
+ * struct ieee80211_scan_ies - descriptors for different blocks of IEs
+ *
+ * This structure is used to point to different blocks of IEs in HW scan.
+ * These blocks contain the IEs passed by userspace and the ones generated
+ * by mac80211.
+ *
+ * @ies: pointers to band specific IEs.
+ * @len: lengths of band_specific IEs.
+ * @common_ies: IEs for all bands (especially vendor specific ones)
+ * @common_ie_len: length of the common_ies
+ */
+struct ieee80211_scan_ies {
+       const u8 *ies[IEEE80211_NUM_BANDS];
+       size_t len[IEEE80211_NUM_BANDS];
+       const u8 *common_ies;
+       size_t common_ie_len;
+};
+
+
 static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
 {
        return (struct ieee80211_tx_info *)skb->cb;
  *     on single-channel hardware.  It can also be used as an
  *     optimization in certain channel switch cases with
  *     multi-channel.
+ *
+ * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
+ *     in one command, mac80211 doesn't have to run separate scans per band.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
        IEEE80211_HW_SUPPORTS_HT_CCK_RATES              = 1<<27,
        IEEE80211_HW_CHANCTX_STA_CSA                    = 1<<28,
        IEEE80211_HW_CHANGE_RUNNING_CHANCTX             = 1<<29,
+       IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS           = 1<<30,
 };
 
 /**
        const struct ieee80211_cipher_scheme *cipher_schemes;
 };
 
+/**
+ * struct ieee80211_scan_request - hw scan request
+ *
+ * @ies: pointers different parts of IEs (in req.ie)
+ * @req: cfg80211 request.
+ */
+struct ieee80211_scan_request {
+       struct ieee80211_scan_ies ies;
+
+       /* Keep last */
+       struct cfg80211_scan_request req;
+};
+
 /**
  * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
  *
        void (*set_default_unicast_key)(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif, int idx);
        int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                      struct cfg80211_scan_request *req);
+                      struct ieee80211_scan_request *req);
        void (*cancel_hw_scan)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif);
        int (*sched_scan_start)(struct ieee80211_hw *hw,
 
 
 static inline int drv_hw_scan(struct ieee80211_local *local,
                              struct ieee80211_sub_if_data *sdata,
-                             struct cfg80211_scan_request *req)
+                             struct ieee80211_scan_request *req)
 {
        int ret;
 
 
        unsigned long scanning;
        struct cfg80211_ssid scan_ssid;
        struct cfg80211_scan_request *int_scan_req;
-       struct cfg80211_scan_request *scan_req, *hw_scan_req;
+       struct cfg80211_scan_request *scan_req;
+       struct ieee80211_scan_request *hw_scan_req;
        struct cfg80211_chan_def scan_chandef;
        enum ieee80211_band hw_scan_band;
        int scan_channel_idx;
                                    const u8 *bssid, u16 stype, u16 reason,
                                    bool send_frame, u8 *frame_buf);
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
-                            size_t buffer_len, const u8 *ie, size_t ie_len,
-                            enum ieee80211_band band, u32 rate_mask,
+                            size_t buffer_len,
+                            struct ieee80211_scan_ies *ie_desc,
+                            const u8 *ie, size_t ie_len,
+                            u8 bands_used, u32 *rate_masks,
                             struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
 
 {
        struct cfg80211_scan_request *req = local->scan_req;
        struct cfg80211_chan_def chandef;
-       enum ieee80211_band band;
+       u8 bands_used = 0;
        int i, ielen, n_chans;
 
        if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
                return false;
 
-       do {
-               if (local->hw_scan_band == IEEE80211_NUM_BANDS)
-                       return false;
-
-               band = local->hw_scan_band;
-               n_chans = 0;
+       if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
                for (i = 0; i < req->n_channels; i++) {
-                       if (req->channels[i]->band == band) {
-                               local->hw_scan_req->channels[n_chans] =
+                       local->hw_scan_req->req.channels[i] = req->channels[i];
+                       bands_used |= BIT(req->channels[i]->band);
+               }
+
+               n_chans = req->n_channels;
+       } else {
+               do {
+                       if (local->hw_scan_band == IEEE80211_NUM_BANDS)
+                               return false;
+
+                       n_chans = 0;
+
+                       for (i = 0; i < req->n_channels; i++) {
+                               if (req->channels[i]->band !=
+                                   local->hw_scan_band)
+                                       continue;
+                               local->hw_scan_req->req.channels[n_chans] =
                                                        req->channels[i];
                                n_chans++;
+                               bands_used |= BIT(req->channels[i]->band);
                        }
-               }
 
-               local->hw_scan_band++;
-       } while (!n_chans);
+                       local->hw_scan_band++;
+               } while (!n_chans);
+       }
 
-       local->hw_scan_req->n_channels = n_chans;
+       local->hw_scan_req->req.n_channels = n_chans;
        ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
-       ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+       ielen = ieee80211_build_preq_ies(local,
+                                        (u8 *)local->hw_scan_req->req.ie,
                                         local->hw_scan_ies_bufsize,
-                                        req->ie, req->ie_len, band,
-                                        req->rates[band], &chandef);
-       local->hw_scan_req->ie_len = ielen;
-       local->hw_scan_req->no_cck = req->no_cck;
+                                        &local->hw_scan_req->ies,
+                                        req->ie, req->ie_len,
+                                        bands_used, req->rates, &chandef);
+       local->hw_scan_req->req.ie_len = ielen;
+       local->hw_scan_req->req.no_cck = req->no_cck;
 
        return true;
 }
        if (WARN_ON(!local->scan_req))
                return;
 
-       if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
+       if (hw_scan && !aborted &&
+           !(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) &&
+           ieee80211_prep_hw_scan(local)) {
                int rc;
 
                rc = drv_hw_scan(local,
                u8 *ies;
 
                local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
+
+               if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
+                       int i, n_bands = 0;
+                       u8 bands_counted = 0;
+
+                       for (i = 0; i < req->n_channels; i++) {
+                               if (bands_counted & BIT(req->channels[i]->band))
+                                       continue;
+                               bands_counted |= BIT(req->channels[i]->band);
+                               n_bands++;
+                       }
+
+                       local->hw_scan_ies_bufsize *= n_bands;
+               }
+
                local->hw_scan_req = kmalloc(
                                sizeof(*local->hw_scan_req) +
                                req->n_channels * sizeof(req->channels[0]) +
                if (!local->hw_scan_req)
                        return -ENOMEM;
 
-               local->hw_scan_req->ssids = req->ssids;
-               local->hw_scan_req->n_ssids = req->n_ssids;
+               local->hw_scan_req->req.ssids = req->ssids;
+               local->hw_scan_req->req.n_ssids = req->n_ssids;
                ies = (u8 *)local->hw_scan_req +
                        sizeof(*local->hw_scan_req) +
                        req->n_channels * sizeof(req->channels[0]);
-               local->hw_scan_req->ie = ies;
-               local->hw_scan_req->flags = req->flags;
+               local->hw_scan_req->req.ie = ies;
+               local->hw_scan_req->req.flags = req->flags;
 
                local->hw_scan_band = 0;
 
        struct ieee80211_sched_scan_ies sched_scan_ies = {};
        struct cfg80211_chan_def chandef;
        int ret, i, iebufsz;
+       struct ieee80211_scan_ies dummy_ie_desc;
 
        iebufsz = local->scan_ies_len + req->ie_len;
 
                return -ENOTSUPP;
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               u32 rate_masks[IEEE80211_NUM_BANDS] = {};
+
                if (!local->hw.wiphy->bands[i])
                        continue;
 
                }
 
                ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+               rate_masks[i] = (u32) -1;
 
                sched_scan_ies.len[i] =
                        ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
-                                                iebufsz, req->ie, req->ie_len,
-                                                i, (u32) -1, &chandef);
+                                                iebufsz, &dummy_ie_desc,
+                                                req->ie, req->ie_len, BIT(i),
+                                                rate_masks, &chandef);
        }
 
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
 
        }
 }
 
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
-                            size_t buffer_len, const u8 *ie, size_t ie_len,
-                            enum ieee80211_band band, u32 rate_mask,
-                            struct cfg80211_chan_def *chandef)
+static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
+                                        u8 *buffer, size_t buffer_len,
+                                        const u8 *ie, size_t ie_len,
+                                        enum ieee80211_band band,
+                                        u32 rate_mask,
+                                        struct cfg80211_chan_def *chandef,
+                                        size_t *offset)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos = buffer, *end = buffer + buffer_len;
-       size_t offset = 0, noffset;
+       size_t noffset;
        int supp_rates_len, i;
        u8 rates[32];
        int num_rates;
        int shift;
        u32 rate_flags;
 
+       *offset = 0;
+
        sband = local->hw.wiphy->bands[band];
        if (WARN_ON_ONCE(!sband))
                return 0;
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_extrates,
                                             ARRAY_SIZE(before_extrates),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        ext_rates_len = num_rates - supp_rates_len;
                };
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_ht, ARRAY_SIZE(before_ht),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        if (sband->ht_cap.ht_supported) {
                };
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_vht, ARRAY_SIZE(before_vht),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        if (sband->vht_cap.vht_supported) {
                                                 sband->vht_cap.cap);
        }
 
-       /* add any remaining custom IEs */
-       if (ie && ie_len) {
-               noffset = ie_len;
-               if (end - pos < noffset - offset)
-                       goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-       }
-
        return pos - buffer;
  out_err:
        WARN_ONCE(1, "not enough space for preq IEs\n");
        return pos - buffer;
 }
 
+int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+                            size_t buffer_len,
+                            struct ieee80211_scan_ies *ie_desc,
+                            const u8 *ie, size_t ie_len,
+                            u8 bands_used, u32 *rate_masks,
+                            struct cfg80211_chan_def *chandef)
+{
+       size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
+       int i;
+
+       memset(ie_desc, 0, sizeof(*ie_desc));
+
+       for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               if (bands_used & BIT(i)) {
+                       pos += ieee80211_build_preq_ies_band(local,
+                                                            buffer + pos,
+                                                            buffer_len - pos,
+                                                            ie, ie_len, i,
+                                                            rate_masks[i],
+                                                            chandef,
+                                                            &custom_ie_offset);
+                       ie_desc->ies[i] = buffer + old_pos;
+                       ie_desc->len[i] = pos - old_pos;
+                       old_pos = pos;
+               }
+       }
+
+       /* add any remaining custom IEs */
+       if (ie && ie_len) {
+               if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset,
+                             "not enough space for preq custom IEs\n"))
+                       return pos;
+               memcpy(buffer + pos, ie + custom_ie_offset,
+                      ie_len - custom_ie_offset);
+               ie_desc->common_ies = buffer + pos;
+               ie_desc->common_ie_len = ie_len - custom_ie_offset;
+               pos += ie_len - custom_ie_offset;
+       }
+
+       return pos;
+};
+
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
                                          struct ieee80211_channel *chan,
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        int ies_len;
+       u32 rate_masks[IEEE80211_NUM_BANDS] = {};
+       struct ieee80211_scan_ies dummy_ie_desc;
 
        /*
         * Do not send DS Channel parameter for directed probe requests
        if (!skb)
                return NULL;
 
+       rate_masks[chan->band] = ratemask;
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
-                                          skb_tailroom(skb),
-                                          ie, ie_len, chan->band,
-                                          ratemask, &chandef);
+                                          skb_tailroom(skb), &dummy_ie_desc,
+                                          ie, ie_len, BIT(chan->band),
+                                          rate_masks, &chandef);
        skb_put(skb, ies_len);
 
        if (dst) {