]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/mac80211/mlme.c
Merge commit '371a00448f95adaa612cf1a0b31a11e7093bc706' of 'git://git.kernel.org...
[mirror_ubuntu-artful-kernel.git] / net / mac80211 / mlme.c
index a4a5acdbaa4dd3ac5e2fb8c5f0ff1d1b97553d6a..a8cf70bf1cbac14bd7b53ff8aa1e51ca80f6024b 100644 (file)
@@ -146,6 +146,9 @@ void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
        if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
                return;
 
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+               return;
+
        mod_timer(&sdata->u.mgd.bcn_mon_timer,
                  round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout));
 }
@@ -182,15 +185,15 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
        u16 ht_opmode;
        bool disable_40 = false;
 
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+       sband = local->hw.wiphy->bands[local->oper_channel->band];
 
        switch (sdata->vif.bss_conf.channel_type) {
        case NL80211_CHAN_HT40PLUS:
-               if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+               if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
                        disable_40 = true;
                break;
        case NL80211_CHAN_HT40MINUS:
-               if (local->hw.conf.channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+               if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
                        disable_40 = true;
                break;
        default:
@@ -326,6 +329,26 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
        ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
 }
 
+static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb,
+                                struct ieee80211_supported_band *sband)
+{
+       u8 *pos;
+       u32 cap;
+       struct ieee80211_sta_vht_cap vht_cap;
+
+       BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
+
+       memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+
+       /* determine capability flags */
+       cap = vht_cap.cap;
+
+       /* reserve and fill IE */
+       pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2);
+       ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
+}
+
 static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -371,6 +394,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        4 + /* power capability */
                        2 + 2 * sband->n_channels + /* supported channels */
                        2 + sizeof(struct ieee80211_ht_cap) + /* HT */
+                       2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */
                        assoc_data->ie_len + /* extra IEs */
                        9, /* WMM */
                        GFP_KERNEL);
@@ -503,6 +527,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
                                    sband, local->oper_channel, ifmgd->ap_smps);
 
+       if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
+               ieee80211_add_vht_ie(sdata, skb, sband);
+
        /* if present, add any custom non-vendor IEs that go after HT */
        if (assoc_data->ie_len && assoc_data->ie) {
                noffset = ieee80211_ie_split_vendor(assoc_data->ie,
@@ -583,8 +610,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                        IEEE80211_SKB_CB(skb)->flags |=
                                IEEE80211_TX_INTFL_DONT_ENCRYPT;
 
-               drv_mgd_prepare_tx(local, sdata);
-
                ieee80211_tx_skb(sdata, skb);
        }
 }
@@ -687,6 +712,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->local->oper_channel;
 
+       /* XXX: wait for a beacon first? */
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
  out:
@@ -763,36 +789,32 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        sdata->local->csa_channel = new_ch;
 
+       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+
+       if (sw_elem->mode)
+               ieee80211_stop_queues_by_reason(&sdata->local->hw,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+
        if (sdata->local->ops->channel_switch) {
                /* use driver's channel switch callback */
-               struct ieee80211_channel_switch ch_switch;
-               memset(&ch_switch, 0, sizeof(ch_switch));
-               ch_switch.timestamp = timestamp;
-               if (sw_elem->mode) {
-                       ch_switch.block_tx = true;
-                       ieee80211_stop_queues_by_reason(&sdata->local->hw,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
-               }
-               ch_switch.channel = new_ch;
-               ch_switch.count = sw_elem->count;
-               ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+               struct ieee80211_channel_switch ch_switch = {
+                       .timestamp = timestamp,
+                       .block_tx = sw_elem->mode,
+                       .channel = new_ch,
+                       .count = sw_elem->count,
+               };
+
                drv_channel_switch(sdata->local, &ch_switch);
                return;
        }
 
        /* channel switch handled in software */
-       if (sw_elem->count <= 1) {
+       if (sw_elem->count <= 1)
                ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
-       } else {
-               if (sw_elem->mode)
-                       ieee80211_stop_queues_by_reason(&sdata->local->hw,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
-               ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+       else
                mod_timer(&ifmgd->chswitch_timer,
-                         jiffies +
-                         msecs_to_jiffies(sw_elem->count *
-                                          cbss->beacon_interval));
-       }
+                         TU_TO_EXP_TIME(sw_elem->count *
+                                        cbss->beacon_interval));
 }
 
 static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
@@ -1007,6 +1029,16 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
        ieee80211_change_ps(local);
 }
 
