]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/mac80211/mlme.c
mac80211: notify the driver about deauth
[karo-tx-linux.git] / net / mac80211 / mlme.c
index 10ac6324c1d014c708749748ce89ef31055561cf..22b1259136619162cec8932d47711c70e60d12c4 100644 (file)
@@ -1157,11 +1157,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (!conf) {
                sdata_info(sdata,
                           "no channel context assigned to vif?, disconnecting\n");
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&local->chanctx_mtx);
-               mutex_unlock(&local->mtx);
-               return;
+               goto drop_connection;
        }
 
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
@@ -1170,11 +1166,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
            !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
                sdata_info(sdata,
                           "driver doesn't support chan-switch with channel contexts\n");
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&local->chanctx_mtx);
-               mutex_unlock(&local->mtx);
-               return;
+               goto drop_connection;
        }
 
        ch_switch.timestamp = timestamp;
@@ -1186,11 +1178,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        if (drv_pre_channel_switch(sdata, &ch_switch)) {
                sdata_info(sdata,
                           "preparing for channel switch failed, disconnecting\n");
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&local->chanctx_mtx);
-               mutex_unlock(&local->mtx);
-               return;
+               goto drop_connection;
        }
 
        res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1199,11 +1187,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                sdata_info(sdata,
                           "failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
                           res);
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&local->chanctx_mtx);
-               mutex_unlock(&local->mtx);
-               return;
+               goto drop_connection;
        }
        mutex_unlock(&local->chanctx_mtx);
 
@@ -1232,6 +1216,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                mod_timer(&ifmgd->chswitch_timer,
                          TU_TO_EXP_TIME((csa_ie.count - 1) *
                                         cbss->beacon_interval));
+       return;
+ drop_connection:
+       ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
+       mutex_unlock(&local->chanctx_mtx);
+       mutex_unlock(&local->mtx);
 }
 
 static bool
@@ -1621,9 +1610,6 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
 {
        struct ieee80211_local *local = (void *) data;
 
-       if (local->quiescing || local->suspended)
-               return;
-
        ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
 }
 
@@ -2359,6 +2345,24 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_ap_probereq_get);
 
+static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
+                                       const u8 *buf, size_t len, bool tx,
+                                       u16 reason)
+{
+       struct ieee80211_event event = {
+               .type = MLME_EVENT,
+               .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
+               .u.mlme.reason = reason,
+       };
+
+       if (tx)
+               cfg80211_tx_mlme_mgmt(sdata->dev, buf, len);
+       else
+               cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
+
+       drv_event_callback(sdata->local, sdata, &event);
+}
+
 static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
@@ -2384,8 +2388,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        }
        mutex_unlock(&local->mtx);
 
-       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                             IEEE80211_DEAUTH_FRAME_LEN);
+       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+                                   WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+
        sdata_unlock(sdata);
 }
 
@@ -2509,6 +2514,10 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
        u8 bssid[ETH_ALEN];
        u16 auth_alg, auth_transaction, status_code;
        struct sta_info *sta;
+       struct ieee80211_event event = {
+               .type = MLME_EVENT,
+               .u.mlme.data = AUTH_EVENT,
+       };
 
        sdata_assert_lock(sdata);
 
@@ -2541,6 +2550,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                           mgmt->sa, status_code);
                ieee80211_destroy_auth_data(sdata, false);
                cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+               event.u.mlme.status = MLME_DENIED;
+               event.u.mlme.reason = status_code;
+               drv_event_callback(sdata->local, sdata, &event);
                return;
        }
 
@@ -2563,6 +2575,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       event.u.mlme.status = MLME_SUCCESS;
+       drv_event_callback(sdata->local, sdata, &event);
        sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
        ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
@@ -2681,7 +2695,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+       ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
 }
 
 
@@ -2707,7 +2721,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+       ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
 }
 
 static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2969,8 +2983,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
 
        rate_control_rate_init(sta);
 
-       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
+       if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
                set_sta_flag(sta, WLAN_STA_MFP);
+               sta->sta.mfp = true;
+       } else {
+               sta->sta.mfp = false;
+       }
 
        sta->sta.wme = elems.wmm_param;
 
@@ -3042,6 +3060,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        u8 *pos;
        bool reassoc;
        struct cfg80211_bss *bss;
+       struct ieee80211_event event = {
+               .type = MLME_EVENT,
+               .u.mlme.data = ASSOC_EVENT,
+       };
 
        sdata_assert_lock(sdata);
 
