mac80211: fix CSA handling timer
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / mlme.c
index 91d84cc77bbf7f9a3bda9b7ca1d04677b0672e7f..9e61fe127a33b992ad9e01973b06ae872c4236ca 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:
@@ -258,12 +261,11 @@ static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
 }
 
 static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
-                               struct sk_buff *skb, const u8 *ht_oper_ie,
+                               struct sk_buff *skb, u8 ap_ht_param,
                                struct ieee80211_supported_band *sband,
                                struct ieee80211_channel *channel,
                                enum ieee80211_smps_mode smps)
 {
-       struct ieee80211_ht_operation *ht_oper;
        u8 *pos;
        u32 flags = channel->flags;
        u16 cap;
@@ -271,21 +273,13 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
 
        BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
 
-       if (!ht_oper_ie)
-               return;
-
-       if (ht_oper_ie[1] < sizeof(struct ieee80211_ht_operation))
-               return;
-
        memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
        ieee80211_apply_htcap_overrides(sdata, &ht_cap);
 
-       ht_oper = (struct ieee80211_ht_operation *)(ht_oper_ie + 2);
-
        /* determine capability flags */
        cap = ht_cap.cap;
 
-       switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+       switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
        case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
                        cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -335,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;
@@ -380,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);
@@ -509,9 +524,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        }
 
        if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
-               ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_operation_ie,
+               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,
@@ -550,6 +568,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                memcpy(pos, assoc_data->ie + offset, noffset - offset);
        }
 
+       drv_mgd_prepare_tx(local, sdata);
+
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
 }
@@ -589,6 +609,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
                        IEEE80211_SKB_CB(skb)->flags |=
                                IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
                ieee80211_tx_skb(sdata, skb);
        }
 }
@@ -691,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:
@@ -767,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,
@@ -911,9 +929,6 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
        if (!mgd->associated)
                return false;
 
-       if (!mgd->associated->beacon_ies)
-               return false;
-
        if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
                          IEEE80211_STA_CONNECTION_POLL))
                return false;
@@ -939,11 +954,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                return;
        }
 
-       if (!list_empty(&local->work_list)) {
-               local->ps_sdata = NULL;
-               goto change;
-       }
-
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (!ieee80211_sdata_running(sdata))
                        continue;
@@ -1016,10 +1026,19 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                local->ps_sdata = NULL;
        }
 
- change:
        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 =
@@ -1121,7 +1140,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
 }
 
 /* MLME */
-static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
+static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
                                     u8 *wmm_param, size_t wmm_param_len)
 {
@@ -1132,23 +1151,23 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        u8 *pos, uapsd_queues = 0;
 
        if (!local->ops->conf_tx)
-               return;
+               return false;
 
        if (local->hw.queues < IEEE80211_NUM_ACS)
-               return;
+               return false;
 
        if (!wmm_param)
-               return;
+               return false;
 
        if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
-               return;
+               return false;
 
        if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
                uapsd_queues = ifmgd->uapsd_queues;
 
        count = wmm_param[6] & 0x0f;
        if (count == ifmgd->wmm_last_param_set)
-               return;
+               return false;
        ifmgd->wmm_last_param_set = count;
 
        pos = wmm_param + 8;
@@ -1156,7 +1175,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
 
        memset(&params, 0, sizeof(params));
 
-       local->wmm_acm = 0;
+       sdata->wmm_acm = 0;
        for (; left >= 4; left -= 4, pos += 4) {
                int aci = (pos[0] >> 5) & 0x03;
                int acm = (pos[0] >> 4) & 0x01;
@@ -1167,21 +1186,21 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                case 1: /* AC_BK */
                        queue = 3;
                        if (acm)
-                               local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
+                               sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
                                uapsd = true;
                        break;
                case 2: /* AC_VI */
                        queue = 1;
                        if (acm)
-                               local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
+                               sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
                                uapsd = true;
                        break;
                case 3: /* AC_VO */
                        queue = 0;
                        if (acm)
-                               local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
+                               sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
                                uapsd = true;
                        break;
@@ -1189,7 +1208,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                default:
                        queue = 2;
                        if (acm)
-                               local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
+                               sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
                        if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
                                uapsd = true;
                        break;
@@ -1201,23 +1220,21 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                params.txop = get_unaligned_le16(pos + 2);
                params.uapsd = uapsd;
 
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-               wiphy_debug(local->hw.wiphy,
-                           "WMM queue=%d aci=%d acm=%d aifs=%d "
-                           "cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
-                           queue, aci, acm,
-                           params.aifs, params.cw_min, params.cw_max,
-                           params.txop, params.uapsd);
-#endif
+               mlme_dbg(sdata,
+                        "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
+                        queue, aci, acm,
+                        params.aifs, params.cw_min, params.cw_max,
+                        params.txop, params.uapsd);
                sdata->tx_conf[queue] = params;
                if (drv_conf_tx(local, sdata, queue, &params))
-                       wiphy_debug(local->hw.wiphy,
-                                   "failed to set TX queue parameters for queue %d\n",
-                                   queue);
+                       sdata_err(sdata,
+                                 "failed to set TX queue parameters for queue %d\n",
+                                 queue);
        }
 
        /* enable WMM or activate new settings */
        sdata->vif.bss_conf.qos = true;
+       return true;
 }
 
 static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
