Merge remote-tracking branch 'wireless-next/master' into mac80211-next
authorJohannes Berg <johannes.berg@intel.com>
Thu, 28 Jun 2012 11:45:58 +0000 (13:45 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 28 Jun 2012 11:45:58 +0000 (13:45 +0200)
1  2 
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ti/wlcore/main.c
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/mlme.c

index b8fce0d4d72e3bcd8401bfbcbfd602617ec65814,fd7dbd4609df3c6da9595a7bf5239cd7932d57fd..aca1d2689e907fce2d5a9841e5acfad515c3ee50
  
  #define DEFAULT_BG_SCAN_PERIOD 60
  
+ struct ath6kl_cfg80211_match_probe_ssid {
+       struct cfg80211_ssid ssid;
+       u8 flag;
+ };
  static struct ieee80211_rate ath6kl_rates[] = {
        RATETAB_ENT(10, 0x1, 0),
        RATETAB_ENT(20, 0x2, 0),
@@@ -576,6 -581,9 +581,9 @@@ static int ath6kl_cfg80211_connect(stru
  
        vif->nw_type = vif->next_mode;
  
+       /* enable enhanced bmiss detection if applicable */
+       ath6kl_cfg80211_sta_bmiss_enhance(vif, true);
        if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)
                nw_subtype = SUBTYPE_P2PCLIENT;
  
@@@ -852,20 -860,6 +860,6 @@@ void ath6kl_cfg80211_disconnect_event(s
                }
        }
  
-       /*
-        * Send a disconnect command to target when a disconnect event is
-        * received with reason code other than 3 (DISCONNECT_CMD - disconnect
-        * request from host) to make the firmware stop trying to connect even
-        * after giving disconnect event. There will be one more disconnect
-        * event for this disconnect command with reason code DISCONNECT_CMD
-        * which will be notified to cfg80211.
-        */
-       if (reason != DISCONNECT_CMD) {
-               ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
-               return;
-       }
        clear_bit(CONNECT_PEND, &vif->flags);
  
        if (vif->sme_state == SME_CONNECTING) {
                                        WLAN_STATUS_UNSPECIFIED_FAILURE,
                                        GFP_KERNEL);
        } else if (vif->sme_state == SME_CONNECTED) {
-               cfg80211_disconnected(vif->ndev, reason,
+               cfg80211_disconnected(vif->ndev, proto_reason,
                                      NULL, 0, GFP_KERNEL);
        }
  
        vif->sme_state = SME_DISCONNECTED;
+       /*
+        * Send a disconnect command to target when a disconnect event is
+        * received with reason code other than 3 (DISCONNECT_CMD - disconnect
+        * request from host) to make the firmware stop trying to connect even
+        * after giving disconnect event. There will be one more disconnect
+        * event for this disconnect command with reason code DISCONNECT_CMD
+        * which won't be notified to cfg80211.
+        */
+       if (reason != DISCONNECT_CMD)
+               ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx);
  }
  
  static int ath6kl_set_probed_ssids(struct ath6kl *ar,
                                   struct ath6kl_vif *vif,
-                                  struct cfg80211_ssid *ssids, int n_ssids)
+                                  struct cfg80211_ssid *ssids, int n_ssids,
+                                  struct cfg80211_match_set *match_set,
+                                  int n_match_ssid)
  {
-       u8 i;
+       u8 i, j, index_to_add, ssid_found = false;
+       struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS];
+       memset(ssid_list, 0, sizeof(ssid_list));
  
-       if (n_ssids > MAX_PROBED_SSID_INDEX)
+       if (n_ssids > MAX_PROBED_SSIDS ||
+           n_match_ssid > MAX_PROBED_SSIDS)
                return -EINVAL;
  
        for (i = 0; i < n_ssids; i++) {
+               memcpy(ssid_list[i].ssid.ssid,
+                      ssids[i].ssid,
+                      ssids[i].ssid_len);
+               ssid_list[i].ssid.ssid_len = ssids[i].ssid_len;
+               if (ssids[i].ssid_len)
+                       ssid_list[i].flag = SPECIFIC_SSID_FLAG;
+               else
+                       ssid_list[i].flag = ANY_SSID_FLAG;
+               if (n_match_ssid == 0)
+                       ssid_list[i].flag |= MATCH_SSID_FLAG;
+       }
+       index_to_add = i;
+       for (i = 0; i < n_match_ssid; i++) {
+               ssid_found = false;
+               for (j = 0; j < n_ssids; j++) {
+                       if ((match_set[i].ssid.ssid_len ==
+                            ssid_list[j].ssid.ssid_len) &&
+                           (!memcmp(ssid_list[j].ssid.ssid,
+                                    match_set[i].ssid.ssid,
+                                    match_set[i].ssid.ssid_len))) {
+                               ssid_list[j].flag |= MATCH_SSID_FLAG;
+                               ssid_found = true;
+                               break;
+                       }
+               }
+               if (ssid_found)
+                       continue;
+               if (index_to_add >= MAX_PROBED_SSIDS)
+                       continue;
+               ssid_list[index_to_add].ssid.ssid_len =
+                       match_set[i].ssid.ssid_len;
+               memcpy(ssid_list[index_to_add].ssid.ssid,
+                      match_set[i].ssid.ssid,
+                      match_set[i].ssid.ssid_len);
+               ssid_list[index_to_add].flag |= MATCH_SSID_FLAG;
+               index_to_add++;
+       }
+       for (i = 0; i < index_to_add; i++) {
                ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
-                                         ssids[i].ssid_len ?
-                                         SPECIFIC_SSID_FLAG : ANY_SSID_FLAG,
-                                         ssids[i].ssid_len,
-                                         ssids[i].ssid);
+                                         ssid_list[i].flag,
+                                         ssid_list[i].ssid.ssid_len,
+                                         ssid_list[i].ssid.ssid);
        }
  
        /* Make sure no old entries are left behind */