@@ -3093,6 +3115,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                sdata_info(sdata, "%pM denied association (code=%d)\n",
                           mgmt->sa, status_code);
                ieee80211_destroy_assoc_data(sdata, false);
+               event.u.mlme.status = MLME_DENIED;
+               event.u.mlme.reason = status_code;
+               drv_event_callback(sdata->local, sdata, &event);
        } else {
                if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
                        /* oops -- internal error -- send timeout for now */
@@ -3100,6 +3125,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                        cfg80211_assoc_timeout(sdata->dev, bss);
                        return;
                }
+               event.u.mlme.status = MLME_SUCCESS;
+               drv_event_callback(sdata->local, sdata, &event);
                sdata_info(sdata, "associated\n");
 
                /*
@@ -3301,6 +3328,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
            ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
                int sig = ifmgd->ave_beacon_signal;
                int last_sig = ifmgd->last_ave_beacon_signal;
+               struct ieee80211_event event = {
+                       .type = RSSI_EVENT,
+               };
 
                /*
                 * if signal crosses either of the boundaries, invoke callback
@@ -3309,12 +3339,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                if (sig > ifmgd->rssi_max_thold &&
                    (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
                        ifmgd->last_ave_beacon_signal = sig;
-                       drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
+                       event.u.rssi.data = RSSI_EVENT_HIGH;
+                       drv_event_callback(local, sdata, &event);
                } else if (sig < ifmgd->rssi_min_thold &&
                           (last_sig >= ifmgd->rssi_max_thold ||
                           last_sig == 0)) {
                        ifmgd->last_ave_beacon_signal = sig;
-                       drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
+                       event.u.rssi.data = RSSI_EVENT_LOW;
+                       drv_event_callback(local, sdata, &event);
                }
        }
 
@@ -3419,6 +3451,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->csa_waiting_bcn)
                ieee80211_chswitch_post_beacon(sdata);
 
+       /*
+        * Update beacon timing and dtim count on every beacon appearance. This
+        * will allow the driver to use the most updated values. Do it before
+        * comparing this one with last received beacon.
+        * IMPORTANT: These parameters would possibly be out of sync by the time
+        * the driver will use them. The synchronized view is currently
+        * guaranteed only in certain callbacks.
+        */
+       if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
+               sdata->vif.bss_conf.sync_tsf =
+                       le64_to_cpu(mgmt->u.beacon.timestamp);
+               sdata->vif.bss_conf.sync_device_ts =
+                       rx_status->device_timestamp;
+               if (elems.tim)
+                       sdata->vif.bss_conf.sync_dtim_count =
+                               elems.tim->dtim_count;
+               else
+                       sdata->vif.bss_conf.sync_dtim_count = 0;
+       }
+
        if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
                return;
        ifmgd->beacon_crc = ncrc;
@@ -3446,18 +3498,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                else
                        bss_conf->dtim_period = 1;
 
-               if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
-                       sdata->vif.bss_conf.sync_tsf =
-                               le64_to_cpu(mgmt->u.beacon.timestamp);
-                       sdata->vif.bss_conf.sync_device_ts =
-                               rx_status->device_timestamp;
-                       if (elems.tim)
-                               sdata->vif.bss_conf.sync_dtim_count =
-                                       elems.tim->dtim_count;
-                       else
-                               sdata->vif.bss_conf.sync_dtim_count = 0;
-               }
-
                changed |= BSS_CHANGED_BEACON_INFO;
                ifmgd->have_beacon = true;
 
@@ -3488,8 +3528,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       WLAN_REASON_DEAUTH_LEAVING,
                                       true, deauth_buf);
-               cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
-                                     sizeof(deauth_buf));
+               ieee80211_report_disconnect(sdata, deauth_buf,
+                                           sizeof(deauth_buf), true,
+                                           WLAN_REASON_DEAUTH_LEAVING);
                return;
        }
 
@@ -3607,8 +3648,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               tx, frame_buf);
 