@@ -1254,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) {
@@ -1284,13 +1301,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
 
        bss_info_changed |= BSS_CHANGED_ASSOC;
-       /* set timing information */
-       bss_conf->beacon_int = cbss->beacon_interval;
-       bss_conf->last_tsf = cbss->tsf;
-
-       bss_info_changed |= BSS_CHANGED_BEACON_INT;
        bss_info_changed |= ieee80211_handle_bss_capability(sdata,
-               cbss->capability, bss->has_erp_value, bss->erp_value);
+               bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value);
 
        sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec(
                IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int));
@@ -1330,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);
 }
@@ -1342,7 +1356,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
        u32 changed = 0;
-       u8 bssid[ETH_ALEN];
 
        ASSERT_MGD_MTX(ifmgd);
 
@@ -1352,10 +1365,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!ifmgd->associated))
                return;
 
-       memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+       ieee80211_stop_poll(sdata);
 
        ifmgd->associated = NULL;
-       memset(ifmgd->bssid, 0, ETH_ALEN);
 
        /*
         * we need to commit the associated = NULL change because the
@@ -1375,22 +1387,43 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        netif_carrier_off(sdata->dev);
 
        mutex_lock(&local->sta_mtx);
-       sta = sta_info_get(sdata, bssid);
+       sta = sta_info_get(sdata, ifmgd->bssid);
        if (sta) {
                set_sta_flag(sta, WLAN_STA_BLOCK_BA);
                ieee80211_sta_tear_down_BA_sessions(sta, tx);
        }
        mutex_unlock(&local->sta_mtx);
 
+       /*
+        * if we want to get out of ps before disassoc (why?) we have
+        * to do it before sending disassoc, as otherwise the null-packet
+        * won't be valid.
+        */
+       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
+       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);
+
        /* deauthenticate/disassociate now */
        if (tx || frame_buf)
-               ieee80211_send_deauth_disassoc(sdata, bssid, stype, reason,
-                                              tx, frame_buf);
+               ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
+                                              reason, tx, frame_buf);
 
        /* flush out frame */
        if (tx)
                drv_flush(local, false);
 
+       /* clear bssid only after building the needed mgmt frames */
+       memset(ifmgd->bssid, 0, ETH_ALEN);
+
        /* remove AP and TDLS peers */
        sta_info_flush(local, sdata);
 