+void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata)
+{
+       bool ps_allowed = ieee80211_powersave_allowed(sdata);
+
+       if (sdata->vif.bss_conf.ps != ps_allowed) {
+               sdata->vif.bss_conf.ps = ps_allowed;
+               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS);
+       }
+}
+
 void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
 {
        struct ieee80211_local *local =
@@ -1239,7 +1271,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
        }
 
        use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
-       if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ)
+       if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ)
                use_short_slot = true;
 
        if (use_protection != bss_conf->use_cts_prot) {
@@ -1310,6 +1342,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        ieee80211_recalc_smps(local);
        mutex_unlock(&local->iflist_mtx);
 
+       ieee80211_recalc_ps_vif(sdata);
+
        netif_tx_start_all_queues(sdata->dev);
        netif_carrier_on(sdata->dev);
 }
@@ -1371,6 +1405,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        }
        local->ps_sdata = NULL;
 
+       /* disable per-vif ps */
+       ieee80211_recalc_ps_vif(sdata);
+
        /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
        if (tx)
                drv_flush(local, false);
@@ -1542,7 +1579,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
                        ssid_len = ssid[1];
 
                ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
-                                        0, (u32) -1, true, false);
+                                        0, (u32) -1, true, false,
+                                        ifmgd->associated->channel);
        }
 
        ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
@@ -1645,7 +1683,9 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
                ssid_len = ssid[1];
 
        skb = ieee80211_build_probe_req(sdata, cbss->bssid,
-                                       (u32) -1, ssid + 2, ssid_len,
+                                       (u32) -1,
+                                       sdata->local->oper_channel,
+                                       ssid + 2, ssid_len,
                                        NULL, 0, true);
 
        return skb;
@@ -1656,7 +1696,6 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
-       u8 bssid[ETH_ALEN];
        u8 frame_buf[DEAUTH_DISASSOC_LEN];
 
        mutex_lock(&ifmgd->mtx);
@@ -1665,9 +1704,8 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
                return;
        }
 
-       memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
-
-       sdata_info(sdata, "Connection to AP %pM lost\n", bssid);
+       sdata_info(sdata, "Connection to AP %pM lost\n",
+                  ifmgd->associated->bssid);
 
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
@@ -1685,7 +1723,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
        mutex_unlock(&local->mtx);
 }
 
-void ieee80211_beacon_connection_loss_work(struct work_struct *work)
+static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
 {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
@@ -2232,14 +2270,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                mutex_unlock(&local->iflist_mtx);
        }
 
-       if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
-           (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
-                                                       ETH_ALEN) == 0)) {
-               struct ieee80211_channel_sw_ie *sw_elem =
-                       (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
-               ieee80211_sta_process_chanswitch(sdata, sw_elem,
+       if (elems->ch_switch_ie &&
+           memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0)
+               ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie,
                                                 bss, rx_status->mactime);
-       }
 }
 
 
@@ -2326,7 +2360,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (baselen > len)
                return;
 
-       if (rx_status->freq != local->hw.conf.channel->center_freq)
+       if (rx_status->freq != local->oper_channel->center_freq)
                return;
 
        if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
@@ -2490,7 +2524,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
            !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
                struct ieee80211_supported_band *sband;
 
-               sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+               sband = local->hw.wiphy->bands[local->oper_channel->band];
 
                changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
                                                  bssid, true);
@@ -2673,7 +2707,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
                 * will not answer to direct packet in unassociated state.
                 */
                ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
-                                        NULL, 0, (u32) -1, true, false);
+                                        NULL, 0, (u32) -1, true, false,
+                                        auth_data->bss->channel);
        }
 
        auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -3000,41 +3035,17 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
        return 0;
 }
 
-static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
-                                    struct cfg80211_bss *cbss, bool assoc)
+static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
+                                 struct cfg80211_bss *cbss)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_bss *bss = (void *)cbss->priv;
-       struct sta_info *sta = NULL;
-       bool have_sta = false;
-       int err;
        int ht_cfreq;
        enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
        const u8 *ht_oper_ie;
        const struct ieee80211_ht_operation *ht_oper = NULL;
        struct ieee80211_supported_band *sband;
 
