]> Git Repo - linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorDavid S. Miller <[email protected]>
Sat, 26 Dec 2009 00:34:56 +0000 (16:34 -0800)
committerDavid S. Miller <[email protected]>
Sat, 26 Dec 2009 00:34:56 +0000 (16:34 -0800)
29 files changed:
1  2 
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/iwlwifi/iwl-3945.c
drivers/net/wireless/iwlwifi/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/iwmc3200wifi/rx.c
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/main.c
drivers/net/wireless/libertas/wext.c
drivers/net/wireless/wl12xx/wl1251_main.c
include/linux/ieee80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/ht.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_pathtbl.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/util.c
net/wireless/reg.c
net/wireless/wext-compat.c

index 9e68c1a8aef01e27743360bc6b3e3e687428c79c,6401b3521049da24a24aa91c35b53f017ee348b8..3f5b887d0fcd92da11e72c23e98c973d50d6ae23
@@@ -363,14 -363,6 +363,6 @@@ static void ath_ani_calibrate(unsigned 
        short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
                ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
  
-       /*
-       * don't calibrate when we're scanning.
-       * we are most likely not on our home channel.
-       */
-       spin_lock(&sc->ani_lock);
-       if (sc->sc_flags & SC_OP_SCANNING)
-               goto set_timer;
        /* Only calibrate if awake */
        if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
                goto set_timer;
        ath9k_ps_restore(sc);
  
  set_timer:
-       spin_unlock(&sc->ani_lock);
        /*
        * Set timer interval based on previous results.
        * The interval must be the shortest necessary to satisfy ANI,
@@@ -1610,7 -1601,6 +1601,6 @@@ static int ath_init_softc(u16 devid, st
        spin_lock_init(&sc->wiphy_lock);
        spin_lock_init(&sc->sc_resetlock);
        spin_lock_init(&sc->sc_serial_rw);
-       spin_lock_init(&sc->ani_lock);
        spin_lock_init(&sc->sc_pm_lock);
        mutex_init(&sc->mutex);
        tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
@@@ -1973,9 -1963,6 +1963,9 @@@ int ath_reset(struct ath_softc *sc, boo
        struct ieee80211_hw *hw = sc->hw;
        int r;
  
 +      /* Stop ANI */
 +      del_timer_sync(&common->ani.timer);
 +
        ath9k_hw_set_interrupts(ah, 0);
        ath_drain_all_txq(sc, retry_tx);
        ath_stoprecv(sc);
                }
        }
  
 +      /* Start ANI */
 +      ath_start_ani(common);
 +
        return r;
  }
  
@@@ -3119,6 -3103,7 +3109,7 @@@ static void ath9k_sw_scan_start(struct 
  {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
  
        mutex_lock(&sc->mutex);
        if (ath9k_wiphy_scanning(sc)) {
  
        aphy->state = ATH_WIPHY_SCAN;
        ath9k_wiphy_pause_all_forced(sc, aphy);
-       spin_lock_bh(&sc->ani_lock);
        sc->sc_flags |= SC_OP_SCANNING;
-       spin_unlock_bh(&sc->ani_lock);
+       del_timer_sync(&common->ani.timer);
+       cancel_delayed_work_sync(&sc->tx_complete_work);
        mutex_unlock(&sc->mutex);
  }
  
@@@ -3145,13 -3129,14 +3135,14 @@@ static void ath9k_sw_scan_complete(stru
  {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
  
        mutex_lock(&sc->mutex);
-       spin_lock_bh(&sc->ani_lock);
        aphy->state = ATH_WIPHY_ACTIVE;
        sc->sc_flags &= ~SC_OP_SCANNING;
        sc->sc_flags |= SC_OP_FULL_RESET;
-       spin_unlock_bh(&sc->ani_lock);
+       ath_start_ani(common);
+       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
        ath_beacon_config(sc, NULL);
        mutex_unlock(&sc->mutex);
  }
index e413bd35bc411a1113177f1576538eb0ac26f00c,3708b5c204e6c96893f8e0eed7e26be0a3e0515d..d1bab141508a29fbd07ddc5e9caffde816aac3c4
@@@ -1835,7 -1835,8 +1835,7 @@@ static int iwl3945_send_rxon_assoc(stru
                rc = -EIO;
        }
  
 -      priv->alloc_rxb_page--;
 -      free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
 +      iwl_free_pages(priv, cmd.reply_page);
  
        return rc;
  }
@@@ -2810,7 -2811,7 +2810,7 @@@ static struct iwl_hcmd_utils_ops iwl394
        .rts_tx_cmd_flag = iwlcore_rts_tx_cmd_flag,
  };
  
- static struct iwl_ops iwl3945_ops = {
+ static const struct iwl_ops iwl3945_ops = {
        .ucode = &iwl3945_ucode,
        .lib = &iwl3945_lib,
        .hcmd = &iwl3945_hcmd,
@@@ -2835,7 -2836,6 +2835,7 @@@ static struct iwl_cfg iwl3945_bg_cfg = 
        .use_isr_legacy = true,
        .ht_greenfield_support = false,
        .led_compensation = 64,
 +      .broken_powersave = true,
  };
  
  static struct iwl_cfg iwl3945_abg_cfg = {
        .use_isr_legacy = true,
        .ht_greenfield_support = false,
        .led_compensation = 64,
 +      .broken_powersave = true,
  };
  
  struct pci_device_id iwl3945_hw_card_ids[] = {
index 484c5fdf7c2a511143ed569ec11833f7d45d7424,a5e3384d56b309a6609f8728f4a778069bcd8fc9..78706ce8b7ae04f2ce93fc927fc2484c367116bf
@@@ -1204,7 -1204,7 +1204,7 @@@ static int iwl4965_fill_txpower_tbl(str
        iwl4965_interpolate_chan(priv, channel, &ch_eeprom_info);
  
        /* calculate tx gain adjustment based on power supply voltage */
 -      voltage = priv->calib_info->voltage;
 +      voltage = le16_to_cpu(priv->calib_info->voltage);
        init_voltage = (s32)le32_to_cpu(priv->card_alive_init.voltage);
        voltage_compensation =
            iwl4965_get_voltage_compensation(voltage, init_voltage);
@@@ -2208,7 -2208,7 +2208,7 @@@ static struct iwl_lib_ops iwl4965_lib 
        },
  };
  
- static struct iwl_ops iwl4965_ops = {
+ static const struct iwl_ops iwl4965_ops = {
        .ucode = &iwl4965_ucode,
        .lib = &iwl4965_lib,
        .hcmd = &iwl4965_hcmd,
@@@ -2239,7 -2239,6 +2239,6 @@@ struct iwl_cfg iwl4965_agn_cfg = 
        .broken_powersave = true,
        .led_compensation = 61,
        .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
  };
  
  /* Module firmware */
index 33a5866538e7dbbbc0c7b0a13085075a59939fba,f2b1915530e7c58ebf570901184b4416e3b3faed..ec6b27689fa84257c7d9e3d41356797686474f74
@@@ -333,15 -333,14 +333,15 @@@ static void iwl5000_set_ct_threshold(st
  static int iwl5000_set_Xtal_calib(struct iwl_priv *priv)
  {
        struct iwl_calib_xtal_freq_cmd cmd;
 -      u16 *xtal_calib = (u16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_XTAL);
 +      __le16 *xtal_calib =
 +              (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_5000_XTAL);
  
        cmd.hdr.op_code = IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD;
        cmd.hdr.first_group = 0;
        cmd.hdr.groups_num = 1;
        cmd.hdr.data_valid = 1;
 -      cmd.cap_pin1 = (u8)xtal_calib[0];
 -      cmd.cap_pin2 = (u8)xtal_calib[1];
 +      cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]);
 +      cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]);
        return iwl_calib_set(&priv->calib_results[IWL_CALIB_XTAL],
                             (u8 *)&cmd, sizeof(cmd));
  }
@@@ -1466,6 -1465,7 +1466,7 @@@ struct iwl_lib_ops iwl5000_lib = 
        .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr,
        .dump_nic_event_log = iwl_dump_nic_event_log,
        .dump_nic_error_log = iwl_dump_nic_error_log,
+       .dump_csr = iwl_dump_csr,
        .load_ucode = iwl5000_load_ucode,
        .init_alive_start = iwl5000_init_alive_start,
        .alive_notify = iwl5000_alive_notify,
@@@ -1518,6 -1518,7 +1519,7 @@@ static struct iwl_lib_ops iwl5150_lib 
        .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr,
        .dump_nic_event_log = iwl_dump_nic_event_log,
        .dump_nic_error_log = iwl_dump_nic_error_log,
+       .dump_csr = iwl_dump_csr,
        .load_ucode = iwl5000_load_ucode,
        .init_alive_start = iwl5000_init_alive_start,
        .alive_notify = iwl5000_alive_notify,
         },
  };
  
- static struct iwl_ops iwl5000_ops = {
+ static const struct iwl_ops iwl5000_ops = {
        .ucode = &iwl5000_ucode,
        .lib = &iwl5000_lib,
        .hcmd = &iwl5000_hcmd,
        .led = &iwlagn_led_ops,
  };
  
- static struct iwl_ops iwl5150_ops = {
+ static const struct iwl_ops iwl5150_ops = {
        .ucode = &iwl5000_ucode,
        .lib = &iwl5150_lib,
        .hcmd = &iwl5000_hcmd,
@@@ -1599,7 -1600,6 +1601,6 @@@ struct iwl_cfg iwl5300_agn_cfg = 
        .ht_greenfield_support = true,
        .led_compensation = 51,
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
  };
  
  struct iwl_cfg iwl5100_bgn_cfg = {
@@@ -1668,7 -1668,6 +1669,6 @@@ struct iwl_cfg iwl5100_agn_cfg = 
        .ht_greenfield_support = true,
        .led_compensation = 51,
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
  };
  
  struct iwl_cfg iwl5350_agn_cfg = {
        .ht_greenfield_support = true,
        .led_compensation = 51,
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
  };
  
  struct iwl_cfg iwl5150_agn_cfg = {
        .ht_greenfield_support = true,
        .led_compensation = 51,
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
  };
  
  struct iwl_cfg iwl5150_abg_cfg = {
index 1c9866daf81565007beb6ee596063702e4bf387f,904b5d8da86021127e5275987c44d3789aa5bf14..771b03c1c7c5b8c291e5f37529f39ea73387e28e
@@@ -657,6 -657,131 +657,131 @@@ static void iwl_bg_statistics_periodic(
        iwl_send_statistics_request(priv, CMD_ASYNC, false);
  }
  
+ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
+                                       u32 start_idx, u32 num_events,
+                                       u32 mode)
+ {
+       u32 i;
+       u32 ptr;        /* SRAM byte address of log data */
+       u32 ev, time, data; /* event log data */
+       unsigned long reg_flags;
+       if (mode == 0)
+               ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32));
+       else
+               ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
+       /* Make sure device is powered up for SRAM reads */
+       spin_lock_irqsave(&priv->reg_lock, reg_flags);
+       if (iwl_grab_nic_access(priv)) {
+               spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+               return;
+       }
+       /* Set starting address; reads will auto-increment */
+       _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr);
+       rmb();
+       /*
+        * "time" is actually "data" for mode 0 (no timestamp).
+        * place event id # at far right for easier visual parsing.
+        */
+       for (i = 0; i < num_events; i++) {
+               ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+               time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+               if (mode == 0) {
+                       trace_iwlwifi_dev_ucode_cont_event(priv,
+                                                       0, time, ev);
+               } else {
+                       data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
+                       trace_iwlwifi_dev_ucode_cont_event(priv,
+                                               time, data, ev);
+               }
+       }
+       /* Allow device to power down */
+       iwl_release_nic_access(priv);
+       spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+ }
+ void iwl_continuous_event_trace(struct iwl_priv *priv)
+ {
+       u32 capacity;   /* event log capacity in # entries */
+       u32 base;       /* SRAM byte address of event log header */
+       u32 mode;       /* 0 - no timestamp, 1 - timestamp recorded */
+       u32 num_wraps;  /* # times uCode wrapped to top of log */
+       u32 next_entry; /* index of next entry to be written by uCode */
+       if (priv->ucode_type == UCODE_INIT)
+               base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr);
+       else
+               base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
+       if (priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) {
+               capacity = iwl_read_targ_mem(priv, base);
+               num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32)));
+               mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
+               next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32)));
+       } else
+               return;
+       if (num_wraps == priv->event_log.num_wraps) {
+               iwl_print_cont_event_trace(priv,
+                                      base, priv->event_log.next_entry,
+                                      next_entry - priv->event_log.next_entry,
+                                      mode);
+               priv->event_log.non_wraps_count++;
+       } else {
+               if ((num_wraps - priv->event_log.num_wraps) > 1)
+                       priv->event_log.wraps_more_count++;
+               else
+                       priv->event_log.wraps_once_count++;
+               trace_iwlwifi_dev_ucode_wrap_event(priv,
+                               num_wraps - priv->event_log.num_wraps,
+                               next_entry, priv->event_log.next_entry);
+               if (next_entry < priv->event_log.next_entry) {
+                       iwl_print_cont_event_trace(priv, base,
+                              priv->event_log.next_entry,
+                              capacity - priv->event_log.next_entry,
+                              mode);
+                       iwl_print_cont_event_trace(priv, base, 0,
+                               next_entry, mode);
+               } else {
+                       iwl_print_cont_event_trace(priv, base,
+                              next_entry, capacity - next_entry,
+                              mode);
+                       iwl_print_cont_event_trace(priv, base, 0,
+                               next_entry, mode);
+               }
+       }
+       priv->event_log.num_wraps = num_wraps;
+       priv->event_log.next_entry = next_entry;
+ }
+ /**
+  * iwl_bg_ucode_trace - Timer callback to log ucode event
+  *
+  * The timer is continually set to execute every
+  * UCODE_TRACE_PERIOD milliseconds after the last timer expired
+  * this function is to perform continuous uCode event logging operation
+  * if enabled
+  */
+ static void iwl_bg_ucode_trace(unsigned long data)
+ {
+       struct iwl_priv *priv = (struct iwl_priv *)data;
+       if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+               return;
+       if (priv->event_log.ucode_trace) {
+               iwl_continuous_event_trace(priv);
+               /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */
+               mod_timer(&priv->ucode_trace,
+                        jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
+       }
+ }
  static void iwl_rx_beacon_notif(struct iwl_priv *priv,
                                struct iwl_rx_mem_buffer *rxb)
  {
@@@ -689,12 -814,14 +814,14 @@@ static void iwl_rx_card_state_notif(str
        u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
        unsigned long status = priv->status;
  
-       IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n",
+       IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n",
                          (flags & HW_CARD_DISABLED) ? "Kill" : "On",
-                         (flags & SW_CARD_DISABLED) ? "Kill" : "On");
+                         (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+                         (flags & CT_CARD_DISABLED) ?
+                         "Reached" : "Not reached");
  
        if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED |
-                    RF_CARD_DISABLED)) {
+                    CT_CARD_DISABLED)) {
  
                iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
                            CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
                        iwl_write_direct32(priv, HBUS_TARG_MBX_C,
                                        HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
                }
-               if (flags & RF_CARD_DISABLED)
+               if (flags & CT_CARD_DISABLED)
                        iwl_tt_enter_ct_kill(priv);
        }
-       if (!(flags & RF_CARD_DISABLED))
+       if (!(flags & CT_CARD_DISABLED))
                iwl_tt_exit_ct_kill(priv);
  
        if (flags & HW_CARD_DISABLED)
@@@ -1705,8 -1832,9 +1832,9 @@@ void iwl_dump_nic_error_log(struct iwl_
   * iwl_print_event_log - Dump error event log to syslog
   *
   */
- static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
-                               u32 num_events, u32 mode)
+ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
+                              u32 num_events, u32 mode,
+                              int pos, char **buf, size_t bufsz)
  {
        u32 i;
        u32 base;       /* SRAM byte address of event log header */
        unsigned long reg_flags;
  
        if (num_events == 0)
-               return;
+               return pos;
        if (priv->ucode_type == UCODE_INIT)
                base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
        else
                time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
                if (mode == 0) {
                        /* data, ev */
-                       trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
-                       IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev);
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "EVT_LOG:0x%08x:%04u\n",
+                                               time, ev);
+                       } else {
+                               trace_iwlwifi_dev_ucode_event(priv, 0,
+                                       time, ev);
+                               IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n",
+                                       time, ev);
+                       }
                } else {
                        data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
-                       IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "EVT_LOGT:%010u:0x%08x:%04u\n",
+                                                time, data, ev);
+                       } else {
+                               IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n",
                                        time, data, ev);
-                       trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
+                               trace_iwlwifi_dev_ucode_event(priv, time,
+                                       data, ev);
+                       }
                }
        }
  
        /* Allow device to power down */
        iwl_release_nic_access(priv);
        spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+       return pos;
  }
  
  /**
   * iwl_print_last_event_logs - Dump the newest # of event log to syslog
   */
- static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
-                                     u32 num_wraps, u32 next_entry,
-                                     u32 size, u32 mode)
+ static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+                                   u32 num_wraps, u32 next_entry,
+                                   u32 size, u32 mode,
+                                   int pos, char **buf, size_t bufsz)
  {
        /*
         * display the newest DEFAULT_LOG_ENTRIES entries
         */
        if (num_wraps) {
                if (next_entry < size) {
-                       iwl_print_event_log(priv,
-                                       capacity - (size - next_entry),
-                                       size - next_entry, mode);
-                       iwl_print_event_log(priv, 0,
-                                   next_entry, mode);
+                       pos = iwl_print_event_log(priv,
+                                               capacity - (size - next_entry),
+                                               size - next_entry, mode,
+                                               pos, buf, bufsz);
+                       pos = iwl_print_event_log(priv, 0,
+                                                 next_entry, mode,
+                                                 pos, buf, bufsz);
                } else
-                       iwl_print_event_log(priv, next_entry - size,
-                                   size, mode);
+                       pos = iwl_print_event_log(priv, next_entry - size,
+                                                 size, mode, pos, buf, bufsz);
        } else {
-               if (next_entry < size)
-                       iwl_print_event_log(priv, 0, next_entry, mode);
-               else
-                       iwl_print_event_log(priv, next_entry - size,
-                                           size, mode);
+               if (next_entry < size) {
+                       pos = iwl_print_event_log(priv, 0, next_entry,
+                                                 mode, pos, buf, bufsz);
+               } else {
+                       pos = iwl_print_event_log(priv, next_entry - size,
+                                                 size, mode, pos, buf, bufsz);
+               }
        }
+       return pos;
  }
  
  /* For sanity check only.  Actual size is determined by uCode, typ. 512 */
  
  #define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20)
  
- void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
+ int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+                           char **buf, bool display)
  {
        u32 base;       /* SRAM byte address of event log header */
        u32 capacity;   /* event log capacity in # entries */
        u32 num_wraps;  /* # times uCode wrapped to top of log */
        u32 next_entry; /* index of next entry to be written by uCode */
        u32 size;       /* # entries that we'll print */
+       int pos = 0;
+       size_t bufsz = 0;
  
        if (priv->ucode_type == UCODE_INIT)
                base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr);
                IWL_ERR(priv,
                        "Invalid event log pointer 0x%08X for %s uCode\n",
                        base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT");
-               return;
+               return pos;
        }
  
        /* event log header */
        /* bail out if nothing in log */
        if (size == 0) {
                IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-               return;
+               return pos;
        }
  
  #ifdef CONFIG_IWLWIFI_DEBUG
 -      if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS))
 +      if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log)
                size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES)
                        ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size;
  #else
                size);
  
  #ifdef CONFIG_IWLWIFI_DEBUG
