]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
Merge git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 6 Jan 2010 22:22:54 +0000 (17:22 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 6 Jan 2010 22:26:13 +0000 (17:26 -0500)
Conflicts:
net/mac80211/scan.c
net/mac80211/wme.c

165 files changed:
Documentation/feature-removal-schedule.txt
Documentation/networking/regulatory.txt
drivers/net/wireless/adm8211.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/ath/ar9170/ar9170.h
drivers/net/wireless/ath/ar9170/hw.h
drivers/net/wireless/ath/ar9170/mac.c
drivers/net/wireless/ath/ar9170/main.c
drivers/net/wireless/ath/ar9170/usb.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/rc.h
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/Makefile
drivers/net/wireless/b43/b43.h
drivers/net/wireless/b43/dma.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/pio.h
drivers/net/wireless/b43legacy/main.c
drivers/net/wireless/hostap/hostap_hw.c
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-3945.c
drivers/net/wireless/iwlwifi/iwl-3945.h
drivers/net/wireless/iwlwifi/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-calib.c
drivers/net/wireless/iwlwifi/iwl-commands.h
drivers/net/wireless/iwlwifi/iwl-core.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-debugfs.c
drivers/net/wireless/iwlwifi/iwl-dev.h
drivers/net/wireless/iwlwifi/iwl-devtrace.c
drivers/net/wireless/iwlwifi/iwl-devtrace.h
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/iwmc3200wifi/rx.c
drivers/net/wireless/libertas/Kconfig
drivers/net/wireless/libertas/Makefile
drivers/net/wireless/libertas/assoc.c
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/libertas/cmd.h
drivers/net/wireless/libertas/cmdresp.c
drivers/net/wireless/libertas/defs.h
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/ethtool.c
drivers/net/wireless/libertas/main.c
drivers/net/wireless/libertas/mesh.c
drivers/net/wireless/libertas/mesh.h
drivers/net/wireless/libertas/scan.c
drivers/net/wireless/libertas/tx.c
drivers/net/wireless/libertas/wext.c
drivers/net/wireless/libertas_tf/main.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rt2x00/Kconfig
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2800usb.h
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00pci.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtl818x/rtl8180.h
drivers/net/wireless/rtl818x/rtl8180_dev.c
drivers/net/wireless/rtl818x/rtl8187.h
drivers/net/wireless/rtl818x/rtl8187_dev.c
drivers/net/wireless/rtl818x/rtl8187_leds.c
drivers/net/wireless/wl12xx/wl1251.h
drivers/net/wireless/wl12xx/wl1251_acx.c
drivers/net/wireless/wl12xx/wl1251_acx.h
drivers/net/wireless/wl12xx/wl1251_debugfs.c
drivers/net/wireless/wl12xx/wl1251_init.c
drivers/net/wireless/wl12xx/wl1251_init.h
drivers/net/wireless/wl12xx/wl1251_main.c
drivers/net/wireless/wl12xx/wl1251_ps.c
drivers/net/wireless/wl12xx/wl1251_rx.c
drivers/net/wireless/wl12xx/wl1251_tx.c
drivers/net/wireless/wl12xx/wl1251_tx.h
drivers/net/wireless/wl12xx/wl1271.h
drivers/net/wireless/wl12xx/wl1271_acx.c
drivers/net/wireless/wl12xx/wl1271_acx.h
drivers/net/wireless/wl12xx/wl1271_boot.c
drivers/net/wireless/wl12xx/wl1271_cmd.c
drivers/net/wireless/wl12xx/wl1271_cmd.h
drivers/net/wireless/wl12xx/wl1271_conf.h
drivers/net/wireless/wl12xx/wl1271_debugfs.c
drivers/net/wireless/wl12xx/wl1271_event.c
drivers/net/wireless/wl12xx/wl1271_event.h
drivers/net/wireless/wl12xx/wl1271_init.c
drivers/net/wireless/wl12xx/wl1271_main.c
drivers/net/wireless/wl12xx/wl1271_ps.c
drivers/net/wireless/wl12xx/wl1271_spi.c
drivers/net/wireless/wl12xx/wl1271_tx.c
drivers/net/wireless/zd1211rw/zd_mac.c
drivers/net/wireless/zd1211rw/zd_usb.c
include/linux/ieee80211.h
include/linux/nl80211.h
include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/Kconfig
net/mac80211/Makefile
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/debugfs_key.c
net/mac80211/debugfs_netdev.c
net/mac80211/debugfs_netdev.h
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_pathtbl.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/offchannel.c [new file with mode: 0644]
net/mac80211/pm.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/spectmgmt.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tkip.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wme.c
net/mac80211/work.c [new file with mode: 0644]
net/wireless/.gitignore [new file with mode: 0644]
net/wireless/Kconfig
net/wireless/Makefile
net/wireless/chan.c
net/wireless/core.h
net/wireless/db.txt [new file with mode: 0644]
net/wireless/genregdb.awk [new file with mode: 0644]
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/reg.c
net/wireless/regdb.h [new file with mode: 0644]
net/wireless/util.c
net/wireless/wext-compat.c

index 591e94448e636edebab3791bdd69363b54a13895..86f2ec90af874246c9110dbcc2bb909da49bda97 100644 (file)
@@ -88,27 +88,6 @@ Who: Luis R. Rodriguez <lrodriguez@atheros.com>
 
 ---------------------------
 
-What:  CONFIG_WIRELESS_OLD_REGULATORY - old static regulatory information
-When:  March 2010 / desktop catchup
-
-Why:   The old regulatory infrastructure has been replaced with a new one
-       which does not require statically defined regulatory domains. We do
-       not want to keep static regulatory domains in the kernel due to the
-       the dynamic nature of regulatory law and localization. We kept around
-       the old static definitions for the regulatory domains of:
-
-               * US
-               * JP
-               * EU
-
-       and used by default the US when CONFIG_WIRELESS_OLD_REGULATORY was
-       set. We will remove this option once the standard Linux desktop catches
-       up with the new userspace APIs we have implemented.
-
-Who:   Luis R. Rodriguez <lrodriguez@atheros.com>
-
----------------------------
-
 What:  dev->power.power_state
 When:  July 2007
 Why:   Broken design for runtime control over driver power states, confusing
index ee31369e9e5baec8300136a1d2992be5a8bb2c97..9551622d0a7b845fe982076b3657ef59ef1ed6b7 100644 (file)
@@ -188,3 +188,27 @@ Then in some part of your code after your wiphy has been registered:
                       &mydriver_jp_regdom.reg_rules[i],
                       sizeof(struct ieee80211_reg_rule));
        regulatory_struct_hint(rd);
+
+Statically compiled regulatory database
+---------------------------------------
+
+In most situations the userland solution using CRDA as described
+above is the preferred solution.  However in some cases a set of
+rules built into the kernel itself may be desirable.  To account
+for this situation, a configuration option has been provided
+(i.e. CONFIG_CFG80211_INTERNAL_REGDB).  With this option enabled,
+the wireless database information contained in net/wireless/db.txt is
+used to generate a data structure encoded in net/wireless/regdb.c.
+That option also enables code in net/wireless/reg.c which queries
+the data in regdb.c as an alternative to using CRDA.
+
+The file net/wireless/db.txt should be kept up-to-date with the db.txt
+file available in the git repository here:
+
+    git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
+
+Again, most users in most situations should be using the CRDA package
+provided with their distribution, and in most other situations users
+should be building and using CRDA on their own rather than using
+this option.  If you are not absolutely sure that you should be using
+CONFIG_CFG80211_INTERNAL_REGDB then _DO_NOT_USE_IT_.
index 39410016b4ffb8e467607ef9ffb0e9737f57e0f5..e1f04bb437e305fd7d085d8f666b50e430435bd0 100644 (file)
@@ -1400,15 +1400,15 @@ static void adm8211_configure_filter(struct ieee80211_hw *dev,
 }
 
 static int adm8211_add_interface(struct ieee80211_hw *dev,
-                                struct ieee80211_if_init_conf *conf)
+                                struct ieee80211_vif *vif)
 {
        struct adm8211_priv *priv = dev->priv;
        if (priv->mode != NL80211_IFTYPE_MONITOR)
                return -EOPNOTSUPP;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               priv->mode = conf->type;
+               priv->mode = vif->type;
                break;
        default:
                return -EOPNOTSUPP;
@@ -1416,8 +1416,8 @@ static int adm8211_add_interface(struct ieee80211_hw *dev,
 
        ADM8211_IDLE();
 
-       ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)conf->mac_addr));
-       ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(conf->mac_addr + 4)));
+       ADM8211_CSR_WRITE(PAR0, le32_to_cpu(*(__le32 *)vif->addr));
+       ADM8211_CSR_WRITE(PAR1, le16_to_cpu(*(__le16 *)(vif->addr + 4)));
 
        adm8211_update_mode(dev);
 
@@ -1427,7 +1427,7 @@ static int adm8211_add_interface(struct ieee80211_hw *dev,
 }
 
 static void adm8211_remove_interface(struct ieee80211_hw *dev,
-                                    struct ieee80211_if_init_conf *conf)
+                                    struct ieee80211_vif *vif)
 {
        struct adm8211_priv *priv = dev->priv;
        priv->mode = NL80211_IFTYPE_MONITOR;
index 2517364d3ebedbf038e48aa6a93bcbf1e2dc1426..0fb419936dfff58e0b8780107c07c70d842d712b 100644 (file)
@@ -1789,7 +1789,7 @@ static void at76_mac80211_stop(struct ieee80211_hw *hw)
 }
 
 static int at76_add_interface(struct ieee80211_hw *hw,
-                             struct ieee80211_if_init_conf *conf)
+                             struct ieee80211_vif *vif)
 {
        struct at76_priv *priv = hw->priv;
        int ret = 0;
@@ -1798,7 +1798,7 @@ static int at76_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&priv->mtx);
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                priv->iw_mode = IW_MODE_INFRA;
                break;
@@ -1814,7 +1814,7 @@ exit:
 }
 
 static void at76_remove_interface(struct ieee80211_hw *hw,
-                                 struct ieee80211_if_init_conf *conf)
+                                 struct ieee80211_vif *vif)
 {
        at76_dbg(DBG_MAC80211, "%s()", __func__);
 }
index 9f9459860d82bba9f092cc6c7621b95b9f7f12b9..b99a8c2053d8f9a3a0321b62fe55fe80495a4af6 100644 (file)
@@ -109,7 +109,6 @@ struct ar9170_rxstream_mpdu_merge {
        bool has_plcp;
 };
 
-#define AR9170_NUM_MAX_BA_RETRY        5
 #define AR9170_NUM_TID 16
 #define WME_BA_BMP_SIZE         64
 #define AR9170_NUM_MAX_AGG_LEN (2 * WME_BA_BMP_SIZE)
@@ -143,7 +142,6 @@ struct ar9170_sta_tid {
        u16 tid;
        enum ar9170_tid_state state;
        bool active;
-       u8 retry;
 };
 
 #define AR9170_QUEUE_TIMEOUT           64
@@ -154,6 +152,8 @@ struct ar9170_sta_tid {
 
 #define AR9170_NUM_TX_STATUS           128
 #define AR9170_NUM_TX_AGG_MAX          30
+#define AR9170_NUM_TX_LIMIT_HARD       AR9170_TXQ_DEPTH
+#define AR9170_NUM_TX_LIMIT_SOFT       (AR9170_TXQ_DEPTH - 10)
 
 struct ar9170 {
        struct ieee80211_hw *hw;
@@ -248,13 +248,8 @@ struct ar9170_sta_info {
        unsigned int ampdu_max_len;
 };
 
-#define AR9170_TX_FLAG_WAIT_FOR_ACK    BIT(0)
-#define AR9170_TX_FLAG_NO_ACK          BIT(1)
-#define AR9170_TX_FLAG_BLOCK_ACK       BIT(2)
-
 struct ar9170_tx_info {
        unsigned long timeout;
-       unsigned int flags;
 };
 
 #define IS_STARTED(a)          (((struct ar9170 *)a)->state >= AR9170_STARTED)
index 701ddb7d84004766a9f9e7e37ae796ecc709f39b..0a1d4c28e68a346e05a82c15c0599824bb5a4678 100644 (file)
@@ -276,6 +276,7 @@ struct ar9170_tx_control {
 #define AR9170_TX_MAC_RATE_PROBE               0x8000
 
 /* either-or */
+#define AR9170_TX_PHY_MOD_MASK                 0x00000003
 #define AR9170_TX_PHY_MOD_CCK                  0x00000000
 #define AR9170_TX_PHY_MOD_OFDM                 0x00000001
 #define AR9170_TX_PHY_MOD_HT                   0x00000002
index ddc8c09dc79e321051bc8325714b9642806eea91..857e86104295a07a6c6f586aa1268faae9027608 100644 (file)
@@ -117,7 +117,7 @@ int ar9170_set_qos(struct ar9170 *ar)
        ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
                        ar->edcf[0].txop | ar->edcf[1].txop << 16);
        ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
-                       ar->edcf[1].txop | ar->edcf[3].txop << 16);
+                       ar->edcf[2].txop | ar->edcf[3].txop << 16);
 
        ar9170_regwrite_finish();
 
index f9d6db8d013ec9bea5545dea749f5ba4013d4786..4d27f7f67c76cbf72f35fbca8958108567a63ab4 100644 (file)
@@ -194,12 +194,15 @@ static inline u16 ar9170_get_seq(struct sk_buff *skb)
        return ar9170_get_seq_h((void *) txc->frame_data);
 }
 
+static inline u16 ar9170_get_tid_h(struct ieee80211_hdr *hdr)
+{
+       return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK;
+}
+
 static inline u16 ar9170_get_tid(struct sk_buff *skb)
 {
        struct ar9170_tx_control *txc = (void *) skb->data;
-       struct ieee80211_hdr *hdr = (void *) txc->frame_data;
-
-       return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK;
+       return ar9170_get_tid_h((struct ieee80211_hdr *) txc->frame_data);
 }
 
 #define GET_NEXT_SEQ(seq)      ((seq + 1) & 0x0fff)
@@ -213,10 +216,10 @@ static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
        struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data;
        struct ieee80211_hdr *hdr = (void *) txc->frame_data;
 
-       printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x s:%d "
+       printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] s:%d "
                          "mac_ctrl:%04x, phy_ctrl:%08x, timeout:[%d ms]]\n",
               wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb),
-              ieee80211_get_DA(hdr), arinfo->flags, ar9170_get_seq_h(hdr),
+              ieee80211_get_DA(hdr), ar9170_get_seq_h(hdr),
               le16_to_cpu(txc->mac_control), le32_to_cpu(txc->phy_control),
               jiffies_to_msecs(arinfo->timeout - jiffies));
 }
@@ -430,7 +433,7 @@ void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
        spin_lock_irqsave(&ar->tx_stats_lock, flags);
        ar->tx_stats[queue].len--;
 
-       if (skb_queue_empty(&ar->tx_pending[queue])) {
+       if (ar->tx_stats[queue].len < AR9170_NUM_TX_LIMIT_SOFT) {
 #ifdef AR9170_QUEUE_STOP_DEBUG
                printk(KERN_DEBUG "%s: wake queue %d\n",
                       wiphy_name(ar->hw->wiphy), queue);
@@ -440,22 +443,17 @@ void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
        }
        spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
 
-       if (arinfo->flags & AR9170_TX_FLAG_BLOCK_ACK) {
-               ar9170_tx_ampdu_callback(ar, skb);
-       } else if (arinfo->flags & AR9170_TX_FLAG_WAIT_FOR_ACK) {
-               arinfo->timeout = jiffies +
-                                 msecs_to_jiffies(AR9170_TX_TIMEOUT);
-
-               skb_queue_tail(&ar->tx_status[queue], skb);
-       } else if (arinfo->flags & AR9170_TX_FLAG_NO_ACK) {
+       if (info->flags & IEEE80211_TX_CTL_NO_ACK) {
                ar9170_tx_status(ar, skb, AR9170_TX_STATUS_FAILED);
        } else {
-#ifdef AR9170_QUEUE_DEBUG
-               printk(KERN_DEBUG "%s: unsupported frame flags!\n",
-                      wiphy_name(ar->hw->wiphy));
-               ar9170_print_txheader(ar, skb);
-#endif /* AR9170_QUEUE_DEBUG */
-               dev_kfree_skb_any(skb);
+               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+                       ar9170_tx_ampdu_callback(ar, skb);
+               } else {
+                       arinfo->timeout = jiffies +
+                                 msecs_to_jiffies(AR9170_TX_TIMEOUT);
+
+                       skb_queue_tail(&ar->tx_status[queue], skb);
+               }
        }
 
        if (!ar->tx_stats[queue].len &&
@@ -1407,17 +1405,6 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
 
        if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
             (is_valid_ether_addr(ieee80211_get_DA(hdr)))) {
-               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
-                       if (unlikely(!info->control.sta))
-                               goto err_out;
-
-                       txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
-                       arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK;
-
-                       goto out;
-               }
-
-               txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
                /*
                 * WARNING:
                 * Putting the QoS queue bits into an unexplored territory is
@@ -1431,12 +1418,17 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
 
                txc->phy_control |=
                        cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
-               arinfo->flags = AR9170_TX_FLAG_WAIT_FOR_ACK;
-       } else {
-               arinfo->flags = AR9170_TX_FLAG_NO_ACK;
+
+               if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+                       if (unlikely(!info->control.sta))
+                               goto err_out;
+
+                       txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+               } else {
+                       txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
+               }
        }
 
-out:
        return 0;
 
 err_out:
@@ -1671,8 +1663,7 @@ static bool ar9170_tx_ampdu(struct ar9170 *ar)
                 * tell the FW/HW that this is the last frame,
                 * that way it will wait for the immediate block ack.
                 */
-               if (likely(skb_peek_tail(&agg)))
-                       ar9170_tx_indicate_immba(ar, skb_peek_tail(&agg));
+               ar9170_tx_indicate_immba(ar, skb_peek_tail(&agg));
 
 #ifdef AR9170_TXAGG_DEBUG
                printk(KERN_DEBUG "%s: generated A-MPDU looks like this:\n",
@@ -1716,6 +1707,21 @@ static void ar9170_tx(struct ar9170 *ar)
 
        for (i = 0; i < __AR9170_NUM_TXQ; i++) {
                spin_lock_irqsave(&ar->tx_stats_lock, flags);
+               frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len,
+                            skb_queue_len(&ar->tx_pending[i]));
+
+               if (remaining_space < frames) {
+#ifdef AR9170_QUEUE_DEBUG
+                       printk(KERN_DEBUG "%s: tx quota reached queue:%d, "
+                              "remaining slots:%d, needed:%d\n",
+                              wiphy_name(ar->hw->wiphy), i, remaining_space,
+                              frames);
+#endif /* AR9170_QUEUE_DEBUG */
+                       frames = remaining_space;
+               }
+
+               ar->tx_stats[i].len += frames;
+               ar->tx_stats[i].count += frames;
                if (ar->tx_stats[i].len >= ar->tx_stats[i].limit) {
 #ifdef AR9170_QUEUE_DEBUG
                        printk(KERN_DEBUG "%s: queue %d full\n",
@@ -1733,25 +1739,8 @@ static void ar9170_tx(struct ar9170 *ar)
                        __ar9170_dump_txstats(ar);
 #endif /* AR9170_QUEUE_STOP_DEBUG */
                        ieee80211_stop_queue(ar->hw, i);
-                       spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
-                       continue;
                }
 
-               frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len,
-                            skb_queue_len(&ar->tx_pending[i]));
-
-               if (remaining_space < frames) {
-#ifdef AR9170_QUEUE_DEBUG
-                       printk(KERN_DEBUG "%s: tx quota reached queue:%d, "
-                              "remaining slots:%d, needed:%d\n",
-                              wiphy_name(ar->hw->wiphy), i, remaining_space,
-                              frames);
-#endif /* AR9170_QUEUE_DEBUG */
-                       frames = remaining_space;
-               }
-
-               ar->tx_stats[i].len += frames;
-               ar->tx_stats[i].count += frames;
                spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
 
                if (!frames)
@@ -1773,7 +1762,7 @@ static void ar9170_tx(struct ar9170 *ar)
                        arinfo->timeout = jiffies +
                                          msecs_to_jiffies(AR9170_TX_TIMEOUT);
 
-                       if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK)
+                       if (info->flags & IEEE80211_TX_CTL_AMPDU)
                                atomic_inc(&ar->tx_ampdu_pending);
 
 #ifdef AR9170_QUEUE_DEBUG
@@ -1784,7 +1773,7 @@ static void ar9170_tx(struct ar9170 *ar)
 
                        err = ar->tx(ar, skb);
                        if (unlikely(err)) {
-                               if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK)
+                               if (info->flags & IEEE80211_TX_CTL_AMPDU)
                                        atomic_dec(&ar->tx_ampdu_pending);
 
                                frames_failed++;
@@ -1950,7 +1939,7 @@ err_free:
 }
 
 static int ar9170_op_add_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_if_init_conf *conf)
+                                  struct ieee80211_vif *vif)
 {
        struct ar9170 *ar = hw->priv;
        struct ath_common *common = &ar->common;
@@ -1963,8 +1952,8 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw,
                goto unlock;
        }
 
-       ar->vif = conf->vif;
-       memcpy(common->macaddr, conf->mac_addr, ETH_ALEN);
+       ar->vif = vif;
+       memcpy(common->macaddr, vif->addr, ETH_ALEN);
 
        if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) {
                ar->rx_software_decryption = true;
@@ -1984,7 +1973,7 @@ unlock:
 }
 
 static void ar9170_op_remove_interface(struct ieee80211_hw *hw,
-                                      struct ieee80211_if_init_conf *conf)
+                                      struct ieee80211_vif *vif)
 {
        struct ar9170 *ar = hw->priv;
 
@@ -2366,7 +2355,6 @@ static void ar9170_sta_notify(struct ieee80211_hw *hw,
                        sta_info->agg[i].state = AR9170_TID_STATE_SHUTDOWN;
                        sta_info->agg[i].active = false;
                        sta_info->agg[i].ssn = 0;
-                       sta_info->agg[i].retry = 0;
                        sta_info->agg[i].tid = i;
                        INIT_LIST_HEAD(&sta_info->agg[i].list);
                        skb_queue_head_init(&sta_info->agg[i].queue);
index e0799d92405773206f9e2128b5a5cddeb6765d06..0f361186b78ffb0dca6e03fdf799493fba0b595d 100644 (file)
@@ -84,6 +84,8 @@ static struct usb_device_id ar9170_usb_ids[] = {
        { USB_DEVICE(0x0cde, 0x0023) },
        /* Z-Com UB82 ABG */
        { USB_DEVICE(0x0cde, 0x0026) },
+       /* Sphairon Homelink 1202 */
+       { USB_DEVICE(0x0cde, 0x0027) },
        /* Arcadyan WN7512 */
        { USB_DEVICE(0x083a, 0xf522) },
        /* Planex GWUS300 */
index e63b7c40d0eeb5ce0b8fcca0df66beece476e209..fdfaf0f618f14cb9b8a934fb2868ce256fe16c31 100644 (file)
@@ -225,9 +225,9 @@ static int ath5k_reset_wake(struct ath5k_softc *sc);
 static int ath5k_start(struct ieee80211_hw *hw);
 static void ath5k_stop(struct ieee80211_hw *hw);
 static int ath5k_add_interface(struct ieee80211_hw *hw,
-               struct ieee80211_if_init_conf *conf);
+               struct ieee80211_vif *vif);
 static void ath5k_remove_interface(struct ieee80211_hw *hw,
-               struct ieee80211_if_init_conf *conf);
+               struct ieee80211_vif *vif);
 static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
 static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw,
                                   int mc_count, struct dev_addr_list *mc_list);
@@ -2773,7 +2773,7 @@ static void ath5k_stop(struct ieee80211_hw *hw)
 }
 
 static int ath5k_add_interface(struct ieee80211_hw *hw,
-               struct ieee80211_if_init_conf *conf)
+               struct ieee80211_vif *vif)
 {
        struct ath5k_softc *sc = hw->priv;
        int ret;
@@ -2784,22 +2784,22 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
                goto end;
        }
 
-       sc->vif = conf->vif;
+       sc->vif = vif;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_MONITOR:
-               sc->opmode = conf->type;
+               sc->opmode = vif->type;
                break;
        default:
                ret = -EOPNOTSUPP;
                goto end;
        }
 
-       ath5k_hw_set_lladdr(sc->ah, conf->mac_addr);
+       ath5k_hw_set_lladdr(sc->ah, vif->addr);
        ath5k_mode_setup(sc);
 
        ret = 0;
@@ -2810,13 +2810,13 @@ end:
 
 static void
 ath5k_remove_interface(struct ieee80211_hw *hw,
-                       struct ieee80211_if_init_conf *conf)
+                       struct ieee80211_vif *vif)
 {
        struct ath5k_softc *sc = hw->priv;
        u8 mac[ETH_ALEN] = {};
 
        mutex_lock(&sc->lock);
-       if (sc->vif != conf->vif)
+       if (sc->vif != vif)
                goto end;
 
        ath5k_hw_set_lladdr(sc->ah, mac);
index 1597a42731edc58c55e3a6a37b69953f57f2623e..9efebac5ed0e8e673e46335ac945f71106ebeaaf 100644 (file)
@@ -453,7 +453,6 @@ struct ath_softc {
        int irq;
        spinlock_t sc_resetlock;
        spinlock_t sc_serial_rw;
-       spinlock_t ani_lock;
        spinlock_t sc_pm_lock;
        struct mutex mutex;
 
index b66f72dbf7b93f9c52bc254b1c54f7c0dd256422..592f1b70f55a43cdc6d49467013930a427a9b131 100644 (file)
@@ -289,23 +289,49 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
        if (sc->cur_rate_table == NULL)
                return 0;
 
-       max = 80 + sc->cur_rate_table->rate_cnt * 64;
+       max = 80 + sc->cur_rate_table->rate_cnt * 1024;
        buf = kmalloc(max + 1, GFP_KERNEL);
        if (buf == NULL)
                return 0;
        buf[max] = 0;
 
-       len += sprintf(buf, "%5s %15s %8s %9s %3s\n\n", "Rate", "Success",
-                      "Retries", "XRetries", "PER");
+       len += sprintf(buf, "%6s %6s %6s "
+                      "%10s %10s %10s %10s\n",
+                      "HT", "MCS", "Rate",
+                      "Success", "Retries", "XRetries", "PER");
 
        for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) {
                u32 ratekbps = sc->cur_rate_table->info[i].ratekbps;
                struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i];
+               char mcs[5];
+               char htmode[5];
+               int used_mcs = 0, used_htmode = 0;
+
+               if (WLAN_RC_PHY_HT(sc->cur_rate_table->info[i].phy)) {
+                       used_mcs = snprintf(mcs, 5, "%d",
+                               sc->cur_rate_table->info[i].ratecode);
+
+                       if (WLAN_RC_PHY_40(sc->cur_rate_table->info[i].phy))
+                               used_htmode = snprintf(htmode, 5, "HT40");
+                       else if (WLAN_RC_PHY_20(sc->cur_rate_table->info[i].phy))
+                               used_htmode = snprintf(htmode, 5, "HT20");
+                       else
+                               used_htmode = snprintf(htmode, 5, "????");
+               }
+
+               mcs[used_mcs] = '\0';
+               htmode[used_htmode] = '\0';
 
                len += snprintf(buf + len, max - len,
-                       "%3u.%d: %8u %8u %8u %8u\n", ratekbps / 1000,
-                       (ratekbps % 1000) / 100, stats->success,
-                       stats->retries, stats->xretries,
+                       "%6s %6s %3u.%d: "
+                       "%10u %10u %10u %10u\n",
+                       htmode,
+                       mcs,
+                       ratekbps / 1000,
+                       (ratekbps % 1000) / 100,
+                       stats->success,
+                       stats->retries,
+                       stats->xretries,
                        stats->per);
        }
 
index 2ec61f08cfdbb073ea02adc1a1947ddf66336793..9474f9f6d4005c37eba9f672325eb94b5178f386 100644 (file)
@@ -343,30 +343,6 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah)
        return true;
 }
 
-static const char *ath9k_hw_devname(u16 devid)
-{
-       switch (devid) {
-       case AR5416_DEVID_PCI:
-               return "Atheros 5416";
-       case AR5416_DEVID_PCIE:
-               return "Atheros 5418";
-       case AR9160_DEVID_PCI:
-               return "Atheros 9160";
-       case AR5416_AR9100_DEVID:
-               return "Atheros 9100";
-       case AR9280_DEVID_PCI:
-       case AR9280_DEVID_PCIE:
-               return "Atheros 9280";
-       case AR9285_DEVID_PCIE:
-               return "Atheros 9285";
-       case AR5416_DEVID_AR9287_PCI:
-       case AR5416_DEVID_AR9287_PCIE:
-               return "Atheros 9287";
-       }
-
-       return NULL;
-}
-
 static void ath9k_hw_init_config(struct ath_hw *ah)
 {
        int i;
@@ -392,7 +368,7 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
                ah->config.spurchans[i][1] = AR_NO_SPUR;
        }
 
-       ah->config.intr_mitigation = true;
+       ah->config.rx_intr_mitigation = true;
 
        /*
         * We need this for PCI devices only (Cardbus, PCI, miniPCI)
@@ -1184,7 +1160,7 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
                AR_IMR_RXORN |
                AR_IMR_BCNMISC;
 
-       if (ah->config.intr_mitigation)
+       if (ah->config.rx_intr_mitigation)
                ah->mask_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
        else
                ah->mask_reg |= AR_IMR_RXOK;
@@ -1266,12 +1242,6 @@ static void ath9k_hw_init_user_settings(struct ath_hw *ah)
                ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout);
 }
 
-const char *ath9k_hw_probe(u16 vendorid, u16 devid)
-{
-       return vendorid == ATHEROS_VENDOR_ID ?
-               ath9k_hw_devname(devid) : NULL;
-}
-
 void ath9k_hw_detach(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -2121,7 +2091,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        REG_WRITE(ah, AR_OBS, 8);
 
-       if (ah->config.intr_mitigation) {
+       if (ah->config.rx_intr_mitigation) {
                REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500);
                REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000);
        }
@@ -2781,7 +2751,7 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
 
                *masked = isr & ATH9K_INT_COMMON;
 
-               if (ah->config.intr_mitigation) {
+               if (ah->config.rx_intr_mitigation) {
                        if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
                                *masked |= ATH9K_INT_RX;
                }
@@ -2914,7 +2884,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
        }
        if (ints & ATH9K_INT_RX) {
                mask |= AR_IMR_RXERR;
-               if (ah->config.intr_mitigation)
+               if (ah->config.rx_intr_mitigation)
                        mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM;
                else
                        mask |= AR_IMR_RXOK | AR_IMR_RXDESC;
index e2b0c73a616f30ac5834a6bc0a2abb9b7ce7259a..8849450dc591b9671346e927981e50eb55c0f3ef 100644 (file)
@@ -212,7 +212,7 @@ struct ath9k_ops_config {
        u32 cck_trig_low;
        u32 enable_ani;
        int serialize_regmode;
-       bool intr_mitigation;
+       bool rx_intr_mitigation;
 #define SPUR_DISABLE           0
 #define SPUR_ENABLE_IOCTL      1
 #define SPUR_ENABLE_EEPROM     2
index 996eb90263ccba96363b17076504bdccfe8863db..79fbbda15493ec24f2b05e5703c11100d5977430 100644 (file)
@@ -363,14 +363,6 @@ static void ath_ani_calibrate(unsigned long data)
        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;
@@ -437,7 +429,6 @@ static void ath_ani_calibrate(unsigned long data)
        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 @@ static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
        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);
@@ -2550,12 +2540,12 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 }
 
 static int ath9k_add_interface(struct ieee80211_hw *hw,
-                              struct ieee80211_if_init_conf *conf)
+                              struct ieee80211_vif *vif)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_vif *avp = (void *)conf->vif->drv_priv;
+       struct ath_vif *avp = (void *)vif->drv_priv;
        enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
        int ret = 0;
 
@@ -2567,7 +2557,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                ic_opmode = NL80211_IFTYPE_STATION;
                break;
@@ -2578,11 +2568,11 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
                        ret = -ENOBUFS;
                        goto out;
                }
-               ic_opmode = conf->type;
+               ic_opmode = vif->type;
                break;
        default:
                ath_print(common, ATH_DBG_FATAL,
-                       "Interface type %d not yet supported\n", conf->type);
+                       "Interface type %d not yet supported\n", vif->type);
                ret = -EOPNOTSUPP;
                goto out;
        }
@@ -2614,18 +2604,18 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
         * Enable MIB interrupts when there are hardware phy counters.
         * Note we only do this (at the moment) for station mode.
         */
-       if ((conf->type == NL80211_IFTYPE_STATION) ||
-           (conf->type == NL80211_IFTYPE_ADHOC) ||
-           (conf->type == NL80211_IFTYPE_MESH_POINT)) {
+       if ((vif->type == NL80211_IFTYPE_STATION) ||
+           (vif->type == NL80211_IFTYPE_ADHOC) ||
+           (vif->type == NL80211_IFTYPE_MESH_POINT)) {
                sc->imask |= ATH9K_INT_MIB;
                sc->imask |= ATH9K_INT_TSFOOR;
        }
 
        ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
 
-       if (conf->type == NL80211_IFTYPE_AP    ||
-           conf->type == NL80211_IFTYPE_ADHOC ||
-           conf->type == NL80211_IFTYPE_MONITOR)
+       if (vif->type == NL80211_IFTYPE_AP    ||
+           vif->type == NL80211_IFTYPE_ADHOC ||
+           vif->type == NL80211_IFTYPE_MONITOR)
                ath_start_ani(common);
 
 out:
@@ -2634,12 +2624,12 @@ out:
 }
 
 static void ath9k_remove_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_if_init_conf *conf)
+                                  struct ieee80211_vif *vif)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_vif *avp = (void *)conf->vif->drv_priv;
+       struct ath_vif *avp = (void *)vif->drv_priv;
        int i;
 
        ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
@@ -2662,7 +2652,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
        sc->sc_flags &= ~SC_OP_BEACONS;
 
        for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
-               if (sc->beacon.bslot[i] == conf->vif) {
+               if (sc->beacon.bslot[i] == vif) {
                        printk(KERN_DEBUG "%s: vif had allocated beacon "
                               "slot\n", __func__);
                        sc->beacon.bslot[i] = NULL;
@@ -3133,6 +3123,7 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
 {
        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)) {
@@ -3148,10 +3139,9 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
 
        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);
 }
 
@@ -3159,13 +3149,14 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
 {
        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 9eb96f506998ea37f6b35992a2dded99ceb29ebe..4f6d6fd442f477f6d7d759539cec602459259b5d 100644 (file)
@@ -57,6 +57,10 @@ enum {
                                || (_phy == WLAN_RC_PHY_HT_40_DS)       \
                                || (_phy == WLAN_RC_PHY_HT_20_DS_HGI)   \
                                || (_phy == WLAN_RC_PHY_HT_40_DS_HGI))
+#define WLAN_RC_PHY_20(_phy)   ((_phy == WLAN_RC_PHY_HT_20_SS)         \
+                               || (_phy == WLAN_RC_PHY_HT_20_DS)       \
+                               || (_phy == WLAN_RC_PHY_HT_20_SS_HGI)   \
+                               || (_phy == WLAN_RC_PHY_HT_20_DS_HGI))
 #define WLAN_RC_PHY_40(_phy)   ((_phy == WLAN_RC_PHY_HT_40_SS)         \
                                || (_phy == WLAN_RC_PHY_HT_40_DS)       \
                                || (_phy == WLAN_RC_PHY_HT_40_SS_HGI)   \
index 64c12e1bced3368fb93d07ad0f0cbc56caed63ef..073be566d05e3355c9c5314846f1fed3be5f7853 100644 (file)
@@ -3,6 +3,7 @@ config B43
        depends on SSB_POSSIBLE && MAC80211 && HAS_DMA
        select SSB
        select FW_LOADER
+       select SSB_BLOCKIO
        ---help---
          b43 is a driver for the Broadcom 43xx series wireless devices.
 
@@ -78,14 +79,6 @@ config B43_SDIO
 
          If unsure, say N.
 
-# Data transfers to the device via PIO
-# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly.
-config B43_PIO
-       bool
-       depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO)
-       select SSB_BLOCKIO
-       default y
-
 config B43_NPHY
        bool "Pre IEEE 802.11n support (BROKEN)"
        depends on B43 && EXPERIMENTAL && BROKEN
@@ -137,12 +130,4 @@ config B43_DEBUG
          for production use.
          Only say Y, if you are debugging a problem in the b43 driver sourcecode.
 
-config B43_FORCE_PIO
-       bool "Force usage of PIO instead of DMA"
-       depends on B43 && B43_DEBUG
-       ---help---
-         This will disable DMA and always enable PIO instead.
 
-         Say N!
-         This is only for debugging the PIO engine code. You do
-         _NOT_ want to enable this.
index 84772a2542dca6a21148e2449e544027213c5b39..5e83b6f0a3a013aeeb8cd090fd2bcd9267223356 100644 (file)
@@ -12,7 +12,7 @@ b43-y                         += xmit.o
 b43-y                          += lo.o
 b43-y                          += wa.o
 b43-y                          += dma.o
-b43-$(CONFIG_B43_PIO)          += pio.o
+b43-y                          += pio.o
 b43-y                          += rfkill.o
 b43-$(CONFIG_B43_LEDS)         += leds.o
 b43-$(CONFIG_B43_PCMCIA)       += pcmcia.o
index fe3bf949199780bea902d3ad3cad122311c5612f..2f12a750bc980a4c22f6f4a26b615f4b392cd87a 100644 (file)
@@ -821,11 +821,9 @@ struct b43_wl {
        /* The device LEDs. */
        struct b43_leds leds;
 
-#ifdef CONFIG_B43_PIO
        /* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */
        u8 pio_scratchspace[110] __attribute__((__aligned__(8)));
        u8 pio_tailspace[4] __attribute__((__aligned__(8)));
-#endif /* CONFIG_B43_PIO */
 };
 
 static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw)
@@ -876,20 +874,9 @@ static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value)
 
 static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
 {
-#ifdef CONFIG_B43_PIO
        return dev->__using_pio_transfers;
-#else
-       return 0;
-#endif
 }
 
-#ifdef CONFIG_B43_FORCE_PIO
-# define B43_FORCE_PIO 1
-#else
-# define B43_FORCE_PIO 0
-#endif
-
-
 /* Message printing */
 void b43info(struct b43_wl *wl, const char *fmt, ...)
     __attribute__ ((format(printf, 2, 3)));
index 88d1fd02d40ae50fd01b645e921e83e8c48fbced..615af22c49fd1f34df3ad688841e4b7643caa843 100644 (file)
@@ -1653,7 +1653,6 @@ void b43_dma_tx_resume(struct b43_wldev *dev)
        b43_power_saving_ctl_bits(dev, 0);
 }
 
-#ifdef CONFIG_B43_PIO
 static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type,
                           u16 mmio_base, bool enable)
 {
@@ -1687,4 +1686,3 @@ void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
        mmio_base = b43_dmacontroller_base(type, engine_index);
        direct_fifo_rx(dev, type, mmio_base, enable);
 }
-#endif /* CONFIG_B43_PIO */
index 19b4eae47b594c95a389853a0dfb7ef67943b85b..6634a77fc766b17d7068e404b197b6fb99c837c3 100644 (file)
@@ -102,6 +102,9 @@ int b43_modparam_verbose = B43_VERBOSITY_DEFAULT;
 module_param_named(verbose, b43_modparam_verbose, int, 0644);
 MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug");
 
+static int modparam_pio;
+module_param_named(pio, modparam_pio, int, 0444);
+MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode");
 
 static const struct ssb_device_id b43_ssb_tbl[] = {
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
@@ -1786,8 +1789,8 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev)
                               dma_reason[4], dma_reason[5]);
                        b43err(dev->wl, "This device does not support DMA "
                               "on your system. Please use PIO instead.\n");
-                       b43err(dev->wl, "CONFIG_B43_FORCE_PIO must be set in "
-                              "your kernel configuration.\n");
+                       b43err(dev->wl, "Unload the b43 module and reload "
+                              "with 'pio=1'\n");
                        return;
                }
                if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) {
@@ -4353,7 +4356,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
 
        if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) ||
            (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) ||
-           B43_FORCE_PIO) {
+           modparam_pio) {
                dev->__using_pio_transfers = 1;
                err = b43_pio_init(dev);
        } else {
@@ -4388,7 +4391,7 @@ err_busdown:
 }
 
 static int b43_op_add_interface(struct ieee80211_hw *hw,
-                               struct ieee80211_if_init_conf *conf)
+                               struct ieee80211_vif *vif)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
        struct b43_wldev *dev;
@@ -4396,24 +4399,24 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
 
        /* TODO: allow WDS/AP devices to coexist */
 
-       if (conf->type != NL80211_IFTYPE_AP &&
-           conf->type != NL80211_IFTYPE_MESH_POINT &&
-           conf->type != NL80211_IFTYPE_STATION &&
-           conf->type != NL80211_IFTYPE_WDS &&
-           conf->type != NL80211_IFTYPE_ADHOC)
+       if (vif->type != NL80211_IFTYPE_AP &&
+           vif->type != NL80211_IFTYPE_MESH_POINT &&
+           vif->type != NL80211_IFTYPE_STATION &&
+           vif->type != NL80211_IFTYPE_WDS &&
+           vif->type != NL80211_IFTYPE_ADHOC)
                return -EOPNOTSUPP;
 
        mutex_lock(&wl->mutex);
        if (wl->operating)
                goto out_mutex_unlock;
 
-       b43dbg(wl, "Adding Interface type %d\n", conf->type);
+       b43dbg(wl, "Adding Interface type %d\n", vif->type);
 
        dev = wl->current_dev;
        wl->operating = 1;
-       wl->vif = conf->vif;
-       wl->if_type = conf->type;
-       memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
+       wl->vif = vif;
+       wl->if_type = vif->type;
+       memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 
        b43_adjust_opmode(dev);
        b43_set_pretbtt(dev);
@@ -4428,17 +4431,17 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
 }
 
 static void b43_op_remove_interface(struct ieee80211_hw *hw,
-                                   struct ieee80211_if_init_conf *conf)
+                                   struct ieee80211_vif *vif)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
        struct b43_wldev *dev = wl->current_dev;
 
-       b43dbg(wl, "Removing Interface type %d\n", conf->type);
+       b43dbg(wl, "Removing Interface type %d\n", vif->type);
 
        mutex_lock(&wl->mutex);
 
        B43_WARN_ON(!wl->operating);
-       B43_WARN_ON(wl->vif != conf->vif);
+       B43_WARN_ON(wl->vif != vif);
        wl->vif = NULL;
 
        wl->operating = 0;
index 7dd649c9ddadcd2a91533395da4f06e9d7fb9c03..7b3c42f93a16217e0c36a1c2c20e40a9f2f93ab8 100644 (file)
@@ -55,8 +55,6 @@
 #define B43_PIO_MAX_NR_TXPACKETS       32
 
 
-#ifdef CONFIG_B43_PIO
-
 struct b43_pio_txpacket {
        /* Pointer to the TX queue we belong to. */
        struct b43_pio_txqueue *queue;
@@ -169,42 +167,4 @@ void b43_pio_rx(struct b43_pio_rxqueue *q);
 void b43_pio_tx_suspend(struct b43_wldev *dev);
 void b43_pio_tx_resume(struct b43_wldev *dev);
 
-
-#else /* CONFIG_B43_PIO */
-
-
-static inline int b43_pio_init(struct b43_wldev *dev)
-{
-       return 0;
-}
-static inline void b43_pio_free(struct b43_wldev *dev)
-{
-}
-static inline void b43_pio_stop(struct b43_wldev *dev)
-{
-}
-static inline int b43_pio_tx(struct b43_wldev *dev,
-                            struct sk_buff *skb)
-{
-       return 0;
-}
-static inline void b43_pio_handle_txstatus(struct b43_wldev *dev,
-                                          const struct b43_txstatus *status)
-{
-}
-static inline void b43_pio_get_tx_stats(struct b43_wldev *dev,
-                                       struct ieee80211_tx_queue_stats *stats)
-{
-}
-static inline void b43_pio_rx(struct b43_pio_rxqueue *q)
-{
-}
-static inline void b43_pio_tx_suspend(struct b43_wldev *dev)
-{
-}
-static inline void b43_pio_tx_resume(struct b43_wldev *dev)
-{
-}
-
-#endif /* CONFIG_B43_PIO */
 #endif /* B43_PIO_H_ */
index ab6a18c2e9d9fe357163f8caaf3073f10747c30f..494017e4fcc98fa7a2dd61729c4b0dcec0afa120 100644 (file)
@@ -3361,7 +3361,7 @@ err_kfree_lo_control:
 }
 
 static int b43legacy_op_add_interface(struct ieee80211_hw *hw,
-                                     struct ieee80211_if_init_conf *conf)
+                                     struct ieee80211_vif *vif)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev;
@@ -3370,23 +3370,23 @@ static int b43legacy_op_add_interface(struct ieee80211_hw *hw,
 
        /* TODO: allow WDS/AP devices to coexist */
 
-       if (conf->type != NL80211_IFTYPE_AP &&
-           conf->type != NL80211_IFTYPE_STATION &&
-           conf->type != NL80211_IFTYPE_WDS &&
-           conf->type != NL80211_IFTYPE_ADHOC)
+       if (vif->type != NL80211_IFTYPE_AP &&
+           vif->type != NL80211_IFTYPE_STATION &&
+           vif->type != NL80211_IFTYPE_WDS &&
+           vif->type != NL80211_IFTYPE_ADHOC)
                return -EOPNOTSUPP;
 
        mutex_lock(&wl->mutex);
        if (wl->operating)
                goto out_mutex_unlock;
 
-       b43legacydbg(wl, "Adding Interface type %d\n", conf->type);
+       b43legacydbg(wl, "Adding Interface type %d\n", vif->type);
 
        dev = wl->current_dev;
        wl->operating = 1;
-       wl->vif = conf->vif;
-       wl->if_type = conf->type;
-       memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
+       wl->vif = vif;
+       wl->if_type = vif->type;
+       memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 
        spin_lock_irqsave(&wl->irq_lock, flags);
        b43legacy_adjust_opmode(dev);
@@ -3403,18 +3403,18 @@ static int b43legacy_op_add_interface(struct ieee80211_hw *hw,
 }
 
 static void b43legacy_op_remove_interface(struct ieee80211_hw *hw,
-                                         struct ieee80211_if_init_conf *conf)
+                                         struct ieee80211_vif *vif)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
        unsigned long flags;
 
-       b43legacydbg(wl, "Removing Interface type %d\n", conf->type);
+       b43legacydbg(wl, "Removing Interface type %d\n", vif->type);
 
        mutex_lock(&wl->mutex);
 
        B43legacy_WARN_ON(!wl->operating);
-       B43legacy_WARN_ON(wl->vif != conf->vif);
+       B43legacy_WARN_ON(wl->vif != vif);
        wl->vif = NULL;
 
        wl->operating = 0;
index ff9b5c882184a00e58316166a62fd5514ead0ae6..d70732819423d25d09627fa0bc371be5f6976896 100644 (file)
@@ -2618,6 +2618,15 @@ static irqreturn_t prism2_interrupt(int irq, void *dev_id)
        int events = 0;
        u16 ev;
 
+       /* Detect early interrupt before driver is fully configued */
+       if (!dev->base_addr) {
+               if (net_ratelimit()) {
+                       printk(KERN_DEBUG "%s: Interrupt, but dev not configured\n",
+                              dev->name);
+               }
+               return IRQ_HANDLED;
+       }
+
        iface = netdev_priv(dev);
        local = iface->local;
 
index 8414178bcff4a58c2d6254ad1c0af496b71eb026..0db1fda94a6574805c786b455c03c6bdbfc6efba 100644 (file)
@@ -105,6 +105,7 @@ static struct iwl_lib_ops iwl1000_lib = {
        .load_ucode = iwl5000_load_ucode,
        .dump_nic_event_log = iwl_dump_nic_event_log,
        .dump_nic_error_log = iwl_dump_nic_error_log,
+       .dump_csr = iwl_dump_csr,
        .init_alive_start = iwl5000_init_alive_start,
        .alive_notify = iwl5000_alive_notify,
        .send_tx_power = iwl5000_send_tx_power,
@@ -140,7 +141,7 @@ static struct iwl_lib_ops iwl1000_lib = {
         },
 };
 
-static struct iwl_ops iwl1000_ops = {
+static const struct iwl_ops iwl1000_ops = {
        .ucode = &iwl5000_ucode,
        .lib = &iwl1000_lib,
        .hcmd = &iwl5000_hcmd,
@@ -173,7 +174,6 @@ struct iwl_cfg iwl1000_bgn_cfg = {
        .use_rts_for_ht = true, /* use rts/cts protection */
        .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
        .support_ct_kill_exit = true,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
 };
 
 struct iwl_cfg iwl1000_bg_cfg = {
index 234891d8cc1080b5358b624cf992fbc6d43510a6..28ffe4c826d8733dc3ebde368209ff3d259d541c 100644 (file)
@@ -2804,7 +2804,7 @@ static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = {
        .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,
index 531fa125f5a60744b4c20f33bc61db4713394420..3ec2fe370b58edaab71315b614c63aa64dcbcdaa 100644 (file)
@@ -226,7 +226,8 @@ extern void iwl3945_rx_replenish(void *data);
 extern void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
 extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv,
                                        struct ieee80211_hdr *hdr,int left);
-extern void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log);
+extern int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log,
+                                      char **buf, bool display);
 extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv);
 
 /*
index 761aab127e7caba1f46e940c53e2af88545cbac3..cee368d4859f06f45ecda81777e11d3351621178 100644 (file)
@@ -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 @@ 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..ec6b27689fa84257c7d9e3d41356797686474f74 100644 (file)
@@ -1466,6 +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 +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,
@@ -1555,7 +1557,7 @@ static struct iwl_lib_ops iwl5150_lib = {
         },
 };
 
-static struct iwl_ops iwl5000_ops = {
+static const struct iwl_ops iwl5000_ops = {
        .ucode = &iwl5000_ucode,
        .lib = &iwl5000_lib,
        .hcmd = &iwl5000_hcmd,
@@ -1563,7 +1565,7 @@ static struct iwl_ops iwl5000_ops = {
        .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 +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 +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 = {
@@ -1692,7 +1692,6 @@ 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 = {
@@ -1716,7 +1715,6 @@ 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 74e5710492731cd668409a45889e75818387f2c8..a5a0ed4817a471562e77c9b5ab42193d0246f253 100644 (file)
@@ -215,6 +215,7 @@ static struct iwl_lib_ops iwl6000_lib = {
        .load_ucode = iwl5000_load_ucode,
        .dump_nic_event_log = iwl_dump_nic_event_log,
        .dump_nic_error_log = iwl_dump_nic_error_log,
+       .dump_csr = iwl_dump_csr,
        .init_alive_start = iwl5000_init_alive_start,
        .alive_notify = iwl5000_alive_notify,
        .send_tx_power = iwl5000_send_tx_power,
@@ -252,7 +253,7 @@ static struct iwl_lib_ops iwl6000_lib = {
         },
 };
 
-static struct iwl_ops iwl6000_ops = {
+static const struct iwl_ops iwl6000_ops = {
        .ucode = &iwl5000_ucode,
        .lib = &iwl6000_lib,
        .hcmd = &iwl5000_hcmd,
@@ -267,7 +268,7 @@ static struct iwl_hcmd_utils_ops iwl6050_hcmd_utils = {
        .calc_rssi = iwl5000_calc_rssi,
 };
 
-static struct iwl_ops iwl6050_ops = {
+static const struct iwl_ops iwl6050_ops = {
        .ucode = &iwl5000_ucode,
        .lib = &iwl6000_lib,
        .hcmd = &iwl5000_hcmd,
@@ -306,7 +307,6 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
        .supports_idle = true,
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
 };
 
 struct iwl_cfg iwl6000i_2abg_cfg = {
@@ -395,7 +395,6 @@ struct iwl_cfg iwl6050_2agn_cfg = {
        .supports_idle = true,
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DYNAMIC,
 };
 
 struct iwl_cfg iwl6050_2abg_cfg = {
@@ -455,7 +454,6 @@ struct iwl_cfg iwl6000_3agn_cfg = {
        .supports_idle = true,
        .adv_thermal_throttle = true,
        .support_ct_kill_exit = true,
-       .sm_ps_mode = WLAN_HT_CAP_SM_PS_DISABLED,
 };
 
 MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
index 1c9866daf81565007beb6ee596063702e4bf387f..771b03c1c7c5b8c291e5f37529f39ea73387e28e 100644 (file)
@@ -657,6 +657,131 @@ static void iwl_bg_statistics_periodic(unsigned long data)
        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 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
        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);
@@ -708,10 +835,10 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
                        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 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
  * 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 */
@@ -1716,7 +1844,7 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
        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
@@ -1744,27 +1872,44 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
                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
@@ -1772,21 +1917,26 @@ static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
         */
        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 */
@@ -1794,7 +1944,8 @@ static void iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
 
 #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 */
@@ -1802,6 +1953,8 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
        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);
@@ -1812,7 +1965,7 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
                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 */
@@ -1838,7 +1991,7 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
        /* 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
@@ -1853,6 +2006,15 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
                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,
@@ -1860,17 +2022,22 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_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 @@ static int iwl_setup_mac(struct iwl_priv *priv)
                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 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
        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 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
        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,
@@ -3188,6 +3364,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
        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)
index 95a57b36a7eafef9a366409d4d7391e0c77861d2..dc61906290e81b92852ab845b96f661858a6d2dc 100644 (file)
@@ -414,7 +414,6 @@ static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
 /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
 static int iwl_sensitivity_write(struct iwl_priv *priv)
 {
-       int ret = 0;
        struct iwl_sensitivity_cmd cmd ;
        struct iwl_sensitivity_data *data = NULL;
        struct iwl_host_cmd cmd_out = {
@@ -477,11 +476,7 @@ static int iwl_sensitivity_write(struct iwl_priv *priv)
        memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]),
               sizeof(u16)*HD_TABLE_SIZE);
 
-       ret = iwl_send_cmd(priv, &cmd_out);
-       if (ret)
-               IWL_ERR(priv, "SENSITIVITY_CMD failed\n");
-
-       return ret;
+       return iwl_send_cmd(priv, &cmd_out);
 }
 
 void iwl_init_sensitivity(struct iwl_priv *priv)
index e9150753192318721a1fe42ff13560b8cb820624..28f3800c560eb1229604b903085e624b11454e4a 100644 (file)
@@ -2510,7 +2510,7 @@ struct iwl_card_state_notif {
 
 #define HW_CARD_DISABLED   0x01
 #define SW_CARD_DISABLED   0x02
-#define RF_CARD_DISABLED   0x04
+#define CT_CARD_DISABLED   0x04
 #define RXON_CARD_DISABLED 0x10
 
 struct iwl_ct_kill_config {
index 574d366587022fdd101f7826221a1d15900d9782..14f482960d7faeda385162a58fafaf484e274cad 100644 (file)
@@ -450,8 +450,6 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv,
        if (priv->cfg->ht_greenfield_support)
                ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
        ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
-       ht_info->cap |= (IEEE80211_HT_CAP_SM_PS &
-                            (priv->cfg->sm_ps_mode << 2));
        max_bit_rate = MAX_BIT_RATE_20_MHZ;
        if (priv->hw_params.ht40_channel & BIT(band)) {
                ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -636,7 +634,7 @@ EXPORT_SYMBOL(iwlcore_rts_tx_cmd_flag);
 
 static bool is_single_rx_stream(struct iwl_priv *priv)
 {
-       return !priv->current_ht_config.is_ht ||
+       return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC ||
               priv->current_ht_config.single_chain_sufficient;
 }
 
@@ -1003,28 +1001,18 @@ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
  */
 static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt)
 {
-       int idle_cnt = active_cnt;
-       bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status);
-
-       /* # Rx chains when idling and maybe trying to save power */
-       switch (priv->cfg->sm_ps_mode) {
-       case WLAN_HT_CAP_SM_PS_STATIC:
-               idle_cnt = (is_cam) ? active_cnt : IWL_NUM_IDLE_CHAINS_SINGLE;
-               break;
-       case WLAN_HT_CAP_SM_PS_DYNAMIC:
-               idle_cnt = (is_cam) ? IWL_NUM_IDLE_CHAINS_DUAL :
-                       IWL_NUM_IDLE_CHAINS_SINGLE;
-               break;
-       case WLAN_HT_CAP_SM_PS_DISABLED:
-               break;
-       case WLAN_HT_CAP_SM_PS_INVALID:
+       /* # Rx chains when idling, depending on SMPS mode */
+       switch (priv->current_ht_config.smps) {
+       case IEEE80211_SMPS_STATIC:
+       case IEEE80211_SMPS_DYNAMIC:
+               return IWL_NUM_IDLE_CHAINS_SINGLE;
+       case IEEE80211_SMPS_OFF:
+               return active_cnt;
        default:
-               IWL_ERR(priv, "invalid sm_ps mode %u\n",
-                       priv->cfg->sm_ps_mode);
-               WARN_ON(1);
-               break;
+               WARN(1, "invalid SMPS mode %d",
+                    priv->current_ht_config.smps);
+               return active_cnt;
        }
-       return idle_cnt;
 }
 
 /* up to 4 chains */
@@ -1363,7 +1351,9 @@ void iwl_irq_handle_error(struct iwl_priv *priv)
        clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
 
        priv->cfg->ops->lib->dump_nic_error_log(priv);
-       priv->cfg->ops->lib->dump_nic_event_log(priv, false);
+       if (priv->cfg->ops->lib->dump_csr)
+               priv->cfg->ops->lib->dump_csr(priv);
+       priv->cfg->ops->lib->dump_nic_event_log(priv, false, NULL, false);
 #ifdef CONFIG_IWLWIFI_DEBUG
        if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS)
                iwl_print_rx_config_cmd(priv);
@@ -2594,12 +2584,12 @@ int iwl_set_mode(struct iwl_priv *priv, int mode)
 EXPORT_SYMBOL(iwl_set_mode);
 
 int iwl_mac_add_interface(struct ieee80211_hw *hw,
-                                struct ieee80211_if_init_conf *conf)
+                                struct ieee80211_vif *vif)
 {
        struct iwl_priv *priv = hw->priv;
        unsigned long flags;
 
-       IWL_DEBUG_MAC80211(priv, "enter: type %d\n", conf->type);
+       IWL_DEBUG_MAC80211(priv, "enter: type %d\n", vif->type);
 
        if (priv->vif) {
                IWL_DEBUG_MAC80211(priv, "leave - vif != NULL\n");
@@ -2607,19 +2597,19 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw,
        }
 
        spin_lock_irqsave(&priv->lock, flags);
-       priv->vif = conf->vif;
-       priv->iw_mode = conf->type;
+       priv->vif = vif;
+       priv->iw_mode = vif->type;
 
        spin_unlock_irqrestore(&priv->lock, flags);
 
        mutex_lock(&priv->mutex);
 
-       if (conf->mac_addr) {
-               IWL_DEBUG_MAC80211(priv, "Set %pM\n", conf->mac_addr);
-               memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
+       if (vif->addr) {
+               IWL_DEBUG_MAC80211(priv, "Set %pM\n", vif->addr);
+               memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
        }
 
-       if (iwl_set_mode(priv, conf->type) == -EAGAIN)
+       if (iwl_set_mode(priv, vif->type) == -EAGAIN)
                /* we are not ready, will run again when ready */
                set_bit(STATUS_MODE_PENDING, &priv->status);
 
@@ -2631,7 +2621,7 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw,
 EXPORT_SYMBOL(iwl_mac_add_interface);
 
 void iwl_mac_remove_interface(struct ieee80211_hw *hw,
-                                    struct ieee80211_if_init_conf *conf)
+                                    struct ieee80211_vif *vif)
 {
        struct iwl_priv *priv = hw->priv;
 
@@ -2644,7 +2634,7 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
                priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
                iwlcore_commit_rxon(priv);
        }
-       if (priv->vif == conf->vif) {
+       if (priv->vif == vif) {
                priv->vif = NULL;
                memset(priv->bssid, 0, ETH_ALEN);
        }
@@ -2684,6 +2674,21 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                IWL_DEBUG_MAC80211(priv, "leave - scanning\n");
        }
 
+       if (changed & (IEEE80211_CONF_CHANGE_SMPS |
+                      IEEE80211_CONF_CHANGE_CHANNEL)) {
+               /* mac80211 uses static for non-HT which is what we want */
+               priv->current_ht_config.smps = conf->smps_mode;
+
+               /*
+                * Recalculate chain counts.
+                *
+                * If monitor mode is enabled then mac80211 will
+                * set up the SM PS mode to OFF if an HT channel is
+                * configured.
+                */
+               if (priv->cfg->ops->hcmd->set_rxon_chain)
+                       priv->cfg->ops->hcmd->set_rxon_chain(priv);
+       }
 
        /* during scanning mac80211 will delay channel setting until
         * scan finish with changed = 0
@@ -2780,10 +2785,6 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
                iwl_set_tx_power(priv, conf->power_level, false);
        }
 
-       /* call to ensure that 4965 rx_chain is set properly in monitor mode */
-       if (priv->cfg->ops->hcmd->set_rxon_chain)
-               priv->cfg->ops->hcmd->set_rxon_chain(priv);
-
        if (!iwl_is_ready(priv)) {
                IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
                goto out;
@@ -3191,6 +3192,77 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len)
 EXPORT_SYMBOL(iwl_update_stats);
 #endif
 
+const static char *get_csr_string(int cmd)
+{
+       switch (cmd) {
+               IWL_CMD(CSR_HW_IF_CONFIG_REG);
+               IWL_CMD(CSR_INT_COALESCING);
+               IWL_CMD(CSR_INT);
+               IWL_CMD(CSR_INT_MASK);
+               IWL_CMD(CSR_FH_INT_STATUS);
+               IWL_CMD(CSR_GPIO_IN);
+               IWL_CMD(CSR_RESET);
+               IWL_CMD(CSR_GP_CNTRL);
+               IWL_CMD(CSR_HW_REV);
+               IWL_CMD(CSR_EEPROM_REG);
+               IWL_CMD(CSR_EEPROM_GP);
+               IWL_CMD(CSR_OTP_GP_REG);
+               IWL_CMD(CSR_GIO_REG);
+               IWL_CMD(CSR_GP_UCODE_REG);
+               IWL_CMD(CSR_GP_DRIVER_REG);
+               IWL_CMD(CSR_UCODE_DRV_GP1);
+               IWL_CMD(CSR_UCODE_DRV_GP2);
+               IWL_CMD(CSR_LED_REG);
+               IWL_CMD(CSR_DRAM_INT_TBL_REG);
+               IWL_CMD(CSR_GIO_CHICKEN_BITS);
+               IWL_CMD(CSR_ANA_PLL_CFG);
+               IWL_CMD(CSR_HW_REV_WA_REG);
+               IWL_CMD(CSR_DBG_HPET_MEM_REG);
+       default:
+               return "UNKNOWN";
+
+       }
+}
+
+void iwl_dump_csr(struct iwl_priv *priv)
+{
+       int i;
+       u32 csr_tbl[] = {
+               CSR_HW_IF_CONFIG_REG,
+               CSR_INT_COALESCING,
+               CSR_INT,
+               CSR_INT_MASK,
+               CSR_FH_INT_STATUS,
+               CSR_GPIO_IN,
+               CSR_RESET,
+               CSR_GP_CNTRL,
+               CSR_HW_REV,
+               CSR_EEPROM_REG,
+               CSR_EEPROM_GP,
+               CSR_OTP_GP_REG,
+               CSR_GIO_REG,
+               CSR_GP_UCODE_REG,
+               CSR_GP_DRIVER_REG,
+               CSR_UCODE_DRV_GP1,
+               CSR_UCODE_DRV_GP2,
+               CSR_LED_REG,
+               CSR_DRAM_INT_TBL_REG,
+               CSR_GIO_CHICKEN_BITS,
+               CSR_ANA_PLL_CFG,
+               CSR_HW_REV_WA_REG,
+               CSR_DBG_HPET_MEM_REG
+       };
+       IWL_ERR(priv, "CSR values:\n");
+       IWL_ERR(priv, "(2nd byte of CSR_INT_COALESCING is "
+               "CSR_INT_PERIODIC_REG)\n");
+       for (i = 0; i <  ARRAY_SIZE(csr_tbl); i++) {
+               IWL_ERR(priv, "  %25s: 0X%08x\n",
+                       get_csr_string(csr_tbl[i]),
+                       iwl_read32(priv, csr_tbl[i]));
+       }
+}
+EXPORT_SYMBOL(iwl_dump_csr);
+
 #ifdef CONFIG_PM
 
 int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
index 675b7df632fc1a1076f9b16862bb5bac2652b964..8deb83bfe182bbadf0eee9db9c9cbc9788221437 100644 (file)
@@ -63,8 +63,6 @@
 #ifndef __iwl_core_h__
 #define __iwl_core_h__
 
-#include <linux/utsrelease.h>
-
 /************************
  * forward declarations *
  ************************/
@@ -72,7 +70,7 @@ struct iwl_host_cmd;
 struct iwl_cmd;
 
 
-#define IWLWIFI_VERSION UTS_RELEASE "-k"
+#define IWLWIFI_VERSION "in-tree:"
 #define DRV_COPYRIGHT  "Copyright(c) 2003-2009 Intel Corporation"
 #define DRV_AUTHOR     "<ilw@linux.intel.com>"
 
@@ -169,8 +167,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 +230,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 +286,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;
 };
 
@@ -332,9 +330,9 @@ int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb);
 int iwl_commit_rxon(struct iwl_priv *priv);
 int iwl_set_mode(struct iwl_priv *priv, int mode);
 int iwl_mac_add_interface(struct ieee80211_hw *hw,
-                                struct ieee80211_if_init_conf *conf);
+                         struct ieee80211_vif *vif);
 void iwl_mac_remove_interface(struct ieee80211_hw *hw,
-                                struct ieee80211_if_init_conf *conf);
+                             struct ieee80211_vif *vif);
 int iwl_mac_config(struct ieee80211_hw *hw, u32 changed);
 void iwl_config_ap(struct iwl_priv *priv);
 int iwl_mac_get_tx_stats(struct ieee80211_hw *hw,
@@ -581,7 +579,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 d61293ab67c994752710b44b2a82beb07bbade42..58e0462cafa35744d84f4d252e1cb61f8d9d133e 100644 (file)
@@ -109,6 +109,8 @@ struct iwl_debugfs {
                struct dentry *file_power_save_status;
                struct dentry *file_clear_ucode_statistics;
                struct dentry *file_clear_traffic_statistics;
+               struct dentry *file_csr;
+               struct dentry *file_ucode_tracing;
        } dbgfs_debug_files;
        u32 sram_offset;
        u32 sram_len;
index 21e0f6699daf138854be2d36f831b405fb44f9e5..ee5aed12a4b1b8e29caacb9600b49ea00d4fe29a 100644 (file)
@@ -420,6 +420,23 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file,
        return ret;
 }
 
+static ssize_t iwl_dbgfs_log_event_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_priv *priv = file->private_data;
+       char *buf;
+       int pos = 0;
+       ssize_t ret = -ENOMEM;
+
+       pos = priv->cfg->ops->lib->dump_nic_event_log(priv, true, &buf, true);
+       if (pos && buf) {
+               ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+               kfree(buf);
+       }
+       return ret;
+}
+
 static ssize_t iwl_dbgfs_log_event_write(struct file *file,
                                        const char __user *user_buf,
                                        size_t count, loff_t *ppos)
@@ -436,7 +453,8 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file,
        if (sscanf(buf, "%d", &event_log_flag) != 1)
                return -EFAULT;
        if (event_log_flag == 1)
-               priv->cfg->ops->lib->dump_nic_event_log(priv, true);
+               priv->cfg->ops->lib->dump_nic_event_log(priv, true,
+                                                       NULL, false);
 
        return count;
 }
@@ -859,7 +877,7 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file,
 }
 
 DEBUGFS_READ_WRITE_FILE_OPS(sram);
-DEBUGFS_WRITE_FILE_OPS(log_event);
+DEBUGFS_READ_WRITE_FILE_OPS(log_event);
 DEBUGFS_READ_FILE_OPS(nvm);
 DEBUGFS_READ_FILE_OPS(stations);
 DEBUGFS_READ_FILE_OPS(channels);
@@ -1845,6 +1863,80 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file,
        return count;
 }
 
+static ssize_t iwl_dbgfs_csr_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_priv *priv = file->private_data;
+       char buf[8];
+       int buf_size;
+       int csr;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       if (sscanf(buf, "%d", &csr) != 1)
+               return -EFAULT;
+
+       if (priv->cfg->ops->lib->dump_csr)
+               priv->cfg->ops->lib->dump_csr(priv);
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos) {
+
+       struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
+       int pos = 0;
+       char buf[128];
+       const size_t bufsz = sizeof(buf);
+       ssize_t ret;
+
+       pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n",
+                       priv->event_log.ucode_trace ? "On" : "Off");
+       pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n",
+                       priv->event_log.non_wraps_count);
+       pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n",
+                       priv->event_log.wraps_once_count);
+       pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n",
+                       priv->event_log.wraps_more_count);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_priv *priv = file->private_data;
+       char buf[8];
+       int buf_size;
+       int trace;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       if (sscanf(buf, "%d", &trace) != 1)
+               return -EFAULT;
+
+       if (trace) {
+               priv->event_log.ucode_trace = true;
+               /* schedule the ucode timer to occur in UCODE_TRACE_PERIOD */
+               mod_timer(&priv->ucode_trace,
+                       jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD));
+       } else {
+               priv->event_log.ucode_trace = false;
+               del_timer_sync(&priv->ucode_trace);
+       }
+
+       return count;
+}
+
 DEBUGFS_READ_FILE_OPS(rx_statistics);
 DEBUGFS_READ_FILE_OPS(tx_statistics);
 DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
@@ -1859,6 +1951,8 @@ DEBUGFS_READ_FILE_OPS(tx_power);
 DEBUGFS_READ_FILE_OPS(power_save_status);
 DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics);
 DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics);
+DEBUGFS_WRITE_FILE_OPS(csr);
+DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing);
 
 /*
  * Create the debugfs files and directories
@@ -1889,7 +1983,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
        DEBUGFS_ADD_DIR(debug, dbgfs->dir_drv);
        DEBUGFS_ADD_FILE(nvm, data, S_IRUSR);
        DEBUGFS_ADD_FILE(sram, data, S_IWUSR | S_IRUSR);
-       DEBUGFS_ADD_FILE(log_event, data, S_IWUSR);
+       DEBUGFS_ADD_FILE(log_event, data, S_IWUSR | S_IRUSR);
        DEBUGFS_ADD_FILE(stations, data, S_IRUSR);
        DEBUGFS_ADD_FILE(channels, data, S_IRUSR);
        DEBUGFS_ADD_FILE(status, data, S_IRUSR);
@@ -1909,12 +2003,14 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
        DEBUGFS_ADD_FILE(power_save_status, debug, S_IRUSR);
        DEBUGFS_ADD_FILE(clear_ucode_statistics, debug, S_IWUSR);
        DEBUGFS_ADD_FILE(clear_traffic_statistics, debug, S_IWUSR);
+       DEBUGFS_ADD_FILE(csr, debug, S_IWUSR);
        if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
                DEBUGFS_ADD_FILE(ucode_rx_stats, debug, S_IRUSR);
                DEBUGFS_ADD_FILE(ucode_tx_stats, debug, S_IRUSR);
                DEBUGFS_ADD_FILE(ucode_general_stats, debug, S_IRUSR);
                DEBUGFS_ADD_FILE(sensitivity, debug, S_IRUSR);
                DEBUGFS_ADD_FILE(chain_noise, debug, S_IRUSR);
+               DEBUGFS_ADD_FILE(ucode_tracing, debug, S_IWUSR | S_IRUSR);
        }
        DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal);
        DEBUGFS_ADD_BOOL(disable_chain_noise, rf,
@@ -1966,6 +2062,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
                        file_clear_ucode_statistics);
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
                        file_clear_traffic_statistics);
+       DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_csr);
        if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
                DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
                        file_ucode_rx_stats);
@@ -1977,6 +2074,8 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
                        file_sensitivity);
                DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
                        file_chain_noise);
+               DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
+                       file_ucode_tracing);
        }
        DEBUGFS_REMOVE(priv->dbgfs->dir_debug);
        DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity);
index 165d1f6e2dd9304046895b437fd65e0d2f88e580..42f9b17327c34040fd3e976a442fb23c0a108055 100644 (file)
@@ -512,6 +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 @@ 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 */
@@ -1261,6 +1288,7 @@ struct iwl_priv {
        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
@@ -1268,6 +1296,8 @@ struct iwl_priv {
        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)
index e7d88d1da15d9d5e2c49d11f7b07d1d4ed1f6363..bf46308b17fad9535dbd53b5ebedebdc0b66dbfa 100644 (file)
@@ -11,4 +11,6 @@ EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_iowrite32);
 EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_rx);
 EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event);
 EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event);
+EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event);
 #endif
index 21361968ab7e86ce1ed14bd4d8bc917b5df4213c..0819f990be6ccaadcf37971ccf25483c5eb171d2 100644 (file)
@@ -64,6 +64,50 @@ TRACE_EVENT(iwlwifi_dev_iowrite32,
        TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val)
 );
 
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iwlwifi_ucode
+
+TRACE_EVENT(iwlwifi_dev_ucode_cont_event,
+       TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev),
+       TP_ARGS(priv, time, data, ev),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+
+               __field(u32, time)
+               __field(u32, data)
+               __field(u32, ev)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->time = time;
+               __entry->data = data;
+               __entry->ev = ev;
+       ),
+       TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u",
+                 __entry->priv, __entry->time, __entry->data, __entry->ev)
+);
+
+TRACE_EVENT(iwlwifi_dev_ucode_wrap_event,
+       TP_PROTO(struct iwl_priv *priv, u32 wraps, u32 n_entry, u32 p_entry),
+       TP_ARGS(priv, wraps, n_entry, p_entry),
+       TP_STRUCT__entry(
+               PRIV_ENTRY
+
+               __field(u32, wraps)
+               __field(u32, n_entry)
+               __field(u32, p_entry)
+       ),
+       TP_fast_assign(
+               PRIV_ASSIGN;
+               __entry->wraps = wraps;
+               __entry->n_entry = n_entry;
+               __entry->p_entry = p_entry;
+       ),
+       TP_printk("[%p] wraps=#%02d n=0x%X p=0x%X",
+                 __entry->priv, __entry->wraps, __entry->n_entry,
+                 __entry->p_entry)
+);
+
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM iwlwifi
 
index f8e4e4b18d02bee66f711e29080c4cc2b8a6366a..10b0aa8024c435d017e0455265ea9e24663e71eb 100644 (file)
@@ -1518,8 +1518,9 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
  * 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 */
@@ -1529,7 +1530,7 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
        unsigned long reg_flags;
 
        if (num_events == 0)
-               return;
+               return pos;
 
        base = le32_to_cpu(priv->card_alive.log_event_table_ptr);
 
@@ -1555,26 +1556,43 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
                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
@@ -1582,21 +1600,28 @@ static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
         */
        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 */
@@ -1604,7 +1629,8 @@ static void iwl3945_print_last_event_logs(struct iwl_priv *priv, u32 capacity,
 
 #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 */
@@ -1612,11 +1638,13 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
        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 */
@@ -1642,7 +1670,7 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
        /* 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
@@ -1658,25 +1686,38 @@ void iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log)
                  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)
index 3db3d8b07491c1428bda6b166fd624ac9a1166bf..fd399e6b1930dfeea0998f40f702b6ecc869addf 100644 (file)
@@ -868,36 +868,35 @@ static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf,
        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);
@@ -1534,6 +1533,33 @@ static void classify8023(struct sk_buff *skb)
        }
 }
 
+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)
@@ -1548,25 +1574,34 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm,
        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++;
index 30aa9d48d67e03ffad9c904f143ed6ff0c3ba355..0485c99575757841edb0f929205d4963c6d082a2 100644 (file)
@@ -37,3 +37,9 @@ config LIBERTAS_DEBUG
        depends on LIBERTAS
        ---help---
          Debugging support.
+
+config LIBERTAS_MESH
+       bool "Enable mesh support"
+       depends on LIBERTAS
+       help
+         This enables Libertas' MESH support, used by e.g. the OLPC people.
index b188cd97a053f8d9459eaf6db9038eb0057a7dbf..45e870e331175f30d47edc2ed85091bce1a924ed 100644 (file)
@@ -5,11 +5,11 @@ libertas-y += cmdresp.o
 libertas-y += debugfs.o
 libertas-y += ethtool.o
 libertas-y += main.o
-libertas-y += mesh.o
 libertas-y += rx.o
 libertas-y += scan.o
 libertas-y += tx.o
 libertas-y += wext.o
+libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o
 
 usb8xxx-objs += if_usb.o
 libertas_cs-objs += if_cs.o
index 751067369ba84ec8506bbe7b087f3691ab3fd0cc..5e650f35841545313d160baf4e658d48bb83d2a8 100644 (file)
@@ -390,10 +390,8 @@ int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
        cmd.enablehwauto = cpu_to_le16(priv->enablehwauto);
        cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto);
        ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd);
-       if (!ret && cmd_action == CMD_ACT_GET) {
-               priv->ratebitmap = le16_to_cpu(cmd.bitmap);
+       if (!ret && cmd_action == CMD_ACT_GET)
                priv->enablehwauto = le16_to_cpu(cmd.enablehwauto);
-       }
 
        lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
        return ret;
@@ -807,8 +805,7 @@ static int lbs_try_associate(struct lbs_private *priv,
        }
 
        /* Use short preamble only when both the BSS and firmware support it */
-       if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
-           (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
+       if (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
                preamble = RADIO_PREAMBLE_SHORT;
 
        ret = lbs_set_radio(priv, preamble, 1);
@@ -939,8 +936,7 @@ static int lbs_adhoc_join(struct lbs_private *priv,
        }
 
        /* Use short preamble only when both the BSS and firmware support it */
-       if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
-           (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
+       if (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
                lbs_deb_join("AdhocJoin: Short preamble\n");
                preamble = RADIO_PREAMBLE_SHORT;
        }
@@ -1049,7 +1045,7 @@ static int lbs_adhoc_start(struct lbs_private *priv,
        struct assoc_request *assoc_req)
 {
        struct cmd_ds_802_11_ad_hoc_start cmd;
-       u8 preamble = RADIO_PREAMBLE_LONG;
+       u8 preamble = RADIO_PREAMBLE_SHORT;
        size_t ratesize = 0;
        u16 tmpcap = 0;
        int ret = 0;
@@ -1057,11 +1053,6 @@ static int lbs_adhoc_start(struct lbs_private *priv,
 
        lbs_deb_enter(LBS_DEB_ASSOC);
 
-       if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
-               lbs_deb_join("ADHOC_START: Will use short preamble\n");
-               preamble = RADIO_PREAMBLE_SHORT;
-       }
-
        ret = lbs_set_radio(priv, preamble, 1);
        if (ret)
                goto out;
index b9b371bfa30fffd30f3b5525863f312707d0f2e3..42051f7cad6df61794ec711dc09d5c297cb3c845 100644 (file)
@@ -143,19 +143,6 @@ int lbs_update_hw_spec(struct lbs_private *priv)
        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 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
        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 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
                ret = 0;
                break;
 
+#ifdef CONFIG_LIBERTAS_MESH
+
        case CMD_BT_ACCESS:
                ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf);
                break;
@@ -1019,6 +1005,8 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
                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 @@ int lbs_execute_next_command(struct lbs_private *priv)
                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 */
index 2862748aef70778f11cc40335827bda66dad0161..cb4138a55fdf9057fe13a0b8e1ced435cf0f0572 100644 (file)
@@ -110,18 +110,6 @@ int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val);
 int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val);
 
 
-/* Mesh related */
-
-int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
-                   struct cmd_ds_mesh_access *cmd);
-
-int lbs_mesh_config_send(struct lbs_private *priv,
-                        struct cmd_ds_mesh_config *cmd,
-                        uint16_t action, uint16_t type);
-
-int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
-
-
 /* Commands only used in wext.c, assoc. and scan.c */
 
 int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
index 21d57690c20a0cb5d213a14447568d6fa1113fbc..0334a58820eed220d2000fc25fcf64ba7e0deeb8 100644 (file)
@@ -485,20 +485,8 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
                break;
 
        case MACREG_INT_CODE_MESH_AUTO_STARTED:
-               /* Ignore spurious autostart events if autostart is disabled */
-               if (!priv->mesh_autostart_enabled) {
-                       lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
-                       break;
-               }
-               lbs_pr_info("EVENT: MESH_AUTO_STARTED\n");
-               priv->mesh_connect_status = LBS_CONNECTED;
-               if (priv->mesh_open) {
-                       netif_carrier_on(priv->mesh_dev);
-                       if (!priv->tx_pending_len)
-                               netif_wake_queue(priv->mesh_dev);
-               }
-               priv->mode = IW_MODE_ADHOC;
-               schedule_work(&priv->sync_channel);
+               /* Ignore spurious autostart events */
+               lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
                break;
 
        default:
index 6b6ea9f7bf5b37de87186a9db1248ab49b28c165..ea3f10ef4e0032c845a15cf973b1c9ffe925b504 100644 (file)
@@ -397,13 +397,6 @@ enum KEY_INFO_WPA {
        KEY_INFO_WPA_ENABLED = 0x04
 };
 
-/** mesh_fw_ver */
-enum _mesh_fw_ver {
-       MESH_NONE = 0, /* MESH is not supported */
-       MESH_FW_OLD,   /* MESH is supported in FW V5 */
-       MESH_FW_NEW,   /* MESH is supported in FW V10 and newer */
-};
-
 /* Default values for fwt commands. */
 #define FWT_DEFAULT_METRIC 0
 #define FWT_DEFAULT_DIR 1
index 6a8d2b291d8c6d20495881257d2771ddc844673f..d5a9dcae40596b966b0419cb85cbe60468ada958 100644 (file)
@@ -39,15 +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;
@@ -176,9 +175,7 @@ struct lbs_private {
        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 63d020374c2bda16b4176c7e5996628de2e7fedc..3804a58d7f4e8903cb8788457c8b99f8bd469419 100644 (file)
@@ -114,9 +114,11 @@ const struct ethtool_ops lbs_ethtool_ops = {
        .get_drvinfo = lbs_ethtool_get_drvinfo,
        .get_eeprom =  lbs_ethtool_get_eeprom,
        .get_eeprom_len = lbs_ethtool_get_eeprom_len,
+#ifdef CONFIG_LIBERTAS_MESH
        .get_sset_count = lbs_mesh_ethtool_get_sset_count,
        .get_ethtool_stats = lbs_mesh_ethtool_get_stats,
        .get_strings = lbs_mesh_ethtool_get_strings,
+#endif
        .get_wol = lbs_ethtool_get_wol,
        .set_wol = lbs_ethtool_set_wol,
 };
index db38a5a719fa58f05c8233826360bbc18d02d9cc..f9f195f7b17cb6145310641f9c0f738fd1ea036f 100644 (file)
@@ -123,7 +123,7 @@ static ssize_t lbs_rtap_set(struct device *dev,
                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,
@@ -619,7 +619,7 @@ static int lbs_thread(void *data)
                                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);
                        }
                }
@@ -806,18 +806,6 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
        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;
@@ -845,14 +833,12 @@ static int lbs_init_adapter(struct lbs_private *priv)
        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;
@@ -997,11 +983,6 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
        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;
@@ -1075,6 +1056,17 @@ void lbs_remove_card(struct lbs_private *priv)
 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;
@@ -1094,12 +1086,14 @@ int lbs_start_card(struct lbs_private *priv)
 
        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");
        }
@@ -1133,7 +1127,9 @@ void lbs_stop_card(struct lbs_private *priv)
        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 */
index 2f91c9b808af0b2b9434b771381b88c95b548dc8..954cd00f74526e19d917c301f0c67d9e8f4a6876 100644 (file)
@@ -1,4 +1,3 @@
-#include <linux/moduleparam.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
 #include <linux/netdevice.h>
@@ -196,7 +195,14 @@ int lbs_init_mesh(struct lbs_private *priv)
 
        lbs_deb_enter(LBS_DEB_MESH);
 
-       if (priv->mesh_fw_ver == MESH_FW_OLD) {
+       priv->mesh_connect_status = LBS_DISCONNECTED;
+
+       /* 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) {
                /* Enable mesh, if supported, and work out which TLV it uses.
                   0x100 + 291 is an unofficial value used in 5.110.20.pXX
                   0x100 + 37 is the official value used in 5.110.21.pXX
@@ -218,7 +224,9 @@ int lbs_init_mesh(struct lbs_private *priv)
                                            priv->channel))
                                priv->mesh_tlv = 0;
                }
-       } else if (priv->mesh_fw_ver == MESH_FW_NEW) {
+       } else
+       if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
+               (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
                /* 10.0.0.pXX new firmwares should succeed with TLV
                 * 0x100+37; Do not invoke command with old TLV.
                 */
@@ -227,7 +235,12 @@ int lbs_init_mesh(struct lbs_private *priv)
                                    priv->channel))
                        priv->mesh_tlv = 0;
        }
+
+
        if (priv->mesh_tlv) {
+               sprintf(priv->mesh_ssid, "mesh");
+               priv->mesh_ssid_len = 4;
+
                lbs_add_mesh(priv);
 
                if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
@@ -416,10 +429,10 @@ struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
        struct net_device *dev, struct rxpd *rxpd)
 {
        if (priv->mesh_dev) {
-               if (priv->mesh_fw_ver == MESH_FW_OLD) {
+               if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
                        if (rxpd->rx_control & RxPD_MESH_FRAME)
                                dev = priv->mesh_dev;
-               } else if (priv->mesh_fw_ver == MESH_FW_NEW) {
+               } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
                        if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
                                dev = priv->mesh_dev;
                }
@@ -432,9 +445,9 @@ void lbs_mesh_set_txpd(struct lbs_private *priv,
        struct net_device *dev, struct txpd *txpd)
 {
        if (dev == priv->mesh_dev) {
-               if (priv->mesh_fw_ver == MESH_FW_OLD)
+               if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
                        txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
-               else if (priv->mesh_fw_ver == MESH_FW_NEW)
+               else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
                        txpd->u.bss.bss_num = MESH_IFACE_ID;
        }
 }
@@ -538,7 +551,7 @@ static int __lbs_mesh_config_send(struct lbs_private *priv,
         * Command id is 0xac for v10 FW along with mesh interface
         * id in bits 14-13-12.
         */
-       if (priv->mesh_fw_ver == MESH_FW_NEW)
+       if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
                command = CMD_MESH_CONFIG |
                          (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
 
index fea9b5d005fcb2bfe218b676c5f3eacd0f89d3a9..e2573303a328f63ac2e28f23f64739c0759d1017 100644 (file)
@@ -9,6 +9,8 @@
 #include <net/lib80211.h>
 
 
+#ifdef CONFIG_LIBERTAS_MESH
+
 /* Mesh statistics */
 struct lbs_mesh_stats {
        u32     fwd_bcast_cnt;          /* Fwd: Broadcast counter */
@@ -46,11 +48,20 @@ void lbs_mesh_set_txpd(struct lbs_private *priv,
 /* Command handling */
 
 struct cmd_ds_command;
+struct cmd_ds_mesh_access;
+struct cmd_ds_mesh_config;
 
 int lbs_cmd_bt_access(struct cmd_ds_command *cmd,
        u16 cmd_action, void *pdata_buf);
 int lbs_cmd_fwt_access(struct cmd_ds_command *cmd,
        u16 cmd_action, void *pdata_buf);
+int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+                   struct cmd_ds_mesh_access *cmd);
+int lbs_mesh_config_send(struct lbs_private *priv,
+                        struct cmd_ds_mesh_config *cmd,
+                        uint16_t action, uint16_t type);
+int lbs_mesh_config(struct lbs_private *priv, uint16_t enable, uint16_t chan);
+
 
 
 /* Persistent configuration */
@@ -75,4 +86,25 @@ void lbs_mesh_ethtool_get_strings(struct net_device *dev,
        uint32_t stringset, uint8_t *s);
 
 
+/* Accessors */
+
+#define lbs_mesh_open(priv) (priv->mesh_open)
+#define lbs_mesh_connected(priv) (priv->mesh_connect_status == LBS_CONNECTED)
+
+#else
+
+#define lbs_init_mesh(priv)
+#define lbs_deinit_mesh(priv)
+#define lbs_add_mesh(priv)
+#define lbs_remove_mesh(priv)
+#define lbs_mesh_set_dev(priv, dev, rxpd) (dev)
+#define lbs_mesh_set_txpd(priv, dev, txpd)
+#define lbs_mesh_config(priv, enable, chan)
+#define lbs_mesh_open(priv) (0)
+#define lbs_mesh_connected(priv) (0)
+
+#endif
+
+
+
 #endif
index b0b1c7841500281eb7031554131aa8e713cdc3b4..220361e69cd3adc7a8a2f680457b49cec2711aaa 100644 (file)
@@ -635,7 +635,7 @@ out:
        if (priv->connect_status == LBS_CONNECTED && !priv->tx_pending_len)
                netif_wake_queue(priv->dev);
 
-       if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED) &&
+       if (priv->mesh_dev && lbs_mesh_connected(priv) &&
            !priv->tx_pending_len)
                netif_wake_queue(priv->mesh_dev);
 
index 315d1ce286caa1d4c0939ffa30304b4775e2b07d..52d244ea3d972809f7a528d8077dea7262879505 100644 (file)
@@ -198,7 +198,7 @@ void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count)
        if (priv->connect_status == LBS_CONNECTED)
                netif_wake_queue(priv->dev);
 
-       if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED))
+       if (priv->mesh_dev && lbs_mesh_connected(priv))
                netif_wake_queue(priv->mesh_dev);
 }
 EXPORT_SYMBOL_GPL(lbs_send_tx_feedback);
index 4b1aab593a84ea93fbfd7721d58004e65113fd26..71f88a08e0906200b6053fcb5875dbeed91da894 100644 (file)
@@ -192,7 +192,7 @@ static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
        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 @@ static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
        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)
 {
@@ -307,7 +308,7 @@ static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
 
        /* 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);
@@ -321,6 +322,7 @@ static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
        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 @@ static int lbs_get_mode(struct net_device *dev,
        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)
@@ -433,6 +436,7 @@ static int mesh_wlan_get_mode(struct net_device *dev,
        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 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
 
        /* 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 @@ 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 @@ 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)
@@ -2108,6 +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 +2168,7 @@ static int lbs_mesh_set_essid(struct net_device *dev,
        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 +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 */
@@ -2325,14 +2339,10 @@ static const iw_handler mesh_wlan_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,
-};
 
 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 26a1abd5bb031c9b9c63565eaf1b9e298373d136..ba3eb0101d558b7247b36adc00449f414a2d9159 100644 (file)
@@ -318,14 +318,14 @@ static void lbtf_op_stop(struct ieee80211_hw *hw)
 }
 
 static int lbtf_op_add_interface(struct ieee80211_hw *hw,
-                       struct ieee80211_if_init_conf *conf)
+                       struct ieee80211_vif *vif)
 {
        struct lbtf_private *priv = hw->priv;
        if (priv->vif != NULL)
                return -EOPNOTSUPP;
 
-       priv->vif = conf->vif;
-       switch (conf->type) {
+       priv->vif = vif;
+       switch (vif->type) {
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                lbtf_set_mode(priv, LBTF_AP_MODE);
@@ -337,12 +337,12 @@ static int lbtf_op_add_interface(struct ieee80211_hw *hw,
                priv->vif = NULL;
                return -EOPNOTSUPP;
        }
-       lbtf_set_mac_address(priv, (u8 *) conf->mac_addr);
+       lbtf_set_mac_address(priv, (u8 *) vif->addr);
        return 0;
 }
 
 static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
-                       struct ieee80211_if_init_conf *conf)
+                       struct ieee80211_vif *vif)
 {
        struct lbtf_private *priv = hw->priv;
 
index 88e41176e7fd243ee773adc492c3207f6faeb56b..84df3fcf37b393c35d4fac12ce948448a7191d12 100644 (file)
@@ -436,6 +436,38 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
 }
 
 
+struct mac80211_hwsim_addr_match_data {
+       bool ret;
+       const u8 *addr;
+};
+
+static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_addr_match_data *md = data;
+       if (memcmp(mac, md->addr, ETH_ALEN) == 0)
+               md->ret = true;
+}
+
+
+static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
+                                     const u8 *addr)
+{
+       struct mac80211_hwsim_addr_match_data md;
+
+       if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0)
+               return true;
+
+       md.ret = false;
+       md.addr = addr;
+       ieee80211_iterate_active_interfaces_atomic(data->hw,
+                                                  mac80211_hwsim_addr_iter,
+                                                  &md);
+
+       return md.ret;
+}
+
+
 static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
                                    struct sk_buff *skb)
 {
@@ -488,8 +520,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
                if (nskb == NULL)
                        continue;
 
-               if (memcmp(hdr->addr1, data2->hw->wiphy->perm_addr,
-                          ETH_ALEN) == 0)
+               if (mac80211_hwsim_addr_match(data2, hdr->addr1))
                        ack = true;
                memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
                ieee80211_rx_irqsafe(data2->hw, nskb);
@@ -553,24 +584,24 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
 
 
 static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
-                                       struct ieee80211_if_init_conf *conf)
+                                       struct ieee80211_vif *vif)
 {
        printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%pM)\n",
-              wiphy_name(hw->wiphy), __func__, conf->type,
-              conf->mac_addr);
-       hwsim_set_magic(conf->vif);
+              wiphy_name(hw->wiphy), __func__, vif->type,
+              vif->addr);
+       hwsim_set_magic(vif);
        return 0;
 }
 
 
 static void mac80211_hwsim_remove_interface(
-       struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf)
+       struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        printk(KERN_DEBUG "%s:%s (type=%d mac_addr=%pM)\n",
-              wiphy_name(hw->wiphy), __func__, conf->type,
-              conf->mac_addr);
-       hwsim_check_magic(conf->vif);
-       hwsim_clear_magic(conf->vif);
+              wiphy_name(hw->wiphy), __func__, vif->type,
+              vif->addr);
+       hwsim_check_magic(vif);
+       hwsim_clear_magic(vif);
 }
 
 
@@ -618,12 +649,26 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct mac80211_hwsim_data *data = hw->priv;
        struct ieee80211_conf *conf = &hw->conf;
-
-       printk(KERN_DEBUG "%s:%s (freq=%d idle=%d ps=%d)\n",
+       static const char *chantypes[4] = {
+               [NL80211_CHAN_NO_HT] = "noht",
+               [NL80211_CHAN_HT20] = "ht20",
+               [NL80211_CHAN_HT40MINUS] = "ht40-",
+               [NL80211_CHAN_HT40PLUS] = "ht40+",
+       };
+       static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
+               [IEEE80211_SMPS_AUTOMATIC] = "auto",
+               [IEEE80211_SMPS_OFF] = "off",
+               [IEEE80211_SMPS_STATIC] = "static",
+               [IEEE80211_SMPS_DYNAMIC] = "dynamic",
+       };
+
+       printk(KERN_DEBUG "%s:%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
               wiphy_name(hw->wiphy), __func__,
               conf->channel->center_freq,
+              chantypes[conf->channel_type],
               !!(conf->flags & IEEE80211_CONF_IDLE),
-              !!(conf->flags & IEEE80211_CONF_PS));
+              !!(conf->flags & IEEE80211_CONF_PS),
+              smps_modes[conf->smps_mode]);
 
        data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
 
@@ -827,6 +872,41 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw,
 }
 #endif
 
+static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      enum ieee80211_ampdu_mlme_action action,
+                                      struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+       switch (action) {
+       case IEEE80211_AMPDU_TX_START:
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       case IEEE80211_AMPDU_TX_STOP:
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               break;
+       case IEEE80211_AMPDU_RX_START:
+       case IEEE80211_AMPDU_RX_STOP:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
+{
+       /*
+        * In this special case, there's nothing we need to
+        * do because hwsim does transmission synchronously.
+        * In the future, when it does transmissions via
+        * userspace, we may need to do something.
+        */
+}
+
+
 static const struct ieee80211_ops mac80211_hwsim_ops =
 {
        .tx = mac80211_hwsim_tx,
@@ -841,6 +921,8 @@ static const struct ieee80211_ops mac80211_hwsim_ops =
        .set_tim = mac80211_hwsim_set_tim,
        .conf_tx = mac80211_hwsim_conf_tx,
        CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd)
+       .ampdu_action = mac80211_hwsim_ampdu_action,
+       .flush = mac80211_hwsim_flush,
 };
 
 
@@ -1082,7 +1164,9 @@ static int __init init_mac80211_hwsim(void)
                        BIT(NL80211_IFTYPE_MESH_POINT);
 
                hw->flags = IEEE80211_HW_MFP_CAPABLE |
-                           IEEE80211_HW_SIGNAL_DBM;
+                           IEEE80211_HW_SIGNAL_DBM |
+                           IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+                           IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
 
                /* ask mac80211 to reserve space for magic */
                hw->vif_data_size = sizeof(struct hwsim_vif_priv);
index 59f92105b0c24880980af6339f6589f469eefa52..a04863633d1a5960b1f283518ea38b8703be0285 100644 (file)
@@ -26,7 +26,7 @@
 
 #define MWL8K_DESC     "Marvell TOPDOG(R) 802.11 Wireless Network Driver"
 #define MWL8K_NAME     KBUILD_MODNAME
-#define MWL8K_VERSION  "0.10"
+#define MWL8K_VERSION  "0.11"
 
 /* Register definitions */
 #define MWL8K_HIU_GEN_PTR                      0x00000c10
@@ -92,8 +92,7 @@ struct mwl8k_device_info {
        char *part_name;
        char *helper_image;
        char *fw_image;
-       struct rxd_ops *rxd_ops;
-       u16 modes;
+       struct rxd_ops *ap_rxd_ops;
 };
 
 struct mwl8k_rx_queue {
@@ -126,29 +125,23 @@ struct mwl8k_tx_queue {
        struct sk_buff **skb;
 };
 
-/* Pointers to the firmware data and meta information about it.  */
-struct mwl8k_firmware {
-       /* Boot helper code */
-       struct firmware *helper;
+struct mwl8k_priv {
+       struct ieee80211_hw *hw;
+       struct pci_dev *pdev;
 
-       /* Microcode */
-       struct firmware *ucode;
-};
+       struct mwl8k_device_info *device_info;
 
-struct mwl8k_priv {
        void __iomem *sram;
        void __iomem *regs;
-       struct ieee80211_hw *hw;
 
-       struct pci_dev *pdev;
+       /* firmware */
+       struct firmware *fw_helper;
+       struct firmware *fw_ucode;
 
-       struct mwl8k_device_info *device_info;
+       /* hardware/firmware parameters */
        bool ap_fw;
        struct rxd_ops *rxd_ops;
 
-       /* firmware files and meta data */
-       struct mwl8k_firmware fw;
-
        /* firmware access */
        struct mutex fw_mutex;
        struct task_struct *fw_mutex_owner;
@@ -192,6 +185,10 @@ struct mwl8k_priv {
        bool sniffer_enabled;
        bool wmm_enabled;
 
+       struct work_struct sta_notify_worker;
+       spinlock_t sta_notify_list_lock;
+       struct list_head sta_notify_list;
+
        /* XXX need to convert this to handle multiple interfaces */
        bool capture_beacon;
        u8 capture_bssid[ETH_ALEN];
@@ -211,25 +208,20 @@ struct mwl8k_priv {
 
 /* Per interface specific private data */
 struct mwl8k_vif {
-       /* backpointer to parent config block */
-       struct mwl8k_priv *priv;
-
-       /* BSS config of AP or IBSS from mac80211*/
-       struct ieee80211_bss_conf bss_info;
-
-       /* BSSID of AP or IBSS */
-       u8      bssid[ETH_ALEN];
-       u8      mac_addr[ETH_ALEN];
-
-        /* Index into station database.Returned by update_sta_db call */
-       u8      peer_id;
+       /* Local MAC address.  */
+       u8 mac_addr[ETH_ALEN];
 
        /* Non AMPDU sequence number assigned by driver */
-       u16     seqno;
+       u16 seqno;
 };
-
 #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
 
+struct mwl8k_sta {
+       /* Index into station database. Returned by UPDATE_STADB.  */
+       u8 peer_id;
+};
+#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))
+
 static const struct ieee80211_channel mwl8k_channels[] = {
        { .center_freq = 2412, .hw_value = 1, },
        { .center_freq = 2417, .hw_value = 2, },
@@ -242,6 +234,9 @@ static const struct ieee80211_channel mwl8k_channels[] = {
        { .center_freq = 2452, .hw_value = 9, },
        { .center_freq = 2457, .hw_value = 10, },
        { .center_freq = 2462, .hw_value = 11, },
+       { .center_freq = 2467, .hw_value = 12, },
+       { .center_freq = 2472, .hw_value = 13, },
+       { .center_freq = 2484, .hw_value = 14, },
 };
 
 static const struct ieee80211_rate mwl8k_rates[] = {
@@ -261,10 +256,6 @@ static const struct ieee80211_rate mwl8k_rates[] = {
        { .bitrate = 720, .hw_value = 144, },
 };
 
-static const u8 mwl8k_rateids[12] = {
-       2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108,
-};
-
 /* Set or get info from Firmware */
 #define MWL8K_CMD_SET                  0x0001
 #define MWL8K_CMD_GET                  0x0000
@@ -355,8 +346,8 @@ static void mwl8k_release_fw(struct firmware **fw)
 
 static void mwl8k_release_firmware(struct mwl8k_priv *priv)
 {
-       mwl8k_release_fw(&priv->fw.ucode);
-       mwl8k_release_fw(&priv->fw.helper);
+       mwl8k_release_fw(&priv->fw_ucode);
+       mwl8k_release_fw(&priv->fw_helper);
 }
 
 /* Request fw image */
@@ -377,7 +368,7 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv)
        int rc;
 
        if (di->helper_image != NULL) {
-               rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw.helper);
+               rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper);
                if (rc) {
                        printk(KERN_ERR "%s: Error requesting helper "
                               "firmware file %s\n", pci_name(priv->pdev),
@@ -386,11 +377,11 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv)
                }
        }
 
-       rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw.ucode);
+       rc = mwl8k_request_fw(priv, di->fw_image, &priv->fw_ucode);
        if (rc) {
                printk(KERN_ERR "%s: Error requesting firmware file %s\n",
                       pci_name(priv->pdev), di->fw_image);
-               mwl8k_release_fw(&priv->fw.helper);
+               mwl8k_release_fw(&priv->fw_helper);
                return rc;
        }
 
@@ -551,13 +542,12 @@ static int mwl8k_feed_fw_image(struct mwl8k_priv *priv,
 static int mwl8k_load_firmware(struct ieee80211_hw *hw)
 {
        struct mwl8k_priv *priv = hw->priv;
-       struct firmware *fw = priv->fw.ucode;
-       struct mwl8k_device_info *di = priv->device_info;
+       struct firmware *fw = priv->fw_ucode;
        int rc;
        int loops;
 
        if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) {
-               struct firmware *helper = priv->fw.helper;
+               struct firmware *helper = priv->fw_helper;
 
                if (helper == NULL) {
                        printk(KERN_ERR "%s: helper image needed but none "
@@ -584,10 +574,7 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)
                return rc;
        }
 
-       if (di->modes & BIT(NL80211_IFTYPE_AP))
-               iowrite32(MWL8K_MODE_AP, priv->regs + MWL8K_HIU_GEN_PTR);
-       else
-               iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
+       iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR);
 
        loops = 500000;
        do {
@@ -610,91 +597,6 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)
 }
 
 
-/*
- * Defines shared between transmission and reception.
- */
-/* HT control fields for firmware */
-struct ewc_ht_info {
-       __le16  control1;
-       __le16  control2;
-       __le16  control3;
-} __attribute__((packed));
-
-/* Firmware Station database operations */
-#define MWL8K_STA_DB_ADD_ENTRY         0
-#define MWL8K_STA_DB_MODIFY_ENTRY      1
-#define MWL8K_STA_DB_DEL_ENTRY         2
-#define MWL8K_STA_DB_FLUSH             3
-
-/* Peer Entry flags - used to define the type of the peer node */
-#define MWL8K_PEER_TYPE_ACCESSPOINT    2
-
-struct peer_capability_info {
-       /* Peer type - AP vs. STA.  */
-       __u8    peer_type;
-
-       /* Basic 802.11 capabilities from assoc resp.  */
-       __le16  basic_caps;
-
-       /* Set if peer supports 802.11n high throughput (HT).  */
-       __u8    ht_support;
-
-       /* Valid if HT is supported.  */
-       __le16  ht_caps;
-       __u8    extended_ht_caps;
-       struct ewc_ht_info      ewc_info;
-
-       /* Legacy rate table. Intersection of our rates and peer rates.  */
-       __u8    legacy_rates[12];
-
-       /* HT rate table. Intersection of our rates and peer rates.  */
-       __u8    ht_rates[16];
-       __u8    pad[16];
-
-       /* If set, interoperability mode, no proprietary extensions.  */
-       __u8    interop;
-       __u8    pad2;
-       __u8    station_id;
-       __le16  amsdu_enabled;
-} __attribute__((packed));
-
-/* Inline functions to manipulate QoS field in data descriptor.  */
-static inline u16 mwl8k_qos_setbit_eosp(u16 qos)
-{
-       u16 val_mask = 1 << 4;
-
-       /* End of Service Period Bit 4 */
-       return qos | val_mask;
-}
-
-static inline u16 mwl8k_qos_setbit_ack(u16 qos, u8 ack_policy)
-{
-       u16 val_mask = 0x3;
-       u8      shift = 5;
-       u16 qos_mask = ~(val_mask << shift);
-
-       /* Ack Policy Bit 5-6 */
-       return (qos & qos_mask) | ((ack_policy & val_mask) << shift);
-}
-
-static inline u16 mwl8k_qos_setbit_amsdu(u16 qos)
-{
-       u16 val_mask = 1 << 7;
-
-       /* AMSDU present Bit 7 */
-       return qos | val_mask;
-}
-
-static inline u16 mwl8k_qos_setbit_qlen(u16 qos, u8 len)
-{
-       u16 val_mask = 0xff;
-       u8      shift = 8;
-       u16 qos_mask = ~(val_mask << shift);
-
-       /* Queue Length Bits 8-15 */
-       return (qos & qos_mask) | ((len & val_mask) << shift);
-}
-
 /* DMA header used by firmware and hardware.  */
 struct mwl8k_dma_data {
        __le16 fwlen;
@@ -761,9 +663,9 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb)
 
 
 /*
- * Packet reception for 88w8366.
+ * Packet reception for 88w8366 AP firmware.
  */
-struct mwl8k_rxd_8366 {
+struct mwl8k_rxd_8366_ap {
        __le16 pkt_len;
        __u8 sq2;
        __u8 rate;
@@ -781,23 +683,23 @@ struct mwl8k_rxd_8366 {
        __u8 rx_ctrl;
 } __attribute__((packed));
 
-#define MWL8K_8366_RATE_INFO_MCS_FORMAT                0x80
-#define MWL8K_8366_RATE_INFO_40MHZ             0x40
-#define MWL8K_8366_RATE_INFO_RATEID(x)         ((x) & 0x3f)
+#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT     0x80
+#define MWL8K_8366_AP_RATE_INFO_40MHZ          0x40
+#define MWL8K_8366_AP_RATE_INFO_RATEID(x)      ((x) & 0x3f)
 
-#define MWL8K_8366_RX_CTRL_OWNED_BY_HOST       0x80
+#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST    0x80
 
-static void mwl8k_rxd_8366_init(void *_rxd, dma_addr_t next_dma_addr)
+static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
 {
-       struct mwl8k_rxd_8366 *rxd = _rxd;
+       struct mwl8k_rxd_8366_ap *rxd = _rxd;
 
        rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
-       rxd->rx_ctrl = MWL8K_8366_RX_CTRL_OWNED_BY_HOST;
+       rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST;
 }
 
-static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len)
+static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)
 {
-       struct mwl8k_rxd_8366 *rxd = _rxd;
+       struct mwl8k_rxd_8366_ap *rxd = _rxd;
 
        rxd->pkt_len = cpu_to_le16(len);
        rxd->pkt_phys_addr = cpu_to_le32(addr);
@@ -806,12 +708,12 @@ static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len)
 }
 
 static int
-mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status,
-                      __le16 *qos)
+mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
+                         __le16 *qos)
 {
-       struct mwl8k_rxd_8366 *rxd = _rxd;
+       struct mwl8k_rxd_8366_ap *rxd = _rxd;
 
-       if (!(rxd->rx_ctrl & MWL8K_8366_RX_CTRL_OWNED_BY_HOST))
+       if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST))
                return -1;
        rmb();
 
@@ -820,11 +722,11 @@ mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status,
        status->signal = -rxd->rssi;
        status->noise = -rxd->noise_floor;
 
-       if (rxd->rate & MWL8K_8366_RATE_INFO_MCS_FORMAT) {
+       if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) {
                status->flag |= RX_FLAG_HT;
-               if (rxd->rate & MWL8K_8366_RATE_INFO_40MHZ)
+               if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ)
                        status->flag |= RX_FLAG_40MHZ;
-               status->rate_idx = MWL8K_8366_RATE_INFO_RATEID(rxd->rate);
+               status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate);
        } else {
                int i;
 
@@ -844,17 +746,17 @@ mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status,
        return le16_to_cpu(rxd->pkt_len);
 }
 
-static struct rxd_ops rxd_8366_ops = {
-       .rxd_size       = sizeof(struct mwl8k_rxd_8366),
-       .rxd_init       = mwl8k_rxd_8366_init,
-       .rxd_refill     = mwl8k_rxd_8366_refill,
-       .rxd_process    = mwl8k_rxd_8366_process,
+static struct rxd_ops rxd_8366_ap_ops = {
+       .rxd_size       = sizeof(struct mwl8k_rxd_8366_ap),
+       .rxd_init       = mwl8k_rxd_8366_ap_init,
+       .rxd_refill     = mwl8k_rxd_8366_ap_refill,
+       .rxd_process    = mwl8k_rxd_8366_ap_process,
 };
 
 /*
- * Packet reception for 88w8687.
+ * Packet reception for STA firmware.
  */
-struct mwl8k_rxd_8687 {
+struct mwl8k_rxd_sta {
        __le16 pkt_len;
        __u8 link_quality;
        __u8 noise_level;
@@ -871,26 +773,26 @@ struct mwl8k_rxd_8687 {
        __u8 pad2[2];
 } __attribute__((packed));
 
-#define MWL8K_8687_RATE_INFO_SHORTPRE          0x8000
-#define MWL8K_8687_RATE_INFO_ANTSELECT(x)      (((x) >> 11) & 0x3)
-#define MWL8K_8687_RATE_INFO_RATEID(x)         (((x) >> 3) & 0x3f)
-#define MWL8K_8687_RATE_INFO_40MHZ             0x0004
-#define MWL8K_8687_RATE_INFO_SHORTGI           0x0002
-#define MWL8K_8687_RATE_INFO_MCS_FORMAT                0x0001
+#define MWL8K_STA_RATE_INFO_SHORTPRE           0x8000
+#define MWL8K_STA_RATE_INFO_ANTSELECT(x)       (((x) >> 11) & 0x3)
+#define MWL8K_STA_RATE_INFO_RATEID(x)          (((x) >> 3) & 0x3f)
+#define MWL8K_STA_RATE_INFO_40MHZ              0x0004
+#define MWL8K_STA_RATE_INFO_SHORTGI            0x0002
+#define MWL8K_STA_RATE_INFO_MCS_FORMAT         0x0001
 
-#define MWL8K_8687_RX_CTRL_OWNED_BY_HOST       0x02
+#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST                0x02
 
-static void mwl8k_rxd_8687_init(void *_rxd, dma_addr_t next_dma_addr)
+static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
 {
-       struct mwl8k_rxd_8687 *rxd = _rxd;
+       struct mwl8k_rxd_sta *rxd = _rxd;
 
        rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr);
-       rxd->rx_ctrl = MWL8K_8687_RX_CTRL_OWNED_BY_HOST;
+       rxd->rx_ctrl = MWL8K_STA_RX_CTRL_OWNED_BY_HOST;
 }
 
-static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len)
+static void mwl8k_rxd_sta_refill(void *_rxd, dma_addr_t addr, int len)
 {
-       struct mwl8k_rxd_8687 *rxd = _rxd;
+       struct mwl8k_rxd_sta *rxd = _rxd;
 
        rxd->pkt_len = cpu_to_le16(len);
        rxd->pkt_phys_addr = cpu_to_le32(addr);
@@ -899,13 +801,13 @@ static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len)
 }
 
 static int
-mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status,
+mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
                       __le16 *qos)
 {
-       struct mwl8k_rxd_8687 *rxd = _rxd;
+       struct mwl8k_rxd_sta *rxd = _rxd;
        u16 rate_info;
 
-       if (!(rxd->rx_ctrl & MWL8K_8687_RX_CTRL_OWNED_BY_HOST))
+       if (!(rxd->rx_ctrl & MWL8K_STA_RX_CTRL_OWNED_BY_HOST))
                return -1;
        rmb();
 
@@ -915,16 +817,16 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status,
 
        status->signal = -rxd->rssi;
        status->noise = -rxd->noise_level;
-       status->antenna = MWL8K_8687_RATE_INFO_ANTSELECT(rate_info);
-       status->rate_idx = MWL8K_8687_RATE_INFO_RATEID(rate_info);
+       status->antenna = MWL8K_STA_RATE_INFO_ANTSELECT(rate_info);
+       status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info);
 
-       if (rate_info & MWL8K_8687_RATE_INFO_SHORTPRE)
+       if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE)
                status->flag |= RX_FLAG_SHORTPRE;
-       if (rate_info & MWL8K_8687_RATE_INFO_40MHZ)
+       if (rate_info & MWL8K_STA_RATE_INFO_40MHZ)
                status->flag |= RX_FLAG_40MHZ;
-       if (rate_info & MWL8K_8687_RATE_INFO_SHORTGI)
+       if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI)
                status->flag |= RX_FLAG_SHORT_GI;
-       if (rate_info & MWL8K_8687_RATE_INFO_MCS_FORMAT)
+       if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT)
                status->flag |= RX_FLAG_HT;
 
        status->band = IEEE80211_BAND_2GHZ;
@@ -935,11 +837,11 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status,
        return le16_to_cpu(rxd->pkt_len);
 }
 
-static struct rxd_ops rxd_8687_ops = {
-       .rxd_size       = sizeof(struct mwl8k_rxd_8687),
-       .rxd_init       = mwl8k_rxd_8687_init,
-       .rxd_refill     = mwl8k_rxd_8687_refill,
-       .rxd_process    = mwl8k_rxd_8687_process,
+static struct rxd_ops rxd_sta_ops = {
+       .rxd_size       = sizeof(struct mwl8k_rxd_sta),
+       .rxd_init       = mwl8k_rxd_sta_init,
+       .rxd_refill     = mwl8k_rxd_sta_refill,
+       .rxd_process    = mwl8k_rxd_sta_process,
 };
 
 
@@ -1153,16 +1055,18 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
  * Packet transmission.
  */
 
-/* Transmit packet ACK policy */
-#define MWL8K_TXD_ACK_POLICY_NORMAL            0
-#define MWL8K_TXD_ACK_POLICY_BLOCKACK          3
-
 #define MWL8K_TXD_STATUS_OK                    0x00000001
 #define MWL8K_TXD_STATUS_OK_RETRY              0x00000002
 #define MWL8K_TXD_STATUS_OK_MORE_RETRY         0x00000004
 #define MWL8K_TXD_STATUS_MULTICAST_TX          0x00000008
 #define MWL8K_TXD_STATUS_FW_OWNED              0x80000000
 
+#define MWL8K_QOS_QLEN_UNSPEC                  0xff00
+#define MWL8K_QOS_ACK_POLICY_MASK              0x0060
+#define MWL8K_QOS_ACK_POLICY_NORMAL            0x0000
+#define MWL8K_QOS_ACK_POLICY_BLOCKACK          0x0060
+#define MWL8K_QOS_EOSP                         0x0010
+
 struct mwl8k_tx_desc {
        __le32 status;
        __u8 data_rate;
@@ -1316,8 +1220,8 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
                }
 
                if (priv->pending_tx_pkts < oldcount) {
-                       printk(KERN_NOTICE "%s: timeout waiting for tx "
-                              "rings to drain (%d -> %d pkts), retrying\n",
+                       printk(KERN_NOTICE "%s: waiting for tx rings "
+                              "to drain (%d -> %d pkts)\n",
                               wiphy_name(hw->wiphy), oldcount,
                               priv->pending_tx_pkts);
                        retry = 1;
@@ -1459,24 +1363,17 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
        if (ieee80211_is_mgmt(wh->frame_control) ||
            ieee80211_is_ctl(wh->frame_control)) {
                txdatarate = 0;
-               qos = mwl8k_qos_setbit_eosp(qos);
-               /* Set Queue size to unspecified */
-               qos = mwl8k_qos_setbit_qlen(qos, 0xff);
+               qos |= MWL8K_QOS_QLEN_UNSPEC | MWL8K_QOS_EOSP;
        } else if (ieee80211_is_data(wh->frame_control)) {
                txdatarate = 1;
                if (is_multicast_ether_addr(wh->addr1))
                        txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX;
 
-               /* Send pkt in an aggregate if AMPDU frame.  */
+               qos &= ~MWL8K_QOS_ACK_POLICY_MASK;
                if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
-                       qos = mwl8k_qos_setbit_ack(qos,
-                               MWL8K_TXD_ACK_POLICY_BLOCKACK);
+                       qos |= MWL8K_QOS_ACK_POLICY_BLOCKACK;
                else
-                       qos = mwl8k_qos_setbit_ack(qos,
-                               MWL8K_TXD_ACK_POLICY_NORMAL);
-
-               if (qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
-                       qos = mwl8k_qos_setbit_amsdu(qos);
+                       qos |= MWL8K_QOS_ACK_POLICY_NORMAL;
        }
 
        dma = pci_map_single(priv->pdev, skb->data,
@@ -1503,7 +1400,10 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
        tx->pkt_phys_addr = cpu_to_le32(dma);
        tx->pkt_len = cpu_to_le16(skb->len);
        tx->rate_info = 0;
-       tx->peer_id = mwl8k_vif->peer_id;
+       if (!priv->ap_fw && tx_info->control.sta != NULL)
+               tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id;
+       else
+               tx->peer_id = 0;
        wmb();
        tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus);
 
@@ -1678,6 +1578,68 @@ struct mwl8k_cmd_get_hw_spec_sta {
        __le32 total_rxd;
 } __attribute__((packed));
 
+#define MWL8K_CAP_MAX_AMSDU            0x20000000
+#define MWL8K_CAP_GREENFIELD           0x08000000
+#define MWL8K_CAP_AMPDU                        0x04000000
+#define MWL8K_CAP_RX_STBC              0x01000000
+#define MWL8K_CAP_TX_STBC              0x00800000
+#define MWL8K_CAP_SHORTGI_40MHZ                0x00400000
+#define MWL8K_CAP_SHORTGI_20MHZ                0x00200000
+#define MWL8K_CAP_RX_ANTENNA_MASK      0x000e0000
+#define MWL8K_CAP_TX_ANTENNA_MASK      0x0001c000
+#define MWL8K_CAP_DELAY_BA             0x00003000
+#define MWL8K_CAP_MIMO                 0x00000200
+#define MWL8K_CAP_40MHZ                        0x00000100
+
+static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, u32 cap)
+{
+       struct mwl8k_priv *priv = hw->priv;
+       int rx_streams;
+       int tx_streams;
+
+       priv->band.ht_cap.ht_supported = 1;
+
+       if (cap & MWL8K_CAP_MAX_AMSDU)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+       if (cap & MWL8K_CAP_GREENFIELD)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
+       if (cap & MWL8K_CAP_AMPDU) {
+               hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
+               priv->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+               priv->band.ht_cap.ampdu_density =
+                               IEEE80211_HT_MPDU_DENSITY_NONE;
+       }
+       if (cap & MWL8K_CAP_RX_STBC)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC;
+       if (cap & MWL8K_CAP_TX_STBC)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+       if (cap & MWL8K_CAP_SHORTGI_40MHZ)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+       if (cap & MWL8K_CAP_SHORTGI_20MHZ)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+       if (cap & MWL8K_CAP_DELAY_BA)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA;
+       if (cap & MWL8K_CAP_40MHZ)
+               priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK);
+       tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK);
+
+       priv->band.ht_cap.mcs.rx_mask[0] = 0xff;
+       if (rx_streams >= 2)
+               priv->band.ht_cap.mcs.rx_mask[1] = 0xff;
+       if (rx_streams >= 3)
+               priv->band.ht_cap.mcs.rx_mask[2] = 0xff;
+       priv->band.ht_cap.mcs.rx_mask[4] = 0x01;
+       priv->band.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+
+       if (rx_streams != tx_streams) {
+               priv->band.ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+               priv->band.ht_cap.mcs.tx_params |= (tx_streams - 1) <<
+                               IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT;
+       }
+}
+
 static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
 {
        struct mwl8k_priv *priv = hw->priv;
@@ -1708,6 +1670,8 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)
                priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);
                priv->fw_rev = le32_to_cpu(cmd->fw_rev);
                priv->hw_rev = cmd->hw_rev;
+               if (cmd->caps & cpu_to_le32(MWL8K_CAP_MIMO))
+                       mwl8k_set_ht_caps(hw, le32_to_cpu(cmd->caps));
        }
 
        kfree(cmd);
@@ -1897,9 +1861,9 @@ __mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, int allmulti,
 }
 
 /*
- * CMD_802_11_GET_STAT.
+ * CMD_GET_STAT.
  */
-struct mwl8k_cmd_802_11_get_stat {
+struct mwl8k_cmd_get_stat {
        struct mwl8k_cmd_pkt header;
        __le32 stats[64];
 } __attribute__((packed));
@@ -1909,10 +1873,10 @@ struct mwl8k_cmd_802_11_get_stat {
 #define MWL8K_STAT_FCS_ERROR   24
 #define MWL8K_STAT_RTS_SUCCESS 11
 
-static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw,
-                               struct ieee80211_low_level_stats *stats)
+static int mwl8k_cmd_get_stat(struct ieee80211_hw *hw,
+                             struct ieee80211_low_level_stats *stats)
 {
-       struct mwl8k_cmd_802_11_get_stat *cmd;
+       struct mwl8k_cmd_get_stat *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -1939,9 +1903,9 @@ static int mwl8k_cmd_802_11_get_stat(struct ieee80211_hw *hw,
 }
 
 /*
- * CMD_802_11_RADIO_CONTROL.
+ * CMD_RADIO_CONTROL.
  */
-struct mwl8k_cmd_802_11_radio_control {
+struct mwl8k_cmd_radio_control {
        struct mwl8k_cmd_pkt header;
        __le16 action;
        __le16 control;
@@ -1949,10 +1913,10 @@ struct mwl8k_cmd_802_11_radio_control {
 } __attribute__((packed));
 
 static int
-mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
+mwl8k_cmd_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
 {
        struct mwl8k_priv *priv = hw->priv;
-       struct mwl8k_cmd_802_11_radio_control *cmd;
+       struct mwl8k_cmd_radio_control *cmd;
        int rc;
 
        if (enable == priv->radio_on && !force)
@@ -1977,36 +1941,32 @@ mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force)
        return rc;
 }
 
-static int mwl8k_cmd_802_11_radio_disable(struct ieee80211_hw *hw)
+static int mwl8k_cmd_radio_disable(struct ieee80211_hw *hw)
 {
-       return mwl8k_cmd_802_11_radio_control(hw, 0, 0);
+       return mwl8k_cmd_radio_control(hw, 0, 0);
 }
 
-static int mwl8k_cmd_802_11_radio_enable(struct ieee80211_hw *hw)
+static int mwl8k_cmd_radio_enable(struct ieee80211_hw *hw)
 {
-       return mwl8k_cmd_802_11_radio_control(hw, 1, 0);
+       return mwl8k_cmd_radio_control(hw, 1, 0);
 }
 
 static int
 mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
 {
-       struct mwl8k_priv *priv;
-
-       if (hw == NULL || hw->priv == NULL)
-               return -EINVAL;
-       priv = hw->priv;
+       struct mwl8k_priv *priv = hw->priv;
 
        priv->radio_short_preamble = short_preamble;
 
-       return mwl8k_cmd_802_11_radio_control(hw, 1, 1);
+       return mwl8k_cmd_radio_control(hw, 1, 1);
 }
 
 /*
- * CMD_802_11_RF_TX_POWER.
+ * CMD_RF_TX_POWER.
  */
 #define MWL8K_TX_POWER_LEVEL_TOTAL     8
 
-struct mwl8k_cmd_802_11_rf_tx_power {
+struct mwl8k_cmd_rf_tx_power {
        struct mwl8k_cmd_pkt header;
        __le16 action;
        __le16 support_level;
@@ -2015,9 +1975,9 @@ struct mwl8k_cmd_802_11_rf_tx_power {
        __le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL];
 } __attribute__((packed));
 
-static int mwl8k_cmd_802_11_rf_tx_power(struct ieee80211_hw *hw, int dBm)
+static int mwl8k_cmd_rf_tx_power(struct ieee80211_hw *hw, int dBm)
 {
-       struct mwl8k_cmd_802_11_rf_tx_power *cmd;
+       struct mwl8k_cmd_rf_tx_power *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -2103,7 +2063,7 @@ struct mwl8k_cmd_set_post_scan {
 } __attribute__((packed));
 
 static int
-mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 *mac)
+mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)
 {
        struct mwl8k_cmd_set_post_scan *cmd;
        int rc;
@@ -2134,8 +2094,9 @@ struct mwl8k_cmd_set_rf_channel {
 } __attribute__((packed));
 
 static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
-                                   struct ieee80211_channel *channel)
+                                   struct ieee80211_conf *conf)
 {
+       struct ieee80211_channel *channel = conf->channel;
        struct mwl8k_cmd_set_rf_channel *cmd;
        int rc;
 
@@ -2147,10 +2108,17 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
        cmd->action = cpu_to_le16(MWL8K_CMD_SET);
        cmd->current_channel = channel->hw_value;
+
        if (channel->band == IEEE80211_BAND_2GHZ)
-               cmd->channel_flags = cpu_to_le32(0x00000081);
-       else
-               cmd->channel_flags = cpu_to_le32(0x00000000);
+               cmd->channel_flags |= cpu_to_le32(0x00000001);
+
+       if (conf->channel_type == NL80211_CHAN_NO_HT ||
+           conf->channel_type == NL80211_CHAN_HT20)
+               cmd->channel_flags |= cpu_to_le32(0x00000080);
+       else if (conf->channel_type == NL80211_CHAN_HT40MINUS)
+               cmd->channel_flags |= cpu_to_le32(0x000001900);
+       else if (conf->channel_type == NL80211_CHAN_HT40PLUS)
+               cmd->channel_flags |= cpu_to_le32(0x000000900);
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2159,27 +2127,75 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,
 }
 
 /*
- * CMD_SET_SLOT.
+ * CMD_SET_AID.
  */
-struct mwl8k_cmd_set_slot {
-       struct mwl8k_cmd_pkt header;
-       __le16 action;
-       __u8 short_slot;
+#define MWL8K_FRAME_PROT_DISABLED                      0x00
+#define MWL8K_FRAME_PROT_11G                           0x07
+#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY             0x02
+#define MWL8K_FRAME_PROT_11N_HT_ALL                    0x06
+
+struct mwl8k_cmd_update_set_aid {
+       struct  mwl8k_cmd_pkt header;
+       __le16  aid;
+
+        /* AP's MAC address (BSSID) */
+       __u8    bssid[ETH_ALEN];
+       __le16  protection_mode;
+       __u8    supp_rates[14];
 } __attribute__((packed));
 
-static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
+static void legacy_rate_mask_to_array(u8 *rates, u32 mask)
 {
-       struct mwl8k_cmd_set_slot *cmd;
+       int i;
+       int j;
+
+       /*
+        * Clear nonstandard rates 4 and 13.
+        */
+       mask &= 0x1fef;
+
+       for (i = 0, j = 0; i < 14; i++) {
+               if (mask & (1 << i))
+                       rates[j++] = mwl8k_rates[i].hw_value;
+       }
+}
+
+static int
+mwl8k_cmd_set_aid(struct ieee80211_hw *hw,
+                 struct ieee80211_vif *vif, u32 legacy_rate_mask)
+{
+       struct mwl8k_cmd_update_set_aid *cmd;
+       u16 prot_mode;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->action = cpu_to_le16(MWL8K_CMD_SET);
-       cmd->short_slot = short_slot_time;
+       cmd->aid = cpu_to_le16(vif->bss_conf.aid);
+       memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
+
+       if (vif->bss_conf.use_cts_prot) {
+               prot_mode = MWL8K_FRAME_PROT_11G;
+       } else {
+               switch (vif->bss_conf.ht_operation_mode &
+                       IEEE80211_HT_OP_MODE_PROTECTION) {
+               case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
+                       prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
+                       break;
+               case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
+                       prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
+                       break;
+               default:
+                       prot_mode = MWL8K_FRAME_PROT_DISABLED;
+                       break;
+               }
+       }
+       cmd->protection_mode = cpu_to_le16(prot_mode);
+
+       legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask);
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2188,29 +2204,32 @@ static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
 }
 
 /*
- * CMD_MIMO_CONFIG.
+ * CMD_SET_RATE.
  */
-struct mwl8k_cmd_mimo_config {
-       struct mwl8k_cmd_pkt header;
-       __le32 action;
-       __u8 rx_antenna_map;
-       __u8 tx_antenna_map;
+struct mwl8k_cmd_set_rate {
+       struct  mwl8k_cmd_pkt header;
+       __u8    legacy_rates[14];
+
+       /* Bitmap for supported MCS codes.  */
+       __u8    mcs_set[16];
+       __u8    reserved[16];
 } __attribute__((packed));
 
-static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
+static int
+mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  u32 legacy_rate_mask, u8 *mcs_rates)
 {
-       struct mwl8k_cmd_mimo_config *cmd;
+       struct mwl8k_cmd_set_rate *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
-       cmd->rx_antenna_map = rx;
-       cmd->tx_antenna_map = tx;
+       legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask);
+       memcpy(cmd->mcs_set, mcs_rates, 16);
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2219,25 +2238,39 @@ static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
 }
 
 /*
- * CMD_ENABLE_SNIFFER.
+ * CMD_FINALIZE_JOIN.
  */
-struct mwl8k_cmd_enable_sniffer {
+#define MWL8K_FJ_BEACON_MAXLEN 128
+
+struct mwl8k_cmd_finalize_join {
        struct mwl8k_cmd_pkt header;
-       __le32 action;
+       __le32 sleep_interval;  /* Number of beacon periods to sleep */
+       __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
 } __attribute__((packed));
 
-static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable)
+static int mwl8k_cmd_finalize_join(struct ieee80211_hw *hw, void *frame,
+                                  int framelen, int dtim)
 {
-       struct mwl8k_cmd_enable_sniffer *cmd;
+       struct mwl8k_cmd_finalize_join *cmd;
+       struct ieee80211_mgmt *payload = frame;
+       int payload_len;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->action = cpu_to_le32(!!enable);
+       cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
+
+       payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
+       if (payload_len < 0)
+               payload_len = 0;
+       else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
+               payload_len = MWL8K_FJ_BEACON_MAXLEN;
+
+       memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2246,37 +2279,28 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable)
 }
 
 /*
- * CMD_SET_MAC_ADDR.
+ * CMD_SET_RTS_THRESHOLD.
  */
-struct mwl8k_cmd_set_mac_addr {
+struct mwl8k_cmd_set_rts_threshold {
        struct mwl8k_cmd_pkt header;
-       union {
-               struct {
-                       __le16 mac_type;
-                       __u8 mac_addr[ETH_ALEN];
-               } mbss;
-               __u8 mac_addr[ETH_ALEN];
-       };
+       __le16 action;
+       __le16 threshold;
 } __attribute__((packed));
 
-static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac)
+static int mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw,
+                                      u16 action, u16 threshold)
 {
-       struct mwl8k_priv *priv = hw->priv;
-       struct mwl8k_cmd_set_mac_addr *cmd;
+       struct mwl8k_cmd_set_rts_threshold *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       if (priv->ap_fw) {
-               cmd->mbss.mac_type = 0;
-               memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
-       } else {
-               memcpy(cmd->mac_addr, mac, ETH_ALEN);
-       }
+       cmd->action = cpu_to_le16(action);
+       cmd->threshold = cpu_to_le16(threshold);
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2284,29 +2308,28 @@ static int mwl8k_set_mac_addr(struct ieee80211_hw *hw, u8 *mac)
        return rc;
 }
 
-
 /*
- * CMD_SET_RATEADAPT_MODE.
+ * CMD_SET_SLOT.
  */
-struct mwl8k_cmd_set_rate_adapt_mode {
+struct mwl8k_cmd_set_slot {
        struct mwl8k_cmd_pkt header;
        __le16 action;
-       __le16 mode;
+       __u8 short_slot;
 } __attribute__((packed));
 
-static int mwl8k_cmd_setrateadaptmode(struct ieee80211_hw *hw, __u16 mode)
+static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time)
 {
-       struct mwl8k_cmd_set_rate_adapt_mode *cmd;
+       struct mwl8k_cmd_set_slot *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
        cmd->action = cpu_to_le16(MWL8K_CMD_SET);
-       cmd->mode = cpu_to_le16(mode);
+       cmd->short_slot = short_slot_time;
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2315,82 +2338,21 @@ static int mwl8k_cmd_setrateadaptmode(struct ieee80211_hw *hw, __u16 mode)
 }
 
 /*
- * CMD_SET_WMM_MODE.
+ * CMD_SET_EDCA_PARAMS.
  */
-struct mwl8k_cmd_set_wmm {
+struct mwl8k_cmd_set_edca_params {
        struct mwl8k_cmd_pkt header;
+
+       /* See MWL8K_SET_EDCA_XXX below */
        __le16 action;
-} __attribute__((packed));
 
-static int mwl8k_set_wmm(struct ieee80211_hw *hw, bool enable)
-{
-       struct mwl8k_priv *priv = hw->priv;
-       struct mwl8k_cmd_set_wmm *cmd;
-       int rc;
+       /* TX opportunity in units of 32 us */
+       __le16 txop;
 
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (cmd == NULL)
-               return -ENOMEM;
-
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
-       cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->action = cpu_to_le16(!!enable);
-
-       rc = mwl8k_post_cmd(hw, &cmd->header);
-       kfree(cmd);
-
-       if (!rc)
-               priv->wmm_enabled = enable;
-
-       return rc;
-}
-
-/*
- * CMD_SET_RTS_THRESHOLD.
- */
-struct mwl8k_cmd_rts_threshold {
-       struct mwl8k_cmd_pkt header;
-       __le16 action;
-       __le16 threshold;
-} __attribute__((packed));
-
-static int mwl8k_rts_threshold(struct ieee80211_hw *hw,
-                              u16 action, u16 threshold)
-{
-       struct mwl8k_cmd_rts_threshold *cmd;
-       int rc;
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (cmd == NULL)
-               return -ENOMEM;
-
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD);
-       cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->action = cpu_to_le16(action);
-       cmd->threshold = cpu_to_le16(threshold);
-
-       rc = mwl8k_post_cmd(hw, &cmd->header);
-       kfree(cmd);
-
-       return rc;
-}
-
-/*
- * CMD_SET_EDCA_PARAMS.
- */
-struct mwl8k_cmd_set_edca_params {
-       struct mwl8k_cmd_pkt header;
-
-       /* See MWL8K_SET_EDCA_XXX below */
-       __le16 action;
-
-       /* TX opportunity in units of 32 us */
-       __le16 txop;
-
-       union {
-               struct {
-                       /* Log exponent of max contention period: 0...15 */
-                       __le32 log_cw_max;
+       union {
+               struct {
+                       /* Log exponent of max contention period: 0...15 */
+                       __le32 log_cw_max;
 
                        /* Log exponent of min contention period: 0...15 */
                        __le32 log_cw_min;
@@ -2426,9 +2388,9 @@ struct mwl8k_cmd_set_edca_params {
                                 MWL8K_SET_EDCA_AIFS)
 
 static int
-mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
-               __u16 cw_min, __u16 cw_max,
-               __u8 aifs, __u16 txop)
+mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
+                         __u16 cw_min, __u16 cw_max,
+                         __u8 aifs, __u16 txop)
 {
        struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_cmd_set_edca_params *cmd;
@@ -2467,39 +2429,60 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum,
 }
 
 /*
- * CMD_FINALIZE_JOIN.
+ * CMD_SET_WMM_MODE.
  */
-#define MWL8K_FJ_BEACON_MAXLEN 128
-
-struct mwl8k_cmd_finalize_join {
+struct mwl8k_cmd_set_wmm_mode {
        struct mwl8k_cmd_pkt header;
-       __le32 sleep_interval;  /* Number of beacon periods to sleep */
-       __u8 beacon_data[MWL8K_FJ_BEACON_MAXLEN];
+       __le16 action;
 } __attribute__((packed));
 
-static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame,
-                              int framelen, int dtim)
+static int mwl8k_cmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
 {
-       struct mwl8k_cmd_finalize_join *cmd;
-       struct ieee80211_mgmt *payload = frame;
-       int payload_len;
+       struct mwl8k_priv *priv = hw->priv;
+       struct mwl8k_cmd_set_wmm_mode *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1);
+       cmd->action = cpu_to_le16(!!enable);
 
-       payload_len = framelen - ieee80211_hdrlen(payload->frame_control);
-       if (payload_len < 0)
-               payload_len = 0;
-       else if (payload_len > MWL8K_FJ_BEACON_MAXLEN)
-               payload_len = MWL8K_FJ_BEACON_MAXLEN;
+       rc = mwl8k_post_cmd(hw, &cmd->header);
+       kfree(cmd);
 
-       memcpy(cmd->beacon_data, &payload->u.beacon, payload_len);
+       if (!rc)
+               priv->wmm_enabled = enable;
+
+       return rc;
+}
+
+/*
+ * CMD_MIMO_CONFIG.
+ */
+struct mwl8k_cmd_mimo_config {
+       struct mwl8k_cmd_pkt header;
+       __le32 action;
+       __u8 rx_antenna_map;
+       __u8 tx_antenna_map;
+} __attribute__((packed));
+
+static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx)
+{
+       struct mwl8k_cmd_mimo_config *cmd;
+       int rc;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_MIMO_CONFIG);
+       cmd->header.length = cpu_to_le16(sizeof(*cmd));
+       cmd->action = cpu_to_le32((u32)MWL8K_CMD_SET);
+       cmd->rx_antenna_map = rx;
+       cmd->tx_antenna_map = tx;
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2508,246 +2491,294 @@ static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame,
 }
 
 /*
- * CMD_UPDATE_STADB.
+ * CMD_USE_FIXED_RATE.
  */
-struct mwl8k_cmd_update_sta_db {
-       struct mwl8k_cmd_pkt header;
+#define MWL8K_RATE_TABLE_SIZE  8
+#define MWL8K_UCAST_RATE       0
+#define MWL8K_USE_AUTO_RATE    0x0002
 
-       /* See STADB_ACTION_TYPE */
-       __le32  action;
+struct mwl8k_rate_entry {
+       /* Set to 1 if HT rate, 0 if legacy.  */
+       __le32  is_ht_rate;
 
-       /* Peer MAC address */
-       __u8    peer_addr[ETH_ALEN];
+       /* Set to 1 to use retry_count field.  */
+       __le32  enable_retry;
 
-       __le32  reserved;
+       /* Specified legacy rate or MCS.  */
+       __le32  rate;
 
-       /* Peer info - valid during add/update.  */
-       struct peer_capability_info     peer_info;
+       /* Number of allowed retries.  */
+       __le32  retry_count;
+} __attribute__((packed));
+
+struct mwl8k_rate_table {
+       /* 1 to allow specified rate and below */
+       __le32  allow_rate_drop;
+       __le32  num_rates;
+       struct mwl8k_rate_entry rate_entry[MWL8K_RATE_TABLE_SIZE];
+} __attribute__((packed));
+
+struct mwl8k_cmd_use_fixed_rate {
+       struct  mwl8k_cmd_pkt header;
+       __le32  action;
+       struct mwl8k_rate_table rate_table;
+
+       /* Unicast, Broadcast or Multicast */
+       __le32  rate_type;
+       __le32  reserved1;
+       __le32  reserved2;
 } __attribute__((packed));
 
-static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif, __u32 action)
+static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw,
+       u32 action, u32 rate_type, struct mwl8k_rate_table *rate_table)
 {
-       struct mwl8k_vif *mv_vif = MWL8K_VIF(vif);
-       struct ieee80211_bss_conf *info = &mv_vif->bss_info;
-       struct mwl8k_cmd_update_sta_db *cmd;
-       struct peer_capability_info *peer_info;
+       struct mwl8k_cmd_use_fixed_rate *cmd;
+       int count;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
 
        cmd->action = cpu_to_le32(action);
-       peer_info = &cmd->peer_info;
-       memcpy(cmd->peer_addr, mv_vif->bssid, ETH_ALEN);
+       cmd->rate_type = cpu_to_le32(rate_type);
 
-       switch (action) {
-       case MWL8K_STA_DB_ADD_ENTRY:
-       case MWL8K_STA_DB_MODIFY_ENTRY:
-               /* Build peer_info block */
-               peer_info->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
-               peer_info->basic_caps = cpu_to_le16(info->assoc_capability);
-               memcpy(peer_info->legacy_rates, mwl8k_rateids,
-                      sizeof(mwl8k_rateids));
-               peer_info->interop = 1;
-               peer_info->amsdu_enabled = 0;
-
-               rc = mwl8k_post_cmd(hw, &cmd->header);
-               if (rc == 0)
-                       mv_vif->peer_id = peer_info->station_id;
-
-               break;
-
-       case MWL8K_STA_DB_DEL_ENTRY:
-       case MWL8K_STA_DB_FLUSH:
-       default:
-               rc = mwl8k_post_cmd(hw, &cmd->header);
-               if (rc == 0)
-                       mv_vif->peer_id = 0;
-               break;
+       if (rate_table != NULL) {
+               /*
+                * Copy over each field manually so that endian
+                * conversion can be done.
+                */
+               cmd->rate_table.allow_rate_drop =
+                               cpu_to_le32(rate_table->allow_rate_drop);
+               cmd->rate_table.num_rates =
+                               cpu_to_le32(rate_table->num_rates);
+
+               for (count = 0; count < rate_table->num_rates; count++) {
+                       struct mwl8k_rate_entry *dst =
+                               &cmd->rate_table.rate_entry[count];
+                       struct mwl8k_rate_entry *src =
+                               &rate_table->rate_entry[count];
+
+                       dst->is_ht_rate = cpu_to_le32(src->is_ht_rate);
+                       dst->enable_retry = cpu_to_le32(src->enable_retry);
+                       dst->rate = cpu_to_le32(src->rate);
+                       dst->retry_count = cpu_to_le32(src->retry_count);
+               }
        }
+
+       rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
 
        return rc;
 }
 
 /*
- * CMD_SET_AID.
+ * CMD_ENABLE_SNIFFER.
  */
-#define MWL8K_FRAME_PROT_DISABLED                      0x00
-#define MWL8K_FRAME_PROT_11G                           0x07
-#define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY             0x02
-#define MWL8K_FRAME_PROT_11N_HT_ALL                    0x06
-
-struct mwl8k_cmd_update_set_aid {
-       struct  mwl8k_cmd_pkt header;
-       __le16  aid;
-
-        /* AP's MAC address (BSSID) */
-       __u8    bssid[ETH_ALEN];
-       __le16  protection_mode;
-       __u8    supp_rates[14];
+struct mwl8k_cmd_enable_sniffer {
+       struct mwl8k_cmd_pkt header;
+       __le32 action;
 } __attribute__((packed));
 
-static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw,
-                                       struct ieee80211_vif *vif)
+static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)
 {
-       struct mwl8k_vif *mv_vif = MWL8K_VIF(vif);
-       struct ieee80211_bss_conf *info = &mv_vif->bss_info;
-       struct mwl8k_cmd_update_set_aid *cmd;
-       u16 prot_mode;
+       struct mwl8k_cmd_enable_sniffer *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       cmd->aid = cpu_to_le16(info->aid);
+       cmd->action = cpu_to_le32(!!enable);
 
-       memcpy(cmd->bssid, mv_vif->bssid, ETH_ALEN);
+       rc = mwl8k_post_cmd(hw, &cmd->header);
+       kfree(cmd);
 
-       if (info->use_cts_prot) {
-               prot_mode = MWL8K_FRAME_PROT_11G;
+       return rc;
+}
+
+/*
+ * CMD_SET_MAC_ADDR.
+ */
+struct mwl8k_cmd_set_mac_addr {
+       struct mwl8k_cmd_pkt header;
+       union {
+               struct {
+                       __le16 mac_type;
+                       __u8 mac_addr[ETH_ALEN];
+               } mbss;
+               __u8 mac_addr[ETH_ALEN];
+       };
+} __attribute__((packed));
+
+static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac)
+{
+       struct mwl8k_priv *priv = hw->priv;
+       struct mwl8k_cmd_set_mac_addr *cmd;
+       int rc;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR);
+       cmd->header.length = cpu_to_le16(sizeof(*cmd));
+       if (priv->ap_fw) {
+               cmd->mbss.mac_type = 0;
+               memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN);
        } else {
-               switch (info->ht_operation_mode &
-                       IEEE80211_HT_OP_MODE_PROTECTION) {
-               case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ:
-                       prot_mode = MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY;
-                       break;
-               case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED:
-                       prot_mode = MWL8K_FRAME_PROT_11N_HT_ALL;
-                       break;
-               default:
-                       prot_mode = MWL8K_FRAME_PROT_DISABLED;
-                       break;
-               }
+               memcpy(cmd->mac_addr, mac, ETH_ALEN);
        }
-       cmd->protection_mode = cpu_to_le16(prot_mode);
 
-       memcpy(cmd->supp_rates, mwl8k_rateids, sizeof(mwl8k_rateids));
+       rc = mwl8k_post_cmd(hw, &cmd->header);
+       kfree(cmd);
+
+       return rc;
+}
+
+/*
+ * CMD_SET_RATEADAPT_MODE.
+ */
+struct mwl8k_cmd_set_rate_adapt_mode {
+       struct mwl8k_cmd_pkt header;
+       __le16 action;
+       __le16 mode;
+} __attribute__((packed));
+
+static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)
+{
+       struct mwl8k_cmd_set_rate_adapt_mode *cmd;
+       int rc;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATEADAPT_MODE);
+       cmd->header.length = cpu_to_le16(sizeof(*cmd));
+       cmd->action = cpu_to_le16(MWL8K_CMD_SET);
+       cmd->mode = cpu_to_le16(mode);
+
+       rc = mwl8k_post_cmd(hw, &cmd->header);
+       kfree(cmd);
+
+       return rc;
+}
+
+/*
+ * CMD_UPDATE_STADB.
+ */
+struct ewc_ht_info {
+       __le16  control1;
+       __le16  control2;
+       __le16  control3;
+} __attribute__((packed));
+
+struct peer_capability_info {
+       /* Peer type - AP vs. STA.  */
+       __u8    peer_type;
+
+       /* Basic 802.11 capabilities from assoc resp.  */
+       __le16  basic_caps;
+
+       /* Set if peer supports 802.11n high throughput (HT).  */
+       __u8    ht_support;
+
+       /* Valid if HT is supported.  */
+       __le16  ht_caps;
+       __u8    extended_ht_caps;
+       struct ewc_ht_info      ewc_info;
+
+       /* Legacy rate table. Intersection of our rates and peer rates.  */
+       __u8    legacy_rates[12];
+
+       /* HT rate table. Intersection of our rates and peer rates.  */
+       __u8    ht_rates[16];
+       __u8    pad[16];
+
+       /* If set, interoperability mode, no proprietary extensions.  */
+       __u8    interop;
+       __u8    pad2;
+       __u8    station_id;
+       __le16  amsdu_enabled;
+} __attribute__((packed));
+
+struct mwl8k_cmd_update_stadb {
+       struct mwl8k_cmd_pkt header;
 
-       rc = mwl8k_post_cmd(hw, &cmd->header);
-       kfree(cmd);
+       /* See STADB_ACTION_TYPE */
+       __le32  action;
 
-       return rc;
-}
+       /* Peer MAC address */
+       __u8    peer_addr[ETH_ALEN];
 
-/*
- * CMD_SET_RATE.
- */
-struct mwl8k_cmd_update_rateset {
-       struct  mwl8k_cmd_pkt header;
-       __u8    legacy_rates[14];
+       __le32  reserved;
 
-       /* Bitmap for supported MCS codes.  */
-       __u8    mcs_set[16];
-       __u8    reserved[16];
+       /* Peer info - valid during add/update.  */
+       struct peer_capability_info     peer_info;
 } __attribute__((packed));
 
-static int mwl8k_update_rateset(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif)
+#define MWL8K_STA_DB_MODIFY_ENTRY      1
+#define MWL8K_STA_DB_DEL_ENTRY         2
+
+/* Peer Entry flags - used to define the type of the peer node */
+#define MWL8K_PEER_TYPE_ACCESSPOINT    2
+
+static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_sta *sta)
 {
-       struct mwl8k_cmd_update_rateset *cmd;
+       struct mwl8k_cmd_update_stadb *cmd;
+       struct peer_capability_info *p;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-       memcpy(cmd->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids));
+       cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY);
+       memcpy(cmd->peer_addr, sta->addr, ETH_ALEN);
+
+       p = &cmd->peer_info;
+       p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT;
+       p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability);
+       p->ht_support = sta->ht_cap.ht_supported;
+       p->ht_caps = sta->ht_cap.cap;
+       p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |
+               ((sta->ht_cap.ampdu_density & 7) << 2);
+       legacy_rate_mask_to_array(p->legacy_rates,
+                                 sta->supp_rates[IEEE80211_BAND_2GHZ]);
+       memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16);
+       p->interop = 1;
+       p->amsdu_enabled = 0;
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
 
-       return rc;
+       return rc ? rc : p->station_id;
 }
 
-/*
- * CMD_USE_FIXED_RATE.
- */
-#define MWL8K_RATE_TABLE_SIZE  8
-#define MWL8K_UCAST_RATE       0
-#define MWL8K_USE_AUTO_RATE    0x0002
-
-struct mwl8k_rate_entry {
-       /* Set to 1 if HT rate, 0 if legacy.  */
-       __le32  is_ht_rate;
-
-       /* Set to 1 to use retry_count field.  */
-       __le32  enable_retry;
-
-       /* Specified legacy rate or MCS.  */
-       __le32  rate;
-
-       /* Number of allowed retries.  */
-       __le32  retry_count;
-} __attribute__((packed));
-
-struct mwl8k_rate_table {
-       /* 1 to allow specified rate and below */
-       __le32  allow_rate_drop;
-       __le32  num_rates;
-       struct mwl8k_rate_entry rate_entry[MWL8K_RATE_TABLE_SIZE];
-} __attribute__((packed));
-
-struct mwl8k_cmd_use_fixed_rate {
-       struct  mwl8k_cmd_pkt header;
-       __le32  action;
-       struct mwl8k_rate_table rate_table;
-
-       /* Unicast, Broadcast or Multicast */
-       __le32  rate_type;
-       __le32  reserved1;
-       __le32  reserved2;
-} __attribute__((packed));
-
-static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw,
-       u32 action, u32 rate_type, struct mwl8k_rate_table *rate_table)
+static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif, u8 *addr)
 {
-       struct mwl8k_cmd_use_fixed_rate *cmd;
-       int count;
+       struct mwl8k_cmd_update_stadb *cmd;
        int rc;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
 
-       cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE);
+       cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB);
        cmd->header.length = cpu_to_le16(sizeof(*cmd));
-
-       cmd->action = cpu_to_le32(action);
-       cmd->rate_type = cpu_to_le32(rate_type);
-
-       if (rate_table != NULL) {
-               /*
-                * Copy over each field manually so that endian
-                * conversion can be done.
-                */
-               cmd->rate_table.allow_rate_drop =
-                               cpu_to_le32(rate_table->allow_rate_drop);
-               cmd->rate_table.num_rates =
-                               cpu_to_le32(rate_table->num_rates);
-
-               for (count = 0; count < rate_table->num_rates; count++) {
-                       struct mwl8k_rate_entry *dst =
-                               &cmd->rate_table.rate_entry[count];
-                       struct mwl8k_rate_entry *src =
-                               &rate_table->rate_entry[count];
-
-                       dst->is_ht_rate = cpu_to_le32(src->is_ht_rate);
-                       dst->enable_retry = cpu_to_le32(src->enable_retry);
-                       dst->rate = cpu_to_le32(src->rate);
-                       dst->retry_count = cpu_to_le32(src->retry_count);
-               }
-       }
+       cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY);
+       memcpy(cmd->peer_addr, addr, ETH_ALEN);
 
        rc = mwl8k_post_cmd(hw, &cmd->header);
        kfree(cmd);
@@ -2836,11 +2867,11 @@ static int mwl8k_start(struct ieee80211_hw *hw)
 
        rc = mwl8k_fw_lock(hw);
        if (!rc) {
-               rc = mwl8k_cmd_802_11_radio_enable(hw);
+               rc = mwl8k_cmd_radio_enable(hw);
 
                if (!priv->ap_fw) {
                        if (!rc)
-                               rc = mwl8k_enable_sniffer(hw, 0);
+                               rc = mwl8k_cmd_enable_sniffer(hw, 0);
 
                        if (!rc)
                                rc = mwl8k_cmd_set_pre_scan(hw);
@@ -2851,10 +2882,10 @@ static int mwl8k_start(struct ieee80211_hw *hw)
                }
 
                if (!rc)
-                       rc = mwl8k_cmd_setrateadaptmode(hw, 0);
+                       rc = mwl8k_cmd_set_rateadapt_mode(hw, 0);
 
                if (!rc)
-                       rc = mwl8k_set_wmm(hw, 0);
+                       rc = mwl8k_cmd_set_wmm_mode(hw, 0);
 
                mwl8k_fw_unlock(hw);
        }
@@ -2873,7 +2904,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw)
        struct mwl8k_priv *priv = hw->priv;
        int i;
 
-       mwl8k_cmd_802_11_radio_disable(hw);
+       mwl8k_cmd_radio_disable(hw);
 
        ieee80211_stop_queues(hw);
 
@@ -2895,7 +2926,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw)
 }
 
 static int mwl8k_add_interface(struct ieee80211_hw *hw,
-                               struct ieee80211_if_init_conf *conf)
+                               struct ieee80211_vif *vif)
 {
        struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_vif *mwl8k_vif;
@@ -2909,7 +2940,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        /*
         * We only support managed interfaces for now.
         */
-       if (conf->type != NL80211_IFTYPE_STATION)
+       if (vif->type != NL80211_IFTYPE_STATION)
                return -EINVAL;
 
        /*
@@ -2925,34 +2956,31 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        }
 
        /* Clean out driver private area */
-       mwl8k_vif = MWL8K_VIF(conf->vif);
+       mwl8k_vif = MWL8K_VIF(vif);
        memset(mwl8k_vif, 0, sizeof(*mwl8k_vif));
 
        /* Set and save the mac address */
-       mwl8k_set_mac_addr(hw, conf->mac_addr);
-       memcpy(mwl8k_vif->mac_addr, conf->mac_addr, ETH_ALEN);
-
-       /* Back pointer to parent config block */
-       mwl8k_vif->priv = priv;
+       mwl8k_cmd_set_mac_addr(hw, vif->addr);
+       memcpy(mwl8k_vif->mac_addr, vif->addr, ETH_ALEN);
 
        /* Set Initial sequence number to zero */
        mwl8k_vif->seqno = 0;
 
-       priv->vif = conf->vif;
+       priv->vif = vif;
        priv->current_channel = NULL;
 
        return 0;
 }
 
 static void mwl8k_remove_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_if_init_conf *conf)
+                                  struct ieee80211_vif *vif)
 {
        struct mwl8k_priv *priv = hw->priv;
 
        if (priv->vif == NULL)
                return;
 
-       mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
+       mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
 
        priv->vif = NULL;
 }
@@ -2964,7 +2992,7 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
        int rc;
 
        if (conf->flags & IEEE80211_CONF_IDLE) {
-               mwl8k_cmd_802_11_radio_disable(hw);
+               mwl8k_cmd_radio_disable(hw);
                priv->current_channel = NULL;
                return 0;
        }
@@ -2973,11 +3001,11 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
        if (rc)
                return rc;
 
-       rc = mwl8k_cmd_802_11_radio_enable(hw);
+       rc = mwl8k_cmd_radio_enable(hw);
        if (rc)
                goto out;
 
-       rc = mwl8k_cmd_set_rf_channel(hw, conf->channel);
+       rc = mwl8k_cmd_set_rf_channel(hw, conf);
        if (rc)
                goto out;
 
@@ -2985,7 +3013,7 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
 
        if (conf->power_level > 18)
                conf->power_level = 18;
-       rc = mwl8k_cmd_802_11_rf_tx_power(hw, conf->power_level);
+       rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
        if (rc)
                goto out;
 
@@ -3009,67 +3037,78 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw,
                                   u32 changed)
 {
        struct mwl8k_priv *priv = hw->priv;
-       struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
+       u32 ap_legacy_rates;
+       u8 ap_mcs_rates[16];
        int rc;
 
-       if ((changed & BSS_CHANGED_ASSOC) == 0)
+       if (mwl8k_fw_lock(hw))
                return;
 
-       priv->capture_beacon = false;
+       /*
+        * No need to capture a beacon if we're no longer associated.
+        */
+       if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc)
+               priv->capture_beacon = false;
 
-       rc = mwl8k_fw_lock(hw);
-       if (rc)
-               return;
+       /*
+        * Get the AP's legacy and MCS rates.
+        */
+       ap_legacy_rates = 0;
+       if (vif->bss_conf.assoc) {
+               struct ieee80211_sta *ap;
+               rcu_read_lock();
+
+               ap = ieee80211_find_sta(vif, vif->bss_conf.bssid);
+               if (ap == NULL) {
+                       rcu_read_unlock();
+                       goto out;
+               }
 
-       if (info->assoc) {
-               memcpy(&mwl8k_vif->bss_info, info,
-                       sizeof(struct ieee80211_bss_conf));
+               ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];
+               memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16);
 
-               memcpy(mwl8k_vif->bssid, info->bssid, ETH_ALEN);
+               rcu_read_unlock();
+       }
 
-               /* Install rates */
-               rc = mwl8k_update_rateset(hw, vif);
+       if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) {
+               rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);
                if (rc)
                        goto out;
 
-               /* Turn on rate adaptation */
                rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE,
                        MWL8K_UCAST_RATE, NULL);
                if (rc)
                        goto out;
+       }
 
-               /* Set radio preamble */
-               rc = mwl8k_set_radio_preamble(hw, info->use_short_preamble);
-               if (rc)
-                       goto out;
-
-               /* Set slot time */
-               rc = mwl8k_cmd_set_slot(hw, info->use_short_slot);
+       if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+               rc = mwl8k_set_radio_preamble(hw,
+                               vif->bss_conf.use_short_preamble);
                if (rc)
                        goto out;
+       }
 
-               /* Update peer rate info */
-               rc = mwl8k_cmd_update_sta_db(hw, vif,
-                               MWL8K_STA_DB_MODIFY_ENTRY);
+       if (changed & BSS_CHANGED_ERP_SLOT) {
+               rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
                if (rc)
                        goto out;
+       }
 
-               /* Set AID */
-               rc = mwl8k_cmd_set_aid(hw, vif);
+       if (((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) ||
+           (changed & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))) {
+               rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
                if (rc)
                        goto out;
+       }
 
+       if (vif->bss_conf.assoc &&
+           (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) {
                /*
                 * Finalize the join.  Tell rx handler to process
                 * next beacon from our BSSID.
                 */
-               memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN);
+               memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN);
                priv->capture_beacon = true;
-       } else {
-               rc = mwl8k_cmd_update_sta_db(hw, vif, MWL8K_STA_DB_DEL_ENTRY);
-               memset(&mwl8k_vif->bss_info, 0,
-                       sizeof(struct ieee80211_bss_conf));
-               memset(mwl8k_vif->bssid, 0, ETH_ALEN);
        }
 
 out:
@@ -3114,7 +3153,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw,
        }
 
        if (!priv->sniffer_enabled) {
-               if (mwl8k_enable_sniffer(hw, 1))
+               if (mwl8k_cmd_enable_sniffer(hw, 1))
                        return 0;
                priv->sniffer_enabled = true;
        }
@@ -3163,7 +3202,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw,
        }
 
        if (priv->sniffer_enabled) {
-               mwl8k_enable_sniffer(hw, 0);
+               mwl8k_cmd_enable_sniffer(hw, 0);
                priv->sniffer_enabled = false;
        }
 
@@ -3174,7 +3213,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw,
                         */
                        mwl8k_cmd_set_pre_scan(hw);
                } else {
-                       u8 *bssid;
+                       const u8 *bssid;
 
                        /*
                         * Enable the BSS filter.
@@ -3186,7 +3225,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw,
                         */
                        bssid = "\x01\x00\x00\x00\x00\x00";
                        if (priv->vif != NULL)
-                               bssid = MWL8K_VIF(priv->vif)->bssid;
+                               bssid = priv->vif->bss_conf.bssid;
 
                        mwl8k_cmd_set_post_scan(hw, bssid);
                }
@@ -3213,7 +3252,79 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw,
 
 static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 {
-       return mwl8k_rts_threshold(hw, MWL8K_CMD_SET, value);
+       return mwl8k_cmd_set_rts_threshold(hw, MWL8K_CMD_SET, value);
+}
+
+struct mwl8k_sta_notify_item
+{
+       struct list_head list;
+       struct ieee80211_vif *vif;
+       enum sta_notify_cmd cmd;
+       struct ieee80211_sta sta;
+};
+
+static void mwl8k_sta_notify_worker(struct work_struct *work)
+{
+       struct mwl8k_priv *priv =
+               container_of(work, struct mwl8k_priv, sta_notify_worker);
+       struct ieee80211_hw *hw = priv->hw;
+
+       spin_lock_bh(&priv->sta_notify_list_lock);
+       while (!list_empty(&priv->sta_notify_list)) {
+               struct mwl8k_sta_notify_item *s;
+
+               s = list_entry(priv->sta_notify_list.next,
+                              struct mwl8k_sta_notify_item, list);
+               list_del(&s->list);
+
+               spin_unlock_bh(&priv->sta_notify_list_lock);
+
+               if (s->cmd == STA_NOTIFY_ADD) {
+                       int rc;
+
+                       rc = mwl8k_cmd_update_stadb_add(hw, s->vif, &s->sta);
+                       if (rc >= 0) {
+                               struct ieee80211_sta *sta;
+
+                               rcu_read_lock();
+                               sta = ieee80211_find_sta(s->vif, s->sta.addr);
+                               if (sta != NULL)
+                                       MWL8K_STA(sta)->peer_id = rc;
+                               rcu_read_unlock();
+                       }
+               } else {
+                       mwl8k_cmd_update_stadb_del(hw, s->vif, s->sta.addr);
+               }
+
+               kfree(s);
+
+               spin_lock_bh(&priv->sta_notify_list_lock);
+       }
+       spin_unlock_bh(&priv->sta_notify_list_lock);
+}
+
+static void
+mwl8k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+{
+       struct mwl8k_priv *priv = hw->priv;
+       struct mwl8k_sta_notify_item *s;
+
+       if (cmd != STA_NOTIFY_ADD && cmd != STA_NOTIFY_REMOVE)
+               return;
+
+       s = kmalloc(sizeof(*s), GFP_ATOMIC);
+       if (s != NULL) {
+               s->vif = vif;
+               s->cmd = cmd;
+               s->sta = *sta;
+
+               spin_lock(&priv->sta_notify_list_lock);
+               list_add_tail(&s->list, &priv->sta_notify_list);
+               spin_unlock(&priv->sta_notify_list_lock);
+
+               ieee80211_queue_work(hw, &priv->sta_notify_worker);
+       }
 }
 
 static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
@@ -3225,14 +3336,14 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
        rc = mwl8k_fw_lock(hw);
        if (!rc) {
                if (!priv->wmm_enabled)
-                       rc = mwl8k_set_wmm(hw, 1);
+                       rc = mwl8k_cmd_set_wmm_mode(hw, 1);
 
                if (!rc)
-                       rc = mwl8k_set_edca_params(hw, queue,
-                                                  params->cw_min,
-                                                  params->cw_max,
-                                                  params->aifs,
-                                                  params->txop);
+                       rc = mwl8k_cmd_set_edca_params(hw, queue,
+                                                      params->cw_min,
+                                                      params->cw_max,
+                                                      params->aifs,
+                                                      params->txop);
 
                mwl8k_fw_unlock(hw);
        }
@@ -3261,7 +3372,23 @@ static int mwl8k_get_tx_stats(struct ieee80211_hw *hw,
 static int mwl8k_get_stats(struct ieee80211_hw *hw,
                           struct ieee80211_low_level_stats *stats)
 {
-       return mwl8k_cmd_802_11_get_stat(hw, stats);
+       return mwl8k_cmd_get_stat(hw, stats);
+}
+
+static int
+mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                  enum ieee80211_ampdu_mlme_action action,
+                  struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+       switch (action) {
+       case IEEE80211_AMPDU_RX_START:
+       case IEEE80211_AMPDU_RX_STOP:
+               if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
+                       return -ENOTSUPP;
+               return 0;
+       default:
+               return -ENOTSUPP;
+       }
 }
 
 static const struct ieee80211_ops mwl8k_ops = {
@@ -3275,9 +3402,11 @@ static const struct ieee80211_ops mwl8k_ops = {
        .prepare_multicast      = mwl8k_prepare_multicast,
        .configure_filter       = mwl8k_configure_filter,
        .set_rts_threshold      = mwl8k_set_rts_threshold,
+       .sta_notify             = mwl8k_sta_notify,
        .conf_tx                = mwl8k_conf_tx,
        .get_tx_stats           = mwl8k_get_tx_stats,
        .get_stats              = mwl8k_get_stats,
+       .ampdu_action           = mwl8k_ampdu_action,
 };
 
 static void mwl8k_tx_reclaim_handler(unsigned long data)
@@ -3302,37 +3431,42 @@ static void mwl8k_finalize_join_worker(struct work_struct *work)
        struct mwl8k_priv *priv =
                container_of(work, struct mwl8k_priv, finalize_join_worker);
        struct sk_buff *skb = priv->beacon_skb;
-       u8 dtim = MWL8K_VIF(priv->vif)->bss_info.dtim_period;
 
-       mwl8k_finalize_join(priv->hw, skb->data, skb->len, dtim);
+       mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len,
+                               priv->vif->bss_conf.dtim_period);
        dev_kfree_skb(skb);
 
        priv->beacon_skb = NULL;
 }
 
 enum {
-       MWL8687 = 0,
+       MWL8363 = 0,
+       MWL8687,
        MWL8366,
 };
 
 static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = {
-       {
+       [MWL8363] = {
+               .part_name      = "88w8363",
+               .helper_image   = "mwl8k/helper_8363.fw",
+               .fw_image       = "mwl8k/fmimage_8363.fw",
+       },
+       [MWL8687] = {
                .part_name      = "88w8687",
                .helper_image   = "mwl8k/helper_8687.fw",
                .fw_image       = "mwl8k/fmimage_8687.fw",
-               .rxd_ops        = &rxd_8687_ops,
-               .modes          = BIT(NL80211_IFTYPE_STATION),
        },
-       {
+       [MWL8366] = {
                .part_name      = "88w8366",
                .helper_image   = "mwl8k/helper_8366.fw",
                .fw_image       = "mwl8k/fmimage_8366.fw",
-               .rxd_ops        = &rxd_8366_ops,
-               .modes          = 0,
+               .ap_rxd_ops     = &rxd_8366_ap_ops,
        },
 };
 
 static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
+       { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, },
+       { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, },
        { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },
        { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },
        { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, },
@@ -3354,6 +3488,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
                printed_version = 1;
        }
 
+
        rc = pci_enable_device(pdev);
        if (rc) {
                printk(KERN_ERR "%s: Cannot enable new PCI device\n",
@@ -3370,6 +3505,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
+
        hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
        if (hw == NULL) {
                printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
@@ -3377,17 +3513,14 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
                goto err_free_reg;
        }
 
+       SET_IEEE80211_DEV(hw, &pdev->dev);
+       pci_set_drvdata(pdev, hw);
+
        priv = hw->priv;
        priv->hw = hw;
        priv->pdev = pdev;
        priv->device_info = &mwl8k_info_tbl[id->driver_data];
-       priv->rxd_ops = priv->device_info->rxd_ops;
-       priv->sniffer_enabled = false;
-       priv->wmm_enabled = false;
-       priv->pending_tx_pkts = 0;
 
-       SET_IEEE80211_DEV(hw, &pdev->dev);
-       pci_set_drvdata(pdev, hw);
 
        priv->sram = pci_iomap(pdev, 0, 0x10000);
        if (priv->sram == NULL) {
@@ -3410,6 +3543,47 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
                }
        }
 
+
+       /* Reset firmware and hardware */
+       mwl8k_hw_reset(priv);
+
+       /* Ask userland hotplug daemon for the device firmware */
+       rc = mwl8k_request_firmware(priv);
+       if (rc) {
+               printk(KERN_ERR "%s: Firmware files not found\n",
+                      wiphy_name(hw->wiphy));
+               goto err_stop_firmware;
+       }
+
+       /* Load firmware into hardware */
+       rc = mwl8k_load_firmware(hw);
+       if (rc) {
+               printk(KERN_ERR "%s: Cannot start firmware\n",
+                      wiphy_name(hw->wiphy));
+               goto err_stop_firmware;
+       }
+
+       /* Reclaim memory once firmware is successfully loaded */
+       mwl8k_release_firmware(priv);
+
+
+       if (priv->ap_fw) {
+               priv->rxd_ops = priv->device_info->ap_rxd_ops;
+               if (priv->rxd_ops == NULL) {
+                       printk(KERN_ERR "%s: Driver does not have AP "
+                              "firmware image support for this hardware\n",
+                              wiphy_name(hw->wiphy));
+                       goto err_stop_firmware;
+               }
+       } else {
+               priv->rxd_ops = &rxd_sta_ops;
+       }
+
+       priv->sniffer_enabled = false;
+       priv->wmm_enabled = false;
+       priv->pending_tx_pkts = 0;
+
+
        memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels));
        priv->band.band = IEEE80211_BAND_2GHZ;
        priv->band.channels = priv->channels;
@@ -3432,17 +3606,21 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 
        hw->queues = MWL8K_TX_QUEUES;
 
-       hw->wiphy->interface_modes = priv->device_info->modes;
-
        /* Set rssi and noise values to dBm */
        hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM;
        hw->vif_data_size = sizeof(struct mwl8k_vif);
+       hw->sta_data_size = sizeof(struct mwl8k_sta);
        priv->vif = NULL;
 
        /* Set default radio state and preamble */
        priv->radio_on = 0;
        priv->radio_short_preamble = 0;
 
+       /* Station database handling */
+       INIT_WORK(&priv->sta_notify_worker, mwl8k_sta_notify_worker);
+       spin_lock_init(&priv->sta_notify_list_lock);
+       INIT_LIST_HEAD(&priv->sta_notify_list);
+
        /* Finalize join worker */
        INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
 
@@ -3454,11 +3632,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
        /* Power management cookie */
        priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
        if (priv->cookie == NULL)
-               goto err_iounmap;
+               goto err_stop_firmware;
 
        rc = mwl8k_rxq_init(hw, 0);
        if (rc)
-               goto err_iounmap;
+               goto err_free_cookie;
        rxq_refill(hw, 0, INT_MAX);
 
        mutex_init(&priv->fw_mutex);
@@ -3489,28 +3667,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
                goto err_free_queues;
        }
 
-       /* Reset firmware and hardware */
-       mwl8k_hw_reset(priv);
-
-       /* Ask userland hotplug daemon for the device firmware */
-       rc = mwl8k_request_firmware(priv);
-       if (rc) {
-               printk(KERN_ERR "%s: Firmware files not found\n",
-                      wiphy_name(hw->wiphy));
-               goto err_free_irq;
-       }
-
-       /* Load firmware into hardware */
-       rc = mwl8k_load_firmware(hw);
-       if (rc) {
-               printk(KERN_ERR "%s: Cannot start firmware\n",
-                      wiphy_name(hw->wiphy));
-               goto err_stop_firmware;
-       }
-
-       /* Reclaim memory once firmware is successfully loaded */
-       mwl8k_release_firmware(priv);
-
        /*
         * Temporarily enable interrupts.  Initial firmware host
         * commands use interrupts and avoids polling.  Disable
@@ -3525,26 +3681,28 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
                        rc = mwl8k_cmd_set_hw_spec(hw);
        } else {
                rc = mwl8k_cmd_get_hw_spec_sta(hw);
+
+               hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
        }
        if (rc) {
                printk(KERN_ERR "%s: Cannot initialise firmware\n",
                       wiphy_name(hw->wiphy));
-               goto err_stop_firmware;
+               goto err_free_irq;
        }
 
        /* Turn radio off */
-       rc = mwl8k_cmd_802_11_radio_disable(hw);
+       rc = mwl8k_cmd_radio_disable(hw);
        if (rc) {
                printk(KERN_ERR "%s: Cannot disable\n", wiphy_name(hw->wiphy));
-               goto err_stop_firmware;
+               goto err_free_irq;
        }
 
        /* Clear MAC address */
-       rc = mwl8k_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
+       rc = mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00");
        if (rc) {
                printk(KERN_ERR "%s: Cannot clear MAC address\n",
                       wiphy_name(hw->wiphy));
-               goto err_stop_firmware;
+               goto err_free_irq;
        }
 
        /* Disable interrupts */
@@ -3555,7 +3713,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
        if (rc) {
                printk(KERN_ERR "%s: Cannot register device\n",
                       wiphy_name(hw->wiphy));
-               goto err_stop_firmware;
+               goto err_free_queues;
        }
 
        printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n",
@@ -3567,10 +3725,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
 
        return 0;
 
-err_stop_firmware:
-       mwl8k_hw_reset(priv);
-       mwl8k_release_firmware(priv);
-
 err_free_irq:
        iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
        free_irq(priv->pdev->irq, hw);
@@ -3580,11 +3734,16 @@ err_free_queues:
                mwl8k_txq_deinit(hw, i);
        mwl8k_rxq_deinit(hw, 0);
 
-err_iounmap:
+err_free_cookie:
        if (priv->cookie != NULL)
                pci_free_consistent(priv->pdev, 4,
                                priv->cookie, priv->cookie_dma);
 
+err_stop_firmware:
+       mwl8k_hw_reset(priv);
+       mwl8k_release_firmware(priv);
+
+err_iounmap:
        if (priv->regs != NULL)
                pci_iounmap(pdev, priv->regs);
 
index 18012dbfb45dff585dbbadc0b0a0224f4d56812c..26428e4c9c60257a4dee119abc885cbb77fd8cd4 100644 (file)
@@ -216,7 +216,7 @@ static void p54_stop(struct ieee80211_hw *dev)
 }
 
 static int p54_add_interface(struct ieee80211_hw *dev,
-                            struct ieee80211_if_init_conf *conf)
+                            struct ieee80211_vif *vif)
 {
        struct p54_common *priv = dev->priv;
 
@@ -226,28 +226,28 @@ static int p54_add_interface(struct ieee80211_hw *dev,
                return -EOPNOTSUPP;
        }
 
-       priv->vif = conf->vif;
+       priv->vif = vif;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_MESH_POINT:
-               priv->mode = conf->type;
+               priv->mode = vif->type;
                break;
        default:
                mutex_unlock(&priv->conf_mutex);
                return -EOPNOTSUPP;
        }
 
-       memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN);
+       memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
        p54_setup_mac(priv);
        mutex_unlock(&priv->conf_mutex);
        return 0;
 }
 
 static void p54_remove_interface(struct ieee80211_hw *dev,
-                                struct ieee80211_if_init_conf *conf)
+                                struct ieee80211_vif *vif)
 {
        struct p54_common *priv = dev->priv;
 
index 2ecbedb26e152c28e697ac8927be151934ee4144..305c106fdc1c442bce51bbf1c84f1a85fa8ed9ab 100644 (file)
@@ -2594,23 +2594,9 @@ end:
 /*
  * driver/device initialization
  */
-static int bcm4320a_early_init(struct usbnet *usbdev)
-{
-       /* bcm4320a doesn't handle configuration parameters well. Try
-        * set any and you get partially zeroed mac and broken device.
-        */
-
-       return 0;
-}
-
-static int bcm4320b_early_init(struct usbnet *usbdev)
+static void rndis_copy_module_params(struct usbnet *usbdev)
 {
        struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
-       char buf[8];
-
-       /* Early initialization settings, setting these won't have effect
-        * if called after generic_rndis_bind().
-        */
 
        priv->param_country[0] = modparam_country[0];
        priv->param_country[1] = modparam_country[1];
@@ -2652,6 +2638,32 @@ static int bcm4320b_early_init(struct usbnet *usbdev)
                priv->param_workaround_interval = 500;
        else
                priv->param_workaround_interval = modparam_workaround_interval;
+}
+
+static int bcm4320a_early_init(struct usbnet *usbdev)
+{
+       /* copy module parameters for bcm4320a so that iwconfig reports txpower
+        * and workaround parameter is copied to private structure correctly.
+        */
+       rndis_copy_module_params(usbdev);
+
+       /* bcm4320a doesn't handle configuration parameters well. Try
+        * set any and you get partially zeroed mac and broken device.
+        */
+
+       return 0;
+}
+
+static int bcm4320b_early_init(struct usbnet *usbdev)
+{
+       struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
+       char buf[8];
+
+       rndis_copy_module_params(usbdev);
+
+       /* Early initialization settings, setting these won't have effect
+        * if called after generic_rndis_bind().
+        */
 
        rndis_set_config_parameter_str(usbdev, "Country", priv->param_country);
        rndis_set_config_parameter_str(usbdev, "FrameBursting",
index bf60689aaabb02e72b58225f9a9fc82571412686..3ca824a91ad903a44ce7f583404c37bd44892fe0 100644 (file)
@@ -54,12 +54,12 @@ config RT61PCI
          When compiled as a module, this driver will be called rt61pci.
 
 config RT2800PCI_PCI
-       tristate
+       boolean
        depends on PCI
        default y
 
 config RT2800PCI_SOC
-       tristate
+       boolean
        depends on RALINK_RT288X || RALINK_RT305X
        default y
 
index e7f46405a418d5ecdacd60f9d92eeb910c5731b4..d86d233c681015f81f114d83fa80153b809cb909 100644 (file)
@@ -451,7 +451,7 @@ static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev,
        /*
         * RF2420 chipset don't need any additional actions.
         */
-       if (rt2x00_rf(&rt2x00dev->chip, RF2420))
+       if (rt2x00_rf(rt2x00dev, RF2420))
                return;
 
        /*
@@ -1343,8 +1343,7 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_chip_rf(rt2x00dev, value, reg);
        rt2x00_print_chip(rt2x00dev);
 
-       if (!rt2x00_rf(&rt2x00dev->chip, RF2420) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2421)) {
+       if (!rt2x00_rf(rt2x00dev, RF2420) && !rt2x00_rf(rt2x00dev, RF2421)) {
                ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
                return -ENODEV;
        }
index 408fcfc120f503a459f7e2fda05c514ff18bf71e..46cbc6ef66abb2e335fac9f7c6c2b717e9d571a4 100644 (file)
@@ -440,8 +440,7 @@ static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev,
        /*
         * RT2525E and RT5222 need to flip TX I/Q
         */
-       if (rt2x00_rf(&rt2x00dev->chip, RF2525E) ||
-           rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+       if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) {
                rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1);
                rt2x00_set_field32(&reg, BBPCSR1_CCK_FLIP, 1);
                rt2x00_set_field32(&reg, BBPCSR1_OFDM_FLIP, 1);
@@ -449,7 +448,7 @@ static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev,
                /*
                 * RT2525E does not need RX I/Q Flip.
                 */
-               if (rt2x00_rf(&rt2x00dev->chip, RF2525E))
+               if (rt2x00_rf(rt2x00dev, RF2525E))
                        rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0);
        } else {
                rt2x00_set_field32(&reg, BBPCSR1_CCK_FLIP, 0);
@@ -475,14 +474,14 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev,
         * Switch on tuning bits.
         * For RT2523 devices we do not need to update the R1 register.
         */
-       if (!rt2x00_rf(&rt2x00dev->chip, RF2523))
+       if (!rt2x00_rf(rt2x00dev, RF2523))
                rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1);
        rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1);
 
        /*
         * For RT2525 we should first set the channel to half band higher.
         */
-       if (rt2x00_rf(&rt2x00dev->chip, RF2525)) {
+       if (rt2x00_rf(rt2x00dev, RF2525)) {
                static const u32 vals[] = {
                        0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a,
                        0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a,
@@ -516,7 +515,7 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev,
         * Switch off tuning bits.
         * For RT2523 devices we do not need to update the R1 register.
         */
-       if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) {
+       if (!rt2x00_rf(rt2x00dev, RF2523)) {
                rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0);
                rt2500pci_rf_write(rt2x00dev, 1, rf->rf1);
        }
@@ -640,7 +639,7 @@ static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev,
         * up to version C the link tuning should halt after 20
         * seconds while being associated.
         */
-       if (rt2x00_rev(&rt2x00dev->chip) < RT2560_VERSION_D &&
+       if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D &&
            rt2x00dev->intf_associated && count > 20)
                return;
 
@@ -650,7 +649,7 @@ static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev,
         * should go straight to dynamic CCA tuning when they
         * are not associated.
         */
-       if (rt2x00_rev(&rt2x00dev->chip) < RT2560_VERSION_D ||
+       if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D ||
            !rt2x00dev->intf_associated)
                goto dynamic_cca_tune;
 
@@ -1507,12 +1506,12 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_chip_rf(rt2x00dev, value, reg);
        rt2x00_print_chip(rt2x00dev);
 
-       if (!rt2x00_rf(&rt2x00dev->chip, RF2522) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2523) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2524) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2525) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2525E) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+       if (!rt2x00_rf(rt2x00dev, RF2522) &&
+           !rt2x00_rf(rt2x00dev, RF2523) &&
+           !rt2x00_rf(rt2x00dev, RF2524) &&
+           !rt2x00_rf(rt2x00dev, RF2525) &&
+           !rt2x00_rf(rt2x00dev, RF2525E) &&
+           !rt2x00_rf(rt2x00dev, RF5222)) {
                ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
                return -ENODEV;
        }
@@ -1744,22 +1743,22 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (rt2x00_rf(&rt2x00dev->chip, RF2522)) {
+       if (rt2x00_rf(rt2x00dev, RF2522)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522);
                spec->channels = rf_vals_bg_2522;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2523)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2523)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523);
                spec->channels = rf_vals_bg_2523;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2524)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2524)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524);
                spec->channels = rf_vals_bg_2524;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2525)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2525)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525);
                spec->channels = rf_vals_bg_2525;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2525E)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e);
                spec->channels = rf_vals_bg_2525e;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+       } else if (rt2x00_rf(rt2x00dev, RF5222)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_5222);
                spec->channels = rf_vals_5222;
index 83f2592c59def385091a81e5e070d184d7a0260e..9e6f865c57f22b377fb53e5f6e44ca0192cac515 100644 (file)
@@ -565,8 +565,7 @@ static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
        /*
         * RT2525E and RT5222 need to flip TX I/Q
         */
-       if (rt2x00_rf(&rt2x00dev->chip, RF2525E) ||
-           rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+       if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) {
                rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1);
                rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1);
                rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1);
@@ -574,7 +573,7 @@ static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev,
                /*
                 * RT2525E does not need RX I/Q Flip.
                 */
-               if (rt2x00_rf(&rt2x00dev->chip, RF2525E))
+               if (rt2x00_rf(rt2x00dev, RF2525E))
                        rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0);
        } else {
                rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0);
@@ -598,7 +597,7 @@ static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev,
        /*
         * For RT2525E we should first set the channel to half band higher.
         */
-       if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) {
+       if (rt2x00_rf(rt2x00dev, RF2525E)) {
                static const u32 vals[] = {
                        0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2,
                        0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba,
@@ -793,7 +792,7 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_field16(&reg, MAC_CSR1_HOST_READY, 1);
        rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg);
 
-       if (rt2x00_rev(&rt2x00dev->chip) >= RT2570_VERSION_C) {
+       if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) {
                rt2500usb_register_read(rt2x00dev, PHY_CSR2, &reg);
                rt2x00_set_field16(&reg, PHY_CSR2_LNA, 0);
        } else {
@@ -1411,19 +1410,18 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_chip(rt2x00dev, RT2570, value, reg);
        rt2x00_print_chip(rt2x00dev);
 
-       if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0) ||
-           rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) {
-
+       if (!rt2x00_check_rev(rt2x00dev, 0x000ffff0, 0) ||
+           rt2x00_check_rev(rt2x00dev, 0x0000000f, 0)) {
                ERROR(rt2x00dev, "Invalid RT chipset detected.\n");
                return -ENODEV;
        }
 
-       if (!rt2x00_rf(&rt2x00dev->chip, RF2522) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2523) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2524) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2525) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2525E) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+       if (!rt2x00_rf(rt2x00dev, RF2522) &&
+           !rt2x00_rf(rt2x00dev, RF2523) &&
+           !rt2x00_rf(rt2x00dev, RF2524) &&
+           !rt2x00_rf(rt2x00dev, RF2525) &&
+           !rt2x00_rf(rt2x00dev, RF2525E) &&
+           !rt2x00_rf(rt2x00dev, RF5222)) {
                ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
                return -ENODEV;
        }
@@ -1667,22 +1665,22 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (rt2x00_rf(&rt2x00dev->chip, RF2522)) {
+       if (rt2x00_rf(rt2x00dev, RF2522)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522);
                spec->channels = rf_vals_bg_2522;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2523)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2523)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523);
                spec->channels = rf_vals_bg_2523;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2524)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2524)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524);
                spec->channels = rf_vals_bg_2524;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2525)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2525)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525);
                spec->channels = rf_vals_bg_2525;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2525E)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e);
                spec->channels = rf_vals_bg_2525e;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) {
+       } else if (rt2x00_rf(rt2x00dev, RF5222)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_5222);
                spec->channels = rf_vals_5222;
index 9deae41cb784ef342b64652363232e76acab4bc6..126899091707ae04a238b5891cf480b1f06c646f 100644 (file)
@@ -37,7 +37,7 @@
 #include <linux/module.h>
 
 #include "rt2x00.h"
-#if defined(CONFIG_RT2800USB) || defined(CONFIG_RT2800USB_MODULE)
+#if defined(CONFIG_RT2X00_LIB_USB) || defined(CONFIG_RT2X00_LIB_USB_MODULE)
 #include "rt2x00usb.h"
 #endif
 #include "rt2800lib.h"
@@ -220,8 +220,7 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
        /*
         * RT2880 and RT3052 don't support MCU requests.
         */
-       if (rt2x00_rt(&rt2x00dev->chip, RT2880) ||
-           rt2x00_rt(&rt2x00dev->chip, RT3052))
+       if (rt2x00_rt(rt2x00dev, RT2880) || rt2x00_rt(rt2x00dev, RT3052))
                return;
 
        mutex_lock(&rt2x00dev->csr_mutex);
@@ -806,12 +805,12 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        unsigned int tx_pin;
        u8 bbp;
 
-       if ((rt2x00_rt(&rt2x00dev->chip, RT3070) ||
-            rt2x00_rt(&rt2x00dev->chip, RT3090)) &&
-           (rt2x00_rf(&rt2x00dev->chip, RF2020) ||
-            rt2x00_rf(&rt2x00dev->chip, RF3020) ||
-            rt2x00_rf(&rt2x00dev->chip, RF3021) ||
-            rt2x00_rf(&rt2x00dev->chip, RF3022)))
+       if ((rt2x00_rt(rt2x00dev, RT3070) ||
+            rt2x00_rt(rt2x00dev, RT3090)) &&
+           (rt2x00_rf(rt2x00dev, RF2020) ||
+            rt2x00_rf(rt2x00dev, RF3020) ||
+            rt2x00_rf(rt2x00dev, RF3021) ||
+            rt2x00_rf(rt2x00dev, RF3022)))
                rt2800_config_channel_rt3x(rt2x00dev, conf, rf, info);
        else
                rt2800_config_channel_rt2x(rt2x00dev, conf, rf, info);
@@ -878,7 +877,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field8(&bbp, BBP3_HT40_PLUS, conf_is_ht40_plus(conf));
        rt2800_bbp_write(rt2x00dev, 3, bbp);
 
-       if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) {
+       if (rt2x00_rev(rt2x00dev) == RT2860C_VERSION) {
                if (conf_is_ht40(conf)) {
                        rt2800_bbp_write(rt2x00dev, 69, 0x1a);
                        rt2800_bbp_write(rt2x00dev, 70, 0x0a);
@@ -1041,7 +1040,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
 {
        if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
                if (rt2x00_intf_is_usb(rt2x00dev) &&
-                   rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION)
+                   rt2x00_rev(rt2x00dev) == RT3070_VERSION)
                        return 0x1c + (2 * rt2x00dev->lna_gain);
                else
                        return 0x2e + rt2x00dev->lna_gain;
@@ -1072,7 +1071,7 @@ EXPORT_SYMBOL_GPL(rt2800_reset_tuner);
 void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
                       const u32 count)
 {
-       if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION)
+       if (rt2x00_rev(rt2x00dev) == RT2860C_VERSION)
                return;
 
        /*
@@ -1121,7 +1120,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
 
        if (rt2x00_intf_is_usb(rt2x00dev)) {
                rt2800_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000);
-#if defined(CONFIG_RT2800USB) || defined(CONFIG_RT2800USB_MODULE)
+#if defined(CONFIG_RT2X00_LIB_USB) || defined(CONFIG_RT2X00_LIB_USB_MODULE)
                rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0,
                                            USB_MODE_RESET, REGISTER_TIMEOUT);
 #endif
@@ -1158,7 +1157,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
 
        if (rt2x00_intf_is_usb(rt2x00dev) &&
-           rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) {
+           rt2x00_rev(rt2x00dev) == RT3070_VERSION) {
                rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
                rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
                rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000);
@@ -1185,8 +1184,8 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
 
        rt2800_register_read(rt2x00dev, MAX_LEN_CFG, &reg);
        rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE);
-       if (rt2x00_rev(&rt2x00dev->chip) >= RT2880E_VERSION &&
-           rt2x00_rev(&rt2x00dev->chip) < RT3070_VERSION)
+       if (rt2x00_rev(rt2x00dev) >= RT2880E_VERSION &&
+           rt2x00_rev(rt2x00dev) < RT3070_VERSION)
                rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_PSDU, 2);
        else
                rt2x00_set_field32(&reg, MAX_LEN_CFG_MAX_PSDU, 1);
@@ -1465,22 +1464,22 @@ int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
        rt2800_bbp_write(rt2x00dev, 103, 0x00);
        rt2800_bbp_write(rt2x00dev, 105, 0x05);
 
-       if (rt2x00_rev(&rt2x00dev->chip) == RT2860C_VERSION) {
+       if (rt2x00_rev(rt2x00dev) == RT2860C_VERSION) {
                rt2800_bbp_write(rt2x00dev, 69, 0x16);
                rt2800_bbp_write(rt2x00dev, 73, 0x12);
        }
 
-       if (rt2x00_rev(&rt2x00dev->chip) > RT2860D_VERSION)
+       if (rt2x00_rev(rt2x00dev) > RT2860D_VERSION)
                rt2800_bbp_write(rt2x00dev, 84, 0x19);
 
        if (rt2x00_intf_is_usb(rt2x00dev) &&
-           rt2x00_rev(&rt2x00dev->chip) == RT3070_VERSION) {
+           rt2x00_rev(rt2x00dev) == RT3070_VERSION) {
                rt2800_bbp_write(rt2x00dev, 70, 0x0a);
                rt2800_bbp_write(rt2x00dev, 84, 0x99);
                rt2800_bbp_write(rt2x00dev, 105, 0x05);
        }
 
-       if (rt2x00_rt(&rt2x00dev->chip, RT3052)) {
+       if (rt2x00_rt(rt2x00dev, RT3052)) {
                rt2800_bbp_write(rt2x00dev, 31, 0x08);
                rt2800_bbp_write(rt2x00dev, 78, 0x0e);
                rt2800_bbp_write(rt2x00dev, 80, 0x08);
@@ -1566,13 +1565,13 @@ int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
        u8 bbp;
 
        if (rt2x00_intf_is_usb(rt2x00dev) &&
-           rt2x00_rev(&rt2x00dev->chip) != RT3070_VERSION)
+           rt2x00_rev(rt2x00dev) != RT3070_VERSION)
                return 0;
 
        if (rt2x00_intf_is_pci(rt2x00dev)) {
-               if (!rt2x00_rf(&rt2x00dev->chip, RF3020) &&
-                   !rt2x00_rf(&rt2x00dev->chip, RF3021) &&
-                   !rt2x00_rf(&rt2x00dev->chip, RF3022))
+               if (!rt2x00_rf(rt2x00dev, RF3020) &&
+                   !rt2x00_rf(rt2x00dev, RF3021) &&
+                   !rt2x00_rf(rt2x00dev, RF3022))
                        return 0;
        }
 
@@ -1737,7 +1736,7 @@ int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
                rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2820);
                rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word);
                EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word);
-       } else if (rt2x00_rev(&rt2x00dev->chip) < RT2883_VERSION) {
+       } else if (rt2x00_rev(rt2x00dev) < RT2883_VERSION) {
                /*
                 * There is a max of 2 RX streams for RT28x0 series
                 */
@@ -1839,17 +1838,15 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_chip_rf(rt2x00dev, value, reg);
 
        if (rt2x00_intf_is_usb(rt2x00dev)) {
-               struct rt2x00_chip *chip = &rt2x00dev->chip;
-
                /*
                 * The check for rt2860 is not a typo, some rt2870 hardware
                 * identifies itself as rt2860 in the CSR register.
                 */
-               if (rt2x00_check_rev(chip, 0xfff00000, 0x28600000) ||
-                   rt2x00_check_rev(chip, 0xfff00000, 0x28700000) ||
-                   rt2x00_check_rev(chip, 0xfff00000, 0x28800000)) {
+               if (rt2x00_check_rev(rt2x00dev, 0xfff00000, 0x28600000) ||
+                   rt2x00_check_rev(rt2x00dev, 0xfff00000, 0x28700000) ||
+                   rt2x00_check_rev(rt2x00dev, 0xfff00000, 0x28800000)) {
                        rt2x00_set_chip_rt(rt2x00dev, RT2870);
-               } else if (rt2x00_check_rev(chip, 0xffff0000, 0x30700000)) {
+               } else if (rt2x00_check_rev(rt2x00dev, 0xffff0000, 0x30700000)) {
                        rt2x00_set_chip_rt(rt2x00dev, RT3070);
                } else {
                        ERROR(rt2x00dev, "Invalid RT chipset detected.\n");
@@ -1858,14 +1855,14 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        }
        rt2x00_print_chip(rt2x00dev);
 
-       if (!rt2x00_rf(&rt2x00dev->chip, RF2820) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2850) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2720) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2750) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF3020) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2020) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF3021) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF3022)) {
+       if (!rt2x00_rf(rt2x00dev, RF2820) &&
+           !rt2x00_rf(rt2x00dev, RF2850) &&
+           !rt2x00_rf(rt2x00dev, RF2720) &&
+           !rt2x00_rf(rt2x00dev, RF2750) &&
+           !rt2x00_rf(rt2x00dev, RF3020) &&
+           !rt2x00_rf(rt2x00dev, RF2020) &&
+           !rt2x00_rf(rt2x00dev, RF3021) &&
+           !rt2x00_rf(rt2x00dev, RF3022)) {
                ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
                return -ENODEV;
        }
@@ -2013,7 +2010,6 @@ static const struct rf_channel rf_vals_302x[] = {
 
 int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
 {
-       struct rt2x00_chip *chip = &rt2x00dev->chip;
        struct hw_mode_spec *spec = &rt2x00dev->spec;
        struct channel_info *info;
        char *tx_power1;
@@ -2049,19 +2045,19 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (rt2x00_rf(chip, RF2820) ||
-           rt2x00_rf(chip, RF2720) ||
-           (rt2x00_intf_is_pci(rt2x00dev) && rt2x00_rf(chip, RF3052))) {
+       if (rt2x00_rf(rt2x00dev, RF2820) ||
+           rt2x00_rf(rt2x00dev, RF2720) ||
+           (rt2x00_intf_is_pci(rt2x00dev) && rt2x00_rf(rt2x00dev, RF3052))) {
                spec->num_channels = 14;
                spec->channels = rf_vals;
-       } else if (rt2x00_rf(chip, RF2850) || rt2x00_rf(chip, RF2750)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2850) || rt2x00_rf(rt2x00dev, RF2750)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals);
                spec->channels = rf_vals;
-       } else if (rt2x00_rf(chip, RF3020) ||
-                  rt2x00_rf(chip, RF2020) ||
-                  rt2x00_rf(chip, RF3021) ||
-                  rt2x00_rf(chip, RF3022)) {
+       } else if (rt2x00_rf(rt2x00dev, RF3020) ||
+                  rt2x00_rf(rt2x00dev, RF2020) ||
+                  rt2x00_rf(rt2x00dev, RF3021) ||
+                  rt2x00_rf(rt2x00dev, RF3022)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_302x);
                spec->channels = rf_vals_302x;
        }
@@ -2069,7 +2065,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        /*
         * Initialize HT information.
         */
-       if (!rt2x00_rf(chip, RF2020))
+       if (!rt2x00_rf(rt2x00dev, RF2020))
                spec->ht.ht_supported = true;
        else
                spec->ht.ht_supported = false;
index dfc886fcb44de55e0265b92d91cc846f58836f5a..b93eabb4fbe10b7e65c11a77f916c5d0fe3b6da8 100644 (file)
 #include "rt2800.h"
 #include "rt2800pci.h"
 
-#ifdef CONFIG_RT2800PCI_PCI_MODULE
-#define CONFIG_RT2800PCI_PCI
-#endif
-
-#ifdef CONFIG_RT2800PCI_WISOC_MODULE
-#define CONFIG_RT2800PCI_WISOC
-#endif
-
 /*
  * Allow hardware encryption to be disabled.
  */
@@ -87,7 +79,7 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token)
        rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0);
 }
 
-#ifdef CONFIG_RT2800PCI_WISOC
+#ifdef CONFIG_RT2800PCI_SOC
 static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
 {
        u32 *base_addr = (u32 *) KSEG1ADDR(0x1F040000); /* XXX for RT3052 */
@@ -98,7 +90,7 @@ static void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
 static inline void rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev)
 {
 }
-#endif /* CONFIG_RT2800PCI_WISOC */
+#endif /* CONFIG_RT2800PCI_SOC */
 
 #ifdef CONFIG_RT2800PCI_PCI
 static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom)
@@ -835,7 +827,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
                                  struct rxdone_entry_desc *rxdesc)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
        struct queue_entry_priv_pci *entry_priv = entry->priv_data;
        __le32 *rxd = entry_priv->desc;
        __le32 *rxwi = (__le32 *)entry->skb->data;
@@ -883,10 +874,8 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
        if (rt2x00_get_field32(rxd3, RXD_W3_MY_BSS))
                rxdesc->dev_flags |= RXDONE_MY_BSS;
 
-       if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD)) {
+       if (rt2x00_get_field32(rxd3, RXD_W3_L2PAD))
                rxdesc->dev_flags |= RXDONE_L2PAD;
-               skbdesc->flags |= SKBDESC_L2_PADDED;
-       }
 
        if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI))
                rxdesc->flags |= RX_FLAG_SHORT_GI;
@@ -927,7 +916,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
         * Remove TXWI descriptor from start of buffer.
         */
        skb_pull(entry->skb, RXWI_DESC_SIZE);
-       skb_trim(entry->skb, rxdesc->size);
 }
 
 /*
@@ -1133,8 +1121,7 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
        /*
         * This device requires firmware.
         */
-       if (!rt2x00_rt(&rt2x00dev->chip, RT2880) &&
-           !rt2x00_rt(&rt2x00dev->chip, RT3052))
+       if (!rt2x00_rt(rt2x00dev, RT2880) && !rt2x00_rt(rt2x00dev, RT3052))
                __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags);
        __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags);
        __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags);
@@ -1255,7 +1242,7 @@ MODULE_DEVICE_TABLE(pci, rt2800pci_device_table);
 #endif /* CONFIG_RT2800PCI_PCI */
 MODULE_LICENSE("GPL");
 
-#ifdef CONFIG_RT2800PCI_WISOC
+#ifdef CONFIG_RT2800PCI_SOC
 #if defined(CONFIG_RALINK_RT288X)
 __rt2x00soc_probe(RT2880, &rt2800pci_ops);
 #elif defined(CONFIG_RALINK_RT305X)
@@ -1273,7 +1260,7 @@ static struct platform_driver rt2800soc_driver = {
        .suspend        = rt2x00soc_suspend,
        .resume         = rt2x00soc_resume,
 };
-#endif /* CONFIG_RT2800PCI_WISOC */
+#endif /* CONFIG_RT2800PCI_SOC */
 
 #ifdef CONFIG_RT2800PCI_PCI
 static struct pci_driver rt2800pci_driver = {
@@ -1290,7 +1277,7 @@ static int __init rt2800pci_init(void)
 {
        int ret = 0;
 
-#ifdef CONFIG_RT2800PCI_WISOC
+#ifdef CONFIG_RT2800PCI_SOC
        ret = platform_driver_register(&rt2800soc_driver);
        if (ret)
                return ret;
@@ -1298,7 +1285,7 @@ static int __init rt2800pci_init(void)
 #ifdef CONFIG_RT2800PCI_PCI
        ret = pci_register_driver(&rt2800pci_driver);
        if (ret) {
-#ifdef CONFIG_RT2800PCI_WISOC
+#ifdef CONFIG_RT2800PCI_SOC
                platform_driver_unregister(&rt2800soc_driver);
 #endif
                return ret;
@@ -1313,7 +1300,7 @@ static void __exit rt2800pci_exit(void)
 #ifdef CONFIG_RT2800PCI_PCI
        pci_unregister_driver(&rt2800pci_driver);
 #endif
-#ifdef CONFIG_RT2800PCI_WISOC
+#ifdef CONFIG_RT2800PCI_SOC
        platform_driver_unregister(&rt2800soc_driver);
 #endif
 }
index ab95346cf6a369102043018b2ee917910c44840d..0510f020dcf56abf48bd297a62e0598e4ad540c6 100644 (file)
@@ -92,7 +92,7 @@ static bool rt2800usb_check_crc(const u8 *data, const size_t len)
 static int rt2800usb_check_firmware(struct rt2x00_dev *rt2x00dev,
                                    const u8 *data, const size_t len)
 {
-       u16 chipset = (rt2x00_rev(&rt2x00dev->chip) >> 16) & 0xffff;
+       u16 chipset = (rt2x00_rev(rt2x00dev) >> 16) & 0xffff;
        size_t offset = 0;
 
        /*
@@ -138,7 +138,7 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
        u32 reg;
        u32 offset;
        u32 length;
-       u16 chipset = (rt2x00_rev(&rt2x00dev->chip) >> 16) & 0xffff;
+       u16 chipset = (rt2x00_rev(rt2x00dev) >> 16) & 0xffff;
 
        /*
         * Check which section of the firmware we need.
@@ -295,9 +295,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
 
        rt2800_register_read(rt2x00dev, USB_DMA_CFG, &reg);
        rt2x00_set_field32(&reg, USB_DMA_CFG_PHY_CLEAR, 0);
-       /* Don't use bulk in aggregation when working with USB 1.1 */
-       rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_EN,
-                          (rt2x00dev->rx->usb_maxpacket == 512));
+       rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_EN, 0);
        rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128);
        /*
         * Total room for RX frames in kilobytes, PBF might still exceed
@@ -573,41 +571,57 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
-       __le32 *rxd = (__le32 *)entry->skb->data;
+       __le32 *rxi = (__le32 *)entry->skb->data;
        __le32 *rxwi;
-       u32 rxd0;
+       __le32 *rxd;
+       u32 rxi0;
        u32 rxwi0;
        u32 rxwi1;
        u32 rxwi2;
        u32 rxwi3;
+       u32 rxd0;
+       int rx_pkt_len;
+
+       /*
+        * RX frame format is :
+        * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad |
+        *          |<------------ rx_pkt_len -------------->|
+        */
+       rt2x00_desc_read(rxi, 0, &rxi0);
+       rx_pkt_len = rt2x00_get_field32(rxi0, RXINFO_W0_USB_DMA_RX_PKT_LEN);
+
+       rxwi = (__le32 *)(entry->skb->data + RXINFO_DESC_SIZE);
+
+       /*
+        * FIXME : we need to check for rx_pkt_len validity
+        */
+       rxd = (__le32 *)(entry->skb->data + RXINFO_DESC_SIZE + rx_pkt_len);
 
        /*
         * Copy descriptor to the skbdesc->desc buffer, making it safe from
         * moving of frame data in rt2x00usb.
         */
-       memcpy(skbdesc->desc, rxd, skbdesc->desc_len);
-       rxd = (__le32 *)skbdesc->desc;
-       rxwi = &rxd[RXINFO_DESC_SIZE / sizeof(__le32)];
+       memcpy(skbdesc->desc, rxi, skbdesc->desc_len);
 
        /*
         * It is now safe to read the descriptor on all architectures.
         */
-       rt2x00_desc_read(rxd, 0, &rxd0);
        rt2x00_desc_read(rxwi, 0, &rxwi0);
        rt2x00_desc_read(rxwi, 1, &rxwi1);
        rt2x00_desc_read(rxwi, 2, &rxwi2);
        rt2x00_desc_read(rxwi, 3, &rxwi3);
+       rt2x00_desc_read(rxd, 0, &rxd0);
 
-       if (rt2x00_get_field32(rxd0, RXINFO_W0_CRC_ERROR))
+       if (rt2x00_get_field32(rxd0, RXD_W0_CRC_ERROR))
                rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC;
 
        if (test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags)) {
                rxdesc->cipher = rt2x00_get_field32(rxwi0, RXWI_W0_UDF);
                rxdesc->cipher_status =
-                   rt2x00_get_field32(rxd0, RXINFO_W0_CIPHER_ERROR);
+                   rt2x00_get_field32(rxd0, RXD_W0_CIPHER_ERROR);
        }
 
-       if (rt2x00_get_field32(rxd0, RXINFO_W0_DECRYPTED)) {
+       if (rt2x00_get_field32(rxd0, RXD_W0_DECRYPTED)) {
                /*
                 * Hardware has stripped IV/EIV data from 802.11 frame during
                 * decryption. Unfortunately the descriptor doesn't contain
@@ -622,13 +636,11 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
                        rxdesc->flags |= RX_FLAG_MMIC_ERROR;
        }
 
-       if (rt2x00_get_field32(rxd0, RXINFO_W0_MY_BSS))
+       if (rt2x00_get_field32(rxd0, RXD_W0_MY_BSS))
                rxdesc->dev_flags |= RXDONE_MY_BSS;
 
-       if (rt2x00_get_field32(rxd0, RXINFO_W0_L2PAD)) {
+       if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD))
                rxdesc->dev_flags |= RXDONE_L2PAD;
-               skbdesc->flags |= SKBDESC_L2_PADDED;
-       }
 
        if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI))
                rxdesc->flags |= RX_FLAG_SHORT_GI;
@@ -663,7 +675,6 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
         * Remove RXWI descriptor from start of buffer.
         */
        skb_pull(entry->skb, skbdesc->desc_len);
-       skb_trim(entry->skb, rxdesc->size);
 }
 
 /*
index 1e4340a182ef2d68bea70ec0eea6c3980d1ba417..d1d8ae94b4d4757026f90867ca46cf1e3dbb37c8 100644 (file)
@@ -79,6 +79,8 @@
  */
 #define TXINFO_DESC_SIZE               ( 1 * sizeof(__le32) )
 #define RXINFO_DESC_SIZE               ( 1 * sizeof(__le32) )
+#define RXWI_DESC_SIZE                 ( 4 * sizeof(__le32) )
+#define RXD_DESC_SIZE                  ( 1 * sizeof(__le32) )
 
 /*
  * TX Info structure
 #define TXINFO_W0_USB_DMA_NEXT_VALID   FIELD32(0x40000000)
 #define TXINFO_W0_USB_DMA_TX_BURST     FIELD32(0x80000000)
 
+/*
+ * RX Info structure
+ */
+
+/*
+ * Word 0
+ */
+
+#define RXINFO_W0_USB_DMA_RX_PKT_LEN   FIELD32(0x0000ffff)
+
+/*
+ * RX WI structure
+ */
+
+/*
+ * Word0
+ */
+#define RXWI_W0_WIRELESS_CLI_ID                FIELD32(0x000000ff)
+#define RXWI_W0_KEY_INDEX              FIELD32(0x00000300)
+#define RXWI_W0_BSSID                  FIELD32(0x00001c00)
+#define RXWI_W0_UDF                    FIELD32(0x0000e000)
+#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT  FIELD32(0x0fff0000)
+#define RXWI_W0_TID                    FIELD32(0xf0000000)
+
+/*
+ * Word1
+ */
+#define RXWI_W1_FRAG                   FIELD32(0x0000000f)
+#define RXWI_W1_SEQUENCE               FIELD32(0x0000fff0)
+#define RXWI_W1_MCS                    FIELD32(0x007f0000)
+#define RXWI_W1_BW                     FIELD32(0x00800000)
+#define RXWI_W1_SHORT_GI               FIELD32(0x01000000)
+#define RXWI_W1_STBC                   FIELD32(0x06000000)
+#define RXWI_W1_PHYMODE                        FIELD32(0xc0000000)
+
+/*
+ * Word2
+ */
+#define RXWI_W2_RSSI0                  FIELD32(0x000000ff)
+#define RXWI_W2_RSSI1                  FIELD32(0x0000ff00)
+#define RXWI_W2_RSSI2                  FIELD32(0x00ff0000)
+
+/*
+ * Word3
+ */
+#define RXWI_W3_SNR0                   FIELD32(0x000000ff)
+#define RXWI_W3_SNR1                   FIELD32(0x0000ff00)
+
 /*
  * RX descriptor format for RX Ring.
  */
  * AMSDU: rx with 802.3 header, not 802.11 header.
  */
 
-#define RXINFO_W0_BA                   FIELD32(0x00000001)
-#define RXINFO_W0_DATA                 FIELD32(0x00000002)
-#define RXINFO_W0_NULLDATA             FIELD32(0x00000004)
-#define RXINFO_W0_FRAG                 FIELD32(0x00000008)
-#define RXINFO_W0_UNICAST_TO_ME                FIELD32(0x00000010)
-#define RXINFO_W0_MULTICAST            FIELD32(0x00000020)
-#define RXINFO_W0_BROADCAST            FIELD32(0x00000040)
-#define RXINFO_W0_MY_BSS               FIELD32(0x00000080)
-#define RXINFO_W0_CRC_ERROR            FIELD32(0x00000100)
-#define RXINFO_W0_CIPHER_ERROR         FIELD32(0x00000600)
-#define RXINFO_W0_AMSDU                        FIELD32(0x00000800)
-#define RXINFO_W0_HTC                  FIELD32(0x00001000)
-#define RXINFO_W0_RSSI                 FIELD32(0x00002000)
-#define RXINFO_W0_L2PAD                        FIELD32(0x00004000)
-#define RXINFO_W0_AMPDU                        FIELD32(0x00008000)
-#define RXINFO_W0_DECRYPTED            FIELD32(0x00010000)
-#define RXINFO_W0_PLCP_RSSI            FIELD32(0x00020000)
-#define RXINFO_W0_CIPHER_ALG           FIELD32(0x00040000)
-#define RXINFO_W0_LAST_AMSDU           FIELD32(0x00080000)
-#define RXINFO_W0_PLCP_SIGNAL          FIELD32(0xfff00000)
+#define RXD_W0_BA                      FIELD32(0x00000001)
+#define RXD_W0_DATA                    FIELD32(0x00000002)
+#define RXD_W0_NULLDATA                        FIELD32(0x00000004)
+#define RXD_W0_FRAG                    FIELD32(0x00000008)
+#define RXD_W0_UNICAST_TO_ME           FIELD32(0x00000010)
+#define RXD_W0_MULTICAST               FIELD32(0x00000020)
+#define RXD_W0_BROADCAST               FIELD32(0x00000040)
+#define RXD_W0_MY_BSS                  FIELD32(0x00000080)
+#define RXD_W0_CRC_ERROR               FIELD32(0x00000100)
+#define RXD_W0_CIPHER_ERROR            FIELD32(0x00000600)
+#define RXD_W0_AMSDU                   FIELD32(0x00000800)
+#define RXD_W0_HTC                     FIELD32(0x00001000)
+#define RXD_W0_RSSI                    FIELD32(0x00002000)
+#define RXD_W0_L2PAD                   FIELD32(0x00004000)
+#define RXD_W0_AMPDU                   FIELD32(0x00008000)
+#define RXD_W0_DECRYPTED               FIELD32(0x00010000)
+#define RXD_W0_PLCP_RSSI               FIELD32(0x00020000)
+#define RXD_W0_CIPHER_ALG              FIELD32(0x00040000)
+#define RXD_W0_LAST_AMSDU              FIELD32(0x00080000)
+#define RXD_W0_PLCP_SIGNAL             FIELD32(0xfff00000)
 
 #endif /* RT2800USB_H */
index dcfc8c25d1a787491b749ba0b3ca5f770066b981..096da85a66fa51a4631731d08d7a65a7236ca811 100644 (file)
 #define GET_DURATION(__size, __rate)   (((__size) * 8 * 10) / (__rate))
 #define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate))
 
+/*
+ * Determine the number of L2 padding bytes required between the header and
+ * the payload.
+ */
+#define L2PAD_SIZE(__hdrlen)   (-(__hdrlen) & 3)
+
 /*
  * Determine the alignment requirement,
  * to make sure the 802.11 payload is padded to a 4-byte boundrary
@@ -937,25 +943,25 @@ static inline void rt2x00_print_chip(struct rt2x00_dev *rt2x00dev)
             rt2x00dev->chip.rt, rt2x00dev->chip.rf, rt2x00dev->chip.rev);
 }
 
-static inline char rt2x00_rt(const struct rt2x00_chip *chipset, const u16 chip)
+static inline char rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt)
 {
-       return (chipset->rt == chip);
+       return (rt2x00dev->chip.rt == rt);
 }
 
-static inline char rt2x00_rf(const struct rt2x00_chip *chipset, const u16 chip)
+static inline char rt2x00_rf(struct rt2x00_dev *rt2x00dev, const u16 rf)
 {
-       return (chipset->rf == chip);
+       return (rt2x00dev->chip.rf == rf);
 }
 
-static inline u32 rt2x00_rev(const struct rt2x00_chip *chipset)
+static inline u32 rt2x00_rev(struct rt2x00_dev *rt2x00dev)
 {
-       return chipset->rev;
+       return rt2x00dev->chip.rev;
 }
 
-static inline bool rt2x00_check_rev(const struct rt2x00_chip *chipset,
+static inline bool rt2x00_check_rev(struct rt2x00_dev *rt2x00dev,
                                    const u32 mask, const u32 rev)
 {
-       return ((chipset->rev & mask) == rev);
+       return ((rt2x00dev->chip.rev & mask) == rev);
 }
 
 static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev,
@@ -964,20 +970,20 @@ static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev,
        rt2x00dev->chip.intf = intf;
 }
 
-static inline bool rt2x00_intf(const struct rt2x00_chip *chipset,
+static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev,
                               enum rt2x00_chip_intf intf)
 {
-       return (chipset->intf == intf);
+       return (rt2x00dev->chip.intf == intf);
 }
 
 static inline bool rt2x00_intf_is_pci(struct rt2x00_dev *rt2x00dev)
 {
-       return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_PCI);
+       return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI);
 }
 
 static inline bool rt2x00_intf_is_usb(struct rt2x00_dev *rt2x00dev)
 {
-       return rt2x00_intf(&rt2x00dev->chip, RT2X00_CHIP_INTF_USB);
+       return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB);
 }
 
 /**
@@ -1019,9 +1025,9 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
 int rt2x00mac_start(struct ieee80211_hw *hw);
 void rt2x00mac_stop(struct ieee80211_hw *hw);
 int rt2x00mac_add_interface(struct ieee80211_hw *hw,
-                           struct ieee80211_if_init_conf *conf);
+                           struct ieee80211_vif *vif);
 void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
-                               struct ieee80211_if_init_conf *conf);
+                               struct ieee80211_vif *vif);
 int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed);
 void rt2x00mac_configure_filter(struct ieee80211_hw *hw,
                                unsigned int changed_flags,
index 265e66dba552c2e3d0188b6baad46c0a32a4d6fb..b93731b79903308d06031326061fde8cde71ac8d 100644 (file)
@@ -385,9 +385,6 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
        memset(&rxdesc, 0, sizeof(rxdesc));
        rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc);
 
-       /* Trim buffer to correct size */
-       skb_trim(entry->skb, rxdesc.size);
-
        /*
         * The data behind the ieee80211 header must be
         * aligned on a 4 byte boundary.
@@ -404,11 +401,16 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
            (rxdesc.flags & RX_FLAG_IV_STRIPPED))
                rt2x00crypto_rx_insert_iv(entry->skb, header_length,
                                          &rxdesc);
-       else if (rxdesc.dev_flags & RXDONE_L2PAD)
+       else if (header_length &&
+                (rxdesc.size > header_length) &&
+                (rxdesc.dev_flags & RXDONE_L2PAD))
                rt2x00queue_remove_l2pad(entry->skb, header_length);
        else
                rt2x00queue_align_payload(entry->skb, header_length);
 
+       /* Trim buffer to correct size */
+       skb_trim(entry->skb, rxdesc.size);
+
        /*
         * Check if the frame was received using HT. In that case,
         * the rate is the MCS index and should be passed to mac80211
index de549c244ed821a2ddbd3eefa070a21d60b3ee0f..00f1f939f1bbf4589dfe857cc7e552a3512ab6ed 100644 (file)
@@ -187,10 +187,10 @@ void rt2x00mac_stop(struct ieee80211_hw *hw)
 EXPORT_SYMBOL_GPL(rt2x00mac_stop);
 
 int rt2x00mac_add_interface(struct ieee80211_hw *hw,
-                           struct ieee80211_if_init_conf *conf)
+                           struct ieee80211_vif *vif)
 {
        struct rt2x00_dev *rt2x00dev = hw->priv;
-       struct rt2x00_intf *intf = vif_to_intf(conf->vif);
+       struct rt2x00_intf *intf = vif_to_intf(vif);
        struct data_queue *queue = rt2x00queue_get_queue(rt2x00dev, QID_BEACON);
        struct queue_entry *entry = NULL;
        unsigned int i;
@@ -203,7 +203,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
            !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags))
                return -ENODEV;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_AP:
                /*
                 * We don't support mixed combinations of
@@ -263,7 +263,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
         * increase interface count and start initialization.
         */
 
-       if (conf->type == NL80211_IFTYPE_AP)
+       if (vif->type == NL80211_IFTYPE_AP)
                rt2x00dev->intf_ap_count++;
        else
                rt2x00dev->intf_sta_count++;
@@ -273,16 +273,16 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
        mutex_init(&intf->beacon_skb_mutex);
        intf->beacon = entry;
 
-       if (conf->type == NL80211_IFTYPE_AP)
-               memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN);
-       memcpy(&intf->mac, conf->mac_addr, ETH_ALEN);
+       if (vif->type == NL80211_IFTYPE_AP)
+               memcpy(&intf->bssid, vif->addr, ETH_ALEN);
+       memcpy(&intf->mac, vif->addr, ETH_ALEN);
 
        /*
         * The MAC adddress must be configured after the device
         * has been initialized. Otherwise the device can reset
         * the MAC registers.
         */
-       rt2x00lib_config_intf(rt2x00dev, intf, conf->type, intf->mac, NULL);
+       rt2x00lib_config_intf(rt2x00dev, intf, vif->type, intf->mac, NULL);
 
        /*
         * Some filters depend on the current working mode. We can force
@@ -296,10 +296,10 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
 EXPORT_SYMBOL_GPL(rt2x00mac_add_interface);
 
 void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
-                               struct ieee80211_if_init_conf *conf)
+                               struct ieee80211_vif *vif)
 {
        struct rt2x00_dev *rt2x00dev = hw->priv;
-       struct rt2x00_intf *intf = vif_to_intf(conf->vif);
+       struct rt2x00_intf *intf = vif_to_intf(vif);
 
        /*
         * Don't allow interfaces to be remove while
@@ -307,11 +307,11 @@ void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
         * no interface is present.
         */
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
-           (conf->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) ||
-           (conf->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count))
+           (vif->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) ||
+           (vif->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count))
                return;
 
-       if (conf->type == NL80211_IFTYPE_AP)
+       if (vif->type == NL80211_IFTYPE_AP)
                rt2x00dev->intf_ap_count--;
        else
                rt2x00dev->intf_sta_count--;
index 0feb4d0e4668910905cdd44f72eb3232e349c4f0..801be436cf1d1bf8f01f16bfd33c84a1ca72641e 100644 (file)
@@ -41,6 +41,9 @@ int rt2x00pci_regbusy_read(struct rt2x00_dev *rt2x00dev,
 {
        unsigned int i;
 
+       if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
+               return 0;
+
        for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
                rt2x00pci_register_read(rt2x00dev, offset, reg);
                if (!rt2x00_get_field32(*reg, field))
index 9915a09141ef294ea94bf70d1afea8ac2ae9c5cc..0b4801a14601fa85b9878a6b4d8d32983411ae43 100644 (file)
@@ -177,55 +177,45 @@ void rt2x00queue_align_payload(struct sk_buff *skb, unsigned int header_length)
 
 void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
 {
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
-       unsigned int frame_length = skb->len;
+       unsigned int payload_length = skb->len - header_length;
        unsigned int header_align = ALIGN_SIZE(skb, 0);
        unsigned int payload_align = ALIGN_SIZE(skb, header_length);
-       unsigned int l2pad = 4 - (payload_align - header_align);
+       unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
 
-       if (header_align == payload_align) {
-               /*
-                * Both header and payload must be moved the same
-                * amount of bytes to align them properly. This means
-                * we don't use the L2 padding but just move the entire
-                * frame.
-                */
-               rt2x00queue_align_frame(skb);
-       } else if (!payload_align) {
-               /*
-                * Simple L2 padding, only the header needs to be moved,
-                * the payload is already properly aligned.
-                */
-               skb_push(skb, header_align);
-               memmove(skb->data, skb->data + header_align, frame_length);
-               skbdesc->flags |= SKBDESC_L2_PADDED;
-       } else {
-               /*
-                *
-                * Complicated L2 padding, both header and payload need
-                * to be moved. By default we only move to the start
-                * of the buffer, so our header alignment needs to be
-                * increased if there is not enough room for the header
-                * to be moved.
-                */
-               if (payload_align > header_align)
-                       header_align += 4;
+       /*
+        * Adjust the header alignment if the payload needs to be moved more
+        * than the header.
+        */
+       if (payload_align > header_align)
+               header_align += 4;
+
+       /* There is nothing to do if no alignment is needed */
+       if (!header_align)
+               return;
+
+       /* Reserve the amount of space needed in front of the frame */
+       skb_push(skb, header_align);
+
+       /*
+        * Move the header.
+        */
+       memmove(skb->data, skb->data + header_align, header_length);
 
-               skb_push(skb, header_align);
-               memmove(skb->data, skb->data + header_align, header_length);
+       /* Move the payload, if present and if required */
+       if (payload_length && payload_align)
                memmove(skb->data + header_length + l2pad,
                        skb->data + header_length + l2pad + payload_align,
-                       frame_length - header_length);
-               skbdesc->flags |= SKBDESC_L2_PADDED;
-       }
+                       payload_length);
+
+       /* Trim the skb to the correct size */
+       skb_trim(skb, header_length + l2pad + payload_length);
 }
 
 void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
 {
-       struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
-       unsigned int l2pad = 4 - (header_length & 3);
+       unsigned int l2pad = L2PAD_SIZE(header_length);
 
-       if (!l2pad || (skbdesc->flags & SKBDESC_L2_PADDED))
+       if (!l2pad)
                return;
 
        memmove(skb->data + l2pad, skb->data, header_length);
@@ -346,7 +336,9 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
         * Header and alignment information.
         */
        txdesc->header_length = ieee80211_get_hdrlen_from_skb(entry->skb);
-       txdesc->l2pad = ALIGN_SIZE(entry->skb, txdesc->header_length);
+       if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags) &&
+           (entry->skb->len > txdesc->header_length))
+               txdesc->l2pad = L2PAD_SIZE(txdesc->header_length);
 
        /*
         * Check whether this frame is to be acked.
@@ -387,10 +379,13 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
 
        /*
         * Beacons and probe responses require the tsf timestamp
-        * to be inserted into the frame.
+        * to be inserted into the frame, except for a frame that has been injected
+        * through a monitor interface. This latter is needed for testing a
+        * monitor interface.
         */
-       if (ieee80211_is_beacon(hdr->frame_control) ||
-           ieee80211_is_probe_resp(hdr->frame_control))
+       if ((ieee80211_is_beacon(hdr->frame_control) ||
+           ieee80211_is_probe_resp(hdr->frame_control)) &&
+           (!(tx_info->flags & IEEE80211_TX_CTL_INJECTED)))
                __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags);
 
        /*
index 70775e5ba1ac65ca13807a5f8428e231b27cebb2..c1e482bb37b36f18544d27eeaef8e41b6e1852be 100644 (file)
@@ -92,8 +92,6 @@ enum data_queue_qid {
  * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX
  * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by
  *     mac80211 but was stripped for processing by the driver.
- * @SKBDESC_L2_PADDED: Payload has been padded for 4-byte alignment,
- *     the padded bytes are located between header and payload.
  * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211,
  *     don't try to pass it back.
  */
@@ -101,8 +99,7 @@ enum skb_frame_desc_flags {
        SKBDESC_DMA_MAPPED_RX = 1 << 0,
        SKBDESC_DMA_MAPPED_TX = 1 << 1,
        SKBDESC_IV_STRIPPED = 1 << 2,
-       SKBDESC_L2_PADDED = 1 << 3,
-       SKBDESC_NOT_MAC80211 = 1 << 4,
+       SKBDESC_NOT_MAC80211 = 1 << 3,
 };
 
 /**
index 0ca589306d7172eec437fb5dfcfd088ee0351ff0..c353b497a65d1e8ea85a9188bd396864b29abb65 100644 (file)
@@ -637,8 +637,7 @@ static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
        rt61pci_bbp_read(rt2x00dev, 4, &r4);
        rt61pci_bbp_read(rt2x00dev, 77, &r77);
 
-       rt2x00_set_field8(&r3, BBP_R3_SMART_MODE,
-                         rt2x00_rf(&rt2x00dev->chip, RF5325));
+       rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF5325));
 
        /*
         * Configure the RX antenna.
@@ -684,8 +683,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
        rt61pci_bbp_read(rt2x00dev, 4, &r4);
        rt61pci_bbp_read(rt2x00dev, 77, &r77);
 
-       rt2x00_set_field8(&r3, BBP_R3_SMART_MODE,
-                         rt2x00_rf(&rt2x00dev->chip, RF2529));
+       rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529));
        rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
                          !test_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags));
 
@@ -833,12 +831,11 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
 
        rt2x00pci_register_write(rt2x00dev, PHY_CSR0, reg);
 
-       if (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
-           rt2x00_rf(&rt2x00dev->chip, RF5325))
+       if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325))
                rt61pci_config_antenna_5x(rt2x00dev, ant);
-       else if (rt2x00_rf(&rt2x00dev->chip, RF2527))
+       else if (rt2x00_rf(rt2x00dev, RF2527))
                rt61pci_config_antenna_2x(rt2x00dev, ant);
-       else if (rt2x00_rf(&rt2x00dev->chip, RF2529)) {
+       else if (rt2x00_rf(rt2x00dev, RF2529)) {
                if (test_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags))
                        rt61pci_config_antenna_2x(rt2x00dev, ant);
                else
@@ -879,8 +876,7 @@ static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
        rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
 
-       smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) ||
-                 rt2x00_rf(&rt2x00dev->chip, RF2527));
+       smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527));
 
        rt61pci_bbp_read(rt2x00dev, 3, &r3);
        rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart);
@@ -2302,10 +2298,10 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_chip_rf(rt2x00dev, value, reg);
        rt2x00_print_chip(rt2x00dev);
 
-       if (!rt2x00_rf(&rt2x00dev->chip, RF5225) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF5325) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2527) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2529)) {
+       if (!rt2x00_rf(rt2x00dev, RF5225) &&
+           !rt2x00_rf(rt2x00dev, RF5325) &&
+           !rt2x00_rf(rt2x00dev, RF2527) &&
+           !rt2x00_rf(rt2x00dev, RF2529)) {
                ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
                return -ENODEV;
        }
@@ -2360,7 +2356,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
         * the antenna settings should be gathered from the NIC
         * eeprom word.
         */
-       if (rt2x00_rf(&rt2x00dev->chip, RF2529) &&
+       if (rt2x00_rf(rt2x00dev, RF2529) &&
            !test_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags)) {
                rt2x00dev->default_ant.rx =
                    ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED);
@@ -2571,8 +2567,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                spec->channels = rf_vals_seq;
        }
 
-       if (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
-           rt2x00_rf(&rt2x00dev->chip, RF5325)) {
+       if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_seq);
        }
index ced3b6ab5e16f89ba4c794d2e131ef55929f362c..a02691294395e1163484b652f1ac0a137af5131c 100644 (file)
@@ -136,8 +136,8 @@ static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev,
                 * all others contain 20 bits.
                 */
                rt2x00_set_field32(&reg, PHY_CSR4_NUMBER_OF_BITS,
-                                  20 + (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
-                                        rt2x00_rf(&rt2x00dev->chip, RF2527)));
+                                  20 + (rt2x00_rf(rt2x00dev, RF5225) ||
+                                        rt2x00_rf(rt2x00dev, RF2527)));
                rt2x00_set_field32(&reg, PHY_CSR4_IF_SELECT, 0);
                rt2x00_set_field32(&reg, PHY_CSR4_BUSY, 1);
 
@@ -741,11 +741,9 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev,
 
        rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg);
 
-       if (rt2x00_rf(&rt2x00dev->chip, RF5226) ||
-           rt2x00_rf(&rt2x00dev->chip, RF5225))
+       if (rt2x00_rf(rt2x00dev, RF5226) || rt2x00_rf(rt2x00dev, RF5225))
                rt73usb_config_antenna_5x(rt2x00dev, ant);
-       else if (rt2x00_rf(&rt2x00dev->chip, RF2528) ||
-                rt2x00_rf(&rt2x00dev->chip, RF2527))
+       else if (rt2x00_rf(rt2x00dev, RF2528) || rt2x00_rf(rt2x00dev, RF2527))
                rt73usb_config_antenna_2x(rt2x00dev, ant);
 }
 
@@ -779,8 +777,7 @@ static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev,
        rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower));
        rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset);
 
-       smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) ||
-                 rt2x00_rf(&rt2x00dev->chip, RF2527));
+       smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527));
 
        rt73usb_bbp_read(rt2x00dev, 3, &r3);
        rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart);
@@ -1210,8 +1207,7 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev)
        rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000);
 
        reg = 0x000023b0;
-       if (rt2x00_rf(&rt2x00dev->chip, RF5225) ||
-           rt2x00_rf(&rt2x00dev->chip, RF2527))
+       if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527))
                rt2x00_set_field32(&reg, PHY_CSR1_RF_RPI, 1);
        rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg);
 
@@ -1827,16 +1823,16 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
        rt2x00_set_chip(rt2x00dev, RT2571, value, reg);
        rt2x00_print_chip(rt2x00dev);
 
-       if (!rt2x00_check_rev(&rt2x00dev->chip, 0x000ffff0, 0x25730) ||
-           rt2x00_check_rev(&rt2x00dev->chip, 0x0000000f, 0)) {
+       if (!rt2x00_check_rev(rt2x00dev, 0x000ffff0, 0x25730) ||
+           rt2x00_check_rev(rt2x00dev, 0x0000000f, 0)) {
                ERROR(rt2x00dev, "Invalid RT chipset detected.\n");
                return -ENODEV;
        }
 
-       if (!rt2x00_rf(&rt2x00dev->chip, RF5226) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2528) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF5225) &&
-           !rt2x00_rf(&rt2x00dev->chip, RF2527)) {
+       if (!rt2x00_rf(rt2x00dev, RF5226) &&
+           !rt2x00_rf(rt2x00dev, RF2528) &&
+           !rt2x00_rf(rt2x00dev, RF5225) &&
+           !rt2x00_rf(rt2x00dev, RF2527)) {
                ERROR(rt2x00dev, "Invalid RF chipset detected.\n");
                return -ENODEV;
        }
@@ -2081,17 +2077,17 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        spec->supported_bands = SUPPORT_BAND_2GHZ;
        spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
 
-       if (rt2x00_rf(&rt2x00dev->chip, RF2528)) {
+       if (rt2x00_rf(rt2x00dev, RF2528)) {
                spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528);
                spec->channels = rf_vals_bg_2528;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF5226)) {
+       } else if (rt2x00_rf(rt2x00dev, RF5226)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_5226);
                spec->channels = rf_vals_5226;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF2527)) {
+       } else if (rt2x00_rf(rt2x00dev, RF2527)) {
                spec->num_channels = 14;
                spec->channels = rf_vals_5225_2527;
-       } else if (rt2x00_rf(&rt2x00dev->chip, RF5225)) {
+       } else if (rt2x00_rf(rt2x00dev, RF5225)) {
                spec->supported_bands |= SUPPORT_BAND_5GHZ;
                spec->num_channels = ARRAY_SIZE(rf_vals_5225_2527);
                spec->channels = rf_vals_5225_2527;
@@ -2354,6 +2350,7 @@ static struct usb_device_id rt73usb_device_table[] = {
        { USB_DEVICE(0x08dd, 0x0120), USB_DEVICE_DATA(&rt73usb_ops) },
        /* Buffalo */
        { USB_DEVICE(0x0411, 0x00d8), USB_DEVICE_DATA(&rt73usb_ops) },
+       { USB_DEVICE(0x0411, 0x00d9), USB_DEVICE_DATA(&rt73usb_ops) },
        { USB_DEVICE(0x0411, 0x00f4), USB_DEVICE_DATA(&rt73usb_ops) },
        { USB_DEVICE(0x0411, 0x0116), USB_DEVICE_DATA(&rt73usb_ops) },
        { USB_DEVICE(0x0411, 0x0119), USB_DEVICE_DATA(&rt73usb_ops) },
index 8721282a8185d05d7db2be8cf7301467cdd26a80..de3844fe06d84bfecde1cbb2d4db3e8c7b5dca94 100644 (file)
@@ -60,7 +60,6 @@ struct rtl8180_priv {
        struct rtl818x_csr __iomem *map;
        const struct rtl818x_rf_ops *rf;
        struct ieee80211_vif *vif;
-       int mode;
 
        /* rtl8180 driver specific */
        spinlock_t lock;
index 8a40a14399842c46b4dc9b845db79ce1141f1718..5a2b7199f5d5ae188e9b8caf3430f58243bb85dd 100644 (file)
@@ -82,8 +82,6 @@ static const struct ieee80211_channel rtl818x_channels[] = {
 };
 
 
-
-
 void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data)
 {
        struct rtl8180_priv *priv = dev->priv;
@@ -615,7 +613,6 @@ static int rtl8180_start(struct ieee80211_hw *dev)
        reg |= RTL818X_CMD_TX_ENABLE;
        rtl818x_iowrite8(priv, &priv->map->CMD, reg);
 
-       priv->mode = NL80211_IFTYPE_MONITOR;
        return 0;
 
  err_free_rings:
@@ -633,8 +630,6 @@ static void rtl8180_stop(struct ieee80211_hw *dev)
        u8 reg;
        int i;
 
-       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
-
        rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
 
        reg = rtl818x_ioread8(priv, &priv->map->CMD);
@@ -657,38 +652,39 @@ static void rtl8180_stop(struct ieee80211_hw *dev)
 }
 
 static int rtl8180_add_interface(struct ieee80211_hw *dev,
-                                struct ieee80211_if_init_conf *conf)
+                                struct ieee80211_vif *vif)
 {
        struct rtl8180_priv *priv = dev->priv;
 
-       if (priv->mode != NL80211_IFTYPE_MONITOR)
-               return -EOPNOTSUPP;
+       /*
+        * We only support one active interface at a time.
+        */
+       if (priv->vif)
+               return -EBUSY;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               priv->mode = conf->type;
                break;
        default:
                return -EOPNOTSUPP;
        }
 
-       priv->vif = conf->vif;
+       priv->vif = vif;
 
        rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
        rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0],
-                         le32_to_cpu(*(__le32 *)conf->mac_addr));
+                         le32_to_cpu(*(__le32 *)vif->addr));
        rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4],
-                         le16_to_cpu(*(__le16 *)(conf->mac_addr + 4)));
+                         le16_to_cpu(*(__le16 *)(vif->addr + 4)));
        rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
 
        return 0;
 }
 
 static void rtl8180_remove_interface(struct ieee80211_hw *dev,
-                                    struct ieee80211_if_init_conf *conf)
+                                    struct ieee80211_vif *vif)
 {
        struct rtl8180_priv *priv = dev->priv;
-       priv->mode = NL80211_IFTYPE_MONITOR;
        priv->vif = NULL;
 }
 
index 6af0f3f71f3a4b6f01789da274a31fdbcba00897..6bb32112e65c16b8b3255b4cd45eb531ea0f4672 100644 (file)
@@ -92,7 +92,7 @@ struct rtl8187_priv {
        struct rtl818x_csr *map;
        const struct rtl818x_rf_ops *rf;
        struct ieee80211_vif *vif;
-       int mode;
+
        /* The mutex protects the TX loopback state.
         * Any attempt to set channels concurrently locks the device.
         */
index bc5726dd5fe49f5fc7582f4a94d6bce4402de7af..f336c63053c1a052bd31a146ef54cfc643a71812 100644 (file)
@@ -1018,31 +1018,30 @@ static void rtl8187_stop(struct ieee80211_hw *dev)
 }
 
 static int rtl8187_add_interface(struct ieee80211_hw *dev,
-                                struct ieee80211_if_init_conf *conf)
+                                struct ieee80211_vif *vif)
 {
        struct rtl8187_priv *priv = dev->priv;
        int i;
        int ret = -EOPNOTSUPP;
 
        mutex_lock(&priv->conf_mutex);
-       if (priv->mode != NL80211_IFTYPE_MONITOR)
+       if (priv->vif)
                goto exit;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               priv->mode = conf->type;
                break;
        default:
                goto exit;
        }
 
        ret = 0;
-       priv->vif = conf->vif;
+       priv->vif = vif;
 
        rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
        for (i = 0; i < ETH_ALEN; i++)
                rtl818x_iowrite8(priv, &priv->map->MAC[i],
-                                ((u8 *)conf->mac_addr)[i]);
+                                ((u8 *)vif->addr)[i]);
        rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
 
 exit:
@@ -1051,11 +1050,10 @@ exit:
 }
 
 static void rtl8187_remove_interface(struct ieee80211_hw *dev,
-                                    struct ieee80211_if_init_conf *conf)
+                                    struct ieee80211_vif *vif)
 {
        struct rtl8187_priv *priv = dev->priv;
        mutex_lock(&priv->conf_mutex);
-       priv->mode = NL80211_IFTYPE_MONITOR;
        priv->vif = NULL;
        mutex_unlock(&priv->conf_mutex);
 }
@@ -1365,7 +1363,6 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
        dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
 
 
-       priv->mode = NL80211_IFTYPE_MONITOR;
        dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                     IEEE80211_HW_SIGNAL_DBM |
                     IEEE80211_HW_RX_INCLUDES_FCS;
index ded44c045eb24307a6377e18eec161a27247d922..f82aa8b4bdde588f95ff0ba812f7eed1f590dddc 100644 (file)
@@ -33,7 +33,7 @@ static void led_turn_on(struct work_struct *work)
        struct rtl8187_led *led = &priv->led_tx;
 
        /* Don't change the LED, when the device is down. */
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+       if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED)
                return ;
 
        /* Skip if the LED is not registered. */
@@ -71,7 +71,7 @@ static void led_turn_off(struct work_struct *work)
        struct rtl8187_led *led = &priv->led_tx;
 
        /* Don't change the LED, when the device is down. */
-       if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+       if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED)
                return ;
 
        /* Skip if the LED is not registered. */
index 054533f7a12432554b424156ad4d2a436badca1f..6301578d15654e8cdccde6f3473fe7ff2417daf9 100644 (file)
@@ -247,6 +247,7 @@ struct wl1251_debugfs {
        struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data;
 
        struct dentry *tx_queue_len;
+       struct dentry *tx_queue_status;
 
        struct dentry *retry_count;
        struct dentry *excessive_retries;
index acfa086dbfc5787d673b6e75ac2883dada80fc63..beff084040b569499efae9dc637a15bb25be3c34 100644 (file)
@@ -976,3 +976,72 @@ out:
        kfree(acx);
        return ret;
 }
+
+int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max,
+                     u8 aifs, u16 txop)
+{
+       struct wl1251_acx_ac_cfg *acx;
+       int ret = 0;
+
+       wl1251_debug(DEBUG_ACX, "acx ac cfg %d cw_ming %d cw_max %d "
+                    "aifs %d txop %d", ac, cw_min, cw_max, aifs, txop);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->ac = ac;
+       acx->cw_min = cw_min;
+       acx->cw_max = cw_max;
+       acx->aifsn = aifs;
+       acx->txop_limit = txop;
+
+       ret = wl1251_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1251_warning("acx ac cfg failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
+
+int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue,
+                      enum wl1251_acx_channel_type type,
+                      u8 tsid, enum wl1251_acx_ps_scheme ps_scheme,
+                      enum wl1251_acx_ack_policy ack_policy)
+{
+       struct wl1251_acx_tid_cfg *acx;
+       int ret = 0;
+
+       wl1251_debug(DEBUG_ACX, "acx tid cfg %d type %d tsid %d "
+                    "ps_scheme %d ack_policy %d", queue, type, tsid,
+                    ps_scheme, ack_policy);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->queue = queue;
+       acx->type = type;
+       acx->tsid = tsid;
+       acx->ps_scheme = ps_scheme;
+       acx->ack_policy = ack_policy;
+
+       ret = wl1251_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1251_warning("acx tid cfg failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
index 652371432cd81c109bc8ac48d3066c8eed81101b..26160c45784cbdae95eb253c1776b2dd796ca75c 100644 (file)
@@ -1166,6 +1166,87 @@ struct wl1251_acx_wr_tbtt_and_dtim {
        u8  padding;
 } __attribute__ ((packed));
 
+struct wl1251_acx_ac_cfg {
+       struct acx_header header;
+
+       /*
+        * Access Category - The TX queue's access category
+        * (refer to AccessCategory_enum)
+        */
+       u8 ac;
+
+       /*
+        * The contention window minimum size (in slots) for
+        * the access class.
+        */
+       u8 cw_min;
+
+       /*
+        * The contention window maximum size (in slots) for
+        * the access class.
+        */
+       u16 cw_max;
+
+       /* The AIF value (in slots) for the access class. */
+       u8 aifsn;
+
+       u8 reserved;
+
+       /* The TX Op Limit (in microseconds) for the access class. */
+       u16 txop_limit;
+} __attribute__ ((packed));
+
+
+enum wl1251_acx_channel_type {
+       CHANNEL_TYPE_DCF        = 0,
+       CHANNEL_TYPE_EDCF       = 1,
+       CHANNEL_TYPE_HCCA       = 2,
+};
+
+enum wl1251_acx_ps_scheme {
+       /* regular ps: simple sending of packets */
+       WL1251_ACX_PS_SCHEME_LEGACY     = 0,
+
+       /* sending a packet triggers a unscheduled apsd downstream */
+       WL1251_ACX_PS_SCHEME_UPSD_TRIGGER       = 1,
+
+       /* a pspoll packet will be sent before every data packet */
+       WL1251_ACX_PS_SCHEME_LEGACY_PSPOLL      = 2,
+
+       /* scheduled apsd mode */
+       WL1251_ACX_PS_SCHEME_SAPSD              = 3,
+};
+
+enum wl1251_acx_ack_policy {
+       WL1251_ACX_ACK_POLICY_LEGACY    = 0,
+       WL1251_ACX_ACK_POLICY_NO_ACK    = 1,
+       WL1251_ACX_ACK_POLICY_BLOCK     = 2,
+};
+
+struct wl1251_acx_tid_cfg {
+       struct acx_header header;
+
+       /* tx queue id number (0-7) */
+       u8 queue;
+
+       /* channel access type for the queue, enum wl1251_acx_channel_type */
+       u8 type;
+
+       /* EDCA: ac index (0-3), HCCA: traffic stream id (8-15) */
+       u8 tsid;
+
+       /* ps scheme of the specified queue, enum wl1251_acx_ps_scheme */
+       u8 ps_scheme;
+
+       /* the tx queue ack policy, enum wl1251_acx_ack_policy */
+       u8 ack_policy;
+
+       u8 padding[3];
+
+       /* not supported */
+       u32 apsdconf[2];
+} __attribute__ ((packed));
+
 /*************************************************************************
 
     Host Interrupt Register (WiLink -> Host)
@@ -1322,5 +1403,11 @@ int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime);
 int wl1251_acx_rate_policies(struct wl1251 *wl);
 int wl1251_acx_mem_cfg(struct wl1251 *wl);
 int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim);
+int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max,
+                     u8 aifs, u16 txop);
+int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue,
+                      enum wl1251_acx_channel_type type,
+                      u8 tsid, enum wl1251_acx_ps_scheme ps_scheme,
+                      enum wl1251_acx_ack_policy ack_policy);
 
 #endif /* __WL1251_ACX_H__ */
index a00723059f83f3ef497d1649e082f36103a6d93e..0ccba57fb9fb9548eaceaf267481af256f9928d3 100644 (file)
@@ -237,6 +237,27 @@ static const struct file_operations tx_queue_len_ops = {
        .open = wl1251_open_file_generic,
 };
 
+static ssize_t tx_queue_status_read(struct file *file, char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       struct wl1251 *wl = file->private_data;
+       char buf[3], status;
+       int len;
+
+       if (wl->tx_queue_stopped)
+               status = 's';
+       else
+               status = 'r';
+
+       len = scnprintf(buf, sizeof(buf), "%c\n", status);
+       return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+
+static const struct file_operations tx_queue_status_ops = {
+       .read = tx_queue_status_read,
+       .open = wl1251_open_file_generic,
+};
+
 static void wl1251_debugfs_delete_files(struct wl1251 *wl)
 {
        DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow);
@@ -331,6 +352,7 @@ static void wl1251_debugfs_delete_files(struct wl1251 *wl)
        DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data);
 
        DEBUGFS_DEL(tx_queue_len);
+       DEBUGFS_DEL(tx_queue_status);
        DEBUGFS_DEL(retry_count);
        DEBUGFS_DEL(excessive_retries);
 }
@@ -431,6 +453,7 @@ static int wl1251_debugfs_add_files(struct wl1251 *wl)
        DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
 
        DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir);
+       DEBUGFS_ADD(tx_queue_status, wl->debugfs.rootdir);
        DEBUGFS_ADD(retry_count, wl->debugfs.rootdir);
        DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir);
 
index 5cb573383eeb4f78f937665adcee44014d38571c..5aad56ea71536fa1dbc5875d601f7bd76eaae961 100644 (file)
@@ -294,6 +294,11 @@ static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl)
                        goto out;
        }
 
+       wl1251_acx_ac_cfg(wl, AC_BE, CWMIN_BE, CWMAX_BE, AIFS_DIFS, TXOP_BE);
+       wl1251_acx_ac_cfg(wl, AC_BK, CWMIN_BK, CWMAX_BK, AIFS_DIFS, TXOP_BK);
+       wl1251_acx_ac_cfg(wl, AC_VI, CWMIN_VI, CWMAX_VI, AIFS_DIFS, TXOP_VI);
+       wl1251_acx_ac_cfg(wl, AC_VO, CWMIN_VO, CWMAX_VO, AIFS_DIFS, TXOP_VO);
+
 out:
        kfree(config);
        return ret;
index b3b25ec885ea7489d19c3f6f3cd2d08d047e7217..269cefb3e7d43f20d96e3ca1902b1388fd5049e3 100644 (file)
 
 #include "wl1251.h"
 
+enum {
+       /* best effort/legacy */
+       AC_BE = 0,
+
+       /* background */
+       AC_BK = 1,
+
+       /* video */
+       AC_VI = 2,
+
+       /* voice */
+       AC_VO = 3,
+
+       /* broadcast dummy access category */
+       AC_BCAST = 4,
+
+       NUM_ACCESS_CATEGORIES = 4
+};
+
+/* following are defult values for the IE fields*/
+#define CWMIN_BK  15
+#define CWMIN_BE  15
+#define CWMIN_VI  7
+#define CWMIN_VO  3
+#define CWMAX_BK  1023
+#define CWMAX_BE  63
+#define CWMAX_VI  15
+#define CWMAX_VO  7
+
+/* slot number setting to start transmission at PIFS interval */
+#define AIFS_PIFS 1
+
+/*
+ * slot number setting to start transmission at DIFS interval - normal DCF
+ * access
+ */
+#define AIFS_DIFS 2
+
+#define AIFSN_BK  7
+#define AIFSN_BE  3
+#define AIFSN_VI  AIFS_PIFS
+#define AIFSN_VO  AIFS_PIFS
+#define TXOP_BK   0
+#define TXOP_BE   0
+#define TXOP_VI   3008
+#define TXOP_VO   1504
+
 int wl1251_hw_init_hwenc_config(struct wl1251 *wl);
 int wl1251_hw_init_templates_config(struct wl1251 *wl);
 int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter);
index 2f50a256efa5a00fa18b45e94ce9be3130f7628c..4e373f3dbc43d9eb55961402abaadbbcbe4d4c3b 100644 (file)
@@ -395,6 +395,7 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * 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);
 
                /*
@@ -510,13 +511,13 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
 }
 
 static int wl1251_op_add_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_if_init_conf *conf)
+                                  struct ieee80211_vif *vif)
 {
        struct wl1251 *wl = hw->priv;
        int ret = 0;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
-                    conf->type, conf->mac_addr);
+                    vif->type, vif->addr);
 
        mutex_lock(&wl->mutex);
        if (wl->vif) {
@@ -524,9 +525,9 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
-       wl->vif = conf->vif;
+       wl->vif = vif;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                wl->bss_type = BSS_TYPE_STA_BSS;
                break;
@@ -538,8 +539,8 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
-       if (memcmp(wl->mac_addr, conf->mac_addr, ETH_ALEN)) {
-               memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
+       if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) {
+               memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
                SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
                ret = wl1251_acx_station_id(wl);
                if (ret < 0)
@@ -552,7 +553,7 @@ out:
 }
 
 static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
-                                        struct ieee80211_if_init_conf *conf)
+                                        struct ieee80211_vif *vif)
 {
        struct wl1251 *wl = hw->priv;
 
@@ -640,20 +641,25 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                 * 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;
        }
@@ -1273,6 +1279,43 @@ static struct ieee80211_channel wl1251_channels[] = {
        { .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 +1336,7 @@ static const struct ieee80211_ops wl1251_ops = {
        .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 +1382,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
        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 9931b197ff77c42c3d6aad6ce6964ff4d16a42b1..851dfb65e474eb80a916a802a42b5e0d38b42a20 100644 (file)
@@ -26,7 +26,8 @@
 #include "wl1251_cmd.h"
 #include "wl1251_io.h"
 
-#define WL1251_WAKEUP_TIMEOUT 2000
+/* in ms */
+#define WL1251_WAKEUP_TIMEOUT 100
 
 void wl1251_elp_work(struct work_struct *work)
 {
@@ -67,7 +68,7 @@ void wl1251_ps_elp_sleep(struct wl1251 *wl)
 
 int wl1251_ps_elp_wakeup(struct wl1251 *wl)
 {
-       unsigned long timeout;
+       unsigned long timeout, start;
        u32 elp_reg;
 
        if (!wl->elp)
@@ -75,6 +76,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
 
        wl1251_debug(DEBUG_PSM, "waking up chip from elp");
 
+       start = jiffies;
        timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT);
 
        wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP);
@@ -95,8 +97,7 @@ int wl1251_ps_elp_wakeup(struct wl1251 *wl)
        }
 
        wl1251_debug(DEBUG_PSM, "wakeup time: %u ms",
-                    jiffies_to_msecs(jiffies) -
-                    (jiffies_to_msecs(timeout) - WL1251_WAKEUP_TIMEOUT));
+                    jiffies_to_msecs(jiffies - start));
 
        wl->elp = false;
 
index f84cc89cbffce2a54c93e37ef709e5aea29e8dab..b56732226cc09943638b976b0e4c95114ad7b15a 100644 (file)
@@ -126,7 +126,7 @@ static void wl1251_rx_body(struct wl1251 *wl,
        if (wl->rx_current_buffer)
                rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size;
 
-       skb = dev_alloc_skb(length);
+       skb = __dev_alloc_skb(length, GFP_KERNEL);
        if (!skb) {
                wl1251_error("Couldn't allocate RX frame");
                return;
index f859706158499703d9492851f33ba97f25f90552..c8223185efd28129325dc46d52760dba7d5d6732 100644 (file)
@@ -167,8 +167,7 @@ static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb,
        tx_hdr->expiry_time = cpu_to_le32(1 << 16);
        tx_hdr->id = id;
 
-       /* FIXME: how to get the correct queue id? */
-       tx_hdr->xmit_queue = 0;
+       tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb));
 
        wl1251_tx_control(tx_hdr, control, fc);
        wl1251_tx_frag_block_num(tx_hdr);
@@ -220,6 +219,7 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb,
                        /* align the buffer on a 4-byte boundary */
                        skb_reserve(skb, offset);
                        memmove(skb->data, src, skb->len);
+                       tx_hdr = (struct tx_double_buffer_desc *) skb->data;
                } else {
                        wl1251_info("No handler, fixme!");
                        return -EINVAL;
@@ -237,8 +237,9 @@ static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb,
 
        wl1251_mem_write(wl, addr, skb->data, len);
 
-       wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x",
-                    tx_hdr->id, skb, tx_hdr->length, tx_hdr->rate);
+       wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x "
+                    "queue %d", tx_hdr->id, skb, tx_hdr->length,
+                    tx_hdr->rate, tx_hdr->xmit_queue);
 
        return 0;
 }
index 7c1c1665c81086f1a44966a55da55d4ddb8897f2..55856c6bb97a85b18f8a65810288f88a61f08ccf 100644 (file)
@@ -26,6 +26,7 @@
 #define __WL1251_TX_H__
 
 #include <linux/bitops.h>
+#include "wl1251_acx.h"
 
 /*
  *
@@ -209,6 +210,22 @@ struct tx_result {
        u8 done_2;
 } __attribute__ ((packed));
 
+static inline int wl1251_tx_get_queue(int queue)
+{
+       switch (queue) {
+       case 0:
+               return QOS_AC_VO;
+       case 1:
+               return QOS_AC_VI;
+       case 2:
+               return QOS_AC_BE;
+       case 3:
+               return QOS_AC_BK;
+       default:
+               return QOS_AC_BE;
+       }
+}
+
 void wl1251_tx_work(struct work_struct *work);
 void wl1251_tx_complete(struct wl1251 *wl);
 void wl1251_tx_flush(struct wl1251 *wl);
index 94359b1a861f2aa39e184590ac87045bcca54bf0..d0938db043b366320bce03cd3b09d47c0ec763e8 100644 (file)
@@ -107,10 +107,9 @@ enum {
                                  CFG_RX_CTL_EN | CFG_RX_BCN_EN |     \
                                  CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)
 
-#define WL1271_DEFAULT_BASIC_RATE_SET (CONF_TX_RATE_MASK_ALL)
-
 #define WL1271_FW_NAME "wl1271-fw.bin"
 #define WL1271_NVS_NAME "wl1271-nvs.bin"
+#define WL1271_NVS_LEN  468
 
 /*
  * Enable/disable 802.11a support for WL1273
@@ -276,6 +275,7 @@ struct wl1271_debugfs {
 
        struct dentry *retry_count;
        struct dentry *excessive_retries;
+       struct dentry *gpio_power;
 };
 
 #define NUM_TX_QUEUES              4
@@ -322,6 +322,17 @@ struct wl1271 {
        enum wl1271_state state;
        struct mutex mutex;
 
+#define WL1271_FLAG_STA_RATES_CHANGED  (0)
+#define WL1271_FLAG_STA_ASSOCIATED     (1)
+#define WL1271_FLAG_JOINED             (2)
+#define WL1271_FLAG_GPIO_POWER         (3)
+#define WL1271_FLAG_TX_QUEUE_STOPPED   (4)
+#define WL1271_FLAG_SCANNING           (5)
+#define WL1271_FLAG_IN_ELP             (6)
+#define WL1271_FLAG_PSM                (7)
+#define WL1271_FLAG_PSM_REQUESTED      (8)
+       unsigned long flags;
+
        struct wl1271_partition_set part;
 
        struct wl1271_chip chip;
@@ -359,7 +370,6 @@ struct wl1271 {
 
        /* Frames scheduled for transmission, not handled yet */
        struct sk_buff_head tx_queue;
-       bool tx_queue_stopped;
 
        struct work_struct tx_work;
 
@@ -387,14 +397,15 @@ struct wl1271 {
        u32 mbox_ptr[2];
 
        /* Are we currently scanning */
-       bool scanning;
        struct wl1271_scan scan;
 
        /* Our association ID */
        u16 aid;
 
        /* currently configured rate set */
+       u32 sta_rate_set;
        u32 basic_rate_set;
+       u32 rate_set;
 
        /* The current band */
        enum ieee80211_band band;
@@ -405,18 +416,9 @@ struct wl1271 {
        unsigned int rx_config;
        unsigned int rx_filter;
 
-       /* is firmware in elp mode */
-       bool elp;
-
        struct completion *elp_compl;
        struct delayed_work elp_work;
 
-       /* we can be in psm, but not in elp, we have to differentiate */
-       bool psm;
-
-       /* PSM mode requested */
-       bool psm_requested;
-
        /* retry counter for PSM entries */
        u8 psm_entry_retry;
 
@@ -435,9 +437,6 @@ struct wl1271 {
 
        struct ieee80211_vif *vif;
 
-       /* Used for a workaround to send disconnect before rejoining */
-       bool joined;
-
        /* Current chipset configuration */
        struct conf_drv_settings conf;
 
@@ -455,7 +454,9 @@ int wl1271_plt_stop(struct wl1271 *wl);
 
 #define WL1271_TX_QUEUE_MAX_LENGTH 20
 
-/* WL1271 needs a 200ms sleep after power on */
+/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power
+   on in case is has been shut down shortly before */
+#define WL1271_PRE_POWER_ON_SLEEP 20 /* in miliseconds */
 #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */
 
 static inline bool wl1271_11a_enabled(void)
index 5cc89bbdac7abe11130df407361e65f20b93c3f3..0b34348434766db6eaa86c6555ae2a8704b376f1 100644 (file)
@@ -390,6 +390,35 @@ out:
        return ret;
 }
 
+int wl1271_acx_dco_itrim_params(struct wl1271 *wl)
+{
+       struct acx_dco_itrim_params *dco;
+       struct conf_itrim_settings *c = &wl->conf.itrim;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx dco itrim parameters");
+
+       dco = kzalloc(sizeof(*dco), GFP_KERNEL);
+       if (!dco) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       dco->enable = c->enable;
+       dco->timeout = cpu_to_le32(c->timeout);
+
+       ret = wl1271_cmd_configure(wl, ACX_SET_DCO_ITRIM_PARAMS,
+                                  dco, sizeof(*dco));
+       if (ret < 0) {
+               wl1271_warning("failed to set dco itrim parameters: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(dco);
+       return ret;
+}
+
 int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
 {
        struct acx_beacon_filter_option *beacon_filter = NULL;
@@ -758,10 +787,11 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
        return 0;
 }
 
-int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates)
+int wl1271_acx_rate_policies(struct wl1271 *wl)
 {
        struct acx_rate_policy *acx;
        struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf;
+       int idx = 0;
        int ret = 0;
 
        wl1271_debug(DEBUG_ACX, "acx rate policies");
@@ -773,12 +803,21 @@ int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates)
                goto out;
        }
 
-       /* configure one default (one-size-fits-all) rate class */
-       acx->rate_class_cnt = cpu_to_le32(1);
-       acx->rate_class[0].enabled_rates = cpu_to_le32(enabled_rates);
-       acx->rate_class[0].short_retry_limit = c->short_retry_limit;
-       acx->rate_class[0].long_retry_limit = c->long_retry_limit;
-       acx->rate_class[0].aflags = c->aflags;
+       /* configure one basic rate class */
+       idx = ACX_TX_BASIC_RATE;
+       acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate_set);
+       acx->rate_class[idx].short_retry_limit = c->short_retry_limit;
+       acx->rate_class[idx].long_retry_limit = c->long_retry_limit;
+       acx->rate_class[idx].aflags = c->aflags;
+
+       /* configure one AP supported rate class */
+       idx = ACX_TX_AP_FULL_RATE;
+       acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->rate_set);
+       acx->rate_class[idx].short_retry_limit = c->short_retry_limit;
+       acx->rate_class[idx].long_retry_limit = c->long_retry_limit;
+       acx->rate_class[idx].aflags = c->aflags;
+
+       acx->rate_class_cnt = cpu_to_le32(ACX_TX_RATE_POLICY_CNT);
 
        ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
        if (ret < 0) {
@@ -1012,59 +1051,6 @@ out:
        return ret;
 }
 
-int wl1271_acx_smart_reflex(struct wl1271 *wl)
-{
-       struct acx_smart_reflex_state *sr_state = NULL;
-       struct acx_smart_reflex_config_params *sr_param = NULL;
-       int i, ret;
-
-       wl1271_debug(DEBUG_ACX, "acx smart reflex");
-
-       sr_param = kzalloc(sizeof(*sr_param), GFP_KERNEL);
-       if (!sr_param) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       for (i = 0; i < CONF_SR_ERR_TBL_COUNT; i++) {
-               struct conf_mart_reflex_err_table *e =
-                       &(wl->conf.init.sr_err_tbl[i]);
-
-               sr_param->error_table[i].len = e->len;
-               sr_param->error_table[i].upper_limit = e->upper_limit;
-               memcpy(sr_param->error_table[i].values, e->values, e->len);
-       }
-
-       ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_PARAMS,
-                                  sr_param, sizeof(*sr_param));
-       if (ret < 0) {
-               wl1271_warning("failed to set smart reflex params: %d", ret);
-               goto out;
-       }
-
-       sr_state = kzalloc(sizeof(*sr_state), GFP_KERNEL);
-       if (!sr_state) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       /* enable smart reflex */
-       sr_state->enable = wl->conf.init.sr_enable;
-
-       ret = wl1271_cmd_configure(wl, ACX_SET_SMART_REFLEX_STATE,
-                                  sr_state, sizeof(*sr_state));
-       if (ret < 0) {
-               wl1271_warning("failed to set smart reflex params: %d", ret);
-               goto out;
-       }
-
-out:
-       kfree(sr_state);
-       kfree(sr_param);
-       return ret;
-
-}
-
 int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
 {
        struct wl1271_acx_bet_enable *acx = NULL;
@@ -1132,3 +1118,31 @@ out:
        kfree(acx);
        return ret;
 }
+
+int wl1271_acx_pm_config(struct wl1271 *wl)
+{
+       struct wl1271_acx_pm_config *acx = NULL;
+       struct  conf_pm_config_settings *c = &wl->conf.pm_config;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_ACX, "acx pm config");
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->host_clk_settling_time = cpu_to_le32(c->host_clk_settling_time);
+       acx->host_fast_wakeup_support = c->host_fast_wakeup_support;
+
+       ret = wl1271_cmd_configure(wl, ACX_PM_CONFIG, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("acx pm config failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
index 2ce0a81285425cd0c451753e0501851c6b51d932..1bb63af64f0e5616bfe02013055249336f10b14b 100644 (file)
@@ -415,23 +415,12 @@ struct acx_bt_wlan_coex {
        u8 pad[3];
 } __attribute__ ((packed));
 
-struct acx_smart_reflex_state {
+struct acx_dco_itrim_params {
        struct acx_header header;
 
        u8 enable;
        u8 padding[3];
-} __attribute__ ((packed));
-
-struct smart_reflex_err_table {
-       u8 len;
-       s8 upper_limit;
-       s8 values[14];
-} __attribute__ ((packed));
-
-struct acx_smart_reflex_config_params {
-       struct acx_header header;
-
-       struct smart_reflex_err_table error_table[3];
+       __le32 timeout;
 } __attribute__ ((packed));
 
 #define PTA_ANTENNA_TYPE_DEF             (0)
@@ -837,6 +826,9 @@ struct acx_rate_class {
        u8 reserved;
 };
 
+#define ACX_TX_BASIC_RATE      0
+#define ACX_TX_AP_FULL_RATE    1
+#define ACX_TX_RATE_POLICY_CNT 2
 struct acx_rate_policy {
        struct acx_header header;
 
@@ -877,8 +869,8 @@ struct acx_tx_config_options {
        __le16 tx_compl_threshold;   /* number of packets */
 } __attribute__ ((packed));
 
-#define ACX_RX_MEM_BLOCKS     64
-#define ACX_TX_MIN_MEM_BLOCKS 64
+#define ACX_RX_MEM_BLOCKS     70
+#define ACX_TX_MIN_MEM_BLOCKS 40
 #define ACX_TX_DESCRIPTORS    32
 #define ACX_NUM_SSID_PROFILES 1
 
@@ -969,6 +961,13 @@ struct wl1271_acx_arp_filter {
                               used. */
 } __attribute__((packed));
 
+struct wl1271_acx_pm_config {
+       struct acx_header header;
+
+       __le32 host_clk_settling_time;
+       u8 host_fast_wakeup_support;
+       u8 padding[3];
+} __attribute__ ((packed));
 
 enum {
        ACX_WAKE_UP_CONDITIONS      = 0x0002,
@@ -1027,13 +1026,13 @@ enum {
        ACX_HT_BSS_OPERATION        = 0x0058,
        ACX_COEX_ACTIVITY           = 0x0059,
        ACX_SET_SMART_REFLEX_DEBUG  = 0x005A,
-       ACX_SET_SMART_REFLEX_STATE  = 0x005B,
-       ACX_SET_SMART_REFLEX_PARAMS = 0x005F,
+       ACX_SET_DCO_ITRIM_PARAMS    = 0x0061,
        DOT11_RX_MSDU_LIFE_TIME     = 0x1004,
        DOT11_CUR_TX_PWR            = 0x100D,
        DOT11_RX_DOT11_MODE         = 0x1012,
        DOT11_RTS_THRESHOLD         = 0x1013,
        DOT11_GROUP_ADDRESS_TBL     = 0x1014,
+       ACX_PM_CONFIG               = 0x1016,
 
        MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL,
 
@@ -1056,6 +1055,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
                                 void *mc_list, u32 mc_list_len);
 int wl1271_acx_service_period_timeout(struct wl1271 *wl);
 int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
+int wl1271_acx_dco_itrim_params(struct wl1271 *wl);
 int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
 int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
 int wl1271_acx_conn_monit_params(struct wl1271 *wl);
@@ -1069,7 +1069,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
 int wl1271_acx_cts_protect(struct wl1271 *wl,
                           enum acx_ctsprotect_type ctsprotect);
 int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
-int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates);
+int wl1271_acx_rate_policies(struct wl1271 *wl);
 int wl1271_acx_ac_cfg(struct wl1271 *wl);
 int wl1271_acx_tid_cfg(struct wl1271 *wl);
 int wl1271_acx_frag_threshold(struct wl1271 *wl);
@@ -1081,5 +1081,6 @@ int wl1271_acx_smart_reflex(struct wl1271 *wl);
 int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
 int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
                             u8 version);
+int wl1271_acx_pm_config(struct wl1271 *wl);
 
 #endif /* __WL1271_ACX_H__ */
index b7c96454cca3444147054d64797993662701600f..e803b876f3f03cb7efa71807024b3d2d44938f52 100644 (file)
@@ -225,9 +225,15 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
        if (nvs == NULL)
                return -ENODEV;
 
+       if (wl->nvs_len < WL1271_NVS_LEN)
+               return -EINVAL;
+
        nvs_ptr = nvs;
 
-       nvs_len = wl->nvs_len;
+       /* only the first part of the NVS needs to be uploaded */
+       nvs_len = WL1271_NVS_LEN;
+
+       /* FIXME: read init settings from the remaining part of the NVS */
 
        /* Update the device MAC address into the nvs */
        nvs[11] = wl->mac_addr[0];
index c3385b3d246cf988d3efb5d882a5d1c0e877ffd5..a74259bb596bb99cd10ee3bc84630907dbf70b0f 100644 (file)
@@ -209,6 +209,26 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
        gen_parms->tx_bip_fem_manufacturer = g->tx_bip_fem_manufacturer;
        gen_parms->settings = g->settings;
 
+       gen_parms->sr_state = g->sr_state;
+
+       memcpy(gen_parms->srf1,
+              g->srf1,
+              CONF_MAX_SMART_REFLEX_PARAMS);
+       memcpy(gen_parms->srf2,
+              g->srf2,
+              CONF_MAX_SMART_REFLEX_PARAMS);
+       memcpy(gen_parms->srf3,
+              g->srf3,
+              CONF_MAX_SMART_REFLEX_PARAMS);
+       memcpy(gen_parms->sr_debug_table,
+              g->sr_debug_table,
+              CONF_MAX_SMART_REFLEX_PARAMS);
+
+       gen_parms->sr_sen_n_p = g->sr_sen_n_p;
+       gen_parms->sr_sen_n_p_gain = g->sr_sen_n_p_gain;
+       gen_parms->sr_sen_nrn = g->sr_sen_nrn;
+       gen_parms->sr_sen_prn = g->sr_sen_prn;
+
        ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0);
        if (ret < 0)
                wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed");
@@ -253,6 +273,8 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
               CONF_NUMBER_OF_RATE_GROUPS);
        memcpy(radio_parms->tx_rate_limits_degraded, r->tx_rate_limits_degraded,
               CONF_NUMBER_OF_RATE_GROUPS);
+       memcpy(radio_parms->tx_rate_limits_extreme, r->tx_rate_limits_extreme,
+              CONF_NUMBER_OF_RATE_GROUPS);
 
        memcpy(radio_parms->tx_channel_limits_11b, r->tx_channel_limits_11b,
               CONF_NUMBER_OF_CHANNELS_2_4);
@@ -263,6 +285,11 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
        memcpy(radio_parms->tx_ibias, r->tx_ibias, CONF_NUMBER_OF_RATE_GROUPS);
 
        radio_parms->rx_fem_insertion_loss = r->rx_fem_insertion_loss;
+       radio_parms->degraded_low_to_normal_threshold =
+               r->degraded_low_to_normal_threshold;
+       radio_parms->degraded_normal_to_high_threshold =
+               r->degraded_normal_to_high_threshold;
+
 
        for (i = 0; i < CONF_NUMBER_OF_SUB_BANDS_5; i++)
                radio_parms->tx_ref_pd_voltage_5[i] =
@@ -275,6 +302,8 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
               r->tx_rate_limits_normal_5, CONF_NUMBER_OF_RATE_GROUPS);
        memcpy(radio_parms->tx_rate_limits_degraded_5,
               r->tx_rate_limits_degraded_5, CONF_NUMBER_OF_RATE_GROUPS);
+       memcpy(radio_parms->tx_rate_limits_extreme_5,
+              r->tx_rate_limits_extreme_5, CONF_NUMBER_OF_RATE_GROUPS);
        memcpy(radio_parms->tx_channel_limits_ofdm_5,
               r->tx_channel_limits_ofdm_5, CONF_NUMBER_OF_CHANNELS_5);
        memcpy(radio_parms->tx_pdv_rate_offsets_5, r->tx_pdv_rate_offsets_5,
@@ -283,6 +312,10 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
               CONF_NUMBER_OF_RATE_GROUPS);
        memcpy(radio_parms->rx_fem_insertion_loss_5,
               r->rx_fem_insertion_loss_5, CONF_NUMBER_OF_SUB_BANDS_5);
+       radio_parms->degraded_low_to_normal_threshold_5 =
+               r->degraded_low_to_normal_threshold_5;
+       radio_parms->degraded_normal_to_high_threshold_5 =
+               r->degraded_normal_to_high_threshold_5;
 
        wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
                    radio_parms, sizeof(*radio_parms));
@@ -311,19 +344,6 @@ int wl1271_cmd_join(struct wl1271 *wl)
                        do_cal = false;
        }
 
-       /* FIXME: This is a workaround, because with the current stack, we
-        * cannot know when we have disassociated.  So, if we have already
-        * joined, we disconnect before joining again. */
-       if (wl->joined) {
-               ret = wl1271_cmd_disconnect(wl);
-               if (ret < 0) {
-                       wl1271_error("failed to disconnect before rejoining");
-                       goto out;
-               }
-
-               wl->joined = false;
-       }
-
        join = kzalloc(sizeof(*join), GFP_KERNEL);
        if (!join) {
                ret = -ENOMEM;
@@ -388,8 +408,6 @@ int wl1271_cmd_join(struct wl1271 *wl)
                goto out_free;
        }
 
-       wl->joined = true;
-
        /*
         * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to
         * simplify locking we just sleep instead, for now
@@ -487,7 +505,7 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
        return 0;
 }
 
-int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable)
+int wl1271_cmd_data_path(struct wl1271 *wl, bool enable)
 {
        struct cmd_enabledisable_path *cmd;
        int ret;
@@ -501,7 +519,8 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable)
                goto out;
        }
 
-       cmd->channel = channel;
+       /* the channel here is only used for calibration, so hardcoded to 1 */
+       cmd->channel = 1;
 
        if (enable) {
                cmd_rx = CMD_ENABLE_RX;
@@ -514,22 +533,22 @@ int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable)
        ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("rx %s cmd for channel %d failed",
-                            enable ? "start" : "stop", channel);
+                            enable ? "start" : "stop", cmd->channel);
                goto out;
        }
 
        wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d",
-                    enable ? "start" : "stop", channel);
+                    enable ? "start" : "stop", cmd->channel);
 
        ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("tx %s cmd for channel %d failed",
-                            enable ? "start" : "stop", channel);
+                            enable ? "start" : "stop", cmd->channel);
                return ret;
        }
 
        wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d",
-                    enable ? "start" : "stop", channel);
+                    enable ? "start" : "stop", cmd->channel);
 
 out:
        kfree(cmd);
@@ -636,7 +655,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
        channels = wl->hw->wiphy->bands[ieee_band]->channels;
        n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels;
 
-       if (wl->scanning)
+       if (test_bit(WL1271_FLAG_SCANNING, &wl->flags))
                return -EINVAL;
 
        params = kzalloc(sizeof(*params), GFP_KERNEL);
@@ -711,7 +730,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
 
        wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params));
 
-       wl->scanning = true;
+       set_bit(WL1271_FLAG_SCANNING, &wl->flags);
        if (wl1271_11a_enabled()) {
                wl->scan.state = band;
                if (band == WL1271_SCAN_BAND_DUAL) {
@@ -729,7 +748,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
        ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0);
        if (ret < 0) {
                wl1271_error("SCAN failed");
-               wl->scanning = false;
+               clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
                goto out;
        }
 
index b4fa4acb922957c912d7461f0a49e5faf286e08f..09fe91297acfe7bd0e2ddaf2883b0520602db1ed 100644 (file)
@@ -37,7 +37,7 @@ int wl1271_cmd_join(struct wl1271 *wl);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
 int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
 int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
-int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable);
+int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
 int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode);
 int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
                           size_t len);
@@ -437,6 +437,21 @@ struct wl1271_general_parms_cmd {
        u8 tx_bip_fem_autodetect;
        u8 tx_bip_fem_manufacturer;
        u8 settings;
+
+       u8 sr_state;
+
+       s8 srf1[CONF_MAX_SMART_REFLEX_PARAMS];
+       s8 srf2[CONF_MAX_SMART_REFLEX_PARAMS];
+       s8 srf3[CONF_MAX_SMART_REFLEX_PARAMS];
+
+       s8 sr_debug_table[CONF_MAX_SMART_REFLEX_PARAMS];
+
+       u8 sr_sen_n_p;
+       u8 sr_sen_n_p_gain;
+       u8 sr_sen_nrn;
+       u8 sr_sen_prn;
+
+       u8 padding[3];
 } __attribute__ ((packed));
 
 struct wl1271_radio_parms_cmd {
@@ -458,11 +473,12 @@ struct wl1271_radio_parms_cmd {
        /* Dynamic radio parameters */
        /* 2.4GHz */
        __le16 tx_ref_pd_voltage;
-       s8  tx_ref_power;
+       u8  tx_ref_power;
        s8  tx_offset_db;
 
        s8  tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS];
        s8  tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS];
+       s8  tx_rate_limits_extreme[CONF_NUMBER_OF_RATE_GROUPS];
 
        s8  tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4];
        s8  tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4];
@@ -471,15 +487,19 @@ struct wl1271_radio_parms_cmd {
        u8  tx_ibias[CONF_NUMBER_OF_RATE_GROUPS];
        u8  rx_fem_insertion_loss;
 
-       u8 padding2;
+       u8  degraded_low_to_normal_threshold;
+       u8  degraded_normal_to_high_threshold;
+
+       u8  padding1; /* our own padding, not in ref driver */
 
        /* 5GHz */
        __le16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5];
-       s8  tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5];
+       u8  tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5];
        s8  tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5];
 
        s8  tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS];
        s8  tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS];
+       s8  tx_rate_limits_extreme_5[CONF_NUMBER_OF_RATE_GROUPS];
 
        s8  tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5];
        s8  tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS];
@@ -488,7 +508,10 @@ struct wl1271_radio_parms_cmd {
        s8  tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS];
        s8  rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
 
-       u8 padding3[2];
+       u8  degraded_low_to_normal_threshold_5;
+       u8  degraded_normal_to_high_threshold_5;
+
+       u8 padding2[2];
 } __attribute__ ((packed));
 
 struct wl1271_cmd_cal_channel_tune {
index 565373ede2652a4b711a5f6a5a233847ba79ec85..1993d63c214ed3a0a2613c83d5f95a7ccc03fded 100644 (file)
@@ -258,7 +258,8 @@ struct conf_rx_settings {
 #define CONF_TX_MAX_RATE_CLASSES       8
 
 #define CONF_TX_RATE_MASK_UNSPECIFIED  0
-#define CONF_TX_RATE_MASK_ALL          0x1eff
+#define CONF_TX_RATE_MASK_BASIC        (CONF_HW_BIT_RATE_1MBPS | \
+                                       CONF_HW_BIT_RATE_2MBPS)
 #define CONF_TX_RATE_RETRY_LIMIT       10
 
 struct conf_tx_rate_class {
@@ -722,31 +723,6 @@ struct conf_conn_settings {
        u8 psm_entry_retries;
 };
 
-#define CONF_SR_ERR_TBL_MAX_VALUES   14
-
-struct conf_mart_reflex_err_table {
-       /*
-        * Length of the error table values table.
-        *
-        * Range: 0 - CONF_SR_ERR_TBL_MAX_VALUES
-        */
-       u8 len;
-
-       /*
-        * Smart Reflex error table upper limit.
-        *
-        * Range: s8
-        */
-       s8 upper_limit;
-
-       /*
-        * Smart Reflex error table values.
-        *
-        * Range: s8
-        */
-       s8 values[CONF_SR_ERR_TBL_MAX_VALUES];
-};
-
 enum {
        CONF_REF_CLK_19_2_E,
        CONF_REF_CLK_26_E,
@@ -759,6 +735,9 @@ enum single_dual_band_enum {
        CONF_DUAL_BAND
 };
 
+
+#define CONF_MAX_SMART_REFLEX_PARAMS 16
+
 struct conf_general_parms {
        /*
         * RF Reference Clock type / speed
@@ -815,6 +794,20 @@ struct conf_general_parms {
         * Range: Unknown
         */
        u8 settings;
+
+       /* Smart reflex settings */
+       u8 sr_state;
+
+       s8 srf1[CONF_MAX_SMART_REFLEX_PARAMS];
+       s8 srf2[CONF_MAX_SMART_REFLEX_PARAMS];
+       s8 srf3[CONF_MAX_SMART_REFLEX_PARAMS];
+
+       s8 sr_debug_table[CONF_MAX_SMART_REFLEX_PARAMS];
+
+       u8 sr_sen_n_p;
+       u8 sr_sen_n_p_gain;
+       u8 sr_sen_nrn;
+       u8 sr_sen_prn;
 };
 
 #define CONF_RSSI_AND_PROCESS_COMPENSATION_SIZE 15
@@ -847,12 +840,13 @@ struct conf_radio_parms {
         *
         * Range: unknown
         */
-       s16 tx_ref_pd_voltage;
-       s8  tx_ref_power;
+       u16 tx_ref_pd_voltage;
+       u8  tx_ref_power;
        s8  tx_offset_db;
 
        s8  tx_rate_limits_normal[CONF_NUMBER_OF_RATE_GROUPS];
        s8  tx_rate_limits_degraded[CONF_NUMBER_OF_RATE_GROUPS];
+       s8  tx_rate_limits_extreme[CONF_NUMBER_OF_RATE_GROUPS];
 
        s8  tx_channel_limits_11b[CONF_NUMBER_OF_CHANNELS_2_4];
        s8  tx_channel_limits_ofdm[CONF_NUMBER_OF_CHANNELS_2_4];
@@ -861,17 +855,22 @@ struct conf_radio_parms {
        u8  tx_ibias[CONF_NUMBER_OF_RATE_GROUPS];
        u8  rx_fem_insertion_loss;
 
+       u8  degraded_low_to_normal_threshold;
+       u8  degraded_normal_to_high_threshold;
+
+
        /*
         * Dynamic radio parameters for 5GHz
         *
         * Range: unknown
         */
-       s16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5];
-       s8  tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5];
+       u16 tx_ref_pd_voltage_5[CONF_NUMBER_OF_SUB_BANDS_5];
+       u8  tx_ref_power_5[CONF_NUMBER_OF_SUB_BANDS_5];
        s8  tx_offset_db_5[CONF_NUMBER_OF_SUB_BANDS_5];
 
        s8  tx_rate_limits_normal_5[CONF_NUMBER_OF_RATE_GROUPS];
        s8  tx_rate_limits_degraded_5[CONF_NUMBER_OF_RATE_GROUPS];
+       s8  tx_rate_limits_extreme_5[CONF_NUMBER_OF_RATE_GROUPS];
 
        s8  tx_channel_limits_ofdm_5[CONF_NUMBER_OF_CHANNELS_5];
        s8  tx_pdv_rate_offsets_5[CONF_NUMBER_OF_RATE_GROUPS];
@@ -879,33 +878,46 @@ struct conf_radio_parms {
        /* FIXME: this is inconsistent with the types for 2.4GHz */
        s8  tx_ibias_5[CONF_NUMBER_OF_RATE_GROUPS];
        s8  rx_fem_insertion_loss_5[CONF_NUMBER_OF_SUB_BANDS_5];
-};
 
-#define CONF_SR_ERR_TBL_COUNT        3
+       u8  degraded_low_to_normal_threshold_5;
+       u8  degraded_normal_to_high_threshold_5;
+};
 
 struct conf_init_settings {
        /*
-        * Configure Smart Reflex error table values.
+        * Configure general parameters.
         */
-       struct conf_mart_reflex_err_table sr_err_tbl[CONF_SR_ERR_TBL_COUNT];
+       struct conf_general_parms genparam;
 
        /*
-        * Smart Reflex enable flag.
-        *
-        * Range: 1 - Smart Reflex enabled, 0 - Smart Reflex disabled
+        * Configure radio parameters.
         */
-       u8 sr_enable;
+       struct conf_radio_parms radioparam;
 
+};
+
+struct conf_itrim_settings {
+       /* enable dco itrim */
+       u8 enable;
+
+       /* moderation timeout in microsecs from the last TX */
+       u32 timeout;
+};
+
+struct conf_pm_config_settings {
        /*
-        * Configure general parameters.
+        * Host clock settling time
+        *
+        * Range: 0 - 30000 us
         */
-       struct conf_general_parms genparam;
+       u32 host_clk_settling_time;
 
        /*
-        * Configure radio parameters.
+        * Host fast wakeup support
+        *
+        * Range: true, false
         */
-       struct conf_radio_parms radioparam;
-
+       bool host_fast_wakeup_support;
 };
 
 struct conf_drv_settings {
@@ -914,6 +926,8 @@ struct conf_drv_settings {
        struct conf_tx_settings tx;
        struct conf_conn_settings conn;
        struct conf_init_settings init;
+       struct conf_itrim_settings itrim;
+       struct conf_pm_config_settings pm_config;
 };
 
 #endif
index c1805e5f8964c1eea83918f9867ea4c702675e53..8d7588ca68fd0c14adc8c6508d1ab5456b674cf9 100644 (file)
@@ -237,6 +237,64 @@ static const struct file_operations tx_queue_len_ops = {
        .open = wl1271_open_file_generic,
 };
 
+static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
+                         size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       bool state = test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+
+       int res;
+       char buf[10];
+
+       res = scnprintf(buf, sizeof(buf), "%d\n", state);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, res);
+}
+
+static ssize_t gpio_power_write(struct file *file,
+                          const char __user *user_buf,
+                          size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       char buf[10];
+       size_t len;
+       unsigned long value;
+       int ret;
+
+       mutex_lock(&wl->mutex);
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len)) {
+               ret = -EFAULT;
+               goto out;
+       }
+       buf[len] = '\0';
+
+       ret = strict_strtoul(buf, 0, &value);
+       if (ret < 0) {
+               wl1271_warning("illegal value in gpio_power");
+               goto out;
+       }
+
+       if (value) {
+               wl->set_power(true);
+               set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+       } else {
+               wl->set_power(false);
+               clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
+       }
+
+out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static const struct file_operations gpio_power_ops = {
+       .read = gpio_power_read,
+       .write = gpio_power_write,
+       .open = wl1271_open_file_generic
+};
+
 static void wl1271_debugfs_delete_files(struct wl1271 *wl)
 {
        DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow);
@@ -333,6 +391,8 @@ static void wl1271_debugfs_delete_files(struct wl1271 *wl)
        DEBUGFS_DEL(tx_queue_len);
        DEBUGFS_DEL(retry_count);
        DEBUGFS_DEL(excessive_retries);
+
+       DEBUGFS_DEL(gpio_power);
 }
 
 static int wl1271_debugfs_add_files(struct wl1271 *wl)
@@ -434,6 +494,8 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl)
        DEBUGFS_ADD(retry_count, wl->debugfs.rootdir);
        DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir);
 
+       DEBUGFS_ADD(gpio_power, wl->debugfs.rootdir);
+
 out:
        if (ret < 0)
                wl1271_debugfs_delete_files(wl);
index d13fdd99c85c515eea99dd128f8e8b8ecd606a31..0a145afc990592a20b5a1b976fb72616a0f9bdc4 100644 (file)
@@ -35,7 +35,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
        wl1271_debug(DEBUG_EVENT, "status: 0x%x",
                     mbox->scheduled_scan_status);
 
-       if (wl->scanning) {
+       if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
                if (wl->scan.state == WL1271_SCAN_BAND_DUAL) {
                        wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
                                                NULL, size);
@@ -43,7 +43,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
                         * to the wl1271_cmd_scan function that we are not
                         * scanning as it checks that.
                         */
-                       wl->scanning = false;
+                       clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
                        wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
                                                wl->scan.active,
                                                wl->scan.high_prio,
@@ -62,7 +62,7 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
                        mutex_unlock(&wl->mutex);
                        ieee80211_scan_completed(wl->hw, false);
                        mutex_lock(&wl->mutex);
-                       wl->scanning = false;
+                       clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
                }
        }
        return 0;
@@ -78,7 +78,7 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
 
        switch (mbox->ps_status) {
        case EVENT_ENTER_POWER_SAVE_FAIL:
-               if (!wl->psm) {
+               if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
                        wl->psm_entry_retry = 0;
                        break;
                }
@@ -89,7 +89,6 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
                } else {
                        wl1271_error("PSM entry failed, giving up.\n");
                        wl->psm_entry_retry = 0;
-                       *beacon_loss = true;
                }
                break;
        case EVENT_ENTER_POWER_SAVE_SUCCESS:
@@ -136,7 +135,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
         * filtering) is enabled. Without PSM, the stack will receive all
         * beacons and can detect beacon loss by itself.
         */
-       if (vector & BSS_LOSE_EVENT_ID && wl->psm) {
+       if (vector & BSS_LOSE_EVENT_ID &&
+           test_bit(WL1271_FLAG_PSM, &wl->flags)) {
                wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
 
                /* indicate to the stack, that beacons have been lost */
@@ -150,7 +150,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
                        return ret;
        }
 
-       if (beacon_loss) {
+       if (wl->vif && beacon_loss) {
                /* Obviously, it's dangerous to release the mutex while
                   we are holding many of the variables in the wl struct.
                   That's why it's done last in the function, and care must
@@ -184,7 +184,7 @@ void wl1271_event_mbox_config(struct wl1271 *wl)
                     wl->mbox_ptr[0], wl->mbox_ptr[1]);
 }
 
-int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack)
+int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
 {
        struct event_mailbox mbox;
        int ret;
@@ -204,9 +204,7 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num, bool do_ack)
                return ret;
 
        /* then we let the firmware know it can go on...*/
-       if (do_ack)
-               wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG,
-                                  INTR_TRIG_EVENT_ACK);
+       wl1271_spi_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK);
 
        return 0;
 }
index 4e3f55ebb1a87eb88ba09630fb94ed5c5f404b3d..278f9206aa5623ad6d9b08a93b066bec8d97de91 100644 (file)
@@ -112,6 +112,6 @@ struct event_mailbox {
 
 int wl1271_event_unmask(struct wl1271 *wl);
 void wl1271_event_mbox_config(struct wl1271 *wl);
-int wl1271_event_handle(struct wl1271 *wl, u8 mbox, bool do_ack);
+int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
 
 #endif
index 11249b436cf1745a2c93ab3db7df4ba4147f29d7..c9848eecb767f45648233a84e0ed79b7131ba9be 100644 (file)
@@ -229,6 +229,10 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                goto out_free_memmap;
 
+       ret = wl1271_acx_dco_itrim_params(wl);
+       if (ret < 0)
+               goto out_free_memmap;
+
        /* Initialize connection monitoring thresholds */
        ret = wl1271_acx_conn_monit_params(wl);
        if (ret < 0)
@@ -280,12 +284,12 @@ int wl1271_hw_init(struct wl1271 *wl)
                goto out_free_memmap;
 
        /* Configure TX rate classes */
-       ret = wl1271_acx_rate_policies(wl, CONF_TX_RATE_MASK_ALL);
+       ret = wl1271_acx_rate_policies(wl);
        if (ret < 0)
                goto out_free_memmap;
 
        /* Enable data path */
-       ret = wl1271_cmd_data_path(wl, wl->channel, 1);
+       ret = wl1271_cmd_data_path(wl, 1);
        if (ret < 0)
                goto out_free_memmap;
 
@@ -299,8 +303,8 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                goto out_free_memmap;
 
-       /* Configure smart reflex */
-       ret = wl1271_acx_smart_reflex(wl);
+       /* configure PM */
+       ret = wl1271_acx_pm_config(wl);
        if (ret < 0)
                goto out_free_memmap;
 
index b62c00ff42fe0c3a724e6030d93421b1ae75a8bf..e4867b895c43b67fdc25e8c56b8ab949c34fff90 100644 (file)
@@ -47,6 +47,8 @@
 #include "wl1271_cmd.h"
 #include "wl1271_boot.h"
 
+#define WL1271_BOOT_RETRIES 3
+
 static struct conf_drv_settings default_conf = {
        .sg = {
                .per_threshold               = 7500,
@@ -67,16 +69,17 @@ static struct conf_drv_settings default_conf = {
                .ps_poll_timeout             = 15,
                .upsd_timeout                = 15,
                .rts_threshold               = 2347,
-               .rx_cca_threshold            = 0xFFEF,
-               .irq_blk_threshold           = 0,
-               .irq_pkt_threshold           = USHORT_MAX,
-               .irq_timeout                 = 5,
+               .rx_cca_threshold            = 0,
+               .irq_blk_threshold           = 0xFFFF,
+               .irq_pkt_threshold           = 0,
+               .irq_timeout                 = 600,
                .queue_type                  = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
        },
        .tx = {
                .tx_energy_detection         = 0,
                .rc_conf                     = {
-                       .enabled_rates       = CONF_TX_RATE_MASK_UNSPECIFIED,
+                       .enabled_rates       = CONF_HW_BIT_RATE_1MBPS |
+                                              CONF_HW_BIT_RATE_2MBPS,
                        .short_retry_limit   = 10,
                        .long_retry_limit    = 10,
                        .aflags              = 0
@@ -172,8 +175,8 @@ static struct conf_drv_settings default_conf = {
                        }
                },
                .frag_threshold              = IEEE80211_MAX_FRAG_THRESHOLD,
-               .tx_compl_timeout            = 5,
-               .tx_compl_threshold          = 5
+               .tx_compl_timeout            = 700,
+               .tx_compl_threshold          = 4
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -186,12 +189,12 @@ static struct conf_drv_settings default_conf = {
                                .rule        = CONF_BCN_RULE_PASS_ON_APPEARANCE,
                        }
                },
-               .synch_fail_thold            = 5,
+               .synch_fail_thold            = 10,
                .bss_lose_timeout            = 100,
                .beacon_rx_timeout           = 10000,
                .broadcast_timeout           = 20000,
                .rx_broadcast_in_ps          = 1,
-               .ps_poll_threshold           = 4,
+               .ps_poll_threshold           = 20,
                .sig_trigger_count           = 2,
                .sig_trigger = {
                        [0] = {
@@ -226,46 +229,35 @@ static struct conf_drv_settings default_conf = {
                .psm_entry_retries           = 3
        },
        .init = {
-               .sr_err_tbl = {
-                       [0] = {
-                               .len         = 7,
-                               .upper_limit = 0x03,
-                               .values      = {
-                                       0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8,
-                                       0x00 }
-                       },
-                       [1] = {
-                               .len         = 7,
-                               .upper_limit = 0x03,
-                               .values      = {
-                                       0x18, 0x10, 0x05, 0xf6, 0xf0, 0xe8,
-                                       0x00 }
-                       },
-                       [2] = {
-                               .len         = 7,
-                               .upper_limit = 0x03,
-                               .values      = {
-                                       0x18, 0x10, 0x05, 0xfb, 0xf0, 0xe8,
-                                       0x00 }
-                       }
-               },
-               .sr_enable                   = 1,
                .genparam                    = {
                        .ref_clk             = CONF_REF_CLK_38_4_E,
                        .settling_time       = 5,
                        .clk_valid_on_wakeup = 0,
                        .dc2dcmode           = 0,
                        .single_dual_band    = CONF_SINGLE_BAND,
-                       .tx_bip_fem_autodetect = 0,
+                       .tx_bip_fem_autodetect = 1,
                        .tx_bip_fem_manufacturer = 1,
                        .settings = 1,
+                       .sr_state = 1,
+                       .srf1 = { 0x07, 0x03, 0x18, 0x10, 0x05, 0xfb, 0xf0,
+                                 0xe8, 0, 0, 0, 0, 0, 0, 0, 0 },
+                       .srf2 = { 0x07, 0x03, 0x18, 0x10, 0x05, 0xfb, 0xf0,
+                                 0xe8, 0, 0, 0, 0, 0, 0, 0, 0 },
+                       .srf3 = { 0x07, 0x03, 0x18, 0x10, 0x05, 0xfb, 0xf0,
+                                 0xe8, 0, 0, 0, 0, 0, 0, 0, 0 },
+                       .sr_debug_table = { 0, 0, 0, 0, 0, 0, 0, 0,
+                                           0, 0, 0, 0, 0, 0, 0, 0 },
+                       .sr_sen_n_p = 0,
+                       .sr_sen_n_p_gain = 0,
+                       .sr_sen_nrn = 0,
+                       .sr_sen_prn = 0,
                },
                .radioparam = {
-                       .rx_trace_loss       = 10,
-                       .tx_trace_loss       = 10,
+                       .rx_trace_loss       = 0x24,
+                       .tx_trace_loss       = 0x0,
                        .rx_rssi_and_proc_compens = {
                                0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8,
-                               0xfc, 0x00, 0x08, 0x10, 0xf0, 0xf8,
+                               0xfc, 0x00, 0x80, 0x10, 0xf0, 0xf8,
                                0x00, 0x0a, 0x14 },
                        .rx_trace_loss_5     = { 0, 0, 0, 0, 0, 0, 0 },
                        .tx_trace_loss_5     = { 0, 0, 0, 0, 0, 0, 0 },
@@ -273,13 +265,15 @@ static struct conf_drv_settings default_conf = {
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                0x00, 0x00, 0x00 },
-                       .tx_ref_pd_voltage   = 0x24e,
-                       .tx_ref_power        = 0x78,
+                       .tx_ref_pd_voltage   = 0x1a9,
+                       .tx_ref_power        = 0x80,
                        .tx_offset_db        = 0x0,
                        .tx_rate_limits_normal = {
-                               0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 },
+                               0x1d, 0x1f, 0x24, 0x28, 0x28, 0x29 },
                        .tx_rate_limits_degraded = {
-                               0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 },
+                               0x19, 0x1f, 0x22, 0x23, 0x27, 0x28 },
+                       .tx_rate_limits_extreme = {
+                               0x19, 0x1c, 0x1e, 0x20, 0x24, 0x25 },
                        .tx_channel_limits_11b = {
                                0x22, 0x50, 0x50, 0x50, 0x50, 0x50,
                                0x50, 0x50, 0x50, 0x50, 0x22, 0x50,
@@ -289,10 +283,12 @@ static struct conf_drv_settings default_conf = {
                                0x50, 0x50, 0x50, 0x50, 0x20, 0x50,
                                0x20, 0x50 },
                        .tx_pdv_rate_offsets = {
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+                               0x07, 0x08, 0x04, 0x02, 0x02, 0x00 },
                        .tx_ibias            = {
-                               0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 },
-                       .rx_fem_insertion_loss = 0x14,
+                               0x11, 0x11, 0x15, 0x11, 0x15, 0x0f },
+                       .rx_fem_insertion_loss = 0x0e,
+                       .degraded_low_to_normal_threshold = 0x1e,
+                       .degraded_normal_to_high_threshold = 0x2d,
                        .tx_ref_pd_voltage_5 = {
                                0x0190, 0x01a4, 0x01c3, 0x01d8,
                                0x020a, 0x021c },
@@ -304,6 +300,8 @@ static struct conf_drv_settings default_conf = {
                                0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
                        .tx_rate_limits_degraded_5 = {
                                0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
+                       .tx_rate_limits_extreme_5 = {
+                               0x1b, 0x1e, 0x21, 0x23, 0x27, 0x00 },
                        .tx_channel_limits_ofdm_5 = {
                                0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
                                0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50,
@@ -315,8 +313,18 @@ static struct conf_drv_settings default_conf = {
                        .tx_ibias_5          = {
                                0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
                        .rx_fem_insertion_loss_5 = {
-                               0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }
+                               0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
+                       .degraded_low_to_normal_threshold_5 = 0x00,
+                       .degraded_normal_to_high_threshold_5 = 0x00
                }
+       },
+       .itrim = {
+               .enable = false,
+               .timeout = 50000,
+       },
+       .pm_config = {
+               .host_clk_settling_time = 5000,
+               .host_fast_wakeup_support = false
        }
 };
 
@@ -359,7 +367,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       ret = wl1271_cmd_data_path(wl, wl->channel, 1);
+       ret = wl1271_cmd_data_path(wl, 1);
        if (ret < 0)
                return ret;
 
@@ -374,11 +382,13 @@ static void wl1271_disable_interrupts(struct wl1271 *wl)
 static void wl1271_power_off(struct wl1271 *wl)
 {
        wl->set_power(false);
+       clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
 }
 
 static void wl1271_power_on(struct wl1271 *wl)
 {
        wl->set_power(true);
+       set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
 }
 
 static void wl1271_fw_status(struct wl1271 *wl,
@@ -447,14 +457,13 @@ static void wl1271_irq_work(struct work_struct *work)
        intr &= WL1271_INTR_MASK;
 
        if (intr & WL1271_ACX_INTR_EVENT_A) {
-               bool do_ack = (intr & WL1271_ACX_INTR_EVENT_B) ? false : true;
                wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A");
-               wl1271_event_handle(wl, 0, do_ack);
+               wl1271_event_handle(wl, 0);
        }
 
        if (intr & WL1271_ACX_INTR_EVENT_B) {
                wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B");
-               wl1271_event_handle(wl, 1, true);
+               wl1271_event_handle(wl, 1);
        }
 
        if (intr & WL1271_ACX_INTR_INIT_COMPLETE)
@@ -614,6 +623,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
        struct wl1271_partition_set partition;
        int ret = 0;
 
+       msleep(WL1271_PRE_POWER_ON_SLEEP);
        wl1271_power_on(wl);
        msleep(WL1271_POWER_ON_SLEEP);
        wl1271_spi_reset(wl);
@@ -643,7 +653,7 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
 
                ret = wl1271_setup(wl);
                if (ret < 0)
-                       goto out_power_off;
+                       goto out;
                break;
        case CHIP_ID_1271_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
@@ -651,38 +661,34 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
 
                ret = wl1271_setup(wl);
                if (ret < 0)
-                       goto out_power_off;
+                       goto out;
                break;
        default:
-               wl1271_error("unsupported chip id: 0x%x", wl->chip.id);
+               wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
                ret = -ENODEV;
-               goto out_power_off;
+               goto out;
        }
 
        if (wl->fw == NULL) {
                ret = wl1271_fetch_firmware(wl);
                if (ret < 0)
-                       goto out_power_off;
+                       goto out;
        }
 
        /* No NVS from netlink, try to get it from the filesystem */
        if (wl->nvs == NULL) {
                ret = wl1271_fetch_nvs(wl);
                if (ret < 0)
-                       goto out_power_off;
+                       goto out;
        }
 
-       goto out;
-
-out_power_off:
-       wl1271_power_off(wl);
-
 out:
        return ret;
 }
 
 int wl1271_plt_start(struct wl1271 *wl)
 {
+       int retries = WL1271_BOOT_RETRIES;
        int ret;
 
        mutex_lock(&wl->mutex);
@@ -696,35 +702,48 @@ int wl1271_plt_start(struct wl1271 *wl)
                goto out;
        }
 
-       wl->state = WL1271_STATE_PLT;
-
-       ret = wl1271_chip_wakeup(wl);
-       if (ret < 0)
-               goto out;
-
-       ret = wl1271_boot(wl);
-       if (ret < 0)
-               goto out_power_off;
-
-       wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver);
+       while (retries) {
+               retries--;
+               ret = wl1271_chip_wakeup(wl);
+               if (ret < 0)
+                       goto power_off;
 
-       ret = wl1271_plt_init(wl);
-       if (ret < 0)
-               goto out_irq_disable;
+               ret = wl1271_boot(wl);
+               if (ret < 0)
+                       goto power_off;
 
-       /* Make sure power saving is disabled */
-       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
-       if (ret < 0)
-               goto out_irq_disable;
+               ret = wl1271_plt_init(wl);
+               if (ret < 0)
+                       goto irq_disable;
 
-       goto out;
+               /* Make sure power saving is disabled */
+               ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+               if (ret < 0)
+                       goto irq_disable;
 
-out_irq_disable:
-       wl1271_disable_interrupts(wl);
+               wl->state = WL1271_STATE_PLT;
+               wl1271_notice("firmware booted in PLT mode (%s)",
+                             wl->chip.fw_ver);
+               goto out;
 
-out_power_off:
-       wl1271_power_off(wl);
+irq_disable:
+               wl1271_disable_interrupts(wl);
+               mutex_unlock(&wl->mutex);
+               /* Unlocking the mutex in the middle of handling is
+                  inherently unsafe. In this case we deem it safe to do,
+                  because we need to let any possibly pending IRQ out of
+                  the system (and while we are WL1271_STATE_OFF the IRQ
+                  work function will not do anything.) Also, any other
+                  possible concurrent operations will fail due to the
+                  current state, hence the wl1271 struct should be safe. */
+               cancel_work_sync(&wl->irq_work);
+               mutex_lock(&wl->mutex);
+power_off:
+               wl1271_power_off(wl);
+       }
 
+       wl1271_error("firmware boot in PLT mode failed despite %d retries",
+                    WL1271_BOOT_RETRIES);
 out:
        mutex_unlock(&wl->mutex);
 
@@ -762,7 +781,20 @@ out:
 static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1271 *wl = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
+       struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+       struct ieee80211_sta *sta = txinfo->control.sta;
+       unsigned long flags;
 
+       /* peek into the rates configured in the STA entry */
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       if (sta && sta->supp_rates[conf->channel->band] != wl->sta_rate_set) {
+               wl->sta_rate_set = sta->supp_rates[conf->channel->band];
+               set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
+       }
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       /* queue the packet */
        skb_queue_tail(&wl->tx_queue, skb);
 
        /*
@@ -784,7 +816,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                 * protected. Maybe fix this by removing the stupid
                 * variable altogether and checking the real queue state?
                 */
-               wl->tx_queue_stopped = true;
+               set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
        }
 
        return NETDEV_TX_OK;
@@ -880,6 +912,7 @@ static struct notifier_block wl1271_dev_notifier = {
 static int wl1271_op_start(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
+       int retries = WL1271_BOOT_RETRIES;
        int ret = 0;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 start");
@@ -893,30 +926,42 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
                goto out;
        }
 
-       ret = wl1271_chip_wakeup(wl);
-       if (ret < 0)
-               goto out;
-
-       ret = wl1271_boot(wl);
-       if (ret < 0)
-               goto out_power_off;
-
-       ret = wl1271_hw_init(wl);
-       if (ret < 0)
-               goto out_irq_disable;
-
-       wl->state = WL1271_STATE_ON;
+       while (retries) {
+               retries--;
+               ret = wl1271_chip_wakeup(wl);
+               if (ret < 0)
+                       goto power_off;
 
-       wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
+               ret = wl1271_boot(wl);
+               if (ret < 0)
+                       goto power_off;
 
-       goto out;
+               ret = wl1271_hw_init(wl);
+               if (ret < 0)
+                       goto irq_disable;
 
-out_irq_disable:
-       wl1271_disable_interrupts(wl);
+               wl->state = WL1271_STATE_ON;
+               wl1271_info("firmware booted (%s)", wl->chip.fw_ver);
+               goto out;
 
-out_power_off:
-       wl1271_power_off(wl);
+irq_disable:
+               wl1271_disable_interrupts(wl);
+               mutex_unlock(&wl->mutex);
+               /* Unlocking the mutex in the middle of handling is
+                  inherently unsafe. In this case we deem it safe to do,
+                  because we need to let any possibly pending IRQ out of
+                  the system (and while we are WL1271_STATE_OFF the IRQ
+                  work function will not do anything.) Also, any other
+                  possible concurrent operations will fail due to the
+                  current state, hence the wl1271 struct should be safe. */
+               cancel_work_sync(&wl->irq_work);
+               mutex_lock(&wl->mutex);
+power_off:
+               wl1271_power_off(wl);
+       }
 
+       wl1271_error("firmware boot failed despite %d retries",
+                    WL1271_BOOT_RETRIES);
 out:
        mutex_unlock(&wl->mutex);
 
@@ -944,11 +989,10 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 
        WARN_ON(wl->state != WL1271_STATE_ON);
 
-       if (wl->scanning) {
+       if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
                mutex_unlock(&wl->mutex);
                ieee80211_scan_completed(wl->hw, true);
                mutex_lock(&wl->mutex);
-               wl->scanning = false;
        }
 
        wl->state = WL1271_STATE_OFF;
@@ -973,10 +1017,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
        wl->band = IEEE80211_BAND_2GHZ;
 
        wl->rx_counter = 0;
-       wl->elp = false;
-       wl->psm = 0;
        wl->psm_entry_retry = 0;
-       wl->tx_queue_stopped = false;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
        wl->tx_blocks_available = 0;
        wl->tx_results_count = 0;
@@ -986,7 +1027,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
        wl->tx_security_seq_32 = 0;
        wl->time_offset = 0;
        wl->session_counter = 0;
-       wl->joined = false;
+       wl->rate_set = CONF_TX_RATE_MASK_BASIC;
+       wl->sta_rate_set = 0;
+       wl->flags = 0;
 
        for (i = 0; i < NUM_TX_QUEUES; i++)
                wl->tx_blocks_freed[i] = 0;
@@ -996,13 +1039,13 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 }
 
 static int wl1271_op_add_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_if_init_conf *conf)
+                                  struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
        int ret = 0;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
-                    conf->type, conf->mac_addr);
+                    vif->type, vif->addr);
 
        mutex_lock(&wl->mutex);
        if (wl->vif) {
@@ -1010,9 +1053,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
-       wl->vif = conf->vif;
+       wl->vif = vif;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                wl->bss_type = BSS_TYPE_STA_BSS;
                break;
@@ -1032,7 +1075,7 @@ out:
 }
 
 static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
-                                        struct ieee80211_if_init_conf *conf)
+                                        struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
 
@@ -1109,6 +1152,51 @@ out:
 }
 #endif
 
+static int wl1271_join_channel(struct wl1271 *wl, int channel)
+{
+       int ret = 0;
+       /* we need to use a dummy BSSID for now */
+       static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
+                                                 0xad, 0xbe, 0xef };
+
+       /* the dummy join is not required for ad-hoc */
+       if (wl->bss_type == BSS_TYPE_IBSS)
+               goto out;
+
+       /* disable mac filter, so we hear everything */
+       wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+
+       wl->channel = channel;
+       memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
+
+       ret = wl1271_cmd_join(wl);
+       if (ret < 0)
+               goto out;
+
+       set_bit(WL1271_FLAG_JOINED, &wl->flags);
+
+out:
+       return ret;
+}
+
+static int wl1271_unjoin_channel(struct wl1271 *wl)
+{
+       int ret;
+
+       /* to stop listening to a channel, we disconnect */
+       ret = wl1271_cmd_disconnect(wl);
+       if (ret < 0)
+               goto out;
+
+       clear_bit(WL1271_FLAG_JOINED, &wl->flags);
+       wl->channel = 0;
+       memset(wl->bssid, 0, ETH_ALEN);
+       wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
+
+out:
+       return ret;
+}
+
 static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct wl1271 *wl = hw->priv;
@@ -1117,10 +1205,11 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 
        channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d",
+       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
                     channel,
                     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
-                    conf->power_level);
+                    conf->power_level,
+                    conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
 
        mutex_lock(&wl->mutex);
 
@@ -1130,34 +1219,44 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
        if (ret < 0)
                goto out;
 
-       if (channel != wl->channel) {
-               /*
-                * We assume that the stack will configure the right channel
-                * before associating, so we don't need to send a join
-                * command here.  We will join the right channel when the
-                * BSSID changes
-                */
-               wl->channel = channel;
+       if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+               if (conf->flags & IEEE80211_CONF_IDLE &&
+                   test_bit(WL1271_FLAG_JOINED, &wl->flags))
+                       wl1271_unjoin_channel(wl);
+               else if (!(conf->flags & IEEE80211_CONF_IDLE))
+                       wl1271_join_channel(wl, channel);
+
+               if (conf->flags & IEEE80211_CONF_IDLE) {
+                       wl->rate_set = CONF_TX_RATE_MASK_BASIC;
+                       wl->sta_rate_set = 0;
+                       wl1271_acx_rate_policies(wl);
+               }
        }
 
-       if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
-               wl1271_info("psm enabled");
+       /* if the channel changes while joined, join again */
+       if (channel != wl->channel && test_bit(WL1271_FLAG_JOINED, &wl->flags))
+               wl1271_join_channel(wl, channel);
 
-               wl->psm_requested = true;
+       if (conf->flags & IEEE80211_CONF_PS &&
+           !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
+               set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
 
                /*
                 * We enter PSM only if we're already associated.
                 * If we're not, we'll enter it when joining an SSID,
                 * through the bss_info_changed() hook.
                 */
-               ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+               if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+                       wl1271_info("psm enabled");
+                       ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+               }
        } else if (!(conf->flags & IEEE80211_CONF_PS) &&
-                  wl->psm_requested) {
+                  test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
                wl1271_info("psm disabled");
 
-               wl->psm_requested = false;
+               clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
 
-               if (wl->psm)
+               if (test_bit(WL1271_FLAG_PSM, &wl->flags))
                        ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
        }
 
@@ -1440,22 +1539,6 @@ out:
        return ret;
 }
 
-static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
-{
-       struct ieee80211_supported_band *band;
-       u32 enabled_rates = 0;
-       int bit;
-
-       band = wl->hw->wiphy->bands[wl->band];
-       for (bit = 0; bit < band->n_bitrates; bit++) {
-               if (basic_rate_set & 0x1)
-                       enabled_rates |= band->bitrates[bit].hw_value;
-               basic_rate_set >>= 1;
-       }
-
-       return enabled_rates;
-}
-
 static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       struct ieee80211_bss_conf *bss_conf,
@@ -1473,9 +1556,68 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
+       if ((changed & BSS_CHANGED_BSSID) &&
+           /*
+            * Now we know the correct bssid, so we send a new join command
+            * and enable the BSSID filter
+            */
+           memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
+                       wl->rx_config |= CFG_BSSID_FILTER_EN;
+                       memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
+                       ret = wl1271_cmd_build_null_data(wl);
+                       if (ret < 0) {
+                               wl1271_warning("cmd buld null data failed %d",
+                                              ret);
+                               goto out_sleep;
+                       }
+                       ret = wl1271_cmd_join(wl);
+                       if (ret < 0) {
+                               wl1271_warning("cmd join failed %d", ret);
+                               goto out_sleep;
+                       }
+                       set_bit(WL1271_FLAG_JOINED, &wl->flags);
+       }
+
+       if (wl->bss_type == BSS_TYPE_IBSS) {
+               /* FIXME: This implements rudimentary ad-hoc support -
+                  proper templates are on the wish list and notification
+                  on when they change. This patch will update the templates
+                  on every call to this function. Also, the firmware will not
+                  answer to probe-requests as it does not have the proper
+                  SSID set in the JOIN command. The probe-response template
+                  is set nevertheless, as the FW will ASSERT without it */
+               struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+
+               if (beacon) {
+                       struct ieee80211_hdr *hdr;
+                       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
+                                                     beacon->data,
+                                                     beacon->len);
+
+                       if (ret < 0) {
+                               dev_kfree_skb(beacon);
+                               goto out_sleep;
+                       }
+
+                       hdr = (struct ieee80211_hdr *) beacon->data;
+                       hdr->frame_control = cpu_to_le16(
+                               IEEE80211_FTYPE_MGMT |
+                               IEEE80211_STYPE_PROBE_RESP);
+
+                       ret = wl1271_cmd_template_set(wl,
+                                                     CMD_TEMPL_PROBE_RESPONSE,
+                                                     beacon->data,
+                                                     beacon->len);
+                       dev_kfree_skb(beacon);
+                       if (ret < 0)
+                               goto out_sleep;
+               }
+       }
+
        if (changed & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        wl->aid = bss_conf->aid;
+                       set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
 
                        /*
                         * with wl1271, we don't need to update the
@@ -1492,7 +1634,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                                goto out_sleep;
 
                        /* If we want to go in PSM but we're not there yet */
-                       if (wl->psm_requested && !wl->psm) {
+                       if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
+                           !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
                                mode = STATION_POWER_SAVE_MODE;
                                ret = wl1271_ps_set_mode(wl, mode);
                                if (ret < 0)
@@ -1500,7 +1643,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                        }
                } else {
                        /* use defaults when not associated */
-                       wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
+                       clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
                        wl->aid = 0;
                }
 
@@ -1535,17 +1678,6 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                }
        }
 
-       if (changed & BSS_CHANGED_BASIC_RATES) {
-               wl->basic_rate_set = wl1271_enabled_rates_get(
-                       wl, bss_conf->basic_rates);
-
-               ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
-               if (ret < 0) {
-                       wl1271_warning("Set rate policies failed %d", ret);
-                       goto out_sleep;
-               }
-       }
-
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 
@@ -1599,19 +1731,19 @@ static struct ieee80211_rate wl1271_rates[] = {
 
 /* can't be const, mac80211 writes to this */
 static struct ieee80211_channel wl1271_channels[] = {
-       { .hw_value = 1, .center_freq = 2412},
-       { .hw_value = 2, .center_freq = 2417},
-       { .hw_value = 3, .center_freq = 2422},
-       { .hw_value = 4, .center_freq = 2427},
-       { .hw_value = 5, .center_freq = 2432},
-       { .hw_value = 6, .center_freq = 2437},
-       { .hw_value = 7, .center_freq = 2442},
-       { .hw_value = 8, .center_freq = 2447},
-       { .hw_value = 9, .center_freq = 2452},
-       { .hw_value = 10, .center_freq = 2457},
-       { .hw_value = 11, .center_freq = 2462},
-       { .hw_value = 12, .center_freq = 2467},
-       { .hw_value = 13, .center_freq = 2472},
+       { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
+       { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
+       { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
+       { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
+       { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
+       { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
+       { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
+       { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
+       { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
+       { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
+       { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
+       { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
+       { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
 };
 
 /* can't be const, mac80211 writes to this */
@@ -1757,7 +1889,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_BEACON_FILTER |
                IEEE80211_HW_SUPPORTS_PS;
 
-       wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+       wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_ADHOC);
        wl->hw->wiphy->max_scan_ssids = 1;
        wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
 
@@ -1818,21 +1951,18 @@ static int __devinit wl1271_probe(struct spi_device *spi)
 
        INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
        wl->channel = WL1271_DEFAULT_CHANNEL;
-       wl->scanning = false;
        wl->default_key = 0;
        wl->rx_counter = 0;
        wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
        wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
-       wl->elp = false;
-       wl->psm = 0;
-       wl->psm_requested = false;
        wl->psm_entry_retry = 0;
-       wl->tx_queue_stopped = false;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
-       wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
+       wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
+       wl->rate_set = CONF_TX_RATE_MASK_BASIC;
+       wl->sta_rate_set = 0;
        wl->band = IEEE80211_BAND_2GHZ;
        wl->vif = NULL;
-       wl->joined = false;
+       wl->flags = 0;
 
        for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
                wl->tx_frames[i] = NULL;
index 507cd91d7eed3418a2c69e57fe98410b23031242..e407790f677131c2a38781b48dff31ab94bd3e63 100644 (file)
@@ -39,12 +39,13 @@ void wl1271_elp_work(struct work_struct *work)
 
        mutex_lock(&wl->mutex);
 
-       if (wl->elp || !wl->psm)
+       if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) ||
+           !test_bit(WL1271_FLAG_PSM, &wl->flags))
                goto out;
 
        wl1271_debug(DEBUG_PSM, "chip to elp");
        wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
-       wl->elp = true;
+       set_bit(WL1271_FLAG_IN_ELP, &wl->flags);
 
 out:
        mutex_unlock(&wl->mutex);
@@ -55,7 +56,7 @@ out:
 /* Routines to toggle sleep mode while in ELP */
 void wl1271_ps_elp_sleep(struct wl1271 *wl)
 {
-       if (wl->psm) {
+       if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
                cancel_delayed_work(&wl->elp_work);
                ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
                                        msecs_to_jiffies(ELP_ENTRY_DELAY));
@@ -70,7 +71,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
        u32 start_time = jiffies;
        bool pending = false;
 
-       if (!wl->elp)
+       if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
                return 0;
 
        wl1271_debug(DEBUG_PSM, "waking up chip from elp");
@@ -101,7 +102,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake)
                }
        }
 
-       wl->elp = false;
+       clear_bit(WL1271_FLAG_IN_ELP, &wl->flags);
 
        wl1271_debug(DEBUG_PSM, "wakeup time: %u ms",
                     jiffies_to_msecs(jiffies - start_time));
@@ -143,7 +144,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
                if (ret < 0)
                        return ret;
 
-               wl->psm = 1;
+               set_bit(WL1271_FLAG_PSM, &wl->flags);
                break;
        case STATION_ACTIVE_MODE:
        default:
@@ -166,7 +167,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
                if (ret < 0)
                        return ret;
 
-               wl->psm = 0;
+               clear_bit(WL1271_FLAG_PSM, &wl->flags);
                break;
        }
 
index 02978a16e73232c87b5af0921ae1514ee54cdadc..ee9564aa6ecc0851bf5f3daabe05588935765286 100644 (file)
@@ -397,8 +397,7 @@ u16 wl1271_top_reg_read(struct wl1271 *wl, int addr)
        /* poll for data ready */
        do {
                val = wl1271_spi_read32(wl, OCP_DATA_READ);
-               timeout--;
-       } while (!(val & OCP_READY_MASK) && timeout);
+       } while (!(val & OCP_READY_MASK) && --timeout);
 
        if (!timeout) {
                wl1271_warning("Top register access timed out.");
index 00af065c77c20e14b5dc5f79dc86ef092054515d..a288cc317d7b076d51f63f7f51490c56fd30f5d6 100644 (file)
@@ -121,6 +121,11 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
        pad = pad - skb->len;
        tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD;
 
+       /* if the packets are destined for AP (have a STA entry) send them
+          with AP rate policies, otherwise use default basic rates */
+       if (control->control.sta)
+               tx_attr |= ACX_TX_AP_FULL_RATE << TX_HW_ATTR_OFST_RATE_POLICY;
+
        desc->tx_attr = cpu_to_le16(tx_attr);
 
        wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
@@ -214,18 +219,50 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
        return ret;
 }
 
+static u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
+{
+       struct ieee80211_supported_band *band;
+       u32 enabled_rates = 0;
+       int bit;
+
+       band = wl->hw->wiphy->bands[wl->band];
+       for (bit = 0; bit < band->n_bitrates; bit++) {
+               if (rate_set & 0x1)
+                       enabled_rates |= band->bitrates[bit].hw_value;
+               rate_set >>= 1;
+       }
+
+       return enabled_rates;
+}
+
 void wl1271_tx_work(struct work_struct *work)
 {
        struct wl1271 *wl = container_of(work, struct wl1271, tx_work);
        struct sk_buff *skb;
        bool woken_up = false;
+       u32 sta_rates = 0;
        int ret;
 
+       /* check if the rates supported by the AP have changed */
+       if (unlikely(test_and_clear_bit(WL1271_FLAG_STA_RATES_CHANGED,
+                                       &wl->flags))) {
+               unsigned long flags;
+               spin_lock_irqsave(&wl->wl_lock, flags);
+               sta_rates = wl->sta_rate_set;
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+       }
+
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
+       /* if rates have changed, re-configure the rate policy */
+       if (unlikely(sta_rates)) {
+               wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
+               wl1271_acx_rate_policies(wl);
+       }
+
        while ((skb = skb_dequeue(&wl->tx_queue))) {
                if (!woken_up) {
                        ret = wl1271_ps_elp_wakeup(wl, false);
@@ -240,18 +277,18 @@ void wl1271_tx_work(struct work_struct *work)
                        wl1271_debug(DEBUG_TX, "tx_work: fw buffer full, "
                                     "stop queues");
                        ieee80211_stop_queues(wl->hw);
-                       wl->tx_queue_stopped = true;
+                       set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
                        skb_queue_head(&wl->tx_queue, skb);
                        goto out;
                } else if (ret < 0) {
                        dev_kfree_skb(skb);
                        goto out;
-               } else if (wl->tx_queue_stopped) {
+               } else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED,
+                                             &wl->flags)) {
                        /* firmware buffer has space, restart queues */
                        wl1271_debug(DEBUG_TX,
                                     "complete_packet: waking queues");
                        ieee80211_wake_queues(wl->hw);
-                       wl->tx_queue_stopped = false;
                }
        }
 
index 9d9b263733e65efdfbdb259fb016b511614ffcd0..d90f0a25b9cf46b41c603a99ea1504fc0277f78c 100644 (file)
@@ -869,7 +869,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
 }
 
 static int zd_op_add_interface(struct ieee80211_hw *hw,
-                               struct ieee80211_if_init_conf *conf)
+                               struct ieee80211_vif *vif)
 {
        struct zd_mac *mac = zd_hw_mac(hw);
 
@@ -877,22 +877,22 @@ static int zd_op_add_interface(struct ieee80211_hw *hw,
        if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
                return -EOPNOTSUPP;
 
-       switch (conf->type) {
+       switch (vif->type) {
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
-               mac->type = conf->type;
+               mac->type = vif->type;
                break;
        default:
                return -EOPNOTSUPP;
        }
 
-       return zd_write_mac_addr(&mac->chip, conf->mac_addr);
+       return zd_write_mac_addr(&mac->chip, vif->addr);
 }
 
 static void zd_op_remove_interface(struct ieee80211_hw *hw,
-                                   struct ieee80211_if_init_conf *conf)
+                                   struct ieee80211_vif *vif)
 {
        struct zd_mac *mac = zd_hw_mac(hw);
        mac->type = NL80211_IFTYPE_UNSPECIFIED;
index ac19ecd19cfe07cb9e42d3bdb8f39e0fa226a7e0..4daf1c94ec04ee5b5b48495d57e5d7563081faad 100644 (file)
@@ -1078,11 +1078,15 @@ static int eject_installer(struct usb_interface *intf)
        int r;
 
        /* Find bulk out endpoint */
-       endpoint = &iface_desc->endpoint[1].desc;
-       if (usb_endpoint_dir_out(endpoint) &&
-           usb_endpoint_xfer_bulk(endpoint)) {
-               bulk_out_ep = endpoint->bEndpointAddress;
-       } else {
+       for (r = 1; r >= 0; r--) {
+               endpoint = &iface_desc->endpoint[r].desc;
+               if (usb_endpoint_dir_out(endpoint) &&
+                   usb_endpoint_xfer_bulk(endpoint)) {
+                       bulk_out_ep = endpoint->bEndpointAddress;
+                       break;
+               }
+       }
+       if (r == -1) {
                dev_err(&udev->dev,
                        "zd1211rw: Could not find bulk out endpoint\n");
                return -ENODEV;
index 163c840437d604ca9993b3fc70e3349a3fabd199..aeea282bd2fe697d1dd16e86de747ba754b5a974 100644 (file)
@@ -707,6 +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 @@ 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 @@ 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
@@ -839,6 +847,7 @@ struct ieee80211_ht_cap {
 /* 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 @@ 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
@@ -1071,12 +1085,12 @@ enum ieee80211_eid {
        WLAN_EID_TIM = 5,
        WLAN_EID_IBSS_PARAMS = 6,
        WLAN_EID_CHALLENGE = 16,
-       /* 802.11d */
+
        WLAN_EID_COUNTRY = 7,
        WLAN_EID_HP_PARAMS = 8,
        WLAN_EID_HP_TABLE = 9,
        WLAN_EID_REQUEST = 10,
-       /* 802.11e */
+
        WLAN_EID_QBSS_LOAD = 11,
        WLAN_EID_EDCA_PARAM_SET = 12,
        WLAN_EID_TSPEC = 13,
@@ -1099,7 +1113,7 @@ enum ieee80211_eid {
        WLAN_EID_PREP = 69,
        WLAN_EID_PERR = 70,
        WLAN_EID_RANN = 49,     /* compatible with FreeBSD */
-       /* 802.11h */
+
        WLAN_EID_PWR_CONSTRAINT = 32,
        WLAN_EID_PWR_CAPABILITY = 33,
        WLAN_EID_TPC_REQUEST = 34,
@@ -1110,20 +1124,41 @@ enum ieee80211_eid {
        WLAN_EID_MEASURE_REPORT = 39,
        WLAN_EID_QUIET = 40,
        WLAN_EID_IBSS_DFS = 41,
-       /* 802.11g */
+
        WLAN_EID_ERP_INFO = 42,
        WLAN_EID_EXT_SUPP_RATES = 50,
-       /* 802.11n */
+
        WLAN_EID_HT_CAPABILITY = 45,
        WLAN_EID_HT_INFORMATION = 61,
-       /* 802.11i */
+
        WLAN_EID_RSN = 48,
-       WLAN_EID_TIMEOUT_INTERVAL = 56,
-       WLAN_EID_MMIE = 76 /* 802.11w */,
+       WLAN_EID_MMIE = 76,
        WLAN_EID_WPA = 221,
        WLAN_EID_GENERIC = 221,
        WLAN_EID_VENDOR_SPECIFIC = 221,
-       WLAN_EID_QOS_PARAMETER = 222
+       WLAN_EID_QOS_PARAMETER = 222,
+
+       WLAN_EID_AP_CHAN_REPORT = 51,
+       WLAN_EID_NEIGHBOR_REPORT = 52,
+       WLAN_EID_RCPI = 53,
+       WLAN_EID_BSS_AVG_ACCESS_DELAY = 63,
+       WLAN_EID_ANTENNA_INFO = 64,
+       WLAN_EID_RSNI = 65,
+       WLAN_EID_MEASUREMENT_PILOT_TX_INFO = 66,
+       WLAN_EID_BSS_AVAILABLE_CAPACITY = 67,
+       WLAN_EID_BSS_AC_ACCESS_DELAY = 68,
+       WLAN_EID_RRM_ENABLED_CAPABILITIES = 70,
+       WLAN_EID_MULTIPLE_BSSID = 71,
+
+       WLAN_EID_MOBILITY_DOMAIN = 54,
+       WLAN_EID_FAST_BSS_TRANSITION = 55,
+       WLAN_EID_TIMEOUT_INTERVAL = 56,
+       WLAN_EID_RIC_DATA = 57,
+       WLAN_EID_RIC_DESCRIPTOR = 75,
+
+       WLAN_EID_DSE_REGISTERED_LOCATION = 58,
+       WLAN_EID_SUPPORTED_REGULATORY_CLASSES = 59,
+       WLAN_EID_EXT_CHANSWITCH_ANN = 60,
 };
 
 /* Action category code */
@@ -1150,6 +1185,18 @@ enum ieee80211_spectrum_mgmt_actioncode {
        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,
index da8ea2e192736d66e07bfd6aa866bbf51a4dbb14..2bfbe88837ef75923ea496ea04cb19c6b19202fd 100644 (file)
  * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices
  *     associated with this wiphy must be down and will follow.
  *
+ * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified
+ *     channel for the specified amount of time. This can be used to do
+ *     off-channel operations like transmit a Public Action frame and wait for
+ *     a response while being associated to an AP on another channel.
+ *     %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which
+ *     radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ *     frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
+ *     optionally used to specify additional channel parameters.
+ *     %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
+ *     to remain on the channel. This command is also used as an event to
+ *     notify when the requested duration starts (it may take a while for the
+ *     driver to schedule this time due to other concurrent needs for the
+ *     radio).
+ *     When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ *     that will be included with any events pertaining to this request;
+ *     the cookie is also used to cancel the request.
+ * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
+ *     pending remain-on-channel duration if the desired operation has been
+ *     completed prior to expiration of the originally requested duration.
+ *     %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
+ *     radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
+ *     uniquely identify the request.
+ *     This command is also used as an event to notify when a requested
+ *     remain-on-channel duration has expired.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -353,6 +378,9 @@ enum nl80211_commands {
        NL80211_CMD_DEL_PMKSA,
        NL80211_CMD_FLUSH_PMKSA,
 
+       NL80211_CMD_REMAIN_ON_CHANNEL,
+       NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -606,6 +634,10 @@ enum nl80211_commands {
  * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
  *     cache, a wiphy attribute.
  *
+ * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ *
+ * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -743,6 +775,10 @@ enum nl80211_attrs {
        NL80211_ATTR_PMKID,
        NL80211_ATTR_MAX_NUM_PMKIDS,
 
+       NL80211_ATTR_DURATION,
+
+       NL80211_ATTR_COOKIE,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
index 0884b9a0f778df6d929ceafc219545d22538e9cf..add79930f47db5c90005c3ce9557f158584d641e 100644 (file)
@@ -988,6 +988,15 @@ struct cfg80211_pmksa {
  *
  * @dump_survey: get site survey information.
  *
+ * @remain_on_channel: Request the driver to remain awake on the specified
+ *     channel for the specified duration to complete an off-channel
+ *     operation (e.g., public action frame exchange). When the driver is
+ *     ready on the requested channel, it must indicate this with an event
+ *     notification by calling cfg80211_ready_on_channel().
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
+ *     This allows the operation to be terminated prior to timeout based on
+ *     the duration value.
+ *
  * @testmode_cmd: run a test mode command
  *
  * @set_pmksa: Cache a PMKID for a BSSID. This is mostly useful for fullmac
@@ -1123,6 +1132,16 @@ struct cfg80211_ops {
                             struct cfg80211_pmksa *pmksa);
        int     (*flush_pmksa)(struct wiphy *wiphy, struct net_device *netdev);
 
+       int     (*remain_on_channel)(struct wiphy *wiphy,
+                                    struct net_device *dev,
+                                    struct ieee80211_channel *chan,
+                                    enum nl80211_channel_type channel_type,
+                                    unsigned int duration,
+                                    u64 *cookie);
+       int     (*cancel_remain_on_channel)(struct wiphy *wiphy,
+                                           struct net_device *dev,
+                                           u64 cookie);
+
        /* some temporary stuff to finish wext */
        int     (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
                                  bool enabled, int timeout);
@@ -1578,7 +1597,7 @@ unsigned int ieee80211_hdrlen(__le16 fc);
  * @addr: the device MAC address
  * @iftype: the virtual interface type
  */
-int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
+int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                           enum nl80211_iftype iftype);
 
 /**
@@ -1589,9 +1608,27 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
  * @bssid: the network bssid (used only for iftype STATION and ADHOC)
  * @qos: build 802.11 QoS data frame
  */
-int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
+int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
                             enum nl80211_iftype iftype, u8 *bssid, bool qos);
 
+/**
+ * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame
+ *
+ * Decode an IEEE 802.11n A-MSDU frame and convert it to a list of
+ * 802.3 frames. The @list will be empty if the decode fails. The
+ * @skb is consumed after the function returns.
+ *
+ * @skb: The input IEEE 802.11n A-MSDU frame.
+ * @list: The output list of 802.3 frames. It must be allocated and
+ *     initialized by by the caller.
+ * @addr: The device MAC address.
+ * @iftype: The device interface type.
+ * @extra_headroom: The hardware extra headroom for SKBs in the @list.
+ */
+void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+                             const u8 *addr, enum nl80211_iftype iftype,
+                             const unsigned int extra_headroom);
+
 /**
  * cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
  * @skb: the data frame
@@ -2129,5 +2166,45 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
                           u8 *ie, size_t ie_len, gfp_t gfp);
 
+/**
+ * cfg80211_ready_on_channel - notification of remain_on_channel start
+ * @dev: network device
+ * @cookie: the request cookie
+ * @chan: The current channel (from remain_on_channel request)
+ * @channel_type: Channel type
+ * @duration: Duration in milliseconds that the driver intents to remain on the
+ *     channel
+ * @gfp: allocation flags
+ */
+void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
+                              struct ieee80211_channel *chan,
+                              enum nl80211_channel_type channel_type,
+                              unsigned int duration, gfp_t gfp);
+
+/**
+ * cfg80211_remain_on_channel_expired - remain_on_channel duration expired
+ * @dev: network device
+ * @cookie: the request cookie
+ * @chan: The current channel (from remain_on_channel request)
+ * @channel_type: Channel type
+ * @gfp: allocation flags
+ */
+void cfg80211_remain_on_channel_expired(struct net_device *dev,
+                                       u64 cookie,
+                                       struct ieee80211_channel *chan,
+                                       enum nl80211_channel_type channel_type,
+                                       gfp_t gfp);
+
+
+/**
+ * cfg80211_new_sta - notify userspace about station
+ *
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @sinfo: the station information
+ * @gfp: allocation flags
+ */
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+                     struct station_info *sinfo, gfp_t gfp);
 
 #endif /* __NET_CFG80211_H */
index 0bf369752274f39658ded037e961900895431058..f073a2a5057461ecadf3b2349a44fec7c4e08941 100644 (file)
@@ -595,8 +595,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),
@@ -606,6 +608,21 @@ enum ieee80211_conf_changed {
        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
  *
@@ -634,6 +651,10 @@ enum ieee80211_conf_changed {
  * @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;
@@ -646,6 +667,7 @@ struct ieee80211_conf {
 
        struct ieee80211_channel *channel;
        enum nl80211_channel_type channel_type;
+       enum ieee80211_smps_mode smps_mode;
 };
 
 /**
@@ -657,12 +679,14 @@ struct ieee80211_conf {
  * @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 *))));
 };
@@ -675,33 +699,6 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
        return false;
 }
 
-/**
- * struct ieee80211_if_init_conf - initial configuration of an interface
- *
- * @vif: pointer to a driver-use per-interface structure. The pointer
- *     itself is also used for various functions including
- *     ieee80211_beacon_get() and ieee80211_get_buffered_bc().
- * @type: one of &enum nl80211_iftype constants. Determines the type of
- *     added/removed interface.
- * @mac_addr: pointer to MAC address of the interface. This pointer is valid
- *     until the interface is removed (i.e. it cannot be used after
- *     remove_interface() callback was called for this interface).
- *
- * This structure is used in add_interface() and remove_interface()
- * callbacks of &struct ieee80211_hw.
- *
- * When you allow multiple interfaces to be added to your PHY, take care
- * that the hardware can actually handle multiple MAC addresses. However,
- * also take care that when there's no interface left with mac_addr != %NULL
- * you remove the MAC address from the device to avoid acknowledging packets
- * in pure monitor mode.
- */
-struct ieee80211_if_init_conf {
-       enum nl80211_iftype type;
-       struct ieee80211_vif *vif;
-       void *mac_addr;
-};
-
 /**
  * enum ieee80211_key_alg - key algorithm
  * @ALG_WEP: WEP40 or WEP104
@@ -926,6 +923,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,
@@ -943,6 +950,8 @@ enum ieee80211_hw_flags {
        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,
 };
 
 /**
@@ -1210,6 +1219,31 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
  * 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
  *
@@ -1347,7 +1381,7 @@ enum ieee80211_ampdu_mlme_action {
  *     When the device is started it should not have a MAC address
  *     to avoid acknowledging frames before a non-monitor device
  *     is added.
- *     Must be implemented.
+ *     Must be implemented and can sleep.
  *
  * @stop: Called after last netdevice attached to the hardware
  *     is disabled. This should turn off the hardware (at least
@@ -1355,7 +1389,7 @@ enum ieee80211_ampdu_mlme_action {
  *     May be called right after add_interface if that rejects
  *     an interface. If you added any work onto the mac80211 workqueue
  *     you should ensure to cancel it on this callback.
- *     Must be implemented.
+ *     Must be implemented and can sleep.
  *
  * @add_interface: Called when a netdevice attached to the hardware is
  *     enabled. Because it is not called for monitor mode devices, @start
@@ -1365,7 +1399,7 @@ enum ieee80211_ampdu_mlme_action {
  *     interface is given in the conf parameter.
  *     The callback may refuse to add an interface by returning a
  *     negative error code (which will be seen in userspace.)
- *     Must be implemented.
+ *     Must be implemented and can sleep.
  *
  * @remove_interface: Notifies a driver that an interface is going down.
  *     The @stop callback is called after this if it is the last interface
@@ -1374,19 +1408,20 @@ enum ieee80211_ampdu_mlme_action {
  *     must be cleared so the device no longer acknowledges packets,
  *     the mac_addr member of the conf structure is, however, set to the
  *     MAC address of the device going away.
- *     Hence, this callback must be implemented.
+ *     Hence, this callback must be implemented. It can sleep.
  *
  * @config: Handler for configuration requests. IEEE 802.11 code calls this
  *     function to change hardware configuration, e.g., channel.
  *     This function should never fail but returns a negative error code
- *     if it does.
+ *     if it does. The callback can sleep.
  *
  * @bss_info_changed: Handler for configuration requests related to BSS
  *     parameters that may vary during BSS's lifespan, and may affect low
  *     level driver (e.g. assoc/disassoc status, erp parameters).
  *     This function should not be used if no BSS has been set, unless
  *     for association indication. The @changed parameter indicates which
- *     of the bss parameters has changed when a call is made.
+ *     of the bss parameters has changed when a call is made. The callback
+ *     can sleep.
  *
  * @prepare_multicast: Prepare for multicast filter configuration.
  *     This callback is optional, and its return value is passed
@@ -1394,20 +1429,22 @@ enum ieee80211_ampdu_mlme_action {
  *
  * @configure_filter: Configure the device's RX filter.
  *     See the section "Frame filtering" for more information.
- *     This callback must be implemented.
+ *     This callback must be implemented and can sleep.
  *
  * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit
  *     must be set or cleared for a given STA. Must be atomic.
  *
  * @set_key: See the section "Hardware crypto acceleration"
- *     This callback can sleep, and is only called between add_interface
- *     and remove_interface calls, i.e. while the given virtual interface
+ *     This callback is only called between add_interface and
+ *     remove_interface calls, i.e. while the given virtual interface
  *     is enabled.
  *     Returns a negative error code if the key can't be added.
+ *     The callback can sleep.
  *
  * @update_tkip_key: See the section "Hardware crypto acceleration"
  *     This callback will be called in the context of Rx. Called for drivers
  *     which set IEEE80211_KEY_FLAG_TKIP_REQ_RX_P1_KEY.
+ *     The callback can sleep.
  *
  * @hw_scan: Ask the hardware to service the scan request, no need to start
  *     the scan state machine in stack. The scan must honour the channel
@@ -1421,21 +1458,28 @@ enum ieee80211_ampdu_mlme_action {
  *     When the scan finishes, ieee80211_scan_completed() must be called;
  *     note that it also must be called when the scan cannot finish due to
  *     any error unless this callback returned a negative error code.
+ *     The callback can sleep.
  *
  * @sw_scan_start: Notifier function that is called just before a software scan
  *     is started. Can be NULL, if the driver doesn't need this notification.
+ *     The callback can sleep.
  *
- * @sw_scan_complete: Notifier function that is called just after a software scan
- *     finished. Can be NULL, if the driver doesn't need this notification.
+ * @sw_scan_complete: Notifier function that is called just after a
+ *     software scan finished. Can be NULL, if the driver doesn't need
+ *     this notification.
+ *     The callback can sleep.
  *
  * @get_stats: Return low-level statistics.
  *     Returns zero if statistics are available.
+ *     The callback can sleep.
  *
  * @get_tkip_seq: If your device implements TKIP encryption in hardware this
  *     callback should be provided to read the TKIP transmit IVs (both IV32
  *     and IV16) for the given key from hardware.
+ *     The callback must be atomic.
  *
  * @set_rts_threshold: Configuration of RTS threshold (if device needs it)
+ *     The callback can sleep.
  *
  * @sta_notify: Notifies low level driver about addition, removal or power
  *     state transition of an associated station, AP,  IBSS/WDS/mesh peer etc.
@@ -1444,30 +1488,36 @@ enum ieee80211_ampdu_mlme_action {
  * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
  *     bursting) for a hardware TX queue.
  *     Returns a negative error code on failure.
+ *     The callback can sleep.
  *
  * @get_tx_stats: Get statistics of the current TX queue status. This is used
  *     to get number of currently queued packets (queue length), maximum queue
  *     size (limit), and total number of packets sent using each TX queue
  *     (count). The 'stats' pointer points to an array that has hw->queues
  *     items.
+ *     The callback must be atomic.
  *
  * @get_tsf: Get the current TSF timer value from firmware/hardware. Currently,
  *     this is only used for IBSS mode BSSID merging and debugging. Is not a
  *     required function.
+ *     The callback can sleep.
  *
  * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
  *      Currently, this is only used for IBSS mode debugging. Is not a
  *     required function.
+ *     The callback can sleep.
  *
  * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize
  *     with other STAs in the IBSS. This is only used in IBSS mode. This
  *     function is optional if the firmware/hardware takes full care of
  *     TSF synchronization.
+ *     The callback can sleep.
  *
  * @tx_last_beacon: Determine whether the last IBSS beacon was sent by us.
  *     This is needed only for IBSS mode and the result of this function is
  *     used to determine whether to reply to Probe Requests.
  *     Returns non-zero if this device sent the last beacon.
+ *     The callback can sleep.
  *
  * @ampdu_action: Perform a certain A-MPDU action
  *     The RA/TID combination determines the destination and TID we want
@@ -1476,21 +1526,28 @@ enum ieee80211_ampdu_mlme_action {
  *     is the first frame we expect to perform the action on. Notice
  *     that TX/RX_STOP can pass NULL for this parameter.
  *     Returns a negative error code on failure.
+ *     The callback must be atomic.
  *
  * @rfkill_poll: Poll rfkill hardware state. If you need this, you also
  *     need to set wiphy->rfkill_poll to %true before registration,
  *     and need to call wiphy_rfkill_set_hw_state() in the callback.
+ *     The callback can sleep.
  *
  * @testmode_cmd: Implement a cfg80211 test mode command.
+ *     The callback can sleep.
+ *
+ * @flush: Flush all pending frames from the hardware queue, making sure
+ *     that the hardware queues are empty. If the parameter @drop is set
+ *     to %true, pending frames may be dropped. The callback can sleep.
  */
 struct ieee80211_ops {
        int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
        int (*start)(struct ieee80211_hw *hw);
        void (*stop)(struct ieee80211_hw *hw);
        int (*add_interface)(struct ieee80211_hw *hw,
-                            struct ieee80211_if_init_conf *conf);
+                            struct ieee80211_vif *vif);
        void (*remove_interface)(struct ieee80211_hw *hw,
-                                struct ieee80211_if_init_conf *conf);
+                                struct ieee80211_vif *vif);
        int (*config)(struct ieee80211_hw *hw, u32 changed);
        void (*bss_info_changed)(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
@@ -1538,6 +1595,7 @@ struct ieee80211_ops {
 #ifdef CONFIG_NL80211_TESTMODE
        int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
 #endif
+       void (*flush)(struct ieee80211_hw *hw, bool drop);
 };
 
 /**
@@ -1777,7 +1835,7 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
 /**
  * ieee80211_beacon_get_tim - beacon generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @tim_offset: pointer to variable that will receive the TIM IE offset.
  *     Set to 0 if invalid (in non-AP modes).
  * @tim_length: pointer to variable that will receive the TIM IE length,
@@ -1805,7 +1863,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 /**
  * ieee80211_beacon_get - beacon generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  * See ieee80211_beacon_get_tim().
  */
@@ -1818,7 +1876,7 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
 /**
  * ieee80211_rts_get - RTS frame generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @frame: pointer to the frame that is going to be protected by the RTS.
  * @frame_len: the frame length (in octets).
  * @frame_txctl: &struct ieee80211_tx_info of the frame.
@@ -1837,7 +1895,7 @@ void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 /**
  * ieee80211_rts_duration - Get the duration field for an RTS frame
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @frame_len: the length of the frame that is going to be protected by the RTS.
  * @frame_txctl: &struct ieee80211_tx_info of the frame.
  *
@@ -1852,7 +1910,7 @@ __le16 ieee80211_rts_duration(struct ieee80211_hw *hw,
 /**
  * ieee80211_ctstoself_get - CTS-to-self frame generation function
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @frame: pointer to the frame that is going to be protected by the CTS-to-self.
  * @frame_len: the frame length (in octets).
  * @frame_txctl: &struct ieee80211_tx_info of the frame.
@@ -1872,7 +1930,7 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw,
 /**
  * ieee80211_ctstoself_duration - Get the duration field for a CTS-to-self frame
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @frame_len: the length of the frame that is going to be protected by the CTS-to-self.
  * @frame_txctl: &struct ieee80211_tx_info of the frame.
  *
@@ -1888,7 +1946,7 @@ __le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw,
 /**
  * ieee80211_generic_frame_duration - Calculate the duration field for a frame
  * @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  * @frame_len: the length of the frame.
  * @rate: the rate at which the frame is going to be transmitted.
  *
@@ -1903,7 +1961,7 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
 /**
  * ieee80211_get_buffered_bc - accessing buffered broadcast and multicast frames
  * @hw: pointer as obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  * Function for accessing buffered broadcast and multicast frames. If
  * hardware/firmware does not implement buffering of broadcast/multicast
@@ -2071,7 +2129,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *sta, u16 tid);
 
 /**
  * ieee80211_start_tx_ba_cb - low level driver ready to aggregate.
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
  * @ra: receiver address of the BA session recipient.
  * @tid: the TID to BA on.
  *
@@ -2082,7 +2140,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid);
 
 /**
  * ieee80211_start_tx_ba_cb_irqsafe - low level driver ready to aggregate.
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
  * @ra: receiver address of the BA session recipient.
  * @tid: the TID to BA on.
  *
@@ -2110,7 +2168,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *sta, u16 tid,
 
 /**
  * ieee80211_stop_tx_ba_cb - low level driver ready to stop aggregate.
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
  * @ra: receiver address of the BA session recipient.
  * @tid: the desired TID to BA on.
  *
@@ -2121,7 +2179,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
 
 /**
  * ieee80211_stop_tx_ba_cb_irqsafe - low level driver ready to stop aggregate.
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
  * @ra: receiver address of the BA session recipient.
  * @tid: the desired TID to BA on.
  *
@@ -2200,7 +2258,7 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
 /**
  * ieee80211_beacon_loss - inform hardware does not receive beacons
  *
- * @vif: &struct ieee80211_vif pointer from &struct ieee80211_if_init_conf.
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
  *
  * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and
  * IEEE80211_CONF_PS is set, the driver needs to inform whenever the
index a10d508b07e146daabe99ad21306fc99ecdc8c2f..a952b7f8c6482ae76b6a14aed59c5f4c63e113d8 100644 (file)
@@ -96,18 +96,6 @@ menuconfig MAC80211_DEBUG_MENU
        ---help---
          This option collects various mac80211 debug settings.
 
-config MAC80211_DEBUG_PACKET_ALIGNMENT
-       bool "Enable packet alignment debugging"
-       depends on MAC80211_DEBUG_MENU
-       ---help---
-         This option is recommended for driver authors and strongly
-         discouraged for everybody else, it will trigger a warning
-         when a driver hands mac80211 a buffer that is aligned in
-         a way that will cause problems with the IP stack on some
-         architectures.
-
-         Say N unless you're writing a mac80211 based driver.
-
 config MAC80211_NOINLINE
        bool "Do not inline TX/RX handlers"
        depends on MAC80211_DEBUG_MENU
index 298cfcc1bf8d205c78ff826116655652af8247ae..04420291e7ad7b0d85e3456cbe93c5804dd2d6f9 100644 (file)
@@ -6,10 +6,10 @@ mac80211-y := \
        sta_info.o \
        wep.o \
        wpa.o \
-       scan.o \
+       scan.o offchannel.o \
        ht.o agg-tx.o agg-rx.o \
        ibss.o \
-       mlme.o \
+       mlme.o work.o \
        iface.o \
        rate.o \
        michael.o \
index 51c7dc3c4c3bac28e849dfd31fb5501ce858e82e..a978e666ed6f06a79fa4b7529feec540a7ab2908 100644 (file)
@@ -41,8 +41,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
               sta->sta.addr, tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
-       if (drv_ampdu_action(local, &sta->sdata->vif,
-                            IEEE80211_AMPDU_RX_STOP,
+       if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
                             &sta->sta, tid, NULL))
                printk(KERN_DEBUG "HW problem - can not stop rx "
                                "aggregation for tid %d\n", tid);
@@ -83,12 +82,11 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid,
                                        u16 initiator, u16 reason)
 {
-       struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(sdata, ra);
        if (!sta) {
                rcu_read_unlock();
                return;
@@ -136,7 +134,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
 
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer "
-                      "for addba resp frame\n", sdata->dev->name);
+                      "for addba resp frame\n", sdata->name);
                return;
        }
 
@@ -144,10 +142,10 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
        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);
 
@@ -281,8 +279,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                goto end;
        }
 
-       ret = drv_ampdu_action(local, &sta->sdata->vif,
-                              IEEE80211_AMPDU_RX_START,
+       ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
                               &sta->sta, tid, &start_seq_num);
 #ifdef CONFIG_MAC80211_HT_DEBUG
        printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
index 5e3a7eccef5ae4c7a52dd65a3e8f310183582433..5aa8f4a3ed174907ca8ded2f68e7dd66123d643a 100644 (file)
@@ -58,17 +58,17 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
 
        if (!skb) {
                printk(KERN_ERR "%s: failed to allocate buffer "
-                               "for addba request frame\n", sdata->dev->name);
+                               "for addba request 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, 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);
 
@@ -104,7 +104,7 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
        skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom);
        if (!skb) {
                printk(KERN_ERR "%s: failed to allocate buffer for "
-                       "bar frame\n", sdata->dev->name);
+                       "bar frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
@@ -113,7 +113,7 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
        bar->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
                                         IEEE80211_STYPE_BACK_REQ);
        memcpy(bar->ra, ra, ETH_ALEN);
-       memcpy(bar->ta, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(bar->ta, sdata->vif.addr, ETH_ALEN);
        bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL;
        bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA;
        bar_control |= (u16)(tid << 12);
@@ -144,7 +144,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
        *state = HT_AGG_STATE_REQ_STOP_BA_MSK |
                (initiator << HT_AGG_STATE_INITIATOR_SHIFT);
 
-       ret = drv_ampdu_action(local, &sta->sdata->vif,
+       ret = drv_ampdu_action(local, sta->sdata,
                               IEEE80211_AMPDU_TX_STOP,
                               &sta->sta, tid, NULL);
 
@@ -301,10 +301,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
         * call back right away, it must see that the flow has begun */
        *state |= HT_ADDBA_REQUESTED_MSK;
 
-       start_seq_num = sta->tid_seq[tid];
+       start_seq_num = sta->tid_seq[tid] >> 4;
 
-       ret = drv_ampdu_action(local, &sdata->vif,
-                              IEEE80211_AMPDU_TX_START,
+       ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
                               pubsta, tid, &start_seq_num);
 
        if (ret) {
@@ -420,7 +419,7 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
        ieee80211_agg_splice_finish(local, sta, tid);
        spin_unlock(&local->ampdu_lock);
 
-       drv_ampdu_action(local, &sta->sdata->vif,
+       drv_ampdu_action(local, sta->sdata,
                         IEEE80211_AMPDU_TX_OPERATIONAL,
                         &sta->sta, tid, NULL);
 }
@@ -441,7 +440,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
        }
 
        rcu_read_lock();
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(sdata, ra);
        if (!sta) {
                rcu_read_unlock();
 #ifdef CONFIG_MAC80211_HT_DEBUG
@@ -489,7 +488,7 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
 #ifdef CONFIG_MAC80211_HT_DEBUG
                if (net_ratelimit())
                        printk(KERN_WARNING "%s: Not enough memory, "
-                              "dropping start BA session", skb->dev->name);
+                              "dropping start BA session", sdata->name);
 #endif
                return;
        }
@@ -564,7 +563,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
        rcu_read_lock();
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(sdata, ra);
        if (!sta) {
 #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Could not find station: %pM\n", ra);
@@ -621,7 +620,7 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
 #ifdef CONFIG_MAC80211_HT_DEBUG
                if (net_ratelimit())
                        printk(KERN_WARNING "%s: Not enough memory, "
-                              "dropping stop BA session", skb->dev->name);
+                              "dropping stop BA session", sdata->name);
 #endif
                return;
        }
index 6dc3579c0ac5444a0d8811504aefd6c620c9d1f1..2e5e841e9b7bb17b3b123f4b3568490f1b417c21 100644 (file)
@@ -78,17 +78,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
                                  enum nl80211_iftype type, u32 *flags,
                                  struct vif_params *params)
 {
-       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        int ret;
 
-       if (netif_running(dev))
+       if (ieee80211_sdata_running(sdata))
                return -EBUSY;
 
        if (!nl80211_params_check(type, params))
                return -EINVAL;
 
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
        ret = ieee80211_if_change_type(sdata, type);
        if (ret)
                return ret;
@@ -150,7 +148,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        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 +179,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
        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 +226,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        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;
 
@@ -415,15 +413,13 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
 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 +728,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        } 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 +775,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
        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 +796,14 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                                    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;
@@ -847,7 +842,6 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 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;
@@ -856,7 +850,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
        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 +889,6 @@ static int ieee80211_change_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;
@@ -904,7 +897,7 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
 
        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 +1317,50 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
 }
 #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->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)
 {
@@ -1341,6 +1378,11 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
        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 +1398,25 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
 {
        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;
 
@@ -1375,20 +1427,40 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
        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;
+}
+
+static int ieee80211_remain_on_channel(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      struct ieee80211_channel *chan,
+                                      enum nl80211_channel_type channel_type,
+                                      unsigned int duration,
+                                      u64 *cookie)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
+                                             duration, cookie);
+}
+
+static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
+                                             struct net_device *dev,
+                                             u64 cookie)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
 }
 
 struct cfg80211_ops mac80211_config_ops = {
@@ -1437,4 +1509,6 @@ struct cfg80211_ops mac80211_config_ops = {
        CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
        .set_power_mgmt = ieee80211_set_power_mgmt,
        .set_bitrate_mask = ieee80211_set_bitrate_mask,
+       .remain_on_channel = ieee80211_remain_on_channel,
+       .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
 };
index e0f5224630dafede797663bb834ffa017086237a..d12e743cb4e1b7716ddd5beeed9954d43e75c9cb 100644 (file)
@@ -56,7 +56,7 @@ KEY_CONF_FILE(keyidx, D);
 KEY_CONF_FILE(hw_key_idx, D);
 KEY_FILE(flags, X);
 KEY_FILE(tx_rx_count, D);
-KEY_READ(ifindex, sdata->dev->ifindex, 20, "%d\n");
+KEY_READ(ifindex, sdata->name, IFNAMSIZ + 2, "%s\n");
 KEY_OPS(ifindex);
 
 static ssize_t key_algorithm_read(struct file *file,
index 472b2039906c89dbd1081b6c8b2cabc2d8f79b55..59f6e3bcbd09d308e5913673707f287cd146cce9 100644 (file)
@@ -41,6 +41,30 @@ static ssize_t ieee80211_if_read(
        return ret;
 }
 
+static ssize_t ieee80211_if_write(
+       struct ieee80211_sub_if_data *sdata,
+       const char __user *userbuf,
+       size_t count, loff_t *ppos,
+       ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int))
+{
+       u8 *buf;
+       ssize_t ret = -ENODEV;
+
+       buf = kzalloc(count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (copy_from_user(buf, userbuf, count))
+               return -EFAULT;
+
+       rtnl_lock();
+       if (sdata->dev->reg_state == NETREG_REGISTERED)
+               ret = (*write)(sdata, buf, count);
+       rtnl_unlock();
+
+       return ret;
+}
+
 #define IEEE80211_IF_FMT(name, field, format_string)                   \
 static ssize_t ieee80211_if_fmt_##name(                                        \
        const struct ieee80211_sub_if_data *sdata, char *buf,           \
@@ -71,7 +95,7 @@ static ssize_t ieee80211_if_fmt_##name(                                       \
        return scnprintf(buf, buflen, "%pM\n", sdata->field);           \
 }
 
-#define __IEEE80211_IF_FILE(name)                                      \
+#define __IEEE80211_IF_FILE(name, _write)                              \
 static ssize_t ieee80211_if_read_##name(struct file *file,             \
                                        char __user *userbuf,           \
                                        size_t count, loff_t *ppos)     \
@@ -82,12 +106,24 @@ static ssize_t ieee80211_if_read_##name(struct file *file,         \
 }                                                                      \
 static const struct file_operations name##_ops = {                     \
        .read = ieee80211_if_read_##name,                               \
+       .write = (_write),                                              \
        .open = mac80211_open_file_generic,                             \
 }
 
+#define __IEEE80211_IF_FILE_W(name)                                    \
+static ssize_t ieee80211_if_write_##name(struct file *file,            \
+                                        const char __user *userbuf,    \
+                                        size_t count, loff_t *ppos)    \
+{                                                                      \
+       return ieee80211_if_write(file->private_data, userbuf, count,   \
+                                 ppos, ieee80211_if_parse_##name);     \
+}                                                                      \
+__IEEE80211_IF_FILE(name, ieee80211_if_write_##name)
+
+
 #define IEEE80211_IF_FILE(name, field, format)                         \
                IEEE80211_IF_FMT_##format(name, field)                  \
-               __IEEE80211_IF_FILE(name)
+               __IEEE80211_IF_FILE(name, NULL)
 
 /* common attributes */
 IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@@ -97,7 +133,70 @@ IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC);
 /* STA attributes */
 IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
 IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
-IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
+
+static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
+                             enum ieee80211_smps_mode smps_mode)
+{
+       struct ieee80211_local *local = sdata->local;
+       int err;
+
+       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) &&
+           smps_mode == IEEE80211_SMPS_STATIC)
+               return -EINVAL;
+
+       /* auto should be dynamic if in PS mode */
+       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) &&
+           (smps_mode == IEEE80211_SMPS_DYNAMIC ||
+            smps_mode == IEEE80211_SMPS_AUTOMATIC))
+               return -EINVAL;
+
+       /* supported only on managed interfaces for now */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&local->iflist_mtx);
+       err = __ieee80211_request_smps(sdata, smps_mode);
+       mutex_unlock(&local->iflist_mtx);
+
+       return err;
+}
+
+static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
+       [IEEE80211_SMPS_AUTOMATIC] = "auto",
+       [IEEE80211_SMPS_OFF] = "off",
+       [IEEE80211_SMPS_STATIC] = "static",
+       [IEEE80211_SMPS_DYNAMIC] = "dynamic",
+};
+
+static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
+                                    char *buf, int buflen)
+{
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return -EOPNOTSUPP;
+
+       return snprintf(buf, buflen, "request: %s\nused: %s\n",
+                       smps_modes[sdata->u.mgd.req_smps],
+                       smps_modes[sdata->u.mgd.ap_smps]);
+}
+
+static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
+                                      const char *buf, int buflen)
+{
+       enum ieee80211_smps_mode mode;
+
+       for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) {
+               if (strncmp(buf, smps_modes[mode], buflen) == 0) {
+                       int err = ieee80211_set_smps(sdata, mode);
+                       if (!err)
+                               return buflen;
+                       return err;
+               }
+       }
+
+       return -EINVAL;
+}
+
+__IEEE80211_IF_FILE_W(smps);
 
 /* AP attributes */
 IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
@@ -109,7 +208,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
        return scnprintf(buf, buflen, "%u\n",
                         skb_queue_len(&sdata->u.ap.ps_bc_buf));
 }
-__IEEE80211_IF_FILE(num_buffered_multicast);
+__IEEE80211_IF_FILE(num_buffered_multicast, NULL);
 
 /* WDS attributes */
 IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
@@ -158,6 +257,10 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode,
        debugfs_create_file(#name, 0400, sdata->debugfs.dir, \
                            sdata, &name##_ops);
 
+#define DEBUGFS_ADD_MODE(name, mode) \
+       debugfs_create_file(#name, mode, sdata->debugfs.dir, \
+                           sdata, &name##_ops);
+
 static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 {
        DEBUGFS_ADD(drop_unencrypted, sta);
@@ -166,7 +269,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 
        DEBUGFS_ADD(bssid, sta);
        DEBUGFS_ADD(aid, sta);
-       DEBUGFS_ADD(capab, sta);
+       DEBUGFS_ADD_MODE(smps, 0600);
 }
 
 static void add_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -280,16 +383,11 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
        }
 }
 
-static int notif_registered;
-
 void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
 {
        char buf[10+IFNAMSIZ];
 
-       if (!notif_registered)
-               return;
-
-       sprintf(buf, "netdev:%s", sdata->dev->name);
+       sprintf(buf, "netdev:%s", sdata->name);
        sdata->debugfs.dir = debugfs_create_dir(buf,
                sdata->local->hw.wiphy->debugfsdir);
        add_files(sdata);
@@ -304,58 +402,18 @@ void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
        sdata->debugfs.dir = NULL;
 }
 
-static int netdev_notify(struct notifier_block *nb,
-                        unsigned long state,
-                        void *ndev)
+void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata)
 {
-       struct net_device *dev = ndev;
        struct dentry *dir;
-       struct ieee80211_sub_if_data *sdata;
-       char buf[10+IFNAMSIZ];
-
-       if (state != NETDEV_CHANGENAME)
-               return 0;
-
-       if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy)
-               return 0;
-
-       if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
-               return 0;
-
-       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       char buf[10 + IFNAMSIZ];
 
        dir = sdata->debugfs.dir;
 
        if (!dir)
-               return 0;
+               return;
 
-       sprintf(buf, "netdev:%s", dev->name);
+       sprintf(buf, "netdev:%s", sdata->name);
        if (!debugfs_rename(dir->d_parent, dir, dir->d_parent, buf))
                printk(KERN_ERR "mac80211: debugfs: failed to rename debugfs "
                       "dir to %s\n", buf);
-
-       return 0;
-}
-
-static struct notifier_block mac80211_debugfs_netdev_notifier = {
-       .notifier_call = netdev_notify,
-};
-
-void ieee80211_debugfs_netdev_init(void)
-{
-       int err;
-
-       err = register_netdevice_notifier(&mac80211_debugfs_netdev_notifier);
-       if (err) {
-               printk(KERN_ERR
-                      "mac80211: failed to install netdev notifier,"
-                      " disabling per-netdev debugfs!\n");
-       } else
-               notif_registered = 1;
-}
-
-void ieee80211_debugfs_netdev_exit(void)
-{
-       unregister_netdevice_notifier(&mac80211_debugfs_netdev_notifier);
-       notif_registered = 0;
 }
index 7af731f0b73113182c54756b53aa3b0b88a37e1d..79025e79f4d6459dd99de5ad496e351e123f53b7 100644 (file)
@@ -6,8 +6,7 @@
 #ifdef CONFIG_MAC80211_DEBUGFS
 void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata);
 void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata);
-void ieee80211_debugfs_netdev_init(void);
-void ieee80211_debugfs_netdev_exit(void);
+void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata);
 #else
 static inline void ieee80211_debugfs_add_netdev(
        struct ieee80211_sub_if_data *sdata)
@@ -15,10 +14,8 @@ static inline void ieee80211_debugfs_add_netdev(
 static inline void ieee80211_debugfs_remove_netdev(
        struct ieee80211_sub_if_data *sdata)
 {}
-static inline void ieee80211_debugfs_netdev_init(void)
-{}
-
-static inline void ieee80211_debugfs_netdev_exit(void)
+static inline void ieee80211_debugfs_rename_netdev(
+       struct ieee80211_sub_if_data *sdata)
 {}
 #endif
 
index 3f41608c8081c3e59f86acc76e94b91cad8219e8..0d4a759ba72c1ae0e60129dd3d095002f0da9eb9 100644 (file)
@@ -44,7 +44,7 @@ static const struct file_operations sta_ ##name## _ops = {            \
                STA_OPS(name)
 
 STA_FILE(aid, sta.aid, D);
-STA_FILE(dev, sdata->dev->name, S);
+STA_FILE(dev, sdata->name, S);
 STA_FILE(rx_packets, rx_packets, LU);
 STA_FILE(tx_packets, tx_packets, LU);
 STA_FILE(rx_bytes, rx_bytes, LU);
@@ -160,7 +160,12 @@ STA_OPS(agg_status);
 static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
                                size_t count, loff_t *ppos)
 {
-       char buf[200], *p = buf;
+#define PRINT_HT_CAP(_cond, _str) \
+       do { \
+       if (_cond) \
+                       p += scnprintf(p, sizeof(buf)+buf-p, "\t" _str "\n"); \
+       } while (0)
+       char buf[1024], *p = buf;
        int i;
        struct sta_info *sta = file->private_data;
        struct ieee80211_sta_ht_cap *htc = &sta->sta.ht_cap;
@@ -168,15 +173,64 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
        p += scnprintf(p, sizeof(buf) + buf - p, "ht %ssupported\n",
                        htc->ht_supported ? "" : "not ");
        if (htc->ht_supported) {
-               p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.2x\n", htc->cap);
+               p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.4x\n", htc->cap);
+
+               PRINT_HT_CAP((htc->cap & BIT(0)), "RX LDCP");
+               PRINT_HT_CAP((htc->cap & BIT(1)), "HT20/HT40");
+               PRINT_HT_CAP(!(htc->cap & BIT(1)), "HT20");
+
+               PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 0, "Static SM Power Save");
+               PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 1, "Dynamic SM Power Save");
+               PRINT_HT_CAP(((htc->cap >> 2) & 0x3) == 3, "SM Power Save disabled");
+
+               PRINT_HT_CAP((htc->cap & BIT(4)), "RX Greenfield");
+               PRINT_HT_CAP((htc->cap & BIT(5)), "RX HT20 SGI");
+               PRINT_HT_CAP((htc->cap & BIT(6)), "RX HT40 SGI");
+               PRINT_HT_CAP((htc->cap & BIT(7)), "TX STBC");
+
+               PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 0, "No RX STBC");
+               PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 1, "RX STBC 1-stream");
+               PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 2, "RX STBC 2-streams");
+               PRINT_HT_CAP(((htc->cap >> 8) & 0x3) == 3, "RX STBC 3-streams");
+
+               PRINT_HT_CAP((htc->cap & BIT(10)), "HT Delayed Block Ack");
+
+               PRINT_HT_CAP((htc->cap & BIT(11)), "Max AMSDU length: "
+                            "3839 bytes");
+               PRINT_HT_CAP(!(htc->cap & BIT(11)), "Max AMSDU length: "
+                            "7935 bytes");
+
+               /*
+                * For beacons and probe response this would mean the BSS
+                * does or does not allow the usage of DSSS/CCK HT40.
+                * Otherwise it means the STA does or does not use
+                * DSSS/CCK HT40.
+                */
+               PRINT_HT_CAP((htc->cap & BIT(12)), "DSSS/CCK HT40");
+               PRINT_HT_CAP(!(htc->cap & BIT(12)), "No DSSS/CCK HT40");
+
+               /* BIT(13) is reserved */
+
+               PRINT_HT_CAP((htc->cap & BIT(14)), "40 MHz Intolerant");
+
+               PRINT_HT_CAP((htc->cap & BIT(15)), "L-SIG TXOP protection");
+
                p += scnprintf(p, sizeof(buf)+buf-p, "ampdu factor/density: %d/%d\n",
                                htc->ampdu_factor, htc->ampdu_density);
                p += scnprintf(p, sizeof(buf)+buf-p, "MCS mask:");
+
                for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
                        p += scnprintf(p, sizeof(buf)+buf-p, " %.2x",
                                        htc->mcs.rx_mask[i]);
-               p += scnprintf(p, sizeof(buf)+buf-p, "\nMCS rx highest: %d\n",
-                               le16_to_cpu(htc->mcs.rx_highest));
+               p += scnprintf(p, sizeof(buf)+buf-p, "\n");
+
+               /* If not set this is meaningless */
+               if (le16_to_cpu(htc->mcs.rx_highest)) {
+                       p += scnprintf(p, sizeof(buf)+buf-p,
+                                      "MCS rx highest: %d Mbps\n",
+                                      le16_to_cpu(htc->mcs.rx_highest));
+               }
+
                p += scnprintf(p, sizeof(buf)+buf-p, "MCS tx params: %x\n",
                                htc->mcs.tx_params);
        }
index 921dd9c9ff6225c8d7b942053313530ca8a3e666..8757ea73d54418ab6de216375d9002d4edff1227 100644 (file)
@@ -14,6 +14,8 @@ static inline int drv_start(struct ieee80211_local *local)
 {
        int ret;
 
+       might_sleep();
+
        local->started = true;
        smp_mb();
        ret = local->ops->start(&local->hw);
@@ -23,6 +25,8 @@ static inline int drv_start(struct ieee80211_local *local)
 
 static inline void drv_stop(struct ieee80211_local *local)
 {
+       might_sleep();
+
        local->ops->stop(&local->hw);
        trace_drv_stop(local);
 
@@ -36,35 +40,47 @@ static inline void drv_stop(struct ieee80211_local *local)
 }
 
 static inline int drv_add_interface(struct ieee80211_local *local,
-                                   struct ieee80211_if_init_conf *conf)
+                                   struct ieee80211_vif *vif)
 {
-       int ret = local->ops->add_interface(&local->hw, conf);
-       trace_drv_add_interface(local, conf->mac_addr, conf->vif, ret);
+       int ret;
+
+       might_sleep();
+
+       ret = local->ops->add_interface(&local->hw, vif);
+       trace_drv_add_interface(local, vif_to_sdata(vif), ret);
        return ret;
 }
 
 static inline void drv_remove_interface(struct ieee80211_local *local,
-                                       struct ieee80211_if_init_conf *conf)
+                                       struct ieee80211_vif *vif)
 {
-       local->ops->remove_interface(&local->hw, conf);
-       trace_drv_remove_interface(local, conf->mac_addr, conf->vif);
+       might_sleep();
+
+       local->ops->remove_interface(&local->hw, vif);
+       trace_drv_remove_interface(local, vif_to_sdata(vif));
 }
 
 static inline int drv_config(struct ieee80211_local *local, u32 changed)
 {
-       int ret = local->ops->config(&local->hw, changed);
+       int ret;
+
+       might_sleep();
+
+       ret = local->ops->config(&local->hw, changed);
        trace_drv_config(local, changed, ret);
        return ret;
 }
 
 static inline void drv_bss_info_changed(struct ieee80211_local *local,
-                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_sub_if_data *sdata,
                                        struct ieee80211_bss_conf *info,
                                        u32 changed)
 {
+       might_sleep();
+
        if (local->ops->bss_info_changed)
-               local->ops->bss_info_changed(&local->hw, vif, info, changed);
-       trace_drv_bss_info_changed(local, vif, info, changed);
+               local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed);
+       trace_drv_bss_info_changed(local, sdata, info, changed);
 }
 
 static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
@@ -106,12 +122,17 @@ static inline int drv_set_tim(struct ieee80211_local *local,
 }
 
 static inline int drv_set_key(struct ieee80211_local *local,
-                             enum set_key_cmd cmd, struct ieee80211_vif *vif,
+                             enum set_key_cmd cmd,
+                             struct ieee80211_sub_if_data *sdata,
                              struct ieee80211_sta *sta,
                              struct ieee80211_key_conf *key)
 {
-       int ret = local->ops->set_key(&local->hw, cmd, vif, sta, key);
-       trace_drv_set_key(local, cmd, vif, sta, key, ret);
+       int ret;
+
+       might_sleep();
+
+       ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
+       trace_drv_set_key(local, cmd, sdata, sta, key, ret);
        return ret;
 }
 
@@ -120,6 +141,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
                                       const u8 *address, u32 iv32,
                                       u16 *phase1key)
 {
+       might_sleep();
+
        if (local->ops->update_tkip_key)
                local->ops->update_tkip_key(&local->hw, conf, address,
                                            iv32, phase1key);
@@ -129,13 +152,19 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
 static inline int drv_hw_scan(struct ieee80211_local *local,
                              struct cfg80211_scan_request *req)
 {
-       int ret = local->ops->hw_scan(&local->hw, req);
+       int ret;
+
+       might_sleep();
+
+       ret = local->ops->hw_scan(&local->hw, req);
        trace_drv_hw_scan(local, req, ret);
        return ret;
 }
 
 static inline void drv_sw_scan_start(struct ieee80211_local *local)
 {
+       might_sleep();
+
        if (local->ops->sw_scan_start)
                local->ops->sw_scan_start(&local->hw);
        trace_drv_sw_scan_start(local);
@@ -143,6 +172,8 @@ static inline void drv_sw_scan_start(struct ieee80211_local *local)
 
 static inline void drv_sw_scan_complete(struct ieee80211_local *local)
 {
+       might_sleep();
+
        if (local->ops->sw_scan_complete)
                local->ops->sw_scan_complete(&local->hw);
        trace_drv_sw_scan_complete(local);
@@ -153,6 +184,8 @@ static inline int drv_get_stats(struct ieee80211_local *local,
 {
        int ret = -EOPNOTSUPP;
 
+       might_sleep();
+
        if (local->ops->get_stats)
                ret = local->ops->get_stats(&local->hw, stats);
        trace_drv_get_stats(local, stats, ret);
@@ -172,6 +205,9 @@ static inline int drv_set_rts_threshold(struct ieee80211_local *local,
                                        u32 value)
 {
        int ret = 0;
+
+       might_sleep();
+
        if (local->ops->set_rts_threshold)
                ret = local->ops->set_rts_threshold(&local->hw, value);
        trace_drv_set_rts_threshold(local, value, ret);
@@ -179,19 +215,22 @@ static inline int drv_set_rts_threshold(struct ieee80211_local *local,
 }
 
 static inline void drv_sta_notify(struct ieee80211_local *local,
-                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sub_if_data *sdata,
                                  enum sta_notify_cmd cmd,
                                  struct ieee80211_sta *sta)
 {
        if (local->ops->sta_notify)
-               local->ops->sta_notify(&local->hw, vif, cmd, sta);
-       trace_drv_sta_notify(local, vif, cmd, sta);
+               local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta);
+       trace_drv_sta_notify(local, sdata, cmd, sta);
 }
 
 static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
                              const struct ieee80211_tx_queue_params *params)
 {
        int ret = -EOPNOTSUPP;
+
+       might_sleep();
+
        if (local->ops->conf_tx)
                ret = local->ops->conf_tx(&local->hw, queue, params);
        trace_drv_conf_tx(local, queue, params, ret);
@@ -209,6 +248,9 @@ static inline int drv_get_tx_stats(struct ieee80211_local *local,
 static inline u64 drv_get_tsf(struct ieee80211_local *local)
 {
        u64 ret = -1ULL;
+
+       might_sleep();
+
        if (local->ops->get_tsf)
                ret = local->ops->get_tsf(&local->hw);
        trace_drv_get_tsf(local, ret);
@@ -217,6 +259,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local)
 
 static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf)
 {
+       might_sleep();
+
        if (local->ops->set_tsf)
                local->ops->set_tsf(&local->hw, tsf);
        trace_drv_set_tsf(local, tsf);
@@ -224,6 +268,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf)
 
 static inline void drv_reset_tsf(struct ieee80211_local *local)
 {
+       might_sleep();
+
        if (local->ops->reset_tsf)
                local->ops->reset_tsf(&local->hw);
        trace_drv_reset_tsf(local);
@@ -232,6 +278,9 @@ static inline void drv_reset_tsf(struct ieee80211_local *local)
 static inline int drv_tx_last_beacon(struct ieee80211_local *local)
 {
        int ret = 1;
+
+       might_sleep();
+
        if (local->ops->tx_last_beacon)
                ret = local->ops->tx_last_beacon(&local->hw);
        trace_drv_tx_last_beacon(local, ret);
@@ -239,23 +288,34 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local)
 }
 
 static inline int drv_ampdu_action(struct ieee80211_local *local,
-                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_sub_if_data *sdata,
                                   enum ieee80211_ampdu_mlme_action action,
                                   struct ieee80211_sta *sta, u16 tid,
                                   u16 *ssn)
 {
        int ret = -EOPNOTSUPP;
        if (local->ops->ampdu_action)
-               ret = local->ops->ampdu_action(&local->hw, vif, action,
+               ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
                                               sta, tid, ssn);
-       trace_drv_ampdu_action(local, vif, action, sta, tid, ssn, ret);
+       trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, ret);
        return ret;
 }
 
 
 static inline void drv_rfkill_poll(struct ieee80211_local *local)
 {
+       might_sleep();
+
        if (local->ops->rfkill_poll)
                local->ops->rfkill_poll(&local->hw);
 }
+
+static inline void drv_flush(struct ieee80211_local *local, bool drop)
+{
+       might_sleep();
+
+       trace_drv_flush(local, drop);
+       if (local->ops->flush)
+               local->ops->flush(&local->hw, drop);
+}
 #endif /* __MAC80211_DRIVER_OPS */
index ee94ea0c67e9f164c71d359b9c4dbb3812d25541..977cc7528bc693fc525db3eae83812e2e5c5f44f 100644 (file)
@@ -25,10 +25,12 @@ static inline void trace_ ## name(proto) {}
 #define STA_PR_FMT     " sta:%pM"
 #define STA_PR_ARG     __entry->sta_addr
 
-#define VIF_ENTRY      __field(enum nl80211_iftype, vif_type) __field(void *, vif)
-#define VIF_ASSIGN     __entry->vif_type = vif ? vif->type : 0; __entry->vif = vif
-#define VIF_PR_FMT     " vif:%p(%d)"
-#define VIF_PR_ARG     __entry->vif, __entry->vif_type
+#define VIF_ENTRY      __field(enum nl80211_iftype, vif_type) __field(void *, sdata) \
+                       __string(vif_name, sdata->dev ? sdata->dev->name : "<nodev>")
+#define VIF_ASSIGN     __entry->vif_type = sdata->vif.type; __entry->sdata = sdata; \
+                       __assign_str(vif_name, sdata->dev ? sdata->dev->name : "<nodev>")
+#define VIF_PR_FMT     " vif:%s(%d)"
+#define VIF_PR_ARG     __get_str(vif_name), __entry->vif_type
 
 TRACE_EVENT(drv_start,
        TP_PROTO(struct ieee80211_local *local, int ret),
@@ -70,11 +72,10 @@ TRACE_EVENT(drv_stop,
 
 TRACE_EVENT(drv_add_interface,
        TP_PROTO(struct ieee80211_local *local,
-                const u8 *addr,
-                struct ieee80211_vif *vif,
+                struct ieee80211_sub_if_data *sdata,
                 int ret),
 
-       TP_ARGS(local, addr, vif, ret),
+       TP_ARGS(local, sdata, ret),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -86,7 +87,7 @@ TRACE_EVENT(drv_add_interface,
        TP_fast_assign(
                LOCAL_ASSIGN;
                VIF_ASSIGN;
-               memcpy(__entry->addr, addr, 6);
+               memcpy(__entry->addr, sdata->vif.addr, 6);
                __entry->ret = ret;
        ),
 
@@ -97,10 +98,9 @@ TRACE_EVENT(drv_add_interface,
 );
 
 TRACE_EVENT(drv_remove_interface,
-       TP_PROTO(struct ieee80211_local *local,
-                const u8 *addr, struct ieee80211_vif *vif),
+       TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata),
 
-       TP_ARGS(local, addr, vif),
+       TP_ARGS(local, sdata),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -111,7 +111,7 @@ TRACE_EVENT(drv_remove_interface,
        TP_fast_assign(
                LOCAL_ASSIGN;
                VIF_ASSIGN;
-               memcpy(__entry->addr, addr, 6);
+               memcpy(__entry->addr, sdata->vif.addr, 6);
        ),
 
        TP_printk(
@@ -140,6 +140,7 @@ TRACE_EVENT(drv_config,
                __field(u8, short_frame_max_tx_count)
                __field(int, center_freq)
                __field(int, channel_type)
+               __field(int, smps)
        ),
 
        TP_fast_assign(
@@ -155,6 +156,7 @@ TRACE_EVENT(drv_config,
                __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count;
                __entry->center_freq = local->hw.conf.channel->center_freq;
                __entry->channel_type = local->hw.conf.channel_type;
+               __entry->smps = local->hw.conf.smps_mode;
        ),
 
        TP_printk(
@@ -165,11 +167,11 @@ TRACE_EVENT(drv_config,
 
 TRACE_EVENT(drv_bss_info_changed,
        TP_PROTO(struct ieee80211_local *local,
-                struct ieee80211_vif *vif,
+                struct ieee80211_sub_if_data *sdata,
                 struct ieee80211_bss_conf *info,
                 u32 changed),
 
-       TP_ARGS(local, vif, info, changed),
+       TP_ARGS(local, sdata, info, changed),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -293,11 +295,11 @@ TRACE_EVENT(drv_set_tim,
 
 TRACE_EVENT(drv_set_key,
        TP_PROTO(struct ieee80211_local *local,
-                enum set_key_cmd cmd, struct ieee80211_vif *vif,
+                enum set_key_cmd cmd, struct ieee80211_sub_if_data *sdata,
                 struct ieee80211_sta *sta,
                 struct ieee80211_key_conf *key, int ret),
 
-       TP_ARGS(local, cmd, vif, sta, key, ret),
+       TP_ARGS(local, cmd, sdata, sta, key, ret),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -491,11 +493,11 @@ TRACE_EVENT(drv_set_rts_threshold,
 
 TRACE_EVENT(drv_sta_notify,
        TP_PROTO(struct ieee80211_local *local,
-                struct ieee80211_vif *vif,
+                struct ieee80211_sub_if_data *sdata,
                 enum sta_notify_cmd cmd,
                 struct ieee80211_sta *sta),
 
-       TP_ARGS(local, vif, cmd, sta),
+       TP_ARGS(local, sdata, cmd, sta),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -656,12 +658,12 @@ TRACE_EVENT(drv_tx_last_beacon,
 
 TRACE_EVENT(drv_ampdu_action,
        TP_PROTO(struct ieee80211_local *local,
-                struct ieee80211_vif *vif,
+                struct ieee80211_sub_if_data *sdata,
                 enum ieee80211_ampdu_mlme_action action,
                 struct ieee80211_sta *sta, u16 tid,
                 u16 *ssn, int ret),
 
-       TP_ARGS(local, vif, action, sta, tid, ssn, ret),
+       TP_ARGS(local, sdata, action, sta, tid, ssn, ret),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -688,6 +690,27 @@ TRACE_EVENT(drv_ampdu_action,
                LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret
        )
 );
+
+TRACE_EVENT(drv_flush,
+       TP_PROTO(struct ieee80211_local *local, bool drop),
+
+       TP_ARGS(local, drop),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(bool, drop)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->drop = drop;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " drop:%d",
+               LOCAL_PR_ARG, __entry->drop
+       )
+);
 #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index d7dcee68072820764d0910c79e740db052512073..bb677a73b7c9d67a623e962ad9419231bb11e3f4 100644 (file)
@@ -125,7 +125,7 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
 
        if (!skb) {
                printk(KERN_ERR "%s: failed to allocate buffer "
-                                       "for delba frame\n", sdata->dev->name);
+                                       "for delba frame\n", sdata->name);
                return;
        }
 
@@ -133,10 +133,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
        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 +185,50 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
                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 1f2db647bb5ccd3526d8fc40ef3e6aeb3cb68da6..5bcde4c3fba1595569dd1675a18dbe48053d1fb0 100644 (file)
@@ -117,7 +117,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_PROBE_RESP);
        memset(mgmt->da, 0xff, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
        memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
        mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int);
        mgmt->u.beacon.timestamp = cpu_to_le64(tsf);
@@ -187,15 +187,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_bss *bss)
 {
+       struct cfg80211_bss *cbss =
+               container_of((void *)bss, struct cfg80211_bss, priv);
        struct ieee80211_supported_band *sband;
        u32 basic_rates;
        int i, j;
-       u16 beacon_int = bss->cbss.beacon_interval;
+       u16 beacon_int = cbss->beacon_interval;
 
        if (beacon_int < 10)
                beacon_int = 10;
 
-       sband = sdata->local->hw.wiphy->bands[bss->cbss.channel->band];
+       sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
 
        basic_rates = 0;
 
@@ -212,12 +214,12 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       __ieee80211_sta_join_ibss(sdata, bss->cbss.bssid,
+       __ieee80211_sta_join_ibss(sdata, cbss->bssid,
                                  beacon_int,
-                                 bss->cbss.channel,
+                                 cbss->channel,
                                  basic_rates,
-                                 bss->cbss.capability,
-                                 bss->cbss.tsf);
+                                 cbss->capability,
+                                 cbss->tsf);
 }
 
 static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -229,6 +231,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        int freq;
+       struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        struct sta_info *sta;
        struct ieee80211_channel *channel;
@@ -252,7 +255,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 
                rcu_read_lock();
 
-               sta = sta_info_get(local, mgmt->sa);
+               sta = sta_info_get(sdata, mgmt->sa);
                if (sta) {
                        u32 prev_rates;
 
@@ -266,7 +269,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                                printk(KERN_DEBUG "%s: updated supp_rates set "
                                    "for %pM based on beacon info (0x%llx | "
                                    "0x%llx -> 0x%llx)\n",
-                                   sdata->dev->name,
+                                   sdata->name,
                                    sta->sta.addr,
                                    (unsigned long long) prev_rates,
                                    (unsigned long long) supp_rates,
@@ -283,8 +286,10 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        if (!bss)
                return;
 
+       cbss = container_of((void *)bss, struct cfg80211_bss, priv);
+
        /* was just updated in ieee80211_bss_info_update */
-       beacon_timestamp = bss->cbss.tsf;
+       beacon_timestamp = cbss->tsf;
 
        /* check if we need to merge IBSS */
 
@@ -297,11 +302,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                goto put_bss;
 
        /* not an IBSS */
-       if (!(bss->cbss.capability & WLAN_CAPABILITY_IBSS))
+       if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
                goto put_bss;
 
        /* different channel */
-       if (bss->cbss.channel != local->oper_channel)
+       if (cbss->channel != local->oper_channel)
                goto put_bss;
 
        /* different SSID */
@@ -311,7 +316,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                goto put_bss;
 
        /* same BSSID */
-       if (memcmp(bss->cbss.bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0)
+       if (memcmp(cbss->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0)
                goto put_bss;
 
        if (rx_status->flag & RX_FLAG_TSFT) {
@@ -364,7 +369,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                printk(KERN_DEBUG "%s: beacon TSF higher than "
                       "local TSF - IBSS merge with BSSID %pM\n",
-                      sdata->dev->name, mgmt->bssid);
+                      sdata->name, mgmt->bssid);
 #endif
                ieee80211_sta_join_ibss(sdata, bss);
                ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
@@ -394,7 +399,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
        if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
                if (net_ratelimit())
                        printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n",
-                              sdata->dev->name, addr);
+                              sdata->name, addr);
                return NULL;
        }
 
@@ -406,7 +411,7 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
        printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
-              wiphy_name(local->hw.wiphy), addr, sdata->dev->name);
+              wiphy_name(local->hw.wiphy), addr, sdata->name);
 #endif
 
        sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
@@ -470,7 +475,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
                return;
 
        printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
-              "IBSS networks with same SSID (merge)\n", sdata->dev->name);
+              "IBSS networks with same SSID (merge)\n", sdata->name);
 
        ieee80211_request_internal_scan(sdata, ifibss->ssid, ifibss->ssid_len);
 }
@@ -492,13 +497,13 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
                 * random number generator get different BSSID. */
                get_random_bytes(bssid, ETH_ALEN);
                for (i = 0; i < ETH_ALEN; i++)
-                       bssid[i] ^= sdata->dev->dev_addr[i];
+                       bssid[i] ^= sdata->vif.addr[i];
                bssid[0] &= ~0x01;
                bssid[0] |= 0x02;
        }
 
        printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
-              sdata->dev->name, bssid);
+              sdata->name, bssid);
 
        sband = local->hw.wiphy->bands[ifibss->channel->band];
 
@@ -518,7 +523,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_bss *bss;
+       struct cfg80211_bss *cbss;
        struct ieee80211_channel *chan = NULL;
        const u8 *bssid = NULL;
        int active_ibss;
@@ -527,7 +532,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        active_ibss = ieee80211_sta_active_ibss(sdata);
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
        printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
-              sdata->dev->name, active_ibss);
+              sdata->name, active_ibss);
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
 
        if (active_ibss)
@@ -542,21 +547,23 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                chan = ifibss->channel;
        if (!is_zero_ether_addr(ifibss->bssid))
                bssid = ifibss->bssid;
-       bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, bssid,
-                                      ifibss->ssid, ifibss->ssid_len,
-                                      WLAN_CAPABILITY_IBSS |
-                                      WLAN_CAPABILITY_PRIVACY,
-                                      capability);
+       cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
+                               ifibss->ssid, ifibss->ssid_len,
+                               WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY,
+                               capability);
+
+       if (cbss) {
+               struct ieee80211_bss *bss;
 
-       if (bss) {
+               bss = (void *)cbss->priv;
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                printk(KERN_DEBUG "   sta_find_ibss: selected %pM current "
-                      "%pM\n", bss->cbss.bssid, ifibss->bssid);
+                      "%pM\n", cbss->bssid, ifibss->bssid);
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
 
                printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
                       " based on configured SSID\n",
-                      sdata->dev->name, bss->cbss.bssid);
+                      sdata->name, cbss->bssid);
 
                ieee80211_sta_join_ibss(sdata, bss);
                ieee80211_rx_bss_put(local, bss);
@@ -575,7 +582,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        } else if (time_after(jiffies, ifibss->last_scan_completed +
                                        IEEE80211_SCAN_INTERVAL)) {
                printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
-                      "join\n", sdata->dev->name);
+                      "join\n", sdata->name);
 
                ieee80211_request_internal_scan(sdata, ifibss->ssid,
                                                ifibss->ssid_len);
@@ -589,7 +596,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
                                return;
                        }
                        printk(KERN_DEBUG "%s: IBSS not allowed on"
-                              " %d MHz\n", sdata->dev->name,
+                              " %d MHz\n", sdata->name,
                               local->hw.conf.channel->center_freq);
 
                        /* No IBSS found - decrease scan interval and continue
@@ -623,7 +630,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
        printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM"
               " (tx_last_beacon=%d)\n",
-              sdata->dev->name, mgmt->sa, mgmt->da,
+              sdata->name, mgmt->sa, mgmt->da,
               mgmt->bssid, tx_last_beacon);
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
 
@@ -641,7 +648,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
                       "from %pM\n",
-                      sdata->dev->name, mgmt->sa);
+                      sdata->name, mgmt->sa);
 #endif
                return;
        }
@@ -661,7 +668,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        memcpy(resp->da, mgmt->sa, ETH_ALEN);
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
        printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n",
-              sdata->dev->name, resp->da);
+              sdata->name, resp->da);
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
@@ -675,7 +682,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
        size_t baselen;
        struct ieee802_11_elems elems;
 
-       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;
@@ -748,7 +755,7 @@ static void ieee80211_ibss_work(struct work_struct *work)
        if (WARN_ON(local->suspended))
                return;
 
-       if (!netif_running(sdata->dev))
+       if (!ieee80211_sdata_running(sdata))
                return;
 
        if (local->scanning)
@@ -831,7 +838,7 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)
 
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
                if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
                        continue;
index 91dc8636d64481bfefe15c063428f0df018b83a1..a27921ee6e637342c64506c7df1cfc352a1e650f 100644 (file)
@@ -71,9 +71,6 @@ struct ieee80211_fragment_entry {
 
 
 struct ieee80211_bss {
-       /* Yes, this is a hack */
-       struct cfg80211_bss cbss;
-
        /* don't want to look up all the time */
        size_t ssid_len;
        u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -140,7 +137,6 @@ typedef unsigned __bitwise__ ieee80211_tx_result;
 
 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;
@@ -228,31 +224,78 @@ struct mesh_preq_queue {
        u8 flags;
 };
 
-enum ieee80211_mgd_state {
-       IEEE80211_MGD_STATE_IDLE,
-       IEEE80211_MGD_STATE_PROBE,
-       IEEE80211_MGD_STATE_AUTH,
-       IEEE80211_MGD_STATE_ASSOC,
+enum ieee80211_work_type {
+       IEEE80211_WORK_ABORT,
+       IEEE80211_WORK_DIRECT_PROBE,
+       IEEE80211_WORK_AUTH,
+       IEEE80211_WORK_ASSOC,
+       IEEE80211_WORK_REMAIN_ON_CHANNEL,
+};
+
+/**
+ * enum work_done_result - indicates what to do after work was done
+ *
+ * @WORK_DONE_DESTROY: This work item is no longer needed, destroy.
+ * @WORK_DONE_REQUEUE: This work item was reset to be reused, and
+ *     should be requeued.
+ */
+enum work_done_result {
+       WORK_DONE_DESTROY,
+       WORK_DONE_REQUEUE,
 };
 
-struct ieee80211_mgd_work {
+struct ieee80211_work {
        struct list_head list;
-       struct ieee80211_bss *bss;
-       int ie_len;
-       u8 prev_bssid[ETH_ALEN];
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-       u8 ssid_len;
+
+       struct rcu_head rcu_head;
+
+       struct ieee80211_sub_if_data *sdata;
+
+       enum work_done_result (*done)(struct ieee80211_work *wk,
+                                     struct sk_buff *skb);
+
+       struct ieee80211_channel *chan;
+       enum nl80211_channel_type chan_type;
+
        unsigned long timeout;
-       enum ieee80211_mgd_state state;
-       u16 auth_alg, auth_transaction;
+       enum ieee80211_work_type type;
+
+       u8 filter_ta[ETH_ALEN];
 
-       int tries;
+       bool started;
 
-       u8 key[WLAN_KEY_LEN_WEP104];
-       u8 key_len, key_idx;
+       union {
+               struct {
+                       int tries;
+                       u16 algorithm, transaction;
+                       u8 ssid[IEEE80211_MAX_SSID_LEN];
+                       u8 ssid_len;
+                       u8 key[WLAN_KEY_LEN_WEP104];
+                       u8 key_len, key_idx;
+                       bool privacy;
+               } probe_auth;
+               struct {
+                       struct cfg80211_bss *bss;
+                       const u8 *supp_rates;
+                       const u8 *ht_information_ie;
+                       enum ieee80211_smps_mode smps;
+                       int tries;
+                       u16 capability;
+                       u8 prev_bssid[ETH_ALEN];
+                       u8 ssid[IEEE80211_MAX_SSID_LEN];
+                       u8 ssid_len;
+                       u8 supp_rates_len;
+                       bool wmm_used, use_11n;
+               } assoc;
+               struct {
+                       u32 duration;
+                       bool started;
+               } remain;
+       };
 
+       int ie_len;
        /* must be last */
-       u8 ie[0]; /* for auth or assoc frame, not probe */
+       u8 ie[0];
 };
 
 /* flags used in struct ieee80211_if_managed.flags */
@@ -260,17 +303,11 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_BEACON_POLL       = BIT(0),
        IEEE80211_STA_CONNECTION_POLL   = BIT(1),
        IEEE80211_STA_CONTROL_PORT      = BIT(2),
-       IEEE80211_STA_WMM_ENABLED       = BIT(3),
        IEEE80211_STA_DISABLE_11N       = BIT(4),
        IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
 };
 
-/* flags for MLME request */
-enum ieee80211_sta_request {
-       IEEE80211_STA_REQ_SCAN,
-};
-
 struct ieee80211_if_managed {
        struct timer_list timer;
        struct timer_list conn_mon_timer;
@@ -285,21 +322,18 @@ struct ieee80211_if_managed {
        int probe_send_count;
 
        struct mutex mtx;
-       struct ieee80211_bss *associated;
-       struct ieee80211_mgd_work *old_associate_work;
-       struct list_head work_list;
+       struct cfg80211_bss *associated;
 
        u8 bssid[ETH_ALEN];
 
        u16 aid;
-       u16 capab;
 
        struct sk_buff_head skb_queue;
 
        unsigned long timers_running; /* used for quiesce/restart */
        bool powersave; /* powersave requested for this iface */
-
-       unsigned long request;
+       enum ieee80211_smps_mode req_smps, /* requested smps mode */
+                                ap_smps; /* smps mode AP thinks we're in */
 
        unsigned int flags;
 
@@ -433,6 +467,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.
@@ -564,6 +600,15 @@ struct ieee80211_local {
 
        const struct ieee80211_ops *ops;
 
+       /*
+        * work stuff, potentially off-channel (in the future)
+        */
+       struct mutex work_mtx;
+       struct list_head work_list;
+       struct timer_list work_timer;
+       struct work_struct work_work;
+       struct sk_buff_head work_skb_queue;
+
        /*
         * private workqueue to mac80211. mac80211 makes this accessible
         * via ieee80211_queue_work()
@@ -586,6 +631,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;
@@ -689,6 +737,10 @@ struct ieee80211_local {
        enum nl80211_channel_type oper_channel_type;
        struct ieee80211_channel *oper_channel, *csa_channel;
 
+       /* Temporary remain-on-channel for off-channel operations */
+       struct ieee80211_channel *tmp_channel;
+       enum nl80211_channel_type tmp_channel_type;
+
        /* SNMP counters */
        /* dot11CountersTable */
        u32 dot11TransmittedFragmentCount;
@@ -746,7 +798,7 @@ struct ieee80211_local {
        unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
 
        bool pspolling;
-       bool scan_ps_enabled;
+       bool offchannel_ps_enabled;
        /*
         * PS can only be enabled when we have exactly one managed
         * interface (and monitors) in PS, this then points there.
@@ -760,6 +812,8 @@ struct ieee80211_local {
        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 +928,8 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
 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,
@@ -937,7 +993,15 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
 void ieee80211_rx_bss_put(struct ieee80211_local *local,
                          struct ieee80211_bss *bss);
 
+/* off-channel helpers */
+void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
+void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
+void ieee80211_offchannel_return(struct ieee80211_local *local,
+                                bool enable_beaconing);
+
 /* 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);
@@ -948,6 +1012,11 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local);
 u32 __ieee80211_recalc_idle(struct ieee80211_local *local);
 void ieee80211_recalc_idle(struct ieee80211_local *local);
 
+static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
+{
+       return netif_running(sdata->dev);
+}
+
 /* tx handling */
 void ieee80211_clear_tx_pending(struct ieee80211_local *local);
 void ieee80211_tx_pending(unsigned long data);
@@ -976,6 +1045,9 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
 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 +1158,28 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
 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);
+
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+                         const u8 *ids, int n_ids, size_t offset);
+size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
+
+/* internal work items */
+void ieee80211_work_init(struct ieee80211_local *local);
+void ieee80211_add_work(struct ieee80211_work *wk);
+void free_work(struct ieee80211_work *wk);
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata);
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+                                          struct sk_buff *skb);
+int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
+                                  struct ieee80211_channel *chan,
+                                  enum nl80211_channel_type channel_type,
+                                  unsigned int duration, u64 *cookie);
+int ieee80211_wk_cancel_remain_on_channel(
+       struct ieee80211_sub_if_data *sdata, u64 cookie);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index ff762ed34f1e9f1c4e42d4ca364d955d06f6b4e9..264a6c975f8b7650ce9f90293f047b072c83b5b1 100644 (file)
@@ -62,6 +62,23 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
+static int ieee80211_change_mac(struct net_device *dev, void *addr)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct sockaddr *sa = addr;
+       int ret;
+
+       if (ieee80211_sdata_running(sdata))
+               return -EBUSY;
+
+       ret = eth_mac_addr(dev, sa);
+
+       if (ret == 0)
+               memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
+
+       return ret;
+}
+
 static inline int identical_mac_addr_allowed(int type1, int type2)
 {
        return type1 == NL80211_IFTYPE_MONITOR ||
@@ -82,7 +99,6 @@ static int ieee80211_open(struct net_device *dev)
        struct ieee80211_sub_if_data *nsdata;
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
-       struct ieee80211_if_init_conf conf;
        u32 changed = 0;
        int res;
        u32 hw_reconf_flags = 0;
@@ -97,7 +113,7 @@ static int ieee80211_open(struct net_device *dev)
        list_for_each_entry(nsdata, &local->interfaces, list) {
                struct net_device *ndev = nsdata->dev;
 
-               if (ndev != dev && netif_running(ndev)) {
+               if (ndev != dev && ieee80211_sdata_running(nsdata)) {
                        /*
                         * Allow only a single IBSS interface to be up at any
                         * time. This is restricted because beacon distribution
@@ -183,7 +199,7 @@ static int ieee80211_open(struct net_device *dev)
                struct net_device *ndev = nsdata->dev;
 
                /*
-                * No need to check netif_running since we do not allow
+                * No need to check running since we do not allow
                 * it to start up with this invalid address.
                 */
                if (compare_ether_addr(null_addr, ndev->dev_addr) == 0) {
@@ -234,10 +250,7 @@ static int ieee80211_open(struct net_device *dev)
                ieee80211_configure_filter(local);
                break;
        default:
-               conf.vif = &sdata->vif;
-               conf.type = sdata->vif.type;
-               conf.mac_addr = dev->dev_addr;
-               res = drv_add_interface(local, &conf);
+               res = drv_add_interface(local, &sdata->vif);
                if (res)
                        goto err_stop;
 
@@ -320,7 +333,7 @@ static int ieee80211_open(struct net_device *dev)
 
        return 0;
  err_del_interface:
-       drv_remove_interface(local, &conf);
+       drv_remove_interface(local, &sdata->vif);
  err_stop:
        if (!local->open_count)
                drv_stop(local);
@@ -335,7 +348,6 @@ static int ieee80211_stop(struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_init_conf conf;
        struct sta_info *sta;
        unsigned long flags;
        struct sk_buff *skb, *tmp;
@@ -347,6 +359,11 @@ static int ieee80211_stop(struct net_device *dev)
         */
        netif_tx_stop_all_queues(dev);
 
+       /*
+        * Purge work for this interface.
+        */
+       ieee80211_work_purge(sdata);
+
        /*
         * Now delete all active aggregation sessions.
         */
@@ -514,12 +531,9 @@ static int ieee80211_stop(struct net_device *dev)
                                BSS_CHANGED_BEACON_ENABLED);
                }
 
-               conf.vif = &sdata->vif;
-               conf.type = sdata->vif.type;
-               conf.mac_addr = dev->dev_addr;
                /* disable all keys for as long as this netdev is down */
                ieee80211_disable_keys(sdata);
-               drv_remove_interface(local, &conf);
+               drv_remove_interface(local, &sdata->vif);
        }
 
        sdata->bss = NULL;
@@ -659,7 +673,7 @@ static const struct net_device_ops ieee80211_dataif_ops = {
        .ndo_start_xmit         = ieee80211_subif_start_xmit,
        .ndo_set_multicast_list = ieee80211_set_multicast_list,
        .ndo_change_mtu         = ieee80211_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_set_mac_address    = ieee80211_change_mac,
        .ndo_select_queue       = ieee80211_netdev_select_queue,
 };
 
@@ -775,7 +789,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
         * and goes into the requested mode.
         */
 
-       if (netif_running(sdata->dev))
+       if (ieee80211_sdata_running(sdata))
                return -EBUSY;
 
        /* Purge and reset type-dependent state. */
@@ -829,6 +843,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */
        sdata = netdev_priv(ndev);
        ndev->ieee80211_ptr = &sdata->wdev;
+       memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN);
+       memcpy(sdata->name, ndev->name, IFNAMSIZ);
 
        /* initialise type-independent data */
        sdata->wdev.wiphy = local->hw.wiphy;
@@ -934,6 +950,8 @@ static u32 ieee80211_idle_on(struct ieee80211_local *local)
               wiphy_name(local->hw.wiphy));
 #endif
 
+       drv_flush(local, false);
+
        local->hw.conf.flags |= IEEE80211_CONF_IDLE;
        return IEEE80211_CONF_CHANGE_IDLE;
 }
@@ -943,16 +961,18 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
        struct ieee80211_sub_if_data *sdata;
        int count = 0;
 
+       if (!list_empty(&local->work_list))
+               return ieee80211_idle_off(local, "working");
+
        if (local->scanning)
                return ieee80211_idle_off(local, "scanning");
 
        list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
                /* do not count disabled managed interfaces */
                if (sdata->vif.type == NL80211_IFTYPE_STATION &&
-                   !sdata->u.mgd.associated &&
-                   list_empty(&sdata->u.mgd.work_list))
+                   !sdata->u.mgd.associated)
                        continue;
                /* do not count unused IBSS interfaces */
                if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
@@ -980,3 +1000,41 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)
        if (chg)
                ieee80211_hw_config(local, chg);
 }
+
+static int netdev_notify(struct notifier_block *nb,
+                        unsigned long state,
+                        void *ndev)
+{
+       struct net_device *dev = ndev;
+       struct ieee80211_sub_if_data *sdata;
+
+       if (state != NETDEV_CHANGENAME)
+               return 0;
+
+       if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy)
+               return 0;
+
+       if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid)
+               return 0;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       memcpy(sdata->name, sdata->name, IFNAMSIZ);
+
+       ieee80211_debugfs_rename_netdev(sdata);
+       return 0;
+}
+
+static struct notifier_block mac80211_netdev_notifier = {
+       .notifier_call = netdev_notify,
+};
+
+int ieee80211_iface_init(void)
+{
+       return register_netdevice_notifier(&mac80211_netdev_notifier);
+}
+
+void ieee80211_iface_exit(void)
+{
+       unregister_netdevice_notifier(&mac80211_netdev_notifier);
+}
index 659a42d529e3cbcd20100452e018b5584fa970a9..8160d9c5372ea09e8fbc6c01b54c3e00703d3188 100644 (file)
@@ -139,7 +139,7 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                                     struct ieee80211_sub_if_data,
                                     u.ap);
 
-       ret = drv_set_key(key->local, SET_KEY, &sdata->vif, sta, &key->conf);
+       ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf);
 
        if (!ret) {
                spin_lock_bh(&todo_lock);
@@ -181,7 +181,7 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
                                     struct ieee80211_sub_if_data,
                                     u.ap);
 
-       ret = drv_set_key(key->local, DISABLE_KEY, &sdata->vif,
+       ret = drv_set_key(key->local, DISABLE_KEY, sdata,
                          sta, &key->conf);
 
        if (ret)
@@ -421,7 +421,7 @@ void ieee80211_key_link(struct ieee80211_key *key,
                         */
 
                        /* same here, the AP could be using QoS */
-                       ap = sta_info_get(key->local, key->sdata->u.mgd.bssid);
+                       ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid);
                        if (ap) {
                                if (test_sta_flags(ap, WLAN_STA_WME))
                                        key->conf.flags |=
@@ -443,7 +443,7 @@ void ieee80211_key_link(struct ieee80211_key *key,
        add_todo(old_key, KEY_FLAG_TODO_DELETE);
 
        add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS);
-       if (netif_running(sdata->dev))
+       if (ieee80211_sdata_running(sdata))
                add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD);
 
        spin_unlock_irqrestore(&sdata->local->key_lock, flags);
@@ -509,7 +509,7 @@ void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
 {
        ASSERT_RTNL();
 
-       if (WARN_ON(!netif_running(sdata->dev)))
+       if (WARN_ON(!ieee80211_sdata_running(sdata)))
                return;
 
        ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_ADD);
index a49f93b79e9299571eecaa714aef060a62999789..bdc2968c2bbe2d72cbe1c55d554a67ff65887b17 100644 (file)
@@ -59,11 +59,17 @@ enum ieee80211_internal_key_flags {
        KEY_FLAG_TODO_DEFMGMTKEY        = BIT(6),
 };
 
+enum ieee80211_internal_tkip_state {
+       TKIP_STATE_NOT_INIT,
+       TKIP_STATE_PHASE1_DONE,
+       TKIP_STATE_PHASE1_HW_UPLOADED,
+};
+
 struct tkip_ctx {
        u32 iv32;
        u16 iv16;
        u16 p1k[5];
-       int initialized;
+       enum ieee80211_internal_tkip_state state;
 };
 
 struct ieee80211_key {
index 0d2d94881f1f14fd3d42b692c144fea3b66909e3..46882914399105bf7c313baa53ebcbbf9ccaa5a3 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
-#include <linux/wireless.h>
 #include <linux/rtnetlink.h>
 #include <linux/bitmap.h>
 #include <linux/pm_qos_params.h>
 #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)
 {
@@ -102,6 +106,9 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
        if (scan_chan) {
                chan = scan_chan;
                channel_type = NL80211_CHAN_NO_HT;
+       } else if (local->tmp_channel) {
+               chan = scan_chan = local->tmp_channel;
+               channel_type = local->tmp_channel_type;
        } else {
                chan = local->oper_channel;
                channel_type = local->oper_channel_type;
@@ -114,6 +121,18 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
                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 +192,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
        } 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 {
@@ -195,7 +214,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
        }
 
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
-               if (local->quiescing || !netif_running(sdata->dev) ||
+               if (local->quiescing || !ieee80211_sdata_running(sdata) ||
                    test_bit(SCAN_SW_SCANNING, &local->scanning)) {
                        sdata->vif.bss_conf.enable_beacon = false;
                } else {
@@ -223,8 +242,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       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 +317,16 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
 }
 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)
 {
@@ -333,9 +361,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
                        WIPHY_FLAG_4ADDR_STATION;
        wiphy->privid = mac80211_wiphy_privid;
 
-       /* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
-       wiphy->bss_priv_size = sizeof(struct ieee80211_bss) -
-                              sizeof(struct cfg80211_bss);
+       wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
 
        local = wiphy_priv(wiphy);
 
@@ -369,9 +395,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 
        INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
 
+       ieee80211_work_init(local);
+
        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);
@@ -674,11 +704,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)
@@ -695,7 +733,7 @@ static void __exit ieee80211_exit(void)
        if (mesh_allocated)
                ieee80211s_stop();
 
-       ieee80211_debugfs_netdev_exit();
+       ieee80211_iface_exit();
 }
 
 
index 6a43314295988c76067017be5d19de785e404d54..61080c5fad50f2ef859ac6461d6168d69cddaaa6 100644 (file)
@@ -457,7 +457,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
 
 #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 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
 
        /* 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;
@@ -645,7 +645,7 @@ static void ieee80211_mesh_work(struct work_struct *work)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        struct sk_buff *skb;
 
-       if (!netif_running(sdata->dev))
+       if (!ieee80211_sdata_running(sdata))
                return;
 
        if (local->scanning)
index d28acb6b1f8151e2a822ec5fc697231b5d76e698..ce84237ebad3b493d5c5d8dc3ea18662395dbfb7 100644 (file)
@@ -128,9 +128,9 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
                                          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 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
                                          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 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
        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;
@@ -374,7 +374,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
                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 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 
        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 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
         * 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 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
                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,
@@ -939,7 +939,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
                if (time_after(jiffies,
                               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 @@ mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)
 {
        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 a8da23905c70520ebbb2da68f2d45f5a26cd081a..fbef678f64c8473269b561156e329b4702ea3c2f 100644 (file)
@@ -260,7 +260,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
        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 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
        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;
 
@@ -605,7 +605,7 @@ void mesh_path_discard_frame(struct sk_buff *skb,
        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;
index 0f7c6e6a4248cf6300b79e21b363b776dd4a5da7..7985e51508987393667dad0feb934bbdec0d09b0 100644 (file)
@@ -169,7 +169,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          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 is left zeroed, wildcard value */
        mgmt->u.action.category = MESH_PLINK_CATEGORY;
        mgmt->u.action.u.plink_action.action_code = action;
@@ -234,7 +234,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, struct ieee80211_sub_if_data
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, hw_addr);
+       sta = sta_info_get(sdata, hw_addr);
        if (!sta) {
                sta = mesh_plink_alloc(sdata, hw_addr, rates);
                if (!sta) {
@@ -455,7 +455,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, mgmt->sa);
+       sta = sta_info_get(sdata, mgmt->sa);
        if (!sta && ftype != PLINK_OPEN) {
                mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
                rcu_read_unlock();
index 3e1eab963b8e55a73c17c566bf909e0eb6e21c89..97bcf2278bdb4b9c5d15d99e7d8985f3afa529ae 100644 (file)
@@ -75,11 +75,8 @@ enum rx_mgmt_action {
        /* caller must call cfg80211_send_disassoc() */
        RX_MGMT_CFG80211_DISASSOC,
 
-       /* caller must call cfg80211_auth_timeout() & free work */
-       RX_MGMT_CFG80211_AUTH_TO,
-
-       /* caller must call cfg80211_assoc_timeout() & free work */
-       RX_MGMT_CFG80211_ASSOC_TO,
+       /* caller must tell cfg80211 about internal error */
+       RX_MGMT_CFG80211_ASSOC_ERROR,
 };
 
 /* utils */
@@ -122,27 +119,6 @@ static int ecw2cw(int ecw)
        return (1 << ecw) - 1;
 }
 
-static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
-                                     struct ieee80211_supported_band *sband,
-                                     u32 *rates)
-{
-       int i, j, count;
-       *rates = 0;
-       count = 0;
-       for (i = 0; i < bss->supp_rates_len; i++) {
-               int rate = (bss->supp_rates[i] & 0x7F) * 5;
-
-               for (j = 0; j < sband->n_bitrates; j++)
-                       if (sband->bitrates[j].bitrate == rate) {
-                               *rates |= BIT(j);
-                               count++;
-                               break;
-                       }
-       }
-
-       return count;
-}
-
 /*
  * ieee80211_enable_ht should be called only after the operating band
  * has been determined as ht configuration depends on the hw's
@@ -202,7 +178,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
                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);
@@ -228,209 +204,6 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
 
 /* frame sending functions */
 
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
-                                struct ieee80211_mgd_work *wk)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt;
-       u8 *pos;
-       const u8 *ies, *ht_ie;
-       int i, len, count, rates_len, supp_rates_len;
-       u16 capab;
-       int wmm = 0;
-       struct ieee80211_supported_band *sband;
-       u32 rates = 0;
-
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 200 + wk->ie_len +
-                           wk->ssid_len);
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
-                      "frame\n", sdata->dev->name);
-               return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
-       capab = ifmgd->capab;
-
-       if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) {
-               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
-                       capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
-               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
-                       capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
-       }
-
-       if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
-               capab |= WLAN_CAPABILITY_PRIVACY;
-       if (wk->bss->wmm_used)
-               wmm = 1;
-
-       /* get all rates supported by the device and the AP as
-        * some APs don't like getting a superset of their rates
-        * in the association request (e.g. D-Link DAP 1353 in
-        * b-only mode) */
-       rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
-
-       if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
-           (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
-               capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
-
-       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->bssid, wk->bss->cbss.bssid, ETH_ALEN);
-
-       if (!is_zero_ether_addr(wk->prev_bssid)) {
-               skb_put(skb, 10);
-               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                                 IEEE80211_STYPE_REASSOC_REQ);
-               mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
-               mgmt->u.reassoc_req.listen_interval =
-                               cpu_to_le16(local->hw.conf.listen_interval);
-               memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid,
-                      ETH_ALEN);
-       } else {
-               skb_put(skb, 4);
-               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                                 IEEE80211_STYPE_ASSOC_REQ);
-               mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
-               mgmt->u.assoc_req.listen_interval =
-                               cpu_to_le16(local->hw.conf.listen_interval);
-       }
-
-       /* SSID */
-       ies = pos = skb_put(skb, 2 + wk->ssid_len);
-       *pos++ = WLAN_EID_SSID;
-       *pos++ = wk->ssid_len;
-       memcpy(pos, wk->ssid, wk->ssid_len);
-
-       /* add all rates which were marked to be used above */
-       supp_rates_len = rates_len;
-       if (supp_rates_len > 8)
-               supp_rates_len = 8;
-
-       len = sband->n_bitrates;
-       pos = skb_put(skb, supp_rates_len + 2);
-       *pos++ = WLAN_EID_SUPP_RATES;
-       *pos++ = supp_rates_len;
-
-       count = 0;
-       for (i = 0; i < sband->n_bitrates; i++) {
-               if (BIT(i) & rates) {
-                       int rate = sband->bitrates[i].bitrate;
-                       *pos++ = (u8) (rate / 5);
-                       if (++count == 8)
-                               break;
-               }
-       }
-
-       if (rates_len > count) {
-               pos = skb_put(skb, rates_len - count + 2);
-               *pos++ = WLAN_EID_EXT_SUPP_RATES;
-               *pos++ = rates_len - count;
-
-               for (i++; i < sband->n_bitrates; i++) {
-                       if (BIT(i) & rates) {
-                               int rate = sband->bitrates[i].bitrate;
-                               *pos++ = (u8) (rate / 5);
-                       }
-               }
-       }
-
-       if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
-               /* 1. power capabilities */
-               pos = skb_put(skb, 4);
-               *pos++ = WLAN_EID_PWR_CAPABILITY;
-               *pos++ = 2;
-               *pos++ = 0; /* min tx power */
-               *pos++ = local->hw.conf.channel->max_power; /* max tx power */
-
-               /* 2. supported channels */
-               /* TODO: get this in reg domain format */
-               pos = skb_put(skb, 2 * sband->n_channels + 2);
-               *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
-               *pos++ = 2 * sband->n_channels;
-               for (i = 0; i < sband->n_channels; i++) {
-                       *pos++ = ieee80211_frequency_to_channel(
-                                       sband->channels[i].center_freq);
-                       *pos++ = 1; /* one channel in the subband*/
-               }
-       }
-
-       if (wk->ie_len && wk->ie) {
-               pos = skb_put(skb, wk->ie_len);
-               memcpy(pos, wk->ie, wk->ie_len);
-       }
-
-       if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
-               pos = skb_put(skb, 9);
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WME */
-               *pos++ = 0; /* WME info */
-               *pos++ = 1; /* WME ver */
-               *pos++ = 0;
-       }
-
-       /* wmm support is a must to HT */
-       /*
-        * IEEE802.11n does not allow TKIP/WEP as pairwise
-        * ciphers in HT mode. We still associate in non-ht
-        * mode (11a/b/g) if any one of these ciphers is
-        * configured as pairwise.
-        */
-       if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
-           sband->ht_cap.ht_supported &&
-           (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) &&
-           ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
-           (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
-               struct ieee80211_ht_info *ht_info =
-                       (struct ieee80211_ht_info *)(ht_ie + 2);
-               u16 cap = sband->ht_cap.cap;
-               __le16 tmp;
-               u32 flags = local->hw.conf.channel->flags;
-
-               switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
-               case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
-                       if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
-                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-                               cap &= ~IEEE80211_HT_CAP_SGI_40;
-                       }
-                       break;
-               case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
-                       if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
-                               cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
-                               cap &= ~IEEE80211_HT_CAP_SGI_40;
-                       }
-                       break;
-               }
-
-               tmp = cpu_to_le16(cap);
-               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));
-               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);
-               memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
-       }
-
-       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-       ieee80211_tx_skb(sdata, skb);
-}
-
-
 static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                           const u8 *bssid, u16 stype, u16 reason,
                                           void *cookie)
@@ -443,7 +216,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        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);
@@ -451,7 +224,7 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        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 +257,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
        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);
@@ -499,7 +272,7 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
        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 +292,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
        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);
@@ -532,7 +305,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
                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;
@@ -546,7 +319,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       if (!netif_running(sdata->dev))
+       if (!ieee80211_sdata_running(sdata))
                return;
 
        mutex_lock(&ifmgd->mtx);
@@ -557,7 +330,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
        ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        /* XXX: shouldn't really modify cfg80211-owned data! */
-       ifmgd->associated->cbss.channel = sdata->local->oper_channel;
+       ifmgd->associated->channel = sdata->local->oper_channel;
 
        ieee80211_wake_queues_by_reason(&sdata->local->hw,
                                        IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -584,6 +357,8 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                                      struct ieee80211_channel_sw_ie *sw_elem,
                                      struct ieee80211_bss *bss)
 {
+       struct cfg80211_bss *cbss =
+               container_of((void *)bss, struct cfg80211_bss, priv);
        struct ieee80211_channel *new_ch;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
@@ -617,7 +392,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                mod_timer(&ifmgd->chswitch_timer,
                          jiffies +
                          msecs_to_jiffies(sw_elem->count *
-                                          bss->cbss.beacon_interval));
+                                          cbss->beacon_interval));
        }
 }
 
@@ -691,8 +466,13 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                return;
        }
 
+       if (!list_empty(&local->work_list)) {
+               local->ps_sdata = NULL;
+               goto change;
+       }
+
        list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
                if (sdata->vif.type != NL80211_IFTYPE_STATION)
                        continue;
@@ -701,7 +481,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
        }
 
        if (count == 1 && found->u.mgd.powersave &&
-           found->u.mgd.associated && list_empty(&found->u.mgd.work_list) &&
+           found->u.mgd.associated &&
            !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
                                    IEEE80211_STA_CONNECTION_POLL))) {
                s32 beaconint_us;
@@ -729,6 +509,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                local->ps_sdata = NULL;
        }
 
+ change:
        ieee80211_change_ps(local);
 }
 
@@ -788,7 +569,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        int count;
        u8 *pos;
 
-       if (!(ifmgd->flags & IEEE80211_STA_WMM_ENABLED))
+       if (local->hw.queues < 4)
                return;
 
        if (!wmm_param)
@@ -891,25 +672,24 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
 }
 
 static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_mgd_work *wk,
+                                    struct cfg80211_bss *cbss,
                                     u32 bss_info_changed)
 {
+       struct ieee80211_bss *bss = (void *)cbss->priv;
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_bss *bss = wk->bss;
 
        bss_info_changed |= BSS_CHANGED_ASSOC;
        /* set timing information */
-       sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
-       sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
+       sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
+       sdata->vif.bss_conf.timestamp = cbss->tsf;
        sdata->vif.bss_conf.dtim_period = bss->dtim_period;
 
        bss_info_changed |= BSS_CHANGED_BEACON_INT;
        bss_info_changed |= ieee80211_handle_bss_capability(sdata,
-               bss->cbss.capability, bss->has_erp_value, bss->erp_value);
+               cbss->capability, bss->has_erp_value, bss->erp_value);
 
-       sdata->u.mgd.associated = bss;
-       sdata->u.mgd.old_associate_work = wk;
-       memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN);
+       sdata->u.mgd.associated = cbss;
+       memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
 
        /* just to be sure */
        sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
@@ -940,99 +720,14 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local, -1);
+       ieee80211_recalc_smps(local, sdata);
        mutex_unlock(&local->iflist_mtx);
 
        netif_tx_start_all_queues(sdata->dev);
        netif_carrier_on(sdata->dev);
 }
 
-static enum rx_mgmt_action __must_check
-ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
-                      struct ieee80211_mgd_work *wk)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-
-       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);
-
-               /*
-                * Most likely AP is not in the range so remove the
-                * bss struct for that AP.
-                */
-               cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
-
-               /*
-                * We might have a pending scan which had no chance to run yet
-                * due to work needing to be done. Hence, queue the STAs work
-                * again for that.
-                */
-               ieee80211_queue_work(&local->hw, &ifmgd->work);
-               return RX_MGMT_CFG80211_AUTH_TO;
-       }
-
-       printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
-                       sdata->dev->name, wk->bss->cbss.bssid,
-                       wk->tries);
-
-       /*
-        * Direct probe is sent to broadcast address as some APs
-        * will not answer to direct packet in unassociated state.
-        */
-       ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0);
-
-       wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-       run_again(ifmgd, wk->timeout);
-
-       return RX_MGMT_NONE;
-}
-
-
-static enum rx_mgmt_action __must_check
-ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
-                      struct ieee80211_mgd_work *wk)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-
-       wk->tries++;
-       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);
-
-               /*
-                * Most likely AP is not in the range so remove the
-                * bss struct for that AP.
-                */
-               cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
-
-               /*
-                * We might have a pending scan which had no chance to run yet
-                * due to work needing to be done. Hence, queue the STAs work
-                * again for that.
-                */
-               ieee80211_queue_work(&local->hw, &ifmgd->work);
-               return RX_MGMT_CFG80211_AUTH_TO;
-       }
-
-       printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
-              sdata->dev->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);
-       wk->auth_transaction = 2;
-
-       wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
-       run_again(ifmgd, wk->timeout);
-
-       return RX_MGMT_NONE;
-}
-
-static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
-                                  bool deauth)
+static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
@@ -1045,21 +740,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!ifmgd->associated))
                return;
 
-       memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN);
+       memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
 
        ifmgd->associated = NULL;
        memset(ifmgd->bssid, 0, ETH_ALEN);
 
-       if (deauth) {
-               kfree(ifmgd->old_associate_work);
-               ifmgd->old_associate_work = NULL;
-       } else {
-               struct ieee80211_mgd_work *wk = ifmgd->old_associate_work;
-
-               wk->state = IEEE80211_MGD_STATE_IDLE;
-               list_add(&wk->list, &ifmgd->work_list);
-       }
-
        /*
         * we need to commit the associated = NULL change because the
         * scan code uses that to determine whether this iface should
@@ -1078,7 +763,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        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();
@@ -1115,7 +800,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, bssid);
+       sta = sta_info_get(sdata, bssid);
        if (!sta) {
                rcu_read_unlock();
                return;
@@ -1128,44 +813,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        sta_info_destroy(sta);
 }
 
-static enum rx_mgmt_action __must_check
-ieee80211_associate(struct ieee80211_sub_if_data *sdata,
-                   struct ieee80211_mgd_work *wk)
-{
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_local *local = sdata->local;
-
-       wk->tries++;
-       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);
-
-               /*
-                * Most likely AP is not in the range so remove the
-                * bss struct for that AP.
-                */
-               cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
-
-               /*
-                * We might have a pending scan which had no chance to run yet
-                * due to work needing to be done. Hence, queue the STAs work
-                * again for that.
-                */
-               ieee80211_queue_work(&local->hw, &ifmgd->work);
-               return RX_MGMT_CFG80211_ASSOC_TO;
-       }
-
-       printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
-              sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
-       ieee80211_send_assoc(sdata, wk);
-
-       wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
-       run_again(ifmgd, wk->timeout);
-
-       return RX_MGMT_NONE;
-}
-
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
                             struct ieee80211_hdr *hdr)
 {
@@ -1189,8 +836,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *ssid;
 
-       ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID);
-       ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid,
+       ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
+       ieee80211_send_probe_req(sdata, ifmgd->associated->bssid,
                                 ssid + 2, ssid[1], NULL, 0);
 
        ifmgd->probe_send_count++;
@@ -1204,12 +851,15 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        bool already = false;
 
-       if (!netif_running(sdata->dev))
+       if (!ieee80211_sdata_running(sdata))
                return;
 
        if (sdata->local->scanning)
                return;
 
+       if (sdata->local->tmp_channel)
+               return;
+
        mutex_lock(&ifmgd->mtx);
 
        if (!ifmgd->associated)
@@ -1218,7 +868,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
 #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
 
        /*
@@ -1271,88 +921,8 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
 }
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_mgd_work *wk)
-{
-       wk->state = IEEE80211_MGD_STATE_IDLE;
-       printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
-}
-
-
-static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_mgd_work *wk,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len)
-{
-       u8 *pos;
-       struct ieee802_11_elems elems;
-
-       pos = mgmt->u.auth.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-       if (!elems.challenge)
-               return;
-       ieee80211_send_auth(sdata, 3, wk->auth_alg,
-                           elems.challenge - 2, elems.challenge_len + 2,
-                           wk->bss->cbss.bssid,
-                           wk->key, wk->key_len, wk->key_idx);
-       wk->auth_transaction = 4;
-}
-
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
-                      struct ieee80211_mgd_work *wk,
-                      struct ieee80211_mgmt *mgmt, size_t len)
-{
-       u16 auth_alg, auth_transaction, status_code;
-
-       if (wk->state != IEEE80211_MGD_STATE_AUTH)
-               return RX_MGMT_NONE;
-
-       if (len < 24 + 6)
-               return RX_MGMT_NONE;
-
-       if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
-               return RX_MGMT_NONE;
-
-       if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
-               return RX_MGMT_NONE;
-
-       auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
-       auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
-       status_code = le16_to_cpu(mgmt->u.auth.status_code);
-
-       if (auth_alg != wk->auth_alg ||
-           auth_transaction != wk->auth_transaction)
-               return RX_MGMT_NONE;
-
-       if (status_code != WLAN_STATUS_SUCCESS) {
-               list_del(&wk->list);
-               kfree(wk);
-               return RX_MGMT_CFG80211_AUTH;
-       }
-
-       switch (wk->auth_alg) {
-       case WLAN_AUTH_OPEN:
-       case WLAN_AUTH_LEAP:
-       case WLAN_AUTH_FT:
-               ieee80211_auth_completed(sdata, wk);
-               return RX_MGMT_CFG80211_AUTH;
-       case WLAN_AUTH_SHARED_KEY:
-               if (wk->auth_transaction == 4) {
-                       ieee80211_auth_completed(sdata, wk);
-                       return RX_MGMT_CFG80211_AUTH;
-               } else
-                       ieee80211_auth_challenge(sdata, wk, mgmt, len);
-               break;
-       }
-
-       return RX_MGMT_NONE;
-}
-
-
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
-                        struct ieee80211_mgd_work *wk,
                         struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1364,23 +934,15 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ASSERT_MGD_MTX(ifmgd);
 
-       if (wk)
-               bssid = wk->bss->cbss.bssid;
-       else
-               bssid = ifmgd->associated->cbss.bssid;
+       bssid = ifmgd->associated->bssid;
 
        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);
-       }
+       ieee80211_set_disassoc(sdata);
+       ieee80211_recalc_idle(sdata->local);
 
        return RX_MGMT_CFG80211_DEAUTH;
 }
@@ -1401,123 +963,72 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
        if (WARN_ON(!ifmgd->associated))
                return RX_MGMT_NONE;
 
-       if (WARN_ON(memcmp(ifmgd->associated->cbss.bssid, mgmt->sa, ETH_ALEN)))
+       if (WARN_ON(memcmp(ifmgd->associated->bssid, mgmt->sa, ETH_ALEN)))
                return RX_MGMT_NONE;
 
        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_set_disassoc(sdata);
        ieee80211_recalc_idle(sdata->local);
        return RX_MGMT_CFG80211_DISASSOC;
 }
 
 
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
-                            struct ieee80211_mgd_work *wk,
-                            struct ieee80211_mgmt *mgmt, size_t len,
-                            bool reassoc)
+static bool ieee80211_assoc_success(struct ieee80211_work *wk,
+                                   struct ieee80211_mgmt *mgmt, size_t len)
 {
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
        struct sta_info *sta;
+       struct cfg80211_bss *cbss = wk->assoc.bss;
+       u8 *pos;
        u32 rates, basic_rates;
-       u16 capab_info, status_code, aid;
+       u16 capab_info, aid;
        struct ieee802_11_elems elems;
        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;
 
-       /*
-        * AssocResp and ReassocResp have identical structure, so process both
-        * of them in this function.
-        */
-
-       if (len < 24 + 6)
-               return RX_MGMT_NONE;
-
-       if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
-               return RX_MGMT_NONE;
+       /* AssocResp and ReassocResp have identical structure */
 
-       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
-       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
        aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
-
-       printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
-              "status=%d aid=%d)\n",
-              sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa,
-              capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
-
-       pos = mgmt->u.assoc_resp.variable;
-       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-
-       if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
-           elems.timeout_int && elems.timeout_int_len == 5 &&
-           elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
-               u32 tu, ms;
-               tu = get_unaligned_le32(elems.timeout_int + 1);
-               ms = tu * 1024 / 1000;
-               printk(KERN_DEBUG "%s: AP rejected association temporarily; "
-                      "comeback duration %u TU (%u ms)\n",
-                      sdata->dev->name, tu, ms);
-               wk->timeout = jiffies + msecs_to_jiffies(ms);
-               if (ms > IEEE80211_ASSOC_TIMEOUT)
-                       run_again(ifmgd, jiffies + msecs_to_jiffies(ms));
-               return RX_MGMT_NONE;
-       }
-
-       if (status_code != WLAN_STATUS_SUCCESS) {
-               printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
-                      sdata->dev->name, status_code);
-               wk->state = IEEE80211_MGD_STATE_IDLE;
-               return RX_MGMT_CFG80211_ASSOC;
-       }
+       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
 
        if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
                printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not "
-                      "set\n", sdata->dev->name, aid);
+                      "set\n", sdata->name, aid);
        aid &= ~(BIT(15) | BIT(14));
 
+       pos = mgmt->u.assoc_resp.variable;
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
        if (!elems.supp_rates) {
                printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
-                      sdata->dev->name);
-               return RX_MGMT_NONE;
+                      sdata->name);
+               return false;
        }
 
-       printk(KERN_DEBUG "%s: associated\n", sdata->dev->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, 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 false;
        }
 
+       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];
@@ -1580,40 +1091,33 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        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);
        else
                ieee80211_set_wmm_default(sdata);
 
+       local->oper_channel = wk->chan;
+
        if (elems.ht_info_elem && elems.wmm_param &&
-           (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
+           (sdata->local->hw.queues >= 4) &&
            !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
                changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
-                                              wk->bss->cbss.bssid,
-                                              ap_ht_cap_flags);
-
-        /* delete work item -- must be before set_associated for PS */
-       list_del(&wk->list);
+                                              cbss->bssid, ap_ht_cap_flags);
 
        /* set AID and assoc capability,
         * ieee80211_set_associated() will tell the driver */
        bss_conf->aid = aid;
        bss_conf->assoc_capability = capab_info;
-       /* this will take ownership of wk */
-       ieee80211_set_associated(sdata, wk, changed);
+       ieee80211_set_associated(sdata, cbss, changed);
 
        /*
         * Start timer to probe the connection to the AP now.
@@ -1622,7 +1126,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
        mod_beacon_timer(sdata);
 
-       return RX_MGMT_CFG80211_ASSOC;
+       return true;
 }
 
 
@@ -1657,7 +1161,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                return;
 
        if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
-           (memcmp(mgmt->bssid, sdata->u.mgd.associated->cbss.bssid,
+           (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
                                                        ETH_ALEN) == 0)) {
                struct ieee80211_channel_sw_ie *sw_elem =
                        (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
@@ -1667,19 +1171,19 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
 
 
 static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
-                                        struct ieee80211_mgd_work *wk,
-                                        struct ieee80211_mgmt *mgmt, size_t len,
-                                        struct ieee80211_rx_status *rx_status)
+                                        struct sk_buff *skb)
 {
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
        struct ieee80211_if_managed *ifmgd;
-       size_t baselen;
+       struct ieee80211_rx_status *rx_status = (void *) skb->cb;
+       size_t baselen, len = skb->len;
        struct ieee802_11_elems elems;
 
        ifmgd = &sdata->u.mgd;
 
        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;
@@ -1691,17 +1195,8 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
 
-       /* 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);
-               wk->tries = 0;
-               wk->state = IEEE80211_MGD_STATE_AUTH;
-               WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
-       }
-
        if (ifmgd->associated &&
-           memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 &&
+           memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0 &&
            ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
                            IEEE80211_STA_CONNECTION_POLL)) {
                ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
@@ -1774,7 +1269,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        if (!ifmgd->associated)
                return;
 
-       bssid = ifmgd->associated->cbss.bssid;
+       bssid = ifmgd->associated->bssid;
 
        /*
         * And in theory even frames from a different AP we were just
@@ -1787,7 +1282,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 #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;
@@ -1865,7 +1360,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
                rcu_read_lock();
 
-               sta = sta_info_get(local, bssid);
+               sta = sta_info_get(sdata, bssid);
                if (WARN_ON(!sta)) {
                        rcu_read_unlock();
                        return;
@@ -1913,9 +1408,6 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_PROBE_RESP:
        case IEEE80211_STYPE_BEACON:
-       case IEEE80211_STYPE_AUTH:
-       case IEEE80211_STYPE_ASSOC_RESP:
-       case IEEE80211_STYPE_REASSOC_RESP:
        case IEEE80211_STYPE_DEAUTH:
        case IEEE80211_STYPE_DISASSOC:
        case IEEE80211_STYPE_ACTION:
@@ -1933,7 +1425,6 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_rx_status *rx_status;
        struct ieee80211_mgmt *mgmt;
-       struct ieee80211_mgd_work *wk;
        enum rx_mgmt_action rma = RX_MGMT_NONE;
        u16 fc;
 
@@ -1944,20 +1435,17 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        mutex_lock(&ifmgd->mtx);
 
        if (ifmgd->associated &&
-           memcmp(ifmgd->associated->cbss.bssid, mgmt->bssid,
-                                                       ETH_ALEN) == 0) {
+           memcmp(ifmgd->associated->bssid, mgmt->bssid, ETH_ALEN) == 0) {
                switch (fc & IEEE80211_FCTL_STYPE) {
                case IEEE80211_STYPE_BEACON:
                        ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
                                                 rx_status);
                        break;
                case IEEE80211_STYPE_PROBE_RESP:
-                       ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt,
-                                                    skb->len, rx_status);
+                       ieee80211_rx_mgmt_probe_resp(sdata, skb);
                        break;
                case IEEE80211_STYPE_DEAUTH:
-                       rma = ieee80211_rx_mgmt_deauth(sdata, NULL,
-                                                      mgmt, skb->len);
+                       rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
                        break;
                case IEEE80211_STYPE_DISASSOC:
                        rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
@@ -1966,7 +1454,7 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                        /* XXX: differentiate, can only happen for CSA now! */
                        ieee80211_sta_process_chanswitch(sdata,
                                        &mgmt->u.action.u.chan_switch.sw_elem,
-                                       ifmgd->associated);
+                                       (void *)ifmgd->associated->priv);
                        break;
                }
                mutex_unlock(&ifmgd->mtx);
@@ -1987,58 +1475,11 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
-       list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
-                       continue;
-
-               switch (fc & IEEE80211_FCTL_STYPE) {
-               case IEEE80211_STYPE_PROBE_RESP:
-                       ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len,
-                                                    rx_status);
-                       break;
-               case IEEE80211_STYPE_AUTH:
-                       rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len);
-                       break;
-               case IEEE80211_STYPE_ASSOC_RESP:
-                       rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
-                                                          skb->len, false);
-                       break;
-               case IEEE80211_STYPE_REASSOC_RESP:
-                       rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
-                                                          skb->len, true);
-                       break;
-               case IEEE80211_STYPE_DEAUTH:
-                       rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt,
-                                                      skb->len);
-                       break;
-               }
-               /*
-                * We've processed this frame for that work, so it can't
-                * belong to another work struct.
-                * NB: this is also required for correctness because the
-                * called functions can free 'wk', and for 'rma'!
-                */
-               break;
-       }
-
        mutex_unlock(&ifmgd->mtx);
 
-       switch (rma) {
-       case RX_MGMT_NONE:
-               /* no action */
-               break;
-       case RX_MGMT_CFG80211_AUTH:
-               cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len);
-               break;
-       case RX_MGMT_CFG80211_ASSOC:
-               cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len);
-               break;
-       case RX_MGMT_CFG80211_DEAUTH:
+       if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
+           (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH)
                cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
-               break;
-       default:
-               WARN(1, "unexpected: %d", rma);
-       }
 
  out:
        kfree_skb(skb);
@@ -2066,12 +1507,8 @@ static void ieee80211_sta_work(struct work_struct *work)
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd;
        struct sk_buff *skb;
-       struct ieee80211_mgd_work *wk, *tmp;
-       LIST_HEAD(free_work);
-       enum rx_mgmt_action rma;
-       bool anybusy = false;
 
-       if (!netif_running(sdata->dev))
+       if (!ieee80211_sdata_running(sdata))
                return;
 
        if (local->scanning)
@@ -2102,7 +1539,7 @@ static void ieee80211_sta_work(struct work_struct *work)
            ifmgd->associated) {
                u8 bssid[ETH_ALEN];
 
-               memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN);
+               memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
                if (time_is_after_jiffies(ifmgd->probe_timeout))
                        run_again(ifmgd, ifmgd->probe_timeout);
 
@@ -2124,7 +1561,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                        printk(KERN_DEBUG "No probe response from AP %pM"
                                " after %dms, disconnecting.\n",
                                bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
-                       ieee80211_set_disassoc(sdata, true);
+                       ieee80211_set_disassoc(sdata);
                        ieee80211_recalc_idle(local);
                        mutex_unlock(&ifmgd->mtx);
                        /*
@@ -2139,87 +1576,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                }
        }
 
-
-       ieee80211_recalc_idle(local);
-
-       list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) {
-               if (time_is_after_jiffies(wk->timeout)) {
-                       /*
-                        * This work item isn't supposed to be worked on
-                        * right now, but take care to adjust the timer
-                        * properly.
-                        */
-                       run_again(ifmgd, wk->timeout);
-                       continue;
-               }
-
-               switch (wk->state) {
-               default:
-                       WARN_ON(1);
-                       /* fall through */
-               case IEEE80211_MGD_STATE_IDLE:
-                       /* nothing */
-                       rma = RX_MGMT_NONE;
-                       break;
-               case IEEE80211_MGD_STATE_PROBE:
-                       rma = ieee80211_direct_probe(sdata, wk);
-                       break;
-               case IEEE80211_MGD_STATE_AUTH:
-                       rma = ieee80211_authenticate(sdata, wk);
-                       break;
-               case IEEE80211_MGD_STATE_ASSOC:
-                       rma = ieee80211_associate(sdata, wk);
-                       break;
-               }
-
-               switch (rma) {
-               case RX_MGMT_NONE:
-                       /* no action required */
-                       break;
-               case RX_MGMT_CFG80211_AUTH_TO:
-               case RX_MGMT_CFG80211_ASSOC_TO:
-                       list_del(&wk->list);
-                       list_add(&wk->list, &free_work);
-                       wk->tries = rma; /* small abuse but only local */
-                       break;
-               default:
-                       WARN(1, "unexpected: %d", rma);
-               }
-       }
-
-       list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (wk->state != IEEE80211_MGD_STATE_IDLE) {
-                       anybusy = true;
-                       break;
-               }
-       }
-       if (!anybusy &&
-           test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
-               ieee80211_queue_delayed_work(&local->hw,
-                                            &local->scan_work,
-                                            round_jiffies_relative(0));
-
        mutex_unlock(&ifmgd->mtx);
-
-       list_for_each_entry_safe(wk, tmp, &free_work, list) {
-               switch (wk->tries) {
-               case RX_MGMT_CFG80211_AUTH_TO:
-                       cfg80211_send_auth_timeout(sdata->dev,
-                                                  wk->bss->cbss.bssid);
-                       break;
-               case RX_MGMT_CFG80211_ASSOC_TO:
-                       cfg80211_send_assoc_timeout(sdata->dev,
-                                                   wk->bss->cbss.bssid);
-                       break;
-               default:
-                       WARN(1, "unexpected: %d", wk->tries);
-               }
-
-               list_del(&wk->list);
-               kfree(wk);
-       }
-
-       ieee80211_recalc_idle(local);
 }
 
 static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@ -2328,14 +1685,14 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
                    (unsigned long) sdata);
        skb_queue_head_init(&ifmgd->skb_queue);
 
-       INIT_LIST_HEAD(&ifmgd->work_list);
-
-       ifmgd->capab = WLAN_CAPABILITY_ESS;
        ifmgd->flags = 0;
-       if (sdata->local->hw.queues >= 4)
-               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 */
@@ -2366,12 +1723,34 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
 }
 
 /* config hooks */
+static enum work_done_result
+ieee80211_probe_auth_done(struct ieee80211_work *wk,
+                         struct sk_buff *skb)
+{
+       if (!skb) {
+               cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta);
+               return WORK_DONE_DESTROY;
+       }
+
+       if (wk->type == IEEE80211_WORK_AUTH) {
+               cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
+               return WORK_DONE_DESTROY;
+       }
+
+       mutex_lock(&wk->sdata->u.mgd.mtx);
+       ieee80211_rx_mgmt_probe_resp(wk->sdata, skb);
+       mutex_unlock(&wk->sdata->u.mgd.mtx);
+
+       wk->type = IEEE80211_WORK_AUTH;
+       wk->probe_auth.tries = 0;
+       return WORK_DONE_REQUEUE;
+}
+
 int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
                       struct cfg80211_auth_request *req)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        const u8 *ssid;
-       struct ieee80211_mgd_work *wk;
+       struct ieee80211_work *wk;
        u16 auth_alg;
 
        switch (req->auth_type) {
@@ -2395,7 +1774,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        if (!wk)
                return -ENOMEM;
 
-       wk->bss = (void *)req->bss;
+       memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);;
 
        if (req->ie && req->ie_len) {
                memcpy(wk->ie, req->ie, req->ie_len);
@@ -2403,66 +1782,76 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        }
 
        if (req->key && req->key_len) {
-               wk->key_len = req->key_len;
-               wk->key_idx = req->key_idx;
-               memcpy(wk->key, req->key, req->key_len);
+               wk->probe_auth.key_len = req->key_len;
+               wk->probe_auth.key_idx = req->key_idx;
+               memcpy(wk->probe_auth.key, req->key, req->key_len);
        }
 
        ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
-       memcpy(wk->ssid, ssid + 2, ssid[1]);
-       wk->ssid_len = ssid[1];
+       memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]);
+       wk->probe_auth.ssid_len = ssid[1];
 
-       wk->state = IEEE80211_MGD_STATE_PROBE;
-       wk->auth_alg = auth_alg;
-       wk->timeout = jiffies; /* run right away */
+       wk->probe_auth.algorithm = auth_alg;
+       wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-       /*
-        * XXX: if still associated need to tell AP that we're going
-        *      to sleep and then change channel etc.
-        */
-       sdata->local->oper_channel = req->bss->channel;
-       ieee80211_hw_config(sdata->local, 0);
+       wk->type = IEEE80211_WORK_DIRECT_PROBE;
+       wk->chan = req->bss->channel;
+       wk->sdata = sdata;
+       wk->done = ieee80211_probe_auth_done;
 
-       mutex_lock(&ifmgd->mtx);
-       list_add(&wk->list, &sdata->u.mgd.work_list);
-       mutex_unlock(&ifmgd->mtx);
-
-       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
+       ieee80211_add_work(wk);
        return 0;
 }
 
-int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
-                       struct cfg80211_assoc_request *req)
+static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
+                                                 struct sk_buff *skb)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_mgd_work *wk, *found = NULL;
-       int i, err;
+       struct ieee80211_mgmt *mgmt;
+       u16 status;
 
-       mutex_lock(&ifmgd->mtx);
+       if (!skb) {
+               cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
+               return WORK_DONE_DESTROY;
+       }
 
-       list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (&wk->bss->cbss == req->bss &&
-                   wk->state == IEEE80211_MGD_STATE_IDLE) {
-                       found = wk;
-                       break;
+       mgmt = (void *)skb->data;
+       status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+
+       if (status == WLAN_STATUS_SUCCESS) {
+               mutex_lock(&wk->sdata->u.mgd.mtx);
+               if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
+                       mutex_unlock(&wk->sdata->u.mgd.mtx);
+                       /* oops -- internal error -- send timeout for now */
+                       cfg80211_send_assoc_timeout(wk->sdata->dev,
+                                                   wk->filter_ta);
+                       return WORK_DONE_DESTROY;
                }
+               mutex_unlock(&wk->sdata->u.mgd.mtx);
        }
 
-       if (!found) {
-               err = -ENOLINK;
-               goto out;
-       }
+       cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
+       return WORK_DONE_DESTROY;
+}
 
-       list_del(&found->list);
+int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+                       struct cfg80211_assoc_request *req)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_bss *bss = (void *)req->bss->priv;
+       struct ieee80211_work *wk;
+       const u8 *ssid;
+       int i;
 
-       wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL);
-       if (!wk) {
-               list_add(&found->list, &ifmgd->work_list);
-               err = -ENOMEM;
-               goto out;
+       mutex_lock(&ifmgd->mtx);
+       if (ifmgd->associated) {
+               mutex_unlock(&ifmgd->mtx);
+               return -EALREADY;
        }
+       mutex_unlock(&ifmgd->mtx);
 
-       list_add(&wk->list, &ifmgd->work_list);
+       wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
+       if (!wk)
+               return -ENOMEM;
 
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 
@@ -2472,8 +1861,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
                        ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
 
-       sdata->local->oper_channel = req->bss->channel;
-       ieee80211_hw_config(sdata->local, 0);
 
        if (req->ie && req->ie_len) {
                memcpy(wk->ie, req->ie, req->ie_len);
@@ -2481,12 +1868,46 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        } else
                wk->ie_len = 0;
 
+       wk->assoc.bss = req->bss;
+
+       memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
+
+       /* 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;
+
+       wk->assoc.smps = ifmgd->ap_smps;
+       /*
+        * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
+        * We still associate in non-HT mode (11a/b/g) if any one of these
+        * ciphers is configured as pairwise.
+        * We can set this to true for non-11n hardware, that'll be checked
+        * separately along with the peer capabilities.
+        */
+       wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N);
+       wk->assoc.capability = req->bss->capability;
+       wk->assoc.wmm_used = bss->wmm_used;
+       wk->assoc.supp_rates = bss->supp_rates;
+       wk->assoc.supp_rates_len = bss->supp_rates_len;
+       wk->assoc.ht_information_ie =
+               ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
+
+       ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+       memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
+       wk->assoc.ssid_len = ssid[1];
+
        if (req->prev_bssid)
-               memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN);
+               memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
 
-       wk->state = IEEE80211_MGD_STATE_ASSOC;
-       wk->tries = 0;
-       wk->timeout = jiffies; /* run right away */
+       wk->type = IEEE80211_WORK_ASSOC;
+       wk->chan = req->bss->channel;
+       wk->sdata = sdata;
+       wk->done = ieee80211_assoc_done;
 
        if (req->use_mfp) {
                ifmgd->mfp = IEEE80211_MFP_REQUIRED;
@@ -2501,69 +1922,59 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        else
                ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
 
-       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
-
-       err = 0;
-
- out:
-       mutex_unlock(&ifmgd->mtx);
-       return err;
+       ieee80211_add_work(wk);
+       return 0;
 }
 
 int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                         struct cfg80211_deauth_request *req,
                         void *cookie)
 {
+       struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_mgd_work *wk;
-       const u8 *bssid = NULL;
-       bool not_auth_yet = false;
+       struct ieee80211_work *wk;
+       const u8 *bssid = req->bss->bssid;
 
        mutex_lock(&ifmgd->mtx);
 
-       if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
+       if (ifmgd->associated == req->bss) {
                bssid = req->bss->bssid;
-               ieee80211_set_disassoc(sdata, true);
-       } else list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (&wk->bss->cbss == req->bss) {
-                       bssid = req->bss->bssid;
-                       if (wk->state == IEEE80211_MGD_STATE_PROBE)
-                               not_auth_yet = true;
+               ieee80211_set_disassoc(sdata);
+               mutex_unlock(&ifmgd->mtx);
+       } else {
+               bool not_auth_yet = false;
+
+               mutex_unlock(&ifmgd->mtx);
+
+               mutex_lock(&local->work_mtx);
+               list_for_each_entry(wk, &local->work_list, list) {
+                       if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
+                               continue;
+                       if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
+                               continue;
+                       not_auth_yet = true;
                        list_del(&wk->list);
-                       kfree(wk);
+                       free_work(wk);
                        break;
                }
-       }
+               mutex_unlock(&local->work_mtx);
 
-       /*
-        * If somebody requests authentication and we haven't
-        * sent out an auth frame yet there's no need to send
-        * out a deauth frame either. If the state was PROBE,
-        * then this is the case. If it's AUTH we have sent a
-        * frame, and if it's IDLE we have completed the auth
-        * process already.
-        */
-       if (not_auth_yet) {
-               mutex_unlock(&ifmgd->mtx);
-               __cfg80211_auth_canceled(sdata->dev, bssid);
-               return 0;
-       }
-
-       /*
-        * cfg80211 should catch this ... but it's racy since
-        * we can receive a deauth frame, process it, hand it
-        * to cfg80211 while that's in a locked section already
-        * trying to tell us that the user wants to disconnect.
-        */
-       if (!bssid) {
-               mutex_unlock(&ifmgd->mtx);
-               return -ENOLINK;
+               /*
+                * If somebody requests authentication and we haven't
+                * sent out an auth frame yet there's no need to send
+                * out a deauth frame either. If the state was PROBE,
+                * then this is the case. If it's AUTH we have sent a
+                * frame, and if it's IDLE we have completed the auth
+                * process already.
+                */
+               if (not_auth_yet) {
+                       __cfg80211_auth_canceled(sdata->dev, bssid);
+                       return 0;
+               }
        }
 
-       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,
@@ -2588,15 +1999,15 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
         * to cfg80211 while that's in a locked section already
         * trying to tell us that the user wants to disconnect.
         */
-       if (&ifmgd->associated->cbss != req->bss) {
+       if (ifmgd->associated != req->bss) {
                mutex_unlock(&ifmgd->mtx);
                return -ENOLINK;
        }
 
        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_set_disassoc(sdata);
 
        mutex_unlock(&ifmgd->mtx);
 
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
new file mode 100644 (file)
index 0000000..1facfeb
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Off-channel operation helpers
+ *
+ * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2004, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+
+/*
+ * inform AP that we will go to sleep so that it will buffer the frames
+ * while we scan
+ */
+static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       local->offchannel_ps_enabled = false;
+
+       /* FIXME: what to do when local->pspolling is true? */
+
+       del_timer_sync(&local->dynamic_ps_timer);
+       cancel_work_sync(&local->dynamic_ps_enable_work);
+
+       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+               local->offchannel_ps_enabled = true;
+               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
+
+       if (!(local->offchannel_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
+                * hardware is creating the nullfunc frame for power save
+                * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
+                * enabled) and power save was enabled, the firmware just
+                * sent a null frame with power save disabled. So we need
+                * to send a new nullfunc frame to inform the AP that we
+                * are again sleeping.
+                */
+               ieee80211_send_nullfunc(local, sdata, 1);
+}
+
+/* inform AP that we are awake again, unless power save is enabled */
+static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+
+       if (!local->ps_sdata)
+               ieee80211_send_nullfunc(local, sdata, 0);
+       else if (local->offchannel_ps_enabled) {
+               /*
+                * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
+                * will send a nullfunc frame with the powersave bit set
+                * even though the AP already knows that we are sleeping.
+                * This could be avoided by sending a null frame with power
+                * save bit disabled before enabling the power save, but
+                * this doesn't gain anything.
+                *
+                * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
+                * to send a nullfunc frame because AP already knows that
+                * we are sleeping, let's just enable power save mode in
+                * hardware.
+                */
+               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));
+       }
+}
+
+void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+
+               /* disable beaconing */
+               if (sdata->vif.type == NL80211_IFTYPE_AP ||
+                   sdata->vif.type == NL80211_IFTYPE_ADHOC ||
+                   sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
+                       ieee80211_bss_info_change_notify(
+                               sdata, BSS_CHANGED_BEACON_ENABLED);
+
+               /*
+                * only handle non-STA interfaces here, STA interfaces
+                * are handled in ieee80211_offchannel_stop_station(),
+                * e.g., from the background scan state machine.
+                *
+                * In addition, do not stop monitor interface to allow it to be
+                * used from user space controlled off-channel operations.
+                */
+               if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+                   sdata->vif.type != NL80211_IFTYPE_MONITOR)
+                       netif_tx_stop_all_queues(sdata->dev);
+       }
+       mutex_unlock(&local->iflist_mtx);
+}
+
+void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       /*
+        * notify the AP about us leaving the channel and stop all STA interfaces
+        */
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       netif_tx_stop_all_queues(sdata->dev);
+                       if (sdata->u.mgd.associated)
+                               ieee80211_offchannel_ps_enable(sdata);
+               }
+       }
+       mutex_unlock(&local->iflist_mtx);
+}
+
+void ieee80211_offchannel_return(struct ieee80211_local *local,
+                                bool enable_beaconing)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (!ieee80211_sdata_running(sdata))
+                       continue;
+
+               /* Tell AP we're back */
+               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+                       if (sdata->u.mgd.associated)
+                               ieee80211_offchannel_ps_disable(sdata);
+                       netif_tx_wake_all_queues(sdata->dev);
+               }
+
+               /* re-enable beaconing */
+               if (enable_beaconing &&
+                   (sdata->vif.type == NL80211_IFTYPE_AP ||
+                    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
+                    sdata->vif.type == NL80211_IFTYPE_MESH_POINT))
+                       ieee80211_bss_info_change_notify(
+                               sdata, BSS_CHANGED_BEACON_ENABLED);
+       }
+       mutex_unlock(&local->iflist_mtx);
+}
index e535f1c988fe74cd2f5b147977b8904e3d74ee36..47f818959ad7a556ee24d6380fc6da440f6fc830 100644 (file)
@@ -10,7 +10,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
-       struct ieee80211_if_init_conf conf;
        struct sta_info *sta;
        unsigned long flags;
 
@@ -65,7 +64,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-                       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
+                       drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
                                       &sta->sta);
                }
 
@@ -93,17 +92,14 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
                        break;
                }
 
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
 
                /* disable beaconing */
                ieee80211_bss_info_change_notify(sdata,
                        BSS_CHANGED_BEACON_ENABLED);
 
-               conf.vif = &sdata->vif;
-               conf.type = sdata->vif.type;
-               conf.mac_addr = sdata->dev->dev_addr;
-               drv_remove_interface(local, &conf);
+               drv_remove_interface(local, &sdata->vif);
        }
 
        /* stop hardware - this must stop RX */
index 54296999834b98a4ef7f2aa3d20e3b5965f00080..e12f39a964609a41a2630ecd333c6288f14810de 100644 (file)
@@ -283,15 +283,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
        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 (!ieee80211_sdata_running(sdata))
+                       continue;
+
                if (prev_dev) {
                        skb2 = skb_clone(skb, GFP_ATOMIC);
                        if (skb2) {
@@ -361,7 +361,9 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
  * 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
@@ -375,25 +377,10 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
  */
 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 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
        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 @@ static void ap_sta_ps_start(struct sta_info *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 @@ static void ap_sta_ps_end(struct sta_info *sta)
 
 #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 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
                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 @@ static int
 __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) &&
@@ -1436,7 +1422,7 @@ __ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
             (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 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
         * 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 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
 {
        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;
@@ -1495,8 +1480,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                                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
@@ -1512,7 +1497,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
        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 @@ static ieee80211_rx_result debug_noinline
 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;
@@ -1576,94 +1555,34 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
        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);
        }
@@ -1721,7 +1640,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 
        /* 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--;
@@ -1738,10 +1657,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
 
                        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;
@@ -1872,7 +1791,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
        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;
        }
@@ -1896,7 +1815,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
        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);
@@ -2028,6 +1947,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+       ieee80211_rx_result rxs;
 
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_DROP_MONITOR;
@@ -2035,6 +1955,10 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
                return RX_DROP_MONITOR;
 
+       rxs = ieee80211_work_rx_mgmt(rx->sdata, rx->skb);
+       if (rxs != RX_CONTINUE)
+               return rxs;
+
        if (ieee80211_vif_is_mesh(&sdata->vif))
                return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
 
@@ -2139,7 +2063,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
        skb->protocol = htons(ETH_P_802_2);
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
 
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR ||
@@ -2276,7 +2200,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
                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;
@@ -2293,7 +2217,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
                                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;
@@ -2310,7 +2234,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
                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;
@@ -2321,11 +2245,11 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
        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;
@@ -2364,6 +2288,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
        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));
@@ -2380,68 +2306,76 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
        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 (!ieee80211_sdata_running(sdata))
+                               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);
index f934c9620b738a393429fba09ece0a80da912106..a4c63d4e6845f5f9dd12836d5f566425a6ccbc0c 100644 (file)
@@ -12,7 +12,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/wireless.h>
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <net/mac80211.h>
@@ -29,16 +28,19 @@ struct ieee80211_bss *
 ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
                     u8 *ssid, u8 ssid_len)
 {
-       return (void *)cfg80211_get_bss(local->hw.wiphy,
-                                       ieee80211_get_channel(local->hw.wiphy,
-                                                             freq),
-                                       bssid, ssid, ssid_len,
-                                       0, 0);
+       struct cfg80211_bss *cbss;
+
+       cbss = cfg80211_get_bss(local->hw.wiphy,
+                               ieee80211_get_channel(local->hw.wiphy, freq),
+                               bssid, ssid, ssid_len, 0, 0);
+       if (!cbss)
+               return NULL;
+       return (void *)cbss->priv;
 }
 
 static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss)
 {
-       struct ieee80211_bss *bss = (void *)cbss;
+       struct ieee80211_bss *bss = (void *)cbss->priv;
 
        kfree(bss_mesh_id(bss));
        kfree(bss_mesh_cfg(bss));
@@ -47,7 +49,9 @@ static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss)
 void ieee80211_rx_bss_put(struct ieee80211_local *local,
                          struct ieee80211_bss *bss)
 {
-       cfg80211_put_bss((struct cfg80211_bss *)bss);
+       if (!bss)
+               return;
+       cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv));
 }
 
 struct ieee80211_bss *
@@ -59,6 +63,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
                          struct ieee80211_channel *channel,
                          bool beacon)
 {
+       struct cfg80211_bss *cbss;
        struct ieee80211_bss *bss;
        int clen;
        s32 signal = 0;
@@ -68,13 +73,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
        else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
                signal = (rx_status->signal * 100) / local->hw.max_signal;
 
-       bss = (void *)cfg80211_inform_bss_frame(local->hw.wiphy, channel,
-                                               mgmt, len, signal, GFP_ATOMIC);
+       cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
+                                        mgmt, len, signal, GFP_ATOMIC);
 
-       if (!bss)
+       if (!cbss)
                return NULL;
 
-       bss->cbss.free_priv = ieee80211_rx_bss_free;
+       cbss->free_priv = ieee80211_rx_bss_free;
+       bss = (void *)cbss->priv;
 
        /* save the ERP value so that it is available at association time */
        if (elems->erp_info && elems->erp_info_len >= 1) {
@@ -147,7 +153,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
        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;
@@ -220,82 +226,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
        return true;
 }
 
-/*
- * inform AP that we will go to sleep so that it will buffer the frames
- * while we scan
- */
-static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-
-       local->scan_ps_enabled = false;
-
-       /* FIXME: what to do when local->pspolling is true? */
-
-       del_timer_sync(&local->dynamic_ps_timer);
-       cancel_work_sync(&local->dynamic_ps_enable_work);
-
-       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-               local->scan_ps_enabled = true;
-               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-       }
-
-       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
-                * hardware is creating the nullfunc frame for power save
-                * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
-                * enabled) and power save was enabled, the firmware just
-                * sent a null frame with power save disabled. So we need
-                * to send a new nullfunc frame to inform the AP that we
-                * are again sleeping.
-                */
-               ieee80211_send_nullfunc(local, sdata, 1);
-}
-
-/* inform AP that we are awake again, unless power save is enabled */
-static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-
-       if (!local->ps_sdata)
-               ieee80211_send_nullfunc(local, sdata, 0);
-       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
-                * even though the AP already knows that we are sleeping.
-                * This could be avoided by sending a null frame with power
-                * save bit disabled before enabling the power save, but
-                * this doesn't gain anything.
-                *
-                * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
-                * to send a nullfunc frame because AP already knows that
-                * we are sleeping, let's just enable power save mode in
-                * hardware.
-                */
-               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));
-       }
-}
-
 void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       struct ieee80211_sub_if_data *sdata;
        bool was_hw_scan;
 
        mutex_lock(&local->scan_mtx);
@@ -344,28 +277,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
 
        drv_sw_scan_complete(local);
 
-       mutex_lock(&local->iflist_mtx);
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
-                       continue;
-
-               /* Tell AP we're back */
-               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-                       if (sdata->u.mgd.associated) {
-                               ieee80211_scan_ps_disable(sdata);
-                               netif_tx_wake_all_queues(sdata->dev);
-                       }
-               } else
-                       netif_tx_wake_all_queues(sdata->dev);
-
-               /* re-enable beaconing */
-               if (sdata->vif.type == NL80211_IFTYPE_AP ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC ||
-                   sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
-                       ieee80211_bss_info_change_notify(
-                               sdata, BSS_CHANGED_BEACON_ENABLED);
-       }
-       mutex_unlock(&local->iflist_mtx);
+       ieee80211_offchannel_return(local, true);
 
  done:
        ieee80211_recalc_idle(local);
@@ -377,8 +289,6 @@ EXPORT_SYMBOL(ieee80211_scan_completed);
 
 static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 {
-       struct ieee80211_sub_if_data *sdata;
-
        /*
         * Hardware/driver doesn't support hw_scan, so use software
         * scanning instead. First send a nullfunc frame with power save
@@ -394,33 +304,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
         */
        drv_sw_scan_start(local);
 
-       mutex_lock(&local->iflist_mtx);
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
-                       continue;
-
-               /* disable beaconing */
-               if (sdata->vif.type == NL80211_IFTYPE_AP ||
-                   sdata->vif.type == NL80211_IFTYPE_ADHOC ||
-                   sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
-                       ieee80211_bss_info_change_notify(
-                               sdata, BSS_CHANGED_BEACON_ENABLED);
-
-               /*
-                * only handle non-STA interfaces here, STA interfaces
-                * are handled in the scan state machine
-                */
-               if (sdata->vif.type != NL80211_IFTYPE_STATION)
-                       netif_tx_stop_all_queues(sdata->dev);
-       }
-       mutex_unlock(&local->iflist_mtx);
+       ieee80211_offchannel_stop_beaconing(local);
 
        local->next_scan_state = SCAN_DECISION;
        local->scan_channel_idx = 0;
 
+       drv_flush(local, false);
+
        ieee80211_configure_filter(local);
 
-       /* TODO: start scan as soon as all nullfunc frames are ACKed */
        ieee80211_queue_delayed_work(&local->hw,
                                     &local->scan_work,
                                     IEEE80211_CHANNEL_TIME);
@@ -433,7 +325,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                                  struct cfg80211_scan_request *req)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        int rc;
 
        if (local->scan_req)
@@ -463,11 +354,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        local->scan_req = req;
        local->scan_sdata = sdata;
 
-       if (req != local->int_scan_req &&
-           sdata->vif.type == NL80211_IFTYPE_STATION &&
-           !list_empty(&ifmgd->work_list)) {
-               /* actually wait for the work it's doing to finish/time out */
-               set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
+       if (!list_empty(&local->work_list)) {
+               /* wait for the work to finish/time out */
                return 0;
        }
 
@@ -526,7 +414,7 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
        /* check if at least one STA interface is associated */
        mutex_lock(&local->iflist_mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
 
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
@@ -564,56 +452,35 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
 static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
                                                    unsigned long *next_delay)
 {
-       struct ieee80211_sub_if_data *sdata;
+       ieee80211_offchannel_stop_station(local);
+
+       __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
 
        /*
-        * notify the AP about us leaving the channel and stop all STA interfaces
+        * What if the nullfunc frames didn't arrive?
         */
-       mutex_lock(&local->iflist_mtx);
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
-                       continue;
-
-               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-                       netif_tx_stop_all_queues(sdata->dev);
-                       if (sdata->u.mgd.associated)
-                               ieee80211_scan_ps_enable(sdata);
-               }
-       }
-       mutex_unlock(&local->iflist_mtx);
-
-       __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
+       drv_flush(local, false);
+       if (local->ops->flush)
+               *next_delay = 0;
+       else
+               *next_delay = HZ / 10;
 
        /* advance to the next channel to be scanned */
-       *next_delay = HZ / 10;
        local->next_scan_state = SCAN_SET_CHANNEL;
 }
 
 static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local,
                                                    unsigned long *next_delay)
 {
-       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
-
        /* switch back to the operating channel */
        local->scan_channel = NULL;
        ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        /*
-        * notify the AP about us being back and restart all STA interfaces
+        * Only re-enable station mode interface now; beaconing will be
+        * re-enabled once the full scan has been completed.
         */
-       mutex_lock(&local->iflist_mtx);
-       list_for_each_entry(sdata, &local->interfaces, list) {
-               if (!netif_running(sdata->dev))
-                       continue;
-
-               /* Tell AP we're back */
-               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-                       if (sdata->u.mgd.associated)
-                               ieee80211_scan_ps_disable(sdata);
-                       netif_tx_wake_all_queues(sdata->dev);
-               }
-       }
-       mutex_unlock(&local->iflist_mtx);
+       ieee80211_offchannel_return(local, false);
 
        __clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
 
@@ -727,7 +594,7 @@ void ieee80211_scan_work(struct work_struct *work)
        /*
         * Avoid re-scheduling when the sdata is going away.
         */
-       if (!netif_running(sdata->dev)) {
+       if (!ieee80211_sdata_running(sdata)) {
                ieee80211_scan_completed(&local->hw, true);
                return;
        }
index aa743a895cf90f4a801758a9f0a4fd090b7ef784..7733f66ee2c411021b2232b70cee1382e51492c7 100644 (file)
@@ -35,7 +35,7 @@ static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_da
 
        if (!skb) {
                printk(KERN_ERR "%s: failed to allocate buffer for "
-                               "measurement report frame\n", sdata->dev->name);
+                               "measurement report frame\n", sdata->name);
                return;
        }
 
@@ -43,7 +43,7 @@ static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_da
        msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24);
        memset(msr_report, 0, 24);
        memcpy(msr_report->da, da, ETH_ALEN);
-       memcpy(msr_report->sa, sdata->dev->dev_addr, ETH_ALEN);
+       memcpy(msr_report->sa, sdata->vif.addr, ETH_ALEN);
        memcpy(msr_report->bssid, bssid, ETH_ALEN);
        msr_report->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                IEEE80211_STYPE_ACTION);
index 71f370dd24bcee545775eb6f9489c0d14b868aee..47da552ce8a6f6781a8553494be76e5478af2824 100644 (file)
@@ -103,13 +103,16 @@ static int sta_info_hash_del(struct ieee80211_local *local,
 }
 
 /* protected by RCU */
-struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr)
+struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
+                             const u8 *addr)
 {
+       struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
 
        sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
        while (sta) {
-               if (memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+               if (sta->sdata == sdata &&
+                   memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
                        break;
                sta = rcu_dereference(sta->hnext);
        }
@@ -356,6 +359,7 @@ int sta_info_insert(struct sta_info *sta)
 {
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct station_info sinfo;
        unsigned long flags;
        int err = 0;
 
@@ -364,12 +368,12 @@ int sta_info_insert(struct sta_info *sta)
         * something inserts a STA (on one CPU) without holding the RTNL
         * and another CPU turns off the net device.
         */
-       if (unlikely(!netif_running(sdata->dev))) {
+       if (unlikely(!ieee80211_sdata_running(sdata))) {
                err = -ENETDOWN;
                goto out_free;
        }
 
-       if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->dev->dev_addr) == 0 ||
+       if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
                    is_multicast_ether_addr(sta->sta.addr))) {
                err = -EINVAL;
                goto out_free;
@@ -377,7 +381,7 @@ int sta_info_insert(struct sta_info *sta)
 
        spin_lock_irqsave(&local->sta_lock, flags);
        /* check if STA exists already */
-       if (sta_info_get(local, sta->sta.addr)) {
+       if (sta_info_get(sdata, sta->sta.addr)) {
                spin_unlock_irqrestore(&local->sta_lock, flags);
                err = -EEXIST;
                goto out_free;
@@ -394,7 +398,7 @@ int sta_info_insert(struct sta_info *sta)
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-               drv_sta_notify(local, &sdata->vif, STA_NOTIFY_ADD, &sta->sta);
+               drv_sta_notify(local, sdata, STA_NOTIFY_ADD, &sta->sta);
                sdata = sta->sdata;
        }
 
@@ -405,6 +409,10 @@ int sta_info_insert(struct sta_info *sta)
 
        spin_unlock_irqrestore(&local->sta_lock, flags);
 
+       sinfo.filled = 0;
+       sinfo.generation = local->sta_generation;
+       cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_ATOMIC);
+
 #ifdef CONFIG_MAC80211_DEBUGFS
        /*
         * Debugfs entry adding might sleep, so schedule process
@@ -534,7 +542,7 @@ static void __sta_info_unlink(struct sta_info **sta)
                                             struct ieee80211_sub_if_data,
                                             u.ap);
 
-               drv_sta_notify(local, &sdata->vif, STA_NOTIFY_REMOVE,
+               drv_sta_notify(local, sdata, STA_NOTIFY_REMOVE,
                               &(*sta)->sta);
                sdata = (*sta)->sdata;
        }
@@ -828,7 +836,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                if (time_after(jiffies, sta->last_rx + exp_time)) {
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                        printk(KERN_DEBUG "%s: expiring inactive STA %pM\n",
-                              sdata->dev->name, sta->sta.addr);
+                              sdata->name, sta->sta.addr);
 #endif
                        __sta_info_unlink(&sta);
                        if (sta)
@@ -843,11 +851,12 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
                                               const u8 *addr)
 {
-       struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
+       struct sta_info *sta, *nxt;
 
-       if (!sta)
-               return NULL;
-       return &sta->sta;
+       /* Just return a random station ... first in list ... */
+       for_each_sta_info(hw_to_local(hw), addr, sta, nxt)
+               return &sta->sta;
+       return NULL;
 }
 EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw);
 
@@ -872,7 +881,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        struct ieee80211_local *local = sdata->local;
        int sent, buffered;
 
-       drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
+       drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
        if (!skb_queue_empty(&sta->ps_tx_buf))
                sta_info_clear_tim_bit(sta);
@@ -885,7 +894,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
-              "since STA not sleeping anymore\n", sdata->dev->name,
+              "since STA not sleeping anymore\n", sdata->name,
               sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 }
@@ -944,7 +953,7 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
                 */
                printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
                       "though there are no buffered frames for it\n",
-                      sdata->dev->name, sta->sta.addr);
+                      sdata->name, sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
        }
 }
index b4810f6aa94fa0ccfbe90af0e20f71882418701e..c8208236e8969aac81d0953cc71fdc85b8facd2d 100644 (file)
@@ -403,9 +403,34 @@ static inline u32 get_sta_flags(struct sta_info *sta)
 #define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
 
 /*
- * Get a STA info, must have be under RCU read lock.
+ * Get a STA info, must be under RCU read lock.
  */
-struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr);
+struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
+                             const u8 *addr);
+
+static inline
+void for_each_sta_info_type_check(struct ieee80211_local *local,
+                                 const u8 *addr,
+                                 struct sta_info *sta,
+                                 struct sta_info *nxt)
+{
+}
+
+#define for_each_sta_info(local, _addr, sta, nxt)                      \
+       for (   /* initialise loop */                                   \
+               sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\
+               nxt = sta ? rcu_dereference(sta->hnext) : NULL;         \
+               /* typecheck */                                         \
+               for_each_sta_info_type_check(local, (_addr), sta, nxt), \
+               /* continue condition */                                \
+               sta;                                                    \
+               /* advance loop */                                      \
+               sta = nxt,                                              \
+               nxt = sta ? rcu_dereference(sta->hnext) : NULL          \
+            )                                                          \
+       /* compare address and run code only if it matches */           \
+       if (memcmp(sta->sta.addr, (_addr), ETH_ALEN) == 0)
+
 /*
  * Get STA info by index, BROKEN!
  */
index d78f36c64c7bf72830303af60c3e2d2a71e7ac55..0ebcdda2420028819c0ed4f90e299d9f28f39f53 100644 (file)
@@ -134,6 +134,40 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
        dev_kfree_skb(skb);
 }
 
+static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
+{
+       struct ieee80211_mgmt *mgmt = (void *) skb->data;
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+       if (ieee80211_is_action(mgmt->frame_control) &&
+           sdata->vif.type == NL80211_IFTYPE_STATION &&
+           mgmt->u.action.category == WLAN_CATEGORY_HT &&
+           mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
+               /*
+                * This update looks racy, but isn't -- if we come
+                * here we've definitely got a station that we're
+                * talking to, and on a managed interface that can
+                * only be the AP. And the only other place updating
+                * this variable is before we're associated.
+                */
+               switch (mgmt->u.action.u.ht_smps.smps_control) {
+               case WLAN_HT_SMPS_CONTROL_DYNAMIC:
+                       sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_STATIC:
+                       sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC;
+                       break;
+               case WLAN_HT_SMPS_CONTROL_DISABLED:
+               default: /* shouldn't happen since we don't send that */
+                       sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF;
+                       break;
+               }
+
+               ieee80211_queue_work(&local->hw, &local->recalc_smps);
+       }
+}
+
 void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct sk_buff *skb2;
@@ -146,7 +180,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        struct ieee80211_tx_status_rtap_hdr *rthdr;
        struct ieee80211_sub_if_data *sdata;
        struct net_device *prev_dev = NULL;
-       struct sta_info *sta;
+       struct sta_info *sta, *tmp;
        int retry_count = -1, i;
        bool injected;
 
@@ -166,9 +200,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        sband = local->hw.wiphy->bands[info->band];
 
-       sta = sta_info_get(local, hdr->addr1);
+       for_each_sta_info(local, hdr->addr1, sta, tmp) {
+               /* skip wrong virtual interface */
+               if (memcmp(hdr->addr2, sta->sdata->vif.addr, ETH_ALEN))
+                       continue;
 
-       if (sta) {
                if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
                    test_sta_flags(sta, WLAN_STA_PS_STA)) {
                        /*
@@ -208,6 +244,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                rate_control_tx_status(local, sband, sta, skb);
                if (ieee80211_vif_is_mesh(&sta->sdata->vif))
                        ieee80211s_update_metric(local, sta, skb);
+
+               if (!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+                   (info->flags & IEEE80211_TX_STAT_ACK))
+                       ieee80211_frame_acked(sta, skb);
        }
 
        rcu_read_unlock();
@@ -311,7 +351,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        rcu_read_lock();
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
-                       if (!netif_running(sdata->dev))
+                       if (!ieee80211_sdata_running(sdata))
                                continue;
 
                        if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
index 4921d724b6c7bad2ed75897d3f75e03c3ef5e659..b73454a507f9cfd2dc89b45bac88352d32c75f2c 100644 (file)
@@ -100,7 +100,7 @@ static void tkip_mixing_phase1(const u8 *tk, struct tkip_ctx *ctx,
                p1k[3] += tkipS(p1k[2] ^ get_unaligned_le16(tk + 12 + j));
                p1k[4] += tkipS(p1k[3] ^ get_unaligned_le16(tk + 0 + j)) + i;
        }
-       ctx->initialized = 1;
+       ctx->state = TKIP_STATE_PHASE1_DONE;
 }
 
 static void tkip_mixing_phase2(const u8 *tk, struct tkip_ctx *ctx,
@@ -183,7 +183,7 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf,
        /* Update the p1k only when the iv16 in the packet wraps around, this
         * might occur after the wrap around of iv16 in the key in case of
         * fragmented packets. */
-       if (iv16 == 0 || !ctx->initialized)
+       if (iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT)
                tkip_mixing_phase1(tk, ctx, hdr->addr2, iv32);
 
        if (type == IEEE80211_TKIP_P1_KEY) {
@@ -209,7 +209,7 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
        const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
 
        /* Calculate per-packet key */
-       if (ctx->iv16 == 0 || !ctx->initialized)
+       if (ctx->iv16 == 0 || ctx->state == TKIP_STATE_NOT_INIT)
                tkip_mixing_phase1(tk, ctx, ta, ctx->iv32);
 
        tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key);
@@ -259,7 +259,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
        if ((keyid >> 6) != key->conf.keyidx)
                return TKIP_DECRYPT_INVALID_KEYIDX;
 
-       if (key->u.tkip.rx[queue].initialized &&
+       if (key->u.tkip.rx[queue].state != TKIP_STATE_NOT_INIT &&
            (iv32 < key->u.tkip.rx[queue].iv32 ||
             (iv32 == key->u.tkip.rx[queue].iv32 &&
              iv16 <= key->u.tkip.rx[queue].iv16))) {
@@ -275,11 +275,11 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
 
        if (only_iv) {
                res = TKIP_DECRYPT_OK;
-               key->u.tkip.rx[queue].initialized = 1;
+               key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED;
                goto done;
        }
 
-       if (!key->u.tkip.rx[queue].initialized ||
+       if (key->u.tkip.rx[queue].state == TKIP_STATE_NOT_INIT ||
            key->u.tkip.rx[queue].iv32 != iv32) {
                /* IV16 wrapped around - perform TKIP phase 1 */
                tkip_mixing_phase1(tk, &key->u.tkip.rx[queue], ta, iv32);
@@ -299,18 +299,20 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
                        printk("\n");
                }
 #endif
-               if (key->local->ops->update_tkip_key &&
-                       key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
-                       static const u8 bcast[ETH_ALEN] =
-                               {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-                       const u8 *sta_addr = key->sta->sta.addr;
-
-                       if (is_multicast_ether_addr(ra))
-                               sta_addr = bcast;
-
-                       drv_update_tkip_key(key->local, &key->conf, sta_addr,
-                                           iv32, key->u.tkip.rx[queue].p1k);
-               }
+       }
+       if (key->local->ops->update_tkip_key &&
+           key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
+           key->u.tkip.rx[queue].state != TKIP_STATE_PHASE1_HW_UPLOADED) {
+               static const u8 bcast[ETH_ALEN] =
+               {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+               const u8 *sta_addr = key->sta->sta.addr;
+
+               if (is_multicast_ether_addr(ra))
+                       sta_addr = bcast;
+
+               drv_update_tkip_key(key->local, &key->conf, sta_addr,
+                               iv32, key->u.tkip.rx[queue].p1k);
+               key->u.tkip.rx[queue].state = TKIP_STATE_PHASE1_HW_UPLOADED;
        }
 
        tkip_mixing_phase2(tk, &key->u.tkip.rx[queue], iv16, rc4key);
index ac210b58670223bdee80d46b8a9792a2a9115d80..140da4a7f13df89da0f7a472db02f0f3d6e3936f 100644 (file)
@@ -223,7 +223,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                        printk(KERN_DEBUG "%s: dropped data frame to not "
                               "associated station %pM\n",
-                              tx->dev->name, hdr->addr1);
+                              tx->sdata->name, hdr->addr1);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
                        I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc);
                        return TX_DROP;
@@ -331,7 +331,7 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
                if (net_ratelimit())
                        printk(KERN_DEBUG "%s: BC TX buffer full - dropping the oldest frame\n",
-                              tx->dev->name);
+                              tx->sdata->name);
 #endif
                dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
        } else
@@ -391,7 +391,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                        if (net_ratelimit()) {
                                printk(KERN_DEBUG "%s: STA %pM TX "
                                       "buffer full - dropping oldest frame\n",
-                                      tx->dev->name, sta->sta.addr);
+                                      tx->sdata->name, sta->sta.addr);
                        }
 #endif
                        dev_kfree_skb(old);
@@ -416,7 +416,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        else if (unlikely(staflags & WLAN_STA_PS_STA)) {
                printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll "
-                      "set -> send frame\n", tx->dev->name,
+                      "set -> send frame\n", tx->sdata->name,
                       sta->sta.addr);
        }
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
@@ -549,7 +549,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
                 "%s: Dropped data frame as no usable bitrate found while "
                 "scanning and associated. Target station: "
                 "%pM on %d GHz band\n",
-                tx->dev->name, hdr->addr1,
+                tx->sdata->name, hdr->addr1,
                 tx->channel->band ? 5 : 2))
                return TX_DROP;
 
@@ -1021,7 +1021,6 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
 
        memset(tx, 0, sizeof(*tx));
        tx->skb = skb;
-       tx->dev = sdata->dev; /* use original interface */
        tx->local = local;
        tx->sdata = sdata;
        tx->channel = local->hw.conf.channel;
@@ -1055,7 +1054,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                tx->sta = rcu_dereference(sdata->u.vlan.sta);
        if (!tx->sta)
-               tx->sta = sta_info_get(local, hdr->addr1);
+               tx->sta = sta_info_get(sdata, hdr->addr1);
 
        if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
            (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
@@ -1474,11 +1473,11 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 
                        list_for_each_entry_rcu(tmp_sdata, &local->interfaces,
                                                list) {
-                               if (!netif_running(tmp_sdata->dev))
+                               if (!ieee80211_sdata_running(tmp_sdata))
                                        continue;
                                if (tmp_sdata->vif.type != NL80211_IFTYPE_AP)
                                        continue;
-                               if (compare_ether_addr(tmp_sdata->dev->dev_addr,
+                               if (compare_ether_addr(tmp_sdata->vif.addr,
                                                       hdr->addr2) == 0) {
                                        sdata = tmp_sdata;
                                        break;
@@ -1642,7 +1641,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                        fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
                        /* RA TA DA SA */
                        memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
-                       memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+                       memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                        memcpy(hdr.addr3, skb->data, ETH_ALEN);
                        memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
                        hdrlen = 30;
@@ -1656,7 +1655,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
                /* DA BSSID SA */
                memcpy(hdr.addr1, skb->data, ETH_ALEN);
-               memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+               memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
                hdrlen = 24;
                break;
@@ -1664,7 +1663,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
                /* RA TA DA SA */
                memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN);
-               memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+               memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                memcpy(hdr.addr3, skb->data, ETH_ALEN);
                memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
                hdrlen = 30;
@@ -1678,8 +1677,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                        goto fail;
                }
 
-               if (compare_ether_addr(dev->dev_addr,
-                                         skb->data + ETH_ALEN) == 0) {
+               if (compare_ether_addr(sdata->vif.addr,
+                                      skb->data + ETH_ALEN) == 0) {
                        hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
                                        skb->data, skb->data + ETH_ALEN);
                        meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
@@ -1709,7 +1708,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                                }
                        }
                        hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
-                                       mesh_da, dev->dev_addr);
+                                       mesh_da, sdata->vif.addr);
                        rcu_read_unlock();
                        if (is_mesh_mcast)
                                meshhdrlen =
@@ -1734,7 +1733,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                if (sdata->u.mgd.use_4addr && ethertype != ETH_P_PAE) {
                        fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
                        /* RA TA DA SA */
-                       memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+                       memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                        memcpy(hdr.addr3, skb->data, ETH_ALEN);
                        memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
                        hdrlen = 30;
@@ -1765,9 +1764,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
         */
        if (!is_multicast_ether_addr(hdr.addr1)) {
                rcu_read_lock();
-               sta = sta_info_get(local, hdr.addr1);
-               /* XXX: in the future, use sdata to look up the sta */
-               if (sta && sta->sdata == sdata)
+               sta = sta_info_get(sdata, hdr.addr1);
+               if (sta)
                        sta_flags = get_sta_flags(sta);
                rcu_read_unlock();
        }
@@ -1786,7 +1784,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                unlikely(!is_multicast_ether_addr(hdr.addr1) &&
                      !(sta_flags & WLAN_STA_AUTHORIZED) &&
                      !(ethertype == ETH_P_PAE &&
-                      compare_ether_addr(dev->dev_addr,
+                      compare_ether_addr(sdata->vif.addr,
                                          skb->data + ETH_ALEN) == 0))) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                if (net_ratelimit())
@@ -1926,7 +1924,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
                ieee80211_tx(sdata, skb, true);
        } else {
                hdr = (struct ieee80211_hdr *)skb->data;
-               sta = sta_info_get(local, hdr->addr1);
+               sta = sta_info_get(sdata, hdr->addr1);
 
                ret = __ieee80211_tx(local, &skb, sta, true);
                if (ret != IEEE80211_TX_OK)
@@ -2150,8 +2148,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
                mgmt->frame_control =
                    cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
                memset(mgmt->da, 0xff, ETH_ALEN);
-               memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
-               memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
+               memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+               memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
                mgmt->u.beacon.beacon_int =
                        cpu_to_le16(sdata->vif.bss_conf.beacon_int);
                mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */
index 3848140313f50dad60759e5fd55a7923fa46f5ff..bc73904d561c4c2c716397c42b6d23d1154c3cfa 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
-#include <linux/wireless.h>
 #include <linux/bitmap.h>
 #include <linux/crc32.h>
 #include <net/net_namespace.h>
@@ -480,8 +479,8 @@ void ieee80211_iterate_active_interfaces(
                case NL80211_IFTYPE_MESH_POINT:
                        break;
                }
-               if (netif_running(sdata->dev))
-                       iterator(data, sdata->dev->dev_addr,
+               if (ieee80211_sdata_running(sdata))
+                       iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
 
@@ -514,8 +513,8 @@ void ieee80211_iterate_active_interfaces_atomic(
                case NL80211_IFTYPE_MESH_POINT:
                        break;
                }
-               if (netif_running(sdata->dev))
-                       iterator(data, sdata->dev->dev_addr,
+               if (ieee80211_sdata_running(sdata))
+                       iterator(data, sdata->vif.addr,
                                 &sdata->vif);
        }
 
@@ -860,7 +859,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                            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);
@@ -870,7 +869,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        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);
@@ -893,43 +892,87 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             enum ieee80211_band band)
 {
        struct ieee80211_supported_band *sband;
-       u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
-       int i;
+       u8 *pos;
+       size_t offset = 0, noffset;
+       int supp_rates_len, i;
 
        sband = local->hw.wiphy->bands[band];
 
        pos = buffer;
 
+       supp_rates_len = min_t(int, sband->n_bitrates, 8);
+
        *pos++ = WLAN_EID_SUPP_RATES;
-       supp_rates_len = pos;
-       *pos++ = 0;
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *rate = &sband->bitrates[i];
-
-               if (esupp_rates_len) {
-                       *esupp_rates_len += 1;
-               } else if (*supp_rates_len == 8) {
-                       *pos++ = WLAN_EID_EXT_SUPP_RATES;
-                       esupp_rates_len = pos;
-                       *pos++ = 1;
-               } else
-                       *supp_rates_len += 1;
+       *pos++ = supp_rates_len;
 
-               *pos++ = rate->bitrate / 5;
+       for (i = 0; i < supp_rates_len; i++) {
+               int rate = sband->bitrates[i].bitrate;
+               *pos++ = (u8) (rate / 5);
+       }
+
+       /* insert "request information" if in custom IEs */
+       if (ie && ie_len) {
+               static const u8 before_extrates[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_REQUEST,
+               };
+               noffset = ieee80211_ie_split(ie, ie_len,
+                                            before_extrates,
+                                            ARRAY_SIZE(before_extrates),
+                                            offset);
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
+               offset = noffset;
+       }
+
+       if (sband->n_bitrates > i) {
+               *pos++ = WLAN_EID_EXT_SUPP_RATES;
+               *pos++ = sband->n_bitrates - i;
+
+               for (; i < sband->n_bitrates; i++) {
+                       int rate = sband->bitrates[i].bitrate;
+                       *pos++ = (u8) (rate / 5);
+               }
+       }
+
+       /* insert custom IEs that go before HT */
+       if (ie && ie_len) {
+               static const u8 before_ht[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_REQUEST,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_DS_PARAMS,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+               };
+               noffset = ieee80211_ie_split(ie, ie_len,
+                                            before_ht, ARRAY_SIZE(before_ht),
+                                            offset);
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
+               offset = noffset;
        }
 
        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 */
@@ -940,9 +983,11 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
         * that calculates local->scan_ies_len.
         */
 
-       if (ie) {
-               memcpy(pos, ie, ie_len);
-               pos += ie_len;
+       /* add any remaining custom IEs */
+       if (ie && ie_len) {
+               noffset = ie_len;
+               memcpy(pos, ie + offset, noffset - offset);
+               pos += noffset - offset;
        }
 
        return pos - buffer;
@@ -961,7 +1006,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
                            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);
@@ -970,7 +1015,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        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);
@@ -1041,7 +1086,6 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 {
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_sub_if_data *sdata;
-       struct ieee80211_if_init_conf conf;
        struct sta_info *sta;
        unsigned long flags;
        int res;
@@ -1061,7 +1105,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                if (res) {
                        WARN(local->suspended, "Harware became unavailable "
                             "upon resume. This is could be a software issue"
-                            "prior to suspend or a harware issue\n");
+                            "prior to suspend or a hardware issue\n");
                        return res;
                }
 
@@ -1072,12 +1116,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
                    sdata->vif.type != NL80211_IFTYPE_MONITOR &&
-                   netif_running(sdata->dev)) {
-                       conf.vif = &sdata->vif;
-                       conf.type = sdata->vif.type;
-                       conf.mac_addr = sdata->dev->dev_addr;
-                       res = drv_add_interface(local, &conf);
-               }
+                   ieee80211_sdata_running(sdata))
+                       res = drv_add_interface(local, &sdata->vif);
        }
 
        /* add STAs back */
@@ -1090,7 +1130,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                             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);
@@ -1119,7 +1159,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        /* Finally also reconfigure all the BSS information */
        list_for_each_entry(sdata, &local->interfaces, list) {
                u32 changed = ~0;
-               if (!netif_running(sdata->dev))
+               if (!ieee80211_sdata_running(sdata))
                        continue;
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_STATION:
@@ -1147,7 +1187,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 
        /* add back keys */
        list_for_each_entry(sdata, &local->interfaces, list)
-               if (netif_running(sdata->dev))
+               if (ieee80211_sdata_running(sdata))
                        ieee80211_enable_keys(sdata);
 
        ieee80211_wake_queues_by_reason(hw,
@@ -1194,3 +1234,133 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        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);
+}
+
+static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
+{
+       int i;
+
+       for (i = 0; i < n_ids; i++)
+               if (ids[i] == id)
+                       return true;
+       return false;
+}
+
+/**
+ * ieee80211_ie_split - split an IE buffer according to ordering
+ *
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ *     the split
+ * @n_ids: the size of the element ID array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+                         const u8 *ids, int n_ids, size_t offset)
+{
+       size_t pos = offset;
+
+       while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
+               pos += 2 + ies[pos + 1];
+
+       return pos;
+}
+
+size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
+{
+       size_t pos = offset;
+
+       while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC)
+               pos += 2 + ies[pos + 1];
+
+       return pos;
+}
index 79d887dae7381b841d6ca8ea25ddf9694e6ba768..34e6d02da779f89731a33261745eea8949d14490 100644 (file)
@@ -96,7 +96,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
        }
 
        if (!sta && ra && !is_multicast_ether_addr(ra)) {
-               sta = sta_info_get(local, ra);
+               sta = sta_info_get(sdata, ra);
                if (sta)
                        sta_flags = get_sta_flags(sta);
        }
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
new file mode 100644 (file)
index 0000000..5ba7599
--- /dev/null
@@ -0,0 +1,1086 @@
+/*
+ * mac80211 work implementation
+ *
+ * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright 2004, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "rate.h"
+
+#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
+#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_MAX_PROBE_TRIES 5
+
+enum work_action {
+       WORK_ACT_NONE,
+       WORK_ACT_TIMEOUT,
+       WORK_ACT_DONE,
+};
+
+
+/* utils */
+static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
+{
+       WARN_ON(!mutex_is_locked(&local->work_mtx));
+}
+
+/*
+ * We can have multiple work items (and connection probing)
+ * scheduling this timer, but we need to take care to only
+ * reschedule it when it should fire _earlier_ than it was
+ * asked for before, or if it's not pending right now. This
+ * function ensures that. Note that it then is required to
+ * run this function for all timeouts after the first one
+ * has happened -- the work that runs from this timer will
+ * do that.
+ */
+static void run_again(struct ieee80211_local *local,
+                     unsigned long timeout)
+{
+       ASSERT_WORK_MTX(local);
+
+       if (!timer_pending(&local->work_timer) ||
+           time_before(timeout, local->work_timer.expires))
+               mod_timer(&local->work_timer, timeout);
+}
+
+static void work_free_rcu(struct rcu_head *head)
+{
+       struct ieee80211_work *wk =
+               container_of(head, struct ieee80211_work, rcu_head);
+
+       kfree(wk);
+}
+
+void free_work(struct ieee80211_work *wk)
+{
+       call_rcu(&wk->rcu_head, work_free_rcu);
+}
+
+static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
+                                     struct ieee80211_supported_band *sband,
+                                     u32 *rates)
+{
+       int i, j, count;
+       *rates = 0;
+       count = 0;
+       for (i = 0; i < supp_rates_len; i++) {
+               int rate = (supp_rates[i] & 0x7F) * 5;
+
+               for (j = 0; j < sband->n_bitrates; j++)
+                       if (sband->bitrates[j].bitrate == rate) {
+                               *rates |= BIT(j);
+                               count++;
+                               break;
+                       }
+       }
+
+       return count;
+}
+
+/* frame sending functions */
+
+static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
+                               struct ieee80211_supported_band *sband,
+                               struct ieee80211_channel *channel,
+                               enum ieee80211_smps_mode smps)
+{
+       struct ieee80211_ht_info *ht_info;
+       u8 *pos;
+       u32 flags = channel->flags;
+       u16 cap = sband->ht_cap.cap;
+       __le16 tmp;
+
+       if (!sband->ht_cap.ht_supported)
+               return;
+
+       if (!ht_info_ie)
+               return;
+
+       if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
+               return;
+
+       ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
+
+       /* 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) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
+               break;
+       case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+               if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
+                       cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+                       cap &= ~IEEE80211_HT_CAP_SGI_40;
+               }
+               break;
+       }
+
+       /* set SM PS mode properly */
+       cap &= ~IEEE80211_HT_CAP_SM_PS;
+       switch (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);
+
+       /* AMPDU parameters */
+       *pos++ = sband->ht_cap.ampdu_factor |
+                (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);
+}
+
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+                                struct ieee80211_work *wk)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt;
+       u8 *pos;
+       const u8 *ies;
+       size_t offset = 0, noffset;
+       int i, len, count, rates_len, supp_rates_len;
+       u16 capab;
+       struct ieee80211_supported_band *sband;
+       u32 rates = 0;
+
+       sband = local->hw.wiphy->bands[wk->chan->band];
+
+       /*
+        * Get all rates supported by the device and the AP as
+        * some APs don't like getting a superset of their rates
+        * in the association request (e.g. D-Link DAP 1353 in
+        * b-only mode)...
+        */
+       rates_len = ieee80211_compatible_rates(wk->assoc.supp_rates,
+                                              wk->assoc.supp_rates_len,
+                                              sband, &rates);
+
+       skb = alloc_skb(local->hw.extra_tx_headroom +
+                       sizeof(*mgmt) + /* bit too much but doesn't matter */
+                       2 + wk->assoc.ssid_len + /* SSID */
+                       4 + rates_len + /* (extended) rates */
+                       4 + /* power capability */
+                       2 + 2 * sband->n_channels + /* supported channels */
+                       2 + sizeof(struct ieee80211_ht_cap) + /* HT */
+                       wk->ie_len + /* extra IEs */
+                       9, /* WMM */
+                       GFP_KERNEL);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
+                      "frame\n", sdata->name);
+               return;
+       }
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       capab = WLAN_CAPABILITY_ESS;
+
+       if (sband->band == IEEE80211_BAND_2GHZ) {
+               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
+                       capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+               if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
+                       capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+       }
+
+       if (wk->assoc.capability & WLAN_CAPABILITY_PRIVACY)
+               capab |= WLAN_CAPABILITY_PRIVACY;
+
+       if ((wk->assoc.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+           (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+               capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+
+       mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+       memset(mgmt, 0, 24);
+       memcpy(mgmt->da, wk->filter_ta, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt->bssid, wk->filter_ta, ETH_ALEN);
+
+       if (!is_zero_ether_addr(wk->assoc.prev_bssid)) {
+               skb_put(skb, 10);
+               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_REASSOC_REQ);
+               mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
+               mgmt->u.reassoc_req.listen_interval =
+                               cpu_to_le16(local->hw.conf.listen_interval);
+               memcpy(mgmt->u.reassoc_req.current_ap, wk->assoc.prev_bssid,
+                      ETH_ALEN);
+       } else {
+               skb_put(skb, 4);
+               mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                                 IEEE80211_STYPE_ASSOC_REQ);
+               mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
+               mgmt->u.assoc_req.listen_interval =
+                               cpu_to_le16(local->hw.conf.listen_interval);
+       }
+
+       /* SSID */
+       ies = pos = skb_put(skb, 2 + wk->assoc.ssid_len);
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = wk->assoc.ssid_len;
+       memcpy(pos, wk->assoc.ssid, wk->assoc.ssid_len);
+
+       /* add all rates which were marked to be used above */
+       supp_rates_len = rates_len;
+       if (supp_rates_len > 8)
+               supp_rates_len = 8;
+
+       len = sband->n_bitrates;
+       pos = skb_put(skb, supp_rates_len + 2);
+       *pos++ = WLAN_EID_SUPP_RATES;
+       *pos++ = supp_rates_len;
+
+       count = 0;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               if (BIT(i) & rates) {
+                       int rate = sband->bitrates[i].bitrate;
+                       *pos++ = (u8) (rate / 5);
+                       if (++count == 8)
+                               break;
+               }
+       }
+
+       if (rates_len > count) {
+               pos = skb_put(skb, rates_len - count + 2);
+               *pos++ = WLAN_EID_EXT_SUPP_RATES;
+               *pos++ = rates_len - count;
+
+               for (i++; i < sband->n_bitrates; i++) {
+                       if (BIT(i) & rates) {
+                               int rate = sband->bitrates[i].bitrate;
+                               *pos++ = (u8) (rate / 5);
+                       }
+               }
+       }
+
+       if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
+               /* 1. power capabilities */
+               pos = skb_put(skb, 4);
+               *pos++ = WLAN_EID_PWR_CAPABILITY;
+               *pos++ = 2;
+               *pos++ = 0; /* min tx power */
+               *pos++ = wk->chan->max_power; /* max tx power */
+
+               /* 2. supported channels */
+               /* TODO: get this in reg domain format */
+               pos = skb_put(skb, 2 * sband->n_channels + 2);
+               *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+               *pos++ = 2 * sband->n_channels;
+               for (i = 0; i < sband->n_channels; i++) {
+                       *pos++ = ieee80211_frequency_to_channel(
+                                       sband->channels[i].center_freq);
+                       *pos++ = 1; /* one channel in the subband*/
+               }
+       }
+
+       /* if present, add any custom IEs that go before HT */
+       if (wk->ie_len && wk->ie) {
+               static const u8 before_ht[] = {
+                       WLAN_EID_SSID,
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_PWR_CAPABILITY,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_RRM_ENABLED_CAPABILITIES,
+                       WLAN_EID_MOBILITY_DOMAIN,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+               };
+               noffset = ieee80211_ie_split(wk->ie, wk->ie_len,
+                                            before_ht, ARRAY_SIZE(before_ht),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, wk->ie + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       if (wk->assoc.use_11n && wk->assoc.wmm_used &&
+           local->hw.queues >= 4)
+               ieee80211_add_ht_ie(skb, wk->assoc.ht_information_ie,
+                                   sband, wk->chan, wk->assoc.smps);
+
+       /* if present, add any custom non-vendor IEs that go after HT */
+       if (wk->ie_len && wk->ie) {
+               noffset = ieee80211_ie_split_vendor(wk->ie, wk->ie_len,
+                                                   offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, wk->ie + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       if (wk->assoc.wmm_used && local->hw.queues >= 4) {
+               pos = skb_put(skb, 9);
+               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+               *pos++ = 7; /* len */
+               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+               *pos++ = 0x50;
+               *pos++ = 0xf2;
+               *pos++ = 2; /* WME */
+               *pos++ = 0; /* WME info */
+               *pos++ = 1; /* WME ver */
+               *pos++ = 0;
+       }
+
+       /* add any remaining custom (i.e. vendor specific here) IEs */
+       if (wk->ie_len && wk->ie) {
+               noffset = wk->ie_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, wk->ie + offset, noffset - offset);
+       }
+
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       ieee80211_tx_skb(sdata, skb);
+}
+
+static void ieee80211_remove_auth_bss(struct ieee80211_local *local,
+                                     struct ieee80211_work *wk)
+{
+       struct cfg80211_bss *cbss;
+       u16 capa_val = WLAN_CAPABILITY_ESS;
+
+       if (wk->probe_auth.privacy)
+               capa_val |= WLAN_CAPABILITY_PRIVACY;
+
+       cbss = cfg80211_get_bss(local->hw.wiphy, wk->chan, wk->filter_ta,
+                               wk->probe_auth.ssid, wk->probe_auth.ssid_len,
+                               WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
+                               capa_val);
+       if (!cbss)
+               return;
+
+       cfg80211_unlink_bss(local->hw.wiphy, cbss);
+       cfg80211_put_bss(cbss);
+}
+
+static enum work_action __must_check
+ieee80211_direct_probe(struct ieee80211_work *wk)
+{
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
+       struct ieee80211_local *local = sdata->local;
+
+       wk->probe_auth.tries++;
+       if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
+               printk(KERN_DEBUG "%s: direct probe to %pM timed out\n",
+                      sdata->name, wk->filter_ta);
+
+               /*
+                * Most likely AP is not in the range so remove the
+                * bss struct for that AP.
+                */
+               ieee80211_remove_auth_bss(local, wk);
+
+               return WORK_ACT_TIMEOUT;
+       }
+
+       printk(KERN_DEBUG "%s: direct probe to %pM (try %d)\n",
+                       sdata->name, wk->filter_ta, wk->probe_auth.tries);
+
+       /*
+        * Direct probe is sent to broadcast address as some APs
+        * will not answer to direct packet in unassociated state.
+        */
+       ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid,
+                                wk->probe_auth.ssid_len, NULL, 0);
+
+       wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+       run_again(local, wk->timeout);
+
+       return WORK_ACT_NONE;
+}
+
+
+static enum work_action __must_check
+ieee80211_authenticate(struct ieee80211_work *wk)
+{
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
+       struct ieee80211_local *local = sdata->local;
+
+       wk->probe_auth.tries++;
+       if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
+               printk(KERN_DEBUG "%s: authentication with %pM"
+                      " timed out\n", sdata->name, wk->filter_ta);
+
+               /*
+                * Most likely AP is not in the range so remove the
+                * bss struct for that AP.
+                */
+               ieee80211_remove_auth_bss(local, wk);
+
+               return WORK_ACT_TIMEOUT;
+       }
+
+       printk(KERN_DEBUG "%s: authenticate with %pM (try %d)\n",
+              sdata->name, wk->filter_ta, wk->probe_auth.tries);
+
+       ieee80211_send_auth(sdata, 1, wk->probe_auth.algorithm, wk->ie,
+                           wk->ie_len, wk->filter_ta, NULL, 0, 0);
+       wk->probe_auth.transaction = 2;
+
+       wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+       run_again(local, wk->timeout);
+
+       return WORK_ACT_NONE;
+}
+
+static enum work_action __must_check
+ieee80211_associate(struct ieee80211_work *wk)
+{
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
+       struct ieee80211_local *local = sdata->local;
+
+       wk->assoc.tries++;
+       if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
+               printk(KERN_DEBUG "%s: association with %pM"
+                      " timed out\n",
+                      sdata->name, wk->filter_ta);
+
+               /*
+                * Most likely AP is not in the range so remove the
+                * bss struct for that AP.
+                */
+               if (wk->assoc.bss)
+                       cfg80211_unlink_bss(local->hw.wiphy, wk->assoc.bss);
+
+               return WORK_ACT_TIMEOUT;
+       }
+
+       printk(KERN_DEBUG "%s: associate with %pM (try %d)\n",
+              sdata->name, wk->filter_ta, wk->assoc.tries);
+       ieee80211_send_assoc(sdata, wk);
+
+       wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+       run_again(local, wk->timeout);
+
+       return WORK_ACT_NONE;
+}
+
+static enum work_action __must_check
+ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
+{
+       /*
+        * First time we run, do nothing -- the generic code will
+        * have switched to the right channel etc.
+        */
+       if (!wk->remain.started) {
+               wk->remain.started = true;
+               wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration);
+
+               cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk,
+                                         wk->chan, wk->chan_type,
+                                         wk->remain.duration, GFP_KERNEL);
+
+               return WORK_ACT_NONE;
+       }
+
+       return WORK_ACT_TIMEOUT;
+}
+
+static void ieee80211_auth_challenge(struct ieee80211_work *wk,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
+{
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
+       u8 *pos;
+       struct ieee802_11_elems elems;
+
+       pos = mgmt->u.auth.variable;
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+       if (!elems.challenge)
+               return;
+       ieee80211_send_auth(sdata, 3, wk->probe_auth.algorithm,
+                           elems.challenge - 2, elems.challenge_len + 2,
+                           wk->filter_ta, wk->probe_auth.key,
+                           wk->probe_auth.key_len, wk->probe_auth.key_idx);
+       wk->probe_auth.transaction = 4;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_auth(struct ieee80211_work *wk,
+                      struct ieee80211_mgmt *mgmt, size_t len)
+{
+       u16 auth_alg, auth_transaction, status_code;
+
+       if (wk->type != IEEE80211_WORK_AUTH)
+               return WORK_ACT_NONE;
+
+       if (len < 24 + 6)
+               return WORK_ACT_NONE;
+
+       auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+       auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+       status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+       if (auth_alg != wk->probe_auth.algorithm ||
+           auth_transaction != wk->probe_auth.transaction)
+               return WORK_ACT_NONE;
+
+       if (status_code != WLAN_STATUS_SUCCESS) {
+               printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n",
+                      wk->sdata->name, mgmt->sa, status_code);
+               return WORK_ACT_DONE;
+       }
+
+       switch (wk->probe_auth.algorithm) {
+       case WLAN_AUTH_OPEN:
+       case WLAN_AUTH_LEAP:
+       case WLAN_AUTH_FT:
+               break;
+       case WLAN_AUTH_SHARED_KEY:
+               if (wk->probe_auth.transaction != 4) {
+                       ieee80211_auth_challenge(wk, mgmt, len);
+                       /* need another frame */
+                       return WORK_ACT_NONE;
+               }
+               break;
+       default:
+               WARN_ON(1);
+               return WORK_ACT_NONE;
+       }
+
+       printk(KERN_DEBUG "%s: authenticated\n", wk->sdata->name);
+       return WORK_ACT_DONE;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk,
+                            struct ieee80211_mgmt *mgmt, size_t len,
+                            bool reassoc)
+{
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
+       struct ieee80211_local *local = sdata->local;
+       u16 capab_info, status_code, aid;
+       struct ieee802_11_elems elems;
+       u8 *pos;
+
+       /*
+        * AssocResp and ReassocResp have identical structure, so process both
+        * of them in this function.
+        */
+
+       if (len < 24 + 6)
+               return WORK_ACT_NONE;
+
+       capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
+       status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+       aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
+
+       printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
+              "status=%d aid=%d)\n",
+              sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
+              capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
+
+       pos = mgmt->u.assoc_resp.variable;
+       ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
+       if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
+           elems.timeout_int && elems.timeout_int_len == 5 &&
+           elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+               u32 tu, ms;
+               tu = get_unaligned_le32(elems.timeout_int + 1);
+               ms = tu * 1024 / 1000;
+               printk(KERN_DEBUG "%s: %pM rejected association temporarily; "
+                      "comeback duration %u TU (%u ms)\n",
+                      sdata->name, mgmt->sa, tu, ms);
+               wk->timeout = jiffies + msecs_to_jiffies(ms);
+               if (ms > IEEE80211_ASSOC_TIMEOUT)
+                       run_again(local, wk->timeout);
+               return WORK_ACT_NONE;
+       }
+
+       if (status_code != WLAN_STATUS_SUCCESS)
+               printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n",
+                      sdata->name, mgmt->sa, status_code);
+       else
+               printk(KERN_DEBUG "%s: associated\n", sdata->name);
+
+       return WORK_ACT_DONE;
+}
+
+static enum work_action __must_check
+ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
+                            struct ieee80211_mgmt *mgmt, size_t len,
+                            struct ieee80211_rx_status *rx_status)
+{
+       struct ieee80211_sub_if_data *sdata = wk->sdata;
+       struct ieee80211_local *local = sdata->local;
+       size_t baselen;
+
+       ASSERT_WORK_MTX(local);
+
+       baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
+       if (baselen > len)
+               return WORK_ACT_NONE;
+
+       printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
+       return WORK_ACT_DONE;
+}
+
+static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
+                                         struct sk_buff *skb)
+{
+       struct ieee80211_rx_status *rx_status;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_work *wk;
+       enum work_action rma = WORK_ACT_NONE;
+       u16 fc;
+
+       rx_status = (struct ieee80211_rx_status *) skb->cb;
+       mgmt = (struct ieee80211_mgmt *) skb->data;
+       fc = le16_to_cpu(mgmt->frame_control);
+
+       mutex_lock(&local->work_mtx);
+
+       list_for_each_entry(wk, &local->work_list, list) {
+               const u8 *bssid = NULL;
+
+               switch (wk->type) {
+               case IEEE80211_WORK_DIRECT_PROBE:
+               case IEEE80211_WORK_AUTH:
+               case IEEE80211_WORK_ASSOC:
+                       bssid = wk->filter_ta;
+                       break;
+               default:
+                       continue;
+               }
+
+               /*
+                * Before queuing, we already verified mgmt->sa,
+                * so this is needed just for matching.
+                */
+               if (compare_ether_addr(bssid, mgmt->bssid))
+                       continue;
+
+               switch (fc & IEEE80211_FCTL_STYPE) {
+               case IEEE80211_STYPE_PROBE_RESP:
+                       rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
+                                                          rx_status);
+                       break;
+               case IEEE80211_STYPE_AUTH:
+                       rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len);
+                       break;
+               case IEEE80211_STYPE_ASSOC_RESP:
+                       rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
+                                                          skb->len, false);
+                       break;
+               case IEEE80211_STYPE_REASSOC_RESP:
+                       rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
+                                                          skb->len, true);
+                       break;
+               default:
+                       WARN_ON(1);
+               }
+               /*
+                * We've processed this frame for that work, so it can't
+                * belong to another work struct.
+                * NB: this is also required for correctness for 'rma'!
+                */
+               break;
+       }
+
+       switch (rma) {
+       case WORK_ACT_NONE:
+               break;
+       case WORK_ACT_DONE:
+               list_del_rcu(&wk->list);
+               break;
+       default:
+               WARN(1, "unexpected: %d", rma);
+       }
+
+       mutex_unlock(&local->work_mtx);
+
+       if (rma != WORK_ACT_DONE)
+               goto out;
+
+       switch (wk->done(wk, skb)) {
+       case WORK_DONE_DESTROY:
+               free_work(wk);
+               break;
+       case WORK_DONE_REQUEUE:
+               synchronize_rcu();
+               wk->started = false; /* restart */
+               mutex_lock(&local->work_mtx);
+               list_add_tail(&wk->list, &local->work_list);
+               mutex_unlock(&local->work_mtx);
+       }
+
+ out:
+       kfree_skb(skb);
+}
+
+static void ieee80211_work_timer(unsigned long data)
+{
+       struct ieee80211_local *local = (void *) data;
+
+       if (local->quiescing)
+               return;
+
+       ieee80211_queue_work(&local->hw, &local->work_work);
+}
+
+static void ieee80211_work_work(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, work_work);
+       struct sk_buff *skb;
+       struct ieee80211_work *wk, *tmp;
+       LIST_HEAD(free_work);
+       enum work_action rma;
+       bool remain_off_channel = false;
+
+       if (local->scanning)
+               return;
+
+       /*
+        * ieee80211_queue_work() should have picked up most cases,
+        * here we'll pick the the rest.
+        */
+       if (WARN(local->suspended, "work scheduled while going to suspend\n"))
+               return;
+
+       /* first process frames to avoid timing out while a frame is pending */
+       while ((skb = skb_dequeue(&local->work_skb_queue)))
+               ieee80211_work_rx_queued_mgmt(local, skb);
+
+       ieee80211_recalc_idle(local);
+
+       mutex_lock(&local->work_mtx);
+
+       list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
+               /* mark work as started if it's on the current off-channel */
+               if (!wk->started && local->tmp_channel &&
+                   wk->chan == local->tmp_channel &&
+                   wk->chan_type == local->tmp_channel_type) {
+                       wk->started = true;
+               }
+
+               if (!wk->started && !local->tmp_channel) {
+                       /*
+                        * TODO: could optimize this by leaving the
+                        *       station vifs in awake mode if they
+                        *       happen to be on the same channel as
+                        *       the requested channel
+                        */
+                       ieee80211_offchannel_stop_beaconing(local);
+                       ieee80211_offchannel_stop_station(local);
+
+                       local->tmp_channel = wk->chan;
+                       local->tmp_channel_type = wk->chan_type;
+                       ieee80211_hw_config(local, 0);
+                       wk->started = true;
+                       wk->timeout = jiffies;
+               }
+
+               /* don't try to work with items that aren't started */
+               if (!wk->started)
+                       continue;
+
+               if (time_is_after_jiffies(wk->timeout)) {
+                       /*
+                        * This work item isn't supposed to be worked on
+                        * right now, but take care to adjust the timer
+                        * properly.
+                        */
+                       run_again(local, wk->timeout);
+                       continue;
+               }
+
+               switch (wk->type) {
+               default:
+                       WARN_ON(1);
+                       /* nothing */
+                       rma = WORK_ACT_NONE;
+                       break;
+               case IEEE80211_WORK_ABORT:
+                       rma = WORK_ACT_TIMEOUT;
+               case IEEE80211_WORK_DIRECT_PROBE:
+                       rma = ieee80211_direct_probe(wk);
+                       break;
+               case IEEE80211_WORK_AUTH:
+                       rma = ieee80211_authenticate(wk);
+                       break;
+               case IEEE80211_WORK_ASSOC:
+                       rma = ieee80211_associate(wk);
+                       break;
+               case IEEE80211_WORK_REMAIN_ON_CHANNEL:
+                       rma = ieee80211_remain_on_channel_timeout(wk);
+                       break;
+               }
+
+               switch (rma) {
+               case WORK_ACT_NONE:
+                       /* might have changed the timeout */
+                       run_again(local, wk->timeout);
+                       break;
+               case WORK_ACT_TIMEOUT:
+                       list_del_rcu(&wk->list);
+                       synchronize_rcu();
+                       list_add(&wk->list, &free_work);
+                       break;
+               default:
+                       WARN(1, "unexpected: %d", rma);
+               }
+       }
+
+       list_for_each_entry(wk, &local->work_list, list) {
+               if (!wk->started)
+                       continue;
+               if (wk->chan != local->tmp_channel)
+                       continue;
+               if (wk->chan_type != local->tmp_channel_type)
+                       continue;
+               remain_off_channel = true;
+       }
+
+       if (!remain_off_channel && local->tmp_channel) {
+               local->tmp_channel = NULL;
+               ieee80211_hw_config(local, 0);
+               ieee80211_offchannel_return(local, true);
+               /* give connection some time to breathe */
+               run_again(local, jiffies + HZ/2);
+       }
+
+       if (list_empty(&local->work_list) && local->scan_req)
+               ieee80211_queue_delayed_work(&local->hw,
+                                            &local->scan_work,
+                                            round_jiffies_relative(0));
+
+       mutex_unlock(&local->work_mtx);
+
+       ieee80211_recalc_idle(local);
+
+       list_for_each_entry_safe(wk, tmp, &free_work, list) {
+               wk->done(wk, NULL);
+               list_del(&wk->list);
+               kfree(wk);
+       }
+}
+
+void ieee80211_add_work(struct ieee80211_work *wk)
+{
+       struct ieee80211_local *local;
+
+       if (WARN_ON(!wk->chan))
+               return;
+
+       if (WARN_ON(!wk->sdata))
+               return;
+
+       if (WARN_ON(!wk->done))
+               return;
+
+       wk->started = false;
+
+       local = wk->sdata->local;
+       mutex_lock(&local->work_mtx);
+       list_add_tail(&wk->list, &local->work_list);
+       mutex_unlock(&local->work_mtx);
+
+       ieee80211_queue_work(&local->hw, &local->work_work);
+}
+
+void ieee80211_work_init(struct ieee80211_local *local)
+{
+       mutex_init(&local->work_mtx);
+       INIT_LIST_HEAD(&local->work_list);
+       setup_timer(&local->work_timer, ieee80211_work_timer,
+                   (unsigned long)local);
+       INIT_WORK(&local->work_work, ieee80211_work_work);
+       skb_queue_head_init(&local->work_skb_queue);
+}
+
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_work *wk;
+
+       mutex_lock(&local->work_mtx);
+       list_for_each_entry(wk, &local->work_list, list) {
+               if (wk->sdata != sdata)
+                       continue;
+               wk->type = IEEE80211_WORK_ABORT;
+               wk->started = true;
+               wk->timeout = jiffies;
+       }
+       mutex_unlock(&local->work_mtx);
+
+       /* run cleanups etc. */
+       ieee80211_work_work(&local->work_work);
+
+       mutex_lock(&local->work_mtx);
+       list_for_each_entry(wk, &local->work_list, list) {
+               if (wk->sdata != sdata)
+                       continue;
+               WARN_ON(1);
+               break;
+       }
+       mutex_unlock(&local->work_mtx);
+}
+
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+                                          struct sk_buff *skb)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_work *wk;
+       u16 fc;
+
+       if (skb->len < 24)
+               return RX_DROP_MONITOR;
+
+       mgmt = (struct ieee80211_mgmt *) skb->data;
+       fc = le16_to_cpu(mgmt->frame_control);
+
+       list_for_each_entry_rcu(wk, &local->work_list, list) {
+               if (sdata != wk->sdata)
+                       continue;
+               if (compare_ether_addr(wk->filter_ta, mgmt->sa))
+                       continue;
+               if (compare_ether_addr(wk->filter_ta, mgmt->bssid))
+                       continue;
+
+               switch (fc & IEEE80211_FCTL_STYPE) {
+               case IEEE80211_STYPE_AUTH:
+               case IEEE80211_STYPE_PROBE_RESP:
+               case IEEE80211_STYPE_ASSOC_RESP:
+               case IEEE80211_STYPE_REASSOC_RESP:
+               case IEEE80211_STYPE_DEAUTH:
+               case IEEE80211_STYPE_DISASSOC:
+                       skb_queue_tail(&local->work_skb_queue, skb);
+                       ieee80211_queue_work(&local->hw, &local->work_work);
+                       return RX_QUEUED;
+               }
+       }
+
+       return RX_CONTINUE;
+}
+
+static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
+                                                  struct sk_buff *skb)
+{
+       /*
+        * We are done serving the remain-on-channel command.
+        */
+       cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk,
+                                          wk->chan, wk->chan_type,
+                                          GFP_KERNEL);
+
+       return WORK_DONE_DESTROY;
+}
+
+int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
+                                  struct ieee80211_channel *chan,
+                                  enum nl80211_channel_type channel_type,
+                                  unsigned int duration, u64 *cookie)
+{
+       struct ieee80211_work *wk;
+
+       wk = kzalloc(sizeof(*wk), GFP_KERNEL);
+       if (!wk)
+               return -ENOMEM;
+
+       wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
+       wk->chan = chan;
+       wk->chan_type = channel_type;
+       wk->sdata = sdata;
+       wk->done = ieee80211_remain_done;
+
+       wk->remain.duration = duration;
+
+       *cookie = (unsigned long) wk;
+
+       ieee80211_add_work(wk);
+
+       return 0;
+}
+
+int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
+                                         u64 cookie)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_work *wk, *tmp;
+       bool found = false;
+
+       mutex_lock(&local->work_mtx);
+       list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
+               if ((unsigned long) wk == cookie) {
+                       wk->timeout = jiffies;
+                       found = true;
+                       break;
+               }
+       }
+       mutex_unlock(&local->work_mtx);
+
+       if (!found)
+               return -ENOENT;
+
+       ieee80211_queue_work(&local->hw, &local->work_work);
+
+       return 0;
+}
diff --git a/net/wireless/.gitignore b/net/wireless/.gitignore
new file mode 100644 (file)
index 0000000..c33451b
--- /dev/null
@@ -0,0 +1 @@
+regdb.c
index 90e93a5701aacdd933384419d591e2e72d936d6b..d0ee29063e5d80190412672552a5b2cfe4886c74 100644 (file)
@@ -94,20 +94,21 @@ config CFG80211_DEBUGFS
 
          If unsure, say N.
 
-config WIRELESS_OLD_REGULATORY
-       bool "Old wireless static regulatory definitions"
+config CFG80211_INTERNAL_REGDB
+       bool "use statically compiled regulatory rules database" if EMBEDDED
        default n
        depends on CFG80211
        ---help---
-         This option enables the old static regulatory information
-         and uses it within the new framework. This option is available
-         for historical reasons and it is advised to leave it off.
+         This option generates an internal data structure representing
+         the wireless regulatory rules described in net/wireless/db.txt
+         and includes code to query that database.  This is an alternative
+         to using CRDA for defining regulatory rules for the kernel.
 
          For details see:
 
          http://wireless.kernel.org/en/developers/Regulatory
 
-         Say N and if you say Y, please tell us why. The default is N.
+         Most distributions have a CRDA package.  So if unsure, say N.
 
 config CFG80211_WEXT
        bool "cfg80211 wireless extensions compatibility"
index f07c8dc7aab2990d543db566028fa1d460426e85..e77e508126fa4870890d330f2d8f653a72a104b6 100644 (file)
@@ -13,5 +13,11 @@ cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
 cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o
 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
 cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
+cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
 
 ccflags-y += -D__CHECK_ENDIAN__
+
+$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
+       @$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@
+
+clean-files := regdb.c
index a46ac6c9b3655dbc056b8a20846a972b2bba9fdb..bf1737fc9a7e47fe448e71a058334fb5fdb1b068 100644 (file)
@@ -41,44 +41,57 @@ rdev_fixed_channel(struct cfg80211_registered_device *rdev,
        return result;
 }
 
-int rdev_set_freq(struct cfg80211_registered_device *rdev,
-                 struct wireless_dev *for_wdev,
+struct ieee80211_channel *
+rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
                  int freq, enum nl80211_channel_type channel_type)
 {
        struct ieee80211_channel *chan;
        struct ieee80211_sta_ht_cap *ht_cap;
-       int result;
-
-       if (rdev_fixed_channel(rdev, for_wdev))
-               return -EBUSY;
-
-       if (!rdev->ops->set_channel)
-               return -EOPNOTSUPP;
 
        chan = ieee80211_get_channel(&rdev->wiphy, freq);
 
        /* Primary channel not allowed */
        if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
-               return -EINVAL;
+               return NULL;
 
        if (channel_type == NL80211_CHAN_HT40MINUS &&
            chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
-               return -EINVAL;
+               return NULL;
        else if (channel_type == NL80211_CHAN_HT40PLUS &&
                 chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
-               return -EINVAL;
+               return NULL;
 
        ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
 
        if (channel_type != NL80211_CHAN_NO_HT) {
                if (!ht_cap->ht_supported)
-                       return -EINVAL;
+                       return NULL;
 
                if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
                    ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
-                       return -EINVAL;
+                       return NULL;
        }
 
+       return chan;
+}
+
+int rdev_set_freq(struct cfg80211_registered_device *rdev,
+                 struct wireless_dev *for_wdev,
+                 int freq, enum nl80211_channel_type channel_type)
+{
+       struct ieee80211_channel *chan;
+       int result;
+
+       if (rdev_fixed_channel(rdev, for_wdev))
+               return -EBUSY;
+
+       if (!rdev->ops->set_channel)
+               return -EOPNOTSUPP;
+
+       chan = rdev_freq_to_chan(rdev, freq, channel_type);
+       if (!chan)
+               return -EINVAL;
+
        result = rdev->ops->set_channel(&rdev->wiphy, chan, channel_type);
        if (result)
                return result;
index 4ef3efc941066119ddbea37766b8081219aa01a6..30ec95f05b52c5f874f8060b957d98bb7f85142c 100644 (file)
@@ -374,10 +374,15 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 struct ieee80211_channel *
 rdev_fixed_channel(struct cfg80211_registered_device *rdev,
                   struct wireless_dev *for_wdev);
+struct ieee80211_channel *
+rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
+                 int freq, enum nl80211_channel_type channel_type);
 int rdev_set_freq(struct cfg80211_registered_device *rdev,
                  struct wireless_dev *for_wdev,
                  int freq, enum nl80211_channel_type channel_type);
 
+u16 cfg80211_calculate_bitrate(struct rate_info *rate);
+
 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
 #define CFG80211_DEV_WARN_ON(cond)     WARN_ON(cond)
 #else
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
new file mode 100644 (file)
index 0000000..a2fc3a0
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# This file is a placeholder to prevent accidental build breakage if someone
+# enables CONFIG_CFG80211_INTERNAL_REGDB.  Almost no one actually needs to
+# enable that build option.
+#
+# You should be using CRDA instead.  It is even better if you use the CRDA
+# package provided by your distribution, since they will probably keep it
+# up-to-date on your behalf.
+#
+# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
+# need to replace this file with one containing appropriately formatted
+# regulatory rules that cover the regulatory domains you will be using.  Your
+# best option is to extract the db.txt file from the wireless-regdb git
+# repository:
+#
+#   git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
+#
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
new file mode 100644 (file)
index 0000000..8316cf0
--- /dev/null
@@ -0,0 +1,118 @@
+#!/usr/bin/awk -f
+#
+# genregdb.awk -- generate regdb.c from db.txt
+#
+# Actually, it reads from stdin (presumed to be db.txt) and writes
+# to stdout (presumed to be regdb.c), but close enough...
+#
+# Copyright 2009 John W. Linville <linville@tuxdriver.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+
+BEGIN {
+       active = 0
+       rules = 0;
+       print "/*"
+       print " * DO NOT EDIT -- file generated from data in db.txt"
+       print " */"
+       print ""
+       print "#include <linux/nl80211.h>"
+       print "#include <net/cfg80211.h>"
+       print ""
+       regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
+}
+
+/^[ \t]*#/ {
+       /* Ignore */
+}
+
+!active && /^[ \t]*$/ {
+       /* Ignore */
+}
+
+!active && /country/ {
+       country=$2
+       sub(/:/, "", country)
+       printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
+       printf "\t.alpha2 = \"%s\",\n", country
+       printf "\t.reg_rules = {\n"
+       active = 1
+       regdb = regdb "\t&regdom_" country ",\n"
+}
+
+active && /^[ \t]*\(/ {
+       start = $1
+       sub(/\(/, "", start)
+       end = $3
+       bw = $5
+       sub(/\),/, "", bw)
+       gain = $6
+       sub(/\(/, "", gain)
+       sub(/,/, "", gain)
+       power = $7
+       sub(/\)/, "", power)
+       sub(/,/, "", power)
+       # power might be in mW...
+       units = $8
+       sub(/\)/, "", units)
+       sub(/,/, "", units)
+       if (units == "mW") {
+               if (power == 100) {
+                       power = 20
+               } else if (power == 200) {
+                       power = 23
+               } else if (power == 500) {
+                       power = 27
+               } else if (power == 1000) {
+                       power = 30
+               } else {
+                       print "Unknown power value in database!"
+               }
+       }
+       flagstr = ""
+       for (i=8; i<=NF; i++)
+               flagstr = flagstr $i
+       split(flagstr, flagarray, ",")
+       flags = ""
+       for (arg in flagarray) {
+               if (flagarray[arg] == "NO-OFDM") {
+                       flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | "
+               } else if (flagarray[arg] == "NO-CCK") {
+                       flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | "
+               } else if (flagarray[arg] == "NO-INDOOR") {
+                       flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | "
+               } else if (flagarray[arg] == "NO-OUTDOOR") {
+                       flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | "
+               } else if (flagarray[arg] == "DFS") {
+                       flags = flags "\n\t\t\tNL80211_RRF_DFS | "
+               } else if (flagarray[arg] == "PTP-ONLY") {
+                       flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | "
+               } else if (flagarray[arg] == "PTMP-ONLY") {
+                       flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
+               } else if (flagarray[arg] == "PASSIVE-SCAN") {
+                       flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
+               } else if (flagarray[arg] == "NO-IBSS") {
+                       flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
+               }
+       }
+       flags = flags "0"
+       printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
+       rules++
+}
+
+active && /^[ \t]*$/ {
+       active = 0
+       printf "\t},\n"
+       printf "\t.n_reg_rules = %d\n", rules
+       printf "};\n\n"
+       rules = 0;
+}
+
+END {
+       print regdb "};"
+       print ""
+       print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
+}
index 82e6002c8d678654f2cdfd7c88baa4ee53586a0b..94d151f6f73e8539e1ab789b684da38c1833f9ac 100644 (file)
@@ -148,22 +148,23 @@ void __cfg80211_send_deauth(struct net_device *dev,
        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
        const u8 *bssid = mgmt->bssid;
        int i;
+       bool found = false;
 
        ASSERT_WDEV_LOCK(wdev);
 
-       nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
-
        if (wdev->current_bss &&
            memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) {
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(&wdev->current_bss->pub);
                wdev->current_bss = NULL;
+               found = true;
        } else for (i = 0; i < MAX_AUTH_BSSES; i++) {
                if (wdev->auth_bsses[i] &&
                    memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) {
                        cfg80211_unhold_bss(wdev->auth_bsses[i]);
                        cfg80211_put_bss(&wdev->auth_bsses[i]->pub);
                        wdev->auth_bsses[i] = NULL;
+                       found = true;
                        break;
                }
                if (wdev->authtry_bsses[i] &&
@@ -171,10 +172,16 @@ void __cfg80211_send_deauth(struct net_device *dev,
                        cfg80211_unhold_bss(wdev->authtry_bsses[i]);
                        cfg80211_put_bss(&wdev->authtry_bsses[i]->pub);
                        wdev->authtry_bsses[i] = NULL;
+                       found = true;
                        break;
                }
        }
 
+       if (!found)
+               return;
+
+       nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL);
+
        if (wdev->sme_state == CFG80211_SME_CONNECTED) {
                u16 reason_code;
                bool from_ap;
@@ -684,3 +691,40 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
                }
        }
 }
+
+void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie,
+                              struct ieee80211_channel *chan,
+                              enum nl80211_channel_type channel_type,
+                              unsigned int duration, gfp_t gfp)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type,
+                                      duration, gfp);
+}
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
+
+void cfg80211_remain_on_channel_expired(struct net_device *dev,
+                                       u64 cookie,
+                                       struct ieee80211_channel *chan,
+                                       enum nl80211_channel_type channel_type,
+                                       gfp_t gfp)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan,
+                                             channel_type, gfp);
+}
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
+
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+                     struct station_info *sinfo, gfp_t gfp)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
+}
+EXPORT_SYMBOL(cfg80211_new_sta);
index a6028433e3a0f9583e48c5b2369abed53ed0ddd5..e3bee3cecdfa82a52b5d3523584a35ae0685045e 100644 (file)
@@ -141,6 +141,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
        [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
                                 .len = WLAN_PMKID_LEN },
+       [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
+       [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
 };
 
 /* policy for the attributes */
@@ -569,6 +571,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        CMD(set_pmksa, SET_PMKSA);
        CMD(del_pmksa, DEL_PMKSA);
        CMD(flush_pmksa, FLUSH_PMKSA);
+       CMD(remain_on_channel, REMAIN_ON_CHANNEL);
        if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
                i++;
                NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
@@ -1637,42 +1640,9 @@ static int parse_station_flags(struct genl_info *info,
        return 0;
 }
 
-static u16 nl80211_calculate_bitrate(struct rate_info *rate)
-{
-       int modulation, streams, bitrate;
-
-       if (!(rate->flags & RATE_INFO_FLAGS_MCS))
-               return rate->legacy;
-
-       /* the formula below does only work for MCS values smaller than 32 */
-       if (rate->mcs >= 32)
-               return 0;
-
-       modulation = rate->mcs & 7;
-       streams = (rate->mcs >> 3) + 1;
-
-       bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
-                       13500000 : 6500000;
-
-       if (modulation < 4)
-               bitrate *= (modulation + 1);
-       else if (modulation == 4)
-               bitrate *= (modulation + 2);
-       else
-               bitrate *= (modulation + 3);
-
-       bitrate *= streams;
-
-       if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
-               bitrate = (bitrate / 9) * 10;
-
-       /* do NOT round down here */
-       return (bitrate + 50000) / 100000;
-}
-
 static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
                                int flags, struct net_device *dev,
-                               u8 *mac_addr, struct station_info *sinfo)
+                               const u8 *mac_addr, struct station_info *sinfo)
 {
        void *hdr;
        struct nlattr *sinfoattr, *txrate;
@@ -1716,8 +1686,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
                if (!txrate)
                        goto nla_put_failure;
 
-               /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
-               bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
+               /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
+               bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
                if (bitrate > 0)
                        NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
 
@@ -2583,12 +2553,6 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 
        data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
 
-#ifdef CONFIG_WIRELESS_OLD_REGULATORY
-       /* We ignore world regdom requests with the old regdom setup */
-       if (is_world_regdom(data))
-               return -EINVAL;
-#endif
-
        r = regulatory_hint_user(data);
 
        return r;
@@ -4322,6 +4286,143 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
 
 }
 
+static int nl80211_remain_on_channel(struct sk_buff *skb,
+                                    struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       struct net_device *dev;
+       struct ieee80211_channel *chan;
+       struct sk_buff *msg;
+       void *hdr;
+       u64 cookie;
+       enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+       u32 freq, duration;
+       int err;
+
+       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+           !info->attrs[NL80211_ATTR_DURATION])
+               return -EINVAL;
+
+       duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+
+       /*
+        * We should be on that channel for at least one jiffie,
+        * and more than 5 seconds seems excessive.
+        */
+       if (!duration || !msecs_to_jiffies(duration) || duration > 5000)
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!rdev->ops->remain_on_channel) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               channel_type = nla_get_u32(
+                       info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+               if (channel_type != NL80211_CHAN_NO_HT &&
+                   channel_type != NL80211_CHAN_HT20 &&
+                   channel_type != NL80211_CHAN_HT40PLUS &&
+                   channel_type != NL80211_CHAN_HT40MINUS)
+                       err = -EINVAL;
+                       goto out;
+       }
+
+       freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+       chan = rdev_freq_to_chan(rdev, freq, channel_type);
+       if (chan == NULL) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+                            NL80211_CMD_REMAIN_ON_CHANNEL);
+
+       if (IS_ERR(hdr)) {
+               err = PTR_ERR(hdr);
+               goto free_msg;
+       }
+
+       err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
+                                          channel_type, duration, &cookie);
+
+       if (err)
+               goto free_msg;
+
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+
+       genlmsg_end(msg, hdr);
+       err = genlmsg_reply(msg, info);
+       goto out;
+
+ nla_put_failure:
+       err = -ENOBUFS;
+ free_msg:
+       nlmsg_free(msg);
+ out:
+       cfg80211_unlock_rdev(rdev);
+       dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
+                                           struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       struct net_device *dev;
+       u64 cookie;
+       int err;
+
+       if (!info->attrs[NL80211_ATTR_COOKIE])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!rdev->ops->cancel_remain_on_channel) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+
+       err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
+
+ out:
+       cfg80211_unlock_rdev(rdev);
+       dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
 static struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_GET_WIPHY,
@@ -4584,8 +4685,20 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
-
+       {
+               .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
+               .doit = nl80211_remain_on_channel,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+               .doit = nl80211_cancel_remain_on_channel,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
 };
+
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
        .name = "mlme",
 };
@@ -5173,6 +5286,89 @@ nla_put_failure:
        nlmsg_free(msg);
 }
 
+static void nl80211_send_remain_on_chan_event(
+       int cmd, struct cfg80211_registered_device *rdev,
+       struct net_device *netdev, u64 cookie,
+       struct ieee80211_channel *chan,
+       enum nl80211_channel_type channel_type,
+       unsigned int duration, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
+
+       if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
+               NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
+void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
+                                   struct net_device *netdev, u64 cookie,
+                                   struct ieee80211_channel *chan,
+                                   enum nl80211_channel_type channel_type,
+                                   unsigned int duration, gfp_t gfp)
+{
+       nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
+                                         rdev, netdev, cookie, chan,
+                                         channel_type, duration, gfp);
+}
+
+void nl80211_send_remain_on_channel_cancel(
+       struct cfg80211_registered_device *rdev, struct net_device *netdev,
+       u64 cookie, struct ieee80211_channel *chan,
+       enum nl80211_channel_type channel_type, gfp_t gfp)
+{
+       nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+                                         rdev, netdev, cookie, chan,
+                                         channel_type, 0, gfp);
+}
+
+void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
+                           struct net_device *dev, const u8 *mac_addr,
+                           struct station_info *sinfo, gfp_t gfp)
+{
+       struct sk_buff *msg;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
+}
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)
index 44cc2a76a1b094149d6f292fd337512d157465e9..14855b8fb4303945a06e20045a6c31a2f1a51488 100644 (file)
@@ -59,4 +59,19 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, const u8 *bssid,
                             gfp_t gfp);
 
+void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
+                                   struct net_device *netdev,
+                                   u64 cookie,
+                                   struct ieee80211_channel *chan,
+                                   enum nl80211_channel_type channel_type,
+                                   unsigned int duration, gfp_t gfp);
+void nl80211_send_remain_on_channel_cancel(
+       struct cfg80211_registered_device *rdev, struct net_device *netdev,
+       u64 cookie, struct ieee80211_channel *chan,
+       enum nl80211_channel_type channel_type, gfp_t gfp);
+
+void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
+                           struct net_device *dev, const u8 *mac_addr,
+                           struct station_info *sinfo, gfp_t gfp);
+
 #endif /* __NET_WIRELESS_NL80211_H */
index 7a0754c92df4702ed1c72c2cdb10945dd1eb56fd..f79d6613c5ff6e49115ab84c19a8c500f47a3794 100644 (file)
 #include <net/cfg80211.h>
 #include "core.h"
 #include "reg.h"
+#include "regdb.h"
 #include "nl80211.h"
 
+#ifdef CONFIG_CFG80211_REG_DEBUG
+#define REG_DBG_PRINT(format, args...) \
+       do { \
+               printk(KERN_DEBUG format , ## args); \
+       } while (0)
+#else
+#define REG_DBG_PRINT(args...)
+#endif
+
 /* Receipt of information from last regulatory request */
 static struct regulatory_request *last_request;
 
@@ -128,78 +138,6 @@ static char *ieee80211_regdom = "00";
 module_param(ieee80211_regdom, charp, 0444);
 MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
 
-#ifdef CONFIG_WIRELESS_OLD_REGULATORY
-/*
- * We assume 40 MHz bandwidth for the old regulatory work.
- * We make emphasis we are using the exact same frequencies
- * as before
- */
-
-static const struct ieee80211_regdomain us_regdom = {
-       .n_reg_rules = 6,
-       .alpha2 =  "US",
-       .reg_rules = {
-               /* IEEE 802.11b/g, channels 1..11 */
-               REG_RULE(2412-10, 2462+10, 40, 6, 27, 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(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 = 6,
-       .alpha2 =  "JP",
-       .reg_rules = {
-               /* 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_DFS),
-               /* IEEE 802.11a, channels 100..144 */
-               REG_RULE(5500-10, 5700+10, 40, 6, 23, NL80211_RRF_DFS),
-       }
-};
-
-static const struct ieee80211_regdomain *static_regdom(char *alpha2)
-{
-       if (alpha2[0] == 'U' && alpha2[1] == 'S')
-               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 &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 == &world_regdom)
-               return true;
-       return false;
-}
-#else
-static inline bool is_old_static_regdom(const struct ieee80211_regdomain *rd)
-{
-       return false;
-}
-#endif
-
 static void reset_regdomains(void)
 {
        /* avoid freeing static information or freeing something twice */
@@ -209,8 +147,6 @@ static void reset_regdomains(void)
                cfg80211_world_regdom = NULL;
        if (cfg80211_regdomain == &world_regdom)
                cfg80211_regdomain = NULL;
-       if (is_old_static_regdom(cfg80211_regdomain))
-               cfg80211_regdomain = NULL;
 
        kfree(cfg80211_regdomain);
        kfree(cfg80211_world_regdom);
@@ -335,6 +271,98 @@ static bool country_ie_integrity_changes(u32 checksum)
        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 +382,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];
 
@@ -950,25 +981,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
                if (r == -ERANGE &&
                    last_request->initiator ==
                    NL80211_REGDOM_SET_BY_COUNTRY_IE) {
-#ifdef CONFIG_CFG80211_REG_DEBUG
-                       printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz "
+                       REG_DBG_PRINT("cfg80211: Leaving channel %d MHz "
                                "intact on %s - no rule found in band on "
                                "Country IE\n",
-                               chan->center_freq, wiphy_name(wiphy));
-#endif
+                       chan->center_freq, wiphy_name(wiphy));
                } else {
                /*
                 * In this case we know the country IE has at least one reg rule
                 * for the band so we respect its band definitions
                 */
-#ifdef CONFIG_CFG80211_REG_DEBUG
                        if (last_request->initiator ==
                            NL80211_REGDOM_SET_BY_COUNTRY_IE)
-                               printk(KERN_DEBUG "cfg80211: Disabling "
+                               REG_DBG_PRINT("cfg80211: Disabling "
                                        "channel %d MHz on %s due to "
                                        "Country IE\n",
                                        chan->center_freq, wiphy_name(wiphy));
-#endif
                        flags |= IEEE80211_CHAN_DISABLED;
                        chan->flags = flags;
                }
@@ -1342,30 +1369,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
 }
 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
@@ -1418,8 +1421,6 @@ static int ignore_request(struct wiphy *wiphy,
                return REG_INTERSECT;
        case NL80211_REGDOM_SET_BY_DRIVER:
                if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) {
-                       if (is_old_static_regdom(cfg80211_regdomain))
-                               return 0;
                        if (regdom_changes(pending_request->alpha2))
                                return 0;
                        return -EALREADY;
@@ -1456,8 +1457,7 @@ static int ignore_request(struct wiphy *wiphy,
                                return -EAGAIN;
                }
 
-               if (!is_old_static_regdom(cfg80211_regdomain) &&
-                   !regdom_changes(pending_request->alpha2))
+               if (!regdom_changes(pending_request->alpha2))
                        return -EALREADY;
 
                return 0;
@@ -1875,13 +1875,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
        if (!reg_beacon)
                return -ENOMEM;
 
-#ifdef CONFIG_CFG80211_REG_DEBUG
-       printk(KERN_DEBUG "cfg80211: Found new beacon on "
-               "frequency: %d MHz (Ch %d) on %s\n",
-               beacon_chan->center_freq,
-               ieee80211_frequency_to_channel(beacon_chan->center_freq),
-               wiphy_name(wiphy));
-#endif
+       REG_DBG_PRINT("cfg80211: Found new beacon on "
+                     "frequency: %d MHz (Ch %d) on %s\n",
+                     beacon_chan->center_freq,
+                     ieee80211_frequency_to_channel(beacon_chan->center_freq),
+                     wiphy_name(wiphy));
+
        memcpy(&reg_beacon->chan, beacon_chan,
                sizeof(struct ieee80211_channel));
 
@@ -2039,8 +2038,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
                 * If someone else asked us to change the rd lets only bother
                 * checking if the alpha2 changes if CRDA was already called
                 */
-               if (!is_old_static_regdom(cfg80211_regdomain) &&
-                   !regdom_changes(rd->alpha2))
+               if (!regdom_changes(rd->alpha2))
                        return -EINVAL;
        }
 
@@ -2239,15 +2237,8 @@ int regulatory_init(void)
        spin_lock_init(&reg_requests_lock);
        spin_lock_init(&reg_pending_beacons_lock);
 
-#ifdef CONFIG_WIRELESS_OLD_REGULATORY
-       cfg80211_regdomain = static_regdom(ieee80211_regdom);
-
-       printk(KERN_INFO "cfg80211: Using static regulatory domain info\n");
-       print_regdomain_info(cfg80211_regdomain);
-#else
        cfg80211_regdomain = cfg80211_world_regdom;
 
-#endif
        /* We always try to get an update for the static regdomain */
        err = regulatory_hint_core(cfg80211_regdomain->alpha2);
        if (err) {
diff --git a/net/wireless/regdb.h b/net/wireless/regdb.h
new file mode 100644 (file)
index 0000000..818222c
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __REGDB_H__
+#define __REGDB_H__
+
+extern const struct ieee80211_regdomain *reg_regdb[];
+extern int reg_regdb_size;
+
+#endif /* __REGDB_H__ */
index 59361fdcb5d03898b8d16ce5c7fbe637954a8cbf..23557c1d0a9caafd0e8c7b211846c9bd0f8a32b0 100644 (file)
@@ -285,7 +285,7 @@ static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
        }
 }
 
-int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
+int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                           enum nl80211_iftype iftype)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -383,7 +383,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
 }
 EXPORT_SYMBOL(ieee80211_data_to_8023);
 
-int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
+int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
                             enum nl80211_iftype iftype, u8 *bssid, bool qos)
 {
        struct ieee80211_hdr hdr;
@@ -497,6 +497,101 @@ int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
 }
 EXPORT_SYMBOL(ieee80211_data_from_8023);
 
+
+void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+                             const u8 *addr, enum nl80211_iftype iftype,
+                             const unsigned int extra_headroom)
+{
+       struct sk_buff *frame = NULL;
+       u16 ethertype;
+       u8 *payload;
+       const struct ethhdr *eth;
+       int remaining, err;
+       u8 dst[ETH_ALEN], src[ETH_ALEN];
+
+       err = ieee80211_data_to_8023(skb, addr, iftype);
+       if (err)
+               goto out;
+
+       /* skip the wrapping header */
+       eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
+       if (!eth)
+               goto out;
+
+       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)
+                       goto purge;
+
+               skb_pull(skb, sizeof(struct ethhdr));
+               /* reuse skb for the last subframe */
+               if (remaining <= subframe_len + padding)
+                       frame = skb;
+               else {
+                       unsigned int hlen = ALIGN(extra_headroom, 4);
+                       /*
+                        * Allocate and reserve two bytes more for payload
+                        * alignment since sizeof(struct ethhdr) is 14.
+                        */
+                       frame = dev_alloc_skb(hlen + subframe_len + 2);
+                       if (!frame)
+                               goto purge;
+
+                       skb_reserve(frame, hlen + 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);
+                               goto purge;
+                       }
+               }
+
+               skb_reset_network_header(frame);
+               frame->dev = skb->dev;
+               frame->priority = skb->priority;
+
+               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);
+               }
+               __skb_queue_tail(list, frame);
+       }
+
+       return;
+
+ purge:
+       __skb_queue_purge(list);
+ out:
+       dev_kfree_skb(skb);
+}
+EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
+
 /* Given a data frame determine the 802.1p/1d tag to use. */
 unsigned int cfg80211_classify8021d(struct sk_buff *skb)
 {
@@ -720,3 +815,36 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
 
        return err;
 }
+
+u16 cfg80211_calculate_bitrate(struct rate_info *rate)
+{
+       int modulation, streams, bitrate;
+
+       if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+               return rate->legacy;
+
+       /* the formula below does only work for MCS values smaller than 32 */
+       if (rate->mcs >= 32)
+               return 0;
+
+       modulation = rate->mcs & 7;
+       streams = (rate->mcs >> 3) + 1;
+
+       bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
+                       13500000 : 6500000;
+
+       if (modulation < 4)
+               bitrate *= (modulation + 1);
+       else if (modulation == 4)
+               bitrate *= (modulation + 2);
+       else
+               bitrate *= (modulation + 3);
+
+       bitrate *= streams;
+
+       if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+               bitrate = (bitrate / 9) * 10;
+
+       /* do NOT round down here */
+       return (bitrate + 50000) / 100000;
+}
index 54face3d4424c973ab6b297ea9582daba6c0beab..4198243a3dff97be788f6cec9062fcd50dc8ebe9 100644 (file)
@@ -1257,10 +1257,7 @@ int cfg80211_wext_giwrate(struct net_device *dev,
        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;
 }