-       for (i = n_ssids; i < MAX_PROBED_SSID_INDEX; i++) {
+       for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) {
                ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i,
                                          DISABLE_SSID_FLAG, 0, NULL);
        }
@@@ -934,7 -992,7 +992,7 @@@ static int ath6kl_cfg80211_scan(struct 
        }
  
        ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
-                                     request->n_ssids);
+                                     request->n_ssids, NULL, 0);
        if (ret < 0)
                return ret;
  
                                       WMI_FRAME_PROBE_REQ,
                                       request->ie, request->ie_len);
        if (ret) {
-               ath6kl_err("failed to set Probe Request appie for scan");
+               ath6kl_err("failed to set Probe Request appie for scan\n");
                return ret;
        }
  
@@@ -1512,6 -1570,9 +1570,9 @@@ static int ath6kl_cfg80211_change_iface
                }
        }
  
+       /* need to clean up enhanced bmiss detection fw state */
+       ath6kl_cfg80211_sta_bmiss_enhance(vif, false);
  set_iface_type:
        switch (type) {
        case NL80211_IFTYPE_STATION:
@@@ -2074,7 -2135,9 +2135,9 @@@ static int ath6kl_wow_suspend(struct at
        if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
                return -EINVAL;
  
-       if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) {
+       if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
+           test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
+                    ar->fw_capabilities)) {
                ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
                                                vif->fw_vif_idx, false);
                if (ret)
@@@ -2209,7 -2272,9 +2272,9 @@@ static int ath6kl_wow_resume(struct ath
  
        ar->state = ATH6KL_STATE_ON;
  
-       if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags)) {
+       if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
+           test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
+                    ar->fw_capabilities)) {
                ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
                                        vif->fw_vif_idx, true);
                if (ret)
@@@ -2475,7 -2540,7 +2540,7 @@@ void ath6kl_check_wow_status(struct ath
  static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band,
                            bool ht_enable)
  {
-       struct ath6kl_htcap *htcap = &vif->htcap;
+       struct ath6kl_htcap *htcap = &vif->htcap[band];
  
        if (htcap->ht_enable == ht_enable)
                return 0;
@@@ -2585,6 -2650,30 +2650,30 @@@ static int ath6kl_set_ies(struct ath6kl
        return 0;
  }
  
+ void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable)
+ {
+       int err;
+       if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag)))
+               return;
+       if (vif->nw_type != INFRA_NETWORK)
+               return;
+       if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE,
+                     vif->ar->fw_capabilities))
+               return;
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n",
+                  enable ? "enable" : "disable");
+       err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi,
+                                              vif->fw_vif_idx, enable);
+       if (err)
+               ath6kl_err("failed to %s enhanced bmiss detection: %d\n",
+                          enable ? "enable" : "disable", err);
+ }
  static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
                                u8 *rsn_capab)
  {
@@@ -2665,9 -2754,15 +2754,15 @@@ static int ath6kl_start_ap(struct wiph
  
        /* TODO:
         * info->interval
-        * info->dtim_period
         */
  
+       ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx,
+                                        info->dtim_period);
+       /* ignore error, just print a warning and continue normally */
+       if (ret)
+               ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret);
        if (info->beacon.head == NULL)
                return -EINVAL;
        mgmt = (struct ieee80211_mgmt *) info->beacon.head;
@@@ -3131,10 -3226,24 +3226,24 @@@ static int ath6kl_cfg80211_sscan_start(
        ath6kl_cfg80211_scan_complete_event(vif, true);
  
        ret = ath6kl_set_probed_ssids(ar, vif, request->ssids,
-                                     request->n_ssids);
+                                     request->n_ssids,
+                                     request->match_sets,
+                                     request->n_match_sets);
        if (ret < 0)
                return ret;
  
+       if (!request->n_match_sets) {
+               ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+                                              ALL_BSS_FILTER, 0);
+               if (ret < 0)
+                       return ret;
+       } else {
+                ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx,
+                                               MATCHED_SSID_FILTER, 0);
+               if (ret < 0)
+                       return ret;
+       }
        /* fw uses seconds, also make sure that it's >0 */
        interval = max_t(u16, 1, request->interval / 1000);
  
                                       WMI_FRAME_PROBE_REQ,
                                       request->ie, request->ie_len);
        if (ret) {
-               ath6kl_warn("Failed to set probe request IE for scheduled scan: %d",
+               ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n",
                            ret);
                return ret;
        }
