Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 9 Aug 2013 19:06:28 +0000 (15:06 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 9 Aug 2013 19:06:28 +0000 (15:06 -0400)
18 files changed:
1  2 
MAINTAINERS
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cfp.c
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_ioctl.c
net/mac80211/mlme.c
net/wireless/core.c
net/wireless/nl80211.c

diff --combined MAINTAINERS
@@@ -4362,7 -4362,7 +4362,7 @@@ F:      drivers/net/wireless/iwlegacy
  
  INTEL WIRELESS WIFI LINK (iwlwifi)
  M:    Johannes Berg <johannes.berg@intel.com>
 -M:    Wey-Yi Guy <wey-yi.w.guy@intel.com>
 +M:    Emmanuel Grumbach <emmanuel.grumbach@intel.com>
  M:    Intel Linux Wireless <ilw@linux.intel.com>
  L:    linux-wireless@vger.kernel.org
  W:    http://intellinuxwireless.org
@@@ -8272,7 -8272,7 +8272,7 @@@ S:      Maintaine
  F:    sound/soc/codecs/twl4030*
  
  TI WILINK WIRELESS DRIVERS
- M:    Luciano Coelho <coelho@ti.com>
+ M:    Luciano Coelho <luca@coelho.fi>
  L:    linux-wireless@vger.kernel.org
  W:    http://wireless.kernel.org/en/users/Drivers/wl12xx
  W:    http://wireless.kernel.org/en/users/Drivers/wl1251
@@@ -574,11 -574,9 +574,11 @@@ il4965_translate_rx_status(struct il_pr
        return decrypt_out;
  }
  
 +#define SMALL_PACKET_SIZE 256
 +
  static void
  il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
 -                             u16 len, u32 ampdu_status, struct il_rx_buf *rxb,
 +                             u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
                               struct ieee80211_rx_status *stats)
  {
        struct sk_buff *skb;
            il_set_decrypted_flag(il, hdr, ampdu_status, stats))
                return;
  
 -      skb = dev_alloc_skb(128);
 +      skb = dev_alloc_skb(SMALL_PACKET_SIZE);
        if (!skb) {
                IL_ERR("dev_alloc_skb failed\n");
                return;
        }
  
 -      skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len,
 -                      len);
 +      if (len <= SMALL_PACKET_SIZE) {
 +              memcpy(skb_put(skb, len), hdr, len);
 +      } else {
 +              skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
 +                              len, PAGE_SIZE << il->hw_params.rx_page_order);
 +              il->alloc_rxb_page--;
 +              rxb->page = NULL;
 +      }
  
        il_update_stats(il, false, fc, len);
        memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
  
        ieee80211_rx(il->hw, skb);
 -      il->alloc_rxb_page--;
 -      rxb->page = NULL;
  }
  
  /* Called for N_RX (legacy ABG frames), or
@@@ -4466,12 -4460,12 +4466,12 @@@ il4965_irq_tasklet(struct il_priv *il
                 * is killed. Hence update the killswitch state here. The
                 * rfkill handler will care about restarting if needed.
                 */
-               if (!test_bit(S_ALIVE, &il->status)) {
-                       if (hw_rf_kill)
-                               set_bit(S_RFKILL, &il->status);
-                       else
-                               clear_bit(S_RFKILL, &il->status);
+               if (hw_rf_kill) {
+                       set_bit(S_RFKILL, &il->status);
+               } else {
+                       clear_bit(S_RFKILL, &il->status);
                        wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill);
+                       il_force_reset(il, true);
                }
  
                handled |= CSR_INT_BIT_RF_KILL;
@@@ -5340,6 -5334,9 +5340,9 @@@ il4965_alive_start(struct il_priv *il
  
        il->active_rate = RATES_MASK;
  
+       il_power_update_mode(il, true);
+       D_INFO("Updated power mode\n");
        if (il_is_associated(il)) {
                struct il_rxon_cmd *active_rxon =
                    (struct il_rxon_cmd *)&il->active;
        D_INFO("ALIVE processing complete.\n");
        wake_up(&il->wait_command_queue);
  
-       il_power_update_mode(il, true);
-       D_INFO("Updated power mode\n");
        return;
  
  restart:
@@@ -105,7 -105,7 +105,7 @@@ void iwl_mvm_ipv6_addr_change(struct ie
        list_for_each_entry(ifa, &idev->addr_list, if_list) {
                mvmvif->target_ipv6_addrs[idx] = ifa->addr;
                idx++;
 -              if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS)
 +              if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
                        break;
        }
        read_unlock_bh(&idev->lock);
@@@ -134,7 -134,7 +134,7 @@@ struct wowlan_key_data 
        struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
        struct iwl_wowlan_tkip_params_cmd *tkip;
        bool error, use_rsc_tsc, use_tkip;
-       int gtk_key_idx;
+       int wep_key_idx;
  };
  
  static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
                        wkc.wep_key.key_offset = 0;
                } else {
                        /* others start at 1 */
-                       data->gtk_key_idx++;
-                       wkc.wep_key.key_offset = data->gtk_key_idx;
+                       data->wep_key_idx++;
+                       wkc.wep_key.key_offset = data->wep_key_idx;
                }
  
                ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC,
                mvm->ptk_ivlen = key->iv_len;
                mvm->ptk_icvlen = key->icv_len;
        } else {
-               data->gtk_key_idx++;
-               key->hw_key_idx = data->gtk_key_idx;
+               /*
+                * firmware only supports TSC/RSC for a single key,
+                * so if there are multiple keep overwriting them
+                * with new ones -- this relies on mac80211 doing
+                * list_add_tail().
+                */
+               key->hw_key_idx = 1;
                mvm->gtk_ivlen = key->iv_len;
                mvm->gtk_icvlen = key->icv_len;
        }