+       if (display) {
+               if (full_log)
+                       bufsz = capacity * 48;
+               else
+                       bufsz = size * 48;
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return pos;
+       }
        if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
                /*
                 * if uCode has wrapped back to top of log,
                 * i.e the next one that uCode would fill.
                 */
                if (num_wraps)
-                       iwl_print_event_log(priv, next_entry,
-                                           capacity - next_entry, mode);
+                       pos = iwl_print_event_log(priv, next_entry,
+                                               capacity - next_entry, mode,
+                                               pos, buf, bufsz);
                /* (then/else) start at top of log */
-               iwl_print_event_log(priv, 0, next_entry, mode);
+               pos = iwl_print_event_log(priv, 0,
+                                         next_entry, mode, pos, buf, bufsz);
        } else
-               iwl_print_last_event_logs(priv, capacity, num_wraps,
-                                       next_entry, size, mode);
+               pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+                                               next_entry, size, mode,
+                                               pos, buf, bufsz);
  #else
-       iwl_print_last_event_logs(priv, capacity, num_wraps,
-                               next_entry, size, mode);
+       pos = iwl_print_last_event_logs(priv, capacity, num_wraps,
+                                       next_entry, size, mode,
+                                       pos, buf, bufsz);
  #endif
+       return pos;
  }
  
  /**
@@@ -2456,6 -2623,10 +2623,10 @@@ static int iwl_setup_mac(struct iwl_pri
                hw->flags |= IEEE80211_HW_SUPPORTS_PS |
                             IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
  
+       if (priv->cfg->sku & IWL_SKU_N)
+               hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+                            IEEE80211_HW_SUPPORTS_STATIC_SMPS;
        hw->sta_data_size = sizeof(struct iwl_station_priv);
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
@@@ -3126,6 -3297,10 +3297,10 @@@ static void iwl_setup_deferred_work(str
        priv->statistics_periodic.data = (unsigned long)priv;
        priv->statistics_periodic.function = iwl_bg_statistics_periodic;
  
+       init_timer(&priv->ucode_trace);
+       priv->ucode_trace.data = (unsigned long)priv;
+       priv->ucode_trace.function = iwl_bg_ucode_trace;
        if (!priv->cfg->use_isr_legacy)
                tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
                        iwl_irq_tasklet, (unsigned long)priv);
@@@ -3144,6 -3319,7 +3319,7 @@@ static void iwl_cancel_deferred_work(st
        cancel_delayed_work(&priv->alive_start);
        cancel_work_sync(&priv->beacon_update);
        del_timer_sync(&priv->statistics_periodic);
+       del_timer_sync(&priv->ucode_trace);
  }
  
  static void iwl_init_hw_rates(struct iwl_priv *priv,
@@@ -3173,6 -3349,7 +3349,6 @@@ static int iwl_init_drv(struct iwl_pri
  
        priv->ibss_beacon = NULL;
  
 -      spin_lock_init(&priv->lock);
        spin_lock_init(&priv->sta_lock);
        spin_lock_init(&priv->hcmd_lock);
  
        priv->band = IEEE80211_BAND_2GHZ;
  
        priv->iw_mode = NL80211_IFTYPE_STATION;
+       priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
  
        /* Choose which receivers/antennas to use */
        if (priv->cfg->ops->hcmd->set_rxon_chain)
@@@ -3360,11 -3538,10 +3537,11 @@@ static int iwl_pci_probe(struct pci_de
                (unsigned long long) pci_resource_len(pdev, 0));
        IWL_DEBUG_INFO(priv, "pci_resource_base = %p\n", priv->hw_base);
  
 -      /* this spin lock will be used in apm_ops.init and EEPROM access
 +      /* these spin locks will be used in apm_ops.init and EEPROM access
         * we should init now
         */
        spin_lock_init(&priv->reg_lock);
 +      spin_lock_init(&priv->lock);
        iwl_hw_detect(priv);
        IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
                priv->cfg->name, priv->hw_rev);
index 27ca859e7453c250ca70aea6d64ce9820ae6f98d,f7acbb32900a7c5030064456435eb4257e8792c5..308f679ef81cec56a250f288d87dbb29509e1c50
@@@ -63,7 -63,7 +63,7 @@@
  #ifndef __iwl_core_h__
  #define __iwl_core_h__
  
 -#include <linux/utsrelease.h>
 +#include <generated/utsrelease.h>
  
  /************************
   * forward declarations *
@@@ -169,8 -169,10 +169,10 @@@ struct iwl_lib_ops 
        int (*is_valid_rtc_data_addr)(u32 addr);
        /* 1st ucode load */
        int (*load_ucode)(struct iwl_priv *priv);
-       void (*dump_nic_event_log)(struct iwl_priv *priv, bool full_log);
+       int (*dump_nic_event_log)(struct iwl_priv *priv,
+                                 bool full_log, char **buf, bool display);
        void (*dump_nic_error_log)(struct iwl_priv *priv);
+       void (*dump_csr)(struct iwl_priv *priv);
        int (*set_channel_switch)(struct iwl_priv *priv, u16 channel);
        /* power management */
        struct iwl_apm_ops apm_ops;
@@@ -230,7 -232,6 +232,6 @@@ struct iwl_mod_params 
   * @chain_noise_num_beacons: number of beacons used to compute chain noise
   * @adv_thermal_throttle: support advance thermal throttle
   * @support_ct_kill_exit: support ct kill exit condition
-  * @sm_ps_mode: spatial multiplexing power save mode
   * @support_wimax_coexist: support wimax/wifi co-exist
   *
   * We enable the driver to be backward compatible wrt API version. The
@@@ -287,7 -288,6 +288,6 @@@ struct iwl_cfg 
        const bool supports_idle;
        bool adv_thermal_throttle;
        bool support_ct_kill_exit;
-       u8 sm_ps_mode;
        const bool support_wimax_coexist;
  };
  
@@@ -581,7 -581,9 +581,9 @@@ int iwl_pci_resume(struct pci_dev *pdev
  *  Error Handling Debugging
  ******************************************************/
  void iwl_dump_nic_error_log(struct iwl_priv *priv);
- void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log);
+ int iwl_dump_nic_event_log(struct iwl_priv *priv,
+                          bool full_log, char **buf, bool display);
+ void iwl_dump_csr(struct iwl_priv *priv);
  #ifdef CONFIG_IWLWIFI_DEBUG
  void iwl_print_rx_config_cmd(struct iwl_priv *priv);
  #else
index 165d1f6e2dd9304046895b437fd65e0d2f88e580,1e12e7340c90d1890341f5a48610c4a7227e8fc8..42f9b17327c34040fd3e976a442fb23c0a108055
@@@ -512,6 -512,7 +512,7 @@@ struct iwl_ht_config 
        bool is_ht;
        bool is_40mhz;
        bool single_chain_sufficient;
+       enum ieee80211_smps_mode smps; /* current smps mode */
        /* BSS related data */
        u8 extension_chan_offset;
        u8 ht_protection;
@@@ -984,6 -985,32 +985,32 @@@ struct iwl_switch_rxon 
        __le16 channel;
  };
  
+ /*
+  * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds
+  * to perform continuous uCode event logging operation if enabled
+  */
+ #define UCODE_TRACE_PERIOD (100)
+ /*
+  * iwl_event_log: current uCode event log position
+  *
+  * @ucode_trace: enable/disable ucode continuous trace timer
+  * @num_wraps: how many times the event buffer wraps
+  * @next_entry:  the entry just before the next one that uCode would fill
+  * @non_wraps_count: counter for no wrap detected when dump ucode events
+  * @wraps_once_count: counter for wrap once detected when dump ucode events
+  * @wraps_more_count: counter for wrap more than once detected
+  *                  when dump ucode events
+  */
+ struct iwl_event_log {
+       bool ucode_trace;
+       u32 num_wraps;
+       u32 next_entry;
+       int non_wraps_count;
+       int wraps_once_count;
+       int wraps_more_count;
+ };
  struct iwl_priv {
  
        /* ieee device used by generic ieee processing code */
        u32 last_beacon_time;
        u64 last_tsf;
  
 -      /* eeprom */
 +      /* eeprom -- this is in the card's little endian byte order */
        u8 *eeprom;
        int    nvm_device_type;
        struct iwl_eeprom_calib_info *calib_info;
        u32 disable_tx_power_cal;
        struct work_struct run_time_calib_work;
        struct timer_list statistics_periodic;
+       struct timer_list ucode_trace;
        bool hw_ready;
        /*For 3945*/
  #define IWL_DEFAULT_TX_POWER 0x0F
        struct iwl3945_notif_statistics statistics_39;
  
        u32 sta_supp_rates;
+       struct iwl_event_log event_log;
  }; /*iwl_priv */
  
  static inline void iwl_txq_ctx_activate(struct iwl_priv *priv, int txq_id)
@@@ -1353,15 -1383,4 +1383,15 @@@ static inline int is_channel_ibss(cons
        return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0;
  }
  
 +static inline void __iwl_free_pages(struct iwl_priv *priv, struct page *page)
 +{
 +      __free_pages(page, priv->hw_params.rx_page_order);
 +      priv->alloc_rxb_page--;
 +}
 +
 +static inline void iwl_free_pages(struct iwl_priv *priv, unsigned long page)
 +{
 +      free_pages(page, priv->hw_params.rx_page_order);
 +      priv->alloc_rxb_page--;
 +}
  #endif                                /* __iwl_dev_h__ */
index e5d8fa38432e48f1caee788711e5dd8f208c8282,f7c7ff4264fbaf35c69eb3173e7b27f6fa0b67c9..6533122ed87af474c2b22796070bed027b608113
@@@ -548,9 -548,6 +548,9 @@@ static int iwl3945_tx_skb(struct iwl_pr
        txq = &priv->txq[txq_id];
        q = &txq->q;
  
 +      if ((iwl_queue_space(q) < q->high_mark))
 +              goto drop;
 +
        spin_lock_irqsave(&priv->lock, flags);
  
        idx = get_cmd_index(q, q->write_ptr, 0);
@@@ -815,7 -812,7 +815,7 @@@ static int iwl3945_get_measurement(stru
                break;
        }
  
 -      free_pages(cmd.reply_page, priv->hw_params.rx_page_order);
 +      iwl_free_pages(priv, cmd.reply_page);
  
        return rc;
  }
@@@ -1201,7 -1198,9 +1201,7 @@@ void iwl3945_rx_queue_reset(struct iwl_
                        pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
                                PAGE_SIZE << priv->hw_params.rx_page_order,
                                PCI_DMA_FROMDEVICE);
 -                      priv->alloc_rxb_page--;
 -                      __free_pages(rxq->pool[i].page,
 -                                   priv->hw_params.rx_page_order);
 +                      __iwl_free_pages(priv, rxq->pool[i].page);
                        rxq->pool[i].page = NULL;
                }
                list_add_tail(&rxq->pool[i].list, &rxq->rx_used);
@@@ -1248,8 -1247,10 +1248,8 @@@ static void iwl3945_rx_queue_free(struc
                        pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma,
                                PAGE_SIZE << priv->hw_params.rx_page_order,
                                PCI_DMA_FROMDEVICE);
 -                      __free_pages(rxq->pool[i].page,
 -                                   priv->hw_params.rx_page_order);
 +                      __iwl_free_pages(priv, rxq->pool[i].page);
                        rxq->pool[i].page = NULL;
 -                      priv->alloc_rxb_page--;
                }
        }
  
@@@ -1559,8 -1560,9 +1559,9 @@@ void iwl3945_dump_nic_error_log(struct 
   * iwl3945_print_event_log - Dump error event log to syslog
   *
   */
- static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
-                               u32 num_events, u32 mode)
+ static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
+                                 u32 num_events, u32 mode,
+                                 int pos, char **buf, size_t bufsz)
  {
        u32 i;
        u32 base;       /* SRAM byte address of event log header */
        unsigned long reg_flags;
  
        if (num_events == 0)
-               return;
+               return pos;
  
        base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
  
                time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
                if (mode == 0) {
                        /* data, ev */
-                       IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
-                       trace_iwlwifi_dev_ucode_event(priv, 0, time, ev);
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "0x%08x:%04u\n",
+                                               time, ev);
+                       } else {
+                               IWL_ERR(priv, "0x%08x\t%04u\n", time, ev);
+                               trace_iwlwifi_dev_ucode_event(priv, 0,
+                                                             time, ev);
+                       }
                } else {
                        data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
-                       IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev);
-                       trace_iwlwifi_dev_ucode_event(priv, time, data, ev);
+                       if (bufsz) {
+                               pos += scnprintf(*buf + pos, bufsz - pos,
+                                               "%010u:0x%08x:%04u\n",
+                                                time, data, ev);
+                       } else {
+                               IWL_ERR(priv, "%010u\t0x%08x\t%04u\n",
+                                       time, data, ev);
+                               trace_iwlwifi_dev_ucode_event(priv, time,
+                                                             data, ev);
+                       }
                }
        }
  
        /* Allow device to power down */
        iwl_release_nic_access(priv);
        spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
+       return pos;
  }
  
  /**
   * iwl3945_print_last_event_logs - Dump the newest # of event log to syslog
   */
- static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
+ static int iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
                                      u32 num_wraps, u32 next_entry,
-                                     u32 size, u32 mode)
+                                     u32 size, u32 mode,
+                                     int pos, char **buf, size_t bufsz)
  {
        /*
         * display the newest DEFAULT_LOG_ENTRIES entries
         */
        if (num_wraps) {
                if (next_entry < size) {
-                       iwl3945_print_event_log(priv,
-                                       capacity - (size - next_entry),
-                                       size - next_entry, mode);
-                       iwl3945_print_event_log(priv, 0,
-                                   next_entry, mode);
+                       pos = iwl3945_print_event_log(priv,
+                                            capacity - (size - next_entry),
+                                            size - next_entry, mode,
+                                            pos, buf, bufsz);
+                       pos = iwl3945_print_event_log(priv, 0,
+                                                     next_entry, mode,
+                                                     pos, buf, bufsz);
                } else
-                       iwl3945_print_event_log(priv, next_entry - size,
-                                   size, mode);
+                       pos = iwl3945_print_event_log(priv, next_entry - size,
+                                                     size, mode,
+                                                     pos, buf, bufsz);
        } else {
                if (next_entry < size)
-                       iwl3945_print_event_log(priv, 0, next_entry, mode);
+                       pos = iwl3945_print_event_log(priv, 0,
+                                                     next_entry, mode,
+                                                     pos, buf, bufsz);
                else
-                       iwl3945_print_event_log(priv, next_entry - size,
-                                           size, mode);
+                       pos = iwl3945_print_event_log(priv, next_entry - size,
+                                                     size, mode,
+                                                     pos, buf, bufsz);
        }
+       return pos;
  }
  
  /* For sanity check only.  Actual size is determined by uCode, typ. 512 */
  
  #define DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES (20)
  
- void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
+ int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+                           char **buf, bool display)
  {
        u32 base;       /* SRAM byte address of event log header */
        u32 capacity;   /* event log capacity in # entries */
        u32 num_wraps;  /* # times uCode wrapped to top of log */
        u32 next_entry; /* index of next entry to be written by uCode */
        u32 size;       /* # entries that we'll print */
+       int pos = 0;
+       size_t bufsz = 0;
  
        base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
        if (!iwl3945_hw_valid_rtc_data_addr(base)) {
                IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base);
-               return;
+               return pos;
        }
  
        /* event log header */
        /* bail out if nothing in log */
        if (size == 0) {
                IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
-               return;
+               return pos;
        }
  
  #ifdef CONFIG_IWLWIFI_DEBUG
 -      if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS))
 +      if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log)
                size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES)
                        ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size;
  #else
                  size);
  
  #ifdef CONFIG_IWLWIFI_DEBUG
+       if (display) {
+               if (full_log)
+                       bufsz = capacity * 48;
+               else
+                       bufsz = size * 48;
+               *buf = kmalloc(bufsz, GFP_KERNEL);
+               if (!*buf)
+                       return pos;
+       }
        if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) {
                /* if uCode has wrapped back to top of log,
                 * start at the oldest entry,
                 * i.e the next one that uCode would fill.
                 */
                if (num_wraps)
-                       iwl3945_print_event_log(priv, next_entry,
-                                   capacity - next_entry, mode);
+                       pos = iwl3945_print_event_log(priv, next_entry,
+                                               capacity - next_entry, mode,
+                                               pos, buf, bufsz);
  
                /* (then/else) start at top of log */
-               iwl3945_print_event_log(priv, 0, next_entry, mode);
+               pos = iwl3945_print_event_log(priv, 0, next_entry, mode,
+                                             pos, buf, bufsz);
        } else
-               iwl3945_print_last_event_logs(priv, capacity, num_wraps,
-                                       next_entry, size, mode);
+               pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps,
+                                                   next_entry, size, mode,
+                                                   pos, buf, bufsz);
  #else
-       iwl3945_print_last_event_logs(priv, capacity, num_wraps,
-                               next_entry, size, mode);
+       pos = iwl3945_print_last_event_logs(priv, capacity, num_wraps,
+                                           next_entry, size, mode,
+                                           pos, buf, bufsz);
  #endif
+       return pos;
  }
  
  static void iwl3945_irq_tasklet(struct iwl_priv *priv)