-       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                             IEEE80211_DEAUTH_FRAME_LEN);
+       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+                                   reason);
 }
 
 static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3802,12 +3843,18 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                        ieee80211_destroy_auth_data(sdata, false);
                } else if (ieee80211_probe_auth(sdata)) {
                        u8 bssid[ETH_ALEN];
+                       struct ieee80211_event event = {
+                               .type = MLME_EVENT,
+                               .u.mlme.data = AUTH_EVENT,
+                               .u.mlme.status = MLME_TIMEOUT,
+                       };
 
                        memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
 
                        ieee80211_destroy_auth_data(sdata, false);
 
                        cfg80211_auth_timeout(sdata->dev, bssid);
+                       drv_event_callback(sdata->local, sdata, &event);
                }
        } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
                run_again(sdata, ifmgd->auth_data->timeout);
@@ -3817,9 +3864,15 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
                    ieee80211_do_assoc(sdata)) {
                        struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
+                       struct ieee80211_event event = {
+                               .type = MLME_EVENT,
+                               .u.mlme.data = ASSOC_EVENT,
+                               .u.mlme.status = MLME_TIMEOUT,
+                       };
 
                        ieee80211_destroy_assoc_data(sdata, false);
                        cfg80211_assoc_timeout(sdata->dev, bss);
+                       drv_event_callback(sdata->local, sdata, &event);
                }
        } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
                run_again(sdata, ifmgd->assoc_data->timeout);
@@ -3891,12 +3944,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
 {
        struct ieee80211_sub_if_data *sdata =
                (struct ieee80211_sub_if_data *) data;
-       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       if (local->quiescing)
-               return;
-
        if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
 
@@ -3912,9 +3961,6 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
 
-       if (local->quiescing)
-               return;
-
        if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
                return;
 
@@ -3977,6 +4023,34 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
                                      IEEE80211_DEAUTH_FRAME_LEN);
        }
 
+       /* This is a bit of a hack - we should find a better and more generic
+        * solution to this. Normally when suspending, cfg80211 will in fact
+        * deauthenticate. However, it doesn't (and cannot) stop an ongoing
+        * auth (not so important) or assoc (this is the problem) process.
+        *
+        * As a consequence, it can happen that we are in the process of both
+        * associating and suspending, and receive an association response
+        * after cfg80211 has checked if it needs to disconnect, but before
+        * we actually set the flag to drop incoming frames. This will then
+        * cause the workqueue flush to process the association response in
+        * the suspend, resulting in a successful association just before it
+        * tries to remove the interface from the driver, which now though
+        * has a channel context assigned ... this results in issues.
+        *
+        * To work around this (for now) simply deauth here again if we're
+        * now connected.
+        */
+       if (ifmgd->associated && !sdata->local->wowlan) {
+               u8 bssid[ETH_ALEN];
+               struct cfg80211_deauth_request req = {
+                       .reason_code = WLAN_REASON_DEAUTH_LEAVING,
+                       .bssid = bssid,
+               };
+
+               memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+               ieee80211_mgd_deauth(sdata, &req);
+       }
+
        sdata_unlock(sdata);
 }
 
@@ -4453,8 +4527,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                                     sizeof(frame_buf));
+               ieee80211_report_disconnect(sdata, frame_buf,
+                                           sizeof(frame_buf), true,
+                                           WLAN_REASON_UNSPECIFIED);
        }
 
        sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4554,8 +4629,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                                       WLAN_REASON_UNSPECIFIED,
                                       false, frame_buf);
 
-               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                                     sizeof(frame_buf));
+               ieee80211_report_disconnect(sdata, frame_buf,
+                                           sizeof(frame_buf), true,
+                                           WLAN_REASON_UNSPECIFIED);
        }
 
        if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4845,8 +4921,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                                               req->reason_code, tx,
                                               frame_buf);
                ieee80211_destroy_auth_data(sdata, false);
-               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                                     IEEE80211_DEAUTH_FRAME_LEN);
+               ieee80211_report_disconnect(sdata, frame_buf,
+                                           sizeof(frame_buf), true,
+                                           req->reason_code);
 
                return 0;
        }
@@ -4860,8 +4937,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
                ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                                       req->reason_code, tx, frame_buf);
-               cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                                     IEEE80211_DEAUTH_FRAME_LEN);
+               ieee80211_report_disconnect(sdata, frame_buf,
+                                           sizeof(frame_buf), true,
+                                           req->reason_code);
                return 0;
        }
 
@@ -4893,8 +4971,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                               req->reason_code, !req->local_state_change,
                               frame_buf);
 
-       cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
-                             IEEE80211_DEAUTH_FRAME_LEN);
+       ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+                                   req->reason_code);
 
        return 0;
 }