@@@ -3188,6 -3297,18 +3297,18 @@@ static int ath6kl_cfg80211_sscan_stop(s
        return 0;
  }
  
+ static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      const u8 *addr,
+                                      const struct cfg80211_bitrate_mask *mask)
+ {
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ath6kl_vif *vif = netdev_priv(dev);
+       return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx,
+                                          mask);
+ }
  static const struct ieee80211_txrx_stypes
  ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = {
        [NL80211_IFTYPE_STATION] = {
@@@ -3253,6 -3374,7 +3374,7 @@@ static struct cfg80211_ops ath6kl_cfg80
        .mgmt_frame_register = ath6kl_mgmt_frame_register,
        .sched_scan_start = ath6kl_cfg80211_sscan_start,
        .sched_scan_stop = ath6kl_cfg80211_sscan_stop,
+       .set_bitrate_mask = ath6kl_cfg80211_set_bitrate,
  };
  
  void ath6kl_cfg80211_stop(struct ath6kl_vif *vif)
@@@ -3380,7 -3502,8 +3502,8 @@@ struct net_device *ath6kl_interface_add
        vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL;
        vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME;
        vif->bg_scan_period = 0;
-       vif->htcap.ht_enable = true;
+       vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true;
+       vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true;
  
        memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);
        if (fw_vif_idx != 0)
@@@ -3440,7 -3563,13 +3563,13 @@@ int ath6kl_cfg80211_init(struct ath6kl 
        }
  
        /* max num of ssids that can be probed during scanning */
-       wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX;
+       wiphy->max_scan_ssids = MAX_PROBED_SSIDS;
+       /* max num of ssids that can be matched after scan */
+       if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST,
+                    ar->fw_capabilities))
+               wiphy->max_match_sets = MAX_PROBED_SSIDS;
        wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */
        switch (ar->hw.cap) {
        case WMI_11AN_CAP:
                ath6kl_band_5ghz.ht_cap.cap = 0;
                ath6kl_band_5ghz.ht_cap.ht_supported = false;
        }
+       if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) {
+               ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+               ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff;
+       } else {
+               ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+               ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
+       }
        if (band_2gig)
                wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz;
        if (band_5gig)
        wiphy->cipher_suites = cipher_suites;
        wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
  
 +#ifdef CONFIG_PM
        wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
                              WIPHY_WOWLAN_DISCONNECT |
                              WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
        wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST;
        wiphy->wowlan.pattern_min_len = 1;
        wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE;
 +#endif
  
-       wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX;
+       wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS;
  
        ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM |
                            WIPHY_FLAG_HAVE_AP_SME |
index 747a997bc608e1e763b07e19ff18176a1aac0a85,2240cca597acf812d87a4291aa855d4e39272f90..0cc9a0240220bdc4c5fb3bb05ff8ad064548d891
@@@ -378,9 -378,9 +378,9 @@@ static void wl12xx_irq_update_links_sta
        }
  }
  
- static void wl12xx_fw_status(struct wl1271 *wl,
-                            struct wl_fw_status_1 *status_1,
-                            struct wl_fw_status_2 *status_2)
+ static int wlcore_fw_status(struct wl1271 *wl,
+                           struct wl_fw_status_1 *status_1,
+                           struct wl_fw_status_2 *status_2)
  {
        struct wl12xx_vif *wlvif;
        struct timespec ts;
        int avail, freed_blocks;
        int i;
        size_t status_len;
+       int ret;
  
        status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                sizeof(*status_2) + wl->fw_status_priv_len;
  
-       wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
-                            status_len, false);
+       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
+                                  status_len, false);
+       if (ret < 0)
+               return ret;
  
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
                     "drv_rx_counter = %d, tx_results_counter = %d)",
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
                (s64)le32_to_cpu(status_2->fw_localtime);
+       return 0;
  }
  
  static void wl1271_flush_deferred_work(struct wl1271 *wl)