@@@ -3866,6 -3908,7 +3907,6 @@@ static int iwl3945_init_drv(struct iwl_
        priv->retry_rate = 1;
        priv->ibss_beacon = NULL;
  
 -      spin_lock_init(&priv->lock);
        spin_lock_init(&priv->sta_lock);
        spin_lock_init(&priv->hcmd_lock);
  
@@@ -3934,11 -3977,9 +3975,11 @@@ static int iwl3945_setup_mac(struct iwl
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_NOISE_DBM |
 -                  IEEE80211_HW_SPECTRUM_MGMT |
 -                  IEEE80211_HW_SUPPORTS_PS |
 -                  IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
 +                  IEEE80211_HW_SPECTRUM_MGMT;
 +
 +      if (!priv->cfg->broken_powersave)
 +              hw->flags |= IEEE80211_HW_SUPPORTS_PS |
 +                           IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
  
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
@@@ -4057,11 -4098,10 +4098,11 @@@ static int iwl3945_pci_probe(struct pci
         * PCI Tx retries from interfering with C3 CPU state */
        pci_write_config_byte(pdev, 0x41, 0x00);
  
 -      /* this spin lock will be used in apm_ops.init and EEPROM access
 +      /* these spin locks will be used in apm_ops.init and EEPROM access
         * we should init now
         */
        spin_lock_init(&priv->reg_lock);
 +      spin_lock_init(&priv->lock);
  
        /***********************
         * 4. Read EEPROM
index 6d6ed7485175a6f2d8f8d05793767e08c6f91c69,0619d6a1103c86d7684516044d3ee8de72c39838..d32adeab68a38e98065d9338e0eb8bb38b0faefe
@@@ -868,36 -868,35 +868,35 @@@ static int iwm_mlme_mgt_frame(struct iw
        struct iwm_umac_notif_mgt_frame *mgt_frame =
                        (struct iwm_umac_notif_mgt_frame *)buf;
        struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame;
-       u8 *ie;
  
        IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame,
                    le16_to_cpu(mgt_frame->len));
  
        if (ieee80211_is_assoc_req(mgt->frame_control)) {
-               ie = mgt->u.assoc_req.variable;;
-               iwm->req_ie_len =
-                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               iwm->req_ie_len = le16_to_cpu(mgt_frame->len)
+                                 - offsetof(struct ieee80211_mgmt,
+                                            u.assoc_req.variable);
                kfree(iwm->req_ie);
                iwm->req_ie = kmemdup(mgt->u.assoc_req.variable,
                                      iwm->req_ie_len, GFP_KERNEL);
        } else if (ieee80211_is_reassoc_req(mgt->frame_control)) {
-               ie = mgt->u.reassoc_req.variable;;
-               iwm->req_ie_len =
-                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               iwm->req_ie_len = le16_to_cpu(mgt_frame->len)
+                                 - offsetof(struct ieee80211_mgmt,
+                                            u.reassoc_req.variable);
                kfree(iwm->req_ie);
                iwm->req_ie = kmemdup(mgt->u.reassoc_req.variable,
                                      iwm->req_ie_len, GFP_KERNEL);
        } else if (ieee80211_is_assoc_resp(mgt->frame_control)) {
-               ie = mgt->u.assoc_resp.variable;;
-               iwm->resp_ie_len =
-                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               iwm->resp_ie_len = le16_to_cpu(mgt_frame->len)
+                                  - offsetof(struct ieee80211_mgmt,
+                                             u.assoc_resp.variable);
                kfree(iwm->resp_ie);
                iwm->resp_ie = kmemdup(mgt->u.assoc_resp.variable,
                                       iwm->resp_ie_len, GFP_KERNEL);
        } else if (ieee80211_is_reassoc_resp(mgt->frame_control)) {
-               ie = mgt->u.reassoc_resp.variable;;
-               iwm->resp_ie_len =
-                               le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt);
+               iwm->resp_ie_len = le16_to_cpu(mgt_frame->len)
+                                  - offsetof(struct ieee80211_mgmt,
+                                             u.reassoc_resp.variable);
                kfree(iwm->resp_ie);
                iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable,
                                       iwm->resp_ie_len, GFP_KERNEL);
@@@ -1126,7 -1125,7 +1125,7 @@@ static int iwm_ntf_stop_resume_tx(struc
  
                if (!stop) {
                        struct iwm_tx_queue *txq;
 -                      u16 queue = iwm_tid_to_queue(bit);
 +                      int queue = iwm_tid_to_queue(bit);
  
                        if (queue < 0)
                                continue;
@@@ -1534,6 -1533,33 +1533,33 @@@ static void classify8023(struct sk_buf
        }
  }
  
+ static void iwm_rx_process_amsdu(struct iwm_priv *iwm, struct sk_buff *skb)
+ {
+       struct wireless_dev *wdev = iwm_to_wdev(iwm);
+       struct net_device *ndev = iwm_to_ndev(iwm);
+       struct sk_buff_head list;
+       struct sk_buff *frame;
+       IWM_HEXDUMP(iwm, DBG, RX, "A-MSDU: ", skb->data, skb->len);
+       __skb_queue_head_init(&list);
+       ieee80211_amsdu_to_8023s(skb, &list, ndev->dev_addr, wdev->iftype, 0);
+       while ((frame = __skb_dequeue(&list))) {
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += frame->len;
+               frame->protocol = eth_type_trans(frame, ndev);
+               frame->ip_summed = CHECKSUM_NONE;
+               memset(frame->cb, 0, sizeof(frame->cb));
+               if (netif_rx_ni(frame) == NET_RX_DROP) {
+                       IWM_ERR(iwm, "Packet dropped\n");
+                       ndev->stats.rx_dropped++;
+               }
+       }
+ }
  static void iwm_rx_process_packet(struct iwm_priv *iwm,
                                  struct iwm_rx_packet *packet,
                                  struct iwm_rx_ticket_node *ticket_node)
        switch (le16_to_cpu(ticket_node->ticket->action)) {
        case IWM_RX_TICKET_RELEASE:
                IWM_DBG_RX(iwm, DBG, "RELEASE packet\n");
-               classify8023(skb);
                iwm_rx_adjust_packet(iwm, packet, ticket_node);
+               skb->dev = iwm_to_ndev(iwm);
+               classify8023(skb);
+               if (le16_to_cpu(ticket_node->ticket->flags) &
+                   IWM_RX_TICKET_AMSDU_MSK) {
+                       iwm_rx_process_amsdu(iwm, skb);
+                       break;
+               }
                ret = ieee80211_data_to_8023(skb, ndev->dev_addr, wdev->iftype);
                if (ret < 0) {
                        IWM_DBG_RX(iwm, DBG, "Couldn't convert 802.11 header - "
                                   "%d\n", ret);
+                       kfree_skb(packet->skb);
                        break;
                }
  
                IWM_HEXDUMP(iwm, DBG, RX, "802.3: ", skb->data, skb->len);
  
-               skb->dev = iwm_to_ndev(iwm);
+               ndev->stats.rx_packets++;
+               ndev->stats.rx_bytes += skb->len;
                skb->protocol = eth_type_trans(skb, ndev);
                skb->ip_summed = CHECKSUM_NONE;
                memset(skb->cb, 0, sizeof(skb->cb));
  
-               ndev->stats.rx_packets++;
-               ndev->stats.rx_bytes += skb->len;
                if (netif_rx_ni(skb) == NET_RX_DROP) {
                        IWM_ERR(iwm, "Packet dropped\n");
                        ndev->stats.rx_dropped++;
                kfree_skb(packet->skb);
                break;
        default:
 -              IWM_ERR(iwm, "Unknow ticket action: %d\n",
 +              IWM_ERR(iwm, "Unknown ticket action: %d\n",
                        le16_to_cpu(ticket_node->ticket->action));
                kfree_skb(packet->skb);
        }
index 42611bea76a3e4805399294123c23837630e74b4,42051f7cad6df61794ec711dc09d5c297cb3c845..82371ef395241901d175ea73ed6dddfa755d5ff6
@@@ -143,19 -143,6 +143,6 @@@ int lbs_update_hw_spec(struct lbs_priva
        lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
                    cmd.hwifversion, cmd.version);
  
-       /* Determine mesh_fw_ver from fwrelease and fwcapinfo */
-       /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
-       /* 5.110.22 have mesh command with 0xa3 command id */
-       /* 10.0.0.p0 FW brings in mesh config command with different id */
-       /* Check FW version MSB and initialize mesh_fw_ver */
-       if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5)
-               priv->mesh_fw_ver = MESH_FW_OLD;
-       else if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
-               (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK))
-               priv->mesh_fw_ver = MESH_FW_NEW;
-       else
-               priv->mesh_fw_ver = MESH_NONE;
        /* Clamp region code to 8-bit since FW spec indicates that it should
         * only ever be 8-bit, even though the field size is 16-bit.  Some firmware
         * returns non-zero high 8 bits here.
@@@ -855,9 -842,6 +842,6 @@@ int lbs_set_radio(struct lbs_private *p
        if (priv->fwrelease < 0x09000000) {
                switch (preamble) {
                case RADIO_PREAMBLE_SHORT:
-                       if (!(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
-                               goto out;
-                       /* Fall through */
                case RADIO_PREAMBLE_AUTO:
                case RADIO_PREAMBLE_LONG:
                        cmd.control = cpu_to_le16(preamble);
@@@ -1011,6 -995,8 +995,8 @@@ int lbs_prepare_and_send_command(struc
                ret = 0;
                break;
  
+ #ifdef CONFIG_LIBERTAS_MESH
        case CMD_BT_ACCESS:
                ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf);
                break;
                ret = lbs_cmd_fwt_access(cmdptr, cmd_action, pdata_buf);
                break;
  
+ #endif
        case CMD_802_11_BEACON_CTRL:
                ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
                break;
@@@ -1317,7 -1305,7 +1305,7 @@@ int lbs_execute_next_command(struct lbs
                if ((priv->psmode != LBS802_11POWERMODECAM) &&
                    (priv->psstate == PS_STATE_FULL_POWER) &&
                    ((priv->connect_status == LBS_CONNECTED) ||
-                   (priv->mesh_connect_status == LBS_CONNECTED))) {
+                   lbs_mesh_connected(priv))) {
                        if (priv->secinfo.WPAenabled ||
                            priv->secinfo.WPA2enabled) {
                                /* check for valid WPA group keys */
@@@ -1365,7 -1353,7 +1353,7 @@@ static void lbs_send_confirmsleep(struc
        priv->dnld_sent = DNLD_RES_RECEIVED;
  
        /* If nothing to do, go back to sleep (?) */
 -      if (!__kfifo_len(priv->event_fifo) && !priv->resp_len[priv->resp_idx])
 +      if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
                priv->psstate = PS_STATE_SLEEP;
  
        spin_unlock_irqrestore(&priv->driver_lock, flags);
@@@ -1439,7 -1427,7 +1427,7 @@@ void lbs_ps_confirm_sleep(struct lbs_pr
        }
  
        /* Pending events or command responses? */
 -      if (__kfifo_len(priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
 +      if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
                allowed = 0;
                lbs_deb_host("pending events or command responses\n");
        }
index 05bb298dfae9a017be7458c8055d961e289e5de6,d5a9dcae40596b966b0419cb85cbe60468ada958..c348aff8f3092761bfea9a5e190325d651d6999f
@@@ -10,7 -10,7 +10,7 @@@
  #include "scan.h"
  #include "assoc.h"
  
 -
 +#include <linux/kfifo.h>
  
  /** sleep_params */
  struct sleep_params {
@@@ -39,15 -39,14 +39,14 @@@ struct lbs_private 
  
        /* Mesh */
        struct net_device *mesh_dev; /* Virtual device */
+ #ifdef CONFIG_LIBERTAS_MESH
        u32 mesh_connect_status;
        struct lbs_mesh_stats mstats;
        int mesh_open;
-       int mesh_fw_ver;
-       int mesh_autostart_enabled;
        uint16_t mesh_tlv;
        u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1];
        u8 mesh_ssid_len;
-       struct work_struct sync_channel;
+ #endif
  
        /* Monitor mode */
        struct net_device *rtap_net_dev;
        u32 resp_len[2];
  
        /* Events sent from hardware to driver */
 -      struct kfifo *event_fifo;
 +      struct kfifo event_fifo;
  
        /** thread to service interrupts */
        struct task_struct *main_thread;
        struct bss_descriptor *networks;
        struct assoc_request * pending_assoc_req;
        struct assoc_request * in_progress_assoc_req;
-       u16 capability;
        uint16_t enablehwauto;
-       uint16_t ratebitmap;
  
        /* ADHOC */
        u16 beacon_period;
index c2975c8e2f211ff2f9cb64ac61db3e26f7d93862,f9f195f7b17cb6145310641f9c0f738fd1ea036f..60bde1233a302684c31f555d155ce46c31cf9d4f
@@@ -123,7 -123,7 +123,7 @@@ static ssize_t lbs_rtap_set(struct devi
                if (priv->monitormode == monitor_mode)
                        return strlen(buf);
                if (!priv->monitormode) {
-                       if (priv->infra_open || priv->mesh_open)
+                       if (priv->infra_open || lbs_mesh_open(priv))
                                return -EBUSY;
                        if (priv->mode == IW_MODE_INFRA)
                                lbs_cmd_80211_deauthenticate(priv,
@@@ -459,7 -459,7 +459,7 @@@ static int lbs_thread(void *data
                else if (!list_empty(&priv->cmdpendingq) &&
                                        !(priv->wakeup_dev_required))
                        shouldsleep = 0;        /* We have a command to send */
 -              else if (__kfifo_len(priv->event_fifo))
 +              else if (kfifo_len(&priv->event_fifo))
                        shouldsleep = 0;        /* We have an event to process */
                else
                        shouldsleep = 1;        /* No command */
  
                /* Process hardware events, e.g. card removed, link lost */
                spin_lock_irq(&priv->driver_lock);
 -              while (__kfifo_len(priv->event_fifo)) {
 +              while (kfifo_len(&priv->event_fifo)) {
                        u32 event;
 -                      __kfifo_get(priv->event_fifo, (unsigned char *) &event,
 -                              sizeof(event));
 +
 +                      if (kfifo_out(&priv->event_fifo,
 +                              (unsigned char *) &event, sizeof(event)) !=
 +                              sizeof(event))
 +                                      break;
                        spin_unlock_irq(&priv->driver_lock);
                        lbs_process_event(priv, event);
                        spin_lock_irq(&priv->driver_lock);
                                if (priv->connect_status == LBS_CONNECTED)
                                        netif_wake_queue(priv->dev);
                                if (priv->mesh_dev &&
-                                   priv->mesh_connect_status == LBS_CONNECTED)
+                                   lbs_mesh_connected(priv))
                                        netif_wake_queue(priv->mesh_dev);
                        }
                }
@@@ -809,18 -806,6 +809,6 @@@ int lbs_exit_auto_deep_sleep(struct lbs
        return 0;
  }
  
- static void lbs_sync_channel_worker(struct work_struct *work)
- {
-       struct lbs_private *priv = container_of(work, struct lbs_private,
-               sync_channel);
-       lbs_deb_enter(LBS_DEB_MAIN);
-       if (lbs_update_channel(priv))
-               lbs_pr_info("Channel synchronization failed.");
-       lbs_deb_leave(LBS_DEB_MAIN);
- }
  static int lbs_init_adapter(struct lbs_private *priv)
  {
        size_t bufsize;
        memset(priv->current_addr, 0xff, ETH_ALEN);
  
        priv->connect_status = LBS_DISCONNECTED;
-       priv->mesh_connect_status = LBS_DISCONNECTED;
        priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
        priv->mode = IW_MODE_INFRA;
        priv->channel = DEFAULT_AD_HOC_CHANNEL;
        priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
        priv->radio_on = 1;
        priv->enablehwauto = 1;
-       priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
        priv->psmode = LBS802_11POWERMODECAM;
        priv->psstate = PS_STATE_FULL_POWER;
        priv->is_deep_sleep = 0;
        priv->resp_len[0] = priv->resp_len[1] = 0;
  
        /* Create the event FIFO */
 -      priv->event_fifo = kfifo_alloc(sizeof(u32) * 16, GFP_KERNEL, NULL);
 -      if (IS_ERR(priv->event_fifo)) {
 +      ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL);
 +      if (ret) {
                lbs_pr_err("Out of memory allocating event FIFO buffer\n");
 -              ret = -ENOMEM;
                goto out;
        }
  
@@@ -903,7 -887,8 +889,7 @@@ static void lbs_free_adapter(struct lbs
        lbs_deb_enter(LBS_DEB_MAIN);
  
        lbs_free_cmd_buffer(priv);
 -      if (priv->event_fifo)
 -              kfifo_free(priv->event_fifo);
 +      kfifo_free(&priv->event_fifo);
        del_timer(&priv->command_timer);
        del_timer(&priv->auto_deepsleep_timer);
        kfree(priv->networks);
@@@ -998,11 -983,6 +984,6 @@@ struct lbs_private *lbs_add_card(void *
        INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker);
        INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
        INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker);
-       INIT_WORK(&priv->sync_channel, lbs_sync_channel_worker);
-       priv->mesh_open = 0;
-       sprintf(priv->mesh_ssid, "mesh");
-       priv->mesh_ssid_len = 4;
  
        priv->wol_criteria = 0xffffffff;
        priv->wol_gpio = 0xff;
@@@ -1076,6 -1056,17 +1057,17 @@@ void lbs_remove_card(struct lbs_privat
  EXPORT_SYMBOL_GPL(lbs_remove_card);
  
  
+ static int lbs_rtap_supported(struct lbs_private *priv)
+ {
+       if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5)
+               return 1;
+       /* newer firmware use a capability mask */
+       return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
+               (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK));
+ }
  int lbs_start_card(struct lbs_private *priv)
  {
        struct net_device *dev = priv->dev;
  
        lbs_update_channel(priv);
  
+       lbs_init_mesh(priv);
        /*
         * While rtap isn't related to mesh, only mesh-enabled
         * firmware implements the rtap functionality via
         * CMD_802_11_MONITOR_MODE.
         */
-       if (lbs_init_mesh(priv)) {
+       if (lbs_rtap_supported(priv)) {
                if (device_create_file(&dev->dev, &dev_attr_lbs_rtap))
                        lbs_pr_err("cannot register lbs_rtap attribute\n");
        }
@@@ -1134,7 -1127,9 +1128,9 @@@ void lbs_stop_card(struct lbs_private *
        netif_carrier_off(dev);
  
        lbs_debugfs_remove_one(priv);
-       if (lbs_deinit_mesh(priv))
+       lbs_deinit_mesh(priv);
+       if (lbs_rtap_supported(priv))
                device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
  
        /* Delete the timeout of the currently processing command */
@@@ -1178,7 -1173,7 +1174,7 @@@ void lbs_queue_event(struct lbs_privat
        if (priv->psstate == PS_STATE_SLEEP)
                priv->psstate = PS_STATE_AWAKE;
  
 -      __kfifo_put(priv->event_fifo, (unsigned char *) &event, sizeof(u32));
 +      kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32));
  
        wake_up_interruptible(&priv->waitq);
  
index 4b1aab593a84ea93fbfd7721d58004e65113fd26,f07ba0b6c5f1a390052993095d4e34ac2e6e16fe..71f88a08e0906200b6053fcb5875dbeed91da894
@@@ -192,7 -192,7 +192,7 @@@ static void copy_active_data_rates(stru
        lbs_deb_enter(LBS_DEB_WEXT);
  
        if ((priv->connect_status != LBS_CONNECTED) &&
-               (priv->mesh_connect_status != LBS_CONNECTED))
+               !lbs_mesh_connected(priv))
                memcpy(rates, lbs_bg_rates, MAX_RATES);
        else
                memcpy(rates, priv->curbssparams.rates, MAX_RATES);
@@@ -298,6 -298,7 +298,7 @@@ static int lbs_get_nick(struct net_devi
        return 0;
  }
  
+ #ifdef CONFIG_LIBERTAS_MESH
  static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
                         struct iw_point *dwrq, char *extra)
  {
  
        /* Use nickname to indicate that mesh is on */
  
-       if (priv->mesh_connect_status == LBS_CONNECTED) {
+       if (lbs_mesh_connected(priv)) {
                strncpy(extra, "Mesh", 12);
                extra[12] = '\0';
                dwrq->length = strlen(extra);
        lbs_deb_leave(LBS_DEB_WEXT);
        return 0;
  }
+ #endif
  
  static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
                        struct iw_param *vwrq, char *extra)
@@@ -422,6 -424,7 +424,7 @@@ static int lbs_get_mode(struct net_devi
        return 0;
  }
  
+ #ifdef CONFIG_LIBERTAS_MESH
  static int mesh_wlan_get_mode(struct net_device *dev,
                              struct iw_request_info *info, u32 * uwrq,
                              char *extra)
        lbs_deb_leave(LBS_DEB_WEXT);
        return 0;
  }
+ #endif
  
  static int lbs_get_txpow(struct net_device *dev,
                          struct iw_request_info *info,
@@@ -863,7 -867,7 +867,7 @@@ static struct iw_statistics *lbs_get_wi
  
        /* If we're not associated, all quality values are meaningless */
        if ((priv->connect_status != LBS_CONNECTED) &&
-           (priv->mesh_connect_status != LBS_CONNECTED))
+           !lbs_mesh_connected(priv))
                goto out;
  
        /* Quality by RSSI */
@@@ -1010,6 -1014,7 +1014,7 @@@ out
        return ret;
  }
  
+ #ifdef CONFIG_LIBERTAS_MESH
  static int lbs_mesh_set_freq(struct net_device *dev,
                             struct iw_request_info *info,
                             struct iw_freq *fwrq, char *extra)
@@@ -1061,6 -1066,7 +1066,7 @@@ out
        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
        return ret;
  }
+ #endif
  
  static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
                  struct iw_param *vwrq, char *extra)
@@@ -2025,8 -2031,10 +2031,8 @@@ static int lbs_get_essid(struct net_dev
        if (priv->connect_status == LBS_CONNECTED) {
                memcpy(extra, priv->curbssparams.ssid,
                       priv->curbssparams.ssid_len);
 -              extra[priv->curbssparams.ssid_len] = '\0';
        } else {
                memset(extra, 0, 32);
 -              extra[priv->curbssparams.ssid_len] = '\0';
        }
        /*
         * If none, we may want to get the one that was set
@@@ -2108,6 -2116,7 +2114,7 @@@ out
        return ret;
  }
  
+ #ifdef CONFIG_LIBERTAS_MESH
  static int lbs_mesh_get_essid(struct net_device *dev,
                              struct iw_request_info *info,
                              struct iw_point *dwrq, char *extra)
@@@ -2161,6 -2170,7 +2168,7 @@@ static int lbs_mesh_set_essid(struct ne
        lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
        return ret;
  }
+ #endif
  
  /**
   *  @brief Connect to the AP or Ad-hoc Network with specific bssid
@@@ -2267,7 -2277,13 +2275,13 @@@ static const iw_handler lbs_handler[] 
        (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
        (iw_handler) NULL,              /* SIOCSIWPMKSA */
  };
+ struct iw_handler_def lbs_handler_def = {
+       .num_standard   = ARRAY_SIZE(lbs_handler),
+       .standard       = (iw_handler *) lbs_handler,
+       .get_wireless_stats = lbs_get_wireless_stats,
+ };
  
+ #ifdef CONFIG_LIBERTAS_MESH
  static const iw_handler mesh_wlan_handler[] = {
        (iw_handler) NULL,      /* SIOCSIWCOMMIT */
        (iw_handler) lbs_get_name,      /* SIOCGIWNAME */
        (iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
        (iw_handler) NULL,              /* SIOCSIWPMKSA */
  };
- struct iw_handler_def lbs_handler_def = {
-       .num_standard   = ARRAY_SIZE(lbs_handler),
-       .standard       = (iw_handler *) lbs_handler,
-       .get_wireless_stats = lbs_get_wireless_stats,
- };
  
  struct iw_handler_def mesh_handler_def = {
        .num_standard   = ARRAY_SIZE(mesh_wlan_handler),
        .standard       = (iw_handler *) mesh_wlan_handler,
        .get_wireless_stats = lbs_get_wireless_stats,
  };
+ #endif
index 2f50a256efa5a00fa18b45e94ce9be3130f7628c,63511ca131b1bfe67c31058e0d60d14b8260b45d..6aeffbe9e40187ce03a4451d518e3f30a9adae5e
@@@ -395,6 -395,7 +395,7 @@@ static int wl1251_op_tx(struct ieee8021
         * the queue here, otherwise the queue will get too long.
         */
        if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) {
+               wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues");
                ieee80211_stop_queues(wl->hw);
  
                /*
@@@ -629,6 -630,10 +630,6 @@@ static int wl1251_op_config(struct ieee
                        goto out_sleep;
        }
  
 -      ret = wl1251_build_null_data(wl);
 -      if (ret < 0)
 -              goto out_sleep;
 -
        if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm enabled");
  
                 * through the bss_info_changed() hook.
                 */
                ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+               if (ret < 0)
+                       goto out_sleep;
        } else if (!(conf->flags & IEEE80211_CONF_PS) &&
                   wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm disabled");
  
                wl->psm_requested = false;
  
-               if (wl->psm)
+               if (wl->psm) {
                        ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
+                       if (ret < 0)
+                               goto out_sleep;
+               }
        }
  
        if (conf->power_level != wl->power_level) {
                ret = wl1251_acx_tx_power(wl, conf->power_level);
                if (ret < 0)
-                       goto out;
+                       goto out_sleep;
  
                wl->power_level = conf->power_level;
        }
@@@ -1106,21 -1116,6 +1112,21 @@@ static void wl1251_op_bss_info_changed(
        if (ret < 0)
                goto out;
  
 +      if (changed & BSS_CHANGED_BSSID) {
 +              memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 +
 +              ret = wl1251_build_null_data(wl);
 +              if (ret < 0)
 +                      goto out;
 +
 +              if (wl->bss_type != BSS_TYPE_IBSS) {
 +                      ret = wl1251_join(wl, wl->bss_type, wl->channel,
 +                                        wl->beacon_int, wl->dtim_period);
 +                      if (ret < 0)
 +                              goto out_sleep;
 +              }
 +      }
 +
        if (changed & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        wl->beacon_int = bss_conf->beacon_int;
                }
        }
  
 -      if (changed & BSS_CHANGED_BSSID) {
 -              memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 -
 -              ret = wl1251_build_null_data(wl);
 -              if (ret < 0)
 -                      goto out;
 -
 -              if (wl->bss_type != BSS_TYPE_IBSS) {
 -                      ret = wl1251_join(wl, wl->bss_type, wl->channel,
 -                                        wl->beacon_int, wl->dtim_period);
 -                      if (ret < 0)
 -                              goto out_sleep;
 -                      wl1251_warning("Set ctsprotect failed %d", ret);
 -                      goto out_sleep;
 -              }
 -      }
 -
        if (changed & BSS_CHANGED_BEACON) {
                beacon = ieee80211_beacon_get(hw, vif);
                ret = wl1251_cmd_template_set(wl, CMD_BEACON, beacon->data,
@@@ -1273,6 -1285,43 +1279,43 @@@ static struct ieee80211_channel wl1251_
        { .hw_value = 13, .center_freq = 2472},
  };
  
+ static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
+                            const struct ieee80211_tx_queue_params *params)
+ {
+       struct wl1251 *wl = hw->priv;
+       int ret;
+       mutex_lock(&wl->mutex);
+       wl1251_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);
+       ret = wl1251_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+       ret = wl1251_acx_ac_cfg(wl, wl1251_tx_get_queue(queue),
+                               params->cw_min, params->cw_max,
+                               params->aifs, params->txop);
+       if (ret < 0)
+               goto out_sleep;
+       ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue),
+                                CHANNEL_TYPE_EDCF,
+                                wl1251_tx_get_queue(queue),
+                                WL1251_ACX_PS_SCHEME_LEGACY,
+                                WL1251_ACX_ACK_POLICY_LEGACY);
+       if (ret < 0)
+               goto out_sleep;
+ out_sleep:
+       wl1251_ps_elp_sleep(wl);
+ out:
+       mutex_unlock(&wl->mutex);
+       return ret;
+ }
  /* can't be const, mac80211 writes to this */
  static struct ieee80211_supported_band wl1251_band_2ghz = {
        .channels = wl1251_channels,
@@@ -1293,6 -1342,7 +1336,7 @@@ static const struct ieee80211_ops wl125
        .hw_scan = wl1251_op_hw_scan,
        .bss_info_changed = wl1251_op_bss_info_changed,
        .set_rts_threshold = wl1251_op_set_rts_threshold,
+       .conf_tx = wl1251_op_conf_tx,
  };
  
  static int wl1251_register_hw(struct wl1251 *wl)
@@@ -1338,6 -1388,8 +1382,8 @@@ int wl1251_init_ieee80211(struct wl125
        wl->hw->wiphy->max_scan_ssids = 1;
        wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz;
  
+       wl->hw->queues = 4;
        ret = wl1251_register_hw(wl);
        if (ret)
                goto out;
index 163c840437d604ca9993b3fc70e3349a3fabd199,098bedcde9bba5ae5f496437f1549f77443df101..d62edc7df3ae79999f31bffb0adc26b1c924a820
@@@ -707,6 -707,10 +707,10 @@@ struct ieee80211_mgmt 
                                        u8 action;
                                        u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
                                } __attribute__ ((packed)) sa_query;
+                               struct {
+                                       u8 action;
+                                       u8 smps_control;
+                               } __attribute__ ((packed)) ht_smps;
                        } u;
                } __attribute__ ((packed)) action;
        } u;
@@@ -771,7 -775,10 +775,10 @@@ struct ieee80211_bar 
  /**
   * struct ieee80211_mcs_info - MCS information
   * @rx_mask: RX mask
-  * @rx_highest: highest supported RX rate
+  * @rx_highest: highest supported RX rate. If set represents
+  *    the highest supported RX data rate in units of 1 Mbps.
+  *    If this field is 0 this value should not be used to
+  *    consider the highest RX data rate supported.
   * @tx_params: TX parameters
   */
  struct ieee80211_mcs_info {
@@@ -824,6 -831,7 +831,7 @@@ struct ieee80211_ht_cap 
  #define IEEE80211_HT_CAP_LDPC_CODING          0x0001
  #define IEEE80211_HT_CAP_SUP_WIDTH_20_40      0x0002
  #define IEEE80211_HT_CAP_SM_PS                        0x000C
+ #define               IEEE80211_HT_CAP_SM_PS_SHIFT    2
  #define IEEE80211_HT_CAP_GRN_FLD              0x0010
  #define IEEE80211_HT_CAP_SGI_20                       0x0020
  #define IEEE80211_HT_CAP_SGI_40                       0x0040
  #define IEEE80211_HT_CAP_DELAY_BA             0x0400
  #define IEEE80211_HT_CAP_MAX_AMSDU            0x0800
  #define IEEE80211_HT_CAP_DSSSCCK40            0x1000
 -#define IEEE80211_HT_CAP_PSMP_SUPPORT         0x2000
 +#define IEEE80211_HT_CAP_RESERVED             0x2000
  #define IEEE80211_HT_CAP_40MHZ_INTOLERANT     0x4000
  #define IEEE80211_HT_CAP_LSIG_TXOP_PROT               0x8000
  
  /* 802.11n HT capability AMPDU settings (for ampdu_params_info) */
  #define IEEE80211_HT_AMPDU_PARM_FACTOR                0x03
  #define IEEE80211_HT_AMPDU_PARM_DENSITY               0x1C
+ #define               IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT   2
  
  /*
   * Maximum length of AMPDU that the STA can receive.
@@@ -922,12 -931,17 +931,17 @@@ struct ieee80211_ht_info 
  #define IEEE80211_MAX_AMPDU_BUF 0x40
  
  
- /* Spatial Multiplexing Power Save Modes */
+ /* Spatial Multiplexing Power Save Modes (for capability) */
  #define WLAN_HT_CAP_SM_PS_STATIC      0
  #define WLAN_HT_CAP_SM_PS_DYNAMIC     1
  #define WLAN_HT_CAP_SM_PS_INVALID     2
  #define WLAN_HT_CAP_SM_PS_DISABLED    3
  
+ /* for SM power control field lower two bits */
+ #define WLAN_HT_SMPS_CONTROL_DISABLED 0
+ #define WLAN_HT_SMPS_CONTROL_STATIC   1
+ #define WLAN_HT_SMPS_CONTROL_DYNAMIC  3
  /* Authentication algorithms */
  #define WLAN_AUTH_OPEN 0
  #define WLAN_AUTH_SHARED_KEY 1
@@@ -1150,6 -1164,18 +1164,18 @@@ enum ieee80211_spectrum_mgmt_actioncod
        WLAN_ACTION_SPCT_CHL_SWITCH = 4,
  };
  
+ /* HT action codes */
+ enum ieee80211_ht_actioncode {
+       WLAN_HT_ACTION_NOTIFY_CHANWIDTH = 0,
+       WLAN_HT_ACTION_SMPS = 1,
+       WLAN_HT_ACTION_PSMP = 2,
+       WLAN_HT_ACTION_PCO_PHASE = 3,
+       WLAN_HT_ACTION_CSI = 4,
+       WLAN_HT_ACTION_NONCOMPRESSED_BF = 5,
+       WLAN_HT_ACTION_COMPRESSED_BF = 6,
+       WLAN_HT_ACTION_ASEL_IDX_FEEDBACK = 7,
+ };
  /* Security key length */
  enum ieee80211_key_len {
        WLAN_KEY_LEN_WEP40 = 5,
diff --combined include/net/mac80211.h
index 538d6b761887d755470d33063a056d9aafcf5fdf,e6b6bf81d5b995612d91dd042d55c2a45a030375..494ac69ff477fe539c753344c7fb5af8d68154fd
@@@ -597,8 -597,10 +597,10 @@@ enum ieee80211_conf_flags 
   * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
   * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
   * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+  * @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
   */
  enum ieee80211_conf_changed {
+       IEEE80211_CONF_CHANGE_SMPS              = BIT(1),
        IEEE80211_CONF_CHANGE_LISTEN_INTERVAL   = BIT(2),
        IEEE80211_CONF_CHANGE_MONITOR           = BIT(3),
        IEEE80211_CONF_CHANGE_PS                = BIT(4),
        IEEE80211_CONF_CHANGE_IDLE              = BIT(8),
  };
  
+ /**
+  * enum ieee80211_smps_mode - spatial multiplexing power save mode
+  *
+  * @
+  */
+ enum ieee80211_smps_mode {
+       IEEE80211_SMPS_AUTOMATIC,
+       IEEE80211_SMPS_OFF,
+       IEEE80211_SMPS_STATIC,
+       IEEE80211_SMPS_DYNAMIC,
+       /* keep last */
+       IEEE80211_SMPS_NUM_MODES,
+ };
  /**
   * struct ieee80211_conf - configuration of the device
   *
   * @short_frame_max_tx_count: Maximum number of transmissions for a "short"
   *    frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
   *    number of transmissions not the number of retries
+  *
+  * @smps_mode: spatial multiplexing powersave mode; note that
+  *    %IEEE80211_SMPS_STATIC is used when the device is not
+  *    configured for an HT channel
   */
  struct ieee80211_conf {
        u32 flags;
  
        struct ieee80211_channel *channel;
        enum nl80211_channel_type channel_type;
+       enum ieee80211_smps_mode smps_mode;
  };
  
  /**
   * @type: type of this virtual interface
   * @bss_conf: BSS configuration for this interface, either our own
   *    or the BSS we're associated to
+  * @addr: address of this interface
   * @drv_priv: data area for driver use, will always be aligned to
   *    sizeof(void *).
   */
  struct ieee80211_vif {
        enum nl80211_iftype type;
        struct ieee80211_bss_conf bss_conf;
+       u8 addr[ETH_ALEN];
        /* must be last */
        u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
  };
@@@ -928,6 -952,16 +952,16 @@@ enum ieee80211_tkip_key_type 
   * @IEEE80211_HW_BEACON_FILTER:
   *    Hardware supports dropping of irrelevant beacon frames to
   *    avoid waking up cpu.
+  *
+  * @IEEE80211_HW_SUPPORTS_STATIC_SMPS:
+  *    Hardware supports static spatial multiplexing powersave,
+  *    ie. can turn off all but one chain even on HT connections
+  *    that should be using more chains.
+  *
+  * @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS:
+  *    Hardware supports dynamic spatial multiplexing powersave,
+  *    ie. can turn off all but one chain and then wake the rest
+  *    up as required after, for example, rts/cts handshake.
   */
  enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
        IEEE80211_HW_SUPPORTS_DYNAMIC_PS                = 1<<12,
        IEEE80211_HW_MFP_CAPABLE                        = 1<<13,
        IEEE80211_HW_BEACON_FILTER                      = 1<<14,
+       IEEE80211_HW_SUPPORTS_STATIC_SMPS               = 1<<15,
+       IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS              = 1<<16,
  };
  
  /**
@@@ -1212,6 -1248,31 +1248,31 @@@ ieee80211_get_alt_retry_rate(const stru
   * signal strength threshold checking.
   */
  
+ /**
+  * DOC: Spatial multiplexing power save
+  *
+  * SMPS (Spatial multiplexing power save) is a mechanism to conserve
+  * power in an 802.11n implementation. For details on the mechanism
+  * and rationale, please refer to 802.11 (as amended by 802.11n-2009)
+  * "11.2.3 SM power save".
+  *
+  * The mac80211 implementation is capable of sending action frames
+  * to update the AP about the station's SMPS mode, and will instruct
+  * the driver to enter the specific mode. It will also announce the
+  * requested SMPS mode during the association handshake. Hardware
+  * support for this feature is required, and can be indicated by
+  * hardware flags.
+  *
+  * The default mode will be "automatic", which nl80211/cfg80211
+  * defines to be dynamic SMPS in (regular) powersave, and SMPS
+  * turned off otherwise.
+  *
+  * To support this feature, the driver must set the appropriate
+  * hardware support flags, and handle the SMPS flag to the config()
+  * operation. It will then with this mechanism be instructed to
+  * enter the requested SMPS mode while associated to an HT AP.
+  */
  /**
   * DOC: Frame filtering
   *
@@@ -1737,12 -1798,6 +1798,12 @@@ static inline void ieee80211_rx_ni(stru
        local_bh_enable();
  }
  
 +/*
 + * The TX headroom reserved by mac80211 for its own tx_status functions.
 + * This is enough for the radiotap header.
 + */
 +#define IEEE80211_TX_STATUS_HEADROOM  13
 +
  /**
   * ieee80211_tx_status - transmit status callback
   *
diff --combined net/mac80211/cfg.c
index 6dc3579c0ac5444a0d8811504aefd6c620c9d1f1,f07c4abefe567ba26c9aaf493f7dd8903d5a6d41..63843e3e576a2164dc2d768f82b442448aa3ee3d
@@@ -150,7 -150,7 +150,7 @@@ static int ieee80211_add_key(struct wip
        rcu_read_lock();
  
        if (mac_addr) {
-               sta = sta_info_get(sdata->local, mac_addr);
+               sta = sta_info_get(sdata, mac_addr);
                if (!sta) {
                        ieee80211_key_free(key);
                        err = -ENOENT;
@@@ -181,7 -181,7 +181,7 @@@ static int ieee80211_del_key(struct wip
        if (mac_addr) {
                ret = -ENOENT;
  
-               sta = sta_info_get(sdata->local, mac_addr);
+               sta = sta_info_get(sdata, mac_addr);
                if (!sta)
                        goto out_unlock;
  
@@@ -228,7 -228,7 +228,7 @@@ static int ieee80211_get_key(struct wip
        rcu_read_lock();
  
        if (mac_addr) {
-               sta = sta_info_get(sdata->local, mac_addr);
+               sta = sta_info_get(sdata, mac_addr);
                if (!sta)
                        goto out;
  
@@@ -354,8 -354,7 +354,8 @@@ static void sta_set_sinfo(struct sta_in
        sinfo->rx_packets = sta->rx_packets;
        sinfo->tx_packets = sta->tx_packets;
  
 -      if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
 +      if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
 +          (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
                sinfo->filled |= STATION_INFO_SIGNAL;
                sinfo->signal = (s8)sta->last_signal;
        }
@@@ -415,15 -414,13 +415,13 @@@ static int ieee80211_dump_station(struc
  static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac, struct station_info *sinfo)
  {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct sta_info *sta;
        int ret = -ENOENT;
  
        rcu_read_lock();
  
-       /* XXX: verify sta->dev == dev */
-       sta = sta_info_get(local, mac);
+       sta = sta_info_get(sdata, mac);
        if (sta) {
                ret = 0;
                sta_set_sinfo(sta, sinfo);
@@@ -732,7 -729,7 +730,7 @@@ static int ieee80211_add_station(struc
        } else
                sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  
-       if (compare_ether_addr(mac, dev->dev_addr) == 0)
+       if (compare_ether_addr(mac, sdata->vif.addr) == 0)
                return -EINVAL;
  
        if (is_multicast_ether_addr(mac))
@@@ -779,8 -776,7 +777,7 @@@ static int ieee80211_del_station(struc
        if (mac) {
                rcu_read_lock();
  
-               /* XXX: get sta belonging to dev */
-               sta = sta_info_get(local, mac);
+               sta = sta_info_get(sdata, mac);
                if (!sta) {
                        rcu_read_unlock();
                        return -ENOENT;
@@@ -801,14 -797,14 +798,14 @@@ static int ieee80211_change_station(str
                                    u8 *mac,
                                    struct station_parameters *params)
  {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct sta_info *sta;
        struct ieee80211_sub_if_data *vlansdata;
  
        rcu_read_lock();
  
-       /* XXX: get sta belonging to dev */
-       sta = sta_info_get(local, mac);
+       sta = sta_info_get(sdata, mac);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
  static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *dst, u8 *next_hop)
  {
-       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
        struct sta_info *sta;
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  
        rcu_read_lock();
-       sta = sta_info_get(local, next_hop);
+       sta = sta_info_get(sdata, next_hop);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
@@@ -895,7 -890,6 +891,6 @@@ static int ieee80211_change_mpath(struc
                                    struct net_device *dev,
                                    u8 *dst, u8 *next_hop)
  {
-       struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
        struct mesh_path *mpath;
        struct sta_info *sta;
  
        rcu_read_lock();
  
-       sta = sta_info_get(local, next_hop);
+       sta = sta_info_get(sdata, next_hop);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
@@@ -1324,6 -1318,50 +1319,50 @@@ static int ieee80211_testmode_cmd(struc
  }
  #endif
  
+ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+                            enum ieee80211_smps_mode smps_mode)
+ {
+       const u8 *ap;
+       enum ieee80211_smps_mode old_req;
+       int err;
+       old_req = sdata->u.mgd.req_smps;
+       sdata->u.mgd.req_smps = smps_mode;
+       if (old_req == smps_mode &&
+           smps_mode != IEEE80211_SMPS_AUTOMATIC)
+               return 0;
+       /*
+        * If not associated, or current association is not an HT
+        * association, there's no need to send an action frame.
+        */
+       if (!sdata->u.mgd.associated ||
+           sdata->local->oper_channel_type == NL80211_CHAN_NO_HT) {
+               mutex_lock(&sdata->local->iflist_mtx);
+               ieee80211_recalc_smps(sdata->local, sdata);
+               mutex_unlock(&sdata->local->iflist_mtx);
+               return 0;
+       }
+       ap = sdata->u.mgd.associated->cbss.bssid;
+       if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
+               if (sdata->u.mgd.powersave)
+                       smps_mode = IEEE80211_SMPS_DYNAMIC;
+               else
+                       smps_mode = IEEE80211_SMPS_OFF;
+       }
+       /* send SM PS frame to AP */
+       err = ieee80211_send_smps_action(sdata, smps_mode,
+                                        ap, ap);
+       if (err)
+               sdata->u.mgd.req_smps = old_req;
+       return err;
+ }
  static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
                                    bool enabled, int timeout)
  {
        sdata->u.mgd.powersave = enabled;
        conf->dynamic_ps_timeout = timeout;
  
+       /* no change, but if automatic follow powersave */
+       mutex_lock(&sdata->u.mgd.mtx);
+       __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
+       mutex_unlock(&sdata->u.mgd.mtx);
        if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
  
@@@ -1356,15 -1399,25 +1400,25 @@@ static int ieee80211_set_bitrate_mask(s
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-       int i, err = -EINVAL;
+       int i;
        u32 target_rate;
        struct ieee80211_supported_band *sband;
  
+       /*
+        * This _could_ be supported by providing a hook for
+        * drivers for this function, but at this point it
+        * doesn't seem worth bothering.
+        */
+       if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
+               return -EOPNOTSUPP;
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
  
-       /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
+       /*
+        * target_rate = -1, rate->fixed = 0 means auto only, so use all rates
         * target_rate = X, rate->fixed = 1 means only rate X
-        * target_rate = X, rate->fixed = 0 means all rates <= X */
+        * target_rate = X, rate->fixed = 0 means all rates <= X
+        */
        sdata->max_ratectrl_rateidx = -1;
        sdata->force_unicast_rateidx = -1;
  
        else
                return 0;
  
-       for (i=0; i< sband->n_bitrates; i++) {
-               struct ieee80211_rate *brate = &sband->bitrates[i];
-               int this_rate = brate->bitrate;
+       for (i = 0; i< sband->n_bitrates; i++) {
+               if (target_rate != sband->bitrates[i].bitrate)
+                       continue;
  
-               if (target_rate == this_rate) {
-                       sdata->max_ratectrl_rateidx = i;
-                       if (mask->fixed)
-                               sdata->force_unicast_rateidx = i;
-                       err = 0;
-                       break;
-               }
+               /* requested bitrate found */
+               sdata->max_ratectrl_rateidx = i;
+               if (mask->fixed)
+                       sdata->force_unicast_rateidx = i;
+               return 0;
        }
  
-       return err;
+       return -EINVAL;
  }
  
  struct cfg80211_ops mac80211_config_ops = {
diff --combined net/mac80211/ht.c
index d7dcee68072820764d0910c79e740db052512073,63b8f86b7f162f4ac73e17049bf9c84dc6eb8683..bb677a73b7c9d67a623e962ad9419231bb11e3f4
@@@ -34,28 -34,9 +34,28 @@@ void ieee80211_ht_cap_ie_to_sta_ht_cap(
  
        ht_cap->ht_supported = true;
  
 -      ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & sband->ht_cap.cap;
 -      ht_cap->cap &= ~IEEE80211_HT_CAP_SM_PS;
 -      ht_cap->cap |= sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS;
 +      /*
 +       * The bits listed in this expression should be
 +       * the same for the peer and us, if the station
 +       * advertises more then we can't use those thus
 +       * we mask them out.
 +       */
 +      ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) &
 +              (sband->ht_cap.cap |
 +               ~(IEEE80211_HT_CAP_LDPC_CODING |
 +                 IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
 +                 IEEE80211_HT_CAP_GRN_FLD |
 +                 IEEE80211_HT_CAP_SGI_20 |
 +                 IEEE80211_HT_CAP_SGI_40 |
 +                 IEEE80211_HT_CAP_DSSSCCK40));
 +      /*
 +       * The STBC bits are asymmetric -- if we don't have
 +       * TX then mask out the peer's RX and vice versa.
 +       */
 +      if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
 +              ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC;
 +      if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
 +              ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
  
        ampdu_info = ht_cap_ie->ampdu_params_info;
        ht_cap->ampdu_factor =
@@@ -125,7 -106,7 +125,7 @@@ void ieee80211_send_delba(struct ieee80
  
        if (!skb) {
                printk(KERN_ERR "%s: failed to allocate buffer "
-                                       "for delba frame\n", sdata->dev->name);
+                                       "for delba frame\n", sdata->name);
                return;
        }
  
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
        memset(mgmt, 0, 24);
        memcpy(mgmt->da, da, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        if (sdata->vif.type == NL80211_IFTYPE_AP ||
            sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+               memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
        else if (sdata->vif.type == NL80211_IFTYPE_STATION)
                memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
  
@@@ -185,3 -166,50 +185,50 @@@ void ieee80211_process_delba(struct iee
                spin_unlock_bh(&sta->lock);
        }
  }
+ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+                              enum ieee80211_smps_mode smps, const u8 *da,
+                              const u8 *bssid)
+ {
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *action_frame;
+       /* 27 = header + category + action + smps mode */
+       skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
+       if (!skb)
+               return -ENOMEM;
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+       action_frame = (void *)skb_put(skb, 27);
+       memcpy(action_frame->da, da, ETH_ALEN);
+       memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(action_frame->bssid, bssid, ETH_ALEN);
+       action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_ACTION);
+       action_frame->u.action.category = WLAN_CATEGORY_HT;
+       action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
+       switch (smps) {
+       case IEEE80211_SMPS_AUTOMATIC:
+       case IEEE80211_SMPS_NUM_MODES:
+               WARN_ON(1);
+       case IEEE80211_SMPS_OFF:
+               action_frame->u.action.u.ht_smps.smps_control =
+                               WLAN_HT_SMPS_CONTROL_DISABLED;
+               break;
+       case IEEE80211_SMPS_STATIC:
+               action_frame->u.action.u.ht_smps.smps_control =
+                               WLAN_HT_SMPS_CONTROL_STATIC;
+               break;
+       case IEEE80211_SMPS_DYNAMIC:
+               action_frame->u.action.u.ht_smps.smps_control =
+                               WLAN_HT_SMPS_CONTROL_DYNAMIC;
+               break;
+       }
+       /* we'll do more on status of this frame */
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+       ieee80211_tx_skb(sdata, skb);
+       return 0;
+ }
index 91dc8636d64481bfefe15c063428f0df018b83a1,6fb3f7181536c7799409adaa07a261ce001109f3..88b0ba6c74842b0120d27eda7c31efa1e8441542
@@@ -140,7 -140,6 +140,6 @@@ typedef unsigned __bitwise__ ieee80211_
  
  struct ieee80211_tx_data {
        struct sk_buff *skb;
-       struct net_device *dev;
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
@@@ -298,6 -297,8 +297,8 @@@ struct ieee80211_if_managed 
  
        unsigned long timers_running; /* used for quiesce/restart */
        bool powersave; /* powersave requested for this iface */
+       enum ieee80211_smps_mode req_smps, /* requested smps mode */
+                                ap_smps; /* smps mode AP thinks we're in */
  
        unsigned long request;
  
@@@ -433,6 -434,8 +434,8 @@@ struct ieee80211_sub_if_data 
  
        int drop_unencrypted;
  
+       char name[IFNAMSIZ];
        /*
         * keep track of whether the HT opmode (stored in
         * vif.bss_info.ht_operation_mode) is valid.
@@@ -586,6 -589,9 +589,9 @@@ struct ieee80211_local 
        /* used for uploading changed mc list */
        struct work_struct reconfig_filter;
  
+       /* used to reconfigure hardware SM PS */
+       struct work_struct recalc_smps;
        /* aggregated multicast list */
        struct dev_addr_list *mc_list;
        int mc_count;
        unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
  
        bool pspolling;
 +      bool scan_ps_enabled;
        /*
         * PS can only be enabled when we have exactly one managed
         * interface (and monitors) in PS, this then points there.
        int user_power_level; /* in dBm */
        int power_constr_level; /* in dBm */
  
+       enum ieee80211_smps_mode smps_mode;
        struct work_struct restart_work;
  
  #ifdef CONFIG_MAC80211_DEBUGFS
@@@ -874,6 -881,8 +882,8 @@@ void ieee80211_bss_info_change_notify(s
  void ieee80211_configure_filter(struct ieee80211_local *local);
  u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
  
+ extern bool ieee80211_disable_40mhz_24ghz;
  /* STA code */
  void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
  int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
@@@ -938,6 -947,8 +948,8 @@@ void ieee80211_rx_bss_put(struct ieee80
                          struct ieee80211_bss *bss);
  
  /* interface handling */
+ int ieee80211_iface_init(void);
+ void ieee80211_iface_exit(void);
  int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                     struct net_device **new_dev, enum nl80211_iftype type,
                     struct vif_params *params);
@@@ -976,6 -987,9 +988,9 @@@ void ieee80211_send_bar(struct ieee8021
  void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
                          const u8 *da, u16 tid,
                          u16 initiator, u16 reason_code);
+ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
+                              enum ieee80211_smps_mode smps, const u8 *da,
+                              const u8 *bssid);
  
  void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *da,
                                u16 tid, u16 initiator, u16 reason);
@@@ -1086,6 -1100,10 +1101,10 @@@ void ieee80211_sta_def_wmm_params(struc
  u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
                            struct ieee802_11_elems *elems,
                            enum ieee80211_band band);
+ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
+                            enum ieee80211_smps_mode smps_mode);
+ void ieee80211_recalc_smps(struct ieee80211_local *local,
+                          struct ieee80211_sub_if_data *forsdata);
  
  #ifdef CONFIG_MAC80211_NOINLINE
  #define debug_noinline noinline
diff --combined net/mac80211/main.c
index 0d2d94881f1f14fd3d42b692c144fea3b66909e3,25f52098b636f8a934469499e54e060e1ef91c3b..d4426748ab10c857a3b690f0953b413360f33609
  #include "led.h"
  #include "cfg.h"
  #include "debugfs.h"
- #include "debugfs_netdev.h"
+ bool ieee80211_disable_40mhz_24ghz;
+ module_param(ieee80211_disable_40mhz_24ghz, bool, 0644);
+ MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz,
+                "Disable 40MHz support in the 2.4GHz band");
  
  void ieee80211_configure_filter(struct ieee80211_local *local)
  {
@@@ -114,6 -119,18 +119,18 @@@ int ieee80211_hw_config(struct ieee8021
                changed |= IEEE80211_CONF_CHANGE_CHANNEL;
        }
  
+       if (!conf_is_ht(&local->hw.conf)) {
+               /*
+                * mac80211.h documents that this is only valid
+                * when the channel is set to an HT type, and
+                * that otherwise STATIC is used.
+                */
+               local->hw.conf.smps_mode = IEEE80211_SMPS_STATIC;
+       } else if (local->hw.conf.smps_mode != local->smps_mode) {
+               local->hw.conf.smps_mode = local->smps_mode;
+               changed |= IEEE80211_CONF_CHANGE_SMPS;
+       }
        if (scan_chan)
                power = chan->max_power;
        else
@@@ -173,7 -190,7 +190,7 @@@ void ieee80211_bss_info_change_notify(s
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
                sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
        else if (sdata->vif.type == NL80211_IFTYPE_AP)
-               sdata->vif.bss_conf.bssid = sdata->dev->dev_addr;
+               sdata->vif.bss_conf.bssid = sdata->vif.addr;
        else if (ieee80211_vif_is_mesh(&sdata->vif)) {
                sdata->vif.bss_conf.bssid = zero;
        } else {
                }
        }
  
-       drv_bss_info_changed(local, &sdata->vif,
-                            &sdata->vif.bss_conf, changed);
+       drv_bss_info_changed(local, sdata, &sdata->vif.bss_conf, changed);
  }
  
  u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
@@@ -299,6 -315,16 +315,16 @@@ void ieee80211_restart_hw(struct ieee80
  }
  EXPORT_SYMBOL(ieee80211_restart_hw);
  
+ static void ieee80211_recalc_smps_work(struct work_struct *work)
+ {
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, recalc_smps);
+       mutex_lock(&local->iflist_mtx);
+       ieee80211_recalc_smps(local, NULL);
+       mutex_unlock(&local->iflist_mtx);
+ }
  struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                                        const struct ieee80211_ops *ops)
  {
        INIT_WORK(&local->restart_work, ieee80211_restart_work);
  
        INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
+       INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
+       local->smps_mode = IEEE80211_SMPS_OFF;
  
        INIT_WORK(&local->dynamic_ps_enable_work,
                  ieee80211_dynamic_ps_enable_work);
@@@ -515,8 -543,6 +543,8 @@@ int ieee80211_register_hw(struct ieee80
         * and we need some headroom for passing the frame to monitor
         * interfaces, but never both at the same time.
         */
 +      BUILD_BUG_ON(IEEE80211_TX_STATUS_HEADROOM !=
 +                      sizeof(struct ieee80211_tx_status_rtap_hdr));
        local->tx_headroom = max_t(unsigned int , local->hw.extra_tx_headroom,
                                   sizeof(struct ieee80211_tx_status_rtap_hdr));
  
@@@ -674,11 -700,19 +702,19 @@@ static int __init ieee80211_init(void
  
        ret = rc80211_pid_init();
        if (ret)
-               return ret;
+               goto err_pid;
  
-       ieee80211_debugfs_netdev_init();
+       ret = ieee80211_iface_init();
+       if (ret)
+               goto err_netdev;
  
        return 0;
+  err_netdev:
+       rc80211_pid_exit();
+  err_pid:
+       rc80211_minstrel_exit();
+       return ret;
  }
  
  static void __exit ieee80211_exit(void)
        if (mesh_allocated)
                ieee80211s_stop();
  
-       ieee80211_debugfs_netdev_exit();
+       ieee80211_iface_exit();
  }
  
  
diff --combined net/mac80211/mesh.c
index 6a43314295988c76067017be5d19de785e404d54,63299b72a7b075df7935fe91b9f5911d25df956e..e0bd85e3d4b64f3137ba58fa8026bda7c023625a
@@@ -427,7 -427,7 +427,7 @@@ int ieee80211_new_mesh_header(struct ie
                char *addr5, char *addr6)
  {
        int aelen = 0;
 -      memset(meshhdr, 0, sizeof(meshhdr));
 +      memset(meshhdr, 0, sizeof(*meshhdr));
        meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
        put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
        sdata->u.mesh.mesh_seqnum++;
@@@ -457,7 -457,7 +457,7 @@@ static void ieee80211_mesh_housekeeping
  
  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
        printk(KERN_DEBUG "%s: running mesh housekeeping\n",
-              sdata->dev->name);
+              sdata->name);
  #endif
  
        ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT);
@@@ -565,7 -565,7 +565,7 @@@ static void ieee80211_mesh_rx_bcn_presp
  
        /* ignore ProbeResp to foreign address */
        if (stype == IEEE80211_STYPE_PROBE_RESP &&
-           compare_ether_addr(mgmt->da, sdata->dev->dev_addr))
+           compare_ether_addr(mgmt->da, sdata->vif.addr))
                return;
  
        baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