@@ -1410,12 +1443,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        del_timer_sync(&local->dynamic_ps_timer);
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
-       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-       }
-       local->ps_sdata = NULL;
-
        /* Disable ARP filtering */
        if (sdata->vif.bss_conf.arp_filter_enabled) {
                sdata->vif.bss_conf.arp_filter_enabled = false;
@@ -1580,11 +1607,12 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
        if (beacon)
-               net_dbg_ratelimited("%s: detected beacon loss from AP - sending probe request\n",
-                                   sdata->name);
-#endif
+               mlme_dbg_ratelimited(sdata,
+                                    "detected beacon loss from AP - sending probe request\n");
+
+       ieee80211_cqm_rssi_notify(&sdata->vif,
+               NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL);
 
        /*
         * The driver/our work has already reported this event or the
@@ -1626,6 +1654,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct cfg80211_bss *cbss;
        struct sk_buff *skb;
        const u8 *ssid;
        int ssid_len;
@@ -1635,17 +1664,25 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
 
        ASSERT_MGD_MTX(ifmgd);
 
-       if (!ifmgd->associated)
+       if (ifmgd->associated)
+               cbss = ifmgd->associated;
+       else if (ifmgd->auth_data)
+               cbss = ifmgd->auth_data->bss;
+       else if (ifmgd->assoc_data)
+               cbss = ifmgd->assoc_data->bss;
+       else
                return NULL;
 
-       ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+       ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
        if (WARN_ON_ONCE(ssid == NULL))
                ssid_len = 0;
        else
                ssid_len = ssid[1];
 
-       skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
-                                       (u32) -1, ssid + 2, ssid_len,
+       skb = ieee80211_build_probe_req(sdata, cbss->bssid,
+                                       (u32) -1,
+                                       sdata->local->oper_channel,
+                                       ssid + 2, ssid_len,
                                        NULL, 0, true);
 
        return skb;
@@ -1656,7 +1693,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,10 +1701,8 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
                return;
        }
 
-       memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
-
-       printk(KERN_DEBUG "%s: Connection to AP %pM lost.\n",
-              sdata->name, 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,
@@ -1764,6 +1798,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
        if (!elems.challenge)
                return;
        auth_data->expected_transaction = 4;
+       drv_mgd_prepare_tx(sdata->local, sdata);
        ieee80211_send_auth(sdata, 3, auth_data->algorithm,
                            elems.challenge - 2, elems.challenge_len + 2,
                            auth_data->bss->bssid, auth_data->bss->bssid,
@@ -1802,9 +1837,10 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                return RX_MGMT_NONE;
 
        if (status_code != WLAN_STATUS_SUCCESS) {
-               printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n",
-                      sdata->name, mgmt->sa, status_code);
-               goto out;
+               sdata_info(sdata, "%pM denied authentication (status %d)\n",
+                          mgmt->sa, status_code);
+               ieee80211_destroy_auth_data(sdata, false);
+               return RX_MGMT_CFG80211_RX_AUTH;
        }
 
        switch (ifmgd->auth_data->algorithm) {
@@ -1825,8 +1861,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                return RX_MGMT_NONE;
        }
 
-       printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
- out:
+       sdata_info(sdata, "authenticated\n");
        ifmgd->auth_data->done = true;
        ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
        run_again(ifmgd, ifmgd->auth_data->timeout);
@@ -1839,8 +1874,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
                goto out_err;
        }
        if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
-               printk(KERN_DEBUG "%s: failed moving %pM to auth\n",
-                      sdata->name, bssid);
+               sdata_info(sdata, "failed moving %pM to auth\n", bssid);
                goto out_err;
        }
        mutex_unlock(&sdata->local->sta_mtx);
@@ -1874,8 +1908,8 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
 
-       printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
-                       sdata->name, bssid, reason_code);
+       sdata_info(sdata, "deauthenticated from %pM (Reason: %u)\n",
+                  bssid, reason_code);
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
@@ -1905,8 +1939,8 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
 
        reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
 
-       printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
-                       sdata->name, mgmt->sa, reason_code);
+       sdata_info(sdata, "disassociated from %pM (Reason: %u)\n",
+                  mgmt->sa, reason_code);
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
@@ -1998,17 +2032,15 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 
        if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
-               printk(KERN_DEBUG
-                      "%s: invalid AID value 0x%x; bits 15:14 not set\n",
-                      sdata->name, aid);
+               sdata_info(sdata, "invalid AID value 0x%x; bits 15:14 not set\n",
+                          aid);
        aid &= ~(BIT(15) | BIT(14));
 
        ifmgd->broken_ap = false;
 
        if (aid == 0 || aid > IEEE80211_MAX_AID) {
-               printk(KERN_DEBUG
-                      "%s: invalid AID value %d (out of range), turn off PS\n",
-                      sdata->name, aid);
+               sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n",
+                          aid);
                aid = 0;
                ifmgd->broken_ap = true;
        }
@@ -2017,8 +2049,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
 
        if (!elems.supp_rates) {
-               printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
-                      sdata->name);
+               sdata_info(sdata, "no SuppRates element in AssocResp\n");
                return false;
        }
 
@@ -2058,9 +2089,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
        if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
                err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
        if (err) {
-               printk(KERN_DEBUG
-                      "%s: failed to move station %pM to desired state\n",
-                      sdata->name, sta->sta.addr);
+               sdata_info(sdata,
+                          "failed to move station %pM to desired state\n",
+                          sta->sta.addr);
                WARN_ON(__sta_info_destroy(sta));
                mutex_unlock(&sdata->local->sta_mtx);
                return false;
@@ -2143,10 +2174,10 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
        aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
 
-       printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
-              "status=%d aid=%d)\n",
-              sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
-              capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
+       sdata_info(sdata,
+                  "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
+                  reassoc ? "Rea" : "A", mgmt->sa,
+                  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);
@@ -2157,9 +2188,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                u32 tu, ms;
                tu = get_unaligned_le32(elems.timeout_int + 1);
                ms = tu * 1024 / 1000;
-               printk(KERN_DEBUG "%s: %pM rejected association temporarily; "
-                      "comeback duration %u TU (%u ms)\n",
-                      sdata->name, mgmt->sa, tu, ms);
+               sdata_info(sdata,
+                          "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
+                          mgmt->sa, tu, ms);
                assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
                if (ms > IEEE80211_ASSOC_TIMEOUT)
                        run_again(ifmgd, assoc_data->timeout);
@@ -2169,19 +2200,17 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        *bss = assoc_data->bss;
 
        if (status_code != WLAN_STATUS_SUCCESS) {
-               printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n",
-                      sdata->name, mgmt->sa, status_code);
+               sdata_info(sdata, "%pM denied association (code=%d)\n",
+                          mgmt->sa, status_code);
                ieee80211_destroy_assoc_data(sdata, false);
        } else {
-               printk(KERN_DEBUG "%s: associated\n", sdata->name);
-
                if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
                        /* oops -- internal error -- send timeout for now */
-                       ieee80211_destroy_assoc_data(sdata, true);
-                       sta_info_destroy_addr(sdata, mgmt->bssid);
+                       ieee80211_destroy_assoc_data(sdata, false);
                        cfg80211_put_bss(*bss);
                        return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
                }