@@@ -489,20 -494,15 +494,15 @@@ static void wl1271_netstack_work(struc
  
  #define WL1271_IRQ_MAX_LOOPS 256
  
- static irqreturn_t wl1271_irq(int irq, void *cookie)
+ static int wlcore_irq_locked(struct wl1271 *wl)
  {
-       int ret;
+       int ret = 0;
        u32 intr;
        int loopcount = WL1271_IRQ_MAX_LOOPS;
-       struct wl1271 *wl = (struct wl1271 *)cookie;
        bool done = false;
        unsigned int defer_count;
        unsigned long flags;
  
-       /* TX might be handled here, avoid redundant work */
-       set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
-       cancel_work_sync(&wl->tx_work);
        /*
         * In case edge triggered interrupt must be used, we cannot iterate
         * more than once without introducing race conditions with the hardirq.
        if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
                loopcount = 1;
  
-       mutex_lock(&wl->mutex);
        wl1271_debug(DEBUG_IRQ, "IRQ work");
  
        if (unlikely(wl->state == WL1271_STATE_OFF))
                clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
                smp_mb__after_clear_bit();
  
-               wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+               ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+               if (ret < 0)
+                       goto out;
  
                wlcore_hw_tx_immediate_compl(wl);
  
                if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
                        wl1271_error("HW watchdog interrupt received! starting recovery.");
                        wl->watchdog_recovery = true;
-                       wl12xx_queue_recovery_work(wl);
+                       ret = -EIO;
  
                        /* restarting the chip. ignore any other interrupt. */
                        goto out;
                        wl1271_error("SW watchdog interrupt received! "
                                     "starting recovery.");
                        wl->watchdog_recovery = true;
-                       wl12xx_queue_recovery_work(wl);
+                       ret = -EIO;
  
                        /* restarting the chip. ignore any other interrupt. */
                        goto out;
                if (likely(intr & WL1271_ACX_INTR_DATA)) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
  
-                       wl12xx_rx(wl, wl->fw_status_1);
+                       ret = wlcore_rx(wl, wl->fw_status_1);
+                       if (ret < 0)
+                               goto out;
  
                        /* Check if any tx blocks were freed */
                        spin_lock_irqsave(&wl->wl_lock, flags);
                                 * In order to avoid starvation of the TX path,
                                 * call the work function directly.
                                 */
-                               wl1271_tx_work_locked(wl);
+                               ret = wlcore_tx_work_locked(wl);
+                               if (ret < 0)
+                                       goto out;
                        } else {
                                spin_unlock_irqrestore(&wl->wl_lock, flags);
                        }
  
                        /* check for tx results */
-                       wlcore_hw_tx_delayed_compl(wl);
+                       ret = wlcore_hw_tx_delayed_compl(wl);
+                       if (ret < 0)
+                               goto out;
  
                        /* Make sure the deferred queues don't get too long */
                        defer_count = skb_queue_len(&wl->deferred_tx_queue) +
  
                if (intr & WL1271_ACX_INTR_EVENT_A) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
-                       wl1271_event_handle(wl, 0);
+                       ret = wl1271_event_handle(wl, 0);
+                       if (ret < 0)
+                               goto out;
                }
  
                if (intr & WL1271_ACX_INTR_EVENT_B) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
-                       wl1271_event_handle(wl, 1);
+                       ret = wl1271_event_handle(wl, 1);
+                       if (ret < 0)
+                               goto out;
                }
  
                if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
        wl1271_ps_elp_sleep(wl);
  
  out:
+       return ret;
+ }
+ static irqreturn_t wlcore_irq(int irq, void *cookie)
+ {
+       int ret;
+       unsigned long flags;
+       struct wl1271 *wl = cookie;
+       /* TX might be handled here, avoid redundant work */
+       set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
+       cancel_work_sync(&wl->tx_work);
+       mutex_lock(&wl->mutex);
+       ret = wlcore_irq_locked(wl);
+       if (ret)
+               wl12xx_queue_recovery_work(wl);
        spin_lock_irqsave(&wl->wl_lock, flags);
        /* In case TX was not handled here, queue TX work */
        clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
@@@ -743,8 -772,13 +772,13 @@@ out
  
  void wl12xx_queue_recovery_work(struct wl1271 *wl)
  {
-       if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+       WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
+       /* Avoid a recursive recovery */
+       if (!test_and_set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
+               wlcore_disable_interrupts_nosync(wl);
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
+       }
  }
  
  size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
        return len;
  }
  
+ #define WLCORE_FW_LOG_END 0x2000000
  static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
  {
        u32 addr;
-       u32 first_addr;
+       u32 offset;
+       u32 end_of_log;
        u8 *block;
+       int ret;
  
        if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) ||
-           (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) ||
            (wl->conf.fwlog.mem_blocks == 0))
                return;
  
         * Make sure the chip is awake and the logger isn't active.
         * Do not send a stop fwlog command if the fw is hanged.
         */
-       if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery)
-               wl12xx_cmd_stop_fwlog(wl);
-       else
+       if (wl1271_ps_elp_wakeup(wl))
                goto out;
+       if (!wl->watchdog_recovery)
+               wl12xx_cmd_stop_fwlog(wl);
  
        /* Read the first memory block address */
-       wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
-       first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
-       if (!first_addr)
+       ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+       if (ret < 0)
                goto out;
  
+       addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
+       if (!addr)
+               goto out;
+       if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
+               offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
+               end_of_log = WLCORE_FW_LOG_END;
+       } else {
+               offset = sizeof(addr);
+               end_of_log = addr;
+       }
        /* Traverse the memory blocks linked list */
-       addr = first_addr;
        do {
                memset(block, 0, WL12XX_HW_BLOCK_SIZE);
-               wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
-                                  false);
+               ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
+                                        false);
+               if (ret < 0)
+                       goto out;
  
                /*
                 * Memory blocks are linked to one another. The first 4 bytes
                 * of each memory block hold the hardware address of the next
-                * one. The last memory block points to the first one.
+                * one. The last memory block points to the first one in
+                * on demand mode and is equal to 0x2000000 in continuous mode.
                 */
                addr = le32_to_cpup((__le32 *)block);