diff --combined net/mac80211/mesh_hwmp.c
index d28acb6b1f8151e2a822ec5fc697231b5d76e698,664f5cc2b273c4ca284defecd35a9e3190c1567b..ce84237ebad3b493d5c5d8dc3ea18662395dbfb7
@@@ -128,9 -128,9 +128,9 @@@ static int mesh_path_sel_frame_tx(enum 
                                          IEEE80211_STYPE_ACTION);
  
        memcpy(mgmt->da, da, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        /* BSSID == SA */
-       memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
        mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
        mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION;
  
@@@ -222,7 -222,7 +222,7 @@@ int mesh_path_error_tx(u8 ttl, u8 *targ
                                          IEEE80211_STYPE_ACTION);
  
        memcpy(mgmt->da, ra, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        /* BSSID is left zeroed, wildcard value */
        mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
        mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION;
@@@ -335,7 -335,7 +335,7 @@@ static u32 hwmp_route_info_get(struct i
        bool process = true;
  
        rcu_read_lock();
-       sta = sta_info_get(local, mgmt->sa);
+       sta = sta_info_get(sdata, mgmt->sa);
        if (!sta) {
                rcu_read_unlock();
                return 0;
                new_metric = MAX_METRIC;
        exp_time = TU_TO_EXP_TIME(orig_lifetime);
  
-       if (memcmp(orig_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) {
+       if (memcmp(orig_addr, sdata->vif.addr, ETH_ALEN) == 0) {
                /* This MP is the originator, we are not interested in this
                 * frame, except for updating transmitter's path info.
                 */
@@@ -486,7 -486,7 +486,7 @@@ static void hwmp_preq_frame_process(str
  
        mhwmp_dbg("received PREQ from %pM\n", orig_addr);
  
-       if (memcmp(target_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) {
+       if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0) {
                mhwmp_dbg("PREQ is for us\n");
                forward = false;
                reply = true;
@@@ -579,7 -579,7 +579,7 @@@ static void hwmp_prep_frame_process(str
         * replies
         */
        target_addr = PREP_IE_TARGET_ADDR(prep_elem);
-       if (memcmp(target_addr, sdata->dev->dev_addr, ETH_ALEN) == 0)
+       if (memcmp(target_addr, sdata->vif.addr, ETH_ALEN) == 0)
                /* destination, no forwarding required */
                return;
  
@@@ -890,7 -890,7 +890,7 @@@ void mesh_path_start_discovery(struct i
                target_flags = MP_F_RF;
  
        spin_unlock_bh(&mpath->state_lock);
-       mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->dev->dev_addr,
+       mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr,
                        cpu_to_le32(ifmsh->sn), target_flags, mpath->dst,
                        cpu_to_le32(mpath->sn), broadcast_addr, 0,
                        ttl, cpu_to_le32(lifetime), 0,
@@@ -937,9 -937,9 +937,9 @@@ int mesh_nexthop_lookup(struct sk_buff 
  
        if (mpath->flags & MESH_PATH_ACTIVE) {
                if (time_after(jiffies,
 -                             mpath->exp_time +
 +                             mpath->exp_time -
                               msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) &&
-                   !memcmp(sdata->dev->dev_addr, hdr->addr4, ETH_ALEN) &&
+                   !memcmp(sdata->vif.addr, hdr->addr4, ETH_ALEN) &&
                    !(mpath->flags & MESH_PATH_RESOLVING) &&
                    !(mpath->flags & MESH_PATH_FIXED)) {
                        mesh_queue_preq(mpath,
@@@ -1010,7 -1010,7 +1010,7 @@@ mesh_path_tx_root_frame(struct ieee8021
  {
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
  
-       mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->dev->dev_addr,
+       mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->vif.addr,
                               cpu_to_le32(++ifmsh->sn),
                               0, NULL, 0, broadcast_addr,
                               0, MESH_TTL, 0, 0, 0, sdata);
index 0192cfdacae48cfefb84ea2c1fe73b51a7968277,fbef678f64c8473269b561156e329b4702ea3c2f..2312efe04c62ea7116ca6de8302fa76f47b3e56a
@@@ -244,7 -244,7 +244,7 @@@ struct mesh_path *mesh_path_lookup_by_i
   * @addr: destination address of the path (ETH_ALEN length)
   * @sdata: local subif
   *
 - * Returns: 0 on sucess
 + * Returns: 0 on success
   *
   * State: the initial state of the new path is set to 0
   */
@@@ -260,7 -260,7 +260,7 @@@ int mesh_path_add(u8 *dst, struct ieee8
        int err = 0;
        u32 hash_idx;
  
-       if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
+       if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0)
                /* never add ourselves as neighbours */
                return -ENOTSUPP;
  
@@@ -377,7 -377,7 +377,7 @@@ int mpp_path_add(u8 *dst, u8 *mpp, stru
        int err = 0;
        u32 hash_idx;
  
-       if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
+       if (memcmp(dst, sdata->vif.addr, ETH_ALEN) == 0)
                /* never add ourselves as neighbours */
                return -ENOTSUPP;
  
@@@ -532,7 -532,7 +532,7 @@@ static void mesh_path_node_reclaim(stru
   * @addr: dst address (ETH_ALEN length)
   * @sdata: local subif
   *
 - * Returns: 0 if succesful
 + * Returns: 0 if successful
   */
  int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
  {
@@@ -605,7 -605,7 +605,7 @@@ void mesh_path_discard_frame(struct sk_
        struct mesh_path *mpath;
        u32 sn = 0;
  
-       if (memcmp(hdr->addr4, sdata->dev->dev_addr, ETH_ALEN) != 0) {
+       if (memcmp(hdr->addr4, sdata->vif.addr, ETH_ALEN) != 0) {
                u8 *ra, *da;
  
                da = hdr->addr3;
diff --combined net/mac80211/mlme.c
index c79e59f82fd9933bd98024d4d9b8551244c1f4a6,5174bfc5710dc662d396e7f4456f539fbc42573d..2f9ed8b9c3f00bd7c6522151bd69e806e3448658
@@@ -75,6 -75,9 +75,9 @@@ enum rx_mgmt_action 
        /* caller must call cfg80211_send_disassoc() */
        RX_MGMT_CFG80211_DISASSOC,
  
+       /* caller must tell cfg80211 about internal error */
+       RX_MGMT_CFG80211_ASSOC_ERROR,
        /* caller must call cfg80211_auth_timeout() & free work */
        RX_MGMT_CFG80211_AUTH_TO,
  
@@@ -202,7 -205,7 +205,7 @@@ static u32 ieee80211_enable_ht(struct i
                ieee80211_hw_config(local, 0);
  
                rcu_read_lock();
-               sta = sta_info_get(local, bssid);
+               sta = sta_info_get(sdata, bssid);
                if (sta)
                        rate_control_rate_update(local, sband, sta,
                                                 IEEE80211_RC_HT_CHANGED);
@@@ -248,7 -251,7 +251,7 @@@ static void ieee80211_send_assoc(struc
                            wk->ssid_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
-                      "frame\n", sdata->dev->name);
+                      "frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
        memset(mgmt, 0, 24);
        memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN);
  
        if (!is_zero_ether_addr(wk->prev_bssid)) {
                __le16 tmp;
                u32 flags = local->hw.conf.channel->flags;
  
+               /* determine capability flags */
+               if (ieee80211_disable_40mhz_24ghz &&
+                   sband->band == IEEE80211_BAND_2GHZ) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
                switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
                case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
                        if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
                        break;
                }
  
-               tmp = cpu_to_le16(cap);
-               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2);
+               /* set SM PS mode properly */
+               cap &= ~IEEE80211_HT_CAP_SM_PS;
+               /* new association always uses requested smps mode */
+               if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
+                       if (ifmgd->powersave)
+                               ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
+                       else
+                               ifmgd->ap_smps = IEEE80211_SMPS_OFF;
+               } else
+                       ifmgd->ap_smps = ifmgd->req_smps;
+               switch (ifmgd->ap_smps) {
+               case IEEE80211_SMPS_AUTOMATIC:
+               case IEEE80211_SMPS_NUM_MODES:
+                       WARN_ON(1);
+               case IEEE80211_SMPS_OFF:
+                       cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
+                               IEEE80211_HT_CAP_SM_PS_SHIFT;
+                       break;
+               case IEEE80211_SMPS_STATIC:
+                       cap |= WLAN_HT_CAP_SM_PS_STATIC <<
+                               IEEE80211_HT_CAP_SM_PS_SHIFT;
+                       break;
+               case IEEE80211_SMPS_DYNAMIC:
+                       cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
+                               IEEE80211_HT_CAP_SM_PS_SHIFT;
+                       break;
+               }
+               /* reserve and fill IE */
+               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
                *pos++ = WLAN_EID_HT_CAPABILITY;
                *pos++ = sizeof(struct ieee80211_ht_cap);
                memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+               /* capability flags */
+               tmp = cpu_to_le16(cap);
                memcpy(pos, &tmp, sizeof(u16));
                pos += sizeof(u16);
-               /* TODO: needs a define here for << 2 */
+               /* AMPDU parameters */
                *pos++ = sband->ht_cap.ampdu_factor |
-                        (sband->ht_cap.ampdu_density << 2);
+                        (sband->ht_cap.ampdu_density <<
+                               IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+               /* MCS set */
                memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+               pos += sizeof(sband->ht_cap.mcs);
+               /* extended capabilities */
+               pos += sizeof(__le16);
+               /* BF capabilities */
+               pos += sizeof(__le32);
+               /* antenna selection */
+               pos += sizeof(u8);
        }
  
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@@ -443,7 -501,7 +501,7 @@@ static void ieee80211_send_deauth_disas
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for "
-                      "deauth/disassoc frame\n", sdata->dev->name);
+                      "deauth/disassoc frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
        mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
        memset(mgmt, 0, 24);
        memcpy(mgmt->da, bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        memcpy(mgmt->bssid, bssid, ETH_ALEN);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
        skb_put(skb, 2);
@@@ -484,7 -542,7 +542,7 @@@ void ieee80211_send_pspoll(struct ieee8
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll));
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for "
-                      "pspoll frame\n", sdata->dev->name);
+                      "pspoll frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
        pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14);
  
        memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN);
-       memcpy(pspoll->ta, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(pspoll->ta, sdata->vif.addr, ETH_ALEN);
  
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
@@@ -519,7 -577,7 +577,7 @@@ void ieee80211_send_nullfunc(struct iee
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
-                      "frame\n", sdata->dev->name);
+                      "frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
                fc |= cpu_to_le16(IEEE80211_FCTL_PM);
        nullfunc->frame_control = fc;
        memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
-       memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
        memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
  
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@@ -915,14 -973,6 +973,14 @@@ static void ieee80211_set_associated(st
        sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
                                IEEE80211_STA_BEACON_POLL);
  
 +      /*
 +       * Always handle WMM once after association regardless
 +       * of the first value the AP uses. Setting -1 here has
 +       * that effect because the AP values is an unsigned
 +       * 4-bit value.
 +       */
 +      sdata->u.mgd.wmm_last_param_set = -1;
 +
        ieee80211_led_assoc(local, 1);
  
        sdata->vif.bss_conf.assoc = 1;
  
        mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_smps(local, sdata);
        mutex_unlock(&local->iflist_mtx);
  
        netif_start_queue(sdata->dev);
@@@ -956,7 -1007,7 +1015,7 @@@ ieee80211_direct_probe(struct ieee80211
        wk->tries++;
        if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
                printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
-                      sdata->dev->name, wk->bss->cbss.bssid);
+                      sdata->name, wk->bss->cbss.bssid);
  
                /*
                 * Most likely AP is not in the range so remove the
        }
  
        printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
-                       sdata->dev->name, wk->bss->cbss.bssid,
+                       sdata->name, wk->bss->cbss.bssid,
                        wk->tries);
  
        /*
@@@ -1001,7 -1052,7 +1060,7 @@@ ieee80211_authenticate(struct ieee80211
        if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
                printk(KERN_DEBUG "%s: authentication with AP %pM"
                       " timed out\n",
-                      sdata->dev->name, wk->bss->cbss.bssid);
+                      sdata->name, wk->bss->cbss.bssid);
  
                /*
                 * Most likely AP is not in the range so remove the
        }
  
        printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
-              sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
+              sdata->name, wk->bss->cbss.bssid, wk->tries);
  
        ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len,
                            wk->bss->cbss.bssid, NULL, 0, 0);
@@@ -1078,7 -1129,7 +1137,7 @@@ static void ieee80211_set_disassoc(stru
        netif_carrier_off(sdata->dev);
  
        rcu_read_lock();
-       sta = sta_info_get(local, bssid);
+       sta = sta_info_get(sdata, bssid);
        if (sta)
                ieee80211_sta_tear_down_BA_sessions(sta);
        rcu_read_unlock();
  
        ieee80211_set_wmm_default(sdata);
  
 -      ieee80211_recalc_idle(local);
 -
        /* channel(_type) changes are handled by ieee80211_hw_config */
        local->oper_channel_type = NL80211_CHAN_NO_HT;
  
  
        rcu_read_lock();
  
-       sta = sta_info_get(local, bssid);
+       sta = sta_info_get(sdata, bssid);
        if (!sta) {
                rcu_read_unlock();
                return;
@@@ -1139,7 -1192,7 +1198,7 @@@ ieee80211_associate(struct ieee80211_su
        if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) {
                printk(KERN_DEBUG "%s: association with AP %pM"
                       " timed out\n",
-                      sdata->dev->name, wk->bss->cbss.bssid);
+                      sdata->name, wk->bss->cbss.bssid);
  
                /*
                 * Most likely AP is not in the range so remove the
        }
  
        printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
-              sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
+              sdata->name, wk->bss->cbss.bssid, wk->tries);
        ieee80211_send_assoc(sdata, wk);
  
        wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
@@@ -1218,7 -1271,7 +1277,7 @@@ static void ieee80211_mgd_probe_ap(stru
  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
        if (beacon && net_ratelimit())
                printk(KERN_DEBUG "%s: detected beacon loss from AP "
-                      "- sending probe request\n", sdata->dev->name);
+                      "- sending probe request\n", sdata->name);
  #endif
  
        /*
@@@ -1275,7 -1328,7 +1334,7 @@@ static void ieee80211_auth_completed(st
                                     struct ieee80211_mgd_work *wk)
  {
        wk->state = IEEE80211_MGD_STATE_IDLE;
-       printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
+       printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
  }
  
  
@@@ -1372,11 -1425,10 +1431,11 @@@ ieee80211_rx_mgmt_deauth(struct ieee802
        reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
  
        printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
-                       sdata->dev->name, bssid, reason_code);
+                       sdata->name, bssid, reason_code);
  
        if (!wk) {
                ieee80211_set_disassoc(sdata, true);
 +              ieee80211_recalc_idle(sdata->local);
        } else {
                list_del(&wk->list);
                kfree(wk);
@@@ -1407,10 -1459,9 +1466,10 @@@ ieee80211_rx_mgmt_disassoc(struct ieee8
        reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
  
        printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
-                       sdata->dev->name, mgmt->sa, reason_code);
+                       sdata->name, mgmt->sa, reason_code);
  
        ieee80211_set_disassoc(sdata, false);
 +      ieee80211_recalc_idle(sdata->local);
        return RX_MGMT_CFG80211_DISASSOC;
  }
  
@@@ -1431,8 -1482,8 +1490,8 @@@ ieee80211_rx_mgmt_assoc_resp(struct iee
        struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
        u8 *pos;
        u32 changed = 0;
-       int i, j;
-       bool have_higher_than_11mbit = false, newsta = false;
+       int i, j, err;
+       bool have_higher_than_11mbit = false;
        u16 ap_ht_cap_flags;
  
        /*
  
        printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
               "status=%d aid=%d)\n",
-              sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa,
+              sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
               capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
  
        pos = mgmt->u.assoc_resp.variable;
                ms = tu * 1024 / 1000;
                printk(KERN_DEBUG "%s: AP rejected association temporarily; "
                       "comeback duration %u TU (%u ms)\n",
-                      sdata->dev->name, tu, ms);
+                      sdata->name, tu, ms);
                wk->timeout = jiffies + msecs_to_jiffies(ms);
                if (ms > IEEE80211_ASSOC_TIMEOUT)
                        run_again(ifmgd, jiffies + msecs_to_jiffies(ms));
  
        if (status_code != WLAN_STATUS_SUCCESS) {
                printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
-                      sdata->dev->name, status_code);
+                      sdata->name, status_code);
                wk->state = IEEE80211_MGD_STATE_IDLE;
                return RX_MGMT_CFG80211_ASSOC;
        }
  
        if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
                printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
-                      "set\n", sdata->dev->name, aid);
+                      "set\n", sdata->name, aid);
        aid &= ~(BIT(15) | BIT(14));
  
        if (!elems.supp_rates) {
                printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
-                      sdata->dev->name);
+                      sdata->name);
                return RX_MGMT_NONE;
        }
  
-       printk(KERN_DEBUG "%s: associated\n", sdata->dev->name);
+       printk(KERN_DEBUG "%s: associated\n", sdata->name);
        ifmgd->aid = aid;
  
-       rcu_read_lock();
-       /* Add STA entry for the AP */
-       sta = sta_info_get(local, wk->bss->cbss.bssid);
+       sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
        if (!sta) {
-               newsta = true;
-               rcu_read_unlock();
-               sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
-               if (!sta) {
-                       printk(KERN_DEBUG "%s: failed to alloc STA entry for"
-                              " the AP\n", sdata->dev->name);
-                       return RX_MGMT_NONE;
-               }
-               set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
-                                  WLAN_STA_ASSOC_AP);
-               if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
-                       set_sta_flags(sta, WLAN_STA_AUTHORIZED);
-               rcu_read_lock();
+               printk(KERN_DEBUG "%s: failed to alloc STA entry for"
+                      " the AP\n", sdata->name);
+               return RX_MGMT_CFG80211_ASSOC_ERROR;
        }
  
+       set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
+                          WLAN_STA_ASSOC_AP);
+       if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
+               set_sta_flags(sta, WLAN_STA_AUTHORIZED);
        rates = 0;
        basic_rates = 0;
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
        if (elems.wmm_param)
                set_sta_flags(sta, WLAN_STA_WME);
  
-       if (newsta) {
-               int err = sta_info_insert(sta);
-               if (err) {
-                       printk(KERN_DEBUG "%s: failed to insert STA entry for"
-                              " the AP (error %d)\n", sdata->dev->name, err);
-                       rcu_read_unlock();
-                       return RX_MGMT_NONE;
-               }
+       err = sta_info_insert(sta);
+       sta = NULL;
+       if (err) {
+               printk(KERN_DEBUG "%s: failed to insert STA entry for"
+                      " the AP (error %d)\n", sdata->name, err);
+               return RX_MGMT_CFG80211_ASSOC_ERROR;
        }
  
-       rcu_read_unlock();
        if (elems.wmm_param)
                ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
                                         elems.wmm_param_len);
@@@ -1679,7 -1714,7 +1722,7 @@@ static void ieee80211_rx_mgmt_probe_res
  
        ASSERT_MGD_MTX(ifmgd);
  
-       if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
+       if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN))
                return; /* ignore ProbeResp to foreign address */
  
        baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
        /* direct probe may be part of the association flow */
        if (wk && wk->state == IEEE80211_MGD_STATE_PROBE) {
                printk(KERN_DEBUG "%s: direct probe responded\n",
-                      sdata->dev->name);
+                      sdata->name);
                wk->tries = 0;
                wk->state = IEEE80211_MGD_STATE_AUTH;
                WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
@@@ -1787,7 -1822,7 +1830,7 @@@ static void ieee80211_rx_mgmt_beacon(st
  #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                if (net_ratelimit()) {
                        printk(KERN_DEBUG "%s: cancelling probereq poll due "
-                              "to a received beacon\n", sdata->dev->name);
+                              "to a received beacon\n", sdata->name);
                }
  #endif
                ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
  
                rcu_read_lock();
  
-               sta = sta_info_get(local, bssid);
+               sta = sta_info_get(sdata, bssid);
                if (WARN_ON(!sta)) {
                        rcu_read_unlock();
                        return;
@@@ -2036,6 -2071,10 +2079,10 @@@ static void ieee80211_sta_rx_queued_mgm
        case RX_MGMT_CFG80211_DEAUTH:
                cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
                break;
+       case RX_MGMT_CFG80211_ASSOC_ERROR:
+               /* an internal error -- pretend timeout for now */
+               cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
+               break;
        default:
                WARN(1, "unexpected: %d", rma);
        }
@@@ -2125,7 -2164,6 +2172,7 @@@ static void ieee80211_sta_work(struct w
                                " after %dms, disconnecting.\n",
                                bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
                        ieee80211_set_disassoc(sdata, true);
 +                      ieee80211_recalc_idle(local);
                        mutex_unlock(&ifmgd->mtx);
                        /*
                         * must be outside lock due to cfg80211,
@@@ -2336,6 -2374,11 +2383,11 @@@ void ieee80211_sta_setup_sdata(struct i
                ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
  
        mutex_init(&ifmgd->mtx);
+       if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
+               ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
+       else
+               ifmgd->req_smps = IEEE80211_SMPS_OFF;
  }
  
  /* scan finished notification */
@@@ -2563,14 -2606,12 +2615,14 @@@ int ieee80211_mgd_deauth(struct ieee802
        mutex_unlock(&ifmgd->mtx);
  
        printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
-              sdata->dev->name, bssid, req->reason_code);
+              sdata->name, bssid, req->reason_code);
  
        ieee80211_send_deauth_disassoc(sdata, bssid,
                        IEEE80211_STYPE_DEAUTH, req->reason_code,
                        cookie);
  
 +      ieee80211_recalc_idle(sdata->local);
 +
        return 0;
  }
  
@@@ -2594,7 -2635,7 +2646,7 @@@ int ieee80211_mgd_disassoc(struct ieee8
        }
  
        printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
-              sdata->dev->name, req->bss->bssid, req->reason_code);
+              sdata->name, req->bss->bssid, req->reason_code);
  
        ieee80211_set_disassoc(sdata, false);
  
        ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
                        IEEE80211_STYPE_DISASSOC, req->reason_code,
                        cookie);
 +
 +      ieee80211_recalc_idle(sdata->local);
 +
        return 0;
  }
diff --combined net/mac80211/rx.c
index 9f2807aeaf52d6a7c3ed00134051bf050c2ec5fe,a182e423109bc471d61a2d1fe383dfec90eb9183..6cbf1a7b3157467785581ad3814ef4f738c3391b
@@@ -283,15 -283,15 +283,15 @@@ ieee80211_rx_monitor(struct ieee80211_l
        skb->protocol = htons(ETH_P_802_2);
  
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
-                       continue;
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
                        continue;
  
                if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)
                        continue;
  
+               if (!netif_running(sdata->dev))
+                       continue;
                if (prev_dev) {
                        skb2 = skb_clone(skb, GFP_ATOMIC);
                        if (skb2) {
@@@ -361,7 -361,9 +361,9 @@@ static void ieee80211_parse_qos(struct 
   * boundary. In the case of regular frames, this simply means aligning the
   * payload to a four-byte boundary (because either the IP header is directly
   * contained, or IV/RFC1042 headers that have a length divisible by four are
-  * in front of it).
+  * in front of it).  If the payload data is not properly aligned and the
+  * architecture doesn't support efficient unaligned operations, mac80211
+  * will align the data.
   *
   * With A-MSDU frames, however, the payload data address must yield two modulo
   * four because there are 14-byte 802.3 headers within the A-MSDU frames that
   */
  static void ieee80211_verify_alignment(struct ieee80211_rx_data *rx)
  {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
-       int hdrlen;
- #ifndef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
-       return;
+ #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+       WARN_ONCE((unsigned long)rx->skb->data & 1,
+                 "unaligned packet at 0x%p\n", rx->skb->data);
  #endif
-       if (WARN_ONCE((unsigned long)rx->skb->data & 1,
-                     "unaligned packet at 0x%p\n", rx->skb->data))
-               return;
-       if (!ieee80211_is_data_present(hdr->frame_control))
-               return;
-       hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       if (rx->flags & IEEE80211_RX_AMSDU)
-               hdrlen += ETH_HLEN;
-       WARN_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3,
-                 "unaligned IP payload at 0x%p\n", rx->skb->data + hdrlen);
  }
  
  
@@@ -476,7 -463,7 +463,7 @@@ ieee80211_rx_mesh_check(struct ieee8021
  {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
        unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       char *dev_addr = rx->sdata->dev->dev_addr;
+       char *dev_addr = rx->sdata->vif.addr;
  
        if (ieee80211_is_data(hdr->frame_control)) {
                if (is_multicast_ether_addr(hdr->addr1)) {
@@@ -1021,10 -1008,10 +1008,10 @@@ static void ap_sta_ps_start(struct sta_
  
        atomic_inc(&sdata->bss->num_sta_ps);
        set_sta_flags(sta, WLAN_STA_PS_STA);
-       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta);
+       drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
  #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
-              sdata->dev->name, sta->sta.addr, sta->sta.aid);
+              sdata->name, sta->sta.addr, sta->sta.aid);
  #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
  }
  
@@@ -1038,13 -1025,13 +1025,13 @@@ static void ap_sta_ps_end(struct sta_in
  
  #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
-              sdata->dev->name, sta->sta.addr, sta->sta.aid);
+              sdata->name, sta->sta.addr, sta->sta.aid);
  #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
  
        if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) {
  #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
                printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n",
-                      sdata->dev->name, sta->sta.addr, sta->sta.aid);
+                      sdata->name, sta->sta.addr, sta->sta.aid);
  #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
                return;
        }
@@@ -1156,7 -1143,7 +1143,7 @@@ ieee80211_reassemble_add(struct ieee802
                printk(KERN_DEBUG "%s: RX reassembly removed oldest "
                       "fragment entry (idx=%d age=%lu seq=%d last_frag=%d "
                       "addr1=%pM addr2=%pM\n",
-                      sdata->dev->name, idx,
+                      sdata->name, idx,
                       jiffies - entry->first_frag_time, entry->seq,
                       entry->last_frag, hdr->addr1, hdr->addr2);
  #endif
@@@ -1424,7 -1411,6 +1411,6 @@@ static in
  __ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
  {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
-       struct net_device *dev = sdata->dev;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
  
        if (ieee80211_has_a4(hdr->frame_control) &&
             (sdata->vif.type == NL80211_IFTYPE_STATION && sdata->u.mgd.use_4addr)))
                return -1;
  
-       return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type);
+       return ieee80211_data_to_8023(rx->skb, sdata->vif.addr, sdata->vif.type);
  }
  
  /*
@@@ -1453,7 -1439,7 +1439,7 @@@ static bool ieee80211_frame_allowed(str
         * of whether the frame was encrypted or not.
         */
        if (ehdr->h_proto == htons(ETH_P_PAE) &&
-           (compare_ether_addr(ehdr->h_dest, rx->sdata->dev->dev_addr) == 0 ||
+           (compare_ether_addr(ehdr->h_dest, rx->sdata->vif.addr) == 0 ||
             compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0))
                return true;
  
@@@ -1472,7 -1458,6 +1458,6 @@@ ieee80211_deliver_skb(struct ieee80211_
  {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct net_device *dev = sdata->dev;
-       struct ieee80211_local *local = rx->local;
        struct sk_buff *skb, *xmit_skb;
        struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
        struct sta_info *dsta;
                                printk(KERN_DEBUG "%s: failed to clone "
                                       "multicast frame\n", dev->name);
                } else {
-                       dsta = sta_info_get(local, skb->data);
-                       if (dsta && dsta->sdata->dev == dev) {
+                       dsta = sta_info_get(sdata, skb->data);
+                       if (dsta) {
                                /*
                                 * The destination station is associated to
                                 * this AP (in this VLAN), so send the frame
        if (skb) {
                int align __maybe_unused;
  
- #if defined(CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT) || !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+ #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
                /*
                 * 'align' will only take the values 0 or 2 here
                 * since all frames are required to be aligned
@@@ -1556,16 -1541,10 +1541,10 @@@ static ieee80211_rx_result debug_noinli
  ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
  {
        struct net_device *dev = rx->sdata->dev;
-       struct ieee80211_local *local = rx->local;
-       u16 ethertype;
-       u8 *payload;
-       struct sk_buff *skb = rx->skb, *frame = NULL;
+       struct sk_buff *skb = rx->skb;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        __le16 fc = hdr->frame_control;
-       const struct ethhdr *eth;
-       int remaining, err;
-       u8 dst[ETH_ALEN];
-       u8 src[ETH_ALEN];
+       struct sk_buff_head frame_list;
  
        if (unlikely(!ieee80211_is_data(fc)))
                return RX_CONTINUE;
        if (!(rx->flags & IEEE80211_RX_AMSDU))
                return RX_CONTINUE;
  
-       err = __ieee80211_data_to_8023(rx);
-       if (unlikely(err))
+       if (ieee80211_has_a4(hdr->frame_control) &&
+           rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+           !rx->sdata->u.vlan.sta)
                return RX_DROP_UNUSABLE;
  
-       skb->dev = dev;
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += skb->len;
-       /* skip the wrapping header */
-       eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
-       if (!eth)
+       if (is_multicast_ether_addr(hdr->addr1) &&
+           ((rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+             rx->sdata->u.vlan.sta) ||
+            (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
+             rx->sdata->u.mgd.use_4addr)))
                return RX_DROP_UNUSABLE;
  
-       while (skb != frame) {
-               u8 padding;
-               __be16 len = eth->h_proto;
-               unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
-               remaining = skb->len;
-               memcpy(dst, eth->h_dest, ETH_ALEN);
-               memcpy(src, eth->h_source, ETH_ALEN);
-               padding = ((4 - subframe_len) & 0x3);
-               /* the last MSDU has no padding */
-               if (subframe_len > remaining)
-                       return RX_DROP_UNUSABLE;
+       skb->dev = dev;
+       __skb_queue_head_init(&frame_list);
  
-               skb_pull(skb, sizeof(struct ethhdr));
-               /* if last subframe reuse skb */
-               if (remaining <= subframe_len + padding)
-                       frame = skb;
-               else {
-                       /*
-                        * Allocate and reserve two bytes more for payload
-                        * alignment since sizeof(struct ethhdr) is 14.
-                        */
-                       frame = dev_alloc_skb(
-                               ALIGN(local->hw.extra_tx_headroom, 4) +
-                               subframe_len + 2);
-                       if (frame == NULL)
-                               return RX_DROP_UNUSABLE;
-                       skb_reserve(frame,
-                                   ALIGN(local->hw.extra_tx_headroom, 4) +
-                                   sizeof(struct ethhdr) + 2);
-                       memcpy(skb_put(frame, ntohs(len)), skb->data,
-                               ntohs(len));
-                       eth = (struct ethhdr *) skb_pull(skb, ntohs(len) +
-                                                       padding);
-                       if (!eth) {
-                               dev_kfree_skb(frame);
-                               return RX_DROP_UNUSABLE;
-                       }
-               }
+       ieee80211_amsdu_to_8023s(skb, &frame_list, dev->dev_addr,
+                                rx->sdata->vif.type,
+                                rx->local->hw.extra_tx_headroom);
  
-               skb_reset_network_header(frame);
-               frame->dev = dev;
-               frame->priority = skb->priority;
-               rx->skb = frame;
-               payload = frame->data;
-               ethertype = (payload[6] << 8) | payload[7];
-               if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
-                           ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-                          compare_ether_addr(payload,
-                                             bridge_tunnel_header) == 0)) {
-                       /* remove RFC1042 or Bridge-Tunnel
-                        * encapsulation and replace EtherType */
-                       skb_pull(frame, 6);
-                       memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
-                       memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
-               } else {
-                       memcpy(skb_push(frame, sizeof(__be16)),
-                              &len, sizeof(__be16));
-                       memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
-                       memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
-               }
+       while (!skb_queue_empty(&frame_list)) {
+               rx->skb = __skb_dequeue(&frame_list);
  
                if (!ieee80211_frame_allowed(rx, fc)) {
-                       if (skb == frame) /* last frame */
-                               return RX_DROP_UNUSABLE;
-                       dev_kfree_skb(frame);
+                       dev_kfree_skb(rx->skb);
                        continue;
                }
+               dev->stats.rx_packets++;
+               dev->stats.rx_bytes += rx->skb->len;
  
                ieee80211_deliver_skb(rx);
        }
@@@ -1712,6 -1631,7 +1631,6 @@@ ieee80211_rx_h_mesh_fwding(struct ieee8
                        mpp_path_add(proxied_addr, mpp_addr, sdata);
                } else {
                        spin_lock_bh(&mppath->state_lock);
 -                      mppath->exp_time = jiffies;
                        if (compare_ether_addr(mppath->mpp, mpp_addr) != 0)
                                memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
                        spin_unlock_bh(&mppath->state_lock);
  
        /* Frame has reached destination.  Don't forward */
        if (!is_multicast_ether_addr(hdr->addr1) &&
-           compare_ether_addr(sdata->dev->dev_addr, hdr->addr3) == 0)
+           compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0)
                return RX_CONTINUE;
  
        mesh_hdr->ttl--;
  
                        if (!fwd_skb && net_ratelimit())
                                printk(KERN_DEBUG "%s: failed to clone mesh frame\n",
-                                                  sdata->dev->name);
+                                                  sdata->name);
  
                        fwd_hdr =  (struct ieee80211_hdr *) fwd_skb->data;
-                       memcpy(fwd_hdr->addr2, sdata->dev->dev_addr, ETH_ALEN);
+                       memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
                        info = IEEE80211_SKB_CB(fwd_skb);
                        memset(info, 0, sizeof(*info));
                        info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
@@@ -1870,7 -1790,7 +1789,7 @@@ static void ieee80211_process_sa_query_
        struct sk_buff *skb;
        struct ieee80211_mgmt *resp;
  
-       if (compare_ether_addr(mgmt->da, sdata->dev->dev_addr) != 0) {
+       if (compare_ether_addr(mgmt->da, sdata->vif.addr) != 0) {
                /* Not to own unicast address */
                return;
        }
        resp = (struct ieee80211_mgmt *) skb_put(skb, 24);
        memset(resp, 0, 24);
        memcpy(resp->da, mgmt->sa, ETH_ALEN);
-       memcpy(resp->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(resp->sa, sdata->vif.addr, ETH_ALEN);
        memcpy(resp->bssid, sdata->u.mgd.bssid, ETH_ALEN);
        resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_ACTION);
@@@ -2274,7 -2194,7 +2193,7 @@@ static int prepare_for_handlers(struct 
                if (!bssid && !sdata->u.mgd.use_4addr)
                        return 0;
                if (!multicast &&
-                   compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
+                   compare_ether_addr(sdata->vif.addr, hdr->addr1) != 0) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
                                return 0;
                        rx->flags &= ~IEEE80211_RX_RA_MATCH;
                                return 0;
                        rx->flags &= ~IEEE80211_RX_RA_MATCH;
                } else if (!multicast &&
-                          compare_ether_addr(sdata->dev->dev_addr,
+                          compare_ether_addr(sdata->vif.addr,
                                              hdr->addr1) != 0) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
                                return 0;
                break;
        case NL80211_IFTYPE_MESH_POINT:
                if (!multicast &&
-                   compare_ether_addr(sdata->dev->dev_addr,
+                   compare_ether_addr(sdata->vif.addr,
                                       hdr->addr1) != 0) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
                                return 0;
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_AP:
                if (!bssid) {
-                       if (compare_ether_addr(sdata->dev->dev_addr,
+                       if (compare_ether_addr(sdata->vif.addr,
                                               hdr->addr1))
                                return 0;
                } else if (!ieee80211_bssid_match(bssid,
-                                       sdata->dev->dev_addr)) {
+                                       sdata->vif.addr)) {
                        if (!(rx->flags & IEEE80211_RX_IN_SCAN))
                                return 0;
                        rx->flags &= ~IEEE80211_RX_RA_MATCH;
@@@ -2362,6 -2282,8 +2281,8 @@@ static void __ieee80211_rx_handle_packe
        int prepares;
        struct ieee80211_sub_if_data *prev = NULL;
        struct sk_buff *skb_new;
+       struct sta_info *sta, *tmp;
+       bool found_sta = false;
  
        hdr = (struct ieee80211_hdr *)skb->data;
        memset(&rx, 0, sizeof(rx));
        ieee80211_parse_qos(&rx);
        ieee80211_verify_alignment(&rx);
  
-       rx.sta = sta_info_get(local, hdr->addr2);
-       if (rx.sta)
-               rx.sdata = rx.sta->sdata;
-       if (rx.sdata && ieee80211_is_data(hdr->frame_control)) {
-               rx.flags |= IEEE80211_RX_RA_MATCH;
-               prepares = prepare_for_handlers(rx.sdata, &rx, hdr);
-               if (prepares) {
-                       if (status->flag & RX_FLAG_MMIC_ERROR) {
-                               if (rx.flags & IEEE80211_RX_RA_MATCH)
-                                       ieee80211_rx_michael_mic_report(hdr, &rx);
-                       } else
-                               prev = rx.sdata;
+       if (ieee80211_is_data(hdr->frame_control)) {
+               for_each_sta_info(local, hdr->addr2, sta, tmp) {
+                       rx.sta = sta;
+                       found_sta = true;
+                       rx.sdata = sta->sdata;
+                       rx.flags |= IEEE80211_RX_RA_MATCH;
+                       prepares = prepare_for_handlers(rx.sdata, &rx, hdr);
+                       if (prepares) {
+                               if (status->flag & RX_FLAG_MMIC_ERROR) {
+                                       if (rx.flags & IEEE80211_RX_RA_MATCH)
+                                               ieee80211_rx_michael_mic_report(hdr, &rx);
+                               } else
+                                       prev = rx.sdata;
+                       }
                }
-       } else list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
-                       continue;
+       }
+       if (!found_sta) {
+               list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+                       if (!netif_running(sdata->dev))
+                               continue;
  
-               if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
-                   sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-                       continue;
+                       if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+                           sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                               continue;
  
-               rx.flags |= IEEE80211_RX_RA_MATCH;
-               prepares = prepare_for_handlers(sdata, &rx, hdr);
+                       rx.sta = sta_info_get(sdata, hdr->addr2);
  
-               if (!prepares)
-                       continue;
+                       rx.flags |= IEEE80211_RX_RA_MATCH;
+                       prepares = prepare_for_handlers(sdata, &rx, hdr);
  
-               if (status->flag & RX_FLAG_MMIC_ERROR) {
-                       rx.sdata = sdata;
-                       if (rx.flags & IEEE80211_RX_RA_MATCH)
-                               ieee80211_rx_michael_mic_report(hdr, &rx);
-                       continue;
-               }
+                       if (!prepares)
+                               continue;
  
-               /*
-                * frame is destined for this interface, but if it's not
-                * also for the previous one we handle that after the
-                * loop to avoid copying the SKB once too much
-                */
+                       if (status->flag & RX_FLAG_MMIC_ERROR) {
+                               rx.sdata = sdata;
+                               if (rx.flags & IEEE80211_RX_RA_MATCH)
+                                       ieee80211_rx_michael_mic_report(hdr,
+                                                                       &rx);
+                               continue;
+                       }
  
-               if (!prev) {
-                       prev = sdata;
-                       continue;
-               }
+                       /*
+                        * frame is destined for this interface, but if it's
+                        * not also for the previous one we handle that after
+                        * the loop to avoid copying the SKB once too much
+                        */
  
-               /*
-                * frame was destined for the previous interface
-                * so invoke RX handlers for it
-                */
+                       if (!prev) {
+                               prev = sdata;
+                               continue;
+                       }
  
-               skb_new = skb_copy(skb, GFP_ATOMIC);
-               if (!skb_new) {
-                       if (net_ratelimit())
-                               printk(KERN_DEBUG "%s: failed to copy "
-                                      "multicast frame for %s\n",
-                                      wiphy_name(local->hw.wiphy),
-                                      prev->dev->name);
-                       continue;
+                       /*
+                        * frame was destined for the previous interface
+                        * so invoke RX handlers for it
+                        */
+                       skb_new = skb_copy(skb, GFP_ATOMIC);
+                       if (!skb_new) {
+                               if (net_ratelimit())
+                                       printk(KERN_DEBUG "%s: failed to copy "
+                                              "multicast frame for %s\n",
+                                              wiphy_name(local->hw.wiphy),
+                                              prev->name);
+                               continue;
+                       }
+                       ieee80211_invoke_rx_handlers(prev, &rx, skb_new, rate);
+                       prev = sdata;
                }
-               ieee80211_invoke_rx_handlers(prev, &rx, skb_new, rate);
-               prev = sdata;
        }
        if (prev)
                ieee80211_invoke_rx_handlers(prev, &rx, skb, rate);
diff --combined net/mac80211/scan.c
index f1a4c7160300a93e8aad02b45a00f3657577574e,d3381ba5f457e8db6060a25169c5e2880fe51501..66da0ab1d8faceafd8affb9303269e03b2c51134
@@@ -147,7 -147,7 +147,7 @@@ ieee80211_scan_rx(struct ieee80211_sub_
        presp = ieee80211_is_probe_resp(fc);
        if (presp) {
                /* ignore ProbeResp to foreign address */
-               if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
+               if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN))
                        return RX_DROP_MONITOR;
  
                presp = true;
@@@ -227,8 -227,7 +227,8 @@@ static bool ieee80211_prep_hw_scan(stru
  static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_local *local = sdata->local;
 -      bool ps = false;
 +
 +      local->scan_ps_enabled = false;
  
        /* FIXME: what to do when local->pspolling is true? */
  
        cancel_work_sync(&local->dynamic_ps_enable_work);
  
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
 -              ps = true;
 +              local->scan_ps_enabled = true;
                local->hw.conf.flags &= ~IEEE80211_CONF_PS;
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
        }
  
 -      if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
 +      if (!(local->scan_ps_enabled) ||
 +          !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
                /*
                 * If power save was enabled, no need to send a nullfunc
                 * frame because AP knows that we are sleeping. But if the
@@@ -263,7 -261,7 +263,7 @@@ static void ieee80211_scan_ps_disable(s
  
        if (!local->ps_sdata)
                ieee80211_send_nullfunc(local, sdata, 0);
 -      else {
 +      else if (local->scan_ps_enabled) {
                /*
                 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
                 * will send a nullfunc frame with the powersave bit set
                 */
                local->hw.conf.flags |= IEEE80211_CONF_PS;
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
 +      } else if (local->hw.conf.dynamic_ps_timeout > 0) {
 +              /*
 +               * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
 +               * had been running before leaving the operating channel,
 +               * restart the timer now and send a nullfunc frame to inform
 +               * the AP that we are awake.
 +               */
 +              ieee80211_send_nullfunc(local, sdata, 0);
 +              mod_timer(&local->dynamic_ps_timer, jiffies +
 +                        msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
        }
  }
  
diff --combined net/mac80211/util.c
index 78a6e924c7e13d11ae31b2acb42e4a2c2c1671af,acb6626ad0a431d9ac8c8007b99add864b017ce6..b01972579c7c09c168dfdc250aef82aacd1090ed
@@@ -469,7 -469,7 +469,7 @@@ void ieee80211_iterate_active_interface
                        break;
                }
                if (netif_running(sdata->dev))
-                       iterator(data, sdata->dev->dev_addr,
+                       iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
  
@@@ -503,7 -503,7 +503,7 @@@ void ieee80211_iterate_active_interface
                        break;
                }
                if (netif_running(sdata->dev))
-                       iterator(data, sdata->dev->dev_addr,
+                       iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
  
@@@ -579,7 -579,7 +579,7 @@@ u32 ieee802_11_parse_elems_crc(u8 *star
                if (elen > left)
                        break;
  
 -              if (calc_crc && id < 64 && (filter & BIT(id)))
 +              if (calc_crc && id < 64 && (filter & (1ULL << id)))
                        crc = crc32_be(crc, pos - 2, elen + 2);
  
                switch (id) {
@@@ -848,7 -848,7 +848,7 @@@ void ieee80211_send_auth(struct ieee802
                            sizeof(*mgmt) + 6 + extra_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
-                      "frame\n", sdata->dev->name);
+                      "frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_AUTH);
        memcpy(mgmt->da, bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        memcpy(mgmt->bssid, bssid, ETH_ALEN);
        mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
        mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
@@@ -908,16 -908,24 +908,24 @@@ int ieee80211_build_preq_ies(struct iee
        }
  
        if (sband->ht_cap.ht_supported) {
-               __le16 tmp = cpu_to_le16(sband->ht_cap.cap);
+               u16 cap = sband->ht_cap.cap;
+               __le16 tmp;
+               if (ieee80211_disable_40mhz_24ghz &&
+                   sband->band == IEEE80211_BAND_2GHZ) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
  
                *pos++ = WLAN_EID_HT_CAPABILITY;
                *pos++ = sizeof(struct ieee80211_ht_cap);
                memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+               tmp = cpu_to_le16(cap);
                memcpy(pos, &tmp, sizeof(u16));
                pos += sizeof(u16);
-               /* TODO: needs a define here for << 2 */
                *pos++ = sband->ht_cap.ampdu_factor |
-                        (sband->ht_cap.ampdu_density << 2);
+                        (sband->ht_cap.ampdu_density <<
+                               IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
                memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
                pos += sizeof(sband->ht_cap.mcs);
                pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
@@@ -949,7 -957,7 +957,7 @@@ void ieee80211_send_probe_req(struct ie
                            ie_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
-                      "request\n", sdata->dev->name);
+                      "request\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
        memset(mgmt, 0, 24);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_PROBE_REQ);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        if (dst) {
                memcpy(mgmt->da, dst, ETH_ALEN);
                memcpy(mgmt->bssid, dst, ETH_ALEN);
@@@ -1051,7 -1059,7 +1059,7 @@@ int ieee80211_reconfig(struct ieee80211
                    netif_running(sdata->dev)) {
                        conf.vif = &sdata->vif;
                        conf.type = sdata->vif.type;
-                       conf.mac_addr = sdata->dev->dev_addr;
+                       conf.mac_addr = sdata->vif.addr;
                        res = drv_add_interface(local, &conf);
                }
        }
                                             struct ieee80211_sub_if_data,
                                             u.ap);
  
-                       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD,
+                       drv_sta_notify(local, sdata, STA_NOTIFY_ADD,
                                       &sta->sta);
                }
                spin_unlock_irqrestore(&local->sta_lock, flags);
        return 0;
  }
  
+ static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
+                         enum ieee80211_smps_mode *smps_mode)
+ {
+       if (ifmgd->associated) {
+               *smps_mode = ifmgd->ap_smps;
+               if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
+                       if (ifmgd->powersave)
+                               *smps_mode = IEEE80211_SMPS_DYNAMIC;
+                       else
+                               *smps_mode = IEEE80211_SMPS_OFF;
+               }
+               return 1;
+       }
+       return 0;
+ }
+ /* must hold iflist_mtx */
+ void ieee80211_recalc_smps(struct ieee80211_local *local,
+                          struct ieee80211_sub_if_data *forsdata)
+ {
+       struct ieee80211_sub_if_data *sdata;
+       enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
+       int count = 0;
+       if (forsdata)
+               WARN_ON(!mutex_is_locked(&forsdata->u.mgd.mtx));
+       WARN_ON(!mutex_is_locked(&local->iflist_mtx));
+       /*
+        * This function could be improved to handle multiple
+        * interfaces better, but right now it makes any
+        * non-station interfaces force SM PS to be turned
+        * off. If there are multiple station interfaces it
+        * could also use the best possible mode, e.g. if
+        * one is in static and the other in dynamic then
+        * dynamic is ok.
+        */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (!netif_running(sdata->dev))
+                       continue;
+               if (sdata->vif.type != NL80211_IFTYPE_STATION)
+                       goto set;
+               if (sdata != forsdata) {
+                       /*
+                        * This nested is ok -- we are holding the iflist_mtx
+                        * so can't get here twice or so. But it's required
+                        * since normally we acquire it first and then the
+                        * iflist_mtx.
+                        */
+                       mutex_lock_nested(&sdata->u.mgd.mtx, SINGLE_DEPTH_NESTING);
+                       count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+                       mutex_unlock(&sdata->u.mgd.mtx);
+               } else
+                       count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+               if (count > 1) {
+                       smps_mode = IEEE80211_SMPS_OFF;
+                       break;
+               }
+       }
+       if (smps_mode == local->smps_mode)
+               return;
+  set:
+       local->smps_mode = smps_mode;
+       /* changed flag is auto-detected for this */
+       ieee80211_hw_config(local, 0);
+ }
diff --combined net/wireless/reg.c
index baa898add28760d8c13642c47cdb1f1c4a49c0c5,65f86264f7bbdbf30dd1c0e80c9e2af9c5893c95..dc13c3ffeca677177fcaefdbb555c1b80c3126b3
@@@ -40,6 -40,7 +40,7 @@@
  #include <net/cfg80211.h>
  #include "core.h"
  #include "reg.h"
+ #include "regdb.h"
  #include "nl80211.h"
  
  /* Receipt of information from last regulatory request */
@@@ -141,35 -142,62 +142,35 @@@ static const struct ieee80211_regdomai
        .reg_rules = {
                /* IEEE 802.11b/g, channels 1..11 */
                REG_RULE(2412-10, 2462+10, 40, 6, 27, 0),
 -              /* IEEE 802.11a, channel 36 */
 -              REG_RULE(5180-10, 5180+10, 40, 6, 23, 0),
 -              /* IEEE 802.11a, channel 40 */
 -              REG_RULE(5200-10, 5200+10, 40, 6, 23, 0),
 -              /* IEEE 802.11a, channel 44 */
 -              REG_RULE(5220-10, 5220+10, 40, 6, 23, 0),
 +              /* IEEE 802.11a, channel 36..48 */
 +              REG_RULE(5180-10, 5240+10, 40, 6, 17, 0),
                /* IEEE 802.11a, channels 48..64 */
 -              REG_RULE(5240-10, 5320+10, 40, 6, 23, 0),
 +              REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS),
 +              /* IEEE 802.11a, channels 100..124 */
 +              REG_RULE(5500-10, 5590+10, 40, 6, 20, NL80211_RRF_DFS),
 +              /* IEEE 802.11a, channels 132..144 */
 +              REG_RULE(5660-10, 5700+10, 40, 6, 20, NL80211_RRF_DFS),
                /* IEEE 802.11a, channels 149..165, outdoor */
                REG_RULE(5745-10, 5825+10, 40, 6, 30, 0),
        }
  };
  
  static const struct ieee80211_regdomain jp_regdom = {
 -      .n_reg_rules = 3,
 +      .n_reg_rules = 6,
        .alpha2 =  "JP",
        .reg_rules = {
 -              /* IEEE 802.11b/g, channels 1..14 */
 -              REG_RULE(2412-10, 2484+10, 40, 6, 20, 0),
 -              /* IEEE 802.11a, channels 34..48 */
 -              REG_RULE(5170-10, 5240+10, 40, 6, 20,
 -                      NL80211_RRF_PASSIVE_SCAN),
 +              /* IEEE 802.11b/g, channels 1..11 */
 +              REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
 +              /* IEEE 802.11b/g, channels 12..13 */
 +              REG_RULE(2467-10, 2472+10, 20, 6, 20, 0),
 +              /* IEEE 802.11b/g, channel 14 */
 +              REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_NO_OFDM),
 +              /* IEEE 802.11a, channels 36..48 */
 +              REG_RULE(5180-10, 5240+10, 40, 6, 20, 0),
                /* IEEE 802.11a, channels 52..64 */
 -              REG_RULE(5260-10, 5320+10, 40, 6, 20,
 -                      NL80211_RRF_NO_IBSS |
 -                      NL80211_RRF_DFS),
 -      }
 -};
 -
 -static const struct ieee80211_regdomain eu_regdom = {
 -      .n_reg_rules = 6,
 -      /*
 -       * This alpha2 is bogus, we leave it here just for stupid
 -       * backward compatibility
 -       */
 -      .alpha2 =  "EU",
 -      .reg_rules = {
 -              /* IEEE 802.11b/g, channels 1..13 */
 -              REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
 -              /* IEEE 802.11a, channel 36 */
 -              REG_RULE(5180-10, 5180+10, 40, 6, 23,
 -                      NL80211_RRF_PASSIVE_SCAN),
 -              /* IEEE 802.11a, channel 40 */
 -              REG_RULE(5200-10, 5200+10, 40, 6, 23,
 -                      NL80211_RRF_PASSIVE_SCAN),
 -              /* IEEE 802.11a, channel 44 */
 -              REG_RULE(5220-10, 5220+10, 40, 6, 23,
 -                      NL80211_RRF_PASSIVE_SCAN),
 -              /* IEEE 802.11a, channels 48..64 */
 -              REG_RULE(5240-10, 5320+10, 40, 6, 20,
 -                      NL80211_RRF_NO_IBSS |
 -                      NL80211_RRF_DFS),
 -              /* IEEE 802.11a, channels 100..140 */
 -              REG_RULE(5500-10, 5700+10, 40, 6, 30,
 -                      NL80211_RRF_NO_IBSS |
 -                      NL80211_RRF_DFS),
 +              REG_RULE(5260-10, 5320+10, 40, 6, 20, NL80211_RRF_DFS),
 +              /* IEEE 802.11a, channels 100..144 */
 +              REG_RULE(5500-10, 5700+10, 40, 6, 23, NL80211_RRF_DFS),
        }
  };
  
@@@ -179,17 -207,15 +180,17 @@@ static const struct ieee80211_regdomai
                return &us_regdom;
        if (alpha2[0] == 'J' && alpha2[1] == 'P')
                return &jp_regdom;
 +      /* Use world roaming rules for "EU", since it was a pseudo
 +         domain anyway... */
        if (alpha2[0] == 'E' && alpha2[1] == 'U')
 -              return &eu_regdom;
 -      /* Default, as per the old rules */
 -      return &us_regdom;
 +              return &world_regdom;
 +      /* Default, world roaming rules */
 +      return &world_regdom;
  }
  
  static bool is_old_static_regdom(const struct ieee80211_regdomain *rd)
  {
 -      if (rd == &us_regdom || rd == &jp_regdom || rd == &eu_regdom)
 +      if (rd == &us_regdom || rd == &jp_regdom || rd == &world_regdom)
                return true;
        return false;
  }
@@@ -335,6 -361,98 +336,98 @@@ static bool country_ie_integrity_change
        return false;
  }
  
+ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
+                        const struct ieee80211_regdomain *src_regd)
+ {
+       struct ieee80211_regdomain *regd;
+       int size_of_regd = 0;
+       unsigned int i;
+       size_of_regd = sizeof(struct ieee80211_regdomain) +
+         ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
+       regd = kzalloc(size_of_regd, GFP_KERNEL);
+       if (!regd)
+               return -ENOMEM;
+       memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
+       for (i = 0; i < src_regd->n_reg_rules; i++)
+               memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
+                       sizeof(struct ieee80211_reg_rule));
+       *dst_regd = regd;
+       return 0;
+ }
+ #ifdef CONFIG_CFG80211_INTERNAL_REGDB
+ struct reg_regdb_search_request {
+       char alpha2[2];
+       struct list_head list;
+ };
+ static LIST_HEAD(reg_regdb_search_list);
+ static DEFINE_SPINLOCK(reg_regdb_search_lock);
+ static void reg_regdb_search(struct work_struct *work)
+ {
+       struct reg_regdb_search_request *request;
+       const struct ieee80211_regdomain *curdom, *regdom;
+       int i, r;
+       spin_lock(&reg_regdb_search_lock);
+       while (!list_empty(&reg_regdb_search_list)) {
+               request = list_first_entry(&reg_regdb_search_list,
+                                          struct reg_regdb_search_request,
+                                          list);
+               list_del(&request->list);
+               for (i=0; i<reg_regdb_size; i++) {
+                       curdom = reg_regdb[i];
+                       if (!memcmp(request->alpha2, curdom->alpha2, 2)) {
+                               r = reg_copy_regd(&regdom, curdom);
+                               if (r)
+                                       break;
+                               spin_unlock(&reg_regdb_search_lock);
+                               mutex_lock(&cfg80211_mutex);
+                               set_regdom(regdom);
+                               mutex_unlock(&cfg80211_mutex);
+                               spin_lock(&reg_regdb_search_lock);
+                               break;
+                       }
+               }
+               kfree(request);
+       }
+       spin_unlock(&reg_regdb_search_lock);
+ }
+ static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
+ static void reg_regdb_query(const char *alpha2)
+ {
+       struct reg_regdb_search_request *request;
+       if (!alpha2)
+               return;
+       request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL);
+       if (!request)
+               return;
+       memcpy(request->alpha2, alpha2, 2);
+       spin_lock(&reg_regdb_search_lock);
+       list_add_tail(&request->list, &reg_regdb_search_list);
+       spin_unlock(&reg_regdb_search_lock);
+       schedule_work(&reg_regdb_work);
+ }
+ #else
+ static inline void reg_regdb_query(const char *alpha2) {}
+ #endif /* CONFIG_CFG80211_INTERNAL_REGDB */
  /*
   * This lets us keep regulatory code which is updated on a regulatory
   * basis in userspace.
@@@ -354,6 -472,9 +447,9 @@@ static int call_crda(const char *alpha2
                printk(KERN_INFO "cfg80211: Calling CRDA to update world "
                        "regulatory domain\n");
  
+       /* query internal regulatory database (if it exists) */
+       reg_regdb_query(alpha2);
        country_env[8] = alpha2[0];
        country_env[9] = alpha2[1];
  
@@@ -1342,30 -1463,6 +1438,6 @@@ void wiphy_apply_custom_regulatory(stru
  }
  EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
  
- static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
-                        const struct ieee80211_regdomain *src_regd)
- {
-       struct ieee80211_regdomain *regd;
-       int size_of_regd = 0;
-       unsigned int i;
-       size_of_regd = sizeof(struct ieee80211_regdomain) +
-         ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule));
-       regd = kzalloc(size_of_regd, GFP_KERNEL);
-       if (!regd)
-               return -ENOMEM;
-       memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
-       for (i = 0; i < src_regd->n_reg_rules; i++)
-               memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
-                       sizeof(struct ieee80211_reg_rule));
-       *dst_regd = regd;
-       return 0;
- }
  /*
   * Return value which can be used by ignore_request() to indicate
   * it has been determined we should intersect two regulatory domains
index 54face3d4424c973ab6b297ea9582daba6c0beab,2fa8de1140e95f86af6c8157f5b10f386a42276e..4198243a3dff97be788f6cec9062fcd50dc8ebe9
@@@ -479,7 -479,6 +479,7 @@@ static int __cfg80211_set_encryption(st
                        }
                        err = rdev->ops->del_key(&rdev->wiphy, dev, idx, addr);
                }
 +              wdev->wext.connect.privacy = false;
                /*
                 * Applications using wireless extensions expect to be
                 * able to delete keys that don't exist, so allow that.
@@@ -1257,10 -1256,7 +1257,7 @@@ int cfg80211_wext_giwrate(struct net_de
        if (!(sinfo.filled & STATION_INFO_TX_BITRATE))
                return -EOPNOTSUPP;
  
-       rate->value = 0;
-       if (!(sinfo.txrate.flags & RATE_INFO_FLAGS_MCS))
-               rate->value = 100000 * sinfo.txrate.legacy;
+       rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate);
  
        return 0;
  }
This page took 0.346744 seconds and 4 git commands to generate.