packet_diag: disclose meminfo values
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / mlme.c
index c53aedb47a6a2dcd4e3a0b30da3c392e39825501..29620bfc7a69663c34ab89b5bd03875eaaa4c25c 100644 (file)
@@ -1027,7 +1027,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        u8 new_chan_no;
        u8 count;
        u8 mode;
+       struct ieee80211_channel *new_chan;
        struct cfg80211_chan_def new_chandef = {};
+       struct cfg80211_chan_def new_vht_chandef = {};
+       const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
+       const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
        int secondary_channel_offset = -1;
 
        ASSERT_MGD_MTX(ifmgd);
@@ -1042,18 +1046,17 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
                return;
 
-       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
-               /* if HT is enabled and the IE not present, it's still HT */
-               secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
-               if (elems->sec_chan_offs)
-                       secondary_channel_offset =
-                               elems->sec_chan_offs->sec_chan_offs;
+       sec_chan_offs = elems->sec_chan_offs;
+       wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
+
+       if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
+                           IEEE80211_STA_DISABLE_40MHZ)) {
+               sec_chan_offs = NULL;
+               wide_bw_chansw_ie = NULL;
        }
 
-       if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
-           (secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE ||
-            secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW))
-               secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+       if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
+               wide_bw_chansw_ie = NULL;
 
        if (elems->ext_chansw_ie) {
                if (!ieee80211_operating_class_to_band(
@@ -1081,9 +1084,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        bss = (void *)cbss->priv;
 
        new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
-       new_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-       if (!new_chandef.chan ||
-           new_chandef.chan->flags & IEEE80211_CHAN_DISABLED) {
+       new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
+       if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
                sdata_info(sdata,
                           "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
                           ifmgd->associated->bssid, new_freq);
@@ -1092,27 +1094,87 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       if (sec_chan_offs) {
+               secondary_channel_offset = sec_chan_offs->sec_chan_offs;
+       } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
+               /* if HT is enabled and the IE not present, it's still HT */
+               secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+       }
+
        switch (secondary_channel_offset) {
        default:
                /* secondary_channel_offset was present but is invalid */
        case IEEE80211_HT_PARAM_CHA_SEC_NONE:
-               cfg80211_chandef_create(&new_chandef, new_chandef.chan,
+               cfg80211_chandef_create(&new_chandef, new_chan,
                                        NL80211_CHAN_HT20);
                break;
        case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-               cfg80211_chandef_create(&new_chandef, new_chandef.chan,
+               cfg80211_chandef_create(&new_chandef, new_chan,
                                        NL80211_CHAN_HT40PLUS);
                break;
        case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-               cfg80211_chandef_create(&new_chandef, new_chandef.chan,
+               cfg80211_chandef_create(&new_chandef, new_chan,
                                        NL80211_CHAN_HT40MINUS);
                break;
        case -1:
-               cfg80211_chandef_create(&new_chandef, new_chandef.chan,
+               cfg80211_chandef_create(&new_chandef, new_chan,
                                        NL80211_CHAN_NO_HT);
                break;
        }
 
+       if (wide_bw_chansw_ie) {
+               new_vht_chandef.chan = new_chan;
+               new_vht_chandef.center_freq1 =
+                       ieee80211_channel_to_frequency(
+                               wide_bw_chansw_ie->new_center_freq_seg0,
+                               new_band);
+
+               switch (wide_bw_chansw_ie->new_channel_width) {
+               default:
+                       /* hmmm, ignore VHT and use HT if present */
+               case IEEE80211_VHT_CHANWIDTH_USE_HT:
+                       new_vht_chandef.chan = NULL;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_80MHZ:
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_160MHZ:
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
+                       break;
+               case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+                       /* field is otherwise reserved */
+                       new_vht_chandef.center_freq2 =
+                               ieee80211_channel_to_frequency(
+                                       wide_bw_chansw_ie->new_center_freq_seg1,
+                                       new_band);
+                       new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+                       break;
+               }
+               if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
+                   new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
+                       chandef_downgrade(&new_vht_chandef);
+               if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
+                   new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
+                       chandef_downgrade(&new_vht_chandef);
+               if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
+                   new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
+                       chandef_downgrade(&new_vht_chandef);
+       }
+
+       /* if VHT data is there validate & use it */
+       if (new_vht_chandef.chan) {
+               if (!cfg80211_chandef_compatible(&new_vht_chandef,
+                                                &new_chandef)) {
+                       sdata_info(sdata,
+                                  "AP %pM CSA has inconsistent channel data, disconnecting\n",
+                                  ifmgd->associated->bssid);
+                       ieee80211_queue_work(&local->hw,
+                                            &ifmgd->csa_connection_drop_work);
+                       return;
+               }
+               new_chandef = new_vht_chandef;
+       }
+
        if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
                                     IEEE80211_CHAN_DISABLED)) {
                sdata_info(sdata,
@@ -1599,6 +1661,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
                params.cw_min = ecw2cw(pos[1] & 0x0f);
                params.txop = get_unaligned_le16(pos + 2);
+               params.acm = acm;
                params.uapsd = uapsd;
 
                mlme_dbg(sdata,
@@ -2153,7 +2216,6 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
 
        trace_api_beacon_loss(sdata);
 
-       WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
        sdata->u.mgd.connection_loss = false;
        ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 }
@@ -2203,7 +2265,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
        u32 tx_flags = 0;
 
        pos = mgmt->u.auth.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
        if (!elems.challenge)
                return;
        auth_data->expected_transaction = 4;
@@ -2468,7 +2530,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        }
 
        pos = mgmt->u.assoc_resp.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
 
        if (!elems.supp_rates) {
                sdata_info(sdata, "no SuppRates element in AssocResp\n");
@@ -2637,7 +2699,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                   capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
 
        pos = mgmt->u.assoc_resp.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
 
        if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
            elems.timeout_int &&
@@ -2760,7 +2822,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
                return;
 
        ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
-                               &elems);
+                              false, &elems);
 
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
 
@@ -2843,7 +2905,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
            ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
                ieee802_11_parse_elems(mgmt->u.beacon.variable,
-                                      len - baselen, &elems);
+                                      len - baselen, false, &elems);
 
                ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
                ifmgd->assoc_data->have_beacon = true;
@@ -2953,7 +3015,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
        ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
-                                         len - baselen, &elems,
+                                         len - baselen, false, &elems,
                                          care_about_ies, ncrc);
 
        if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
@@ -3141,7 +3203,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 
                        ieee802_11_parse_elems(
                                mgmt->u.action.u.chan_switch.variable,
-                               ies_len, &elems);
+                               ies_len, true, &elems);
 
                        if (elems.parse_error)
                                break;
@@ -3159,7 +3221,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 
                        ieee802_11_parse_elems(
                                mgmt->u.action.u.ext_chan_switch.variable,
-                               ies_len, &elems);
+                               ies_len, true, &elems);
 
                        if (elems.parse_error)
                                break;
@@ -3604,8 +3666,10 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
 
        /* Restart STA timers */
        rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list)
-               ieee80211_restart_sta_timer(sdata);
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (ieee80211_sdata_running(sdata))
+                       ieee80211_restart_sta_timer(sdata);
+       }
        rcu_read_unlock();
 }
 
@@ -3958,8 +4022,16 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        /* prep auth_data so we don't go into idle on disassoc */
        ifmgd->auth_data = auth_data;
 
-       if (ifmgd->associated)
-               ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
+       if (ifmgd->associated) {
+               u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+
+               ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+                                      WLAN_REASON_UNSPECIFIED,
+                                      false, frame_buf);
+
+               __cfg80211_send_deauth(sdata->dev, frame_buf,
+                                      sizeof(frame_buf));
+       }
 
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
 
@@ -4019,8 +4091,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        mutex_lock(&ifmgd->mtx);
 
-       if (ifmgd->associated)
-               ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
+       if (ifmgd->associated) {
+               u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+
+               ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+                                      WLAN_REASON_UNSPECIFIED,
+                                      false, frame_buf);
+
+               __cfg80211_send_deauth(sdata->dev, frame_buf,
+                                      sizeof(frame_buf));
+       }
 
        if (ifmgd->auth_data && !ifmgd->auth_data->done) {
                err = -EBUSY;