mac80211: avoid reconfig if no interfaces are up
[platform/kernel/linux-starfive.git] / net / mac80211 / util.c
index 3c61060..0f9bf47 100644 (file)
@@ -576,15 +576,19 @@ ieee80211_get_vif_queues(struct ieee80211_local *local,
        return queues;
 }
 
-void ieee80211_flush_queues(struct ieee80211_local *local,
-                           struct ieee80211_sub_if_data *sdata)
+void __ieee80211_flush_queues(struct ieee80211_local *local,
+                             struct ieee80211_sub_if_data *sdata,
+                             unsigned int queues)
 {
-       unsigned int queues;
-
        if (!local->ops->flush)
                return;
 
-       queues = ieee80211_get_vif_queues(local, sdata);
+       /*
+        * If no queue was set, or if the HW doesn't support
+        * IEEE80211_HW_QUEUE_CONTROL - flush all queues
+        */
+       if (!queues || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
+               queues = ieee80211_get_vif_queues(local, sdata);
 
        ieee80211_stop_queues_by_reason(&local->hw, queues,
                                        IEEE80211_QUEUE_STOP_REASON_FLUSH,
@@ -597,6 +601,12 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
                                        false);
 }
 
+void ieee80211_flush_queues(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata)
+{
+       __ieee80211_flush_queues(local, sdata, 0);
+}
+
 void ieee80211_stop_vif_queues(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata,
                               enum queue_stop_reason reason)
@@ -693,6 +703,34 @@ void ieee80211_iterate_active_interfaces_rtnl(
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
 
+static void __iterate_stations(struct ieee80211_local *local,
+                              void (*iterator)(void *data,
+                                               struct ieee80211_sta *sta),
+                              void *data)
+{
+       struct sta_info *sta;
+
+       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (!sta->uploaded)
+                       continue;
+
+               iterator(data, &sta->sta);
+       }
+}
+
+void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
+                       void (*iterator)(void *data,
+                                        struct ieee80211_sta *sta),
+                       void *data)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       rcu_read_lock();
+       __iterate_stations(local, iterator, data);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
+
 struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -803,6 +841,9 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
                case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
                case WLAN_EID_CHAN_SWITCH_PARAM:
+               case WLAN_EID_EXT_CAPABILITY:
+               case WLAN_EID_CHAN_SWITCH_TIMING:
+               case WLAN_EID_LINK_ID:
                /*
                 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
                 * that if the content gets bigger it might be needed more than once
@@ -822,6 +863,24 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
                elem_parse_failed = false;
 
                switch (id) {
+               case WLAN_EID_LINK_ID:
+                       if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) {
+                               elem_parse_failed = true;
+                               break;
+                       }
+                       elems->lnk_id = (void *)(pos - 2);
+                       break;
+               case WLAN_EID_CHAN_SWITCH_TIMING:
+                       if (elen != sizeof(struct ieee80211_ch_switch_timing)) {
+                               elem_parse_failed = true;
+                               break;
+                       }
+                       elems->ch_sw_timing = (void *)pos;
+                       break;
+               case WLAN_EID_EXT_CAPABILITY:
+                       elems->ext_capab = pos;
+                       elems->ext_capab_len = elen;
+                       break;
                case WLAN_EID_SSID:
                        elems->ssid = pos;
                        elems->ssid_len = elen;
@@ -1073,6 +1132,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_chanctx_conf *chanctx_conf;
        int ac;
        bool use_11b, enable_qos;
+       bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
        int aCWmin, aCWmax;
 
        if (!local->ops->conf_tx)
@@ -1097,6 +1157,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
         */
        enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
 
+       is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
+
        /* Set defaults according to 802.11-2007 Table 7-37 */
        aCWmax = 1023;
        if (use_11b)
@@ -1118,7 +1180,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                                qparam.cw_max = aCWmax;
                                qparam.cw_min = aCWmin;
                                qparam.txop = 0;
-                               qparam.aifs = 7;
+                               if (is_ocb)
+                                       qparam.aifs = 9;
+                               else
+                                       qparam.aifs = 7;
                                break;
                        /* never happens but let's not leave undefined */
                        default:
@@ -1126,21 +1191,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                                qparam.cw_max = aCWmax;
                                qparam.cw_min = aCWmin;
                                qparam.txop = 0;
-                               qparam.aifs = 3;
+                               if (is_ocb)
+                                       qparam.aifs = 6;
+                               else
+                                       qparam.aifs = 3;
                                break;
                        case IEEE80211_AC_VI:
                                qparam.cw_max = aCWmin;
                                qparam.cw_min = (aCWmin + 1) / 2 - 1;
-                               if (use_11b)
+                               if (is_ocb)
+                                       qparam.txop = 0;
+                               else if (use_11b)
                                        qparam.txop = 6016/32;
                                else
                                        qparam.txop = 3008/32;
-                               qparam.aifs = 2;
+
+                               if (is_ocb)
+                                       qparam.aifs = 3;
+                               else
+                                       qparam.aifs = 2;
                                break;
                        case IEEE80211_AC_VO:
                                qparam.cw_max = (aCWmin + 1) / 2 - 1;
                                qparam.cw_min = (aCWmin + 1) / 4 - 1;
-                               if (use_11b)
+                               if (is_ocb)
+                                       qparam.txop = 0;
+                               else if (use_11b)
                                        qparam.txop = 3264/32;
                                else
                                        qparam.txop = 1504/32;
@@ -1263,6 +1339,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
        int ext_rates_len;
        int shift;
        u32 rate_flags;
+       bool have_80mhz = false;
 
        *offset = 0;
 
@@ -1391,7 +1468,15 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
                *offset = noffset;
        }
 
-       if (sband->vht_cap.vht_supported) {
+       /* Check if any channel in this sband supports at least 80 MHz */
+       for (i = 0; i < sband->n_channels; i++) {
+               if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) {
+                       have_80mhz = true;
+                       break;
+               }
+       }
+
+       if (sband->vht_cap.vht_supported && have_80mhz) {
                if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
                        goto out_err;
                pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
@@ -1447,7 +1532,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
 };
 
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
-                                         u8 *dst, u32 ratemask,
+                                         const u8 *src, const u8 *dst,
+                                         u32 ratemask,
                                          struct ieee80211_channel *chan,
                                          const u8 *ssid, size_t ssid_len,
                                          const u8 *ie, size_t ie_len,
@@ -1472,8 +1558,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        else
                chandef.chan = chan;
 
-       skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
-                                    ssid, ssid_len, 100 + ie_len);
+       skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
+                                    100 + ie_len);
        if (!skb)
                return NULL;
 
@@ -1495,7 +1581,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        return skb;
 }
 
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
+                             const u8 *src, const u8 *dst,
                              const u8 *ssid, size_t ssid_len,
                              const u8 *ie, size_t ie_len,
                              u32 ratemask, bool directed, u32 tx_flags,
@@ -1503,7 +1590,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
 {
        struct sk_buff *skb;
 
-       skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
+       skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel,
                                        ssid, ssid_len,
                                        ie, ie_len, directed);
        if (skb) {
@@ -1645,8 +1732,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        int res, i;
        bool reconfig_due_to_wowlan = false;
        struct ieee80211_sub_if_data *sched_scan_sdata;
+       struct cfg80211_sched_scan_request *sched_scan_req;
        bool sched_scan_stopped = false;
 
+       /* nothing to do if HW shouldn't run */
+       if (!local->open_count)
+               goto wake_up;
+
 #ifdef CONFIG_PM
        if (local->suspended)
                local->resuming = true;
@@ -1668,9 +1760,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                reconfig_due_to_wowlan = true;
        }
 #endif