-               if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
-                                      WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
+               if (!wl12xx_copy_fwlog(wl, block + offset,
+                                      WL12XX_HW_BLOCK_SIZE - offset))
                        break;
-       } while (addr && (addr != first_addr));
+       } while (addr && (addr != end_of_log));
  
        wake_up_interruptible(&wl->fwlog_waitq);
  
@@@ -826,6 -876,34 +876,34 @@@ out
        kfree(block);
  }
  
+ static void wlcore_print_recovery(struct wl1271 *wl)
+ {
+       u32 pc = 0;
+       u32 hint_sts = 0;
+       int ret;
+       wl1271_info("Hardware recovery in progress. FW ver: %s",
+                   wl->chip.fw_ver_str);
+       /* change partitions momentarily so we can read the FW pc */
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+       if (ret < 0)
+               return;
+       ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc);
+       if (ret < 0)
+               return;
+       ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &hint_sts);
+       if (ret < 0)
+               return;
+       wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts);
+       wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+ }
  static void wl1271_recovery_work(struct work_struct *work)
  {
        struct wl1271 *wl =
        if (wl->state != WL1271_STATE_ON || wl->plt)
                goto out_unlock;
  
-       /* Avoid a recursive recovery */
-       set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
        wl12xx_read_fwlog_panic(wl);
  
-       /* change partitions momentarily so we can read the FW pc */
-       wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
-       wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x "
-                   "hint_sts: 0x%08x",
-                   wl->chip.fw_ver_str,
-                   wlcore_read_reg(wl, REG_PC_ON_RECOVERY),
-                   wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR));
-       wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
+       wlcore_print_recovery(wl);
  
        BUG_ON(bug_on_recovery &&
               !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
        mutex_unlock(&wl->mutex);
        wl1271_op_stop(wl->hw);
  
-       clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
        ieee80211_restart_hw(wl->hw);
  
        /*
@@@ -907,9 -973,9 +973,9 @@@ out_unlock
        mutex_unlock(&wl->mutex);
  }
  
- static void wl1271_fw_wakeup(struct wl1271 *wl)
+ static int wlcore_fw_wakeup(struct wl1271 *wl)
  {
-       wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
+       return wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP);
  }
  
  static int wl1271_setup(struct wl1271 *wl)
@@@ -945,13 -1011,21 +1011,21 @@@ static int wl12xx_set_power_on(struct w
        wl1271_io_reset(wl);
        wl1271_io_init(wl);
  
-       wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+       ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+       if (ret < 0)
+               goto fail;
  
        /* ELP module wake up */
-       wl1271_fw_wakeup(wl);
+       ret = wlcore_fw_wakeup(wl);
+       if (ret < 0)
+               goto fail;
  
  out:
        return ret;
+ fail:
+       wl1271_power_off(wl);
+       return ret;
  }
  
  static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