+               sdata_info(sdata, "associated\n");
 
                /*
                 * destroy assoc_data afterwards, as otherwise an idle
@@ -2281,7 +2310,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies &&
            ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) {
                /* got probe response, continue with auth */
-               printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
+               sdata_info(sdata, "direct probe responded\n");
                ifmgd->auth_data->tries = 0;
                ifmgd->auth_data->timeout = jiffies;
                run_again(ifmgd, ifmgd->auth_data->timeout);
@@ -2332,7 +2361,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 &&
@@ -2417,10 +2446,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        }
 
        if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-               net_dbg_ratelimited("%s: cancelling probereq poll due to a received beacon\n",
-                                   sdata->name);
-#endif
+               mlme_dbg_ratelimited(sdata,
+                                    "cancelling probereq poll due to a received beacon\n");
                mutex_lock(&local->mtx);
                ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
                ieee80211_run_deferred_scan(local);
@@ -2446,14 +2473,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
                                                   ifmgd->aid);
 
-       if (ncrc != ifmgd->beacon_crc || !ifmgd->beacon_crc_valid) {
-               ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
-                                     true);
-
-               ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
-                                        elems.wmm_param_len);
-       }
-
        if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
                if (directed_tim) {
                        if (local->hw.conf.dynamic_ps_timeout > 0) {
@@ -2484,6 +2503,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        ifmgd->beacon_crc = ncrc;
        ifmgd->beacon_crc_valid = true;
 
+       ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
+                             true);
+
+       if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
+                                    elems.wmm_param_len))
+               changed |= BSS_CHANGED_QOS;
+
        if (elems.erp_info && elems.erp_info_len >= 1) {
                erp_valid = true;
                erp_value = elems.erp_info[0];
@@ -2499,7 +2525,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);
@@ -2612,8 +2638,6 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[DEAUTH_DISASSOC_LEN];
 