-       /* everything else happens only if HW was up & running */
-       if (!local->open_count)
-               goto wake_up;
 
        /*
         * Upon resume hardware can sometimes be goofy due to
@@ -1813,6 +1902,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                        ieee80211_bss_info_change_notify(sdata, changed);
                        sdata_unlock(sdata);
                        break;
+               case NL80211_IFTYPE_OCB:
+                       changed |= BSS_CHANGED_OCB;
+                       ieee80211_bss_info_change_notify(sdata, changed);
+                       break;
                case NL80211_IFTYPE_ADHOC:
                        changed |= BSS_CHANGED_IBSS;
                        /* fall through */
@@ -1931,13 +2024,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mutex_lock(&local->mtx);
        sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
                                                lockdep_is_held(&local->mtx));
-       if (sched_scan_sdata && local->sched_scan_req)
+       sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
+                                               lockdep_is_held(&local->mtx));
+       if (sched_scan_sdata && sched_scan_req)
                /*
                 * Sched scan stopped, but we don't want to report it. Instead,
                 * we're trying to reschedule.
                 */
                if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
-                                                        local->sched_scan_req))
+                                                        sched_scan_req))
                        sched_scan_stopped = true;
        mutex_unlock(&local->mtx);
 
@@ -1948,8 +2043,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
         * If this is for hw restart things are still running.
         * We may want to change that later, however.
         */
-       if (!local->suspended || reconfig_due_to_wowlan)
-               drv_restart_complete(local);
+       if (local->open_count && (!local->suspended || reconfig_due_to_wowlan))
+               drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
 
        if (!local->suspended)
                return 0;
@@ -1960,6 +2055,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mb();
        local->resuming = false;
 
+       if (local->open_count && !reconfig_due_to_wowlan)
+               drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
+
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (!ieee80211_sdata_running(sdata))
                        continue;
@@ -2052,42 +2150,36 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
        return false;
 }
 
-/**
- * ieee80211_ie_split - split an IE buffer according to ordering
- *
- * @ies: the IE buffer
- * @ielen: the length of the IE buffer
- * @ids: an array with element IDs that are allowed before
- *     the split
- * @n_ids: the size of the element ID array
- * @offset: offset where to start splitting in the buffer
- *
- * This function splits an IE buffer by updating the @offset
- * variable to point to the location where the buffer should be
- * split.
- *
- * It assumes that the given IE buffer is well-formed, this
- * has to be guaranteed by the caller!
- *
- * It also assumes that the IEs in the buffer are ordered
- * correctly, if not the result of using this function will not
- * be ordered correctly either, i.e. it does no reordering.
- *
- * The function returns the offset where the next part of the
- * buffer starts, which may be @ielen if the entire (remainder)
- * of the buffer should be used.
- */
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
-                         const u8 *ids, int n_ids, size_t offset)
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+                             const u8 *ids, int n_ids,
+                             const u8 *after_ric, int n_after_ric,
+                             size_t offset)
 {
        size_t pos = offset;
 
-       while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
-               pos += 2 + ies[pos + 1];
+       while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
+               if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
+                       pos += 2 + ies[pos + 1];
+
+                       while (pos < ielen &&
+                              !ieee80211_id_in_list(after_ric, n_after_ric,
+                                                    ies[pos]))
+                               pos += 2 + ies[pos + 1];
+               } else {
+                       pos += 2 + ies[pos + 1];
+               }
+       }
 
        return pos;
 }
 
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+                         const u8 *ids, int n_ids, size_t offset)
+{
+       return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
+EXPORT_SYMBOL(ieee80211_ie_split);
+
 size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
 {
        size_t pos = offset;
@@ -2526,11 +2618,23 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work)
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local, radar_detected_work);
        struct cfg80211_chan_def chandef = local->hw.conf.chandef;
+       struct ieee80211_chanctx *ctx;
+       int num_chanctx = 0;
+
+       mutex_lock(&local->chanctx_mtx);
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               num_chanctx++;
+               chandef = ctx->conf.def;
+       }
+       mutex_unlock(&local->chanctx_mtx);
 
        ieee80211_dfs_cac_cancel(local);
 
-       if (local->use_chanctx)
-               /* currently not handled */
+       if (num_chanctx > 1)
+               /* XXX: multi-channel is not supported yet */
                WARN_ON(1);
        else
                cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);