@@@ -373,68 -378,36 +378,68 @@@ static int iwl_mvm_send_patterns(struc
  static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
                                      struct ieee80211_vif *vif)
  {
 -      struct iwl_proto_offload_cmd cmd = {};
 +      union {
 +              struct iwl_proto_offload_cmd_v1 v1;
 +              struct iwl_proto_offload_cmd_v2 v2;
 +      } cmd = {};
 +      struct iwl_proto_offload_cmd_common *common;
 +      u32 enabled = 0, size;
  #if IS_ENABLED(CONFIG_IPV6)
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int i;
  
 -      if (mvmvif->num_target_ipv6_addrs) {
 -              cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS);
 -              memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN);
 -      }
 +      if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
 +              if (mvmvif->num_target_ipv6_addrs) {
 +                      enabled |= IWL_D3_PROTO_OFFLOAD_NS;
 +                      memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
 +              }
 +
 +              BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
 +                           sizeof(mvmvif->target_ipv6_addrs[0]));
 +
 +              for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
 +                                  IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
 +                      memcpy(cmd.v2.target_ipv6_addr[i],
 +                             &mvmvif->target_ipv6_addrs[i],
 +                             sizeof(cmd.v2.target_ipv6_addr[i]));
 +      } else {
 +              if (mvmvif->num_target_ipv6_addrs) {
 +                      enabled |= IWL_D3_PROTO_OFFLOAD_NS;
 +                      memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
 +              }
  
 -      BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) !=
 -                   sizeof(mvmvif->target_ipv6_addrs[i]));
 +              BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
 +                           sizeof(mvmvif->target_ipv6_addrs[0]));
  
 -      for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++)
 -              memcpy(cmd.target_ipv6_addr[i],
 -                     &mvmvif->target_ipv6_addrs[i],
 -                     sizeof(cmd.target_ipv6_addr[i]));
 +              for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
 +                                  IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
 +                      memcpy(cmd.v1.target_ipv6_addr[i],
 +                             &mvmvif->target_ipv6_addrs[i],
 +                             sizeof(cmd.v1.target_ipv6_addr[i]));
 +      }
  #endif
  
 +      if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
 +              common = &cmd.v2.common;
 +              size = sizeof(cmd.v2);
 +      } else {
 +              common = &cmd.v1.common;
 +              size = sizeof(cmd.v1);
 +      }
 +
        if (vif->bss_conf.arp_addr_cnt) {
 -              cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP);
 -              cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
 -              memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN);
 +              enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
 +              common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
 +              memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
        }
  
 -      if (!cmd.enabled)
 +      if (!enabled)
                return 0;
  
 +      common->enabled = cpu_to_le32(enabled);
 +
        return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
 -                                  sizeof(cmd), &cmd);
 +                                  size, &cmd);
  }
  
  enum iwl_mvm_tcp_packet_type {
@@@ -69,7 -69,6 +69,6 @@@
  /* Scan Commands, Responses, Notifications */
  
  /* Masks for iwl_scan_channel.type flags */
- #define SCAN_CHANNEL_TYPE_PASSIVE     0
  #define SCAN_CHANNEL_TYPE_ACTIVE      BIT(0)
  #define SCAN_CHANNEL_NARROW_BAND      BIT(22)
  
@@@ -138,8 -137,6 +137,8 @@@ struct iwl_ssid_ie 
   *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
   *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
   *@SCAN_FLAGS_FRAGMENTED_SCAN:
 + *@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active
 + *    in the past hour, even if they are marked as passive.
   */
  enum iwl_scan_flags {
        SCAN_FLAGS_PERIODIC_SCAN                = BIT(0),
        SCAN_FLAGS_DELAYED_SCAN_LOWBAND         = BIT(2),
        SCAN_FLAGS_DELAYED_SCAN_HIGHBAND        = BIT(3),
        SCAN_FLAGS_FRAGMENTED_SCAN              = BIT(4),
 +      SCAN_FLAGS_PASSIVE2ACTIVE               = BIT(5),
  };
  
  /**
@@@ -182,7 -178,7 +181,7 @@@ enum iwl_scan_type 
   * @quiet_time: in msecs, dwell this time for active scan on quiet channels
   * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
   *    this number of packets were received (typically 1)
 - * @passive2active: is auto switching from passive to active allowed (0 or 1)
 + * @passive2active: is auto switching from passive to active during scan allowed
   * @rxchain_sel_flags: RXON_RX_CHAIN_*
   * @max_out_time: in usecs, max out of serving channel time
   * @suspend_time: how long to pause scan when returning to service channel:
@@@ -153,9 -153,7 +153,9 @@@ int iwl_mvm_mac_setup_register(struct i
                    IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
 -                  IEEE80211_HW_CONNECTION_MONITOR;
 +                  IEEE80211_HW_CONNECTION_MONITOR |
 +                  IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
 +                  IEEE80211_HW_SUPPORTS_STATIC_SMPS;
  
        hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
        hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@@@ -514,6 -512,27 +514,27 @@@ static int iwl_mvm_mac_add_interface(st
                goto out_unlock;
  
        /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * If new interface added, disable PM on existing interface.
+        * P2P device is a special case, since it is handled by FW similary to
+        * scan. If P2P deviced is added, PM remains enabled on existing
+        * interface.
+        * Note: the method below does not count the new interface being added
+        * at this moment.
+        */
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+               mvm->vif_count++;
+       if (mvm->vif_count > 1) {
+               IWL_DEBUG_MAC80211(mvm,
+                                  "Disable power on existing interfaces\n");
+               ieee80211_iterate_active_interfaces_atomic(
+                                           mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_pm_disable_iterator, mvm);
+       }
+       /*
         * The AP binding flow can be done only after the beacon
         * template is configured (which happens only in the mac80211
         * start_ap() flow), and adding the broadcast station can happen
                        goto out_release;
                }
  
 +              iwl_mvm_vif_dbgfs_register(mvm, vif);
                goto out_unlock;
        }
  
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * If new interface added, disable PM on existing interface.
-        * P2P device is a special case, since it is handled by FW similary to
-        * scan. If P2P deviced is added, PM remains enabled on existing
-        * interface.
-        * Note: the method below does not count the new interface being added
-        * at this moment.
-        */
-       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
-               mvm->vif_count++;
-       if (mvm->vif_count > 1) {
-               IWL_DEBUG_MAC80211(mvm,
-                                  "Disable power on existing interfaces\n");
-               ieee80211_iterate_active_interfaces_atomic(
-                                           mvm->hw,
-                                           IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_pm_disable_iterator, mvm);
-       }
        ret = iwl_mvm_mac_ctxt_add(mvm, vif);
        if (ret)
                goto out_release;
  
        /* beacon filtering */
        if (!mvm->bf_allowed_vif &&
 -          vif->type == NL80211_IFTYPE_STATION && !vif->p2p){
 +          vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
 +          mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
        }
@@@ -723,20 -719,6 +723,20 @@@ out_release
        mutex_unlock(&mvm->mutex);
  }
  
 +static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 +                              s8 tx_power)
 +{
 +      /* FW is in charge of regulatory enforcement */
 +      struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
 +              .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
 +              .pwr_restriction = cpu_to_le16(tx_power),
 +      };
 +
 +      return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
 +                                  sizeof(reduce_txpwr_cmd),
 +                                  &reduce_txpwr_cmd);
 +}
 +
  static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
  {
        return 0;
@@@ -784,6 -766,7 +784,6 @@@ static void iwl_mvm_bss_info_changed_st
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
                        }
 -                      iwl_mvm_bt_coex_vif_assoc(mvm, vif);
                        iwl_mvm_configure_mcast_filter(mvm, vif);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                        /* remove AP station now that the MAC is unassoc */
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
 -              ret = iwl_mvm_power_update_mode(mvm, vif);
 -              if (ret)
 -                      IWL_ERR(mvm, "failed to update power mode\n");
 +              if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) {
 +                      /* Workaround for FW bug, otherwise FW disables device
 +                       * power save upon disassociation
 +                       */
 +                      ret = iwl_mvm_power_update_mode(mvm, vif);
 +                      if (ret)
 +                              IWL_ERR(mvm, "failed to update power mode\n");
 +              }
 +              iwl_mvm_bt_coex_vif_assoc(mvm, vif);
        } else if (changes & BSS_CHANGED_BEACON_INFO) {
                /*
                 * We received a beacon _after_ association so
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
 +      if (changes & BSS_CHANGED_TXPOWER) {
 +              IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
 +                              bss_conf->txpower);
 +              iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
 +      }
  }
  
  static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
@@@ -178,19 -178,12 +178,12 @@@ static void iwl_mvm_scan_fill_channels(
        struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
                (cmd->data + le16_to_cpu(cmd->tx_cmd.len));
        int i;
-       __le32 chan_type_value;
-       if (req->n_ssids > 0)
-               chan_type_value = cpu_to_le32(BIT(req->n_ssids) - 1);
-       else
-               chan_type_value = SCAN_CHANNEL_TYPE_PASSIVE;
  
        for (i = 0; i < cmd->channel_count; i++) {
                chan->channel = cpu_to_le16(req->channels[i]->hw_value);
+               chan->type = cpu_to_le32(BIT(req->n_ssids) - 1);
                if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN)
-                       chan->type = SCAN_CHANNEL_TYPE_PASSIVE;
-               else
-                       chan->type = chan_type_value;
+                       chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE);
                chan->active_dwell = cpu_to_le16(active_dwell);
                chan->passive_dwell = cpu_to_le16(passive_dwell);
                chan->iteration_count = cpu_to_le16(1);
@@@ -308,12 -301,10 +301,12 @@@ int iwl_mvm_scan_request(struct iwl_mv
         */
        if (req->n_ssids > 0) {
                cmd->passive2active = cpu_to_le16(1);
 +              cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE;
                ssid = req->ssids[0].ssid;
                ssid_len = req->ssids[0].ssid_len;
        } else {
                cmd->passive2active = 0;
 +              cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
  
        iwl_mvm_scan_fill_ssids(cmd, req);
@@@ -826,7 -826,8 +826,7 @@@ int iwl_mvm_sta_tx_agg_oper(struct iwl_
                 * method for HT traffic
                 * this function also sends the LQ command
                 */
 -              return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
 -                                           mvmsta, true);
 +              return iwl_mvm_tx_protection(mvm, mvmsta, true);
                /*
                 * TODO: remove the TLC_RTS flag when we tear down the last
                 * AGG session (agg_tids_count in DVM)
@@@ -914,6 -915,7 +914,7 @@@ int iwl_mvm_sta_tx_agg_flush(struct iwl
        struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
        struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
        u16 txq_id;
+       enum iwl_mvm_agg_state old_state;
  
        /*
         * First set the agg state to OFF to avoid calling
        txq_id = tid_data->txq_id;
        IWL_DEBUG_TX_QUEUES(mvm, "Flush AGG: sta %d tid %d q %d state %d\n",
                            mvmsta->sta_id, tid, txq_id, tid_data->state);
+       old_state = tid_data->state;
        tid_data->state = IWL_AGG_OFF;
        spin_unlock_bh(&mvmsta->lock);
  
-       if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
-               IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+       if (old_state >= IWL_AGG_ON) {
+               if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), true))
+                       IWL_ERR(mvm, "Couldn't flush the AGG queue\n");
+               iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+       }
  
-       iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
        mvm->queue_to_mac80211[tid_data->txq_id] =
                                IWL_INVALID_MAC80211_QUEUE;
  
@@@ -130,6 -130,7 +130,7 @@@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_c
        {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */
        {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */
        {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */
+       {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */
  
        {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */
        {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */
        {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
 -      {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)},
 -      {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)},
 -      {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)},
 +      {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
 +      {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
 +      {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
        {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
        {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
@@@ -793,7 -793,7 +793,7 @@@ static void iwl_pcie_irq_handle_error(s
        }
  
        iwl_pcie_dump_csr(trans);
 -      iwl_pcie_dump_fh(trans, NULL);
 +      iwl_dump_fh(trans, NULL);
  
        set_bit(STATUS_FW_ERROR, &trans_pcie->status);
        clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
@@@ -888,6 -888,14 +888,14 @@@ irqreturn_t iwl_pcie_irq_handler(int ir
  
                iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
                if (hw_rfkill) {
+                       /*
+                        * Clear the interrupt in APMG if the NIC is going down.
+                        * Note that when the NIC exits RFkill (else branch), we
+                        * can't access prph and the NIC will be reset in
+                        * start_hw anyway.
+                        */
+                       iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
+                                      APMG_RTC_INT_STT_RFKILL);
                        set_bit(STATUS_RFKILL, &trans_pcie->status);
                        if (test_and_clear_bit(STATUS_HCMD_ACTIVE,
                                               &trans_pcie->status))
@@@ -670,6 -670,11 +670,11 @@@ static int iwl_trans_pcie_start_hw(stru
                return err;
        }
  
+       /* Reset the entire device */
+       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+       usleep_range(10, 15);
        iwl_pcie_apm_init(trans);
  
        /* From now on, the op_mode will be kept updated about RF kill state */
@@@ -1033,6 -1038,71 +1038,6 @@@ static void iwl_trans_pcie_set_bits_mas
        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
  }
  
 -static const char *get_fh_string(int cmd)
 -{
 -#define IWL_CMD(x) case x: return #x
 -      switch (cmd) {
 -      IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
 -      IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
 -      IWL_CMD(FH_RSCSR_CHNL0_WPTR);
 -      IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
 -      IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
 -      IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
 -      IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
 -      IWL_CMD(FH_TSSR_TX_STATUS_REG);
 -      IWL_CMD(FH_TSSR_TX_ERROR_REG);
 -      default:
 -              return "UNKNOWN";
 -      }
 -#undef IWL_CMD
 -}
 -
 -int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf)
 -{
 -      int i;
 -      static const u32 fh_tbl[] = {
 -              FH_RSCSR_CHNL0_STTS_WPTR_REG,
 -              FH_RSCSR_CHNL0_RBDCB_BASE_REG,
 -              FH_RSCSR_CHNL0_WPTR,
 -              FH_MEM_RCSR_CHNL0_CONFIG_REG,
 -              FH_MEM_RSSR_SHARED_CTRL_REG,
 -              FH_MEM_RSSR_RX_STATUS_REG,
 -              FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
 -              FH_TSSR_TX_STATUS_REG,
 -              FH_TSSR_TX_ERROR_REG
 -      };
 -
 -#ifdef CONFIG_IWLWIFI_DEBUGFS
 -      if (buf) {
 -              int pos = 0;
 -              size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
 -
 -              *buf = kmalloc(bufsz, GFP_KERNEL);
 -              if (!*buf)
 -                      return -ENOMEM;
 -
 -              pos += scnprintf(*buf + pos, bufsz - pos,
 -                              "FH register values:\n");
 -
 -              for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
 -                      pos += scnprintf(*buf + pos, bufsz - pos,
 -                              "  %34s: 0X%08x\n",
 -                              get_fh_string(fh_tbl[i]),
 -                              iwl_read_direct32(trans, fh_tbl[i]));
 -
 -              return pos;
 -      }
 -#endif
 -
 -      IWL_ERR(trans, "FH register values:\n");
 -      for (i = 0; i <  ARRAY_SIZE(fh_tbl); i++)
 -              IWL_ERR(trans, "  %34s: 0X%08x\n",
 -                      get_fh_string(fh_tbl[i]),
 -                      iwl_read_direct32(trans, fh_tbl[i]));
 -
 -      return 0;
 -}
 -
  static const char *get_csr_string(int cmd)
  {
  #define IWL_CMD(x) case x: return #x
@@@ -1113,7 -1183,18 +1118,7 @@@ void iwl_pcie_dump_csr(struct iwl_tran
  } while (0)
  
  /* file operation */
 -#define DEBUGFS_READ_FUNC(name)                                         \
 -static ssize_t iwl_dbgfs_##name##_read(struct file *file,               \
 -                                      char __user *user_buf,          \
 -                                      size_t count, loff_t *ppos);
 -
 -#define DEBUGFS_WRITE_FUNC(name)                                        \
 -static ssize_t iwl_dbgfs_##name##_write(struct file *file,              \
 -                                      const char __user *user_buf,    \
 -                                      size_t count, loff_t *ppos);
 -
  #define DEBUGFS_READ_FILE_OPS(name)                                   \
 -      DEBUGFS_READ_FUNC(name);                                        \
  static const struct file_operations iwl_dbgfs_##name##_ops = {                \
        .read = iwl_dbgfs_##name##_read,                                \
        .open = simple_open,                                            \
  };
  
  #define DEBUGFS_WRITE_FILE_OPS(name)                                    \
 -      DEBUGFS_WRITE_FUNC(name);                                       \
  static const struct file_operations iwl_dbgfs_##name##_ops = {          \
        .write = iwl_dbgfs_##name##_write,                              \
        .open = simple_open,                                            \
  };
  
  #define DEBUGFS_READ_WRITE_FILE_OPS(name)                             \
 -      DEBUGFS_READ_FUNC(name);                                        \
 -      DEBUGFS_WRITE_FUNC(name);                                       \
  static const struct file_operations iwl_dbgfs_##name##_ops = {                \
        .write = iwl_dbgfs_##name##_write,                              \
        .read = iwl_dbgfs_##name##_read,                                \
@@@ -1311,7 -1395,7 +1316,7 @@@ static ssize_t iwl_dbgfs_fh_reg_read(st
        int pos = 0;
        ssize_t ret = -EFAULT;
  
 -      ret = pos = iwl_pcie_dump_fh(trans, &buf);
 +      ret = pos = iwl_dump_fh(trans, &buf);
        if (buf) {
                ret = simple_read_from_buffer(user_buf,
                                              count, ppos, buf, pos);
@@@ -1418,16 -1502,10 +1423,16 @@@ struct iwl_trans *iwl_trans_pcie_alloc(
        spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
  
 -      /* W/A - seems to solve weird behavior. We need to remove this if we
 -       * don't want to stay in L1 all the time. This wastes a lot of power */
 -      pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
 -                             PCIE_LINK_STATE_CLKPM);
 +      if (!cfg->base_params->pcie_l1_allowed) {
 +              /*
 +               * W/A - seems to solve weird behavior. We need to remove this
 +               * if we don't want to stay in L1 all the time. This wastes a
 +               * lot of power.
 +               */
 +              pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
 +                                     PCIE_LINK_STATE_L1 |
 +                                     PCIE_LINK_STATE_CLKPM);
 +      }
  
        if (pci_enable_device(pdev)) {
                err = -ENODEV;
@@@ -25,9 -25,7 +25,9 @@@ module_param(reg_alpha2, charp, 0)
  
  static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
        {
 -              .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
 +              .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
 +                                 BIT(NL80211_IFTYPE_P2P_GO) |
 +                                 BIT(NL80211_IFTYPE_P2P_CLIENT),
        },
        {
                .max = 1, .types = BIT(NL80211_IFTYPE_AP),
@@@ -191,7 -189,6 +191,7 @@@ mwifiex_cfg80211_mgmt_tx(struct wiphy *
        struct sk_buff *skb;
        u16 pkt_len;
        const struct ieee80211_mgmt *mgmt;
 +      struct mwifiex_txinfo *tx_info;
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
  
        if (!buf || !len) {
                return -ENOMEM;
        }
  
 +      tx_info = MWIFIEX_SKB_TXCB(skb);
 +      tx_info->bss_num = priv->bss_num;
 +      tx_info->bss_type = priv->bss_type;
 +
        mwifiex_form_mgmt_frame(skb, buf, len);
        mwifiex_queue_tx_pkt(priv, skb);
  
@@@ -242,20 -235,16 +242,20 @@@ mwifiex_cfg80211_mgmt_frame_register(st
                                     u16 frame_type, bool reg)
  {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
 +      u32 mask;
  
        if (reg)
 -              priv->mgmt_frame_mask |= BIT(frame_type >> 4);
 +              mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
        else
 -              priv->mgmt_frame_mask &= ~BIT(frame_type >> 4);
 -
 -      mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
 -                             HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask);
 +              mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
  
 -      wiphy_dbg(wiphy, "info: mgmt frame registered\n");
 +      if (mask != priv->mgmt_frame_mask) {
 +              priv->mgmt_frame_mask = mask;
 +              mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
 +                                     HostCmd_ACT_GEN_SET, 0,
 +                                     &priv->mgmt_frame_mask);
 +              wiphy_dbg(wiphy, "info: mgmt frame registered\n");
 +      }
  }
  
  /*
@@@ -1727,9 -1716,9 +1727,9 @@@ mwifiex_cfg80211_connect(struct wiphy *
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        int ret;
  
-       if (priv->bss_mode != NL80211_IFTYPE_STATION) {
+       if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) {
                wiphy_err(wiphy,
-                         "%s: reject infra assoc request in non-STA mode\n",
+                         "%s: reject infra assoc request in non-STA role\n",
                          dev->name);
                return -EINVAL;
        }
@@@ -2309,7 -2298,8 +2309,7 @@@ EXPORT_SYMBOL_GPL(mwifiex_del_virtual_i
  
  #ifdef CONFIG_PM
  static bool
 -mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
 -                           s8 *byte_seq)
 +mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq)
  {
        int j, k, valid_byte_cnt = 0;
        bool dont_care_byte = false;
@@@ -404,50 -404,19 +404,51 @@@ mwifiex_is_rate_auto(struct mwifiex_pri
                return false;
  }
  
 -/*
 - * This function gets the supported data rates.
 - *
 - * The function works in both Ad-Hoc and infra mode by printing the
 - * band and returning the data rates.
 +/* This function gets the supported data rates from bitmask inside
 + * cfg80211_scan_request.
 + */
 +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
 +                                  u8 *rates, u8 radio_type)
 +{
 +      struct wiphy *wiphy = priv->adapter->wiphy;
 +      struct cfg80211_scan_request *request = priv->scan_request;
 +      u32 num_rates, rate_mask;
 +      struct ieee80211_supported_band *sband;
 +      int i;
 +
 +      if (radio_type) {
 +              sband = wiphy->bands[IEEE80211_BAND_5GHZ];
 +              if (WARN_ON_ONCE(!sband))
 +                      return 0;
 +              rate_mask = request->rates[IEEE80211_BAND_5GHZ];
 +      } else {
 +              sband = wiphy->bands[IEEE80211_BAND_2GHZ];
 +              if (WARN_ON_ONCE(!sband))
 +                      return 0;
 +              rate_mask = request->rates[IEEE80211_BAND_2GHZ];
 +      }
 +
 +      num_rates = 0;
 +      for (i = 0; i < sband->n_bitrates; i++) {
 +              if ((BIT(i) & rate_mask) == 0)
 +                      continue; /* skip rate */
 +              rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
 +      }
 +
 +      return num_rates;
 +}
 +
 +/* This function gets the supported data rates. The function works in
 + * both Ad-Hoc and infra mode by printing the band and returning the
 + * data rates.
   */
  u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
  {
        u32 k = 0;
        struct mwifiex_adapter *adapter = priv->adapter;
  
-       if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+       if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+           priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
                switch (adapter->config_bands) {
                case BAND_B:
                        dev_dbg(adapter->dev, "info: infra band=%d "
@@@ -1291,8 -1291,10 +1291,10 @@@ int mwifiex_associate(struct mwifiex_pr
  {
        u8 current_bssid[ETH_ALEN];
  
-       /* Return error if the adapter or table entry is not marked as infra */
-       if ((priv->bss_mode != NL80211_IFTYPE_STATION) ||
+       /* Return error if the adapter is not STA role or table entry
+        * is not marked as infra.
+        */
+       if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) ||
            (bss_desc->bss_mode != NL80211_IFTYPE_STATION))
                return -1;
  
@@@ -1425,7 -1427,6 +1427,7 @@@ int mwifiex_deauthenticate(struct mwifi
  
        switch (priv->bss_mode) {
        case NL80211_IFTYPE_STATION:
 +      case NL80211_IFTYPE_P2P_CLIENT:
                return mwifiex_deauthenticate_infra(priv, mac);
        case NL80211_IFTYPE_ADHOC:
                return mwifiex_send_cmd_sync(priv,
@@@ -50,6 -50,9 +50,6 @@@ static struct mwifiex_if_ops sdio_ops
  
  static struct semaphore add_remove_card_sem;
  
 -static int mwifiex_sdio_resume(struct device *dev);
 -static void mwifiex_sdio_interrupt(struct sdio_func *func);
 -
  /*
   * SDIO probe.
   *
@@@ -110,51 -113,6 +110,51 @@@ mwifiex_sdio_probe(struct sdio_func *fu
  }
  
  /*
 + * SDIO resume.
 + *
 + * Kernel needs to suspend all functions separately. Therefore all
 + * registered functions must have drivers with suspend and resume
 + * methods. Failing that the kernel simply removes the whole card.
 + *
 + * If already not resumed, this function turns on the traffic and
 + * sends a host sleep cancel request to the firmware.
 + */
 +static int mwifiex_sdio_resume(struct device *dev)
 +{
 +      struct sdio_func *func = dev_to_sdio_func(dev);
 +      struct sdio_mmc_card *card;
 +      struct mwifiex_adapter *adapter;
 +      mmc_pm_flag_t pm_flag = 0;
 +
 +      if (func) {
 +              pm_flag = sdio_get_host_pm_caps(func);
 +              card = sdio_get_drvdata(func);
 +              if (!card || !card->adapter) {
 +                      pr_err("resume: invalid card or adapter\n");
 +                      return 0;
 +              }
 +      } else {
 +              pr_err("resume: sdio_func is not specified\n");
 +              return 0;
 +      }
 +
 +      adapter = card->adapter;
 +
 +      if (!adapter->is_suspended) {
 +              dev_warn(adapter->dev, "device already resumed\n");
 +              return 0;
 +      }
 +
 +      adapter->is_suspended = false;
 +
 +      /* Disable Host Sleep */
 +      mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
 +                        MWIFIEX_ASYNC_CMD);
 +
 +      return 0;
 +}
 +
 +/*
   * SDIO remove.
   *
   * This function removes the interface and frees up the card structure.
@@@ -254,6 -212,51 +254,6 @@@ static int mwifiex_sdio_suspend(struct 
        return ret;
  }
  
 -/*
 - * SDIO resume.
 - *
 - * Kernel needs to suspend all functions separately. Therefore all
 - * registered functions must have drivers with suspend and resume
 - * methods. Failing that the kernel simply removes the whole card.
 - *
 - * If already not resumed, this function turns on the traffic and
 - * sends a host sleep cancel request to the firmware.
 - */
 -static int mwifiex_sdio_resume(struct device *dev)
 -{
 -      struct sdio_func *func = dev_to_sdio_func(dev);
 -      struct sdio_mmc_card *card;
 -      struct mwifiex_adapter *adapter;
 -      mmc_pm_flag_t pm_flag = 0;
 -
 -      if (func) {
 -              pm_flag = sdio_get_host_pm_caps(func);
 -              card = sdio_get_drvdata(func);
 -              if (!card || !card->adapter) {
 -                      pr_err("resume: invalid card or adapter\n");
 -                      return 0;
 -              }
 -      } else {
 -              pr_err("resume: sdio_func is not specified\n");
 -              return 0;
 -      }
 -
 -      adapter = card->adapter;
 -
 -      if (!adapter->is_suspended) {
 -              dev_warn(adapter->dev, "device already resumed\n");
 -              return 0;
 -      }
 -
 -      adapter->is_suspended = false;
 -
 -      /* Disable Host Sleep */
 -      mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
 -                        MWIFIEX_ASYNC_CMD);
 -
 -      return 0;
 -}
 -
  /* Device ID for SD8786 */
  #define SDIO_DEVICE_ID_MARVELL_8786   (0x9116)
  /* Device ID for SD8787 */
@@@ -704,65 -707,6 +704,65 @@@ static void mwifiex_sdio_disable_host_i
  }
  
  /*
 + * This function reads the interrupt status from card.
 + */
 +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
 +{
 +      struct sdio_mmc_card *card = adapter->card;
 +      u8 sdio_ireg;
 +      unsigned long flags;
 +
 +      if (mwifiex_read_data_sync(adapter, card->mp_regs,
 +                                 card->reg->max_mp_regs,
 +                                 REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
 +              dev_err(adapter->dev, "read mp_regs failed\n");
 +              return;
 +      }
 +
 +      sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
 +      if (sdio_ireg) {
 +              /*
 +               * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
 +               * For SDIO new mode CMD port interrupts
 +               *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
 +               *      UP_LD_CMD_PORT_HOST_INT_STATUS
 +               * Clear the interrupt status register
 +               */
 +              dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
 +              spin_lock_irqsave(&adapter->int_lock, flags);
 +              adapter->int_status |= sdio_ireg;
 +              spin_unlock_irqrestore(&adapter->int_lock, flags);
 +      }
 +}
 +
 +/*
 + * SDIO interrupt handler.
 + *
 + * This function reads the interrupt status from firmware and handles
 + * the interrupt in current thread (ksdioirqd) right away.
 + */
 +static void
 +mwifiex_sdio_interrupt(struct sdio_func *func)
 +{
 +      struct mwifiex_adapter *adapter;
 +      struct sdio_mmc_card *card;
 +
 +      card = sdio_get_drvdata(func);
 +      if (!card || !card->adapter) {
 +              pr_debug("int: func=%p card=%p adapter=%p\n",
 +                       func, card, card ? card->adapter : NULL);
 +              return;
 +      }
 +      adapter = card->adapter;
 +
 +      if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
 +              adapter->ps_state = PS_STATE_AWAKE;
 +
 +      mwifiex_interrupt_status(adapter);
 +      mwifiex_main_process(adapter);
 +}
 +
 +/*
   * This function enables the host interrupt.
   *
   * The host interrupt enable mask is written to the card
@@@ -1000,7 -944,7 +1000,7 @@@ static int mwifiex_check_fw_status(stru
                        ret = 0;
                        break;
                } else {
 -                      mdelay(100);
 +                      msleep(100);
                        ret = -1;
                }
        }
  }
  
  /*
 - * This function reads the interrupt status from card.
 - */
 -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
 -{
 -      struct sdio_mmc_card *card = adapter->card;
 -      u8 sdio_ireg;
 -      unsigned long flags;
 -
 -      if (mwifiex_read_data_sync(adapter, card->mp_regs,
 -                                 card->reg->max_mp_regs,
 -                                 REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
 -              dev_err(adapter->dev, "read mp_regs failed\n");
 -              return;
 -      }
 -
 -      sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
 -      if (sdio_ireg) {
 -              /*
 -               * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
 -               * For SDIO new mode CMD port interrupts
 -               *      DN_LD_CMD_PORT_HOST_INT_STATUS and/or
 -               *      UP_LD_CMD_PORT_HOST_INT_STATUS
 -               * Clear the interrupt status register
 -               */
 -              dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
 -              spin_lock_irqsave(&adapter->int_lock, flags);
 -              adapter->int_status |= sdio_ireg;
 -              spin_unlock_irqrestore(&adapter->int_lock, flags);
 -      }
 -}
 -
 -/*
 - * SDIO interrupt handler.
 - *
 - * This function reads the interrupt status from firmware and handles
 - * the interrupt in current thread (ksdioirqd) right away.
 - */
 -static void
 -mwifiex_sdio_interrupt(struct sdio_func *func)
 -{
 -      struct mwifiex_adapter *adapter;
 -      struct sdio_mmc_card *card;
 -
 -      card = sdio_get_drvdata(func);
 -      if (!card || !card->adapter) {
 -              pr_debug("int: func=%p card=%p adapter=%p\n",
 -                       func, card, card ? card->adapter : NULL);
 -              return;
 -      }
 -      adapter = card->adapter;
 -
 -      if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
 -              adapter->ps_state = PS_STATE_AWAKE;
 -
 -      mwifiex_interrupt_status(adapter);
 -      mwifiex_main_process(adapter);
 -}
 -
 -/*
   * This function decodes a received packet.
   *
   * Based on the type, the packet is treated as either a data, or
@@@ -1636,8 -1639,8 +1636,8 @@@ static int mwifiex_sdio_host_to_card(st
        /* Allocate buffer and copy payload */
        blk_size = MWIFIEX_SDIO_BLOCK_SIZE;
        buf_block_len = (pkt_len + blk_size - 1) / blk_size;
-       *(u16 *) &payload[0] = (u16) pkt_len;
-       *(u16 *) &payload[2] = type;
+       *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len);
+       *(__le16 *)&payload[2] = cpu_to_le16(type);
  
        /*
         * This is SDIO specific header
@@@ -257,10 -257,10 +257,10 @@@ int mwifiex_bss_start(struct mwifiex_pr
                        goto done;
        }
  
-       if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+       if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+           priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
                u8 config_bands;
  
-               /* Infra mode */
                ret = mwifiex_deauthenticate(priv, NULL);
                if (ret)
                        goto done;
@@@ -797,16 -797,15 +797,16 @@@ static int mwifiex_set_wps_ie(struct mw
                               u8 *ie_data_ptr, u16 ie_len)
  {
        if (ie_len) {
 -              priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
 -              if (!priv->wps_ie)
 -                      return -ENOMEM;
 -              if (ie_len > sizeof(priv->wps_ie)) {
 +              if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
                        dev_dbg(priv->adapter->dev,
                                "info: failed to copy WPS IE, too big\n");
 -                      kfree(priv->wps_ie);
                        return -1;
                }
 +
 +              priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
 +              if (!priv->wps_ie)
 +                      return -ENOMEM;
 +
                memcpy(priv->wps_ie, ie_data_ptr, ie_len);
                priv->wps_ie_len = ie_len;
                dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n",
diff --combined net/mac80211/mlme.c
  #include "led.h"
  
  #define IEEE80211_AUTH_TIMEOUT                (HZ / 5)
+ #define IEEE80211_AUTH_TIMEOUT_LONG   (HZ / 2)
  #define IEEE80211_AUTH_TIMEOUT_SHORT  (HZ / 10)
  #define IEEE80211_AUTH_MAX_TRIES      3
  #define IEEE80211_AUTH_WAIT_ASSOC     (HZ * 5)
  #define IEEE80211_ASSOC_TIMEOUT               (HZ / 5)
+ #define IEEE80211_ASSOC_TIMEOUT_LONG  (HZ / 2)
  #define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10)
  #define IEEE80211_ASSOC_MAX_TRIES     3
  
@@@ -209,8 -211,9 +211,9 @@@ ieee80211_determine_chantype(struct iee
                             struct ieee80211_channel *channel,
                             const struct ieee80211_ht_operation *ht_oper,
                             const struct ieee80211_vht_operation *vht_oper,
-                            struct cfg80211_chan_def *chandef, bool verbose)
+                            struct cfg80211_chan_def *chandef, bool tracking)
  {
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct cfg80211_chan_def vht_chandef;
        u32 ht_cfreq, ret;
  
        ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
                                                  channel->band);
        /* check that channel matches the right operating channel */
-       if (channel->center_freq != ht_cfreq) {
+       if (!tracking && channel->center_freq != ht_cfreq) {
                /*
                 * It's possible that some APs are confused here;
                 * Netgear WNDR3700 sometimes reports 4 higher than
                 * since we look at probe response/beacon data here
                 * it should be OK.
                 */
-               if (verbose)
-                       sdata_info(sdata,
-                                  "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
-                                  channel->center_freq, ht_cfreq,
-                                  ht_oper->primary_chan, channel->band);
+               sdata_info(sdata,
+                          "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
+                          channel->center_freq, ht_cfreq,
+                          ht_oper->primary_chan, channel->band);
                ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
                goto out;
        }
                                channel->band);
                break;
        default:
-               if (verbose)
+               if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                        sdata_info(sdata,
                                   "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
                                   vht_oper->chan_width);
        }
  
        if (!cfg80211_chandef_valid(&vht_chandef)) {
-               if (verbose)
+               if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                        sdata_info(sdata,
                                   "AP VHT information is invalid, disable VHT\n");
                ret = IEEE80211_STA_DISABLE_VHT;
        }
  
        if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
-               if (verbose)
+               if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
                        sdata_info(sdata,
                                   "AP VHT information doesn't match HT, disable VHT\n");
                ret = IEEE80211_STA_DISABLE_VHT;
@@@ -333,18 -335,27 +335,27 @@@ out
        if (ret & IEEE80211_STA_DISABLE_VHT)
                vht_chandef = *chandef;
  
+       /*
+        * Ignore the DISABLED flag when we're already connected and only
+        * tracking the APs beacon for bandwidth changes - otherwise we
+        * might get disconnected here if we connect to an AP, update our
+        * regulatory information based on the AP's country IE and the
+        * information we have is wrong/outdated and disables the channel
+        * that we're actually using for the connection to the AP.
+        */
        while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
-                                       IEEE80211_CHAN_DISABLED)) {
+                                       tracking ? 0 :
+                                                  IEEE80211_CHAN_DISABLED)) {
                if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
                        ret = IEEE80211_STA_DISABLE_HT |
                              IEEE80211_STA_DISABLE_VHT;
-                       goto out;
+                       break;
                }
  
                ret |= chandef_downgrade(chandef);
        }
  
-       if (chandef->width != vht_chandef.width && verbose)
+       if (chandef->width != vht_chandef.width && !tracking)
                sdata_info(sdata,
                           "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n");
  
@@@ -384,7 -395,7 +395,7 @@@ static int ieee80211_config_bw(struct i
  
        /* calculate new channel (type) based on HT/VHT operation IEs */
        flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper,
-                                            vht_oper, &chandef, false);
+                                            vht_oper, &chandef, true);
  
        /*
         * Downgrade the new channel if we associated with restricted
  
  /* frame sending functions */
  
 -static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
 -                                    struct ieee80211_supported_band *sband,
 -                                    u32 *rates)
 -{
 -      int i, j, count;
 -      *rates = 0;
 -      count = 0;
 -      for (i = 0; i < supp_rates_len; i++) {
 -              int rate = (supp_rates[i] & 0x7F) * 5;
 -
 -              for (j = 0; j < sband->n_bitrates; j++)
 -                      if (sband->bitrates[j].bitrate == rate) {
 -                              *rates |= BIT(j);
 -                              count++;
 -                              break;
 -                      }
 -      }
 -
 -      return count;
 -}
 -
  static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, u8 ap_ht_param,
                                struct ieee80211_supported_band *sband,
@@@ -596,12 -628,12 +607,12 @@@ static void ieee80211_send_assoc(struc
        struct ieee80211_mgmt *mgmt;
        u8 *pos, qos_info;
        size_t offset = 0, noffset;
 -      int i, count, rates_len, supp_rates_len;
 +      int i, count, rates_len, supp_rates_len, shift;
        u16 capab;
        struct ieee80211_supported_band *sband;
        struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_channel *chan;
 -      u32 rates = 0;
 +      u32 rate_flags, rates = 0;
  
        sdata_assert_lock(sdata);
  
                return;
        }
        chan = chanctx_conf->def.chan;
 +      rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
        rcu_read_unlock();
        sband = local->hw.wiphy->bands[chan->band];
 +      shift = ieee80211_vif_get_shift(&sdata->vif);
  
        if (assoc_data->supp_rates_len) {
                /*
                 * in the association request (e.g. D-Link DAP 1353 in
                 * b-only mode)...
                 */
 -              rates_len = ieee80211_compatible_rates(assoc_data->supp_rates,
 -                                                     assoc_data->supp_rates_len,
 -                                                     sband, &rates);
 +              rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
 +                                                   assoc_data->supp_rates,
 +                                                   assoc_data->supp_rates_len,
 +                                                   &rates);
        } else {
                /*
                 * In case AP not provide any supported rates information
                 * before association, we send information element(s) with
                 * all rates that we support.
                 */
 -              rates = ~0;
 -              rates_len = sband->n_bitrates;
 +              rates_len = 0;
 +              for (i = 0; i < sband->n_bitrates; i++) {
 +                      if ((rate_flags & sband->bitrates[i].flags)
 +                          != rate_flags)
 +                              continue;
 +                      rates |= BIT(i);
 +                      rates_len++;
 +              }
        }
  
        skb = alloc_skb(local->hw.extra_tx_headroom +
        count = 0;
        for (i = 0; i < sband->n_bitrates; i++) {
                if (BIT(i) & rates) {
 -                      int rate = sband->bitrates[i].bitrate;
 -                      *pos++ = (u8) (rate / 5);
 +                      int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
 +                                              5 * (1 << shift));
 +                      *pos++ = (u8) rate;
                        if (++count == 8)
                                break;
                }
  
                for (i++; i < sband->n_bitrates; i++) {
                        if (BIT(i) & rates) {
 -                              int rate = sband->bitrates[i].bitrate;
 -                              *pos++ = (u8) (rate / 5);
 +                              int rate;
 +                              rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
 +                                                  5 * (1 << shift));
 +                              *pos++ = (u8) rate;
                        }
                }
        }
                *pos++ = WLAN_EID_PWR_CAPABILITY;
                *pos++ = 2;
                *pos++ = 0; /* min tx power */
 -              *pos++ = chan->max_power; /* max tx power */
 +               /* max tx power */
 +              *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
  
                /* 2. supported channels */
                /* TODO: get this in reg domain format */
@@@ -2424,16 -2443,15 +2435,16 @@@ static void ieee80211_get_rates(struct 
                                u8 *supp_rates, unsigned int supp_rates_len,
                                u32 *rates, u32 *basic_rates,
                                bool *have_higher_than_11mbit,
 -                              int *min_rate, int *min_rate_index)
 +                              int *min_rate, int *min_rate_index,
 +                              int shift, u32 rate_flags)
  {
        int i, j;
  
        for (i = 0; i < supp_rates_len; i++) {
 -              int rate = (supp_rates[i] & 0x7f) * 5;
 +              int rate = supp_rates[i] & 0x7f;
                bool is_basic = !!(supp_rates[i] & 0x80);
  
 -              if (rate > 110)
 +              if ((rate * 5 * (1 << shift)) > 110)
                        *have_higher_than_11mbit = true;
  
                /*
                        continue;
  
                for (j = 0; j < sband->n_bitrates; j++) {
 -                      if (sband->bitrates[j].bitrate == rate) {
 +                      struct ieee80211_rate *br;
 +                      int brate;
 +
 +                      br = &sband->bitrates[j];
 +                      if ((rate_flags & br->flags) != rate_flags)
 +                              continue;
 +
 +                      brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
 +                      if (brate == rate) {
                                *rates |= BIT(j);
                                if (is_basic)
                                        *basic_rates |= BIT(j);
 -                              if (rate < *min_rate) {
 -                                      *min_rate = rate;
 +                              if ((rate * 5) < *min_rate) {
 +                                      *min_rate = rate * 5;
                                        *min_rate_index = j;
                                }
                                break;
@@@ -3395,10 -3405,13 +3406,13 @@@ static int ieee80211_probe_auth(struct 
  
        if (tx_flags == 0) {
                auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-               ifmgd->auth_data->timeout_started = true;
+               auth_data->timeout_started = true;
                run_again(sdata, auth_data->timeout);
        } else {
-               auth_data->timeout_started = false;
+               auth_data->timeout =
+                       round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG);
+               auth_data->timeout_started = true;
+               run_again(sdata, auth_data->timeout);
        }
  
        return 0;
@@@ -3435,7 -3448,11 +3449,11 @@@ static int ieee80211_do_assoc(struct ie
                assoc_data->timeout_started = true;
                run_again(sdata, assoc_data->timeout);
        } else {
-               assoc_data->timeout_started = false;
+               assoc_data->timeout =
+                       round_jiffies_up(jiffies +
+                                        IEEE80211_ASSOC_TIMEOUT_LONG);
+               assoc_data->timeout_started = true;
+               run_again(sdata, assoc_data->timeout);
        }
  
        return 0;
@@@ -3830,7 -3847,7 +3848,7 @@@ static int ieee80211_prep_channel(struc
        ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
                                                     cbss->channel,
                                                     ht_oper, vht_oper,
-                                                    &chandef, true);
+                                                    &chandef, false);
  
        sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
                                      local->rx_chains);
@@@ -3885,40 -3902,27 +3903,40 @@@ static int ieee80211_prep_connection(st
                if (!new_sta)
                        return -ENOMEM;
        }
 -
        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_chanctx_conf *chanctx_conf;
                struct ieee80211_supported_band *sband;
                const struct cfg80211_bss_ies *ies;
 +              int shift;
 +              u32 rate_flags;
  
                sband = local->hw.wiphy->bands[cbss->channel->band];
  
                err = ieee80211_prep_channel(sdata, cbss);
                if (err) {
                        sta_info_free(local, new_sta);
 -                      return err;
 +                      return -EINVAL;
                }
 +              shift = ieee80211_vif_get_shift(&sdata->vif);
 +
 +              rcu_read_lock();
 +              chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 +              if (WARN_ON(!chanctx_conf)) {
 +                      rcu_read_unlock();
 +                      return -EINVAL;
 +              }
 +              rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
 +              rcu_read_unlock();
  
                ieee80211_get_rates(sband, bss->supp_rates,
                                    bss->supp_rates_len,
                                    &rates, &basic_rates,
                                    &have_higher_than_11mbit,
 -                                  &min_rate, &min_rate_index);
 +                                  &min_rate, &min_rate_index,
 +                                  shift, rate_flags);
  
                /*
                 * This used to be a workaround for basic rates missing
diff --combined net/wireless/core.c
@@@ -462,14 -462,6 +462,14 @@@ int wiphy_register(struct wiphy *wiphy
                return -EINVAL;
  #endif
  
 +      if (WARN_ON(wiphy->coalesce &&
 +                  (!wiphy->coalesce->n_rules ||
 +                   !wiphy->coalesce->n_patterns) &&
 +                  (!wiphy->coalesce->pattern_min_len ||
 +                   wiphy->coalesce->pattern_min_len >
 +                      wiphy->coalesce->pattern_max_len)))
 +              return -EINVAL;
 +
        if (WARN_ON(wiphy->ap_sme_capa &&
                    !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
                return -EINVAL;
@@@ -676,7 -668,6 +676,7 @@@ void wiphy_unregister(struct wiphy *wip
                rdev_set_wakeup(rdev, false);
  #endif
        cfg80211_rdev_free_wowlan(rdev);
 +      cfg80211_rdev_free_coalesce(rdev);
  }
  EXPORT_SYMBOL(wiphy_unregister);
  
@@@ -774,6 -765,7 +774,7 @@@ void cfg80211_leave(struct cfg80211_reg
                cfg80211_leave_mesh(rdev, dev);
                break;
        case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
                cfg80211_stop_ap(rdev, dev);
                break;
        default:
diff --combined net/wireless/nl80211.c
@@@ -403,14 -403,6 +403,14 @@@ nl80211_wowlan_tcp_policy[NUM_NL80211_W
        [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
  };
  
 +/* policy for coalesce rule attributes */
 +static const struct nla_policy
 +nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
 +      [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
 +      [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
 +      [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
 +};
 +
  /* policy for GTK rekey offload attributes */
  static const struct nla_policy
  nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
@@@ -449,10 -441,12 +449,12 @@@ static int nl80211_prepare_wdev_dump(st
                        goto out_unlock;
                }
                *rdev = wiphy_to_dev((*wdev)->wiphy);
-               cb->args[0] = (*rdev)->wiphy_idx;
+               /* 0 is the first index - add 1 to parse only once */
+               cb->args[0] = (*rdev)->wiphy_idx + 1;
                cb->args[1] = (*wdev)->identifier;
        } else {
-               struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0]);
+               /* subtract the 1 again here */
+               struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
                struct wireless_dev *tmp;
  
                if (!wiphy) {
@@@ -982,7 -976,7 +984,7 @@@ static int nl80211_send_wowlan(struct s
                return -ENOBUFS;
  
        if (dev->wiphy.wowlan->n_patterns) {
 -              struct nl80211_wowlan_pattern_support pat = {
 +              struct nl80211_pattern_support pat = {
                        .max_patterns = dev->wiphy.wowlan->n_patterns,
                        .min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
                        .max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
  }
  #endif
  
 +static int nl80211_send_coalesce(struct sk_buff *msg,
 +                               struct cfg80211_registered_device *dev)
 +{
 +      struct nl80211_coalesce_rule_support rule;
 +
 +      if (!dev->wiphy.coalesce)
 +              return 0;
 +
 +      rule.max_rules = dev->wiphy.coalesce->n_rules;
 +      rule.max_delay = dev->wiphy.coalesce->max_delay;
 +      rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
 +      rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
 +      rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
 +      rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
 +
 +      if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
 +              return -ENOBUFS;
 +
 +      return 0;
 +}
 +
  static int nl80211_send_band_rateinfo(struct sk_buff *msg,
                                      struct ieee80211_supported_band *sband)
  {
@@@ -1542,12 -1515,6 +1544,12 @@@ static int nl80211_send_wiphy(struct cf
                            dev->wiphy.vht_capa_mod_mask))
                        goto nla_put_failure;
  
 +              state->split_start++;
 +              break;
 +      case 10:
 +              if (nl80211_send_coalesce(msg, dev))
 +                      goto nla_put_failure;
 +
                /* done */
                state->split_start = 0;
                break;
@@@ -5674,7 -5641,6 +5676,7 @@@ static int nl80211_send_bss(struct sk_b
                goto nla_put_failure;
        if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
            nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
 +          nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
            nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
                        jiffies_to_msecs(jiffies - intbss->ts)))
                goto nla_put_failure;
@@@ -6355,8 -6321,6 +6357,8 @@@ static int nl80211_join_ibss(struct sk_
                return -EINVAL;
  
        switch (ibss.chandef.width) {
 +      case NL80211_CHAN_WIDTH_5:
 +      case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20_NOHT:
                break;
        case NL80211_CHAN_WIDTH_20:
                        return err;
        }
  
 +      if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
 +              memcpy(&ibss.ht_capa_mask,
 +                     nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
 +                     sizeof(ibss.ht_capa_mask));
 +
 +      if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
 +              if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
 +                      return -EINVAL;
 +              memcpy(&ibss.ht_capa,
 +                     nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
 +                     sizeof(ibss.ht_capa));
 +      }
 +
        if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
            !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
                        nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
@@@ -7644,11 -7595,12 +7646,11 @@@ static int nl80211_send_wowlan_patterns
                if (!nl_pat)
                        return -ENOBUFS;
                pat_len = wowlan->patterns[i].pattern_len;
 -              if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
 -                          DIV_ROUND_UP(pat_len, 8),
 +              if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
                            wowlan->patterns[i].mask) ||
 -                  nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
 -                          pat_len, wowlan->patterns[i].pattern) ||
 -                  nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
 +                  nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
 +                          wowlan->patterns[i].pattern) ||
 +                  nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
                                wowlan->patterns[i].pkt_offset))
                        return -ENOBUFS;
                nla_nest_end(msg, nl_pat);
@@@ -7989,7 -7941,7 +7991,7 @@@ static int nl80211_set_wowlan(struct sk
                struct nlattr *pat;
                int n_patterns = 0;
                int rem, pat_len, mask_len, pkt_offset;
 -              struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
 +              struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
  
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem)
  
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem) {
 -                      nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
 -                                nla_data(pat), nla_len(pat), NULL);
 +                      nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
 +                                nla_len(pat), NULL);
                        err = -EINVAL;
 -                      if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
 -                          !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
 +                      if (!pat_tb[NL80211_PKTPAT_MASK] ||
 +                          !pat_tb[NL80211_PKTPAT_PATTERN])
                                goto error;
 -                      pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
 +                      pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
                        mask_len = DIV_ROUND_UP(pat_len, 8);
 -                      if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
 -                          mask_len)
 +                      if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
                                goto error;
                        if (pat_len > wowlan->pattern_max_len ||
                            pat_len < wowlan->pattern_min_len)
                                goto error;
  
 -                      if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
 +                      if (!pat_tb[NL80211_PKTPAT_OFFSET])
                                pkt_offset = 0;
                        else
                                pkt_offset = nla_get_u32(
 -                                      pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
 +                                      pat_tb[NL80211_PKTPAT_OFFSET]);
                        if (pkt_offset > wowlan->max_pkt_offset)
                                goto error;
                        new_triggers.patterns[i].pkt_offset = pkt_offset;
                        new_triggers.patterns[i].pattern =
                                new_triggers.patterns[i].mask + mask_len;
                        memcpy(new_triggers.patterns[i].mask,
 -                             nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
 +                             nla_data(pat_tb[NL80211_PKTPAT_MASK]),
                               mask_len);
                        new_triggers.patterns[i].pattern_len = pat_len;
                        memcpy(new_triggers.patterns[i].pattern,
 -                             nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
 +                             nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
                               pat_len);
                        i++;
                }
  }
  #endif
  
 +static int nl80211_send_coalesce_rules(struct sk_buff *msg,
 +                                     struct cfg80211_registered_device *rdev)
 +{
 +      struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
 +      int i, j, pat_len;
 +      struct cfg80211_coalesce_rules *rule;
 +
 +      if (!rdev->coalesce->n_rules)
 +              return 0;
 +
 +      nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
 +      if (!nl_rules)
 +              return -ENOBUFS;
 +
 +      for (i = 0; i < rdev->coalesce->n_rules; i++) {
 +              nl_rule = nla_nest_start(msg, i + 1);
 +              if (!nl_rule)
 +                      return -ENOBUFS;
 +
 +              rule = &rdev->coalesce->rules[i];
 +              if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
 +                              rule->delay))
 +                      return -ENOBUFS;
 +
 +              if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
 +                              rule->condition))
 +                      return -ENOBUFS;
 +
 +              nl_pats = nla_nest_start(msg,
 +                              NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
 +              if (!nl_pats)
 +                      return -ENOBUFS;
 +
 +              for (j = 0; j < rule->n_patterns; j++) {
 +                      nl_pat = nla_nest_start(msg, j + 1);
 +                      if (!nl_pat)
 +                              return -ENOBUFS;
 +                      pat_len = rule->patterns[j].pattern_len;
 +                      if (nla_put(msg, NL80211_PKTPAT_MASK,
 +                                  DIV_ROUND_UP(pat_len, 8),
 +                                  rule->patterns[j].mask) ||
 +                          nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
 +                                  rule->patterns[j].pattern) ||
 +                          nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
 +                                      rule->patterns[j].pkt_offset))
 +                              return -ENOBUFS;
 +                      nla_nest_end(msg, nl_pat);
 +              }
 +              nla_nest_end(msg, nl_pats);
 +              nla_nest_end(msg, nl_rule);
 +      }
 +      nla_nest_end(msg, nl_rules);
 +
 +      return 0;
 +}
 +
 +static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct cfg80211_registered_device *rdev = info->user_ptr[0];
 +      struct sk_buff *msg;
 +      void *hdr;
 +
 +      if (!rdev->wiphy.coalesce)
 +              return -EOPNOTSUPP;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
 +                           NL80211_CMD_GET_COALESCE);
 +      if (!hdr)
 +              goto nla_put_failure;
 +
 +      if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
 +              goto nla_put_failure;
 +
 +      genlmsg_end(msg, hdr);
 +      return genlmsg_reply(msg, info);
 +
 +nla_put_failure:
 +      nlmsg_free(msg);
 +      return -ENOBUFS;
 +}
 +
 +void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
 +{
 +      struct cfg80211_coalesce *coalesce = rdev->coalesce;
 +      int i, j;
 +      struct cfg80211_coalesce_rules *rule;
 +
 +      if (!coalesce)
 +              return;
 +
 +      for (i = 0; i < coalesce->n_rules; i++) {
 +              rule = &coalesce->rules[i];
 +              for (j = 0; j < rule->n_patterns; j++)
 +                      kfree(rule->patterns[j].mask);
 +              kfree(rule->patterns);
 +      }
 +      kfree(coalesce->rules);
 +      kfree(coalesce);
 +      rdev->coalesce = NULL;
 +}
 +
 +static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
 +                                     struct nlattr *rule,
 +                                     struct cfg80211_coalesce_rules *new_rule)
 +{
 +      int err, i;
 +      const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
 +      struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
 +      int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
 +      struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 +
 +      err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
 +                      nla_len(rule), nl80211_coalesce_policy);
 +      if (err)
 +              return err;
 +
 +      if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
 +              new_rule->delay =
 +                      nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
 +      if (new_rule->delay > coalesce->max_delay)
 +              return -EINVAL;
 +
 +      if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
 +              new_rule->condition =
 +                      nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
 +      if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
 +          new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
 +              return -EINVAL;
 +
 +      if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
 +              return -EINVAL;
 +
 +      nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
 +                          rem)
 +              n_patterns++;
 +      if (n_patterns > coalesce->n_patterns)
 +              return -EINVAL;
 +
 +      new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
 +                                   GFP_KERNEL);
 +      if (!new_rule->patterns)
 +              return -ENOMEM;
 +
 +      new_rule->n_patterns = n_patterns;
 +      i = 0;
 +
 +      nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
 +                          rem) {
 +              nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
 +                        nla_len(pat), NULL);
 +              if (!pat_tb[NL80211_PKTPAT_MASK] ||
 +                  !pat_tb[NL80211_PKTPAT_PATTERN])
 +                      return -EINVAL;
 +              pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
 +              mask_len = DIV_ROUND_UP(pat_len, 8);
 +              if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
 +                      return -EINVAL;
 +              if (pat_len > coalesce->pattern_max_len ||
 +                  pat_len < coalesce->pattern_min_len)
 +                      return -EINVAL;
 +
 +              if (!pat_tb[NL80211_PKTPAT_OFFSET])
 +                      pkt_offset = 0;
 +              else
 +                      pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
 +              if (pkt_offset > coalesce->max_pkt_offset)
 +                      return -EINVAL;
 +              new_rule->patterns[i].pkt_offset = pkt_offset;
 +
 +              new_rule->patterns[i].mask =
 +                      kmalloc(mask_len + pat_len, GFP_KERNEL);
 +              if (!new_rule->patterns[i].mask)
 +                      return -ENOMEM;
 +              new_rule->patterns[i].pattern =
 +                      new_rule->patterns[i].mask + mask_len;
 +              memcpy(new_rule->patterns[i].mask,
 +                     nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
 +              new_rule->patterns[i].pattern_len = pat_len;
 +              memcpy(new_rule->patterns[i].pattern,
 +                     nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
 +              i++;
 +      }
 +
 +      return 0;
 +}
 +
 +static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct cfg80211_registered_device *rdev = info->user_ptr[0];
 +      const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
 +      struct cfg80211_coalesce new_coalesce = {};
 +      struct cfg80211_coalesce *n_coalesce;
 +      int err, rem_rule, n_rules = 0, i, j;
 +      struct nlattr *rule;
 +      struct cfg80211_coalesce_rules *tmp_rule;
 +
 +      if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
 +              return -EOPNOTSUPP;
 +
 +      if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
 +              cfg80211_rdev_free_coalesce(rdev);
 +              rdev->ops->set_coalesce(&rdev->wiphy, NULL);
 +              return 0;
 +      }
 +
 +      nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
 +                          rem_rule)
 +              n_rules++;
 +      if (n_rules > coalesce->n_rules)
 +              return -EINVAL;
 +
 +      new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
 +                                   GFP_KERNEL);
 +      if (!new_coalesce.rules)
 +              return -ENOMEM;
 +
 +      new_coalesce.n_rules = n_rules;
 +      i = 0;
 +
 +      nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
 +                          rem_rule) {
 +              err = nl80211_parse_coalesce_rule(rdev, rule,
 +                                                &new_coalesce.rules[i]);
 +              if (err)
 +                      goto error;
 +
 +              i++;
 +      }
 +
 +      err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
 +      if (err)
 +              goto error;
 +
 +      n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
 +      if (!n_coalesce) {
 +              err = -ENOMEM;
 +              goto error;
 +      }
 +      cfg80211_rdev_free_coalesce(rdev);
 +      rdev->coalesce = n_coalesce;
 +
 +      return 0;
 +error:
 +      for (i = 0; i < new_coalesce.n_rules; i++) {
 +              tmp_rule = &new_coalesce.rules[i];
 +              for (j = 0; j < tmp_rule->n_patterns; j++)
 +                      kfree(tmp_rule->patterns[j].mask);
 +              kfree(tmp_rule->patterns);
 +      }
 +      kfree(new_coalesce.rules);
 +
 +      return err;
 +}
 +
  static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
  {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@@ -9348,21 -9043,6 +9350,21 @@@ static struct genl_ops nl80211_ops[] = 
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
 +      },
 +      {
 +              .cmd = NL80211_CMD_GET_COALESCE,
 +              .doit = nl80211_get_coalesce,
 +              .policy = nl80211_policy,
 +              .internal_flags = NL80211_FLAG_NEED_WIPHY |
 +                                NL80211_FLAG_NEED_RTNL,
 +      },
 +      {
 +              .cmd = NL80211_CMD_SET_COALESCE,
 +              .doit = nl80211_set_coalesce,
 +              .policy = nl80211_policy,
 +              .flags = GENL_ADMIN_PERM,
 +              .internal_flags = NL80211_FLAG_NEED_WIPHY |
 +                                NL80211_FLAG_NEED_RTNL,
        }
  };