-       ieee80211_stop_poll(sdata);
-
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               false, frame_buf);
        mutex_unlock(&ifmgd->mtx);
@@ -2645,8 +2669,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
        auth_data->tries++;
 
        if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
-               printk(KERN_DEBUG "%s: authentication with %pM timed out\n",
-                      sdata->name, auth_data->bss->bssid);
+               sdata_info(sdata, "authentication with %pM timed out\n",
+                          auth_data->bss->bssid);
 
                /*
                 * Most likely AP is not in the range so remove the
@@ -2657,10 +2681,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
                return -ETIMEDOUT;
        }
 
+       drv_mgd_prepare_tx(local, sdata);
+
        if (auth_data->bss->proberesp_ies) {
-               printk(KERN_DEBUG "%s: send auth to %pM (try %d/%d)\n",
-                      sdata->name, auth_data->bss->bssid, auth_data->tries,
-                      IEEE80211_AUTH_MAX_TRIES);
+               sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
+                          auth_data->bss->bssid, auth_data->tries,
+                          IEEE80211_AUTH_MAX_TRIES);
 
                auth_data->expected_transaction = 2;
                ieee80211_send_auth(sdata, 1, auth_data->algorithm,
@@ -2670,9 +2696,9 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
        } else {
                const u8 *ssidie;
 
-               printk(KERN_DEBUG "%s: direct probe to %pM (try %d/%i)\n",
-                      sdata->name, auth_data->bss->bssid, auth_data->tries,
-                      IEEE80211_AUTH_MAX_TRIES);
+               sdata_info(sdata, "direct probe to %pM (try %d/%i)\n",
+                          auth_data->bss->bssid, auth_data->tries,
+                          IEEE80211_AUTH_MAX_TRIES);
 
                ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
                if (!ssidie)
@@ -2700,8 +2726,8 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
 
        assoc_data->tries++;
        if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
-               printk(KERN_DEBUG "%s: association with %pM timed out\n",
-                      sdata->name, assoc_data->bss->bssid);
+               sdata_info(sdata, "association with %pM timed out\n",
+                          assoc_data->bss->bssid);
 
                /*
                 * Most likely AP is not in the range so remove the
@@ -2712,9 +2738,9 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
                return -ETIMEDOUT;
        }
 
-       printk(KERN_DEBUG "%s: associate with %pM (try %d/%d)\n",
-              sdata->name, assoc_data->bss->bssid, assoc_data->tries,
-              IEEE80211_ASSOC_MAX_TRIES);
+       sdata_info(sdata, "associate with %pM (try %d/%d)\n",
+                  assoc_data->bss->bssid, assoc_data->tries,
+                  IEEE80211_ASSOC_MAX_TRIES);
        ieee80211_send_assoc(sdata);
 
        assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
@@ -2787,45 +2813,31 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                        ieee80211_reset_ap_probe(sdata);
                else if (ifmgd->nullfunc_failed) {
                        if (ifmgd->probe_send_count < max_tries) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-                               wiphy_debug(local->hw.wiphy,
-                                           "%s: No ack for nullfunc frame to"
-                                           " AP %pM, try %d/%i\n",
-                                           sdata->name, bssid,
-                                           ifmgd->probe_send_count, max_tries);
-#endif
+                               mlme_dbg(sdata,
+                                        "No ack for nullfunc frame to AP %pM, try %d/%i\n",
+                                        bssid, ifmgd->probe_send_count,
+                                        max_tries);
                                ieee80211_mgd_probe_ap_send(sdata);
                        } else {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-                               wiphy_debug(local->hw.wiphy,
-                                           "%s: No ack for nullfunc frame to"
-                                           " AP %pM, disconnecting.\n",
-                                           sdata->name, bssid);
-#endif
+                               mlme_dbg(sdata,
+                                        "No ack for nullfunc frame to AP %pM, disconnecting.\n",
+                                        bssid);
                                ieee80211_sta_connection_lost(sdata, bssid,
                                        WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
                        }
                } else if (time_is_after_jiffies(ifmgd->probe_timeout))
                        run_again(ifmgd, ifmgd->probe_timeout);
                else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-                       wiphy_debug(local->hw.wiphy,
-                                   "%s: Failed to send nullfunc to AP %pM"
-                                   " after %dms, disconnecting.\n",
-                                   sdata->name,
-                                   bssid, probe_wait_ms);
-#endif
+                       mlme_dbg(sdata,
+                                "Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
+                                bssid, probe_wait_ms);
                        ieee80211_sta_connection_lost(sdata, bssid,
                                WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
                } else if (ifmgd->probe_send_count < max_tries) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-                       wiphy_debug(local->hw.wiphy,
-                                   "%s: No probe response from AP %pM"
-                                   " after %dms, try %d/%i\n",
-                                   sdata->name,
-                                   bssid, probe_wait_ms,
-                                   ifmgd->probe_send_count, max_tries);
-#endif
+                       mlme_dbg(sdata,
+                                "No probe response from AP %pM after %dms, try %d/%i\n",
+                                bssid, probe_wait_ms,
+                                ifmgd->probe_send_count, max_tries);
                        ieee80211_mgd_probe_ap_send(sdata);
                } else {
                        /*
@@ -2940,11 +2952,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
                sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
                mutex_lock(&ifmgd->mtx);
                if (ifmgd->associated) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-                       wiphy_debug(sdata->local->hw.wiphy,
-                                   "%s: driver requested disconnect after resume.\n",
-                                   sdata->name);
-#endif
+                       mlme_dbg(sdata,
+                                "driver requested disconnect after resume\n");
                        ieee80211_sta_connection_lost(sdata,
                                ifmgd->associated->bssid,
                                WLAN_REASON_UNSPECIFIED);
@@ -3002,7 +3011,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 /* scan finished notification */
 void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
 {
-       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+       struct ieee80211_sub_if_data *sdata;
 
        /* Restart STA timers */
        rcu_read_lock();
@@ -3026,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;
-       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;
@@ -3085,13 +3070,11 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                         * since we look at probe response/beacon data here
                         * it should be OK.
                         */
-                       printk(KERN_DEBUG
-                              "%s: Wrong control channel: center-freq: %d"
-                              " ht-cfreq: %d ht->primary_chan: %d"
-                              " band: %d. Disabling HT.\n",
-                              sdata->name, cbss->channel->center_freq,
-                              ht_cfreq, ht_oper->primary_chan,
-                              cbss->channel->band);
+                       sdata_info(sdata,
+                                  "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
+                                  cbss->channel->center_freq,
+                                  ht_cfreq, ht_oper->primary_chan,
+                                  cbss->channel->band);
                        ht_oper = NULL;
                }
        }