@@@ -1082,6 -1156,7 +1156,7 @@@ int wl1271_plt_stop(struct wl1271 *wl
        mutex_lock(&wl->mutex);
        wl1271_power_off(wl);
        wl->flags = 0;
+       wl->sleep_auth = WL1271_PSM_ILLEGAL;
        wl->state = WL1271_STATE_OFF;
        wl->plt = false;
        wl->rx_counter = 0;
@@@ -1173,7 -1248,7 +1248,7 @@@ int wl1271_tx_dummy_packet(struct wl127
  
        /* The FW is low on RX memory blocks, so send the dummy packet asap */
        if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
-               wl1271_tx_work_locked(wl);
+               return wlcore_tx_work_locked(wl);
  
        /*
         * If the FW TX is busy, TX work will be scheduled by the threaded
@@@ -1440,8 -1515,15 +1515,15 @@@ static int wl1271_configure_wowlan(stru
        int i, ret;
  
        if (!wow || wow->any || !wow->n_patterns) {
-               wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
-               wl1271_rx_filter_clear_all(wl);
+               ret = wl1271_acx_default_rx_filter_enable(wl, 0,
+                                                         FILTER_SIGNAL);
+               if (ret)
+                       goto out;
+               ret = wl1271_rx_filter_clear_all(wl);
+               if (ret)
+                       goto out;
                return 0;
        }
  
                }
        }
  
-       wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
-       wl1271_rx_filter_clear_all(wl);
+       ret = wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
+       if (ret)
+               goto out;
+       ret = wl1271_rx_filter_clear_all(wl);
+       if (ret)
+               goto out;
  
        /* Translate WoWLAN patterns into filters */
        for (i = 0; i < wow->n_patterns; i++) {
@@@ -1500,7 -1587,10 +1587,10 @@@ static int wl1271_configure_suspend_sta
        if (ret < 0)
                goto out;
  
-       wl1271_configure_wowlan(wl, wow);
+       ret = wl1271_configure_wowlan(wl, wow);
+       if (ret < 0)
+               goto out_sleep;
        ret = wl1271_acx_wake_up_conditions(wl, wlvif,
                                    wl->conf.conn.suspend_wake_up_event,
                                    wl->conf.conn.suspend_listen_interval);
        if (ret < 0)
                wl1271_error("suspend: set wake up conditions failed: %d", ret);
  
+ out_sleep:
        wl1271_ps_elp_sleep(wl);
  out:
        return ret;
  
@@@ -1588,6 -1678,12 +1678,12 @@@ static int wl1271_op_suspend(struct iee
        wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
        WARN_ON(!wow);
  
+       /* we want to perform the recovery before suspending */
+       if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
+               wl1271_warning("postponing suspend to perform recovery");
+               return -EBUSY;
+       }
        wl1271_tx_flush(wl);
  
        mutex_lock(&wl->mutex);
@@@ -1628,7 -1724,8 +1724,8 @@@ static int wl1271_op_resume(struct ieee
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif;
        unsigned long flags;
-       bool run_irq_work = false;
+       bool run_irq_work = false, pending_recovery;
+       int ret;
  
        wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d",
                     wl->wow_enabled);
                run_irq_work = true;
        spin_unlock_irqrestore(&wl->wl_lock, flags);
  
+       mutex_lock(&wl->mutex);
+       /* test the recovery flag before calling any SDIO functions */
+       pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
+                                   &wl->flags);
        if (run_irq_work) {
                wl1271_debug(DEBUG_MAC80211,
                             "run postponed irq_work directly");
-               wl1271_irq(0, wl);
+               /* don't talk to the HW if recovery is pending */
+               if (!pending_recovery) {
+                       ret = wlcore_irq_locked(wl);
+                       if (ret)
+                               wl12xx_queue_recovery_work(wl);
+               }
                wlcore_enable_interrupts(wl);
        }
  
-       mutex_lock(&wl->mutex);
+       if (pending_recovery) {
+               wl1271_warning("queuing forgotten recovery on resume");
+               ieee80211_queue_work(wl->hw, &wl->recovery_work);
+               goto out;
+       }
        wl12xx_for_each_wlvif(wl, wlvif) {
                wl1271_configure_resume(wl, wlvif);
        }
+ out:
        wl->wow_enabled = false;
        mutex_unlock(&wl->mutex);
  
@@@ -1695,6 -1812,10 +1812,10 @@@ static void wl1271_op_stop(struct ieee8
        wlcore_disable_interrupts(wl);
        mutex_lock(&wl->mutex);
        if (wl->state == WL1271_STATE_OFF) {
+               if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
+                                       &wl->flags))
+                       wlcore_enable_interrupts(wl);
                mutex_unlock(&wl->mutex);
  
                /*
        mutex_lock(&wl->mutex);
  
        wl1271_power_off(wl);
+       /*
+        * In case a recovery was scheduled, interrupts were disabled to avoid
+        * an interrupt storm. Now that the power is down, it is safe to
+        * re-enable interrupts to balance the disable depth
+        */
+       if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+               wlcore_enable_interrupts(wl);
  
        wl->band = IEEE80211_BAND_2GHZ;
  
        wl->ap_fw_ps_map = 0;
        wl->ap_ps_map = 0;
        wl->sched_scanning = false;
+       wl->sleep_auth = WL1271_PSM_ILLEGAL;
        memset(wl->roles_map, 0, sizeof(wl->roles_map));
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
@@@ -2146,6 -2275,7 +2275,7 @@@ static void __wl1271_op_remove_interfac
  {
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int i, ret;
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
  
        wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
  
@@@ -2226,11 -2356,25 +2356,25 @@@ deinit
        wlvif->role_id = WL12XX_INVALID_ROLE_ID;
        wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
  
-       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+       if (is_ap)
                wl->ap_count--;
        else
                wl->sta_count--;
  
+       /* Last AP, have more stations. Configure according to STA. */
+       if (wl->ap_count == 0 && is_ap && wl->sta_count) {
+               u8 sta_auth = wl->conf.conn.sta_sleep_auth;
+               /* Configure for power according to debugfs */
+               if (sta_auth != WL1271_PSM_ILLEGAL)
+                       wl1271_acx_sleep_auth(wl, sta_auth);
+               /* Configure for power always on */
+               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
+                       wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+               /* Configure for ELP power saving */
+               else
+                       wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
+       }
        mutex_unlock(&wl->mutex);
  
        del_timer_sync(&wlvif->rx_streaming_timer);
@@@ -2448,12 -2592,16 +2592,16 @@@ static int wl12xx_config_vif(struct wl1
             (wlvif->channel != channel) ||
             (wlvif->channel_type != conf->channel_type))) {
                /* send all pending packets */
-               wl1271_tx_work_locked(wl);
+               ret = wlcore_tx_work_locked(wl);
+               if (ret < 0)
+                       return ret;
                wlvif->band = conf->channel->band;
                wlvif->channel = channel;
                wlvif->channel_type = conf->channel_type;
  
                if (is_ap) {
+                       wl1271_set_band_rate(wl, wlvif);
                        ret = wl1271_init_ap_rates(wl, wlvif);
                        if (ret < 0)
                                wl1271_error("AP rate policy change failed %d",
  static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
                                struct survey_info *survey)
  {
-       struct wl1271 *wl = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
  
        if (idx != 0)
                return -ENOENT;
  
        survey->channel = conf->channel;
-       survey->filled = SURVEY_INFO_NOISE_DBM;
-       survey->noise = wl->noise;
+       survey->filled = 0;
        return 0;
  }
  
@@@ -4365,9 -4510,14 +4510,14 @@@ static int wl1271_op_ampdu_action(struc
  
        case IEEE80211_AMPDU_RX_STOP:
                if (!(*ba_bitmap & BIT(tid))) {
-                       ret = -EINVAL;
-                       wl1271_error("no active RX BA session on tid: %d",
+                       /*
+                        * this happens on reconfig - so only output a debug
+                        * message for now, and don't fail the function.
+                        */
+                       wl1271_debug(DEBUG_MAC80211,
+                                    "no active RX BA session on tid: %d",
                                     tid);
+                       ret = 0;
                        break;
                }
  
@@@ -4904,18 -5054,22 +5054,22 @@@ static int wl12xx_get_hw_info(struct wl
        if (ret < 0)
                goto out;
  
-       wl->chip.id = wlcore_read_reg(wl, REG_CHIP_ID_B);
+       ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
+       if (ret < 0)
+               goto out;
  
        wl->fuse_oui_addr = 0;
        wl->fuse_nic_addr = 0;
  
-       wl->hw_pg_ver = wl->ops->get_pg_ver(wl);
+       ret = wl->ops->get_pg_ver(wl, &wl->hw_pg_ver);
+       if (ret < 0)
+               goto out;
  
        if (wl->ops->get_mac)
-               wl->ops->get_mac(wl);
+               ret = wl->ops->get_mac(wl);
  
-       wl1271_power_off(wl);
  out:
+       wl1271_power_off(wl);
        return ret;
  }
  
@@@ -4976,6 -5130,29 +5130,29 @@@ static void wl1271_unregister_hw(struc
  
  }
  
+ static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
+       {
+               .max = 2,
+               .types = BIT(NL80211_IFTYPE_STATION),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_AP) |
+                        BIT(NL80211_IFTYPE_P2P_GO) |
+                        BIT(NL80211_IFTYPE_P2P_CLIENT),
+       },
+ };
+ static const struct ieee80211_iface_combination
+ wlcore_iface_combinations[] = {
+       {
+         .num_different_channels = 1,
+         .max_interfaces = 2,
+         .limits = wlcore_iface_limits,
+         .n_limits = ARRAY_SIZE(wlcore_iface_limits),
+       },
+ };
  static int wl1271_init_ieee80211(struct wl1271 *wl)
  {
        static const u32 cipher_suites[] = {
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
  
+       /* allowed interface combinations */
+       wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
+       wl->hw->wiphy->n_iface_combinations =
+               ARRAY_SIZE(wlcore_iface_combinations);
        SET_IEEE80211_DEV(wl->hw, wl->dev);
  
        wl->hw->sta_data_size = sizeof(struct wl1271_station);
@@@ -5140,6 -5322,7 +5322,7 @@@ struct ieee80211_hw *wlcore_alloc_hw(si
        wl->channel_type = NL80211_CHAN_NO_HT;
        wl->flags = 0;
        wl->sg_enabled = true;
+       wl->sleep_auth = WL1271_PSM_ILLEGAL;
        wl->hw_pg_ver = -1;
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
@@@ -5313,7 -5496,7 +5496,7 @@@ int __devinit wlcore_probe(struct wl127
        else
                irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
  
-       ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wl1271_irq,
+       ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq,
                                   irqflags,
                                   pdev->name, wl);
        if (ret < 0) {
                goto out_free_hw;
        }
  
 +#ifdef CONFIG_PM
        ret = enable_irq_wake(wl->irq);
        if (!ret) {
                wl->irq_wake_enabled = true;
                                WL1271_RX_FILTER_MAX_PATTERN_SIZE;
                }
        }
 +#endif
        disable_irq(wl->irq);
  
        ret = wl12xx_get_hw_info(wl);
        if (ret < 0) {
                wl1271_error("couldn't get hw info");
-               goto out;
+               goto out_irq;
        }
  
        ret = wl->ops->identify_chip(wl);
        if (ret < 0)
-               goto out;
+               goto out_irq;
  
        ret = wl1271_init_ieee80211(wl);
        if (ret)
        ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
        if (ret < 0) {
                wl1271_error("failed to create sysfs file bt_coex_state");
-               goto out_irq;
+               goto out_unreg;
        }
  
        /* Create sysfs file to get HW PG version */
@@@ -5385,6 -5566,9 +5568,9 @@@ out_hw_pg_ver
  out_bt_coex_state:
        device_remove_file(wl->dev, &dev_attr_bt_coex_state);
  
+ out_unreg:
+       wl1271_unregister_hw(wl);
  out_irq:
        free_irq(wl->irq, wl);
  
diff --combined include/net/mac80211.h
index 5e67020b1702d68f87bb3ace4f35e6b1616b67b3,670a58ba8a41a52d832b7237cc552ef631177f2a..b5da094468f17e20f6097fc7e9983ccb4bdc1165
@@@ -1945,6 -1945,11 +1945,11 @@@ enum ieee80211_rate_control_changed 
   *    to also unregister the device. If it returns 1, then mac80211
   *    will also go through the regular complete restart on resume.
   *
+  * @set_wakeup: Enable or disable wakeup when WoWLAN configuration is
+  *    modified. The reason is that device_set_wakeup_enable() is
+  *    supposed to be called when the configuration changes, not only
+  *    in suspend().
+  *
   * @add_interface: Called when a netdevice attached to the hardware is
   *    enabled. Because it is not called for monitor mode devices, @start
   *    and @stop must be implemented.
@@@ -2979,6 -2984,7 +2984,7 @@@ __le16 ieee80211_ctstoself_duration(str
   * ieee80211_generic_frame_duration - Calculate the duration field for a frame
   * @hw: pointer obtained from ieee80211_alloc_hw().
   * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+  * @band: the band to calculate the frame duration on
   * @frame_len: the length of the frame.
   * @rate: the rate at which the frame is going to be transmitted.
   *
@@@ -3826,6 -3832,12 +3832,6 @@@ void ieee80211_enable_rssi_reports(stru
  
  void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif);
  
 -int ieee80211_add_srates_ie(struct ieee80211_vif *vif,
 -                          struct sk_buff *skb, bool need_basic);
 -
 -int ieee80211_add_ext_srates_ie(struct ieee80211_vif *vif,
 -                              struct sk_buff *skb, bool need_basic);
 -
  /**
   * ieee80211_ave_rssi - report the average rssi for the specified interface
   *
diff --combined net/mac80211/cfg.c
index ebc353ef6902dd6535503fa8159863d37604bd46,c2a2dcbfdf017abc788b58dd3744e3e7a390a18e..0f02c8b77e1c8f95769d738b0b32cce482768675
@@@ -2111,6 -2111,9 +2111,9 @@@ static int ieee80211_set_bitrate_mask(s
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        int i, ret;
  
+       if (!ieee80211_sdata_running(sdata))
+               return -ENETDOWN;
        if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) {
                ret = drv_set_bitrate_mask(local, sdata, mask);
                if (ret)
@@@ -2665,8 -2668,8 +2668,8 @@@ ieee80211_prep_tdls_encap_data(struct w
                tf->u.setup_req.capability =
                        cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
  
 -              ieee80211_add_srates_ie(&sdata->vif, skb, false);
 -              ieee80211_add_ext_srates_ie(&sdata->vif, skb, false);
 +              ieee80211_add_srates_ie(sdata, skb, false);
 +              ieee80211_add_ext_srates_ie(sdata, skb, false);
                ieee80211_tdls_add_ext_capab(skb);
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
                tf->u.setup_resp.capability =
                        cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
  
 -              ieee80211_add_srates_ie(&sdata->vif, skb, false);
 -              ieee80211_add_ext_srates_ie(&sdata->vif, skb, false);
 +              ieee80211_add_srates_ie(sdata, skb, false);
 +              ieee80211_add_ext_srates_ie(sdata, skb, false);
                ieee80211_tdls_add_ext_capab(skb);
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
@@@ -2740,8 -2743,8 +2743,8 @@@ ieee80211_prep_tdls_direct(struct wiph
                mgmt->u.action.u.tdls_discover_resp.capability =
                        cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
  
 -              ieee80211_add_srates_ie(&sdata->vif, skb, false);
 -              ieee80211_add_ext_srates_ie(&sdata->vif, skb, false);
 +              ieee80211_add_srates_ie(sdata, skb, false);
 +              ieee80211_add_ext_srates_ie(sdata, skb, false);
                ieee80211_tdls_add_ext_capab(skb);
                break;
        default:
diff --combined net/mac80211/mlme.c
index 398acc4f649d426f74d7b4af9e36970fa05153ff,f1a80da4e56ac6dacb384034aceb20329b6bc69e..398ce8e9c4d733084e4d01b90c21433ab3dddb57
@@@ -902,6 -902,9 +902,6 @@@ static bool ieee80211_powersave_allowed
        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;
@@@ -1331,6 -1334,8 +1331,8 @@@ static void ieee80211_set_disassoc(stru
        if (WARN_ON(!ifmgd->associated))
                return;
  
+       ieee80211_stop_poll(sdata);
        memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
  
        ifmgd->associated = NULL;
        }
        mutex_unlock(&local->sta_mtx);
  
 +      /* 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,
@@@ -2589,8 -2590,6 +2591,6 @@@ static void ieee80211_sta_connection_lo
        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);
@@@ -3081,7 -3080,7 +3081,7 @@@ static int ieee80211_prep_connection(st
        }
  
        local->oper_channel = cbss->channel;
-       ieee80211_hw_config(local, 0);
+       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
  
        if (sta) {
                u32 rates = 0, basic_rates = 0;