-       if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
-               return -EINVAL;
-
-       if (assoc) {
-               rcu_read_lock();
-               have_sta = sta_info_get(sdata, cbss->bssid);
-               rcu_read_unlock();
-       }
-
-       if (!have_sta) {
-               sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
-               if (!sta)
-                       return -ENOMEM;
-       }
-
-       mutex_lock(&local->mtx);
-       ieee80211_recalc_idle(sdata->local);
-       mutex_unlock(&local->mtx);
-
-       /* switch to the right channel */
        sband = local->hw.wiphy->bands[cbss->channel->band];
 
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
@@ -3097,10 +3108,51 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
        local->oper_channel = cbss->channel;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
-       if (sta) {
+       return 0;
+}
+
+static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
+                                    struct cfg80211_bss *cbss, bool assoc)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_bss *bss = (void *)cbss->priv;
+       struct sta_info *new_sta = NULL;
+       bool have_sta = false;
+       int err;
+
+       if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
+               return -EINVAL;
+
+       if (assoc) {
+               rcu_read_lock();
+               have_sta = sta_info_get(sdata, cbss->bssid);
+               rcu_read_unlock();
+       }
+
+       if (!have_sta) {
+               new_sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
+               if (!new_sta)
+                       return -ENOMEM;
+       }
+
+       mutex_lock(&local->mtx);
+       ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&local->mtx);
+
+       if (new_sta) {
                u32 rates = 0, basic_rates = 0;
                bool have_higher_than_11mbit;
                int min_rate = INT_MAX, min_rate_index = -1;
+               struct ieee80211_supported_band *sband;
+
+               sband = local->hw.wiphy->bands[cbss->channel->band];
+
+               err = ieee80211_prep_channel(sdata, cbss);
+               if (err) {
+                       sta_info_free(local, new_sta);
+                       return err;
+               }
 
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
@@ -3122,7 +3174,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                        basic_rates = BIT(min_rate_index);
                }
 
-               sta->sta.supp_rates[cbss->channel->band] = rates;
+               new_sta->sta.supp_rates[cbss->channel->band] = rates;
                sdata->vif.bss_conf.basic_rates = basic_rates;
 
                /* cf. IEEE 802.11 9.2.12 */
@@ -3145,10 +3197,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                        BSS_CHANGED_BEACON_INT);
 
                if (assoc)
-                       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+                       sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH);
 
-               err = sta_info_insert(sta);
-               sta = NULL;
+               err = sta_info_insert(new_sta);
+               new_sta = NULL;
                if (err) {
                        sdata_info(sdata,
                                   "failed to insert STA entry for the AP (error %d)\n",
@@ -3300,9 +3352,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        }
 
        /* prepare assoc data */
-
-       ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
-       ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+       
+       /*
+        * keep only the 40 MHz disable bit set as it might have
+        * been set during authentication already, all other bits
+        * should be reset for a new connection
+        */
+       ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ;
 
        ifmgd->beacon_crc_valid = false;
 
@@ -3318,21 +3374,34 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
                        ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+                       ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
                        netdev_info(sdata->dev,
-                                   "disabling HT due to WEP/TKIP use\n");
+                                   "disabling HT/VHT due to WEP/TKIP use\n");
                }
        }
 
-       if (req->flags & ASSOC_REQ_DISABLE_HT)
+       if (req->flags & ASSOC_REQ_DISABLE_HT) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+       }
 
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
            local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
-               netdev_info(sdata->dev,
-                           "disabling HT as WMM/QoS is not supported\n");
+               if (!bss->wmm_used)
+                       netdev_info(sdata->dev,
+                                   "disabling HT as WMM/QoS is not supported by the AP\n");
+       }
+
+       /* disable VHT if we don't support it or the AP doesn't use WMM */
+       if (!sband->vht_cap.vht_supported ||
+           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+               ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+               if (!bss->wmm_used)
+                       netdev_info(sdata->dev,
+                                   "disabling VHT as WMM/QoS is not supported by the AP\n");
        }
 
        memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
@@ -3467,14 +3536,17 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                   req->bssid, req->reason_code);
 
        if (ifmgd->associated &&
-           ether_addr_equal(ifmgd->associated->bssid, req->bssid))
+           ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       req->reason_code, true, frame_buf);
-       else
+       } else {
+               drv_mgd_prepare_tx(sdata->local, sdata);
                ieee80211_send_deauth_disassoc(sdata, req->bssid,
                                               IEEE80211_STYPE_DEAUTH,
                                               req->reason_code, true,
                                               frame_buf);
+       }
+
        mutex_unlock(&ifmgd->mtx);
 
        __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);