@@ -3115,9 +3098,8 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
        if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
                /* can only fail due to HT40+/- mismatch */
                channel_type = NL80211_CHAN_HT20;
-               printk(KERN_DEBUG
-                      "%s: disabling 40 MHz due to multi-vif mismatch\n",
-                      sdata->name);
+               sdata_info(sdata,
+                          "disabling 40 MHz due to multi-vif mismatch\n");
                ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
                WARN_ON(!ieee80211_set_channel_type(local, sdata,
                                                    channel_type));
@@ -3126,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);
 
+       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,
@@ -3146,13 +3169,12 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
                 * we can connect -- with a warning.
                 */
                if (!basic_rates && min_rate_index >= 0) {
-                       printk(KERN_DEBUG
-                              "%s: No basic rates, using min rate instead.\n",
-                              sdata->name);
+                       sdata_info(sdata,
+                                  "No basic rates, using min rate instead\n");
                        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 */
@@ -3164,19 +3186,25 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
 
                memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN);
 
-               /* tell driver about BSSID and basic rates */
+               /* set timing information */
+               sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
+               sdata->vif.bss_conf.sync_tsf = cbss->tsf;
+               sdata->vif.bss_conf.sync_device_ts = bss->device_ts;
+
+               /* tell driver about BSSID, basic rates and timing */
                ieee80211_bss_info_change_notify(sdata,
-                       BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES);
+                       BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES |
+                       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) {
-                       printk(KERN_DEBUG
-                              "%s: failed to insert STA entry for the AP (error %d)\n",
-                              sdata->name, err);
+                       sdata_info(sdata,
+                                  "failed to insert STA entry for the AP (error %d)\n",
+                                  err);
                        return err;
                }
        } else
@@ -3254,8 +3282,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        if (ifmgd->associated)
                ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
-       printk(KERN_DEBUG "%s: authenticate with %pM\n",
-              sdata->name, req->bss->bssid);
+       sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
 
        err = ieee80211_prep_connection(sdata, req->bss, false);
        if (err)
@@ -3290,7 +3317,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_bss *bss = (void *)req->bss->priv;
        struct ieee80211_mgd_assoc_data *assoc_data;
        struct ieee80211_supported_band *sband;
-       const u8 *ssidie;
+       const u8 *ssidie, *ht_ie;
        int i, err;
 
        ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
@@ -3325,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;
 
@@ -3338,20 +3369,40 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
         * We can set this to true for non-11n hardware, that'll be checked
         * separately along with the peer capabilities.
         */
-       for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
+       for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) {
                if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
-                   req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
+                   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/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)
+           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+               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));
        memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
@@ -3377,8 +3428,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                          (local->hw.queues >= IEEE80211_NUM_ACS);
        assoc_data->supp_rates = bss->supp_rates;
        assoc_data->supp_rates_len = bss->supp_rates_len;
-       assoc_data->ht_operation_ie =
-               ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
+
+       ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
+       if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
+               assoc_data->ap_ht_param =
+                       ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
+       else
+               ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
 
        if (bss->wmm_used && bss->uapsd_supported &&
            (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@@ -3425,8 +3481,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                 * Wait up to one beacon interval ...
                 * should this be more if we miss one?
                 */
-               printk(KERN_DEBUG "%s: waiting for beacon from %pM\n",
-                      sdata->name, ifmgd->bssid);
+               sdata_info(sdata, "waiting for beacon from %pM\n",
+                          ifmgd->bssid);
                assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
        } else {
                assoc_data->have_beacon = true;
@@ -3445,8 +3501,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                                corrupt_type = "beacon";
                } else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP)
                        corrupt_type = "probe response";
-               printk(KERN_DEBUG "%s: associating with AP with corrupt %s\n",
-                      sdata->name, corrupt_type);
+               sdata_info(sdata, "associating with AP with corrupt %s\n",
+                          corrupt_type);
        }
 
        err = 0;
@@ -3475,19 +3531,22 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                return 0;
        }
 
-       printk(KERN_DEBUG
-              "%s: deauthenticating from %pM by local choice (reason=%d)\n",
-              sdata->name, req->bssid, req->reason_code);
+       sdata_info(sdata,
+                  "deauthenticating from %pM by local choice (reason=%d)\n",
+                  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);
@@ -3519,8 +3578,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                return -ENOLINK;
        }
 
-       printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
-              sdata->name, req->bss->bssid, req->reason_code);
+       sdata_info(sdata,
+                  "disassociating from %pM by local choice (reason=%d)\n",
+                  req->bss->bssid, req->reason_code);
 
        memcpy(bssid, req->bss->bssid, ETH_ALEN);
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
@@ -3561,10 +3621,3 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
        cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
 }
 EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
-
-unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif)
-{
-       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
-       return sdata->dev->operstate;
-}
-EXPORT_SYMBOL(ieee80211_get_operstate);