]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorLuciano Coelho <coelho@ti.com>
Thu, 1 Dec 2011 10:14:48 +0000 (12:14 +0200)
committerLuciano Coelho <coelho@ti.com>
Thu, 1 Dec 2011 10:14:48 +0000 (12:14 +0200)
32 files changed:
drivers/net/wireless/wl12xx/Kconfig
drivers/net/wireless/wl12xx/Makefile
drivers/net/wireless/wl12xx/acx.c
drivers/net/wireless/wl12xx/acx.h
drivers/net/wireless/wl12xx/boot.c
drivers/net/wireless/wl12xx/cmd.c
drivers/net/wireless/wl12xx/cmd.h
drivers/net/wireless/wl12xx/conf.h
drivers/net/wireless/wl12xx/debug.h [new file with mode: 0644]
drivers/net/wireless/wl12xx/debugfs.c
drivers/net/wireless/wl12xx/event.c
drivers/net/wireless/wl12xx/event.h
drivers/net/wireless/wl12xx/init.c
drivers/net/wireless/wl12xx/init.h
drivers/net/wireless/wl12xx/io.c
drivers/net/wireless/wl12xx/io.h
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/ps.c
drivers/net/wireless/wl12xx/ps.h
drivers/net/wireless/wl12xx/reg.h
drivers/net/wireless/wl12xx/rx.c
drivers/net/wireless/wl12xx/scan.c
drivers/net/wireless/wl12xx/scan.h
drivers/net/wireless/wl12xx/sdio.c
drivers/net/wireless/wl12xx/sdio_test.c [deleted file]
drivers/net/wireless/wl12xx/spi.c
drivers/net/wireless/wl12xx/testmode.c
drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/tx.h
drivers/net/wireless/wl12xx/wl12xx.h
drivers/net/wireless/wl12xx/wl12xx_platform_data.c
include/linux/wl12xx.h

index 3fe388b87c2e339204833eff359eb22b6a0ccba7..af08c8609c63a65a0561364fdf4e9284d974248e 100644 (file)
@@ -42,16 +42,6 @@ config WL12XX_SDIO
          If you choose to build a module, it'll be called wl12xx_sdio.
          Say N if unsure.
 
-config WL12XX_SDIO_TEST
-       tristate "TI wl12xx SDIO testing support"
-       depends on WL12XX && MMC && WL12XX_SDIO
-       default n
-       ---help---
-         This module adds support for the SDIO bus testing with the
-         TI wl12xx chipsets.  You probably don't want this unless you are
-         testing a new hardware platform.  Select this if you want to test the
-         SDIO bus which is connected to the wl12xx chip.
-
 config WL12XX_PLATFORM_DATA
        bool
        depends on WL12XX_SDIO != n || WL1251_SDIO != n
index 621b3483ca2c578037c8620a3b0dc6cc562be8d6..fe67262ba19f7997b16c37b9a42395c25890e6ec 100644 (file)
@@ -3,14 +3,11 @@ wl12xx-objs           = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
 
 wl12xx_spi-objs        = spi.o
 wl12xx_sdio-objs       = sdio.o
-wl12xx_sdio_test-objs  = sdio_test.o
 
 wl12xx-$(CONFIG_NL80211_TESTMODE)      += testmode.o
 obj-$(CONFIG_WL12XX)                   += wl12xx.o
 obj-$(CONFIG_WL12XX_SPI)               += wl12xx_spi.o
 obj-$(CONFIG_WL12XX_SDIO)              += wl12xx_sdio.o
 
-obj-$(CONFIG_WL12XX_SDIO_TEST)         += wl12xx_sdio_test.o
-
 # small builtin driver bit
 obj-$(CONFIG_WL12XX_PLATFORM_DATA)     += wl12xx_platform_data.o
index ca044a743191ac24b3268dccc12eae68b90484b6..bde1d862bdd595cc88f6943d40d44f949c51b8f9 100644 (file)
 #include <linux/slab.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "wl12xx_80211.h"
 #include "reg.h"
 #include "ps.h"
 
-int wl1271_acx_wake_up_conditions(struct wl1271 *wl)
+int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct acx_wake_up_condition *wake_up;
        int ret;
@@ -46,7 +47,7 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl)
                goto out;
        }
 
-       wake_up->role_id = wl->role_id;
+       wake_up->role_id = wlvif->role_id;
        wake_up->wake_up_event = wl->conf.conn.wake_up_event;
        wake_up->listen_interval = wl->conf.conn.listen_interval;
 
@@ -84,7 +85,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_tx_power(struct wl1271 *wl, int power)
+int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       int power)
 {
        struct acx_current_tx_power *acx;
        int ret;
@@ -100,7 +102,7 @@ int wl1271_acx_tx_power(struct wl1271 *wl, int power)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->current_tx_power = power * 10;
 
        ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx));
@@ -114,7 +116,7 @@ out:
        return ret;
 }
 
-int wl1271_acx_feature_cfg(struct wl1271 *wl)
+int wl1271_acx_feature_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct acx_feature_config *feature;
        int ret;
@@ -128,7 +130,7 @@ int wl1271_acx_feature_cfg(struct wl1271 *wl)
        }
 
        /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */
-       feature->role_id = wl->role_id;
+       feature->role_id = wlvif->role_id;
        feature->data_flow_options = 0;
        feature->options = 0;
 
@@ -210,7 +212,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time)
+int wl1271_acx_slot(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                   enum acx_slot_type slot_time)
 {
        struct acx_slot *slot;
        int ret;
@@ -223,7 +226,7 @@ int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time)
                goto out;
        }
 
-       slot->role_id = wl->role_id;
+       slot->role_id = wlvif->role_id;
        slot->wone_index = STATION_WONE_INDEX;
        slot->slot_time = slot_time;
 
@@ -238,8 +241,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
-                                void *mc_list, u32 mc_list_len)
+int wl1271_acx_group_address_tbl(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                bool enable, void *mc_list, u32 mc_list_len)
 {
        struct acx_dot11_grp_addr_tbl *acx;
        int ret;
@@ -253,7 +256,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
        }
 
        /* MAC filtering */
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->enabled = enable;
        acx->num_groups = mc_list_len;
        memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
@@ -270,7 +273,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_service_period_timeout(struct wl1271 *wl)
+int wl1271_acx_service_period_timeout(struct wl1271 *wl,
+                                     struct wl12xx_vif *wlvif)
 {
        struct acx_rx_timeout *rx_timeout;
        int ret;
@@ -283,7 +287,7 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_ACX, "acx service period timeout");
 
-       rx_timeout->role_id = wl->role_id;
+       rx_timeout->role_id = wlvif->role_id;
        rx_timeout->ps_poll_timeout = cpu_to_le16(wl->conf.rx.ps_poll_timeout);
        rx_timeout->upsd_timeout = cpu_to_le16(wl->conf.rx.upsd_timeout);
 
@@ -300,7 +304,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold)
+int wl1271_acx_rts_threshold(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            u32 rts_threshold)
 {
        struct acx_rts_threshold *rts;
        int ret;
@@ -320,7 +325,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold)
                goto out;
        }
 
-       rts->role_id = wl->role_id;
+       rts->role_id = wlvif->role_id;
        rts->threshold = cpu_to_le16((u16)rts_threshold);
 
        ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts));
@@ -363,7 +368,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
+int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                bool enable_filter)
 {
        struct acx_beacon_filter_option *beacon_filter = NULL;
        int ret = 0;
@@ -380,7 +386,7 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
                goto out;
        }
 
-       beacon_filter->role_id = wl->role_id;
+       beacon_filter->role_id = wlvif->role_id;
        beacon_filter->enable = enable_filter;
 
        /*
@@ -401,7 +407,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
+int wl1271_acx_beacon_filter_table(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif)
 {
        struct acx_beacon_filter_ie_table *ie_table;
        int i, idx = 0;
@@ -417,7 +424,7 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
        }
 
        /* configure default beacon pass-through rules */
-       ie_table->role_id = wl->role_id;
+       ie_table->role_id = wlvif->role_id;
        ie_table->num_ie = 0;
        for (i = 0; i < wl->conf.conn.bcn_filt_ie_count; i++) {
                struct conf_bcn_filt_rule *r = &(wl->conf.conn.bcn_filt_ie[i]);
@@ -458,7 +465,8 @@ out:
 
 #define ACX_CONN_MONIT_DISABLE_VALUE  0xffffffff
 
-int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable)
+int wl1271_acx_conn_monit_params(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                bool enable)
 {
        struct acx_conn_monit_params *acx;
        u32 threshold = ACX_CONN_MONIT_DISABLE_VALUE;
@@ -479,7 +487,7 @@ int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable)
                timeout = wl->conf.conn.bss_lose_timeout;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->synch_fail_thold = cpu_to_le32(threshold);
        acx->bss_lose_timeout = cpu_to_le32(timeout);
 
@@ -582,7 +590,7 @@ out:
        return ret;
 }
 
-int wl1271_acx_bcn_dtim_options(struct wl1271 *wl)
+int wl1271_acx_bcn_dtim_options(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct acx_beacon_broadcast *bb;
        int ret;
@@ -595,7 +603,7 @@ int wl1271_acx_bcn_dtim_options(struct wl1271 *wl)
                goto out;
        }
 
-       bb->role_id = wl->role_id;
+       bb->role_id = wlvif->role_id;
        bb->beacon_rx_timeout = cpu_to_le16(wl->conf.conn.beacon_rx_timeout);
        bb->broadcast_timeout = cpu_to_le16(wl->conf.conn.broadcast_timeout);
        bb->rx_broadcast_in_ps = wl->conf.conn.rx_broadcast_in_ps;
@@ -612,7 +620,7 @@ out:
        return ret;
 }
 
-int wl1271_acx_aid(struct wl1271 *wl, u16 aid)
+int wl1271_acx_aid(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid)
 {
        struct acx_aid *acx_aid;
        int ret;
@@ -625,7 +633,7 @@ int wl1271_acx_aid(struct wl1271 *wl, u16 aid)
                goto out;
        }
 
-       acx_aid->role_id = wl->role_id;
+       acx_aid->role_id = wlvif->role_id;
        acx_aid->aid = cpu_to_le16(aid);
 
        ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid));
@@ -668,7 +676,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble)
+int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           enum acx_preamble_type preamble)
 {
        struct acx_preamble *acx;
        int ret;
@@ -681,7 +690,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->preamble = preamble;
 
        ret = wl1271_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx));
@@ -695,7 +704,7 @@ out:
        return ret;
 }
 
-int wl1271_acx_cts_protect(struct wl1271 *wl,
+int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                           enum acx_ctsprotect_type ctsprotect)
 {
        struct acx_ctsprotect *acx;
@@ -709,7 +718,7 @@ int wl1271_acx_cts_protect(struct wl1271 *wl,
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->ctsprotect = ctsprotect;
 
        ret = wl1271_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx));
@@ -739,7 +748,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
        return 0;
 }
 
-int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
+int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct acx_rate_policy *acx;
        struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
@@ -755,11 +764,11 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
        }
 
        wl1271_debug(DEBUG_ACX, "basic_rate: 0x%x, full_rate: 0x%x",
-               wl->basic_rate, wl->rate_set);
+               wlvif->basic_rate, wlvif->rate_set);
 
        /* configure one basic rate class */
-       acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE);
-       acx->rate_policy.enabled_rates = cpu_to_le32(wl->basic_rate);
+       acx->rate_policy_idx = cpu_to_le32(wlvif->sta.basic_rate_idx);
+       acx->rate_policy.enabled_rates = cpu_to_le32(wlvif->basic_rate);
        acx->rate_policy.short_retry_limit = c->short_retry_limit;
        acx->rate_policy.long_retry_limit = c->long_retry_limit;
        acx->rate_policy.aflags = c->aflags;
@@ -771,8 +780,8 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
        }
 
        /* configure one AP supported rate class */
-       acx->rate_policy_idx = cpu_to_le32(ACX_TX_AP_FULL_RATE);
-       acx->rate_policy.enabled_rates = cpu_to_le32(wl->rate_set);
+       acx->rate_policy_idx = cpu_to_le32(wlvif->sta.ap_rate_idx);
+       acx->rate_policy.enabled_rates = cpu_to_le32(wlvif->rate_set);
        acx->rate_policy.short_retry_limit = c->short_retry_limit;
        acx->rate_policy.long_retry_limit = c->long_retry_limit;
        acx->rate_policy.aflags = c->aflags;
@@ -788,7 +797,7 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
         * (p2p packets should always go out with OFDM rates, even
         * if we are currently connected to 11b AP)
         */
-       acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE_P2P);
+       acx->rate_policy_idx = cpu_to_le32(wlvif->sta.p2p_rate_idx);
        acx->rate_policy.enabled_rates =
                                cpu_to_le32(CONF_TX_RATE_MASK_BASIC_P2P);
        acx->rate_policy.short_retry_limit = c->short_retry_limit;
@@ -839,8 +848,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
-                     u8 aifsn, u16 txop)
+int wl1271_acx_ac_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop)
 {
        struct acx_ac_cfg *acx;
        int ret = 0;
@@ -855,7 +864,7 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->ac = ac;
        acx->cw_min = cw_min;
        acx->cw_max = cpu_to_le16(cw_max);
@@ -873,7 +882,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
+int wl1271_acx_tid_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      u8 queue_id, u8 channel_type,
                       u8 tsid, u8 ps_scheme, u8 ack_policy,
                       u32 apsd_conf0, u32 apsd_conf1)
 {
@@ -889,7 +899,7 @@ int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->queue_id = queue_id;
        acx->channel_type = channel_type;
        acx->tsid = tsid;
@@ -1098,7 +1108,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
+int wl1271_acx_bet_enable(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         bool enable)
 {
        struct wl1271_acx_bet_enable *acx = NULL;
        int ret = 0;
@@ -1114,7 +1125,7 @@ int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->enable = enable ? CONF_BET_MODE_ENABLE : CONF_BET_MODE_DISABLE;
        acx->max_consecutive = wl->conf.conn.bet_max_consecutive;
 
@@ -1129,7 +1140,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address)
+int wl1271_acx_arp_ip_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            u8 enable, __be32 address)
 {
        struct wl1271_acx_arp_filter *acx;
        int ret;
@@ -1142,7 +1154,7 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->version = ACX_IPV4_VERSION;
        acx->enable = enable;
 
@@ -1189,7 +1201,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
+int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              bool enable)
 {
        struct wl1271_acx_keep_alive_mode *acx = NULL;
        int ret = 0;
@@ -1202,7 +1215,7 @@ int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->enabled = enable;
 
        ret = wl1271_cmd_configure(wl, ACX_KEEP_ALIVE_MODE, acx, sizeof(*acx));
@@ -1216,7 +1229,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
+int wl1271_acx_keep_alive_config(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                u8 index, u8 tpl_valid)
 {
        struct wl1271_acx_keep_alive_config *acx = NULL;
        int ret = 0;
@@ -1229,7 +1243,7 @@ int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->period = cpu_to_le32(wl->conf.conn.keep_alive_interval);
        acx->index = index;
        acx->tpl_validation = tpl_valid;
@@ -1247,8 +1261,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
-                               s16 thold, u8 hyst)
+int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                               bool enable, s16 thold, u8 hyst)
 {
        struct wl1271_acx_rssi_snr_trigger *acx = NULL;
        int ret = 0;
@@ -1261,9 +1275,9 @@ int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
                goto out;
        }
 
-       wl->last_rssi_event = -1;
+       wlvif->last_rssi_event = -1;
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing);
        acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON;
        acx->type = WL1271_ACX_TRIG_TYPE_EDGE;
@@ -1288,7 +1302,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
+int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif)
 {
        struct wl1271_acx_rssi_snr_avg_weights *acx = NULL;
        struct conf_roam_trigger_settings *c = &wl->conf.roam_trigger;
@@ -1302,7 +1317,7 @@ int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->rssi_beacon = c->avg_weight_rssi_beacon;
        acx->rssi_data = c->avg_weight_rssi_data;
        acx->snr_beacon = c->avg_weight_snr_beacon;
@@ -1367,6 +1382,7 @@ out:
 }
 
 int wl1271_acx_set_ht_information(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif,
                                   u16 ht_operation_mode)
 {
        struct wl1271_acx_ht_information *acx;
@@ -1380,7 +1396,7 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl,
                goto out;
        }
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->ht_protection =
                (u8)(ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION);
        acx->rifs_mode = 0;
@@ -1402,7 +1418,8 @@ out:
 }
 
 /* Configure BA session initiator/receiver parameters setting in the FW. */
-int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl)
+int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif)
 {
        struct wl1271_acx_ba_initiator_policy *acx;
        int ret;
@@ -1416,7 +1433,7 @@ int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl)
        }
 
        /* set for the current role */
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->tid_bitmap = wl->conf.ht.tx_ba_tid_bitmap;
        acx->win_size = wl->conf.ht.tx_ba_win_size;
        acx->inactivity_timeout = wl->conf.ht.inactivity_timeout;
@@ -1494,7 +1511,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
+int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              bool enable)
 {
        struct wl1271_acx_ps_rx_streaming *rx_streaming;
        u32 conf_queues, enable_queues;
@@ -1523,7 +1541,7 @@ int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable)
                if (!(conf_queues & BIT(i)))
                        continue;
 
-               rx_streaming->role_id = wl->role_id;
+               rx_streaming->role_id = wlvif->role_id;
                rx_streaming->tid = i;
                rx_streaming->enable = enable_queues & BIT(i);
                rx_streaming->period = wl->conf.rx_streaming.interval;
@@ -1542,7 +1560,7 @@ out:
        return ret;
 }
 
-int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl)
+int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl1271_acx_ap_max_tx_retry *acx = NULL;
        int ret;
@@ -1553,7 +1571,7 @@ int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl)
        if (!acx)
                return -ENOMEM;
 
-       acx->role_id = wl->role_id;
+       acx->role_id = wlvif->role_id;
        acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries);
 
        ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
@@ -1567,7 +1585,7 @@ out:
        return ret;
 }
 
-int wl1271_acx_config_ps(struct wl1271 *wl)
+int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl1271_acx_config_ps *config_ps;
        int ret;
@@ -1582,7 +1600,7 @@ int wl1271_acx_config_ps(struct wl1271 *wl)
 
        config_ps->exit_retries = wl->conf.conn.psm_exit_retries;
        config_ps->enter_retries = wl->conf.conn.psm_entry_retries;
-       config_ps->null_data_rate = cpu_to_le32(wl->basic_rate);
+       config_ps->null_data_rate = cpu_to_le32(wlvif->basic_rate);
 
        ret = wl1271_cmd_configure(wl, ACX_CONFIG_PS, config_ps,
                                   sizeof(*config_ps));
index e3f93b4b3429a1d6ae35b6ffe89f89322a3393d8..b2d85bea6378a6feaed850faec2c6e506c2a504c 100644 (file)
@@ -654,11 +654,6 @@ struct acx_rate_class {
        u8 reserved;
 };
 
-#define ACX_TX_BASIC_RATE      0
-#define ACX_TX_AP_FULL_RATE    1
-#define ACX_TX_BASIC_RATE_P2P  2
-#define ACX_TX_AP_MODE_MGMT_RATE 4
-#define ACX_TX_AP_MODE_BCST_RATE 5
 struct acx_rate_policy {
        struct acx_header header;
 
@@ -1234,39 +1229,49 @@ enum {
 };
 
 
-int wl1271_acx_wake_up_conditions(struct wl1271 *wl);
+int wl1271_acx_wake_up_conditions(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif);
 int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth);
-int wl1271_acx_tx_power(struct wl1271 *wl, int power);
-int wl1271_acx_feature_cfg(struct wl1271 *wl);
+int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       int power);
+int wl1271_acx_feature_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_acx_mem_map(struct wl1271 *wl,
                       struct acx_header *mem_map, size_t len);
 int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl);
 int wl1271_acx_pd_threshold(struct wl1271 *wl);
-int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time);
-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, u32 rts_threshold);
+int wl1271_acx_slot(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                   enum acx_slot_type slot_time);
+int wl1271_acx_group_address_tbl(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                bool enable, void *mc_list, u32 mc_list_len);
+int wl1271_acx_service_period_timeout(struct wl1271 *wl,
+                                     struct wl12xx_vif *wlvif);
+int wl1271_acx_rts_threshold(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            u32 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, bool enable);
+int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                bool enable_filter);
+int wl1271_acx_beacon_filter_table(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif);
+int wl1271_acx_conn_monit_params(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                bool enable);
 int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable);
 int wl12xx_acx_sg_cfg(struct wl1271 *wl);
 int wl1271_acx_cca_threshold(struct wl1271 *wl);
-int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);
-int wl1271_acx_aid(struct wl1271 *wl, u16 aid);
+int wl1271_acx_bcn_dtim_options(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl1271_acx_aid(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid);
 int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask);
-int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
-int wl1271_acx_cts_protect(struct wl1271 *wl,
+int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           enum acx_preamble_type preamble);
+int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                           enum acx_ctsprotect_type ctsprotect);
 int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
-int wl1271_acx_sta_rate_policies(struct wl1271 *wl);
+int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
                      u8 idx);
-int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
-                     u8 aifsn, u16 txop);
-int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
+int wl1271_acx_ac_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     u8 ac, u8 cw_min, u16 cw_max, u8 aifsn, u16 txop);
+int wl1271_acx_tid_cfg(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      u8 queue_id, u8 channel_type,
                       u8 tsid, u8 ps_scheme, u8 ack_policy,
                       u32 apsd_conf0, u32 apsd_conf1);
 int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold);
@@ -1276,26 +1281,34 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl);
 int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
 int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
 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, u8 enable, __be32 address);
+int wl1271_acx_bet_enable(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         bool enable);
+int wl1271_acx_arp_ip_filter(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            u8 enable, __be32 address);
 int wl1271_acx_pm_config(struct wl1271 *wl);
-int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable);
-int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
-int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
-                               s16 thold, u8 hyst);
-int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
+int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *vif,
+                              bool enable);
+int wl1271_acx_keep_alive_config(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                u8 index, u8 tpl_valid);
+int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                               bool enable, s16 thold, u8 hyst);
+int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif);
 int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
                                    struct ieee80211_sta_ht_cap *ht_cap,
                                    bool allow_ht_operation, u8 hlid);
 int wl1271_acx_set_ht_information(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif,
                                   u16 ht_operation_mode);
-int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl);
+int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif);
 int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
                                       u16 ssn, bool enable, u8 peer_hlid);
 int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
-int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable);
-int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl);
-int wl1271_acx_config_ps(struct wl1271 *wl);
+int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              bool enable);
+int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
index 6813379149767ea4c6236d6b43a0f1f0c219c8d1..6e140bf4e23666f8baf689320f2ab24e2c03482f 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/wl12xx.h>
 #include <linux/export.h>
 
+#include "debug.h"
 #include "acx.h"
 #include "reg.h"
 #include "boot.h"
index a52299e548fa38bfd8fa76d4013b2d37d8e4f3e0..afd597387e7e776ae38569738874ba8964f96cfb 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/slab.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "reg.h"
 #include "io.h"
 #include "acx.h"
@@ -358,7 +359,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
        return 0;
 }
 
-int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 role_type, u8 *role_id)
+int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
+                          u8 *role_id)
 {
        struct wl12xx_cmd_role_enable *cmd;
        int ret;
@@ -381,7 +383,7 @@ int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 role_type, u8 *role_id)
                goto out_free;
        }
 
-       memcpy(cmd->mac_address, wl->mac_addr, ETH_ALEN);
+       memcpy(cmd->mac_address, addr, ETH_ALEN);
        cmd->role_type = role_type;
 
        ret = wl1271_cmd_send(wl, CMD_ROLE_ENABLE, cmd, sizeof(*cmd), 0);
@@ -433,37 +435,41 @@ out:
        return ret;
 }
 
-static int wl12xx_allocate_link(struct wl1271 *wl, u8 *hlid)
+int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
        if (link >= WL12XX_MAX_LINKS)
                return -EBUSY;
 
        __set_bit(link, wl->links_map);
+       __set_bit(link, wlvif->links_map);
        *hlid = link;
        return 0;
 }
 
-static void wl12xx_free_link(struct wl1271 *wl, u8 *hlid)
+void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        if (*hlid == WL12XX_INVALID_LINK_ID)
                return;
 
        __clear_bit(*hlid, wl->links_map);
+       __clear_bit(*hlid, wlvif->links_map);
        *hlid = WL12XX_INVALID_LINK_ID;
 }
 
-static int wl12xx_get_new_session_id(struct wl1271 *wl)
+static int wl12xx_get_new_session_id(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif)
 {
-       if (wl->session_counter >= SESSION_COUNTER_MAX)
-               wl->session_counter = 0;
+       if (wlvif->session_counter >= SESSION_COUNTER_MAX)
+               wlvif->session_counter = 0;
 
-       wl->session_counter++;
+       wlvif->session_counter++;
 
-       return wl->session_counter;
+       return wlvif->session_counter;
 }
 
-int wl12xx_cmd_role_start_dev(struct wl1271 *wl)
+static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_start *cmd;
        int ret;
@@ -474,20 +480,20 @@ int wl12xx_cmd_role_start_dev(struct wl1271 *wl)
                goto out;
        }
 
-       wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wl->dev_role_id);
+       wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
 
-       cmd->role_id = wl->dev_role_id;
-       if (wl->band == IEEE80211_BAND_5GHZ)
+       cmd->role_id = wlvif->dev_role_id;
+       if (wlvif->band == IEEE80211_BAND_5GHZ)
                cmd->band = WL12XX_BAND_5GHZ;
-       cmd->channel = wl->channel;
+       cmd->channel = wlvif->channel;
 
-       if (wl->dev_hlid == WL12XX_INVALID_LINK_ID) {
-               ret = wl12xx_allocate_link(wl, &wl->dev_hlid);
+       if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
+               ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
                if (ret)
                        goto out_free;
        }
-       cmd->device.hlid = wl->dev_hlid;
-       cmd->device.session = wl->session_counter;
+       cmd->device.hlid = wlvif->dev_hlid;
+       cmd->device.session = wlvif->session_counter;
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
                     cmd->role_id, cmd->device.hlid, cmd->device.session);
@@ -502,9 +508,7 @@ int wl12xx_cmd_role_start_dev(struct wl1271 *wl)
 
 err_hlid:
        /* clear links on error */
-       __clear_bit(wl->dev_hlid, wl->links_map);
-       wl->dev_hlid = WL12XX_INVALID_LINK_ID;
-
+       wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
 
 out_free:
        kfree(cmd);
@@ -513,12 +517,13 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_role_stop_dev(struct wl1271 *wl)
+static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_stop *cmd;
        int ret;
 
-       if (WARN_ON(wl->dev_hlid == WL12XX_INVALID_LINK_ID))
+       if (WARN_ON(wlvif->dev_hlid == WL12XX_INVALID_LINK_ID))
                return -EINVAL;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -529,7 +534,7 @@ int wl12xx_cmd_role_stop_dev(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_CMD, "cmd role stop dev");
 
-       cmd->role_id = wl->dev_role_id;
+       cmd->role_id = wlvif->dev_role_id;
        cmd->disc_type = DISCONNECT_IMMEDIATE;
        cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED);
 
@@ -545,7 +550,7 @@ int wl12xx_cmd_role_stop_dev(struct wl1271 *wl)
                goto out_free;
        }
 
-       wl12xx_free_link(wl, &wl->dev_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
 
 out_free:
        kfree(cmd);
@@ -554,8 +559,9 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_role_start_sta(struct wl1271 *wl)
+int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct wl12xx_cmd_role_start *cmd;
        int ret;
 
@@ -565,33 +571,33 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl)
                goto out;
        }
 
-       wl1271_debug(DEBUG_CMD, "cmd role start sta %d", wl->role_id);
+       wl1271_debug(DEBUG_CMD, "cmd role start sta %d", wlvif->role_id);
 
-       cmd->role_id = wl->role_id;
-       if (wl->band == IEEE80211_BAND_5GHZ)
+       cmd->role_id = wlvif->role_id;
+       if (wlvif->band == IEEE80211_BAND_5GHZ)
                cmd->band = WL12XX_BAND_5GHZ;
-       cmd->channel = wl->channel;
-       cmd->sta.basic_rate_set = cpu_to_le32(wl->basic_rate_set);
-       cmd->sta.beacon_interval = cpu_to_le16(wl->beacon_int);
+       cmd->channel = wlvif->channel;
+       cmd->sta.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+       cmd->sta.beacon_interval = cpu_to_le16(wlvif->beacon_int);
        cmd->sta.ssid_type = WL12XX_SSID_TYPE_ANY;
-       cmd->sta.ssid_len = wl->ssid_len;
-       memcpy(cmd->sta.ssid, wl->ssid, wl->ssid_len);
-       memcpy(cmd->sta.bssid, wl->bssid, ETH_ALEN);
-       cmd->sta.local_rates = cpu_to_le32(wl->rate_set);
+       cmd->sta.ssid_len = wlvif->ssid_len;
+       memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
+       memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
+       cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
 
-       if (wl->sta_hlid == WL12XX_INVALID_LINK_ID) {
-               ret = wl12xx_allocate_link(wl, &wl->sta_hlid);
+       if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
+               ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
                if (ret)
                        goto out_free;
        }
-       cmd->sta.hlid = wl->sta_hlid;
-       cmd->sta.session = wl12xx_get_new_session_id(wl);
-       cmd->sta.remote_rates = cpu_to_le32(wl->rate_set);
+       cmd->sta.hlid = wlvif->sta.hlid;
+       cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
+       cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
                     "basic_rate_set: 0x%x, remote_rates: 0x%x",
-                    wl->role_id, cmd->sta.hlid, cmd->sta.session,
-                    wl->basic_rate_set, wl->rate_set);
+                    wlvif->role_id, cmd->sta.hlid, cmd->sta.session,
+                    wlvif->basic_rate_set, wlvif->rate_set);
 
        ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
@@ -603,7 +609,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl)
 
 err_hlid:
        /* clear links on error. */
-       wl12xx_free_link(wl, &wl->sta_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
 
 out_free:
        kfree(cmd);
@@ -613,12 +619,12 @@ out:
 }
 
 /* use this function to stop ibss as well */
-int wl12xx_cmd_role_stop_sta(struct wl1271 *wl)
+int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_stop *cmd;
        int ret;
 
-       if (WARN_ON(wl->sta_hlid == WL12XX_INVALID_LINK_ID))
+       if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID))
                return -EINVAL;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -627,9 +633,9 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl)
                goto out;
        }
 
-       wl1271_debug(DEBUG_CMD, "cmd role stop sta %d", wl->role_id);
+       wl1271_debug(DEBUG_CMD, "cmd role stop sta %d", wlvif->role_id);
 
-       cmd->role_id = wl->role_id;
+       cmd->role_id = wlvif->role_id;
        cmd->disc_type = DISCONNECT_IMMEDIATE;
        cmd->reason = cpu_to_le16(WLAN_REASON_UNSPECIFIED);
 
@@ -639,7 +645,7 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl)
                goto out_free;
        }
 
-       wl12xx_free_link(wl, &wl->sta_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
 
 out_free:
        kfree(cmd);
@@ -648,16 +654,17 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
+int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_start *cmd;
-       struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
        int ret;
 
-       wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wl->role_id);
+       wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
 
        /* trying to use hidden SSID with an old hostapd version */
-       if (wl->ssid_len == 0 && !bss_conf->hidden_ssid) {
+       if (wlvif->ssid_len == 0 && !bss_conf->hidden_ssid) {
                wl1271_error("got a null SSID from beacon/bss");
                ret = -EINVAL;
                goto out;
@@ -669,30 +676,30 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
                goto out;
        }
 
-       ret = wl12xx_allocate_link(wl, &wl->ap_global_hlid);
+       ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.global_hlid);
        if (ret < 0)
                goto out_free;
 
-       ret = wl12xx_allocate_link(wl, &wl->ap_bcast_hlid);
+       ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.bcast_hlid);
        if (ret < 0)
                goto out_free_global;
 
-       cmd->role_id = wl->role_id;
+       cmd->role_id = wlvif->role_id;
        cmd->ap.aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period);
        cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
-       cmd->ap.global_hlid = wl->ap_global_hlid;
-       cmd->ap.broadcast_hlid = wl->ap_bcast_hlid;
-       cmd->ap.basic_rate_set = cpu_to_le32(wl->basic_rate_set);
-       cmd->ap.beacon_interval = cpu_to_le16(wl->beacon_int);
+       cmd->ap.global_hlid = wlvif->ap.global_hlid;
+       cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid;
+       cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+       cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
        cmd->ap.dtim_interval = bss_conf->dtim_period;
        cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
-       cmd->channel = wl->channel;
+       cmd->channel = wlvif->channel;
 
        if (!bss_conf->hidden_ssid) {
                /* take the SSID from the beacon for backward compatibility */
                cmd->ap.ssid_type = WL12XX_SSID_TYPE_PUBLIC;
-               cmd->ap.ssid_len = wl->ssid_len;
-               memcpy(cmd->ap.ssid, wl->ssid, wl->ssid_len);
+               cmd->ap.ssid_len = wlvif->ssid_len;
+               memcpy(cmd->ap.ssid, wlvif->ssid, wlvif->ssid_len);
        } else {
                cmd->ap.ssid_type = WL12XX_SSID_TYPE_HIDDEN;
                cmd->ap.ssid_len = bss_conf->ssid_len;
@@ -701,7 +708,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
 
        cmd->ap.local_rates = cpu_to_le32(0xffffffff);
 
-       switch (wl->band) {
+       switch (wlvif->band) {
        case IEEE80211_BAND_2GHZ:
                cmd->band = RADIO_BAND_2_4GHZ;
                break;
@@ -709,7 +716,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
                cmd->band = RADIO_BAND_5GHZ;
                break;
        default:
-               wl1271_warning("ap start - unknown band: %d", (int)wl->band);
+               wl1271_warning("ap start - unknown band: %d", (int)wlvif->band);
                cmd->band = RADIO_BAND_2_4GHZ;
                break;
        }
@@ -723,10 +730,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl)
        goto out_free;
 
 out_free_bcast:
-       wl12xx_free_link(wl, &wl->ap_bcast_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->ap.bcast_hlid);
 
 out_free_global:
-       wl12xx_free_link(wl, &wl->ap_global_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->ap.global_hlid);
 
 out_free:
        kfree(cmd);
@@ -735,7 +742,7 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_role_stop_ap(struct wl1271 *wl)
+int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_stop *cmd;
        int ret;
@@ -746,9 +753,9 @@ int wl12xx_cmd_role_stop_ap(struct wl1271 *wl)
                goto out;
        }
 
-       wl1271_debug(DEBUG_CMD, "cmd role stop ap %d", wl->role_id);
+       wl1271_debug(DEBUG_CMD, "cmd role stop ap %d", wlvif->role_id);
 
-       cmd->role_id = wl->role_id;
+       cmd->role_id = wlvif->role_id;
 
        ret = wl1271_cmd_send(wl, CMD_ROLE_STOP, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
@@ -756,8 +763,8 @@ int wl12xx_cmd_role_stop_ap(struct wl1271 *wl)
                goto out_free;
        }
 
-       wl12xx_free_link(wl, &wl->ap_bcast_hlid);
-       wl12xx_free_link(wl, &wl->ap_global_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->ap.bcast_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->ap.global_hlid);
 
 out_free:
        kfree(cmd);
@@ -766,10 +773,11 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_role_start_ibss(struct wl1271 *wl)
+int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct wl12xx_cmd_role_start *cmd;
-       struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
        int ret;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -778,35 +786,36 @@ int wl12xx_cmd_role_start_ibss(struct wl1271 *wl)
                goto out;
        }
 
-       wl1271_debug(DEBUG_CMD, "cmd role start ibss %d", wl->role_id);
+       wl1271_debug(DEBUG_CMD, "cmd role start ibss %d", wlvif->role_id);
 
-       cmd->role_id = wl->role_id;
-       if (wl->band == IEEE80211_BAND_5GHZ)
+       cmd->role_id = wlvif->role_id;
+       if (wlvif->band == IEEE80211_BAND_5GHZ)
                cmd->band = WL12XX_BAND_5GHZ;
-       cmd->channel = wl->channel;
-       cmd->ibss.basic_rate_set = cpu_to_le32(wl->basic_rate_set);
-       cmd->ibss.beacon_interval = cpu_to_le16(wl->beacon_int);
+       cmd->channel = wlvif->channel;
+       cmd->ibss.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
+       cmd->ibss.beacon_interval = cpu_to_le16(wlvif->beacon_int);
        cmd->ibss.dtim_interval = bss_conf->dtim_period;
        cmd->ibss.ssid_type = WL12XX_SSID_TYPE_ANY;
-       cmd->ibss.ssid_len = wl->ssid_len;
-       memcpy(cmd->ibss.ssid, wl->ssid, wl->ssid_len);
-       memcpy(cmd->ibss.bssid, wl->bssid, ETH_ALEN);
-       cmd->sta.local_rates = cpu_to_le32(wl->rate_set);
+       cmd->ibss.ssid_len = wlvif->ssid_len;
+       memcpy(cmd->ibss.ssid, wlvif->ssid, wlvif->ssid_len);
+       memcpy(cmd->ibss.bssid, vif->bss_conf.bssid, ETH_ALEN);
+       cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
 
-       if (wl->sta_hlid == WL12XX_INVALID_LINK_ID) {
-               ret = wl12xx_allocate_link(wl, &wl->sta_hlid);
+       if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
+               ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
                if (ret)
                        goto out_free;
        }
-       cmd->ibss.hlid = wl->sta_hlid;
-       cmd->ibss.remote_rates = cpu_to_le32(wl->rate_set);
+       cmd->ibss.hlid = wlvif->sta.hlid;
+       cmd->ibss.remote_rates = cpu_to_le32(wlvif->rate_set);
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
                     "basic_rate_set: 0x%x, remote_rates: 0x%x",
-                    wl->role_id, cmd->sta.hlid, cmd->sta.session,
-                    wl->basic_rate_set, wl->rate_set);
+                    wlvif->role_id, cmd->sta.hlid, cmd->sta.session,
+                    wlvif->basic_rate_set, wlvif->rate_set);
 
-       wl1271_debug(DEBUG_CMD, "wl->bssid = %pM", wl->bssid);
+       wl1271_debug(DEBUG_CMD, "vif->bss_conf.bssid = %pM",
+                    vif->bss_conf.bssid);
 
        ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
@@ -818,7 +827,7 @@ int wl12xx_cmd_role_start_ibss(struct wl1271 *wl)
 
 err_hlid:
        /* clear links on error. */
-       wl12xx_free_link(wl, &wl->sta_hlid);
+       wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
 
 out_free:
        kfree(cmd);
@@ -962,7 +971,8 @@ out:
        return ret;
 }
 
-int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
+int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      u8 ps_mode)
 {
        struct wl1271_cmd_ps_params *ps_params = NULL;
        int ret = 0;
@@ -975,7 +985,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode)
                goto out;
        }
 
-       ps_params->role_id = wl->role_id;
+       ps_params->role_id = wlvif->role_id;
        ps_params->ps_mode = ps_mode;
 
        ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
@@ -1030,7 +1040,7 @@ out:
        return ret;
 }
 
-int wl1271_cmd_build_null_data(struct wl1271 *wl)
+int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct sk_buff *skb = NULL;
        int size;
@@ -1038,11 +1048,12 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl)
        int ret = -ENOMEM;
 
 
-       if (wl->bss_type == BSS_TYPE_IBSS) {
+       if (wlvif->bss_type == BSS_TYPE_IBSS) {
                size = sizeof(struct wl12xx_null_data_template);
                ptr = NULL;
        } else {
-               skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+               skb = ieee80211_nullfunc_get(wl->hw,
+                                            wl12xx_wlvif_to_vif(wlvif));
                if (!skb)
                        goto out;
                size = skb->len;
@@ -1050,7 +1061,7 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl)
        }
 
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0,
-                                     wl->basic_rate);
+                                     wlvif->basic_rate);
 
 out:
        dev_kfree_skb(skb);
@@ -1061,19 +1072,21 @@ out:
 
 }
 
-int wl1271_cmd_build_klv_null_data(struct wl1271 *wl)
+int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct sk_buff *skb = NULL;
        int ret = -ENOMEM;
 
-       skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+       skb = ieee80211_nullfunc_get(wl->hw, vif);
        if (!skb)
                goto out;
 
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV,
                                      skb->data, skb->len,
                                      CMD_TEMPL_KLV_IDX_NULL_DATA,
-                                     wl->basic_rate);
+                                     wlvif->basic_rate);
 
 out:
        dev_kfree_skb(skb);
@@ -1084,32 +1097,35 @@ out:
 
 }
 
-int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
+int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            u16 aid)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct sk_buff *skb;
        int ret = 0;
 
-       skb = ieee80211_pspoll_get(wl->hw, wl->vif);
+       skb = ieee80211_pspoll_get(wl->hw, vif);
        if (!skb)
                goto out;
 
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
-                                     skb->len, 0, wl->basic_rate_set);
+                                     skb->len, 0, wlvif->basic_rate_set);
 
 out:
        dev_kfree_skb(skb);
        return ret;
 }
 
-int wl1271_cmd_build_probe_req(struct wl1271 *wl,
+int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               const u8 *ssid, size_t ssid_len,
                               const u8 *ie, size_t ie_len, u8 band)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct sk_buff *skb;
        int ret;
        u32 rate;
 
-       skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
+       skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
                                     ie, ie_len);
        if (!skb) {
                ret = -ENOMEM;
@@ -1118,7 +1134,7 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl,
 
        wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
 
-       rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
+       rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
        if (band == IEEE80211_BAND_2GHZ)
                ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
                                              skb->data, skb->len, 0, rate);
@@ -1132,20 +1148,22 @@ out:
 }
 
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
+                                             struct wl12xx_vif *wlvif,
                                              struct sk_buff *skb)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        int ret;
        u32 rate;
 
        if (!skb)
-               skb = ieee80211_ap_probereq_get(wl->hw, wl->vif);
+               skb = ieee80211_ap_probereq_get(wl->hw, vif);
        if (!skb)
                goto out;
 
        wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len);
 
-       rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[wl->band]);
-       if (wl->band == IEEE80211_BAND_2GHZ)
+       rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]);
+       if (wlvif->band == IEEE80211_BAND_2GHZ)
                ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
                                              skb->data, skb->len, 0, rate);
        else
@@ -1159,9 +1177,11 @@ out:
        return skb;
 }
 
-int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
+int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            __be32 ip_addr)
 {
        int ret;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct wl12xx_arp_rsp_template tmpl;
        struct ieee80211_hdr_3addr *hdr;
        struct arphdr *arp_hdr;
@@ -1173,8 +1193,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
        hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
                                         IEEE80211_STYPE_DATA |
                                         IEEE80211_FCTL_TODS);
-       memcpy(hdr->addr1, wl->vif->bss_conf.bssid, ETH_ALEN);
-       memcpy(hdr->addr2, wl->vif->addr, ETH_ALEN);
+       memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
+       memcpy(hdr->addr2, vif->addr, ETH_ALEN);
        memset(hdr->addr3, 0xff, ETH_ALEN);
 
        /* llc layer */
@@ -1190,25 +1210,26 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr)
        arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
 
        /* arp payload */
-       memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN);
+       memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN);
        tmpl.sender_ip = ip_addr;
 
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP,
                                      &tmpl, sizeof(tmpl), 0,
-                                     wl->basic_rate);
+                                     wlvif->basic_rate);
 
        return ret;
 }
 
-int wl1271_build_qos_null_data(struct wl1271 *wl)
+int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct ieee80211_qos_hdr template;
 
        memset(&template, 0, sizeof(template));
 
-       memcpy(template.addr1, wl->bssid, ETH_ALEN);
-       memcpy(template.addr2, wl->mac_addr, ETH_ALEN);
-       memcpy(template.addr3, wl->bssid, ETH_ALEN);
+       memcpy(template.addr1, vif->bss_conf.bssid, ETH_ALEN);
+       memcpy(template.addr2, vif->addr, ETH_ALEN);
+       memcpy(template.addr3, vif->bss_conf.bssid, ETH_ALEN);
 
        template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
                                             IEEE80211_STYPE_QOS_NULLFUNC |
@@ -1219,7 +1240,7 @@ int wl1271_build_qos_null_data(struct wl1271 *wl)
 
        return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template,
                                       sizeof(template), 0,
-                                      wl->basic_rate);
+                                      wlvif->basic_rate);
 }
 
 int wl12xx_cmd_set_default_wep_key(struct wl1271 *wl, u8 id, u8 hlid)
@@ -1253,7 +1274,8 @@ out:
        return ret;
 }
 
-int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      u16 action, u8 id, u8 key_type,
                       u8 key_size, const u8 *key, const u8 *addr,
                       u32 tx_seq_32, u16 tx_seq_16)
 {
@@ -1261,7 +1283,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
        int ret = 0;
 
        /* hlid might have already been deleted */
-       if (wl->sta_hlid == WL12XX_INVALID_LINK_ID)
+       if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
                return 0;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -1270,7 +1292,7 @@ int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                goto out;
        }
 
-       cmd->hlid = wl->sta_hlid;
+       cmd->hlid = wlvif->sta.hlid;
 
        if (key_type == KEY_WEP)
                cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
@@ -1321,9 +1343,10 @@ out:
  * TODO: merge with sta/ibss into 1 set_key function.
  * note there are slight diffs
  */
-int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
-                       u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
-                       u16 tx_seq_16)
+int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         u16 action, u8 id, u8 key_type,
+                         u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
+                         u16 tx_seq_16)
 {
        struct wl1271_cmd_set_keys *cmd;
        int ret = 0;
@@ -1333,7 +1356,7 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
        if (!cmd)
                return -ENOMEM;
 
-       if (hlid == wl->ap_bcast_hlid) {
+       if (hlid == wlvif->ap.bcast_hlid) {
                if (key_type == KEY_WEP)
                        lid_type = WEP_DEFAULT_LID_TYPE;
                else
@@ -1411,7 +1434,8 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
+int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       struct ieee80211_sta *sta, u8 hlid)
 {
        struct wl12xx_cmd_add_peer *cmd;
        int i, ret;
@@ -1438,13 +1462,13 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
                else
                        cmd->psd_type[i] = WL1271_PSD_LEGACY;
 
-       sta_rates = sta->supp_rates[wl->band];
+       sta_rates = sta->supp_rates[wlvif->band];
        if (sta->ht_cap.ht_supported)
                sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET;
 
        cmd->supported_rates =
                cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
-                                                       wl->band));
+                                                       wlvif->band));
 
        wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
                     cmd->supported_rates, sta->uapsd_queues);
@@ -1584,12 +1608,13 @@ out:
        return ret;
 }
 
-static int wl12xx_cmd_roc(struct wl1271 *wl, u8 role_id)
+static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         u8 role_id)
 {
        struct wl12xx_cmd_roc *cmd;
        int ret = 0;
 
-       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wl->channel, role_id);
+       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
 
        if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
                return -EINVAL;
@@ -1601,8 +1626,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, u8 role_id)
        }
 
        cmd->role_id = role_id;
-       cmd->channel = wl->channel;
-       switch (wl->band) {
+       cmd->channel = wlvif->channel;
+       switch (wlvif->band) {
        case IEEE80211_BAND_2GHZ:
                cmd->band = RADIO_BAND_2_4GHZ;
                break;
@@ -1610,7 +1635,7 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, u8 role_id)
                cmd->band = RADIO_BAND_5GHZ;
                break;
        default:
-               wl1271_error("roc - unknown band: %d", (int)wl->band);
+               wl1271_error("roc - unknown band: %d", (int)wlvif->band);
                ret = -EINVAL;
                goto out_free;
        }
@@ -1657,14 +1682,14 @@ out:
        return ret;
 }
 
-int wl12xx_roc(struct wl1271 *wl, u8 role_id)
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
 {
        int ret = 0;
 
        if (WARN_ON(test_bit(role_id, wl->roc_map)))
                return 0;
 
-       ret = wl12xx_cmd_roc(wl, role_id);
+       ret = wl12xx_cmd_roc(wl, wlvif, role_id);
        if (ret < 0)
                goto out;
 
@@ -1753,3 +1778,50 @@ out_free:
 out:
        return ret;
 }
+
+/* start dev role and roc on its channel */
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
+                     wlvif->bss_type == BSS_TYPE_IBSS)))
+               return -EINVAL;
+
+       ret = wl12xx_cmd_role_start_dev(wl, wlvif);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
+       if (ret < 0)
+               goto out_stop;
+
+       return 0;
+
+out_stop:
+       wl12xx_cmd_role_stop_dev(wl, wlvif);
+out:
+       return ret;
+}
+
+/* croc dev hlid, and stop the role */
+int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
+                     wlvif->bss_type == BSS_TYPE_IBSS)))
+               return -EINVAL;
+
+       if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
+               ret = wl12xx_croc(wl, wlvif->dev_role_id);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = wl12xx_cmd_role_stop_dev(wl, wlvif);
+       if (ret < 0)
+               goto out;
+out:
+       return ret;
+}
index b7bd42769aa72eedf0cffe59716728554e063e75..3f7d0b93c24d76c2853fc17ce43580646904e283 100644 (file)
@@ -36,45 +36,54 @@ int wl128x_cmd_general_parms(struct wl1271 *wl);
 int wl1271_cmd_radio_parms(struct wl1271 *wl);
 int wl128x_cmd_radio_parms(struct wl1271 *wl);
 int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
-int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 role_type, u8 *role_id);
+int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
+                          u8 *role_id);
 int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
-int wl12xx_cmd_role_start_dev(struct wl1271 *wl);
-int wl12xx_cmd_role_stop_dev(struct wl1271 *wl);
-int wl12xx_cmd_role_start_sta(struct wl1271 *wl);
-int wl12xx_cmd_role_stop_sta(struct wl1271 *wl);
-int wl12xx_cmd_role_start_ap(struct wl1271 *wl);
-int wl12xx_cmd_role_stop_ap(struct wl1271 *wl);
-int wl12xx_cmd_role_start_ibss(struct wl1271 *wl);
+int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 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, bool enable);
-int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode);
+int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      u8 ps_mode);
 int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
                           size_t len);
 int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
                            void *buf, size_t buf_len, int index, u32 rates);
-int wl1271_cmd_build_null_data(struct wl1271 *wl);
-int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
-int wl1271_cmd_build_probe_req(struct wl1271 *wl,
+int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            u16 aid);
+int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               const u8 *ssid, size_t ssid_len,
                               const u8 *ie, size_t ie_len, u8 band);
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
+                                             struct wl12xx_vif *wlvif,
                                              struct sk_buff *skb);
-int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr);
-int wl1271_build_qos_null_data(struct wl1271 *wl);
-int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
+int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            __be32 ip_addr);
+int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif);
+int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif);
 int wl12xx_cmd_set_default_wep_key(struct wl1271 *wl, u8 id, u8 hlid);
-int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+int wl1271_cmd_set_sta_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                          u16 action, u8 id, u8 key_type,
                           u8 key_size, const u8 *key, const u8 *addr,
                           u32 tx_seq_32, u16 tx_seq_16);
-int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         u16 action, u8 id, u8 key_type,
                          u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
                          u16 tx_seq_16);
 int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid);
-int wl12xx_roc(struct wl1271 *wl, u8 role_id);
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
 int wl12xx_croc(struct wl1271 *wl, u8 role_id);
-int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
+int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       struct ieee80211_sta *sta, u8 hlid);
 int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
@@ -82,6 +91,9 @@ int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_channel_switch(struct wl1271 *wl,
                              struct ieee80211_channel_switch *ch_switch);
 int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl);
+int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                        u8 *hlid);
+void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
 
 enum wl1271_commands {
        CMD_INTERROGATE     = 1,    /*use this to read information elements*/
index 04bb8fbf93f96630386326994bd8d119e96b9917..1bcfb017058d19da42682bdb817488575d8be6d1 100644 (file)
@@ -440,6 +440,10 @@ struct conf_rx_settings {
        CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS |      \
        CONF_HW_BIT_RATE_54MBPS)
 
+#define CONF_TX_CCK_RATES  (CONF_HW_BIT_RATE_1MBPS |           \
+       CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS |     \
+       CONF_HW_BIT_RATE_11MBPS)
+
 #define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS |             \
        CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS |      \
        CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS |      \
diff --git a/drivers/net/wireless/wl12xx/debug.h b/drivers/net/wireless/wl12xx/debug.h
new file mode 100644 (file)
index 0000000..b85fd8c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <coelho@ti.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#include <linux/bitops.h>
+#include <linux/printk.h>
+
+#define DRIVER_NAME "wl12xx"
+#define DRIVER_PREFIX DRIVER_NAME ": "
+
+enum {
+       DEBUG_NONE      = 0,
+       DEBUG_IRQ       = BIT(0),
+       DEBUG_SPI       = BIT(1),
+       DEBUG_BOOT      = BIT(2),
+       DEBUG_MAILBOX   = BIT(3),
+       DEBUG_TESTMODE  = BIT(4),
+       DEBUG_EVENT     = BIT(5),
+       DEBUG_TX        = BIT(6),
+       DEBUG_RX        = BIT(7),
+       DEBUG_SCAN      = BIT(8),
+       DEBUG_CRYPT     = BIT(9),
+       DEBUG_PSM       = BIT(10),
+       DEBUG_MAC80211  = BIT(11),
+       DEBUG_CMD       = BIT(12),
+       DEBUG_ACX       = BIT(13),
+       DEBUG_SDIO      = BIT(14),
+       DEBUG_FILTERS   = BIT(15),
+       DEBUG_ADHOC     = BIT(16),
+       DEBUG_AP        = BIT(17),
+       DEBUG_MASTER    = (DEBUG_ADHOC | DEBUG_AP),
+       DEBUG_ALL       = ~0,
+};
+
+extern u32 wl12xx_debug_level;
+
+#define DEBUG_DUMP_LIMIT 1024
+
+#define wl1271_error(fmt, arg...) \
+       pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
+
+#define wl1271_warning(fmt, arg...) \
+       pr_warning(DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
+
+#define wl1271_notice(fmt, arg...) \
+       pr_info(DRIVER_PREFIX fmt "\n", ##arg)
+
+#define wl1271_info(fmt, arg...) \
+       pr_info(DRIVER_PREFIX fmt "\n", ##arg)
+
+#define wl1271_debug(level, fmt, arg...) \
+       do { \
+               if (level & wl12xx_debug_level) \
+                       pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \
+       } while (0)
+
+/* TODO: use pr_debug_hex_dump when it becomes available */
+#define wl1271_dump(level, prefix, buf, len)   \
+       do { \
+               if (level & wl12xx_debug_level) \
+                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
+                                      DUMP_PREFIX_OFFSET, 16, 1,       \
+                                      buf,                             \
+                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+                                      0);                              \
+       } while (0)
+
+#define wl1271_dump_ascii(level, prefix, buf, len)     \
+       do { \
+               if (level & wl12xx_debug_level) \
+                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
+                                      DUMP_PREFIX_OFFSET, 16, 1,       \
+                                      buf,                             \
+                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
+                                      true);                           \
+       } while (0)
+
+#endif /* __DEBUG_H__ */
index 3999fd528302cfbadd26a7f9fe8862caaf175656..2e14b436101f9f2e2d1dde9bf4e8ef5328f11f58 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "acx.h"
 #include "ps.h"
 #include "io.h"
@@ -346,29 +347,14 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_INT(tx_results_count);
        DRIVER_STATE_PRINT_LHEX(flags);
        DRIVER_STATE_PRINT_INT(tx_blocks_freed);
-       DRIVER_STATE_PRINT_INT(tx_security_last_seq_lsb);
        DRIVER_STATE_PRINT_INT(rx_counter);
-       DRIVER_STATE_PRINT_INT(session_counter);
        DRIVER_STATE_PRINT_INT(state);
-       DRIVER_STATE_PRINT_INT(bss_type);
        DRIVER_STATE_PRINT_INT(channel);
-       DRIVER_STATE_PRINT_HEX(rate_set);
-       DRIVER_STATE_PRINT_HEX(basic_rate_set);
-       DRIVER_STATE_PRINT_HEX(basic_rate);
        DRIVER_STATE_PRINT_INT(band);
-       DRIVER_STATE_PRINT_INT(beacon_int);
-       DRIVER_STATE_PRINT_INT(psm_entry_retry);
-       DRIVER_STATE_PRINT_INT(ps_poll_failures);
        DRIVER_STATE_PRINT_INT(power_level);
-       DRIVER_STATE_PRINT_INT(rssi_thold);
-       DRIVER_STATE_PRINT_INT(last_rssi_event);
        DRIVER_STATE_PRINT_INT(sg_enabled);
        DRIVER_STATE_PRINT_INT(enable_11a);
        DRIVER_STATE_PRINT_INT(noise);
-       DRIVER_STATE_PRINT_LHEX(ap_hlid_map[0]);
-       DRIVER_STATE_PRINT_INT(last_tx_hlid);
-       DRIVER_STATE_PRINT_INT(ba_support);
-       DRIVER_STATE_PRINT_HEX(ba_rx_bitmap);
        DRIVER_STATE_PRINT_HEX(ap_fw_ps_map);
        DRIVER_STATE_PRINT_LHEX(ap_ps_map);
        DRIVER_STATE_PRINT_HEX(quirks);
@@ -399,6 +385,115 @@ static const struct file_operations driver_state_ops = {
        .llseek = default_llseek,
 };
 
+static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       struct wl12xx_vif *wlvif;
+       int ret, res = 0;
+       const int buf_size = 4096;
+       char *buf;
+       char tmp_buf[64];
+
+       buf = kzalloc(buf_size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&wl->mutex);
+
+#define VIF_STATE_PRINT(x, fmt)                                \
+       (res += scnprintf(buf + res, buf_size - res,    \
+                         #x " = " fmt "\n", wlvif->x))
+
+#define VIF_STATE_PRINT_LONG(x)  VIF_STATE_PRINT(x, "%ld")
+#define VIF_STATE_PRINT_INT(x)   VIF_STATE_PRINT(x, "%d")
+#define VIF_STATE_PRINT_STR(x)   VIF_STATE_PRINT(x, "%s")
+#define VIF_STATE_PRINT_LHEX(x)  VIF_STATE_PRINT(x, "0x%lx")
+#define VIF_STATE_PRINT_LLHEX(x) VIF_STATE_PRINT(x, "0x%llx")
+#define VIF_STATE_PRINT_HEX(x)   VIF_STATE_PRINT(x, "0x%x")
+
+#define VIF_STATE_PRINT_NSTR(x, len)                           \
+       do {                                                    \
+               memset(tmp_buf, 0, sizeof(tmp_buf));            \
+               memcpy(tmp_buf, wlvif->x,                       \
+                      min_t(u8, len, sizeof(tmp_buf) - 1));    \
+               res += scnprintf(buf + res, buf_size - res,     \
+                                #x " = %s\n", tmp_buf);        \
+       } while (0)
+
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               VIF_STATE_PRINT_INT(role_id);
+               VIF_STATE_PRINT_INT(bss_type);
+               VIF_STATE_PRINT_LHEX(flags);
+               VIF_STATE_PRINT_INT(p2p);
+               VIF_STATE_PRINT_INT(dev_role_id);
+               VIF_STATE_PRINT_INT(dev_hlid);
+
+               if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+                   wlvif->bss_type == BSS_TYPE_IBSS) {
+                       VIF_STATE_PRINT_INT(sta.hlid);
+                       VIF_STATE_PRINT_INT(sta.ba_rx_bitmap);
+                       VIF_STATE_PRINT_INT(sta.basic_rate_idx);
+                       VIF_STATE_PRINT_INT(sta.ap_rate_idx);
+                       VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
+               } else {
+                       VIF_STATE_PRINT_INT(ap.global_hlid);
+                       VIF_STATE_PRINT_INT(ap.bcast_hlid);
+                       VIF_STATE_PRINT_LHEX(ap.sta_hlid_map[0]);
+                       VIF_STATE_PRINT_INT(ap.mgmt_rate_idx);
+                       VIF_STATE_PRINT_INT(ap.bcast_rate_idx);
+                       VIF_STATE_PRINT_INT(ap.ucast_rate_idx[0]);
+                       VIF_STATE_PRINT_INT(ap.ucast_rate_idx[1]);
+                       VIF_STATE_PRINT_INT(ap.ucast_rate_idx[2]);
+                       VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
+               }
+               VIF_STATE_PRINT_INT(last_tx_hlid);
+               VIF_STATE_PRINT_LHEX(links_map[0]);
+               VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
+               VIF_STATE_PRINT_INT(band);
+               VIF_STATE_PRINT_INT(channel);
+               VIF_STATE_PRINT_HEX(bitrate_masks[0]);
+               VIF_STATE_PRINT_HEX(bitrate_masks[1]);
+               VIF_STATE_PRINT_HEX(basic_rate_set);
+               VIF_STATE_PRINT_HEX(basic_rate);
+               VIF_STATE_PRINT_HEX(rate_set);
+               VIF_STATE_PRINT_INT(beacon_int);
+               VIF_STATE_PRINT_INT(default_key);
+               VIF_STATE_PRINT_INT(aid);
+               VIF_STATE_PRINT_INT(session_counter);
+               VIF_STATE_PRINT_INT(ps_poll_failures);
+               VIF_STATE_PRINT_INT(psm_entry_retry);
+               VIF_STATE_PRINT_INT(power_level);
+               VIF_STATE_PRINT_INT(rssi_thold);
+               VIF_STATE_PRINT_INT(last_rssi_event);
+               VIF_STATE_PRINT_INT(ba_support);
+               VIF_STATE_PRINT_INT(ba_allowed);
+               VIF_STATE_PRINT_LLHEX(tx_security_seq);
+               VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
+       }
+
+#undef VIF_STATE_PRINT_INT
+#undef VIF_STATE_PRINT_LONG
+#undef VIF_STATE_PRINT_HEX
+#undef VIF_STATE_PRINT_LHEX
+#undef VIF_STATE_PRINT_LLHEX
+#undef VIF_STATE_PRINT_STR
+#undef VIF_STATE_PRINT_NSTR
+#undef VIF_STATE_PRINT
+
+       mutex_unlock(&wl->mutex);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, res);
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations vifs_state_ops = {
+       .read = vifs_state_read,
+       .open = wl1271_open_file_generic,
+       .llseek = default_llseek,
+};
+
 static ssize_t dtim_interval_read(struct file *file, char __user *user_buf,
                                  size_t count, loff_t *ppos)
 {
@@ -520,6 +615,7 @@ static ssize_t rx_streaming_interval_write(struct file *file,
                           size_t count, loff_t *ppos)
 {
        struct wl1271 *wl = file->private_data;
+       struct wl12xx_vif *wlvif;
        unsigned long value;
        int ret;
 
@@ -543,7 +639,9 @@ static ssize_t rx_streaming_interval_write(struct file *file,
        if (ret < 0)
                goto out;
 
-       wl1271_recalc_rx_streaming(wl);
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               wl1271_recalc_rx_streaming(wl, wlvif);
+       }
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -572,6 +670,7 @@ static ssize_t rx_streaming_always_write(struct file *file,
                           size_t count, loff_t *ppos)
 {
        struct wl1271 *wl = file->private_data;
+       struct wl12xx_vif *wlvif;
        unsigned long value;
        int ret;
 
@@ -595,7 +694,9 @@ static ssize_t rx_streaming_always_write(struct file *file,
        if (ret < 0)
                goto out;
 
-       wl1271_recalc_rx_streaming(wl);
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               wl1271_recalc_rx_streaming(wl, wlvif);
+       }
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -624,6 +725,7 @@ static ssize_t beacon_filtering_write(struct file *file,
                                      size_t count, loff_t *ppos)
 {
        struct wl1271 *wl = file->private_data;
+       struct wl12xx_vif *wlvif;
        char buf[10];
        size_t len;
        unsigned long value;
@@ -646,7 +748,9 @@ static ssize_t beacon_filtering_write(struct file *file,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_acx_beacon_filter_opt(wl, !!value);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value);
+       }
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -770,6 +874,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
        DEBUGFS_ADD(gpio_power, rootdir);
        DEBUGFS_ADD(start_recovery, rootdir);
        DEBUGFS_ADD(driver_state, rootdir);
+       DEBUGFS_ADD(vifs_state, rootdir);
        DEBUGFS_ADD(dtim_interval, rootdir);
        DEBUGFS_ADD(beacon_interval, rootdir);
        DEBUGFS_ADD(beacon_filtering, rootdir);
index 674ad2a9e40972be89d4188150917782f05da90e..00ce794eebaebb305cf706bfe385bd2c9846b06e 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "reg.h"
 #include "io.h"
 #include "event.h"
 
 void wl1271_pspoll_work(struct work_struct *work)
 {
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
        struct delayed_work *dwork;
        struct wl1271 *wl;
        int ret;
 
        dwork = container_of(work, struct delayed_work, work);
-       wl = container_of(dwork, struct wl1271, pspoll_work);
+       wlvif = container_of(dwork, struct wl12xx_vif, pspoll_work);
+       vif = container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
+       wl = wlvif->wl;
 
        wl1271_debug(DEBUG_EVENT, "pspoll work");
 
@@ -45,10 +50,10 @@ void wl1271_pspoll_work(struct work_struct *work)
        if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
-       if (!test_and_clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags))
+       if (!test_and_clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags))
                goto out;
 
-       if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
                goto out;
 
        /*
@@ -60,31 +65,33 @@ void wl1271_pspoll_work(struct work_struct *work)
        if (ret < 0)
                goto out;
 
-       wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true);
+       wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE,
+                          wlvif->basic_rate, true);
 
        wl1271_ps_elp_sleep(wl);
 out:
        mutex_unlock(&wl->mutex);
 };
 
-static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
+static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl,
+                                             struct wl12xx_vif *wlvif)
 {
        int delay = wl->conf.conn.ps_poll_recovery_period;
        int ret;
 
-       wl->ps_poll_failures++;
-       if (wl->ps_poll_failures == 1)
+       wlvif->ps_poll_failures++;
+       if (wlvif->ps_poll_failures == 1)
                wl1271_info("AP with dysfunctional ps-poll, "
                            "trying to work around it.");
 
        /* force active mode receive data from the AP */
-       if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
-               ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
-                                        wl->basic_rate, true);
+       if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
+               ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE,
+                                        wlvif->basic_rate, true);
                if (ret < 0)
                        return;
-               set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
-               ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work,
+               set_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
+               ieee80211_queue_delayed_work(wl->hw, &wlvif->pspoll_work,
                                             msecs_to_jiffies(delay));
        }
 
@@ -97,6 +104,7 @@ static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
 }
 
 static int wl1271_event_ps_report(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
                                  struct event_mailbox *mbox,
                                  bool *beacon_loss)
 {
@@ -109,41 +117,37 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
        case EVENT_ENTER_POWER_SAVE_FAIL:
                wl1271_debug(DEBUG_PSM, "PSM entry failed");
 
-               if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+               if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
                        /* remain in active mode */
-                       wl->psm_entry_retry = 0;
+                       wlvif->psm_entry_retry = 0;
                        break;
                }
 
-               if (wl->psm_entry_retry < total_retries) {
-                       wl->psm_entry_retry++;
-                       ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
-                                                wl->basic_rate, true);
+               if (wlvif->psm_entry_retry < total_retries) {
+                       wlvif->psm_entry_retry++;
+                       ret = wl1271_ps_set_mode(wl, wlvif,
+                                                STATION_POWER_SAVE_MODE,
+                                                wlvif->basic_rate, true);
                } else {
                        wl1271_info("No ack to nullfunc from AP.");
-                       wl->psm_entry_retry = 0;
+                       wlvif->psm_entry_retry = 0;
                        *beacon_loss = true;
                }
                break;
        case EVENT_ENTER_POWER_SAVE_SUCCESS:
-               wl->psm_entry_retry = 0;
-
-               /* enable beacon filtering */
-               ret = wl1271_acx_beacon_filter_opt(wl, true);
-               if (ret < 0)
-                       break;
+               wlvif->psm_entry_retry = 0;
 
                /*
                 * BET has only a minor effect in 5GHz and masks
                 * channel switch IEs, so we only enable BET on 2.4GHz
                */
-               if (wl->band == IEEE80211_BAND_2GHZ)
+               if (wlvif->band == IEEE80211_BAND_2GHZ)
                        /* enable beacon early termination */
-                       ret = wl1271_acx_bet_enable(wl, true);
+                       ret = wl1271_acx_bet_enable(wl, wlvif, true);
 
-               if (wl->ps_compl) {
-                       complete(wl->ps_compl);
-                       wl->ps_compl = NULL;
+               if (wlvif->ps_compl) {
+                       complete(wlvif->ps_compl);
+                       wlvif->ps_compl = NULL;
                }
                break;
        default:
@@ -154,39 +158,44 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
 }
 
 static void wl1271_event_rssi_trigger(struct wl1271 *wl,
+                                     struct wl12xx_vif *wlvif,
                                      struct event_mailbox *mbox)
 {
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        enum nl80211_cqm_rssi_threshold_event event;
        s8 metric = mbox->rssi_snr_trigger_metric[0];
 
        wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
 
-       if (metric <= wl->rssi_thold)
+       if (metric <= wlvif->rssi_thold)
                event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
        else
                event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
 
-       if (event != wl->last_rssi_event)
-               ieee80211_cqm_rssi_notify(wl->vif, event, GFP_KERNEL);
-       wl->last_rssi_event = event;
+       if (event != wlvif->last_rssi_event)
+               ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
+       wlvif->last_rssi_event = event;
 }
 
-static void wl1271_stop_ba_event(struct wl1271 *wl)
+static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
-       if (wl->bss_type != BSS_TYPE_AP_BSS) {
-               if (!wl->ba_rx_bitmap)
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+       if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
+               if (!wlvif->sta.ba_rx_bitmap)
                        return;
-               ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap,
-                                            wl->bssid);
+               ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap,
+                                            vif->bss_conf.bssid);
        } else {
-               int i;
+               u8 hlid;
                struct wl1271_link *lnk;
-               for (i = WL1271_AP_STA_HLID_START; i < AP_MAX_LINKS; i++) {
-                       lnk = &wl->links[i];
-                       if (!wl1271_is_active_sta(wl, i) || !lnk->ba_bitmap)
+               for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
+                                WL12XX_MAX_LINKS) {
+                       lnk = &wl->links[hlid];
+                       if (!lnk->ba_bitmap)
                                continue;
 
-                       ieee80211_stop_rx_ba_session(wl->vif,
+                       ieee80211_stop_rx_ba_session(vif,
                                                     lnk->ba_bitmap,
                                                     lnk->addr);
                }
@@ -196,14 +205,23 @@ static void wl1271_stop_ba_event(struct wl1271 *wl)
 static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
                                               u8 enable)
 {
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+
        if (enable) {
                /* disable dynamic PS when requested by the firmware */
-               ieee80211_disable_dyn_ps(wl->vif);
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       vif = wl12xx_wlvif_to_vif(wlvif);
+                       ieee80211_disable_dyn_ps(vif);
+               }
                set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
        } else {
-               ieee80211_enable_dyn_ps(wl->vif);
                clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
-               wl1271_recalc_rx_streaming(wl);
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       vif = wl12xx_wlvif_to_vif(wlvif);
+                       ieee80211_enable_dyn_ps(vif);
+                       wl1271_recalc_rx_streaming(wl, wlvif);
+               }
        }
 
 }
@@ -217,10 +235,11 @@ static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
 
 static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
 {
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
        int ret;
        u32 vector;
        bool beacon_loss = false;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
        bool disconnect_sta = false;
        unsigned long sta_bitmap = 0;
 
@@ -234,7 +253,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
                wl1271_debug(DEBUG_EVENT, "status: 0x%x",
                             mbox->scheduled_scan_status);
 
-               wl1271_scan_stm(wl);
+               wl1271_scan_stm(wl, wl->scan_vif);
        }
 
        if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
@@ -253,8 +272,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
                }
        }
 
-       if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
-           wl->bss_type == BSS_TYPE_STA_BSS)
+       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
                wl12xx_event_soft_gemini_sense(wl,
                                               mbox->soft_gemini_sense_info);
 
@@ -267,40 +285,54 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
         * BSS_LOSE_EVENT, beacon loss has to be reported to the stack.
         *
         */
-       if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) {
+       if (vector & BSS_LOSE_EVENT_ID) {
+               /* TODO: check for multi-role */
                wl1271_info("Beacon loss detected.");
 
                /* indicate to the stack, that beacons have been lost */
                beacon_loss = true;
        }
 
-       if ((vector & PS_REPORT_EVENT_ID) && !is_ap) {
+       if (vector & PS_REPORT_EVENT_ID) {
                wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
-               ret = wl1271_event_ps_report(wl, mbox, &beacon_loss);
-               if (ret < 0)
-                       return ret;
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       ret = wl1271_event_ps_report(wl, wlvif,
+                                                    mbox, &beacon_loss);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
-       if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap)
-               wl1271_event_pspoll_delivery_fail(wl);
+       if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       wl1271_event_pspoll_delivery_fail(wl, wlvif);
+               }
 
        if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
+               /* TODO: check actual multi-role support */
                wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
-               if (wl->vif)
-                       wl1271_event_rssi_trigger(wl, mbox);
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       wl1271_event_rssi_trigger(wl, wlvif, mbox);
+               }
        }
 
-       if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)) {
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) {
+               u8 role_id = mbox->role_id;
                wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
-                            "ba_allowed = 0x%x", mbox->rx_ba_allowed);
+                            "ba_allowed = 0x%x, role_id=%d",
+                            mbox->rx_ba_allowed, role_id);
 
-               wl->ba_allowed = !!mbox->rx_ba_allowed;
+               wl12xx_for_each_wlvif(wl, wlvif) {
+                       if (role_id != 0xff && role_id != wlvif->role_id)
+                               continue;
 
-               if (wl->vif && !wl->ba_allowed)
-                       wl1271_stop_ba_event(wl);
+                       wlvif->ba_allowed = !!mbox->rx_ba_allowed;
+                       if (!wlvif->ba_allowed)
+                               wl1271_stop_ba_event(wl, wlvif);
+               }
        }
 
-       if ((vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) && !is_ap) {
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
                wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
                                          "status = 0x%x",
                                          mbox->channel_switch_status);
@@ -309,50 +341,65 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
                 * 1) channel switch complete with status=0
                 * 2) channel switch failed status=1
                 */
-               if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags) &&
-                   (wl->vif))
-                       ieee80211_chswitch_done(wl->vif,
-                               mbox->channel_switch_status ? false : true);
+
+               /* TODO: configure only the relevant vif */
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+                       bool success;
+
+                       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
+                                               &wl->flags))
+                               continue;
+
+                       success = mbox->channel_switch_status ? false : true;
+                       ieee80211_chswitch_done(vif, success);
+               }
        }
 
        if ((vector & DUMMY_PACKET_EVENT_ID)) {
                wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
-               if (wl->vif)
-                       wl1271_tx_dummy_packet(wl);
+               wl1271_tx_dummy_packet(wl);
        }
 
        /*
         * "TX retries exceeded" has a different meaning according to mode.
         * In AP mode the offending station is disconnected.
         */
-       if ((vector & MAX_TX_RETRY_EVENT_ID) && is_ap) {
+       if (vector & MAX_TX_RETRY_EVENT_ID) {
                wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
                sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
                disconnect_sta = true;
        }
 
-       if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) {
+       if (vector & INACTIVE_STA_EVENT_ID) {
                wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
                sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
                disconnect_sta = true;
        }
 
-       if (is_ap && disconnect_sta) {
+       if (disconnect_sta) {
                u32 num_packets = wl->conf.tx.max_tx_retries;
                struct ieee80211_sta *sta;
                const u8 *addr;
                int h;
 
-               for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS);
-                    h < AP_MAX_LINKS;
-                    h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) {
-                       if (!wl1271_is_active_sta(wl, h))
+               for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+                       bool found = false;
+                       /* find the ap vif connected to this sta */
+                       wl12xx_for_each_wlvif_ap(wl, wlvif) {
+                               if (!test_bit(h, wlvif->ap.sta_hlid_map))
+                                       continue;
+                               found = true;
+                               break;
+                       }
+                       if (!found)
                                continue;
 
+                       vif = wl12xx_wlvif_to_vif(wlvif);
                        addr = wl->links[h].addr;
 
                        rcu_read_lock();
-                       sta = ieee80211_find_sta(wl->vif, addr);
+                       sta = ieee80211_find_sta(vif, addr);
                        if (sta) {
                                wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
                                ieee80211_report_low_ack(sta, num_packets);
@@ -361,8 +408,11 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
                }
        }
 
-       if (wl->vif && beacon_loss)
-               ieee80211_connection_loss(wl->vif);
+       if (beacon_loss)
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       vif = wl12xx_wlvif_to_vif(wlvif);
+                       ieee80211_connection_loss(vif);
+               }
 
        return 0;
 }
index 49c1a0ede5b13b4fa866e0958f6dd32cab396d25..1d878ba47bf47b1dc85e9af485963a610ff92e59 100644 (file)
@@ -132,7 +132,4 @@ void wl1271_event_mbox_config(struct wl1271 *wl);
 int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
 void wl1271_pspoll_work(struct work_struct *work);
 
-/* Functions from main.c */
-bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid);
-
 #endif
index 04db64c94e9a642fa21885ccdf276d8c3fcc9545..c413abd26d1cab5c00ebb4e46da00a1cfb85a460 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 
+#include "debug.h"
 #include "init.h"
 #include "wl12xx_80211.h"
 #include "acx.h"
@@ -33,7 +34,7 @@
 #include "tx.h"
 #include "io.h"
 
-int wl1271_sta_init_templates_config(struct wl1271 *wl)
+int wl1271_init_templates_config(struct wl1271 *wl)
 {
        int ret, i;
 
@@ -88,6 +89,29 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       /*
+        * Put very large empty placeholders for all templates. These
+        * reserve memory for later.
+        */
+       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
+                                     WL1271_CMD_TEMPL_MAX_SIZE,
+                                     0, WL1271_RATE_AUTOMATIC);
+       if (ret < 0)
+               return ret;
+
+       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL,
+                                     WL1271_CMD_TEMPL_MAX_SIZE,
+                                     0, WL1271_RATE_AUTOMATIC);
+       if (ret < 0)
+               return ret;
+
+       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL,
+                                     sizeof
+                                     (struct wl12xx_disconn_template),
+                                     0, WL1271_RATE_AUTOMATIC);
+       if (ret < 0)
+               return ret;
+
        for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
                ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL,
                                              WL1271_CMD_TEMPL_DFLT_SIZE, i,
@@ -99,7 +123,8 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl)
        return 0;
 }
 
-static int wl1271_ap_init_deauth_template(struct wl1271 *wl)
+static int wl1271_ap_init_deauth_template(struct wl1271 *wl,
+                                         struct wl12xx_vif *wlvif)
 {
        struct wl12xx_disconn_template *tmpl;
        int ret;
@@ -114,7 +139,7 @@ static int wl1271_ap_init_deauth_template(struct wl1271 *wl)
        tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                             IEEE80211_STYPE_DEAUTH);
 
-       rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+       rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP,
                                      tmpl, sizeof(*tmpl), 0, rate);
 
@@ -123,8 +148,10 @@ out:
        return ret;
 }
 
-static int wl1271_ap_init_null_template(struct wl1271 *wl)
+static int wl1271_ap_init_null_template(struct wl1271 *wl,
+                                       struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct ieee80211_hdr_3addr *nullfunc;
        int ret;
        u32 rate;
@@ -141,10 +168,10 @@ static int wl1271_ap_init_null_template(struct wl1271 *wl)
 
        /* nullfunc->addr1 is filled by FW */
 
-       memcpy(nullfunc->addr2, wl->mac_addr, ETH_ALEN);
-       memcpy(nullfunc->addr3, wl->mac_addr, ETH_ALEN);
+       memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
+       memcpy(nullfunc->addr3, vif->addr, ETH_ALEN);
 
-       rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+       rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc,
                                      sizeof(*nullfunc), 0, rate);
 
@@ -153,8 +180,10 @@ out:
        return ret;
 }
 
-static int wl1271_ap_init_qos_null_template(struct wl1271 *wl)
+static int wl1271_ap_init_qos_null_template(struct wl1271 *wl,
+                                           struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct ieee80211_qos_hdr *qosnull;
        int ret;
        u32 rate;
@@ -171,10 +200,10 @@ static int wl1271_ap_init_qos_null_template(struct wl1271 *wl)
 
        /* qosnull->addr1 is filled by FW */
 
-       memcpy(qosnull->addr2, wl->mac_addr, ETH_ALEN);
-       memcpy(qosnull->addr3, wl->mac_addr, ETH_ALEN);
+       memcpy(qosnull->addr2, vif->addr, ETH_ALEN);
+       memcpy(qosnull->addr3, vif->addr, ETH_ALEN);
 
-       rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+       rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull,
                                      sizeof(*qosnull), 0, rate);
 
@@ -183,93 +212,59 @@ out:
        return ret;
 }
 
-static int wl1271_ap_init_templates_config(struct wl1271 *wl)
+static int wl12xx_init_rx_config(struct wl1271 *wl)
 {
        int ret;
 
-       /*
-        * Put very large empty placeholders for all templates. These
-        * reserve memory for later.
-        */
-       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
-                                     WL1271_CMD_TEMPL_MAX_SIZE,
-                                     0, WL1271_RATE_AUTOMATIC);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL,
-                                     WL1271_CMD_TEMPL_MAX_SIZE,
-                                     0, WL1271_RATE_AUTOMATIC);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL,
-                                     sizeof
-                                     (struct wl12xx_disconn_template),
-                                     0, WL1271_RATE_AUTOMATIC);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
-                                     sizeof(struct wl12xx_null_data_template),
-                                     0, WL1271_RATE_AUTOMATIC);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
-                                     sizeof
-                                     (struct wl12xx_qos_null_data_template),
-                                     0, WL1271_RATE_AUTOMATIC);
+       ret = wl1271_acx_rx_msdu_life_time(wl);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int wl12xx_init_rx_config(struct wl1271 *wl)
+int wl1271_init_phy_config(struct wl1271 *wl)
 {
        int ret;
 
-       ret = wl1271_acx_rx_msdu_life_time(wl);
+       ret = wl1271_acx_pd_threshold(wl);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-int wl1271_init_phy_config(struct wl1271 *wl)
+static int wl12xx_init_phy_vif_config(struct wl1271 *wl,
+                                           struct wl12xx_vif *wlvif)
 {
        int ret;
 
-       ret = wl1271_acx_pd_threshold(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_acx_slot(wl, DEFAULT_SLOT_TIME);
+       ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME);
        if (ret < 0)
                return ret;
 
-       ret = wl1271_acx_service_period_timeout(wl);
+       ret = wl1271_acx_service_period_timeout(wl, wlvif);
        if (ret < 0)
                return ret;
 
-       ret = wl1271_acx_rts_threshold(wl, wl->hw->wiphy->rts_threshold);
+       ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int wl1271_init_beacon_filter(struct wl1271 *wl)
+static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif)
 {
        int ret;
 
-       /* disable beacon filtering at this stage */
-       ret = wl1271_acx_beacon_filter_opt(wl, false);
+       ret = wl1271_acx_beacon_filter_table(wl, wlvif);
        if (ret < 0)
                return ret;
 
-       ret = wl1271_acx_beacon_filter_table(wl);
+       /* enable beacon filtering */
+       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
        if (ret < 0)
                return ret;
 
@@ -302,11 +297,12 @@ int wl1271_init_energy_detection(struct wl1271 *wl)
        return 0;
 }
 
-static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
+static int wl1271_init_beacon_broadcast(struct wl1271 *wl,
+                                       struct wl12xx_vif *wlvif)
 {
        int ret;
 
-       ret = wl1271_acx_bcn_dtim_options(wl);
+       ret = wl1271_acx_bcn_dtim_options(wl, wlvif);
        if (ret < 0)
                return ret;
 
@@ -327,7 +323,8 @@ static int wl12xx_init_fwlog(struct wl1271 *wl)
        return 0;
 }
 
-static int wl1271_sta_hw_init(struct wl1271 *wl)
+/* generic sta initialization (non vif-specific) */
+static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
 
@@ -338,25 +335,7 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
        }
 
        /* PS config */
-       ret = wl1271_acx_config_ps(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_sta_init_templates_config(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
-       if (ret < 0)
-               return ret;
-
-       /* Initialize connection monitoring thresholds */
-       ret = wl1271_acx_conn_monit_params(wl, false);
-       if (ret < 0)
-               return ret;
-
-       /* Beacon filtering */
-       ret = wl1271_init_beacon_filter(wl);
+       ret = wl12xx_acx_config_ps(wl, wlvif);
        if (ret < 0)
                return ret;
 
@@ -365,103 +344,61 @@ static int wl1271_sta_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       /* Beacons and broadcast settings */
-       ret = wl1271_init_beacon_broadcast(wl);
-       if (ret < 0)
-               return ret;
-
-       /* Configure for ELP power saving */
-       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
-       if (ret < 0)
-               return ret;
-
-       /* Configure rssi/snr averaging weights */
-       ret = wl1271_acx_rssi_snr_avg_weights(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_acx_sta_rate_policies(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl12xx_acx_mem_cfg(wl);
-       if (ret < 0)
-               return ret;
-
-       /* Configure the FW logger */
-       ret = wl12xx_init_fwlog(wl);
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl)
+static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl,
+                                      struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret, i;
 
        /* disable all keep-alive templates */
        for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
-               ret = wl1271_acx_keep_alive_config(wl, i,
+               ret = wl1271_acx_keep_alive_config(wl, wlvif, i,
                                                   ACX_KEEP_ALIVE_TPL_INVALID);
                if (ret < 0)
                        return ret;
        }
 
        /* disable the keep-alive feature */
-       ret = wl1271_acx_keep_alive_mode(wl, false);
+       ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int wl1271_ap_hw_init(struct wl1271 *wl)
+/* generic ap initialization (non vif-specific) */
+static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
 
-       ret = wl1271_ap_init_templates_config(wl);
-       if (ret < 0)
-               return ret;
-
-       /* Configure for power always on */
-       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_init_ap_rates(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_acx_ap_max_tx_retry(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl12xx_acx_mem_cfg(wl);
-       if (ret < 0)
-               return ret;
-
-       /* initialize Tx power */
-       ret = wl1271_acx_tx_power(wl, wl->power_level);
+       ret = wl1271_init_ap_rates(wl, wlvif);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-int wl1271_ap_init_templates(struct wl1271 *wl)
+int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
 
-       ret = wl1271_ap_init_deauth_template(wl);
+       ret = wl1271_ap_init_deauth_template(wl, wlvif);
        if (ret < 0)
                return ret;
 
-       ret = wl1271_ap_init_null_template(wl);
+       ret = wl1271_ap_init_null_template(wl, vif);
        if (ret < 0)
                return ret;
 
-       ret = wl1271_ap_init_qos_null_template(wl);
+       ret = wl1271_ap_init_qos_null_template(wl, vif);
        if (ret < 0)
                return ret;
 
@@ -469,43 +406,45 @@ int wl1271_ap_init_templates(struct wl1271 *wl)
         * when operating as AP we want to receive external beacons for
         * configuring ERP protection.
         */
-       ret = wl1271_acx_beacon_filter_opt(wl, false);
+       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl)
+static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl,
+                                     struct ieee80211_vif *vif)
 {
-       return wl1271_ap_init_templates(wl);
+       return wl1271_ap_init_templates(wl, vif);
 }
 
-int wl1271_init_ap_rates(struct wl1271 *wl)
+int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int i, ret;
        struct conf_tx_rate_class rc;
        u32 supported_rates;
 
-       wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", wl->basic_rate_set);
+       wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x",
+                    wlvif->basic_rate_set);
 
-       if (wl->basic_rate_set == 0)
+       if (wlvif->basic_rate_set == 0)
                return -EINVAL;
 
-       rc.enabled_rates = wl->basic_rate_set;
+       rc.enabled_rates = wlvif->basic_rate_set;
        rc.long_retry_limit = 10;
        rc.short_retry_limit = 10;
        rc.aflags = 0;
-       ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_MGMT_RATE);
+       ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx);
        if (ret < 0)
                return ret;
 
        /* use the min basic rate for AP broadcast/multicast */
-       rc.enabled_rates = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+       rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
        rc.short_retry_limit = 10;
        rc.long_retry_limit = 10;
        rc.aflags = 0;
-       ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_BCST_RATE);
+       ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx);
        if (ret < 0)
                return ret;
 
@@ -513,7 +452,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl)
         * If the basic rates contain OFDM rates, use OFDM only
         * rates for unicast TX as well. Else use all supported rates.
         */
-       if ((wl->basic_rate_set & CONF_TX_OFDM_RATES))
+       if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
                supported_rates = CONF_TX_OFDM_RATES;
        else
                supported_rates = CONF_TX_AP_ENABLED_RATES;
@@ -527,7 +466,8 @@ int wl1271_init_ap_rates(struct wl1271 *wl)
                rc.short_retry_limit = 10;
                rc.long_retry_limit = 10;
                rc.aflags = 0;
-               ret = wl1271_acx_ap_rate_policy(wl, &rc, i);
+               ret = wl1271_acx_ap_rate_policy(wl, &rc,
+                                               wlvif->ap.ucast_rate_idx[i]);
                if (ret < 0)
                        return ret;
        }
@@ -535,24 +475,23 @@ int wl1271_init_ap_rates(struct wl1271 *wl)
        return 0;
 }
 
-static int wl1271_set_ba_policies(struct wl1271 *wl)
+static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        /* Reset the BA RX indicators */
-       wl->ba_rx_bitmap = 0;
-       wl->ba_allowed = true;
+       wlvif->ba_allowed = true;
        wl->ba_rx_session_count = 0;
 
        /* BA is supported in STA/AP modes */
-       if (wl->bss_type != BSS_TYPE_AP_BSS &&
-           wl->bss_type != BSS_TYPE_STA_BSS) {
-               wl->ba_support = false;
+       if (wlvif->bss_type != BSS_TYPE_AP_BSS &&
+           wlvif->bss_type != BSS_TYPE_STA_BSS) {
+               wlvif->ba_support = false;
                return 0;
        }
 
-       wl->ba_support = true;
+       wlvif->ba_support = true;
 
        /* 802.11n initiator BA session setting */
-       return wl12xx_acx_set_ba_initiator_policy(wl);
+       return wl12xx_acx_set_ba_initiator_policy(wl, wlvif);
 }
 
 int wl1271_chip_specific_init(struct wl1271 *wl)
@@ -562,7 +501,7 @@ int wl1271_chip_specific_init(struct wl1271 *wl)
        if (wl->chip.id == CHIP_ID_1283_PG20) {
                u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
 
-               if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT)
+               if (!(wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT))
                        /* Enable SDIO padding */
                        host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
 
@@ -575,13 +514,150 @@ out:
        return ret;
 }
 
+/* vif-specifc initialization */
+static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0);
+       if (ret < 0)
+               return ret;
 
-int wl1271_hw_init(struct wl1271 *wl)
+       /* Initialize connection monitoring thresholds */
+       ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
+       if (ret < 0)
+               return ret;
+
+       /* Beacon filtering */
+       ret = wl1271_init_sta_beacon_filter(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       /* Beacons and broadcast settings */
+       ret = wl1271_init_beacon_broadcast(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       /* Configure rssi/snr averaging weights */
+       ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* vif-specific intialization */
+static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       ret = wl1271_acx_ap_max_tx_retry(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       /* initialize Tx power */
+       ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct conf_tx_ac_category *conf_ac;
        struct conf_tx_tid *conf_tid;
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret, i;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+
+       /*
+        * consider all existing roles before configuring psm.
+        * TODO: reconfigure on interface removal.
+        */
+       if (!wl->ap_count) {
+               if (is_ap) {
+                       /* Configure for power always on */
+                       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+                       if (ret < 0)
+                               return ret;
+               } else if (!wl->sta_count) {
+                       /* Configure for ELP power saving */
+                       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       /* Mode specific init */
+       if (is_ap) {
+               ret = wl1271_ap_hw_init(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+
+               ret = wl12xx_init_ap_role(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       } else {
+               ret = wl1271_sta_hw_init(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+
+               ret = wl12xx_init_sta_role(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
+
+       wl12xx_init_phy_vif_config(wl, wlvif);
+
+       /* Default TID/AC configuration */
+       BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
+       for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
+               conf_ac = &wl->conf.tx.ac_conf[i];
+               ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac,
+                                       conf_ac->cw_min, conf_ac->cw_max,
+                                       conf_ac->aifsn, conf_ac->tx_op_limit);
+               if (ret < 0)
+                       return ret;
+
+               conf_tid = &wl->conf.tx.tid_conf[i];
+               ret = wl1271_acx_tid_cfg(wl, wlvif,
+                                        conf_tid->queue_id,
+                                        conf_tid->channel_type,
+                                        conf_tid->tsid,
+                                        conf_tid->ps_scheme,
+                                        conf_tid->ack_policy,
+                                        conf_tid->apsd_conf[0],
+                                        conf_tid->apsd_conf[1]);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Configure HW encryption */
+       ret = wl1271_acx_feature_cfg(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       /* Mode specific init - post mem init */
+       if (is_ap)
+               ret = wl1271_ap_hw_init_post_mem(wl, vif);
+       else
+               ret = wl1271_sta_hw_init_post_mem(wl, vif);
+
+       if (ret < 0)
+               return ret;
+
+       /* Configure initiator BA sessions policies */
+       ret = wl1271_set_ba_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+int wl1271_hw_init(struct wl1271 *wl)
+{
+       int ret;
 
        if (wl->chip.id == CHIP_ID_1283_PG20)
                ret = wl128x_cmd_general_parms(wl);
@@ -602,12 +678,17 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       /* Mode specific init */
-       if (is_ap)
-               ret = wl1271_ap_hw_init(wl);
-       else
-               ret = wl1271_sta_hw_init(wl);
+       /* Init templates */
+       ret = wl1271_init_templates_config(wl);
+       if (ret < 0)
+               return ret;
+
+       ret = wl12xx_acx_mem_cfg(wl);
+       if (ret < 0)
+               return ret;
 
+       /* Configure the FW logger */
+       ret = wl12xx_init_fwlog(wl);
        if (ret < 0)
                return ret;
 
@@ -655,61 +736,20 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                goto out_free_memmap;
 
-       /* Default TID/AC configuration */
-       BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
-       for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
-               conf_ac = &wl->conf.tx.ac_conf[i];
-               ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
-                                       conf_ac->cw_max, conf_ac->aifsn,
-                                       conf_ac->tx_op_limit);
-               if (ret < 0)
-                       goto out_free_memmap;
-
-               conf_tid = &wl->conf.tx.tid_conf[i];
-               ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
-                                        conf_tid->channel_type,
-                                        conf_tid->tsid,
-                                        conf_tid->ps_scheme,
-                                        conf_tid->ack_policy,
-                                        conf_tid->apsd_conf[0],
-                                        conf_tid->apsd_conf[1]);
-               if (ret < 0)
-                       goto out_free_memmap;
-       }
-
        /* Enable data path */
        ret = wl1271_cmd_data_path(wl, 1);
        if (ret < 0)
                goto out_free_memmap;
 
-       /* Configure HW encryption */
-       ret = wl1271_acx_feature_cfg(wl);
-       if (ret < 0)
-               goto out_free_memmap;
-
        /* configure PM */
        ret = wl1271_acx_pm_config(wl);
        if (ret < 0)
                goto out_free_memmap;
 
-       /* Mode specific init - post mem init */
-       if (is_ap)
-               ret = wl1271_ap_hw_init_post_mem(wl);
-       else
-               ret = wl1271_sta_hw_init_post_mem(wl);
-
-       if (ret < 0)
-               goto out_free_memmap;
-
        ret = wl12xx_acx_set_rate_mgmt_params(wl);
        if (ret < 0)
                goto out_free_memmap;
 
-       /* Configure initiator BA sessions policies */
-       ret = wl1271_set_ba_policies(wl);
-       if (ret < 0)
-               goto out_free_memmap;
-
        /* configure hangover */
        ret = wl12xx_acx_config_hangover(wl);
        if (ret < 0)
index 3a3c230fd2922eec9d0a83adad3030576e1f2454..81140b81f654757b3b6d00588760ff6bc3ba23ad 100644 (file)
 #include "wl12xx.h"
 
 int wl1271_hw_init_power_auth(struct wl1271 *wl);
-int wl1271_sta_init_templates_config(struct wl1271 *wl);
+int wl1271_init_templates_config(struct wl1271 *wl);
 int wl1271_init_phy_config(struct wl1271 *wl);
 int wl1271_init_pta(struct wl1271 *wl);
 int wl1271_init_energy_detection(struct wl1271 *wl);
 int wl1271_chip_specific_init(struct wl1271 *wl);
 int wl1271_hw_init(struct wl1271 *wl);
-int wl1271_init_ap_rates(struct wl1271 *wl);
-int wl1271_ap_init_templates(struct wl1271 *wl);
+int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif);
+int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif);
 
 #endif
index c2da66f45046774e183160bb45f6ef6bc773a5d3..079ad380e8ff7902ea2359d8e99a203e19941b81 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/interrupt.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "wl12xx_80211.h"
 #include "io.h"
 #include "tx.h"
@@ -46,7 +48,7 @@
 bool wl1271_set_block_size(struct wl1271 *wl)
 {
        if (wl->if_ops->set_block_size) {
-               wl->if_ops->set_block_size(wl, WL12XX_BUS_BLOCK_SIZE);
+               wl->if_ops->set_block_size(wl->dev, WL12XX_BUS_BLOCK_SIZE);
                return true;
        }
 
@@ -55,12 +57,12 @@ bool wl1271_set_block_size(struct wl1271 *wl)
 
 void wl1271_disable_interrupts(struct wl1271 *wl)
 {
-       wl->if_ops->disable_irq(wl);
+       disable_irq(wl->irq);
 }
 
 void wl1271_enable_interrupts(struct wl1271 *wl)
 {
-       wl->if_ops->enable_irq(wl);
+       enable_irq(wl->irq);
 }
 
 /* Set the SPI partitions to access the chip addresses
@@ -128,13 +130,13 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition);
 void wl1271_io_reset(struct wl1271 *wl)
 {
        if (wl->if_ops->reset)
-               wl->if_ops->reset(wl);
+               wl->if_ops->reset(wl->dev);
 }
 
 void wl1271_io_init(struct wl1271 *wl)
 {
        if (wl->if_ops->init)
-               wl->if_ops->init(wl);
+               wl->if_ops->init(wl->dev);
 }
 
 void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val)
index e839341dfafeac3faa0b0c0d7daf22171a149fd9..d398cbcea98677ae4b2c952b3077ed7d10c8920d 100644 (file)
@@ -51,23 +51,17 @@ void wl1271_enable_interrupts(struct wl1271 *wl);
 void wl1271_io_reset(struct wl1271 *wl);
 void wl1271_io_init(struct wl1271 *wl);
 
-static inline struct device *wl1271_wl_to_dev(struct wl1271 *wl)
-{
-       return wl->if_ops->dev(wl);
-}
-
-
 /* Raw target IO, address is not translated */
 static inline void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf,
                                    size_t len, bool fixed)
 {
-       wl->if_ops->write(wl, addr, buf, len, fixed);
+       wl->if_ops->write(wl->dev, addr, buf, len, fixed);
 }
 
 static inline void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf,
                                   size_t len, bool fixed)
 {
-       wl->if_ops->read(wl, addr, buf, len, fixed);
+       wl->if_ops->read(wl->dev, addr, buf, len, fixed);
 }
 
 static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr)
@@ -155,13 +149,13 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val)
 
 static inline void wl1271_power_off(struct wl1271 *wl)
 {
-       wl->if_ops->power(wl, false);
+       wl->if_ops->power(wl->dev, false);
        clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
 }
 
 static inline int wl1271_power_on(struct wl1271 *wl)
 {
-       int ret = wl->if_ops->power(wl, true);
+       int ret = wl->if_ops->power(wl->dev, true);
        if (ret == 0)
                set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
 
@@ -176,15 +170,10 @@ u16 wl1271_top_reg_read(struct wl1271 *wl, int addr);
 int wl1271_set_partition(struct wl1271 *wl,
                         struct wl1271_partition_set *p);
 
+bool wl1271_set_block_size(struct wl1271 *wl);
+
 /* Functions from wl1271_main.c */
 
-int wl1271_register_hw(struct wl1271 *wl);
-void wl1271_unregister_hw(struct wl1271 *wl);
-int wl1271_init_ieee80211(struct wl1271 *wl);
-struct ieee80211_hw *wl1271_alloc_hw(void);
-int wl1271_free_hw(struct wl1271 *wl);
-irqreturn_t wl1271_irq(int irq, void *data);
-bool wl1271_set_block_size(struct wl1271 *wl);
 int wl1271_tx_dummy_packet(struct wl1271 *wl);
 
 #endif
index 884f82b632190e6d480d15dcd14af33b04fc1d2a..dbb088eadf71f5089f7e02c24425ff1f8584fefb 100644 (file)
 #include <linux/slab.h>
 #include <linux/wl12xx.h>
 #include <linux/sched.h>
+#include <linux/interrupt.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "wl12xx_80211.h"
 #include "reg.h"
 #include "io.h"
@@ -377,42 +379,30 @@ static char *fwlog_param;
 static bool bug_on_recovery;
 
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
+                                        struct ieee80211_vif *vif,
                                         bool reset_tx_queues);
-static void wl1271_free_ap_keys(struct wl1271 *wl);
-
-
-static void wl1271_device_release(struct device *dev)
-{
-
-}
-
-static struct platform_device wl1271_device = {
-       .name           = "wl1271",
-       .id             = -1,
-
-       /* device model insists to have a release function */
-       .dev            = {
-               .release = wl1271_device_release,
-       },
-};
+static void wl1271_op_stop(struct ieee80211_hw *hw);
+static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 
 static DEFINE_MUTEX(wl_list_mutex);
 static LIST_HEAD(wl_list);
 
-static int wl1271_check_operstate(struct wl1271 *wl, unsigned char operstate)
+static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                 unsigned char operstate)
 {
        int ret;
+
        if (operstate != IF_OPER_UP)
                return 0;
 
-       if (test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags))
+       if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
                return 0;
 
-       ret = wl12xx_cmd_set_peer_state(wl, wl->sta_hlid);
+       ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
        if (ret < 0)
                return ret;
 
-       wl12xx_croc(wl, wl->role_id);
+       wl12xx_croc(wl, wlvif->role_id);
 
        wl1271_info("Association completed.");
        return 0;
@@ -426,6 +416,7 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
        struct ieee80211_hw *hw;
        struct wl1271 *wl;
        struct wl1271 *wl_temp;
+       struct wl12xx_vif *wlvif;
        int ret = 0;
 
        /* Check that this notification is for us. */
@@ -459,17 +450,18 @@ static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
        if (wl->state == WL1271_STATE_OFF)
                goto out;
 
-       if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
-               goto out;
-
-       ret = wl1271_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out;
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       continue;
 
-       wl1271_check_operstate(wl, dev->operstate);
+               ret = wl1271_ps_elp_wakeup(wl);
+               if (ret < 0)
+                       goto out;
 
-       wl1271_ps_elp_sleep(wl);
+               wl1271_check_operstate(wl, wlvif, dev->operstate);
 
+               wl1271_ps_elp_sleep(wl);
+       }
 out:
        mutex_unlock(&wl->mutex);
 
@@ -498,19 +490,20 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
        return 0;
 }
 
-static int wl1271_set_rx_streaming(struct wl1271 *wl, bool enable)
+static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                  bool enable)
 {
        int ret = 0;
 
        /* we should hold wl->mutex */
-       ret = wl1271_acx_ps_rx_streaming(wl, enable);
+       ret = wl1271_acx_ps_rx_streaming(wl, wlvif, enable);
        if (ret < 0)
                goto out;
 
        if (enable)
-               set_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
+               set_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
        else
-               clear_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags);
+               clear_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags);
 out:
        return ret;
 }
@@ -519,25 +512,25 @@ out:
  * this function is being called when the rx_streaming interval
  * has beed changed or rx_streaming should be disabled
  */
-int wl1271_recalc_rx_streaming(struct wl1271 *wl)
+int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret = 0;
        int period = wl->conf.rx_streaming.interval;
 
        /* don't reconfigure if rx_streaming is disabled */
-       if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
+       if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
                goto out;
 
        /* reconfigure/disable according to new streaming_period */
        if (period &&
-           test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) &&
+           test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
            (wl->conf.rx_streaming.always ||
             test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
-               ret = wl1271_set_rx_streaming(wl, true);
+               ret = wl1271_set_rx_streaming(wl, wlvif, true);
        else {
-               ret = wl1271_set_rx_streaming(wl, false);
+               ret = wl1271_set_rx_streaming(wl, wlvif, false);
                /* don't cancel_work_sync since we might deadlock */
-               del_timer_sync(&wl->rx_streaming_timer);
+               del_timer_sync(&wlvif->rx_streaming_timer);
        }
 out:
        return ret;
@@ -546,13 +539,14 @@ out:
 static void wl1271_rx_streaming_enable_work(struct work_struct *work)
 {
        int ret;
-       struct wl1271 *wl =
-               container_of(work, struct wl1271, rx_streaming_enable_work);
+       struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
+                                               rx_streaming_enable_work);
+       struct wl1271 *wl = wlvif->wl;
 
        mutex_lock(&wl->mutex);
 
-       if (test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags) ||
-           !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
+       if (test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags) ||
+           !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
            (!wl->conf.rx_streaming.always &&
             !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags)))
                goto out;
@@ -564,12 +558,12 @@ static void wl1271_rx_streaming_enable_work(struct work_struct *work)
        if (ret < 0)
                goto out;
 
-       ret = wl1271_set_rx_streaming(wl, true);
+       ret = wl1271_set_rx_streaming(wl, wlvif, true);
        if (ret < 0)
                goto out_sleep;
 
        /* stop it after some time of inactivity */
-       mod_timer(&wl->rx_streaming_timer,
+       mod_timer(&wlvif->rx_streaming_timer,
                  jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
 
 out_sleep:
@@ -581,19 +575,20 @@ out:
 static void wl1271_rx_streaming_disable_work(struct work_struct *work)
 {
        int ret;
-       struct wl1271 *wl =
-               container_of(work, struct wl1271, rx_streaming_disable_work);
+       struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif,
+                                               rx_streaming_disable_work);
+       struct wl1271 *wl = wlvif->wl;
 
        mutex_lock(&wl->mutex);
 
-       if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
+       if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
                goto out;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
 
-       ret = wl1271_set_rx_streaming(wl, false);
+       ret = wl1271_set_rx_streaming(wl, wlvif, false);
        if (ret)
                goto out_sleep;
 
@@ -605,8 +600,9 @@ out:
 
 static void wl1271_rx_streaming_timer(unsigned long data)
 {
-       struct wl1271 *wl = (struct wl1271 *)data;
-       ieee80211_queue_work(wl->hw, &wl->rx_streaming_disable_work);
+       struct wl12xx_vif *wlvif = (struct wl12xx_vif *)data;
+       struct wl1271 *wl = wlvif->wl;
+       ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
 }
 
 static void wl1271_conf_init(struct wl1271 *wl)
@@ -676,7 +672,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       ret = wl1271_sta_init_templates_config(wl);
+       ret = wl1271_init_templates_config(wl);
        if (ret < 0)
                return ret;
 
@@ -694,7 +690,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
                goto out_free_memmap;
 
        /* Initialize connection monitoring thresholds */
-       ret = wl1271_acx_conn_monit_params(wl, false);
+       ret = wl1271_acx_conn_monit_params(wl, NULL, false); /* TODO: fix */
        if (ret < 0)
                goto out_free_memmap;
 
@@ -726,14 +722,16 @@ static int wl1271_plt_init(struct wl1271 *wl)
        BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
        for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
                conf_ac = &wl->conf.tx.ac_conf[i];
-               ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min,
+               /* TODO: fix */
+               ret = wl1271_acx_ac_cfg(wl, NULL, conf_ac->ac, conf_ac->cw_min,
                                        conf_ac->cw_max, conf_ac->aifsn,
                                        conf_ac->tx_op_limit);
                if (ret < 0)
                        goto out_free_memmap;
 
                conf_tid = &wl->conf.tx.tid_conf[i];
-               ret = wl1271_acx_tid_cfg(wl, conf_tid->queue_id,
+               /* TODO: fix */
+               ret = wl1271_acx_tid_cfg(wl, NULL, conf_tid->queue_id,
                                         conf_tid->channel_type,
                                         conf_tid->tsid,
                                         conf_tid->ps_scheme,
@@ -768,14 +766,12 @@ static int wl1271_plt_init(struct wl1271 *wl)
        return ret;
 }
 
-static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
+static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
+                                       struct wl12xx_vif *wlvif,
+                                       u8 hlid, u8 tx_pkts)
 {
        bool fw_ps, single_sta;
 
-       /* only regulate station links */
-       if (hlid < WL1271_AP_STA_HLID_START)
-               return;
-
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        single_sta = (wl->active_sta_count == 1);
 
@@ -784,7 +780,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
         * packets in FW or if the STA is awake.
         */
        if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
-               wl1271_ps_link_end(wl, hlid);
+               wl12xx_ps_link_end(wl, wlvif, hlid);
 
        /*
         * Start high-level PS if the STA is asleep with enough blocks in FW.
@@ -792,24 +788,14 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
         * case FW-memory congestion is not a problem.
         */
        else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
-               wl1271_ps_link_start(wl, hlid, true);
-}
-
-bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid)
-{
-       int id;
-
-       /* global/broadcast "stations" are always active */
-       if (hlid < WL1271_AP_STA_HLID_START)
-               return true;
-
-       id = hlid - WL1271_AP_STA_HLID_START;
-       return test_bit(id, wl->ap_hlid_map);
+               wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
 static void wl12xx_irq_update_links_status(struct wl1271 *wl,
+                                          struct wl12xx_vif *wlvif,
                                           struct wl12xx_fw_status *status)
 {
+       struct wl1271_link *lnk;
        u32 cur_fw_ps_map;
        u8 hlid, cnt;
 
@@ -825,25 +811,22 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) {
-               if (!wl1271_is_active_sta(wl, hlid))
-                       continue;
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
+               lnk = &wl->links[hlid];
+               cnt = status->tx_lnk_free_pkts[hlid] - lnk->prev_freed_pkts;
 
-               cnt = status->tx_lnk_free_pkts[hlid] -
-                     wl->links[hlid].prev_freed_pkts;
+               lnk->prev_freed_pkts = status->tx_lnk_free_pkts[hlid];
+               lnk->allocated_pkts -= cnt;
 
-               wl->links[hlid].prev_freed_pkts =
-                       status->tx_lnk_free_pkts[hlid];
-               wl->links[hlid].allocated_pkts -= cnt;
-
-               wl12xx_irq_ps_regulate_link(wl, hlid,
-                                           wl->links[hlid].allocated_pkts);
+               wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
+                                           lnk->allocated_pkts);
        }
 }
 
 static void wl12xx_fw_status(struct wl1271 *wl,
                             struct wl12xx_fw_status *status)
 {
+       struct wl12xx_vif *wlvif;
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
        int avail, freed_blocks;
@@ -898,8 +881,9 @@ static void wl12xx_fw_status(struct wl1271 *wl,
                clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
 
        /* for AP update num of allocated TX blocks per link and ps status */
-       if (wl->bss_type == BSS_TYPE_AP_BSS)
-               wl12xx_irq_update_links_status(wl, status);
+       wl12xx_for_each_wlvif_ap(wl, wlvif) {
+               wl12xx_irq_update_links_status(wl, wlvif, status);
+       }
 
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
@@ -932,7 +916,7 @@ static void wl1271_netstack_work(struct work_struct *work)
 
 #define WL1271_IRQ_MAX_LOOPS 256
 
-irqreturn_t wl1271_irq(int irq, void *cookie)
+static irqreturn_t wl1271_irq(int irq, void *cookie)
 {
        int ret;
        u32 intr;
@@ -1054,7 +1038,6 @@ out:
 
        return IRQ_HANDLED;
 }
-EXPORT_SYMBOL_GPL(wl1271_irq);
 
 static int wl1271_fetch_firmware(struct wl1271 *wl)
 {
@@ -1069,7 +1052,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
 
-       ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));
+       ret = request_firmware(&fw, fw_name, wl->dev);
 
        if (ret < 0) {
                wl1271_error("could not get firmware: %d", ret);
@@ -1107,7 +1090,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
        const struct firmware *fw;
        int ret;
 
-       ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
+       ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
 
        if (ret < 0) {
                wl1271_error("could not get nvs file: %d", ret);
@@ -1217,11 +1200,13 @@ static void wl1271_recovery_work(struct work_struct *work)
 {
        struct wl1271 *wl =
                container_of(work, struct wl1271, recovery_work);
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
 
        mutex_lock(&wl->mutex);
 
        if (wl->state != WL1271_STATE_ON)
-               goto out;
+               goto out_unlock;
 
        /* Avoid a recursive recovery */
        set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
@@ -1238,9 +1223,12 @@ static void wl1271_recovery_work(struct work_struct *work)
         * in the firmware during recovery. This doens't hurt if the network is
         * not encrypted.
         */
-       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
-           test_bit(WL1271_FLAG_AP_STARTED, &wl->flags))
-               wl->tx_security_seq += WL1271_TX_SQN_POST_RECOVERY_PADDING;
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
+                   test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
+                       wlvif->tx_security_seq +=
+                               WL1271_TX_SQN_POST_RECOVERY_PADDING;
+       }
 
        /* Prevent spurious TX during FW restart */
        ieee80211_stop_queues(wl->hw);
@@ -1251,7 +1239,14 @@ static void wl1271_recovery_work(struct work_struct *work)
        }
 
        /* reboot the chipset */
-       __wl1271_op_remove_interface(wl, false);
+       while (!list_empty(&wl->wlvif_list)) {
+               wlvif = list_first_entry(&wl->wlvif_list,
+                                      struct wl12xx_vif, list);
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               __wl1271_op_remove_interface(wl, vif, false);
+       }
+       mutex_unlock(&wl->mutex);
+       wl1271_op_stop(wl->hw);
 
        clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
 
@@ -1262,8 +1257,8 @@ static void wl1271_recovery_work(struct work_struct *work)
         * to restart the HW.
         */
        ieee80211_wake_queues(wl->hw);
-
-out:
+       return;
+out_unlock:
        mutex_unlock(&wl->mutex);
 }
 
@@ -1318,7 +1313,16 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
        /* 0. read chip id from CHIP_ID */
        wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
 
-       /* 1. check if chip id is valid */
+       /*
+        * For wl127x based devices we could use the default block
+        * size (512 bytes), but due to a bug in the sdio driver, we
+        * need to set it explicitly after the chip is powered on.  To
+        * simplify the code and since the performance impact is
+        * negligible, we use the same block size for all different
+        * chip types.
+        */
+       if (!wl1271_set_block_size(wl))
+               wl->quirks |= WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT;
 
        switch (wl->chip.id) {
        case CHIP_ID_1271_PG10:
@@ -1328,7 +1332,9 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                ret = wl1271_setup(wl);
                if (ret < 0)
                        goto out;
+               wl->quirks |= WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT;
                break;
+
        case CHIP_ID_1271_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
@@ -1336,7 +1342,9 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                ret = wl1271_setup(wl);
                if (ret < 0)
                        goto out;
+               wl->quirks |= WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT;
                break;
+
        case CHIP_ID_1283_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
                             wl->chip.id);
@@ -1344,9 +1352,6 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                ret = wl1271_setup(wl);
                if (ret < 0)
                        goto out;
-
-               if (wl1271_set_block_size(wl))
-                       wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
                break;
        case CHIP_ID_1283_PG10:
        default:
@@ -1389,8 +1394,6 @@ int wl1271_plt_start(struct wl1271 *wl)
                goto out;
        }
 
-       wl->bss_type = BSS_TYPE_STA_BSS;
-
        while (retries) {
                retries--;
                ret = wl1271_chip_wakeup(wl);
@@ -1482,33 +1485,34 @@ int wl1271_plt_stop(struct wl1271 *wl)
 static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1271 *wl = hw->priv;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_vif *vif = info->control.vif;
+       struct wl12xx_vif *wlvif = NULL;
        unsigned long flags;
        int q, mapping;
-       u8 hlid = 0;
+       u8 hlid;
+
+       if (vif)
+               wlvif = wl12xx_vif_to_data(vif);
 
        mapping = skb_get_queue_mapping(skb);
        q = wl1271_tx_get_queue(mapping);
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS)
-               hlid = wl12xx_tx_get_hlid_ap(wl, skb);
+       hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
 
        spin_lock_irqsave(&wl->wl_lock, flags);
 
        /* queue the packet */
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
-               if (!wl1271_is_active_sta(wl, hlid)) {
-                       wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d",
-                                    hlid, q);
-                       dev_kfree_skb(skb);
-                       goto out;
-               }
-
-               wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
-               skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
-       } else {
-               skb_queue_tail(&wl->tx_queue[q], skb);
+       if (hlid == WL12XX_INVALID_LINK_ID ||
+           (wlvif && !test_bit(hlid, wlvif->links_map))) {
+               wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
+               dev_kfree_skb(skb);
+               goto out;
        }
 
+       wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
+       skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
+
        wl->tx_queue_count[q]++;
 
        /*
@@ -1609,13 +1613,14 @@ static struct notifier_block wl1271_dev_notifier = {
 };
 
 #ifdef CONFIG_PM
-static int wl1271_configure_suspend_sta(struct wl1271 *wl)
+static int wl1271_configure_suspend_sta(struct wl1271 *wl,
+                                       struct wl12xx_vif *wlvif)
 {
        int ret = 0;
 
        mutex_lock(&wl->mutex);
 
-       if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
                goto out_unlock;
 
        ret = wl1271_ps_elp_wakeup(wl);
@@ -1623,12 +1628,12 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl)
                goto out_unlock;
 
        /* enter psm if needed*/
-       if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+       if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
                DECLARE_COMPLETION_ONSTACK(compl);
 
-               wl->ps_compl = &compl;
-               ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
-                                  wl->basic_rate, true);
+               wlvif->ps_compl = &compl;
+               ret = wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE,
+                                  wlvif->basic_rate, true);
                if (ret < 0)
                        goto out_sleep;
 
@@ -1660,20 +1665,21 @@ out:
 
 }
 
-static int wl1271_configure_suspend_ap(struct wl1271 *wl)
+static int wl1271_configure_suspend_ap(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif)
 {
        int ret = 0;
 
        mutex_lock(&wl->mutex);
 
-       if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags))
+       if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
                goto out_unlock;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out_unlock;
 
-       ret = wl1271_acx_beacon_filter_opt(wl, true);
+       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
 
        wl1271_ps_elp_sleep(wl);
 out_unlock:
@@ -1682,20 +1688,22 @@ out_unlock:
 
 }
 
-static int wl1271_configure_suspend(struct wl1271 *wl)
+static int wl1271_configure_suspend(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif)
 {
-       if (wl->bss_type == BSS_TYPE_STA_BSS)
-               return wl1271_configure_suspend_sta(wl);
-       if (wl->bss_type == BSS_TYPE_AP_BSS)
-               return wl1271_configure_suspend_ap(wl);
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+               return wl1271_configure_suspend_sta(wl, wlvif);
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               return wl1271_configure_suspend_ap(wl, wlvif);
        return 0;
 }
 
-static void wl1271_configure_resume(struct wl1271 *wl)
+static void wl1271_configure_resume(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif)
 {
        int ret;
-       bool is_sta = wl->bss_type == BSS_TYPE_STA_BSS;
-       bool is_ap = wl->bss_type == BSS_TYPE_AP_BSS;
+       bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+       bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
 
        if (!is_sta && !is_ap)
                return;
@@ -1707,11 +1715,11 @@ static void wl1271_configure_resume(struct wl1271 *wl)
 
        if (is_sta) {
                /* exit psm if it wasn't configured */
-               if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags))
-                       wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
-                                          wl->basic_rate, true);
+               if (!test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags))
+                       wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE,
+                                          wlvif->basic_rate, true);
        } else if (is_ap) {
-               wl1271_acx_beacon_filter_opt(wl, false);
+               wl1271_acx_beacon_filter_opt(wl, wlvif, false);
        }
 
        wl1271_ps_elp_sleep(wl);
@@ -1723,16 +1731,19 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
                            struct cfg80211_wowlan *wow)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
        WARN_ON(!wow || !wow->any);
 
        wl->wow_enabled = true;
-       ret = wl1271_configure_suspend(wl);
-       if (ret < 0) {
-               wl1271_warning("couldn't prepare device to suspend");
-               return ret;
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               ret = wl1271_configure_suspend(wl, wlvif);
+               if (ret < 0) {
+                       wl1271_warning("couldn't prepare device to suspend");
+                       return ret;
+               }
        }
        /* flush any remaining work */
        wl1271_debug(DEBUG_MAC80211, "flushing remaining works");
@@ -1751,7 +1762,9 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
 
        wl1271_enable_interrupts(wl);
        flush_work(&wl->tx_work);
-       flush_delayed_work(&wl->pspoll_work);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               flush_delayed_work(&wlvif->pspoll_work);
+       }
        flush_delayed_work(&wl->elp_work);
 
        return 0;
@@ -1760,6 +1773,7 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
 static int wl1271_op_resume(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
        unsigned long flags;
        bool run_irq_work = false;
 
@@ -1783,7 +1797,9 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
                wl1271_irq(0, wl);
                wl1271_enable_interrupts(wl);
        }
-       wl1271_configure_resume(wl);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               wl1271_configure_resume(wl, wlvif);
+       }
        wl->wow_enabled = false;
 
        return 0;
@@ -1810,20 +1826,119 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
 
 static void wl1271_op_stop(struct ieee80211_hw *hw)
 {
+       struct wl1271 *wl = hw->priv;
+       int i;
+
        wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
+
+       mutex_lock(&wl->mutex);
+       if (wl->state == WL1271_STATE_OFF) {
+               mutex_unlock(&wl->mutex);
+               return;
+       }
+       /*
+        * this must be before the cancel_work calls below, so that the work
+        * functions don't perform further work.
+        */
+       wl->state = WL1271_STATE_OFF;
+       mutex_unlock(&wl->mutex);
+
+       mutex_lock(&wl_list_mutex);
+       list_del(&wl->list);
+       mutex_unlock(&wl_list_mutex);
+
+       wl1271_disable_interrupts(wl);
+       wl1271_flush_deferred_work(wl);
+       cancel_delayed_work_sync(&wl->scan_complete_work);
+       cancel_work_sync(&wl->netstack_work);
+       cancel_work_sync(&wl->tx_work);
+       cancel_delayed_work_sync(&wl->elp_work);
+
+       /* let's notify MAC80211 about the remaining pending TX frames */
+       wl12xx_tx_reset(wl, true);
+       mutex_lock(&wl->mutex);
+
+       wl1271_power_off(wl);
+
+       wl->band = IEEE80211_BAND_2GHZ;
+
+       wl->rx_counter = 0;
+       wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
+       wl->tx_blocks_available = 0;
+       wl->tx_allocated_blocks = 0;
+       wl->tx_results_count = 0;
+       wl->tx_packets_count = 0;
+       wl->time_offset = 0;
+       wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
+       wl->ap_fw_ps_map = 0;
+       wl->ap_ps_map = 0;
+       wl->sched_scanning = false;
+       memset(wl->roles_map, 0, sizeof(wl->roles_map));
+       memset(wl->links_map, 0, sizeof(wl->links_map));
+       memset(wl->roc_map, 0, sizeof(wl->roc_map));
+       wl->active_sta_count = 0;
+
+       /* The system link is always allocated */
+       __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
+
+       /*
+        * this is performed after the cancel_work calls and the associated
+        * mutex_lock, so that wl1271_op_add_interface does not accidentally
+        * get executed before all these vars have been reset.
+        */
+       wl->flags = 0;
+
+       wl->tx_blocks_freed = 0;
+
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               wl->tx_pkts_freed[i] = 0;
+               wl->tx_allocated_pkts[i] = 0;
+       }
+
+       wl1271_debugfs_reset(wl);
+
+       kfree(wl->fw_status);
+       wl->fw_status = NULL;
+       kfree(wl->tx_res_if);
+       wl->tx_res_if = NULL;
+       kfree(wl->target_mem_map);
+       wl->target_mem_map = NULL;
+
+       mutex_unlock(&wl->mutex);
+}
+
+static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
+{
+       u8 policy = find_first_zero_bit(wl->rate_policies_map,
+                                       WL12XX_MAX_RATE_POLICIES);
+       if (policy >= WL12XX_MAX_RATE_POLICIES)
+               return -EBUSY;
+
+       __set_bit(policy, wl->rate_policies_map);
+       *idx = policy;
+       return 0;
 }
 
-static u8 wl12xx_get_role_type(struct wl1271 *wl)
+static void wl12xx_free_rate_policy(struct wl1271 *wl, u8 *idx)
 {
-       switch (wl->bss_type) {
+       if (WARN_ON(*idx >= WL12XX_MAX_RATE_POLICIES))
+               return;
+
+       __clear_bit(*idx, wl->rate_policies_map);
+       *idx = WL12XX_MAX_RATE_POLICIES;
+}
+
+static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       switch (wlvif->bss_type) {
        case BSS_TYPE_AP_BSS:
-               if (wl->p2p)
+               if (wlvif->p2p)
                        return WL1271_ROLE_P2P_GO;
                else
                        return WL1271_ROLE_AP;
 
        case BSS_TYPE_STA_BSS:
-               if (wl->p2p)
+               if (wlvif->p2p)
                        return WL1271_ROLE_P2P_CL;
                else
                        return WL1271_ROLE_STA;
@@ -1832,78 +1947,95 @@ static u8 wl12xx_get_role_type(struct wl1271 *wl)
                return WL1271_ROLE_IBSS;
 
        default:
-               wl1271_error("invalid bss_type: %d", wl->bss_type);
+               wl1271_error("invalid bss_type: %d", wlvif->bss_type);
        }
        return WL12XX_INVALID_ROLE_TYPE;
 }
 
-static int wl1271_op_add_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif)
+static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
 {
-       struct wl1271 *wl = hw->priv;
-       struct wiphy *wiphy = hw->wiphy;
-       int retries = WL1271_BOOT_RETRIES;
-       int ret = 0;
-       u8 role_type;
-       bool booted = false;
-
-       wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
-                    ieee80211_vif_type_p2p(vif), vif->addr);
-
-       mutex_lock(&wl->mutex);
-       if (wl->vif) {
-               wl1271_debug(DEBUG_MAC80211,
-                            "multiple vifs are not supported yet");
-               ret = -EBUSY;
-               goto out;
-       }
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int i;
 
-       /*
-        * in some very corner case HW recovery scenarios its possible to
-        * get here before __wl1271_op_remove_interface is complete, so
-        * opt out if that is the case.
-        */
-       if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) {
-               ret = -EBUSY;
-               goto out;
-       }
+       /* clear everything but the persistent data */
+       memset(wlvif, 0, offsetof(struct wl12xx_vif, persistent));
 
        switch (ieee80211_vif_type_p2p(vif)) {
        case NL80211_IFTYPE_P2P_CLIENT:
-               wl->p2p = 1;
+               wlvif->p2p = 1;
                /* fall-through */
        case NL80211_IFTYPE_STATION:
-               wl->bss_type = BSS_TYPE_STA_BSS;
-               wl->set_bss_type = BSS_TYPE_STA_BSS;
+               wlvif->bss_type = BSS_TYPE_STA_BSS;
                break;
        case NL80211_IFTYPE_ADHOC:
-               wl->bss_type = BSS_TYPE_IBSS;
-               wl->set_bss_type = BSS_TYPE_STA_BSS;
+               wlvif->bss_type = BSS_TYPE_IBSS;
                break;
        case NL80211_IFTYPE_P2P_GO:
-               wl->p2p = 1;
+               wlvif->p2p = 1;
                /* fall-through */
        case NL80211_IFTYPE_AP:
-               wl->bss_type = BSS_TYPE_AP_BSS;
+               wlvif->bss_type = BSS_TYPE_AP_BSS;
                break;
        default:
-               ret = -EOPNOTSUPP;
-               goto out;
+               wlvif->bss_type = MAX_BSS_TYPE;
+               return -EOPNOTSUPP;
        }
 
-       role_type = wl12xx_get_role_type(wl);
-       if (role_type == WL12XX_INVALID_ROLE_TYPE) {
-               ret = -EINVAL;
-               goto out;
+       wlvif->role_id = WL12XX_INVALID_ROLE_ID;
+       wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
+       wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+           wlvif->bss_type == BSS_TYPE_IBSS) {
+               /* init sta/ibss data */
+               wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
+               wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
+               wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
+               wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
+       } else {
+               /* init ap data */
+               wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
+               wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
+               wl12xx_allocate_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
+               wl12xx_allocate_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
+               for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
+                       wl12xx_allocate_rate_policy(wl,
+                                               &wlvif->ap.ucast_rate_idx[i]);
        }
-       memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 
-       if (wl->state != WL1271_STATE_OFF) {
-               wl1271_error("cannot start because not in off state: %d",
-                            wl->state);
-               ret = -EBUSY;
-               goto out;
-       }
+       wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
+       wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
+       wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
+       wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
+       wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
+       wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
+
+       /*
+        * mac80211 configures some values globally, while we treat them
+        * per-interface. thus, on init, we have to copy them from wl
+        */
+       wlvif->band = wl->band;
+       wlvif->channel = wl->channel;
+       wlvif->power_level = wl->power_level;
+
+       INIT_WORK(&wlvif->rx_streaming_enable_work,
+                 wl1271_rx_streaming_enable_work);
+       INIT_WORK(&wlvif->rx_streaming_disable_work,
+                 wl1271_rx_streaming_disable_work);
+       INIT_DELAYED_WORK(&wlvif->pspoll_work, wl1271_pspoll_work);
+       INIT_LIST_HEAD(&wlvif->list);
+
+       setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
+                   (unsigned long) wlvif);
+       return 0;
+}
+
+static bool wl12xx_init_fw(struct wl1271 *wl)
+{
+       int retries = WL1271_BOOT_RETRIES;
+       bool booted = false;
+       struct wiphy *wiphy = wl->hw->wiphy;
+       int ret;
 
        while (retries) {
                retries--;
@@ -1915,25 +2047,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                if (ret < 0)
                        goto power_off;
 
-               if (wl->bss_type == BSS_TYPE_STA_BSS ||
-                   wl->bss_type == BSS_TYPE_IBSS) {
-                       /*
-                        * The device role is a special role used for
-                        * rx and tx frames prior to association (as
-                        * the STA role can get packets only from
-                        * its associated bssid)
-                        */
-                       ret = wl12xx_cmd_role_enable(wl,
-                                                        WL1271_ROLE_DEVICE,
-                                                        &wl->dev_role_id);
-                       if (ret < 0)
-                               goto irq_disable;
-               }
-
-               ret = wl12xx_cmd_role_enable(wl, role_type, &wl->role_id);
-               if (ret < 0)
-                       goto irq_disable;
-
                ret = wl1271_hw_init(wl);
                if (ret < 0)
                        goto irq_disable;
@@ -1964,9 +2077,6 @@ power_off:
                goto out;
        }
 
-       wl->vif = vif;
-       wl->state = WL1271_STATE_ON;
-       set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags);
        wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
 
        /* update hw/fw version info in wiphy struct */
@@ -1984,7 +2094,110 @@ power_off:
        wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
                     wl->enable_11a ? "" : "not ");
 
+       wl->state = WL1271_STATE_ON;
+out:
+       return booted;
+}
+
+static int wl1271_op_add_interface(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret = 0;
+       u8 role_type;
+       bool booted = false;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
+                    ieee80211_vif_type_p2p(vif), vif->addr);
+
+       mutex_lock(&wl->mutex);
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
+       if (wl->vif) {
+               wl1271_debug(DEBUG_MAC80211,
+                            "multiple vifs are not supported yet");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       /*
+        * in some very corner case HW recovery scenarios its possible to
+        * get here before __wl1271_op_remove_interface is complete, so
+        * opt out if that is the case.
+        */
+       if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags) ||
+           test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl12xx_init_vif_data(wl, vif);
+       if (ret < 0)
+               goto out;
+
+       wlvif->wl = wl;
+       role_type = wl12xx_get_role_type(wl, wlvif);
+       if (role_type == WL12XX_INVALID_ROLE_TYPE) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * TODO: after the nvs issue will be solved, move this block
+        * to start(), and make sure here the driver is ON.
+        */
+       if (wl->state == WL1271_STATE_OFF) {
+               /*
+                * we still need this in order to configure the fw
+                * while uploading the nvs
+                */
+               memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
+
+               booted = wl12xx_init_fw(wl);
+               if (!booted) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+           wlvif->bss_type == BSS_TYPE_IBSS) {
+               /*
+                * The device role is a special role used for
+                * rx and tx frames prior to association (as
+                * the STA role can get packets only from
+                * its associated bssid)
+                */
+               ret = wl12xx_cmd_role_enable(wl, vif->addr,
+                                                WL1271_ROLE_DEVICE,
+                                                &wlvif->dev_role_id);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = wl12xx_cmd_role_enable(wl, vif->addr,
+                                    role_type, &wlvif->role_id);
+       if (ret < 0)
+               goto out;
+
+       ret = wl1271_init_vif_specific(wl, vif);
+       if (ret < 0)
+               goto out;
+
+       wl->vif = vif;
+       list_add(&wlvif->list, &wl->wlvif_list);
+       set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
+
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               wl->ap_count++;
+       else
+               wl->sta_count++;
 out:
+       wl1271_ps_elp_sleep(wl);
+out_unlock:
        mutex_unlock(&wl->mutex);
 
        mutex_lock(&wl_list_mutex);
@@ -1996,29 +2209,34 @@ out:
 }
 
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
+                                        struct ieee80211_vif *vif,
                                         bool reset_tx_queues)
 {
-       int ret, i;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int i, ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
 
+       if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+               return;
+
+       wl->vif = NULL;
+
        /* because of hardware recovery, we may get here twice */
        if (wl->state != WL1271_STATE_ON)
                return;
 
        wl1271_info("down");
 
-       mutex_lock(&wl_list_mutex);
-       list_del(&wl->list);
-       mutex_unlock(&wl_list_mutex);
-
        /* enable dyn ps just in case (if left on due to fw crash etc) */
-       if (wl->bss_type == BSS_TYPE_STA_BSS)
-               ieee80211_enable_dyn_ps(wl->vif);
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+               ieee80211_enable_dyn_ps(vif);
 
-       if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
+       if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
+           wl->scan_vif == vif) {
                wl->scan.state = WL1271_SCAN_STATE_IDLE;
                memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+               wl->scan_vif = NULL;
                wl->scan.req = NULL;
                ieee80211_scan_completed(wl->hw, true);
        }
@@ -2029,13 +2247,13 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                if (ret < 0)
                        goto deinit;
 
-               if (wl->bss_type == BSS_TYPE_STA_BSS) {
-                       ret = wl12xx_cmd_role_disable(wl, &wl->dev_role_id);
+               if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
+                       ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id);
                        if (ret < 0)
                                goto deinit;
                }
 
-               ret = wl12xx_cmd_role_disable(wl, &wl->role_id);
+               ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
                if (ret < 0)
                        goto deinit;
 
@@ -2043,120 +2261,82 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        }
 deinit:
        /* clear all hlids (except system_hlid) */
-       wl->sta_hlid = WL12XX_INVALID_LINK_ID;
-       wl->dev_hlid = WL12XX_INVALID_LINK_ID;
-       wl->ap_bcast_hlid = WL12XX_INVALID_LINK_ID;
-       wl->ap_global_hlid = WL12XX_INVALID_LINK_ID;
+       wlvif->dev_hlid = WL12XX_INVALID_LINK_ID;
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
+           wlvif->bss_type == BSS_TYPE_IBSS) {
+               wlvif->sta.hlid = WL12XX_INVALID_LINK_ID;
+               wl12xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx);
+               wl12xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx);
+               wl12xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
+       } else {
+               wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
+               wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
+               wl12xx_free_rate_policy(wl, &wlvif->ap.mgmt_rate_idx);
+               wl12xx_free_rate_policy(wl, &wlvif->ap.bcast_rate_idx);
+               for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
+                       wl12xx_free_rate_policy(wl,
+                                               &wlvif->ap.ucast_rate_idx[i]);
+       }
 
-       /*
-        * this must be before the cancel_work calls below, so that the work
-        * functions don't perform further work.
-        */
-       wl->state = WL1271_STATE_OFF;
+       wl12xx_tx_reset_wlvif(wl, wlvif);
+       wl1271_free_ap_keys(wl, wlvif);
+       if (wl->last_wlvif == wlvif)
+               wl->last_wlvif = NULL;
+       list_del(&wlvif->list);
+       memset(wlvif->ap.sta_hlid_map, 0, sizeof(wlvif->ap.sta_hlid_map));
+       wlvif->role_id = WL12XX_INVALID_ROLE_ID;
+       wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID;
+
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               wl->ap_count--;
+       else
+               wl->sta_count--;
 
        mutex_unlock(&wl->mutex);
-
-       wl1271_disable_interrupts(wl);
-       wl1271_flush_deferred_work(wl);
-       cancel_delayed_work_sync(&wl->scan_complete_work);
-       cancel_work_sync(&wl->netstack_work);
-       cancel_work_sync(&wl->tx_work);
-       del_timer_sync(&wl->rx_streaming_timer);
-       cancel_work_sync(&wl->rx_streaming_enable_work);
-       cancel_work_sync(&wl->rx_streaming_disable_work);
-       cancel_delayed_work_sync(&wl->pspoll_work);
-       cancel_delayed_work_sync(&wl->elp_work);
+       del_timer_sync(&wlvif->rx_streaming_timer);
+       cancel_work_sync(&wlvif->rx_streaming_enable_work);
+       cancel_work_sync(&wlvif->rx_streaming_disable_work);
+       cancel_delayed_work_sync(&wlvif->pspoll_work);
 
        mutex_lock(&wl->mutex);
-
-       /* let's notify MAC80211 about the remaining pending TX frames */
-       wl1271_tx_reset(wl, reset_tx_queues);
-       wl1271_power_off(wl);
-
-       memset(wl->bssid, 0, ETH_ALEN);
-       memset(wl->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
-       wl->ssid_len = 0;
-       wl->bss_type = MAX_BSS_TYPE;
-       wl->set_bss_type = MAX_BSS_TYPE;
-       wl->p2p = 0;
-       wl->band = IEEE80211_BAND_2GHZ;
-
-       wl->rx_counter = 0;
-       wl->psm_entry_retry = 0;
-       wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
-       wl->tx_blocks_available = 0;
-       wl->tx_allocated_blocks = 0;
-       wl->tx_results_count = 0;
-       wl->tx_packets_count = 0;
-       wl->time_offset = 0;
-       wl->session_counter = 0;
-       wl->rate_set = CONF_TX_RATE_MASK_BASIC;
-       wl->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
-       wl->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
-       wl->vif = NULL;
-       wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
-       wl1271_free_ap_keys(wl);
-       memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
-       wl->ap_fw_ps_map = 0;
-       wl->ap_ps_map = 0;
-       wl->sched_scanning = false;
-       wl->role_id = WL12XX_INVALID_ROLE_ID;
-       wl->dev_role_id = WL12XX_INVALID_ROLE_ID;
-       memset(wl->roles_map, 0, sizeof(wl->roles_map));
-       memset(wl->links_map, 0, sizeof(wl->links_map));
-       memset(wl->roc_map, 0, sizeof(wl->roc_map));
-       wl->active_sta_count = 0;
-
-       /* The system link is always allocated */
-       __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
-
-       /*
-        * this is performed after the cancel_work calls and the associated
-        * mutex_lock, so that wl1271_op_add_interface does not accidentally
-        * get executed before all these vars have been reset.
-        */
-       wl->flags = 0;
-
-       wl->tx_blocks_freed = 0;
-
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               wl->tx_pkts_freed[i] = 0;
-               wl->tx_allocated_pkts[i] = 0;
-       }
-
-       wl1271_debugfs_reset(wl);
-
-       kfree(wl->fw_status);
-       wl->fw_status = NULL;
-       kfree(wl->tx_res_if);
-       wl->tx_res_if = NULL;
-       kfree(wl->target_mem_map);
-       wl->target_mem_map = NULL;
 }
 
 static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl12xx_vif *iter;
 
        mutex_lock(&wl->mutex);
+
+       if (wl->state == WL1271_STATE_OFF ||
+           !test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+               goto out;
+
        /*
         * wl->vif can be null here if someone shuts down the interface
         * just when hardware recovery has been started.
         */
-       if (wl->vif) {
-               WARN_ON(wl->vif != vif);
-               __wl1271_op_remove_interface(wl, true);
-       }
+       wl12xx_for_each_wlvif(wl, iter) {
+               if (iter != wlvif)
+                       continue;
 
+               __wl1271_op_remove_interface(wl, vif, true);
+               break;
+       }
+       WARN_ON(iter != wlvif);
+out:
        mutex_unlock(&wl->mutex);
        cancel_work_sync(&wl->recovery_work);
 }
 
-static int wl1271_join(struct wl1271 *wl, bool set_assoc)
+static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         bool set_assoc)
 {
        int ret;
-       bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
+       bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
 
        /*
         * One of the side effects of the JOIN command is that is clears
@@ -2167,20 +2347,20 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc)
         * Keep the below message for now, unless it starts bothering
         * users who really like to roam a lot :)
         */
-       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+       if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
                wl1271_info("JOIN while associated.");
 
        if (set_assoc)
-               set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
+               set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
 
        if (is_ibss)
-               ret = wl12xx_cmd_role_start_ibss(wl);
+               ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
        else
-               ret = wl12xx_cmd_role_start_sta(wl);
+               ret = wl12xx_cmd_role_start_sta(wl, wlvif);
        if (ret < 0)
                goto out;
 
-       if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
                goto out;
 
        /*
@@ -2189,19 +2369,20 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc)
         * the join. The acx_aid starts the keep-alive process, and the order
         * of the commands below is relevant.
         */
-       ret = wl1271_acx_keep_alive_mode(wl, true);
+       ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
        if (ret < 0)
                goto out;
 
-       ret = wl1271_acx_aid(wl, wl->aid);
+       ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
        if (ret < 0)
                goto out;
 
-       ret = wl1271_cmd_build_klv_null_data(wl);
+       ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
        if (ret < 0)
                goto out;
 
-       ret = wl1271_acx_keep_alive_config(wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
+       ret = wl1271_acx_keep_alive_config(wl, wlvif,
+                                          CMD_TEMPL_KLV_IDX_NULL_DATA,
                                           ACX_KEEP_ALIVE_TPL_VALID);
        if (ret < 0)
                goto out;
@@ -2210,34 +2391,34 @@ out:
        return ret;
 }
 
-static int wl1271_unjoin(struct wl1271 *wl)
+static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
 
-       if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags)) {
+       if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
+               struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
                wl12xx_cmd_stop_channel_switch(wl);
-               ieee80211_chswitch_done(wl->vif, false);
+               ieee80211_chswitch_done(vif, false);
        }
 
        /* to stop listening to a channel, we disconnect */
-       ret = wl12xx_cmd_role_stop_sta(wl);
+       ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
        if (ret < 0)
                goto out;
 
-       memset(wl->bssid, 0, ETH_ALEN);
-
        /* reset TX security counters on a clean disconnect */
-       wl->tx_security_last_seq_lsb = 0;
-       wl->tx_security_seq = 0;
+       wlvif->tx_security_last_seq_lsb = 0;
+       wlvif->tx_security_seq = 0;
 
 out:
        return ret;
 }
 
-static void wl1271_set_band_rate(struct wl1271 *wl)
+static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
-       wl->basic_rate_set = wl->bitrate_masks[wl->band];
-       wl->rate_set = wl->basic_rate_set;
+       wlvif->basic_rate_set = wlvif->bitrate_masks[wlvif->band];
+       wlvif->rate_set = wlvif->basic_rate_set;
 }
 
 static bool wl12xx_is_roc(struct wl1271 *wl)
@@ -2251,107 +2432,62 @@ static bool wl12xx_is_roc(struct wl1271 *wl)
        return true;
 }
 
-static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
+static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                 bool idle)
 {
        int ret;
 
-       if (idle) {
-               /* no need to croc if we weren't busy (e.g. during boot) */
-               if (wl12xx_is_roc(wl)) {
-                       ret = wl12xx_croc(wl, wl->dev_role_id);
-                       if (ret < 0)
-                               goto out;
-
-                       ret = wl12xx_cmd_role_stop_dev(wl);
-                       if (ret < 0)
-                               goto out;
-               }
-               wl->rate_set = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
-               ret = wl1271_acx_sta_rate_policies(wl);
-               if (ret < 0)
-                       goto out;
-               ret = wl1271_acx_keep_alive_config(
-                       wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
-                       ACX_KEEP_ALIVE_TPL_INVALID);
-               if (ret < 0)
-                       goto out;
-               set_bit(WL1271_FLAG_IDLE, &wl->flags);
-       } else {
-               /* The current firmware only supports sched_scan in idle */
-               if (wl->sched_scanning) {
-                       wl1271_scan_sched_scan_stop(wl);
-                       ieee80211_sched_scan_stopped(wl->hw);
-               }
-
-               ret = wl12xx_cmd_role_start_dev(wl);
-               if (ret < 0)
-                       goto out;
-
-               ret = wl12xx_roc(wl, wl->dev_role_id);
-               if (ret < 0)
-                       goto out;
-               clear_bit(WL1271_FLAG_IDLE, &wl->flags);
-       }
-
-out:
-       return ret;
-}
-
-static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
-{
-       struct wl1271 *wl = hw->priv;
-       struct ieee80211_conf *conf = &hw->conf;
-       int channel, ret = 0;
-       bool is_ap;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
-       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
-                    " changed 0x%x",
-                    channel,
-                    conf->flags & IEEE80211_CONF_PS ? "on" : "off",
-                    conf->power_level,
-                    conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
-                        changed);
-
-       /*
-        * mac80211 will go to idle nearly immediately after transmitting some
-        * frames, such as the deauth. To make sure those frames reach the air,
-        * wait here until the TX queue is fully flushed.
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
-           (conf->flags & IEEE80211_CONF_IDLE))
-               wl1271_tx_flush(wl);
-
-       mutex_lock(&wl->mutex);
-
-       if (unlikely(wl->state == WL1271_STATE_OFF)) {
-               /* we support configuring the channel and band while off */
-               if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
-                       wl->band = conf->channel->band;
-                       wl->channel = channel;
-               }
-
-               if ((changed & IEEE80211_CONF_CHANGE_POWER))
-                       wl->power_level = conf->power_level;
+       if (idle) {
+               /* no need to croc if we weren't busy (e.g. during boot) */
+               if (wl12xx_is_roc(wl)) {
+                       ret = wl12xx_stop_dev(wl, wlvif);
+                       if (ret < 0)
+                               goto out;
+               }
+               wlvif->rate_set =
+                       wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+               if (ret < 0)
+                       goto out;
+               ret = wl1271_acx_keep_alive_config(
+                       wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA,
+                       ACX_KEEP_ALIVE_TPL_INVALID);
+               if (ret < 0)
+                       goto out;
+               set_bit(WL1271_FLAG_IDLE, &wl->flags);
+       } else {
+               /* The current firmware only supports sched_scan in idle */
+               if (wl->sched_scanning) {
+                       wl1271_scan_sched_scan_stop(wl);
+                       ieee80211_sched_scan_stopped(wl->hw);
+               }
 
-               goto out;
+               ret = wl12xx_start_dev(wl, wlvif);
+               if (ret < 0)
+                       goto out;
+               clear_bit(WL1271_FLAG_IDLE, &wl->flags);
        }
 
-       is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+out:
+       return ret;
+}
+
+static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            struct ieee80211_conf *conf, u32 changed)
+{
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
+       int channel, ret;
 
-       ret = wl1271_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out;
+       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
 
        /* if the channel changes while joined, join again */
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
-           ((wl->band != conf->channel->band) ||
-            (wl->channel != channel))) {
+           ((wlvif->band != conf->channel->band) ||
+            (wlvif->channel != channel))) {
                /* send all pending packets */
                wl1271_tx_work_locked(wl);
-               wl->band = conf->channel->band;
-               wl->channel = channel;
+               wlvif->band = conf->channel->band;
+               wlvif->channel = channel;
 
                if (!is_ap) {
                        /*
@@ -2360,24 +2496,27 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
                         * possible rate for the band as a fixed rate for
                         * association frames and other control messages.
                         */
-                       if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
-                               wl1271_set_band_rate(wl);
+                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                               wl1271_set_band_rate(wl, wlvif);
 
-                       wl->basic_rate =
-                               wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl);
+                       wlvif->basic_rate =
+                               wl1271_tx_min_rate_get(wl,
+                                                      wlvif->basic_rate_set);
+                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
                        if (ret < 0)
                                wl1271_warning("rate policy for channel "
                                               "failed %d", ret);
 
-                       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+                       if (test_bit(WLVIF_FLAG_STA_ASSOCIATED,
+                                    &wlvif->flags)) {
                                if (wl12xx_is_roc(wl)) {
                                        /* roaming */
-                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       ret = wl12xx_croc(wl,
+                                                         wlvif->dev_role_id);
                                        if (ret < 0)
-                                               goto out_sleep;
+                                               return ret;
                                }
-                               ret = wl1271_join(wl, false);
+                               ret = wl1271_join(wl, wlvif, false);
                                if (ret < 0)
                                        wl1271_warning("cmd join on channel "
                                                       "failed %d", ret);
@@ -2389,64 +2528,112 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
                                 */
                                if (wl12xx_is_roc(wl) &&
                                    !(conf->flags & IEEE80211_CONF_IDLE)) {
-                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       ret = wl12xx_stop_dev(wl, wlvif);
                                        if (ret < 0)
-                                               goto out_sleep;
+                                               return ret;
 
-                                       ret = wl12xx_roc(wl, wl->dev_role_id);
+                                       ret = wl12xx_start_dev(wl, wlvif);
                                        if (ret < 0)
-                                               wl1271_warning("roc failed %d",
-                                                              ret);
+                                               return ret;
                                }
                        }
                }
        }
 
-       if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
-               ret = wl1271_sta_handle_idle(wl,
-                                       conf->flags & IEEE80211_CONF_IDLE);
-               if (ret < 0)
-                       wl1271_warning("idle mode change failed %d", ret);
-       }
-
        /*
         * if mac80211 changes the PSM mode, make sure the mode is not
         * incorrectly changed after the pspoll failure active window.
         */
        if (changed & IEEE80211_CONF_CHANGE_PS)
-               clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
+               clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
 
        if (conf->flags & IEEE80211_CONF_PS &&
-           !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
-               set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
+           !test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
+               set_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->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.
                 */
-               if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
                        wl1271_debug(DEBUG_PSM, "psm enabled");
-                       ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
-                                                wl->basic_rate, true);
+                       ret = wl1271_ps_set_mode(wl, wlvif,
+                                                STATION_POWER_SAVE_MODE,
+                                                wlvif->basic_rate, true);
                }
        } else if (!(conf->flags & IEEE80211_CONF_PS) &&
-                  test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
+                  test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
                wl1271_debug(DEBUG_PSM, "psm disabled");
 
-               clear_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
+               clear_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
 
-               if (test_bit(WL1271_FLAG_PSM, &wl->flags))
-                       ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
-                                                wl->basic_rate, true);
+               if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags))
+                       ret = wl1271_ps_set_mode(wl, wlvif,
+                                                STATION_ACTIVE_MODE,
+                                                wlvif->basic_rate, true);
        }
 
-       if (conf->power_level != wl->power_level) {
-               ret = wl1271_acx_tx_power(wl, conf->power_level);
+       if (conf->power_level != wlvif->power_level) {
+               ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
                if (ret < 0)
-                       goto out_sleep;
+                       return ret;
+
+               wlvif->power_level = conf->power_level;
+       }
+
+       return 0;
+}
+
+static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_conf *conf = &hw->conf;
+       int channel, ret = 0;
+
+       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
+                    " changed 0x%x",
+                    channel,
+                    conf->flags & IEEE80211_CONF_PS ? "on" : "off",
+                    conf->power_level,
+                    conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
+                        changed);
+
+       /*
+        * mac80211 will go to idle nearly immediately after transmitting some
+        * frames, such as the deauth. To make sure those frames reach the air,
+        * wait here until the TX queue is fully flushed.
+        */
+       if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
+           (conf->flags & IEEE80211_CONF_IDLE))
+               wl1271_tx_flush(wl);
+
+       mutex_lock(&wl->mutex);
 
+       /* we support configuring the channel and band even while off */
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               wl->band = conf->channel->band;
+               wl->channel = channel;
+       }
+
+       if (changed & IEEE80211_CONF_CHANGE_POWER)
                wl->power_level = conf->power_level;
+
+       if (unlikely(wl->state == WL1271_STATE_OFF))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* configure each interface */
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               ret = wl12xx_config_vif(wl, wlvif, conf, changed);
+               if (ret < 0)
+                       goto out_sleep;
        }
 
 out_sleep:
@@ -2509,6 +2696,8 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
 {
        struct wl1271_filter_params *fp = (void *)(unsigned long)multicast;
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
+
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
@@ -2526,15 +2715,20 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       if (wl->bss_type != BSS_TYPE_AP_BSS) {
-               if (*total & FIF_ALLMULTI)
-                       ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
-               else if (fp)
-                       ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
-                                                          fp->mc_list,
-                                                          fp->mc_list_length);
-               if (ret < 0)
-                       goto out_sleep;
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
+                       if (*total & FIF_ALLMULTI)
+                               ret = wl1271_acx_group_address_tbl(wl, wlvif,
+                                                                  false,
+                                                                  NULL, 0);
+                       else if (fp)
+                               ret = wl1271_acx_group_address_tbl(wl, wlvif,
+                                                       fp->enabled,
+                                                       fp->mc_list,
+                                                       fp->mc_list_length);
+                       if (ret < 0)
+                               goto out_sleep;
+               }
        }
 
        /*
@@ -2551,9 +2745,10 @@ out:
        kfree(fp);
 }
 
-static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
-                       u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
-                       u16 tx_seq_16)
+static int wl1271_record_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                               u8 id, u8 key_type, u8 key_size,
+                               const u8 *key, u8 hlid, u32 tx_seq_32,
+                               u16 tx_seq_16)
 {
        struct wl1271_ap_key *ap_key;
        int i;
@@ -2568,10 +2763,10 @@ static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
         * an existing key.
         */
        for (i = 0; i < MAX_NUM_KEYS; i++) {
-               if (wl->recorded_ap_keys[i] == NULL)
+               if (wlvif->ap.recorded_keys[i] == NULL)
                        break;
 
-               if (wl->recorded_ap_keys[i]->id == id) {
+               if (wlvif->ap.recorded_keys[i]->id == id) {
                        wl1271_warning("trying to record key replacement");
                        return -EINVAL;
                }
@@ -2592,21 +2787,21 @@ static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
        ap_key->tx_seq_32 = tx_seq_32;
        ap_key->tx_seq_16 = tx_seq_16;
 
-       wl->recorded_ap_keys[i] = ap_key;
+       wlvif->ap.recorded_keys[i] = ap_key;
        return 0;
 }
 
-static void wl1271_free_ap_keys(struct wl1271 *wl)
+static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int i;
 
        for (i = 0; i < MAX_NUM_KEYS; i++) {
-               kfree(wl->recorded_ap_keys[i]);
-               wl->recorded_ap_keys[i] = NULL;
+               kfree(wlvif->ap.recorded_keys[i]);
+               wlvif->ap.recorded_keys[i] = NULL;
        }
 }
 
-static int wl1271_ap_init_hwenc(struct wl1271 *wl)
+static int wl1271_ap_init_hwenc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int i, ret = 0;
        struct wl1271_ap_key *key;
@@ -2614,15 +2809,15 @@ static int wl1271_ap_init_hwenc(struct wl1271 *wl)
 
        for (i = 0; i < MAX_NUM_KEYS; i++) {
                u8 hlid;
-               if (wl->recorded_ap_keys[i] == NULL)
+               if (wlvif->ap.recorded_keys[i] == NULL)
                        break;
 
-               key = wl->recorded_ap_keys[i];
+               key = wlvif->ap.recorded_keys[i];
                hlid = key->hlid;
                if (hlid == WL12XX_INVALID_LINK_ID)
-                       hlid = wl->ap_bcast_hlid;
+                       hlid = wlvif->ap.bcast_hlid;
 
-               ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
+               ret = wl1271_cmd_set_ap_key(wl, wlvif, KEY_ADD_OR_REPLACE,
                                            key->id, key->key_type,
                                            key->key_size, key->key,
                                            hlid, key->tx_seq_32,
@@ -2635,23 +2830,24 @@ static int wl1271_ap_init_hwenc(struct wl1271 *wl)
        }
 
        if (wep_key_added) {
-               ret = wl12xx_cmd_set_default_wep_key(wl, wl->default_key,
-                                                    wl->ap_bcast_hlid);
+               ret = wl12xx_cmd_set_default_wep_key(wl, wlvif->default_key,
+                                                    wlvif->ap.bcast_hlid);
                if (ret < 0)
                        goto out;
        }
 
 out:
-       wl1271_free_ap_keys(wl);
+       wl1271_free_ap_keys(wl, wlvif);
        return ret;
 }
 
-static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      u16 action, u8 id, u8 key_type,
                       u8 key_size, const u8 *key, u32 tx_seq_32,
                       u16 tx_seq_16, struct ieee80211_sta *sta)
 {
        int ret;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
 
        if (is_ap) {
                struct wl1271_station *wl_sta;
@@ -2661,10 +2857,10 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                        wl_sta = (struct wl1271_station *)sta->drv_priv;
                        hlid = wl_sta->hlid;
                } else {
-                       hlid = wl->ap_bcast_hlid;
+                       hlid = wlvif->ap.bcast_hlid;
                }
 
-               if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
+               if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
                        /*
                         * We do not support removing keys after AP shutdown.
                         * Pretend we do to make mac80211 happy.
@@ -2672,12 +2868,12 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                        if (action != KEY_ADD_OR_REPLACE)
                                return 0;
 
-                       ret = wl1271_record_ap_key(wl, id,
+                       ret = wl1271_record_ap_key(wl, wlvif, id,
                                             key_type, key_size,
                                             key, hlid, tx_seq_32,
                                             tx_seq_16);
                } else {
-                       ret = wl1271_cmd_set_ap_key(wl, action,
+                       ret = wl1271_cmd_set_ap_key(wl, wlvif, action,
                                             id, key_type, key_size,
                                             key, hlid, tx_seq_32,
                                             tx_seq_16);
@@ -2718,10 +2914,10 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
 
                /* don't remove key if hlid was already deleted */
                if (action == KEY_REMOVE &&
-                   wl->sta_hlid == WL12XX_INVALID_LINK_ID)
+                   wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)
                        return 0;
 
-               ret = wl1271_cmd_set_sta_key(wl, action,
+               ret = wl1271_cmd_set_sta_key(wl, wlvif, action,
                                             id, key_type, key_size,
                                             key, addr, tx_seq_32,
                                             tx_seq_16);
@@ -2731,8 +2927,8 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                /* the default WEP key needs to be configured at least once */
                if (key_type == KEY_WEP) {
                        ret = wl12xx_cmd_set_default_wep_key(wl,
-                                                            wl->default_key,
-                                                            wl->sta_hlid);
+                                                       wlvif->default_key,
+                                                       wlvif->sta.hlid);
                        if (ret < 0)
                                return ret;
                }
@@ -2747,6 +2943,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                             struct ieee80211_key_conf *key_conf)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
        u32 tx_seq_32 = 0;
        u16 tx_seq_16 = 0;
@@ -2782,20 +2979,20 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                key_type = KEY_TKIP;
 
                key_conf->hw_key_idx = key_conf->keyidx;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
+               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
+               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
                key_type = KEY_AES;
 
                key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
+               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
+               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        case WL1271_CIPHER_SUITE_GEM:
                key_type = KEY_GEM;
-               tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq);
-               tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq);
+               tx_seq_32 = WL1271_TX_SECURITY_HI32(wlvif->tx_security_seq);
+               tx_seq_16 = WL1271_TX_SECURITY_LO16(wlvif->tx_security_seq);
                break;
        default:
                wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
@@ -2806,7 +3003,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        switch (cmd) {
        case SET_KEY:
-               ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
+               ret = wl1271_set_key(wl, wlvif, KEY_ADD_OR_REPLACE,
                                 key_conf->keyidx, key_type,
                                 key_conf->keylen, key_conf->key,
                                 tx_seq_32, tx_seq_16, sta);
@@ -2817,7 +3014,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
 
        case DISABLE_KEY:
-               ret = wl1271_set_key(wl, KEY_REMOVE,
+               ret = wl1271_set_key(wl, wlvif, KEY_REMOVE,
                                     key_conf->keyidx, key_type,
                                     key_conf->keylen, key_conf->key,
                                     0, 0, sta);
@@ -2847,6 +3044,8 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct cfg80211_scan_request *req)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
        int ret;
        u8 *ssid = NULL;
        size_t len = 0;
@@ -2876,16 +3075,15 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
 
        /* cancel ROC before scanning */
        if (wl12xx_is_roc(wl)) {
-               if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+               if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
                        /* don't allow scanning right now */
                        ret = -EBUSY;
                        goto out_sleep;
                }
-               wl12xx_croc(wl, wl->dev_role_id);
-               wl12xx_cmd_role_stop_dev(wl);
+               wl12xx_stop_dev(wl, wlvif);
        }
 
-       ret = wl1271_scan(hw->priv, ssid, len, req);
+       ret = wl1271_scan(hw->priv, vif, ssid, len, req);
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 out:
@@ -2921,6 +3119,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
        }
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+       wl->scan_vif = NULL;
        wl->scan.req = NULL;
        ieee80211_scan_completed(wl->hw, true);
 
@@ -2938,6 +3137,7 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
                                      struct ieee80211_sched_scan_ies *ies)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start");
@@ -2948,11 +3148,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_scan_sched_scan_config(wl, req, ies);
+       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
        if (ret < 0)
                goto out_sleep;
 
-       ret = wl1271_scan_sched_scan_start(wl);
+       ret = wl1271_scan_sched_scan_start(wl, wlvif);
        if (ret < 0)
                goto out_sleep;
 
@@ -3017,6 +3217,7 @@ out:
 static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
        int ret = 0;
 
        mutex_lock(&wl->mutex);
@@ -3030,10 +3231,11 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        if (ret < 0)
                goto out;
 
-       ret = wl1271_acx_rts_threshold(wl, value);
-       if (ret < 0)
-               wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
-
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               ret = wl1271_acx_rts_threshold(wl, wlvif, value);
+               if (ret < 0)
+                       wl1271_warning("set rts threshold failed: %d", ret);
+       }
        wl1271_ps_elp_sleep(wl);
 
 out:
@@ -3042,9 +3244,10 @@ out:
        return ret;
 }
 
-static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
+static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
                            int offset)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        u8 ssid_len;
        const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
                                         skb->len - offset);
@@ -3060,8 +3263,8 @@ static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
                return -EINVAL;
        }
 
-       wl->ssid_len = ssid_len;
-       memcpy(wl->ssid, ptr+2, ssid_len);
+       wlvif->ssid_len = ssid_len;
+       memcpy(wlvif->ssid, ptr+2, ssid_len);
        return 0;
 }
 
@@ -3097,17 +3300,19 @@ static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
 }
 
 static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl,
+                                        struct ieee80211_vif *vif,
                                         u8 *probe_rsp_data,
                                         size_t probe_rsp_len,
                                         u32 rates)
 {
-       struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
        u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
        int ssid_ie_offset, ie_offset, templ_len;
        const u8 *ptr;
 
        /* no need to change probe response if the SSID is set correctly */
-       if (wl->ssid_len > 0)
+       if (wlvif->ssid_len > 0)
                return wl1271_cmd_template_set(wl,
                                               CMD_TEMPL_AP_PROBE_RESPONSE,
                                               probe_rsp_data,
@@ -3153,16 +3358,18 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl,
 }
 
 static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
+                                      struct ieee80211_vif *vif,
                                       struct ieee80211_bss_conf *bss_conf,
                                       u32 changed)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret = 0;
 
        if (changed & BSS_CHANGED_ERP_SLOT) {
                if (bss_conf->use_short_slot)
-                       ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
+                       ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_SHORT);
                else
-                       ret = wl1271_acx_slot(wl, SLOT_TIME_LONG);
+                       ret = wl1271_acx_slot(wl, wlvif, SLOT_TIME_LONG);
                if (ret < 0) {
                        wl1271_warning("Set slot time failed %d", ret);
                        goto out;
@@ -3171,16 +3378,18 @@ static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
 
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
                if (bss_conf->use_short_preamble)
-                       wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT);
+                       wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_SHORT);
                else
-                       wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG);
+                       wl1271_acx_set_preamble(wl, wlvif, ACX_PREAMBLE_LONG);
        }
 
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
                if (bss_conf->use_cts_prot)
-                       ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE);
+                       ret = wl1271_acx_cts_protect(wl, wlvif,
+                                                    CTSPROTECT_ENABLE);
                else
-                       ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE);
+                       ret = wl1271_acx_cts_protect(wl, wlvif,
+                                                    CTSPROTECT_DISABLE);
                if (ret < 0) {
                        wl1271_warning("Set ctsprotect failed %d", ret);
                        goto out;
@@ -3196,14 +3405,15 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                                          struct ieee80211_bss_conf *bss_conf,
                                          u32 changed)
 {
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret = 0;
 
        if ((changed & BSS_CHANGED_BEACON_INT)) {
                wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
                        bss_conf->beacon_int);
 
-               wl->beacon_int = bss_conf->beacon_int;
+               wlvif->beacon_int = bss_conf->beacon_int;
        }
 
        if ((changed & BSS_CHANGED_BEACON)) {
@@ -3219,12 +3429,12 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
 
                wl1271_debug(DEBUG_MASTER, "beacon updated");
 
-               ret = wl1271_ssid_set(wl, beacon, ieoffset);
+               ret = wl1271_ssid_set(vif, beacon, ieoffset);
                if (ret < 0) {
                        dev_kfree_skb(beacon);
                        goto out;
                }
-               min_rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+               min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
                tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
                                  CMD_TEMPL_BEACON;
                ret = wl1271_cmd_template_set(wl, tmpl_id,
@@ -3254,7 +3464,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                 IEEE80211_STYPE_PROBE_RESP);
                if (is_ap)
-                       ret = wl1271_ap_set_probe_resp_tmpl(wl,
+                       ret = wl1271_ap_set_probe_resp_tmpl(wl, vif,
                                                beacon->data,
                                                beacon->len,
                                                min_rate);
@@ -3279,23 +3489,24 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                                       struct ieee80211_bss_conf *bss_conf,
                                       u32 changed)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret = 0;
 
        if ((changed & BSS_CHANGED_BASIC_RATES)) {
                u32 rates = bss_conf->basic_rates;
 
-               wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
-                                                                wl->band);
-               wl->basic_rate = wl1271_tx_min_rate_get(wl,
-                                                       wl->basic_rate_set);
+               wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
+                                                                wlvif->band);
+               wlvif->basic_rate = wl1271_tx_min_rate_get(wl,
+                                                       wlvif->basic_rate_set);
 
-               ret = wl1271_init_ap_rates(wl);
+               ret = wl1271_init_ap_rates(wl, wlvif);
                if (ret < 0) {
                        wl1271_error("AP rate policy change failed %d", ret);
                        goto out;
                }
 
-               ret = wl1271_ap_init_templates(wl);
+               ret = wl1271_ap_init_templates(wl, vif);
                if (ret < 0)
                        goto out;
        }
@@ -3306,38 +3517,38 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
 
        if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
                if (bss_conf->enable_beacon) {
-                       if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
-                               ret = wl12xx_cmd_role_start_ap(wl);
+                       if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+                               ret = wl12xx_cmd_role_start_ap(wl, wlvif);
                                if (ret < 0)
                                        goto out;
 
-                               ret = wl1271_ap_init_hwenc(wl);
+                               ret = wl1271_ap_init_hwenc(wl, wlvif);
                                if (ret < 0)
                                        goto out;
 
-                               set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
+                               set_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
                                wl1271_debug(DEBUG_AP, "started AP");
                        }
                } else {
-                       if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
-                               ret = wl12xx_cmd_role_stop_ap(wl);
+                       if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+                               ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
                                if (ret < 0)
                                        goto out;
 
-                               clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
+                               clear_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags);
                                wl1271_debug(DEBUG_AP, "stopped AP");
                        }
                }
        }
 
-       ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
+       ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
        if (ret < 0)
                goto out;
 
        /* Handle HT information change */
        if ((changed & BSS_CHANGED_HT) &&
            (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
-               ret = wl1271_acx_set_ht_information(wl,
+               ret = wl1271_acx_set_ht_information(wl, wlvif,
                                        bss_conf->ht_operation_mode);
                if (ret < 0) {
                        wl1271_warning("Set ht information failed %d", ret);
@@ -3355,8 +3566,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        struct ieee80211_bss_conf *bss_conf,
                                        u32 changed)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        bool do_join = false, set_assoc = false;
-       bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
+       bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
        bool ibss_joined = false;
        u32 sta_rate_set = 0;
        int ret;
@@ -3373,14 +3585,13 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
 
        if (changed & BSS_CHANGED_IBSS) {
                if (bss_conf->ibss_joined) {
-                       set_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags);
+                       set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
                        ibss_joined = true;
                } else {
-                       if (test_and_clear_bit(WL1271_FLAG_IBSS_JOINED,
-                                              &wl->flags)) {
-                               wl1271_unjoin(wl);
-                               wl12xx_cmd_role_start_dev(wl);
-                               wl12xx_roc(wl, wl->dev_role_id);
+                       if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
+                                              &wlvif->flags)) {
+                               wl1271_unjoin(wl, wlvif);
+                               wl12xx_start_dev(wl, wlvif);
                        }
                }
        }
@@ -3396,46 +3607,40 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
                             bss_conf->enable_beacon ? "enabled" : "disabled");
 
-               if (bss_conf->enable_beacon)
-                       wl->set_bss_type = BSS_TYPE_IBSS;
-               else
-                       wl->set_bss_type = BSS_TYPE_STA_BSS;
                do_join = true;
        }
 
+       if (changed & BSS_CHANGED_IDLE) {
+               ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
+               if (ret < 0)
+                       wl1271_warning("idle mode change failed %d", ret);
+       }
+
        if ((changed & BSS_CHANGED_CQM)) {
                bool enable = false;
                if (bss_conf->cqm_rssi_thold)
                        enable = true;
-               ret = wl1271_acx_rssi_snr_trigger(wl, enable,
+               ret = wl1271_acx_rssi_snr_trigger(wl, wlvif, enable,
                                                  bss_conf->cqm_rssi_thold,
                                                  bss_conf->cqm_rssi_hyst);
                if (ret < 0)
                        goto out;
-               wl->rssi_thold = bss_conf->cqm_rssi_thold;
+               wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       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)) {
-               memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
-
-               if (!is_zero_ether_addr(wl->bssid)) {
-                       ret = wl1271_cmd_build_null_data(wl);
+       if (changed & BSS_CHANGED_BSSID)
+               if (!is_zero_ether_addr(bss_conf->bssid)) {
+                       ret = wl12xx_cmd_build_null_data(wl, wlvif);
                        if (ret < 0)
                                goto out;
 
-                       ret = wl1271_build_qos_null_data(wl);
+                       ret = wl1271_build_qos_null_data(wl, vif);
                        if (ret < 0)
                                goto out;
 
                        /* Need to update the BSSID (for filtering etc) */
                        do_join = true;
                }
-       }
 
        if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
                rcu_read_lock();
@@ -3459,26 +3664,28 @@ sta_not_found:
                if (bss_conf->assoc) {
                        u32 rates;
                        int ieoffset;
-                       wl->aid = bss_conf->aid;
+                       wlvif->aid = bss_conf->aid;
                        set_assoc = true;
 
-                       wl->ps_poll_failures = 0;
+                       wlvif->ps_poll_failures = 0;
 
                        /*
                         * use basic rates from AP, and determine lowest rate
                         * to use with control frames.
                         */
                        rates = bss_conf->basic_rates;
-                       wl->basic_rate_set =
+                       wlvif->basic_rate_set =
                                wl1271_tx_enabled_rates_get(wl, rates,
-                                                           wl->band);
-                       wl->basic_rate =
-                               wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+                                                           wlvif->band);
+                       wlvif->basic_rate =
+                               wl1271_tx_min_rate_get(wl,
+                                                      wlvif->basic_rate_set);
                        if (sta_rate_set)
-                               wl->rate_set = wl1271_tx_enabled_rates_get(wl,
+                               wlvif->rate_set =
+                                       wl1271_tx_enabled_rates_get(wl,
                                                                sta_rate_set,
-                                                               wl->band);
-                       ret = wl1271_acx_sta_rate_policies(wl);
+                                                               wlvif->band);
+                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
                        if (ret < 0)
                                goto out;
 
@@ -3488,53 +3695,56 @@ sta_not_found:
                         * updates it by itself when the first beacon is
                         * received after a join.
                         */
-                       ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
+                       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
                        if (ret < 0)
                                goto out;
 
                        /*
                         * Get a template for hardware connection maintenance
                         */
-                       dev_kfree_skb(wl->probereq);
-                       wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
+                       dev_kfree_skb(wlvif->probereq);
+                       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
+                                                                       wlvif,
+                                                                       NULL);
                        ieoffset = offsetof(struct ieee80211_mgmt,
                                            u.probe_req.variable);
-                       wl1271_ssid_set(wl, wl->probereq, ieoffset);
+                       wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
 
                        /* enable the connection monitoring feature */
-                       ret = wl1271_acx_conn_monit_params(wl, true);
+                       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
                        if (ret < 0)
                                goto out;
                } else {
                        /* use defaults when not associated */
                        bool was_assoc =
-                           !!test_and_clear_bit(WL1271_FLAG_STA_ASSOCIATED,
-                                                &wl->flags);
+                           !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
+                                                &wlvif->flags);
                        bool was_ifup =
-                           !!test_and_clear_bit(WL1271_FLAG_STA_STATE_SENT,
-                                                &wl->flags);
-                       wl->aid = 0;
+                           !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
+                                                &wlvif->flags);
+                       wlvif->aid = 0;
 
                        /* free probe-request template */
-                       dev_kfree_skb(wl->probereq);
-                       wl->probereq = NULL;
+                       dev_kfree_skb(wlvif->probereq);
+                       wlvif->probereq = NULL;
 
                        /* re-enable dynamic ps - just in case */
-                       ieee80211_enable_dyn_ps(wl->vif);
+                       ieee80211_enable_dyn_ps(vif);
 
                        /* revert back to minimum rates for the current band */
-                       wl1271_set_band_rate(wl);
-                       wl->basic_rate =
-                               wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl);
+                       wl1271_set_band_rate(wl, wlvif);
+                       wlvif->basic_rate =
+                               wl1271_tx_min_rate_get(wl,
+                                                      wlvif->basic_rate_set);
+                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
                        if (ret < 0)
                                goto out;
 
                        /* disable connection monitor features */
-                       ret = wl1271_acx_conn_monit_params(wl, false);
+                       ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
 
                        /* Disable the keep-alive feature */
-                       ret = wl1271_acx_keep_alive_mode(wl, false);
+                       ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
                        if (ret < 0)
                                goto out;
 
@@ -3546,7 +3756,7 @@ sta_not_found:
                                 * no IF_OPER_UP notification.
                                 */
                                if (!was_ifup) {
-                                       ret = wl12xx_croc(wl, wl->role_id);
+                                       ret = wl12xx_croc(wl, wlvif->role_id);
                                        if (ret < 0)
                                                goto out;
                                }
@@ -3555,17 +3765,16 @@ sta_not_found:
                                 * roaming on the same channel. until we will
                                 * have a better flow...)
                                 */
-                               if (test_bit(wl->dev_role_id, wl->roc_map)) {
-                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                               if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
+                                       ret = wl12xx_croc(wl,
+                                                         wlvif->dev_role_id);
                                        if (ret < 0)
                                                goto out;
                                }
 
-                               wl1271_unjoin(wl);
-                               if (!(conf_flags & IEEE80211_CONF_IDLE)) {
-                                       wl12xx_cmd_role_start_dev(wl);
-                                       wl12xx_roc(wl, wl->dev_role_id);
-                               }
+                               wl1271_unjoin(wl, wlvif);
+                               if (!(conf_flags & IEEE80211_CONF_IDLE))
+                                       wl12xx_start_dev(wl, wlvif);
                        }
                }
        }
@@ -3576,27 +3785,28 @@ sta_not_found:
 
                if (bss_conf->ibss_joined) {
                        u32 rates = bss_conf->basic_rates;
-                       wl->basic_rate_set =
+                       wlvif->basic_rate_set =
                                wl1271_tx_enabled_rates_get(wl, rates,
-                                                           wl->band);
-                       wl->basic_rate =
-                               wl1271_tx_min_rate_get(wl, wl->basic_rate_set);
+                                                           wlvif->band);
+                       wlvif->basic_rate =
+                               wl1271_tx_min_rate_get(wl,
+                                                      wlvif->basic_rate_set);
 
                        /* by default, use 11b + OFDM rates */
-                       wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
-                       ret = wl1271_acx_sta_rate_policies(wl);
+                       wlvif->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
+                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
                        if (ret < 0)
                                goto out;
                }
        }
 
-       ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
+       ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
        if (ret < 0)
                goto out;
 
        if (changed & BSS_CHANGED_ARP_FILTER) {
                __be32 addr = bss_conf->arp_addr_list[0];
-               WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
+               WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
 
                if (bss_conf->arp_addr_cnt == 1 &&
                    bss_conf->arp_filter_enabled) {
@@ -3606,24 +3816,24 @@ sta_not_found:
                         * isn't being set (when sending), so we have to
                         * reconfigure the template upon every ip change.
                         */
-                       ret = wl1271_cmd_build_arp_rsp(wl, addr);
+                       ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr);
                        if (ret < 0) {
                                wl1271_warning("build arp rsp failed: %d", ret);
                                goto out;
                        }
 
-                       ret = wl1271_acx_arp_ip_filter(wl,
+                       ret = wl1271_acx_arp_ip_filter(wl, wlvif,
                                ACX_ARP_FILTER_ARP_FILTERING,
                                addr);
                } else
-                       ret = wl1271_acx_arp_ip_filter(wl, 0, addr);
+                       ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
 
                if (ret < 0)
                        goto out;
        }
 
        if (do_join) {
-               ret = wl1271_join(wl, set_assoc);
+               ret = wl1271_join(wl, wlvif, set_assoc);
                if (ret < 0) {
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
@@ -3631,35 +3841,31 @@ sta_not_found:
 
                /* ROC until connected (after EAPOL exchange) */
                if (!is_ibss) {
-                       ret = wl12xx_roc(wl, wl->role_id);
+                       ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
                        if (ret < 0)
                                goto out;
 
-                       wl1271_check_operstate(wl,
+                       wl1271_check_operstate(wl, wlvif,
                                               ieee80211_get_operstate(vif));
                }
                /*
                 * stop device role if started (we might already be in
                 * STA role). TODO: make it better.
                 */
-               if (wl->dev_role_id != WL12XX_INVALID_ROLE_ID) {
-                       ret = wl12xx_croc(wl, wl->dev_role_id);
-                       if (ret < 0)
-                               goto out;
-
-                       ret = wl12xx_cmd_role_stop_dev(wl);
+               if (wlvif->dev_role_id != WL12XX_INVALID_ROLE_ID) {
+                       ret = wl12xx_stop_dev(wl, wlvif);
                        if (ret < 0)
                                goto out;
                }
 
                /* If we want to go in PSM but we're not there yet */
-               if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
-                   !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+               if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) &&
+                   !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
                        enum wl1271_cmd_ps_mode mode;
 
                        mode = STATION_POWER_SAVE_MODE;
-                       ret = wl1271_ps_set_mode(wl, mode,
-                                                wl->basic_rate,
+                       ret = wl1271_ps_set_mode(wl, wlvif, mode,
+                                                wlvif->basic_rate,
                                                 true);
                        if (ret < 0)
                                goto out;
@@ -3673,7 +3879,7 @@ sta_not_found:
                        ret = wl1271_acx_set_ht_capabilities(wl,
                                                             &sta_ht_cap,
                                                             true,
-                                                            wl->sta_hlid);
+                                                            wlvif->sta.hlid);
                        if (ret < 0) {
                                wl1271_warning("Set ht cap true failed %d",
                                               ret);
@@ -3685,7 +3891,7 @@ sta_not_found:
                        ret = wl1271_acx_set_ht_capabilities(wl,
                                                             &sta_ht_cap,
                                                             false,
-                                                            wl->sta_hlid);
+                                                            wlvif->sta.hlid);
                        if (ret < 0) {
                                wl1271_warning("Set ht cap false failed %d",
                                               ret);
@@ -3697,7 +3903,7 @@ sta_not_found:
        /* Handle HT information change. Done after join. */
        if ((changed & BSS_CHANGED_HT) &&
            (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
-               ret = wl1271_acx_set_ht_information(wl,
+               ret = wl1271_acx_set_ht_information(wl, wlvif,
                                        bss_conf->ht_operation_mode);
                if (ret < 0) {
                        wl1271_warning("Set ht information failed %d", ret);
@@ -3715,7 +3921,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                                       u32 changed)
 {
        struct wl1271 *wl = hw->priv;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
@@ -3726,6 +3933,9 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
+       if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+               goto out;
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
@@ -3746,6 +3956,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
                             const struct ieee80211_tx_queue_params *params)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        u8 ps_scheme;
        int ret = 0;
 
@@ -3792,13 +4003,13 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
         * the txop is confed in units of 32us by the mac80211,
         * we need us
         */
-       ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
+       ret = wl1271_acx_ac_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
                                params->cw_min, params->cw_max,
                                params->aifs, params->txop << 5);
        if (ret < 0)
                goto out_sleep;
 
-       ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
+       ret = wl1271_acx_tid_cfg(wl, wlvif, wl1271_tx_get_queue(queue),
                                 CONF_CHANNEL_TYPE_EDCF,
                                 wl1271_tx_get_queue(queue),
                                 ps_scheme, CONF_ACK_POLICY_LEGACY,
@@ -3861,43 +4072,43 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
 }
 
 static int wl1271_allocate_sta(struct wl1271 *wl,
-                            struct ieee80211_sta *sta,
-                            u8 *hlid)
+                            struct wl12xx_vif *wlvif,
+                            struct ieee80211_sta *sta)
 {
        struct wl1271_station *wl_sta;
-       int id;
+       int ret;
+
 
-       id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS);
-       if (id >= AP_MAX_STATIONS) {
+       if (wl->active_sta_count >= AP_MAX_STATIONS) {
                wl1271_warning("could not allocate HLID - too much stations");
                return -EBUSY;
        }
 
        wl_sta = (struct wl1271_station *)sta->drv_priv;
-       set_bit(id, wl->ap_hlid_map);
-       wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
-       *hlid = wl_sta->hlid;
+       ret = wl12xx_allocate_link(wl, wlvif, &wl_sta->hlid);
+       if (ret < 0) {
+               wl1271_warning("could not allocate HLID - too many links");
+               return -EBUSY;
+       }
+
+       set_bit(wl_sta->hlid, wlvif->ap.sta_hlid_map);
        memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
        wl->active_sta_count++;
        return 0;
 }
 
-void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
+void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
 {
-       int id = hlid - WL1271_AP_STA_HLID_START;
-
-       if (hlid < WL1271_AP_STA_HLID_START)
-               return;
-
-       if (!test_bit(id, wl->ap_hlid_map))
+       if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
                return;
 
-       clear_bit(id, wl->ap_hlid_map);
+       clear_bit(hlid, wlvif->ap.sta_hlid_map);
        memset(wl->links[hlid].addr, 0, ETH_ALEN);
        wl->links[hlid].ba_bitmap = 0;
        wl1271_tx_reset_link_queues(wl, hlid);
        __clear_bit(hlid, &wl->ap_ps_map);
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       wl12xx_free_link(wl, wlvif, &hlid);
        wl->active_sta_count--;
 }
 
@@ -3906,6 +4117,8 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
                             struct ieee80211_sta *sta)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271_station *wl_sta;
        int ret = 0;
        u8 hlid;
 
@@ -3914,20 +4127,23 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
        if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
-       if (wl->bss_type != BSS_TYPE_AP_BSS)
+       if (wlvif->bss_type != BSS_TYPE_AP_BSS)
                goto out;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
 
-       ret = wl1271_allocate_sta(wl, sta, &hlid);
+       ret = wl1271_allocate_sta(wl, wlvif, sta);
        if (ret < 0)
                goto out;
 
+       wl_sta = (struct wl1271_station *)sta->drv_priv;
+       hlid = wl_sta->hlid;
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out_free_sta;
 
-       ret = wl12xx_cmd_add_peer(wl, sta, hlid);
+       ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
        if (ret < 0)
                goto out_sleep;
 
@@ -3944,7 +4160,7 @@ out_sleep:
 
 out_free_sta:
        if (ret < 0)
-               wl1271_free_sta(wl, hlid);
+               wl1271_free_sta(wl, wlvif, hlid);
 
 out:
        mutex_unlock(&wl->mutex);
@@ -3956,6 +4172,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
                                struct ieee80211_sta *sta)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct wl1271_station *wl_sta;
        int ret = 0, id;
 
@@ -3964,14 +4181,14 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
        if (unlikely(wl->state == WL1271_STATE_OFF))
                goto out;
 
-       if (wl->bss_type != BSS_TYPE_AP_BSS)
+       if (wlvif->bss_type != BSS_TYPE_AP_BSS)
                goto out;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
 
        wl_sta = (struct wl1271_station *)sta->drv_priv;
-       id = wl_sta->hlid - WL1271_AP_STA_HLID_START;
-       if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
+       id = wl_sta->hlid;
+       if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
                goto out;
 
        ret = wl1271_ps_elp_wakeup(wl);
@@ -3982,7 +4199,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out_sleep;
 
-       wl1271_free_sta(wl, wl_sta->hlid);
+       wl1271_free_sta(wl, wlvif, wl_sta->hlid);
 
 out_sleep:
        wl1271_ps_elp_sleep(wl);
@@ -3999,6 +4216,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                                  u8 buf_size)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
        u8 hlid, *ba_bitmap;
 
@@ -4016,10 +4234,10 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                goto out;
        }
 
-       if (wl->bss_type == BSS_TYPE_STA_BSS) {
-               hlid = wl->sta_hlid;
-               ba_bitmap = &wl->ba_rx_bitmap;
-       } else if (wl->bss_type == BSS_TYPE_AP_BSS) {
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
+               hlid = wlvif->sta.hlid;
+               ba_bitmap = &wlvif->sta.ba_rx_bitmap;
+       } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
                struct wl1271_station *wl_sta;
 
                wl_sta = (struct wl1271_station *)sta->drv_priv;
@@ -4039,7 +4257,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
-               if (!wl->ba_support || !wl->ba_allowed) {
+               if (!wlvif->ba_support || !wlvif->ba_allowed) {
                        ret = -ENOTSUPP;
                        break;
                }
@@ -4108,8 +4326,9 @@ static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif,
                                   const struct cfg80211_bitrate_mask *mask)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct wl1271 *wl = hw->priv;
-       int i;
+       int i, ret = 0;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x",
                mask->control[NL80211_BAND_2GHZ].legacy,
@@ -4118,19 +4337,39 @@ static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
        mutex_lock(&wl->mutex);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++)
-               wl->bitrate_masks[i] =
+               wlvif->bitrate_masks[i] =
                        wl1271_tx_enabled_rates_get(wl,
                                                    mask->control[i].legacy,
                                                    i);
+
+       if (unlikely(wl->state == WL1271_STATE_OFF))
+               goto out;
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+           !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+
+               ret = wl1271_ps_elp_wakeup(wl);
+               if (ret < 0)
+                       goto out;
+
+               wl1271_set_band_rate(wl, wlvif);
+               wlvif->basic_rate =
+                       wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+
+               wl1271_ps_elp_sleep(wl);
+       }
+out:
        mutex_unlock(&wl->mutex);
 
-       return 0;
+       return ret;
 }
 
 static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
                                     struct ieee80211_channel_switch *ch_switch)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
@@ -4138,19 +4377,24 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state == WL1271_STATE_OFF)) {
-               mutex_unlock(&wl->mutex);
-               ieee80211_chswitch_done(wl->vif, false);
-               return;
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+                       ieee80211_chswitch_done(vif, false);
+               }
+               goto out;
        }
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
 
-       ret = wl12xx_cmd_channel_switch(wl, ch_switch);
+       /* TODO: change mac80211 to pass vif as param */
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               ret = wl12xx_cmd_channel_switch(wl, ch_switch);
 
-       if (!ret)
-               set_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags);
+               if (!ret)
+                       set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+       }
 
        wl1271_ps_elp_sleep(wl);
 
@@ -4170,10 +4414,6 @@ static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
 
        /* packets are considered pending if in the TX queue or the FW */
        ret = (wl1271_tx_total_queue_count(wl) > 0) || (wl->tx_frames_cnt > 0);
-
-       /* the above is appropriate for STA mode for PS purposes */
-       WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
-
 out:
        mutex_unlock(&wl->mutex);
 
@@ -4604,7 +4844,7 @@ static struct bin_attribute fwlog_attr = {
        .read = wl1271_sysfs_read_fwlog,
 };
 
-int wl1271_register_hw(struct wl1271 *wl)
+static int wl1271_register_hw(struct wl1271 *wl)
 {
        int ret;
 
@@ -4645,9 +4885,8 @@ int wl1271_register_hw(struct wl1271 *wl)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(wl1271_register_hw);
 
-void wl1271_unregister_hw(struct wl1271 *wl)
+static void wl1271_unregister_hw(struct wl1271 *wl)
 {
        if (wl->state == WL1271_STATE_PLT)
                __wl1271_plt_stop(wl);
@@ -4657,9 +4896,8 @@ void wl1271_unregister_hw(struct wl1271 *wl)
        wl->mac80211_registered = false;
 
 }
-EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
 
-int wl1271_init_ieee80211(struct wl1271 *wl)
+static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
        static const u32 cipher_suites[] = {
                WLAN_CIPHER_SUITE_WEP40,
@@ -4736,27 +4974,26 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
 
        wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
 
-       SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
+       SET_IEEE80211_DEV(wl->hw, wl->dev);
 
        wl->hw->sta_data_size = sizeof(struct wl1271_station);
+       wl->hw->vif_data_size = sizeof(struct wl12xx_vif);
 
        wl->hw->max_rx_aggregation_subframes = 8;
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
 
 #define WL1271_DEFAULT_CHANNEL 0
 
-struct ieee80211_hw *wl1271_alloc_hw(void)
+static struct ieee80211_hw *wl1271_alloc_hw(void)
 {
        struct ieee80211_hw *hw;
-       struct platform_device *plat_dev = NULL;
        struct wl1271 *wl;
        int i, j, ret;
        unsigned int order;
 
-       BUILD_BUG_ON(AP_MAX_LINKS > WL12XX_MAX_LINKS);
+       BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
 
        hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
        if (!hw) {
@@ -4765,41 +5002,26 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
                goto err_hw_alloc;
        }
 
-       plat_dev = kmemdup(&wl1271_device, sizeof(wl1271_device), GFP_KERNEL);
-       if (!plat_dev) {
-               wl1271_error("could not allocate platform_device");
-               ret = -ENOMEM;
-               goto err_plat_alloc;
-       }
-
        wl = hw->priv;
        memset(wl, 0, sizeof(*wl));
 
        INIT_LIST_HEAD(&wl->list);
+       INIT_LIST_HEAD(&wl->wlvif_list);
 
        wl->hw = hw;
-       wl->plat_dev = plat_dev;
 
        for (i = 0; i < NUM_TX_QUEUES; i++)
-               skb_queue_head_init(&wl->tx_queue[i]);
-
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               for (j = 0; j < AP_MAX_LINKS; j++)
+               for (j = 0; j < WL12XX_MAX_LINKS; j++)
                        skb_queue_head_init(&wl->links[j].tx_queue[i]);
 
        skb_queue_head_init(&wl->deferred_rx_queue);
        skb_queue_head_init(&wl->deferred_tx_queue);
 
        INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
-       INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
        INIT_WORK(&wl->netstack_work, wl1271_netstack_work);
        INIT_WORK(&wl->tx_work, wl1271_tx_work);
        INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
        INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
-       INIT_WORK(&wl->rx_streaming_enable_work,
-                 wl1271_rx_streaming_enable_work);
-       INIT_WORK(&wl->rx_streaming_disable_work,
-                 wl1271_rx_streaming_disable_work);
 
        wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
        if (!wl->freezable_wq) {
@@ -4808,41 +5030,21 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        }
 
        wl->channel = WL1271_DEFAULT_CHANNEL;
-       wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
-       wl->default_key = 0;
        wl->rx_counter = 0;
-       wl->psm_entry_retry = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
-       wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
-       wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
-       wl->rate_set = CONF_TX_RATE_MASK_BASIC;
        wl->band = IEEE80211_BAND_2GHZ;
        wl->vif = NULL;
        wl->flags = 0;
        wl->sg_enabled = true;
        wl->hw_pg_ver = -1;
-       wl->bss_type = MAX_BSS_TYPE;
-       wl->set_bss_type = MAX_BSS_TYPE;
-       wl->last_tx_hlid = 0;
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
        wl->quirks = 0;
        wl->platform_quirks = 0;
        wl->sched_scanning = false;
-       wl->tx_security_seq = 0;
-       wl->tx_security_last_seq_lsb = 0;
        wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
-       wl->role_id = WL12XX_INVALID_ROLE_ID;
        wl->system_hlid = WL12XX_SYSTEM_HLID;
-       wl->sta_hlid = WL12XX_INVALID_LINK_ID;
-       wl->dev_role_id = WL12XX_INVALID_ROLE_ID;
-       wl->dev_hlid = WL12XX_INVALID_LINK_ID;
-       wl->session_counter = 0;
-       wl->ap_bcast_hlid = WL12XX_INVALID_LINK_ID;
-       wl->ap_global_hlid = WL12XX_INVALID_LINK_ID;
        wl->active_sta_count = 0;
-       setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
-                   (unsigned long) wl);
        wl->fwlog_size = 0;
        init_waitqueue_head(&wl->fwlog_waitq);
 
@@ -4860,8 +5062,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
 
        /* Apply default driver configuration. */
        wl1271_conf_init(wl);
-       wl->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
-       wl->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
 
        order = get_order(WL1271_AGGR_BUFFER_SIZE);
        wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
@@ -4883,49 +5083,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
                goto err_dummy_packet;
        }
 
-       /* Register platform device */
-       ret = platform_device_register(wl->plat_dev);
-       if (ret) {
-               wl1271_error("couldn't register platform device");
-               goto err_fwlog;
-       }
-       dev_set_drvdata(&wl->plat_dev->dev, wl);
-
-       /* Create sysfs file to control bt coex state */
-       ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file bt_coex_state");
-               goto err_platform;
-       }
-
-       /* Create sysfs file to get HW PG version */
-       ret = device_create_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file hw_pg_ver");
-               goto err_bt_coex_state;
-       }
-
-       /* Create sysfs file for the FW log */
-       ret = device_create_bin_file(&wl->plat_dev->dev, &fwlog_attr);
-       if (ret < 0) {
-               wl1271_error("failed to create sysfs file fwlog");
-               goto err_hw_pg_ver;
-       }
-
        return hw;
 
-err_hw_pg_ver:
-       device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
-
-err_bt_coex_state:
-       device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
-
-err_platform:
-       platform_device_unregister(wl->plat_dev);
-
-err_fwlog:
-       free_page((unsigned long)wl->fwlog);
-
 err_dummy_packet:
        dev_kfree_skb(wl->dummy_packet);
 
@@ -4937,18 +5096,14 @@ err_wq:
 
 err_hw:
        wl1271_debugfs_exit(wl);
-       kfree(plat_dev);
-
-err_plat_alloc:
        ieee80211_free_hw(hw);
 
 err_hw_alloc:
 
        return ERR_PTR(ret);
 }
-EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
 
-int wl1271_free_hw(struct wl1271 *wl)
+static int wl1271_free_hw(struct wl1271 *wl)
 {
        /* Unblock any fwlog readers */
        mutex_lock(&wl->mutex);
@@ -4956,17 +5111,15 @@ int wl1271_free_hw(struct wl1271 *wl)
        wake_up_interruptible_all(&wl->fwlog_waitq);
        mutex_unlock(&wl->mutex);
 
-       device_remove_bin_file(&wl->plat_dev->dev, &fwlog_attr);
+       device_remove_bin_file(wl->dev, &fwlog_attr);
 
-       device_remove_file(&wl->plat_dev->dev, &dev_attr_hw_pg_ver);
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
 
-       device_remove_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
-       platform_device_unregister(wl->plat_dev);
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
        free_page((unsigned long)wl->fwlog);
        dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf,
                        get_order(WL1271_AGGR_BUFFER_SIZE));
-       kfree(wl->plat_dev);
 
        wl1271_debugfs_exit(wl);
 
@@ -4983,7 +5136,174 @@ int wl1271_free_hw(struct wl1271 *wl)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(wl1271_free_hw);
+
+static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
+{
+       struct wl1271 *wl = cookie;
+       unsigned long flags;
+
+       wl1271_debug(DEBUG_IRQ, "IRQ");
+
+       /* complete the ELP completion */
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
+       if (wl->elp_compl) {
+               complete(wl->elp_compl);
+               wl->elp_compl = NULL;
+       }
+
+       if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
+               /* don't enqueue a work right now. mark it as pending */
+               set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
+               wl1271_debug(DEBUG_IRQ, "should not enqueue work");
+               disable_irq_nosync(wl->irq);
+               pm_wakeup_event(wl->dev, 0);
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
+               return IRQ_HANDLED;
+       }
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       return IRQ_WAKE_THREAD;
+}
+
+static int __devinit wl12xx_probe(struct platform_device *pdev)
+{
+       struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
+       struct ieee80211_hw *hw;
+       struct wl1271 *wl;
+       unsigned long irqflags;
+       int ret = -ENODEV;
+
+       hw = wl1271_alloc_hw();
+       if (IS_ERR(hw)) {
+               wl1271_error("can't allocate hw");
+               ret = PTR_ERR(hw);
+               goto out;
+       }
+
+       wl = hw->priv;
+       wl->irq = platform_get_irq(pdev, 0);
+       wl->ref_clock = pdata->board_ref_clock;
+       wl->tcxo_clock = pdata->board_tcxo_clock;
+       wl->platform_quirks = pdata->platform_quirks;
+       wl->set_power = pdata->set_power;
+       wl->dev = &pdev->dev;
+       wl->if_ops = pdata->ops;
+
+       platform_set_drvdata(pdev, wl);
+
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+               irqflags = IRQF_TRIGGER_RISING;
+       else
+               irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+
+       ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wl1271_irq,
+                                  irqflags,
+                                  pdev->name, wl);
+       if (ret < 0) {
+               wl1271_error("request_irq() failed: %d", ret);
+               goto out_free_hw;
+       }
+
+       ret = enable_irq_wake(wl->irq);
+       if (!ret) {
+               wl->irq_wake_enabled = true;
+               device_init_wakeup(wl->dev, 1);
+               if (pdata->pwr_in_suspend)
+                       hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
+
+       }
+       disable_irq(wl->irq);
+
+       ret = wl1271_init_ieee80211(wl);
+       if (ret)
+               goto out_irq;
+
+       ret = wl1271_register_hw(wl);
+       if (ret)
+               goto out_irq;
+
+       /* Create sysfs file to control bt coex state */
+       ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file bt_coex_state");
+               goto out_irq;
+       }
+
+       /* Create sysfs file to get HW PG version */
+       ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file hw_pg_ver");
+               goto out_bt_coex_state;
+       }
+
+       /* Create sysfs file for the FW log */
+       ret = device_create_bin_file(wl->dev, &fwlog_attr);
+       if (ret < 0) {
+               wl1271_error("failed to create sysfs file fwlog");
+               goto out_hw_pg_ver;
+       }
+
+       return 0;
+
+out_hw_pg_ver:
+       device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
+
+out_bt_coex_state:
+       device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+
+out_irq:
+       free_irq(wl->irq, wl);
+
+out_free_hw:
+       wl1271_free_hw(wl);
+
+out:
+       return ret;
+}
+
+static int __devexit wl12xx_remove(struct platform_device *pdev)
+{
+       struct wl1271 *wl = platform_get_drvdata(pdev);
+
+       if (wl->irq_wake_enabled) {
+               device_init_wakeup(wl->dev, 0);
+               disable_irq_wake(wl->irq);
+       }
+       wl1271_unregister_hw(wl);
+       free_irq(wl->irq, wl);
+       wl1271_free_hw(wl);
+
+       return 0;
+}
+
+static const struct platform_device_id wl12xx_id_table[] __devinitconst = {
+       { "wl12xx", 0 },
+       {  } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
+
+static struct platform_driver wl12xx_driver = {
+       .probe          = wl12xx_probe,
+       .remove         = __devexit_p(wl12xx_remove),
+       .id_table       = wl12xx_id_table,
+       .driver = {
+               .name   = "wl12xx_driver",
+               .owner  = THIS_MODULE,
+       }
+};
+
+static int __init wl12xx_init(void)
+{
+       return platform_driver_register(&wl12xx_driver);
+}
+module_init(wl12xx_init);
+
+static void __exit wl12xx_exit(void)
+{
+       platform_driver_unregister(&wl12xx_driver);
+}
+module_exit(wl12xx_exit);
 
 u32 wl12xx_debug_level = DEBUG_NONE;
 EXPORT_SYMBOL_GPL(wl12xx_debug_level);
index c15ebf2efd40a6e71fc710da1de8206c98c5294d..a7a11088dd3104f2f8b9a2941d567c20bba822b1 100644 (file)
@@ -25,6 +25,7 @@
 #include "ps.h"
 #include "io.h"
 #include "tx.h"
+#include "debug.h"
 
 #define WL1271_WAKEUP_TIMEOUT 500
 
@@ -32,6 +33,7 @@ void wl1271_elp_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
        struct wl1271 *wl;
+       struct wl12xx_vif *wlvif;
 
        dwork = container_of(work, struct delayed_work, work);
        wl = container_of(dwork, struct wl1271, elp_work);
@@ -47,11 +49,15 @@ void wl1271_elp_work(struct work_struct *work)
        if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
                goto out;
 
-       if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) ||
-           (!test_bit(WL1271_FLAG_PSM, &wl->flags) &&
-            !test_bit(WL1271_FLAG_IDLE, &wl->flags)))
+       if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags))
                goto out;
 
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) &&
+                   !test_bit(WL1271_FLAG_IDLE, &wl->flags))
+                       goto out;
+       }
+
        wl1271_debug(DEBUG_PSM, "chip to elp");
        wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
        set_bit(WL1271_FLAG_IN_ELP, &wl->flags);
@@ -65,13 +71,17 @@ out:
 /* Routines to toggle sleep mode while in ELP */
 void wl1271_ps_elp_sleep(struct wl1271 *wl)
 {
+       struct wl12xx_vif *wlvif;
+
        /* we shouldn't get consecutive sleep requests */
        if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)))
                return;
 
-       if (!test_bit(WL1271_FLAG_PSM, &wl->flags) &&
-           !test_bit(WL1271_FLAG_IDLE, &wl->flags))
-               return;
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) &&
+                   !test_bit(WL1271_FLAG_IDLE, &wl->flags))
+                       return;
+       }
 
        ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
                                     msecs_to_jiffies(ELP_ENTRY_DELAY));
@@ -143,8 +153,8 @@ out:
        return 0;
 }
 
-int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
-                      u32 rates, bool send)
+int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      enum wl1271_cmd_ps_mode mode, u32 rates, bool send)
 {
        int ret;
 
@@ -152,39 +162,34 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
        case STATION_POWER_SAVE_MODE:
                wl1271_debug(DEBUG_PSM, "entering psm");
 
-               ret = wl1271_acx_wake_up_conditions(wl);
+               ret = wl1271_acx_wake_up_conditions(wl, wlvif);
                if (ret < 0) {
                        wl1271_error("couldn't set wake up conditions");
                        return ret;
                }
 
-               ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
+               ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
                if (ret < 0)
                        return ret;
 
-               set_bit(WL1271_FLAG_PSM, &wl->flags);
+               set_bit(WLVIF_FLAG_PSM, &wlvif->flags);
                break;
        case STATION_ACTIVE_MODE:
        default:
                wl1271_debug(DEBUG_PSM, "leaving psm");
 
                /* disable beacon early termination */
-               if (wl->band == IEEE80211_BAND_2GHZ) {
-                       ret = wl1271_acx_bet_enable(wl, false);
+               if (wlvif->band == IEEE80211_BAND_2GHZ) {
+                       ret = wl1271_acx_bet_enable(wl, wlvif, false);
                        if (ret < 0)
                                return ret;
                }
 
-               /* disable beacon filtering */
-               ret = wl1271_acx_beacon_filter_opt(wl, false);
-               if (ret < 0)
-                       return ret;
-
-               ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
+               ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_ACTIVE_MODE);
                if (ret < 0)
                        return ret;
 
-               clear_bit(WL1271_FLAG_PSM, &wl->flags);
+               clear_bit(WLVIF_FLAG_PSM, &wlvif->flags);
                break;
        }
 
@@ -223,9 +228,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        wl1271_handle_tx_low_watermark(wl);
 }
 
-void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
+void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         u8 hlid, bool clean_queues)
 {
        struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
        if (test_bit(hlid, &wl->ap_ps_map))
                return;
@@ -235,7 +242,7 @@ void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
                     clean_queues);
 
        rcu_read_lock();
-       sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
+       sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
        if (!sta) {
                wl1271_error("could not find sta %pM for starting ps",
                             wl->links[hlid].addr);
@@ -253,9 +260,10 @@ void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues)
        __set_bit(hlid, &wl->ap_ps_map);
 }
 
-void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
+void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
 {
        struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
        if (!test_bit(hlid, &wl->ap_ps_map))
                return;
@@ -265,7 +273,7 @@ void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid)
        __clear_bit(hlid, &wl->ap_ps_map);
 
        rcu_read_lock();
-       sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr);
+       sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
        if (!sta) {
                wl1271_error("could not find sta %pM for ending ps",
                             wl->links[hlid].addr);
index 25eb9bc9b628283afffa2ed825967c45256f1db5..a12052f02026fba0a943258913cb7ad099254f75 100644 (file)
 #include "wl12xx.h"
 #include "acx.h"
 
-int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode,
-                      u32 rates, bool send);
+int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      enum wl1271_cmd_ps_mode mode, u32 rates, bool send);
 void wl1271_ps_elp_sleep(struct wl1271 *wl);
 int wl1271_ps_elp_wakeup(struct wl1271 *wl);
 void wl1271_elp_work(struct work_struct *work);
-void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues);
-void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid);
+void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         u8 hlid, bool clean_queues);
+void wl12xx_ps_link_end(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
 
 #define WL1271_PS_COMPLETE_TIMEOUT 500
 
index 3f570f39758654430ef9a04602b8caf422c8acb5..df34d5977b98581a2e4b321665a9d0de0dc00398 100644 (file)
 
 
 /* Firmware image load chunk size */
-#define CHUNK_SIZE          512
+#define CHUNK_SIZE     16384
 
 /* Firmware image header size */
 #define FW_HDR_SIZE 8
index dee4cfe9ccc1ed87a9979418e12bfc1f46ace708..8c277c0cb3728fb06ccd70176b77dea7a7af5e61 100644 (file)
 #include <linux/sched.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "acx.h"
 #include "reg.h"
 #include "rx.h"
+#include "tx.h"
 #include "io.h"
 
 static u8 wl12xx_rx_get_mem_block(struct wl12xx_fw_status *status,
@@ -96,7 +98,7 @@ static void wl1271_rx_status(struct wl1271 *wl,
 }
 
 static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
-                                bool unaligned)
+                                bool unaligned, u8 *hlid)
 {
        struct wl1271_rx_descriptor *desc;
        struct sk_buff *skb;
@@ -159,6 +161,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
         * payload aligned to 4 bytes.
         */
        memcpy(buf, data + sizeof(*desc), length - sizeof(*desc));
+       *hlid = desc->hlid;
 
        hdr = (struct ieee80211_hdr *)skb->data;
        if (ieee80211_is_beacon(hdr->frame_control))
@@ -169,10 +172,10 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
 
        seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
-       wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d", skb,
+       wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb,
                     skb->len - desc->pad_len,
                     beacon ? "beacon" : "",
-                    seq_num);
+                    seq_num, *hlid);
 
        skb_trim(skb, skb->len - desc->pad_len);
 
@@ -185,6 +188,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
 void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
 {
        struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
+       unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
        u32 buf_size;
        u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
        u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
@@ -192,8 +196,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
        u32 mem_block;
        u32 pkt_length;
        u32 pkt_offset;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
-       bool had_data = false;
+       u8 hlid;
        bool unaligned = false;
 
        while (drv_rx_counter != fw_rx_counter) {
@@ -253,8 +256,11 @@ void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
                         */
                        if (wl1271_rx_handle_data(wl,
                                                  wl->aggr_buf + pkt_offset,
-                                                 pkt_length, unaligned) == 1)
-                               had_data = true;
+                                                 pkt_length, unaligned,
+                                                 &hlid) == 1) {
+                               WARN_ON(hlid >= WL12XX_MAX_LINKS);
+                               __set_bit(hlid, active_hlids);
+                       }
 
                        wl->rx_counter++;
                        drv_rx_counter++;
@@ -270,17 +276,5 @@ void wl12xx_rx(struct wl1271 *wl, struct wl12xx_fw_status *status)
        if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION)
                wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter);
 
-       if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
-           (wl->conf.rx_streaming.always ||
-            test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
-               u32 timeout = wl->conf.rx_streaming.duration;
-
-               /* restart rx streaming */
-               if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
-                       ieee80211_queue_work(wl->hw,
-                                            &wl->rx_streaming_enable_work);
-
-               mod_timer(&wl->rx_streaming_timer,
-                         jiffies + msecs_to_jiffies(timeout));
-       }
+       wl12xx_rearm_rx_streaming(wl, active_hlids);
 }
index fc29c671cf3b34db3fc695fbd70209720a3c52e8..330d6788a916d7b6689ebd96aa237b0a17c13bd7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/ieee80211.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "cmd.h"
 #include "scan.h"
 #include "acx.h"
@@ -34,6 +35,8 @@ void wl1271_scan_complete_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
        struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
        int ret;
        bool is_sta, is_ibss;
 
@@ -50,28 +53,31 @@ void wl1271_scan_complete_work(struct work_struct *work)
        if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
                goto out;
 
+       vif = wl->scan_vif;
+       wlvif = wl12xx_vif_to_data(vif);
+
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
        wl->scan.req = NULL;
+       wl->scan_vif = NULL;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
 
-       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+       if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
                /* restore hardware connection monitoring template */
-               wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
+               wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq);
        }
 
        /* return to ROC if needed */
-       is_sta = (wl->bss_type == BSS_TYPE_STA_BSS);
-       is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
-       if (((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ||
-            (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) &&
-           !test_bit(wl->dev_role_id, wl->roc_map)) {
+       is_sta = (wlvif->bss_type == BSS_TYPE_STA_BSS);
+       is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
+       if (((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ||
+            (is_ibss && !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))) &&
+           !test_bit(wlvif->dev_role_id, wl->roc_map)) {
                /* restore remain on channel */
-               wl12xx_cmd_role_start_dev(wl);
-               wl12xx_roc(wl, wl->dev_role_id);
+               wl12xx_start_dev(wl, wlvif);
        }
        wl1271_ps_elp_sleep(wl);
 
@@ -155,9 +161,11 @@ static int wl1271_get_scan_channels(struct wl1271 *wl,
 
 #define WL1271_NOTHING_TO_SCAN 1
 
-static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
-                            bool passive, u32 basic_rate)
+static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
+                           enum ieee80211_band band,
+                           bool passive, u32 basic_rate)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        struct wl1271_cmd_scan *cmd;
        struct wl1271_cmd_trigger_scan_to *trigger;
        int ret;
@@ -177,11 +185,11 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
        if (passive)
                scan_options |= WL1271_SCAN_OPT_PASSIVE;
 
-       if (WARN_ON(wl->role_id == WL12XX_INVALID_ROLE_ID)) {
+       if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) {
                ret = -EINVAL;
                goto out;
        }
-       cmd->params.role_id = wl->role_id;
+       cmd->params.role_id = wlvif->role_id;
        cmd->params.scan_options = cpu_to_le16(scan_options);
 
        cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
@@ -194,7 +202,6 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
 
        cmd->params.tx_rate = cpu_to_le32(basic_rate);
        cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
-       cmd->params.tx_rate = cpu_to_le32(basic_rate);
        cmd->params.tid_trigger = 0;
        cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
 
@@ -208,11 +215,11 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
                memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
        }
 
-       memcpy(cmd->addr, wl->mac_addr, ETH_ALEN);
+       memcpy(cmd->addr, vif->addr, ETH_ALEN);
 
-       ret = wl1271_cmd_build_probe_req(wl, wl->scan.ssid, wl->scan.ssid_len,
-                                        wl->scan.req->ie, wl->scan.req->ie_len,
-                                        band);
+       ret = wl1271_cmd_build_probe_req(wl, wlvif, wl->scan.ssid,
+                                        wl->scan.ssid_len, wl->scan.req->ie,
+                                        wl->scan.req->ie_len, band);
        if (ret < 0) {
                wl1271_error("PROBE request template failed");
                goto out;
@@ -241,11 +248,12 @@ out:
        return ret;
 }
 
-void wl1271_scan_stm(struct wl1271 *wl)
+void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif)
 {
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret = 0;
        enum ieee80211_band band;
-       u32 rate;
+       u32 rate, mask;
 
        switch (wl->scan.state) {
        case WL1271_SCAN_STATE_IDLE:
@@ -253,47 +261,59 @@ void wl1271_scan_stm(struct wl1271 *wl)
 
        case WL1271_SCAN_STATE_2GHZ_ACTIVE:
                band = IEEE80211_BAND_2GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, band, false, rate);
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, vif, band, false, rate);
                if (ret == WL1271_NOTHING_TO_SCAN) {
                        wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
-                       wl1271_scan_stm(wl);
+                       wl1271_scan_stm(wl, vif);
                }
 
                break;
 
        case WL1271_SCAN_STATE_2GHZ_PASSIVE:
                band = IEEE80211_BAND_2GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, band, true, rate);
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, vif, band, true, rate);
                if (ret == WL1271_NOTHING_TO_SCAN) {
                        if (wl->enable_11a)
                                wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
                        else
                                wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl);
+                       wl1271_scan_stm(wl, vif);
                }
 
                break;
 
        case WL1271_SCAN_STATE_5GHZ_ACTIVE:
                band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, band, false, rate);
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, vif, band, false, rate);
                if (ret == WL1271_NOTHING_TO_SCAN) {
                        wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
-                       wl1271_scan_stm(wl);
+                       wl1271_scan_stm(wl, vif);
                }
 
                break;
 
        case WL1271_SCAN_STATE_5GHZ_PASSIVE:
                band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, band, true, rate);
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, vif, band, true, rate);
                if (ret == WL1271_NOTHING_TO_SCAN) {
                        wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl);
+                       wl1271_scan_stm(wl, vif);
                }
 
                break;
@@ -317,7 +337,8 @@ void wl1271_scan_stm(struct wl1271 *wl)
        }
 }
 
-int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
+int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+               const u8 *ssid, size_t ssid_len,
                struct cfg80211_scan_request *req)
 {
        /*
@@ -338,6 +359,7 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
                wl->scan.ssid_len = 0;
        }
 
+       wl->scan_vif = vif;
        wl->scan.req = req;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
 
@@ -346,7 +368,7 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
        ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
                                     msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
 
-       wl1271_scan_stm(wl);
+       wl1271_scan_stm(wl, vif);
 
        return 0;
 }
@@ -585,6 +607,7 @@ out:
 }
 
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
                                  struct cfg80211_sched_scan_request *req,
                                  struct ieee80211_sched_scan_ies *ies)
 {
@@ -631,7 +654,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
        }
 
        if (!force_passive && cfg->active[0]) {
-               ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid,
+               ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid,
                                                 req->ssids[0].ssid_len,
                                                 ies->ie[IEEE80211_BAND_2GHZ],
                                                 ies->len[IEEE80211_BAND_2GHZ],
@@ -643,7 +666,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
        }
 
        if (!force_passive && cfg->active[1]) {
-               ret = wl1271_cmd_build_probe_req(wl,  req->ssids[0].ssid,
+               ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid,
                                                 req->ssids[0].ssid_len,
                                                 ies->ie[IEEE80211_BAND_5GHZ],
                                                 ies->len[IEEE80211_BAND_5GHZ],
@@ -667,14 +690,14 @@ out:
        return ret;
 }
 
-int wl1271_scan_sched_scan_start(struct wl1271 *wl)
+int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl1271_cmd_sched_scan_start *start;
        int ret = 0;
 
        wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
 
-       if (wl->bss_type != BSS_TYPE_STA_BSS)
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
                return -EOPNOTSUPP;
 
        if (!test_bit(WL1271_FLAG_IDLE, &wl->flags))
index 92115156522fd73512fb2d78c2f7267710fac08b..a7ed43dc08c9e89217bc4430ca8b5b2b3ce2607a 100644 (file)
 
 #include "wl12xx.h"
 
-int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
+int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+               const u8 *ssid, size_t ssid_len,
                struct cfg80211_scan_request *req);
 int wl1271_scan_stop(struct wl1271 *wl);
 int wl1271_scan_build_probe_req(struct wl1271 *wl,
                                const u8 *ssid, size_t ssid_len,
                                const u8 *ie, size_t ie_len, u8 band);
-void wl1271_scan_stm(struct wl1271 *wl);
+void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif);
 void wl1271_scan_complete_work(struct work_struct *work);
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif,
                                     struct cfg80211_sched_scan_request *req,
                                     struct ieee80211_sched_scan_ies *ies);
-int wl1271_scan_sched_scan_start(struct wl1271 *wl);
+int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl1271_scan_sched_scan_stop(struct wl1271 *wl);
 void wl1271_scan_sched_scan_results(struct wl1271 *wl);
 
index 516a8980723c3c5ec90773db35ab24d2163ccb3f..468a50553fac8f345e689432f82cd075d9413828 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
+#include <linux/platform_device.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
 #define SDIO_DEVICE_ID_TI_WL1271       0x4076
 #endif
 
+struct wl12xx_sdio_glue {
+       struct device *dev;
+       struct platform_device *core;
+};
+
 static const struct sdio_device_id wl1271_devices[] __devinitconst = {
        { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
        {}
 };
 MODULE_DEVICE_TABLE(sdio, wl1271_devices);
 
-static void wl1271_sdio_set_block_size(struct wl1271 *wl, unsigned int blksz)
-{
-       sdio_claim_host(wl->if_priv);
-       sdio_set_block_size(wl->if_priv, blksz);
-       sdio_release_host(wl->if_priv);
-}
-
-static inline struct sdio_func *wl_to_func(struct wl1271 *wl)
-{
-       return wl->if_priv;
-}
-
-static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl)
-{
-       return &(wl_to_func(wl)->dev);
-}
-
-static irqreturn_t wl1271_hardirq(int irq, void *cookie)
+static void wl1271_sdio_set_block_size(struct device *child,
+                                      unsigned int blksz)
 {
-       struct wl1271 *wl = cookie;
-       unsigned long flags;
+       struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+       struct sdio_func *func = dev_to_sdio_func(glue->dev);
 
-       wl1271_debug(DEBUG_IRQ, "IRQ");
-
-       /* complete the ELP completion */
-       spin_lock_irqsave(&wl->wl_lock, flags);
-       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
-       if (wl->elp_compl) {
-               complete(wl->elp_compl);
-               wl->elp_compl = NULL;
-       }
-
-       if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) {
-               /* don't enqueue a work right now. mark it as pending */
-               set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags);
-               wl1271_debug(DEBUG_IRQ, "should not enqueue work");
-               disable_irq_nosync(wl->irq);
-               pm_wakeup_event(wl1271_sdio_wl_to_dev(wl), 0);
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
-               return IRQ_HANDLED;
-       }
-       spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-       return IRQ_WAKE_THREAD;
-}
-
-static void wl1271_sdio_disable_interrupts(struct wl1271 *wl)
-{
-       disable_irq(wl->irq);
-}
-
-static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
-{
-       enable_irq(wl->irq);
+       sdio_claim_host(func);
+       sdio_set_block_size(func, blksz);
+       sdio_release_host(func);
 }
 
-static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
+static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf,
                                 size_t len, bool fixed)
 {
        int ret;
-       struct sdio_func *func = wl_to_func(wl);
+       struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+       struct sdio_func *func = dev_to_sdio_func(glue->dev);
 
        if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
                ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
-               wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
-                            addr, ((u8 *)buf)[0]);
+               dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
+                       addr, ((u8 *)buf)[0]);
        } else {
                if (fixed)
                        ret = sdio_readsb(func, buf, addr, len);
                else
                        ret = sdio_memcpy_fromio(func, buf, addr, len);
 
-               wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes",
-                            addr, len);
-               wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
+               dev_dbg(child->parent, "sdio read 53 addr 0x%x, %zu bytes\n",
+                       addr, len);
        }
 
        if (ret)
-               wl1271_error("sdio read failed (%d)", ret);
+               dev_err(child->parent, "sdio read failed (%d)\n", ret);
 }
 
-static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
+static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf,
                                  size_t len, bool fixed)
 {
        int ret;
-       struct sdio_func *func = wl_to_func(wl);
+       struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+       struct sdio_func *func = dev_to_sdio_func(glue->dev);
 
        if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
                sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
-               wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
-                            addr, ((u8 *)buf)[0]);
+               dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
+                       addr, ((u8 *)buf)[0]);
        } else {
-               wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes",
-                            addr, len);
-               wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
+               dev_dbg(child->parent, "sdio write 53 addr 0x%x, %zu bytes\n",
+                       addr, len);
 
                if (fixed)
                        ret = sdio_writesb(func, addr, buf, len);
@@ -153,13 +114,13 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
        }
 
        if (ret)
-               wl1271_error("sdio write failed (%d)", ret);
+               dev_err(child->parent, "sdio write failed (%d)\n", ret);
 }
 
-static int wl1271_sdio_power_on(struct wl1271 *wl)
+static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
 {
-       struct sdio_func *func = wl_to_func(wl);
        int ret;
+       struct sdio_func *func = dev_to_sdio_func(glue->dev);
 
        /* If enabled, tell runtime PM not to power off the card */
        if (pm_runtime_enabled(&func->dev)) {
@@ -180,10 +141,10 @@ out:
        return ret;
 }
 
-static int wl1271_sdio_power_off(struct wl1271 *wl)
+static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
 {
-       struct sdio_func *func = wl_to_func(wl);
        int ret;
+       struct sdio_func *func = dev_to_sdio_func(glue->dev);
 
        sdio_disable_func(func);
        sdio_release_host(func);
@@ -200,46 +161,43 @@ static int wl1271_sdio_power_off(struct wl1271 *wl)
        return ret;
 }
 
-static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
+static int wl12xx_sdio_set_power(struct device *child, bool enable)
 {
+       struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
+
        if (enable)
-               return wl1271_sdio_power_on(wl);
+               return wl12xx_sdio_power_on(glue);
        else
-               return wl1271_sdio_power_off(wl);
+               return wl12xx_sdio_power_off(glue);
 }
 
 static struct wl1271_if_operations sdio_ops = {
-       .read           = wl1271_sdio_raw_read,
-       .write          = wl1271_sdio_raw_write,
-       .power          = wl1271_sdio_set_power,
-       .dev            = wl1271_sdio_wl_to_dev,
-       .enable_irq     = wl1271_sdio_enable_interrupts,
-       .disable_irq    = wl1271_sdio_disable_interrupts,
+       .read           = wl12xx_sdio_raw_read,
+       .write          = wl12xx_sdio_raw_write,
+       .power          = wl12xx_sdio_set_power,
        .set_block_size = wl1271_sdio_set_block_size,
 };
 
 static int __devinit wl1271_probe(struct sdio_func *func,
                                  const struct sdio_device_id *id)
 {
-       struct ieee80211_hw *hw;
-       const struct wl12xx_platform_data *wlan_data;
-       struct wl1271 *wl;
-       unsigned long irqflags;
+       struct wl12xx_platform_data *wlan_data;
+       struct wl12xx_sdio_glue *glue;
+       struct resource res[1];
        mmc_pm_flag_t mmcflags;
-       int ret;
+       int ret = -ENOMEM;
 
        /* We are only able to handle the wlan function */
        if (func->num != 0x02)
                return -ENODEV;
 
-       hw = wl1271_alloc_hw();
-       if (IS_ERR(hw))
-               return PTR_ERR(hw);
-
-       wl = hw->priv;
+       glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+       if (!glue) {
+               dev_err(&func->dev, "can't allocate glue\n");
+               goto out;
+       }
 
-       wl->if_priv = func;
-       wl->if_ops = &sdio_ops;
+       glue->dev = &func->dev;
 
        /* Grab access to FN0 for ELP reg. */
        func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
@@ -250,80 +208,79 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        wlan_data = wl12xx_get_platform_data();
        if (IS_ERR(wlan_data)) {
                ret = PTR_ERR(wlan_data);
-               wl1271_error("missing wlan platform data: %d", ret);
-               goto out_free;
+               dev_err(glue->dev, "missing wlan platform data: %d\n", ret);
+               goto out_free_glue;
        }
 
-       wl->irq = wlan_data->irq;
-       wl->ref_clock = wlan_data->board_ref_clock;
-       wl->tcxo_clock = wlan_data->board_tcxo_clock;
-       wl->platform_quirks = wlan_data->platform_quirks;
+       /* if sdio can keep power while host is suspended, enable wow */
+       mmcflags = sdio_get_host_pm_caps(func);
+       dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags);
 
-       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
-               irqflags = IRQF_TRIGGER_RISING;
-       else
-               irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
-
-       ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
-                                  irqflags,
-                                  DRIVER_NAME, wl);
-       if (ret < 0) {
-               wl1271_error("request_irq() failed: %d", ret);
-               goto out_free;
-       }
+       if (mmcflags & MMC_PM_KEEP_POWER)
+               wlan_data->pwr_in_suspend = true;
+
+       wlan_data->ops = &sdio_ops;
 
-       ret = enable_irq_wake(wl->irq);
-       if (!ret) {
-               wl->irq_wake_enabled = true;
-               device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1);
+       sdio_set_drvdata(func, glue);
 
-               /* if sdio can keep power while host is suspended, enable wow */
-               mmcflags = sdio_get_host_pm_caps(func);
-               wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags);
+       /* Tell PM core that we don't need the card to be powered now */
+       pm_runtime_put_noidle(&func->dev);
 
-               if (mmcflags & MMC_PM_KEEP_POWER)
-                       hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
+       glue->core = platform_device_alloc("wl12xx", -1);
+       if (!glue->core) {
+               dev_err(glue->dev, "can't allocate platform_device");
+               ret = -ENOMEM;
+               goto out_free_glue;
        }
-       disable_irq(wl->irq);
 
-       ret = wl1271_init_ieee80211(wl);
-       if (ret)
-               goto out_irq;
+       glue->core->dev.parent = &func->dev;
 
-       ret = wl1271_register_hw(wl);
-       if (ret)
-               goto out_irq;
+       memset(res, 0x00, sizeof(res));
 
-       sdio_set_drvdata(func, wl);
+       res[0].start = wlan_data->irq;
+       res[0].flags = IORESOURCE_IRQ;
+       res[0].name = "irq";
 
-       /* Tell PM core that we don't need the card to be powered now */
-       pm_runtime_put_noidle(&func->dev);
+       ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
+       if (ret) {
+               dev_err(glue->dev, "can't add resources\n");
+               goto out_dev_put;
+       }
 
+       ret = platform_device_add_data(glue->core, wlan_data,
+                                      sizeof(*wlan_data));
+       if (ret) {
+               dev_err(glue->dev, "can't add platform data\n");
+               goto out_dev_put;
+       }
+
+       ret = platform_device_add(glue->core);
+       if (ret) {
+               dev_err(glue->dev, "can't add platform device\n");
+               goto out_dev_put;
+       }
        return 0;
 
- out_irq:
-       free_irq(wl->irq, wl);
+out_dev_put:
+       platform_device_put(glue->core);
 
- out_free:
-       wl1271_free_hw(wl);
+out_free_glue:
+       kfree(glue);
 
+out:
        return ret;
 }
 
 static void __devexit wl1271_remove(struct sdio_func *func)
 {
-       struct wl1271 *wl = sdio_get_drvdata(func);
+       struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
 
        /* Undo decrement done above in wl1271_probe */
        pm_runtime_get_noresume(&func->dev);
 
-       wl1271_unregister_hw(wl);
-       if (wl->irq_wake_enabled) {
-               device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0);
-               disable_irq_wake(wl->irq);
-       }
-       free_irq(wl->irq, wl);
-       wl1271_free_hw(wl);
+       platform_device_del(glue->core);
+       platform_device_put(glue->core);
+       kfree(glue);
 }
 
 #ifdef CONFIG_PM
@@ -332,20 +289,21 @@ static int wl1271_suspend(struct device *dev)
        /* Tell MMC/SDIO core it's OK to power down the card
         * (if it isn't already), but not to remove it completely */
        struct sdio_func *func = dev_to_sdio_func(dev);
-       struct wl1271 *wl = sdio_get_drvdata(func);
+       struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
+       struct wl1271 *wl = platform_get_drvdata(glue->core);
        mmc_pm_flag_t sdio_flags;
        int ret = 0;
 
-       wl1271_debug(DEBUG_MAC80211, "wl1271 suspend. wow_enabled: %d",
-                    wl->wow_enabled);
+       dev_dbg(dev, "wl1271 suspend. wow_enabled: %d\n",
+               wl->wow_enabled);
 
        /* check whether sdio should keep power */
        if (wl->wow_enabled) {
                sdio_flags = sdio_get_host_pm_caps(func);
 
                if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
-                       wl1271_error("can't keep power while host "
-                                    "is suspended");
+                       dev_err(dev, "can't keep power while host "
+                                    "is suspended\n");
                        ret = -EINVAL;
                        goto out;
                }
@@ -353,7 +311,7 @@ static int wl1271_suspend(struct device *dev)
                /* keep power while host suspended */
                ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
                if (ret) {
-                       wl1271_error("error while trying to keep power");
+                       dev_err(dev, "error while trying to keep power\n");
                        goto out;
                }
 
@@ -367,9 +325,10 @@ out:
 static int wl1271_resume(struct device *dev)
 {
        struct sdio_func *func = dev_to_sdio_func(dev);
-       struct wl1271 *wl = sdio_get_drvdata(func);
+       struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
+       struct wl1271 *wl = platform_get_drvdata(glue->core);
 
-       wl1271_debug(DEBUG_MAC80211, "wl1271 resume");
+       dev_dbg(dev, "wl1271 resume\n");
        if (wl->wow_enabled) {
                /* claim back host */
                sdio_claim_host(func);
diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c
deleted file mode 100644 (file)
index f25d5d9..0000000
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
- * SDIO testing driver for wl12xx
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contact: Roger Quadros <roger.quadros@nokia.com>
- *
- * wl12xx read/write routines taken from the main module
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/crc7.h>
-#include <linux/vmalloc.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
-#include <linux/gpio.h>
-#include <linux/wl12xx.h>
-#include <linux/kthread.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-
-#include "wl12xx.h"
-#include "io.h"
-#include "boot.h"
-
-#ifndef SDIO_VENDOR_ID_TI
-#define SDIO_VENDOR_ID_TI              0x0097
-#endif
-
-#ifndef SDIO_DEVICE_ID_TI_WL1271
-#define SDIO_DEVICE_ID_TI_WL1271       0x4076
-#endif
-
-static bool rx, tx;
-
-module_param(rx, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(rx, "Perform rx test. Default (0). "
-       "This test continuously reads data from the SDIO device.\n");
-
-module_param(tx, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(tx, "Perform tx test. Default (0). "
-       "This test continuously writes data to the SDIO device.\n");
-
-struct wl1271_test {
-       struct wl1271 wl;
-       struct task_struct *test_task;
-};
-
-static const struct sdio_device_id wl1271_devices[] = {
-       { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) },
-       {}
-};
-
-static inline struct sdio_func *wl_to_func(struct wl1271 *wl)
-{
-       return wl->if_priv;
-}
-
-static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl)
-{
-       return &(wl_to_func(wl)->dev);
-}
-
-static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
-               size_t len, bool fixed)
-{
-       int ret = 0;
-       struct sdio_func *func = wl_to_func(wl);
-
-       if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
-               ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
-               wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
-                               addr, ((u8 *)buf)[0]);
-       } else {
-               if (fixed)
-                       ret = sdio_readsb(func, buf, addr, len);
-               else
-                       ret = sdio_memcpy_fromio(func, buf, addr, len);
-
-               wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes",
-                               addr, len);
-               wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
-       }
-
-       if (ret)
-               wl1271_error("sdio read failed (%d)", ret);
-}
-
-static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
-               size_t len, bool fixed)
-{
-       int ret = 0;
-       struct sdio_func *func = wl_to_func(wl);
-
-       if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
-               sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
-               wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
-                               addr, ((u8 *)buf)[0]);
-       } else {
-               wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes",
-                               addr, len);
-               wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
-
-               if (fixed)
-                       ret = sdio_writesb(func, addr, buf, len);
-               else
-                       ret = sdio_memcpy_toio(func, addr, buf, len);
-       }
-       if (ret)
-               wl1271_error("sdio write failed (%d)", ret);
-
-}
-
-static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
-{
-       struct sdio_func *func = wl_to_func(wl);
-       int ret;
-
-       /* Let the SDIO stack handle wlan_enable control, so we
-        * keep host claimed while wlan is in use to keep wl1271
-        * alive.
-        */
-       if (enable) {
-               /* Power up the card */
-               ret = pm_runtime_get_sync(&func->dev);
-               if (ret < 0)
-                       goto out;
-
-               /* Runtime PM might be disabled, power up the card manually */
-               ret = mmc_power_restore_host(func->card->host);
-               if (ret < 0)
-                       goto out;
-
-               sdio_claim_host(func);
-               sdio_enable_func(func);
-       } else {
-               sdio_disable_func(func);
-               sdio_release_host(func);
-
-               /* Runtime PM might be disabled, power off the card manually */
-               ret = mmc_power_save_host(func->card->host);
-               if (ret < 0)
-                       goto out;
-
-               /* Power down the card */
-               ret = pm_runtime_put_sync(&func->dev);
-       }
-
-out:
-       return ret;
-}
-
-static void wl1271_sdio_disable_interrupts(struct wl1271 *wl)
-{
-}
-
-static void wl1271_sdio_enable_interrupts(struct wl1271 *wl)
-{
-}
-
-
-static struct wl1271_if_operations sdio_ops = {
-       .read           = wl1271_sdio_raw_read,
-       .write          = wl1271_sdio_raw_write,
-       .power          = wl1271_sdio_set_power,
-       .dev            = wl1271_sdio_wl_to_dev,
-       .enable_irq     = wl1271_sdio_enable_interrupts,
-       .disable_irq    = wl1271_sdio_disable_interrupts,
-};
-
-static void wl1271_fw_wakeup(struct wl1271 *wl)
-{
-       u32 elp_reg;
-
-       elp_reg = ELPCTRL_WAKE_UP;
-       wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg);
-}
-
-static int wl1271_fetch_firmware(struct wl1271 *wl)
-{
-       const struct firmware *fw;
-       int ret;
-
-       if (wl->chip.id == CHIP_ID_1283_PG20)
-               ret = request_firmware(&fw, WL128X_FW_NAME,
-                                      wl1271_wl_to_dev(wl));
-       else
-               ret = request_firmware(&fw, WL127X_FW_NAME,
-                                      wl1271_wl_to_dev(wl));
-
-       if (ret < 0) {
-               wl1271_error("could not get firmware: %d", ret);
-               return ret;
-       }
-
-       if (fw->size % 4) {
-               wl1271_error("firmware size is not multiple of 32 bits: %zu",
-                               fw->size);
-               ret = -EILSEQ;
-               goto out;
-       }
-
-       wl->fw_len = fw->size;
-       wl->fw = vmalloc(wl->fw_len);
-
-       if (!wl->fw) {
-               wl1271_error("could not allocate memory for the firmware");
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       memcpy(wl->fw, fw->data, wl->fw_len);
-
-       ret = 0;
-
-out:
-       release_firmware(fw);
-
-       return ret;
-}
-
-static int wl1271_fetch_nvs(struct wl1271 *wl)
-{
-       const struct firmware *fw;
-       int ret;
-
-       ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
-
-       if (ret < 0) {
-               wl1271_error("could not get nvs file: %d", ret);
-               return ret;
-       }
-
-       wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
-
-       if (!wl->nvs) {
-               wl1271_error("could not allocate memory for the nvs file");
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       wl->nvs_len = fw->size;
-
-out:
-       release_firmware(fw);
-
-       return ret;
-}
-
-static int wl1271_chip_wakeup(struct wl1271 *wl)
-{
-       struct wl1271_partition_set partition;
-       int ret;
-
-       msleep(WL1271_PRE_POWER_ON_SLEEP);
-       ret = wl1271_power_on(wl);
-       if (ret)
-               return ret;
-
-       msleep(WL1271_POWER_ON_SLEEP);
-
-       /* We don't need a real memory partition here, because we only want
-        * to use the registers at this point. */
-       memset(&partition, 0, sizeof(partition));
-       partition.reg.start = REGISTERS_BASE;
-       partition.reg.size = REGISTERS_DOWN_SIZE;
-       wl1271_set_partition(wl, &partition);
-
-       /* ELP module wake up */
-       wl1271_fw_wakeup(wl);
-
-       /* whal_FwCtrl_BootSm() */
-
-       /* 0. read chip id from CHIP_ID */
-       wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
-
-       /* 1. check if chip id is valid */
-
-       switch (wl->chip.id) {
-       case CHIP_ID_1271_PG10:
-               wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
-                               wl->chip.id);
-               break;
-       case CHIP_ID_1271_PG20:
-               wl1271_notice("chip id 0x%x (1271 PG20)",
-                               wl->chip.id);
-               break;
-       case CHIP_ID_1283_PG20:
-               wl1271_notice("chip id 0x%x (1283 PG20)",
-                               wl->chip.id);
-               break;
-       case CHIP_ID_1283_PG10:
-       default:
-               wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
-               return -ENODEV;
-       }
-
-       return ret;
-}
-
-static struct wl1271_partition_set part_down = {
-       .mem = {
-               .start = 0x00000000,
-               .size  = 0x000177c0
-       },
-       .reg = {
-               .start = REGISTERS_BASE,
-               .size  = 0x00008800
-       },
-       .mem2 = {
-               .start = 0x00000000,
-               .size  = 0x00000000
-       },
-       .mem3 = {
-               .start = 0x00000000,
-               .size  = 0x00000000
-       },
-};
-
-static int tester(void *data)
-{
-       struct wl1271 *wl = data;
-       struct sdio_func *func = wl_to_func(wl);
-       struct device *pdev = &func->dev;
-       int ret = 0;
-       bool rx_started = 0;
-       bool tx_started = 0;
-       uint8_t *tx_buf, *rx_buf;
-       int test_size = PAGE_SIZE;
-       u32 addr = 0;
-       struct wl1271_partition_set partition;
-
-       /* We assume chip is powered up and firmware fetched */
-
-       memcpy(&partition, &part_down, sizeof(partition));
-       partition.mem.start = addr;
-       wl1271_set_partition(wl, &partition);
-
-       tx_buf = kmalloc(test_size, GFP_KERNEL);
-       rx_buf = kmalloc(test_size, GFP_KERNEL);
-       if (!tx_buf || !rx_buf) {
-               dev_err(pdev,
-                       "Could not allocate memory. Test will not run.\n");
-               ret = -ENOMEM;
-               goto free;
-       }
-
-       memset(tx_buf, 0x5a, test_size);
-
-       /* write something in data area so we can read it back */
-       wl1271_write(wl, addr, tx_buf, test_size, false);
-
-       while (!kthread_should_stop()) {
-               if (rx && !rx_started) {
-                       dev_info(pdev, "starting rx test\n");
-                       rx_started = 1;
-               } else if (!rx && rx_started) {
-                       dev_info(pdev, "stopping rx test\n");
-                       rx_started = 0;
-               }
-
-               if (tx && !tx_started) {
-                       dev_info(pdev, "starting tx test\n");
-                       tx_started = 1;
-               } else if (!tx && tx_started) {
-                       dev_info(pdev, "stopping tx test\n");
-                       tx_started = 0;
-               }
-
-               if (rx_started)
-                       wl1271_read(wl, addr, rx_buf, test_size, false);
-
-               if (tx_started)
-                       wl1271_write(wl, addr, tx_buf, test_size, false);
-
-               if (!rx_started && !tx_started)
-                       msleep(100);
-       }
-
-free:
-       kfree(tx_buf);
-       kfree(rx_buf);
-       return ret;
-}
-
-static int __devinit wl1271_probe(struct sdio_func *func,
-               const struct sdio_device_id *id)
-{
-       const struct wl12xx_platform_data *wlan_data;
-       struct wl1271 *wl;
-       struct wl1271_test *wl_test;
-       int ret = 0;
-
-       /* wl1271 has 2 sdio functions we handle just the wlan part */
-       if (func->num != 0x02)
-               return -ENODEV;
-
-       wl_test = kzalloc(sizeof(struct wl1271_test), GFP_KERNEL);
-       if (!wl_test) {
-               dev_err(&func->dev, "Could not allocate memory\n");
-               return -ENOMEM;
-       }
-
-       wl = &wl_test->wl;
-
-       wl->if_priv = func;
-       wl->if_ops = &sdio_ops;
-
-       /* Grab access to FN0 for ELP reg. */
-       func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
-
-       /* Use block mode for transferring over one block size of data */
-       func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
-
-       wlan_data = wl12xx_get_platform_data();
-       if (IS_ERR(wlan_data)) {
-               ret = PTR_ERR(wlan_data);
-               dev_err(&func->dev, "missing wlan platform data: %d\n", ret);
-               goto out_free;
-       }
-
-       wl->irq = wlan_data->irq;
-       wl->ref_clock = wlan_data->board_ref_clock;
-       wl->tcxo_clock = wlan_data->board_tcxo_clock;
-
-       sdio_set_drvdata(func, wl_test);
-
-       /* power up the device */
-       ret = wl1271_chip_wakeup(wl);
-       if (ret) {
-               dev_err(&func->dev, "could not wake up chip\n");
-               goto out_free;
-       }
-
-       if (wl->fw == NULL) {
-               ret = wl1271_fetch_firmware(wl);
-               if (ret < 0) {
-                       dev_err(&func->dev, "firmware fetch error\n");
-                       goto out_off;
-               }
-       }
-
-       /* fetch NVS */
-       if (wl->nvs == NULL) {
-               ret = wl1271_fetch_nvs(wl);
-               if (ret < 0) {
-                       dev_err(&func->dev, "NVS fetch error\n");
-                       goto out_off;
-               }
-       }
-
-       ret = wl1271_load_firmware(wl);
-       if (ret < 0) {
-               dev_err(&func->dev, "firmware load error: %d\n", ret);
-               goto out_free;
-       }
-
-       dev_info(&func->dev, "initialized\n");
-
-       /* I/O testing will be done in the tester thread */
-
-       wl_test->test_task = kthread_run(tester, wl, "sdio_tester");
-       if (IS_ERR(wl_test->test_task)) {
-               dev_err(&func->dev, "unable to create kernel thread\n");
-               ret = PTR_ERR(wl_test->test_task);
-               goto out_free;
-       }
-
-       return 0;
-
-out_off:
-       /* power off the chip */
-       wl1271_power_off(wl);
-
-out_free:
-       kfree(wl_test);
-       return ret;
-}
-
-static void __devexit wl1271_remove(struct sdio_func *func)
-{
-       struct wl1271_test *wl_test = sdio_get_drvdata(func);
-
-       /* stop the I/O test thread */
-       kthread_stop(wl_test->test_task);
-
-       /* power off the chip */
-       wl1271_power_off(&wl_test->wl);
-
-       vfree(wl_test->wl.fw);
-       wl_test->wl.fw = NULL;
-       kfree(wl_test->wl.nvs);
-       wl_test->wl.nvs = NULL;
-
-       kfree(wl_test);
-}
-
-static struct sdio_driver wl1271_sdio_driver = {
-       .name           = "wl12xx_sdio_test",
-       .id_table       = wl1271_devices,
-       .probe          = wl1271_probe,
-       .remove         = __devexit_p(wl1271_remove),
-};
-
-static int __init wl1271_init(void)
-{
-       int ret;
-
-       ret = sdio_register_driver(&wl1271_sdio_driver);
-       if (ret < 0)
-               pr_err("failed to register sdio driver: %d\n", ret);
-
-       return ret;
-}
-module_init(wl1271_init);
-
-static void __exit wl1271_exit(void)
-{
-       sdio_unregister_driver(&wl1271_sdio_driver);
-}
-module_exit(wl1271_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Roger Quadros <roger.quadros@nokia.com>");
-
index 12421a64d891c185c20307ffd940b7b3ebfc2175..92caa7ce6053efc1c36227553b03b05e61844cbe 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/crc7.h>
 #include <linux/spi/spi.h>
 #include <linux/wl12xx.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 
 #include "wl12xx.h"
 
 #define WSPI_MAX_NUM_OF_CHUNKS (WL1271_AGGR_BUFFER_SIZE / WSPI_MAX_CHUNK_SIZE)
 
-static inline struct spi_device *wl_to_spi(struct wl1271 *wl)
-{
-       return wl->if_priv;
-}
-
-static struct device *wl1271_spi_wl_to_dev(struct wl1271 *wl)
-{
-       return &(wl_to_spi(wl)->dev);
-}
-
-static void wl1271_spi_disable_interrupts(struct wl1271 *wl)
-{
-       disable_irq(wl->irq);
-}
-
-static void wl1271_spi_enable_interrupts(struct wl1271 *wl)
-{
-       enable_irq(wl->irq);
-}
+struct wl12xx_spi_glue {
+       struct device *dev;
+       struct platform_device *core;
+};
 
-static void wl1271_spi_reset(struct wl1271 *wl)
+static void wl12xx_spi_reset(struct device *child)
 {
+       struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
        u8 *cmd;
        struct spi_transfer t;
        struct spi_message m;
 
        cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
        if (!cmd) {
-               wl1271_error("could not allocate cmd for spi reset");
+               dev_err(child->parent,
+                       "could not allocate cmd for spi reset\n");
                return;
        }
 
@@ -110,21 +98,22 @@ static void wl1271_spi_reset(struct wl1271 *wl)
        t.len = WSPI_INIT_CMD_LEN;
        spi_message_add_tail(&t, &m);
 
-       spi_sync(wl_to_spi(wl), &m);
+       spi_sync(to_spi_device(glue->dev), &m);
 
-       wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN);
        kfree(cmd);
 }
 
-static void wl1271_spi_init(struct wl1271 *wl)
+static void wl12xx_spi_init(struct device *child)
 {
+       struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
        u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd;
        struct spi_transfer t;
        struct spi_message m;
 
        cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL);
        if (!cmd) {
-               wl1271_error("could not allocate cmd for spi init");
+               dev_err(child->parent,
+                       "could not allocate cmd for spi init\n");
                return;
        }
 
@@ -165,15 +154,16 @@ static void wl1271_spi_init(struct wl1271 *wl)
        t.len = WSPI_INIT_CMD_LEN;
        spi_message_add_tail(&t, &m);
 
-       spi_sync(wl_to_spi(wl), &m);
-       wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN);
+       spi_sync(to_spi_device(glue->dev), &m);
        kfree(cmd);
 }
 
 #define WL1271_BUSY_WORD_TIMEOUT 1000
 
-static int wl1271_spi_read_busy(struct wl1271 *wl)
+static int wl12xx_spi_read_busy(struct device *child)
 {
+       struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
+       struct wl1271 *wl = dev_get_drvdata(child);
        struct spi_transfer t[1];
        struct spi_message m;
        u32 *busy_buf;
@@ -194,20 +184,22 @@ static int wl1271_spi_read_busy(struct wl1271 *wl)
                t[0].len = sizeof(u32);
                t[0].cs_change = true;
                spi_message_add_tail(&t[0], &m);
-               spi_sync(wl_to_spi(wl), &m);
+               spi_sync(to_spi_device(glue->dev), &m);
 
                if (*busy_buf & 0x1)
                        return 0;
        }
 
        /* The SPI bus is unresponsive, the read failed. */
-       wl1271_error("SPI read busy-word timeout!\n");
+       dev_err(child->parent, "SPI read busy-word timeout!\n");
        return -ETIMEDOUT;
 }
 
-static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
+static void wl12xx_spi_raw_read(struct device *child, int addr, void *buf,
                                size_t len, bool fixed)
 {
+       struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
+       struct wl1271 *wl = dev_get_drvdata(child);
        struct spi_transfer t[2];
        struct spi_message m;
        u32 *busy_buf;
@@ -243,10 +235,10 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
                t[1].cs_change = true;
                spi_message_add_tail(&t[1], &m);
 
-               spi_sync(wl_to_spi(wl), &m);
+               spi_sync(to_spi_device(glue->dev), &m);
 
                if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) &&
-                   wl1271_spi_read_busy(wl)) {
+                   wl12xx_spi_read_busy(child)) {
                        memset(buf, 0, chunk_len);
                        return;
                }
@@ -259,10 +251,7 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
                t[0].cs_change = true;
                spi_message_add_tail(&t[0], &m);
 
-               spi_sync(wl_to_spi(wl), &m);
-
-               wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
-               wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, chunk_len);
+               spi_sync(to_spi_device(glue->dev), &m);
 
                if (!fixed)
                        addr += chunk_len;
@@ -271,9 +260,10 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
        }
 }
 
-static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
-                         size_t len, bool fixed)
+static void wl12xx_spi_raw_write(struct device *child, int addr, void *buf,
+                                size_t len, bool fixed)
 {
+       struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
        struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
        struct spi_message m;
        u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
@@ -308,9 +298,6 @@ static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
                t[i].len = chunk_len;
                spi_message_add_tail(&t[i++], &m);
 
-               wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd));
-               wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, chunk_len);
-
                if (!fixed)
                        addr += chunk_len;
                buf += chunk_len;
@@ -318,72 +305,41 @@ static void wl1271_spi_raw_write(struct wl1271 *wl, int addr, void *buf,
                cmd++;
        }
 
-       spi_sync(wl_to_spi(wl), &m);
-}
-
-static irqreturn_t wl1271_hardirq(int irq, void *cookie)
-{
-       struct wl1271 *wl = cookie;
-       unsigned long flags;
-
-       wl1271_debug(DEBUG_IRQ, "IRQ");
-
-       /* complete the ELP completion */
-       spin_lock_irqsave(&wl->wl_lock, flags);
-       set_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
-       if (wl->elp_compl) {
-               complete(wl->elp_compl);
-               wl->elp_compl = NULL;
-       }
-       spin_unlock_irqrestore(&wl->wl_lock, flags);
-
-       return IRQ_WAKE_THREAD;
-}
-
-static int wl1271_spi_set_power(struct wl1271 *wl, bool enable)
-{
-       if (wl->set_power)
-               wl->set_power(enable);
-
-       return 0;
+       spi_sync(to_spi_device(glue->dev), &m);
 }
 
 static struct wl1271_if_operations spi_ops = {
-       .read           = wl1271_spi_raw_read,
-       .write          = wl1271_spi_raw_write,
-       .reset          = wl1271_spi_reset,
-       .init           = wl1271_spi_init,
-       .power          = wl1271_spi_set_power,
-       .dev            = wl1271_spi_wl_to_dev,
-       .enable_irq     = wl1271_spi_enable_interrupts,
-       .disable_irq    = wl1271_spi_disable_interrupts,
+       .read           = wl12xx_spi_raw_read,
+       .write          = wl12xx_spi_raw_write,
+       .reset          = wl12xx_spi_reset,
+       .init           = wl12xx_spi_init,
        .set_block_size = NULL,
 };
 
 static int __devinit wl1271_probe(struct spi_device *spi)
 {
+       struct wl12xx_spi_glue *glue;
        struct wl12xx_platform_data *pdata;
-       struct ieee80211_hw *hw;
-       struct wl1271 *wl;
-       unsigned long irqflags;
-       int ret;
+       struct resource res[1];
+       int ret = -ENOMEM;
 
        pdata = spi->dev.platform_data;
        if (!pdata) {
-               wl1271_error("no platform data");
+               dev_err(&spi->dev, "no platform data\n");
                return -ENODEV;
        }
 
-       hw = wl1271_alloc_hw();
-       if (IS_ERR(hw))
-               return PTR_ERR(hw);
+       pdata->ops = &spi_ops;
 
-       wl = hw->priv;
+       glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+       if (!glue) {
+               dev_err(&spi->dev, "can't allocate glue\n");
+               goto out;
+       }
 
-       dev_set_drvdata(&spi->dev, wl);
-       wl->if_priv = spi;
+       glue->dev = &spi->dev;
 
-       wl->if_ops = &spi_ops;
+       spi_set_drvdata(spi, glue);
 
        /* This is the only SPI value that we need to set here, the rest
         * comes from the board-peripherals file */
@@ -391,69 +347,61 @@ static int __devinit wl1271_probe(struct spi_device *spi)
 
        ret = spi_setup(spi);
        if (ret < 0) {
-               wl1271_error("spi_setup failed");
-               goto out_free;
+               dev_err(glue->dev, "spi_setup failed\n");
+               goto out_free_glue;
        }
 
-       wl->set_power = pdata->set_power;
-       if (!wl->set_power) {
-               wl1271_error("set power function missing in platform data");
-               ret = -ENODEV;
-               goto out_free;
+       glue->core = platform_device_alloc("wl12xx", -1);
+       if (!glue->core) {
+               dev_err(glue->dev, "can't allocate platform_device\n");
+               ret = -ENOMEM;
+               goto out_free_glue;
        }
 
-       wl->ref_clock = pdata->board_ref_clock;
-       wl->tcxo_clock = pdata->board_tcxo_clock;
-       wl->platform_quirks = pdata->platform_quirks;
+       glue->core->dev.parent = &spi->dev;
 
-       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
-               irqflags = IRQF_TRIGGER_RISING;
-       else
-               irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+       memset(res, 0x00, sizeof(res));
 
-       wl->irq = spi->irq;
-       if (wl->irq < 0) {
-               wl1271_error("irq missing in platform data");
-               ret = -ENODEV;
-               goto out_free;
-       }
+       res[0].start = spi->irq;
+       res[0].flags = IORESOURCE_IRQ;
+       res[0].name = "irq";
 
-       ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq,
-                                  irqflags,
-                                  DRIVER_NAME, wl);
-       if (ret < 0) {
-               wl1271_error("request_irq() failed: %d", ret);
-               goto out_free;
+       ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res));
+       if (ret) {
+               dev_err(glue->dev, "can't add resources\n");
+               goto out_dev_put;
        }
 
-       disable_irq(wl->irq);
-
-       ret = wl1271_init_ieee80211(wl);
-       if (ret)
-               goto out_irq;
+       ret = platform_device_add_data(glue->core, pdata, sizeof(*pdata));
+       if (ret) {
+               dev_err(glue->dev, "can't add platform data\n");
+               goto out_dev_put;
+       }
 
-       ret = wl1271_register_hw(wl);
-       if (ret)
-               goto out_irq;
+       ret = platform_device_add(glue->core);
+       if (ret) {
+               dev_err(glue->dev, "can't register platform device\n");
+               goto out_dev_put;
+       }
 
        return 0;
 
- out_irq:
-       free_irq(wl->irq, wl);
-
- out_free:
-       wl1271_free_hw(wl);
+out_dev_put:
+       platform_device_put(glue->core);
 
+out_free_glue:
+       kfree(glue);
+out:
        return ret;
 }
 
 static int __devexit wl1271_remove(struct spi_device *spi)
 {
-       struct wl1271 *wl = dev_get_drvdata(&spi->dev);
+       struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
 
-       wl1271_unregister_hw(wl);
-       free_irq(wl->irq, wl);
-       wl1271_free_hw(wl);
+       platform_device_del(glue->core);
+       platform_device_put(glue->core);
+       kfree(glue);
 
        return 0;
 }
index 4ae8effaee22828acf9565d47d9ad599f4ac9c00..61fff45686ad3356d72ce46cb43cea9e66a15c60 100644 (file)
@@ -26,6 +26,7 @@
 #include <net/genetlink.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "acx.h"
 #include "reg.h"
 
index bad9e29d49b0145bd3ec5220be6372cc16840c36..fa518a5b7c8b39e4b3c27c28bd38e84d7016990e 100644 (file)
 #include <linux/etherdevice.h>
 
 #include "wl12xx.h"
+#include "debug.h"
 #include "io.h"
 #include "reg.h"
 #include "ps.h"
 #include "tx.h"
 #include "event.h"
 
-static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id)
+static int wl1271_set_default_wep_key(struct wl1271 *wl,
+                                     struct wl12xx_vif *wlvif, u8 id)
 {
        int ret;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
 
        if (is_ap)
                ret = wl12xx_cmd_set_default_wep_key(wl, id,
-                                                    wl->ap_bcast_hlid);
+                                                    wlvif->ap.bcast_hlid);
        else
-               ret = wl12xx_cmd_set_default_wep_key(wl, id, wl->sta_hlid);
+               ret = wl12xx_cmd_set_default_wep_key(wl, id, wlvif->sta.hlid);
 
        if (ret < 0)
                return ret;
@@ -76,7 +78,8 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
 }
 
 static int wl1271_tx_update_filters(struct wl1271 *wl,
-                                                struct sk_buff *skb)
+                                   struct wl12xx_vif *wlvif,
+                                   struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr;
        int ret;
@@ -92,15 +95,11 @@ static int wl1271_tx_update_filters(struct wl1271 *wl,
        if (!ieee80211_is_auth(hdr->frame_control))
                return 0;
 
-       if (wl->dev_hlid != WL12XX_INVALID_LINK_ID)
+       if (wlvif->dev_hlid != WL12XX_INVALID_LINK_ID)
                goto out;
 
        wl1271_debug(DEBUG_CMD, "starting device role for roaming");
-       ret = wl12xx_cmd_role_start_dev(wl);
-       if (ret < 0)
-               goto out;
-
-       ret = wl12xx_roc(wl, wl->dev_role_id);
+       ret = wl12xx_start_dev(wl, wlvif);
        if (ret < 0)
                goto out;
 out:
@@ -123,18 +122,16 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
                wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
 }
 
-static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
+static void wl1271_tx_regulate_link(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif,
+                                   u8 hlid)
 {
        bool fw_ps, single_sta;
        u8 tx_pkts;
 
-       /* only regulate station links */
-       if (hlid < WL1271_AP_STA_HLID_START)
+       if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
                return;
 
-       if (WARN_ON(!wl1271_is_active_sta(wl, hlid)))
-           return;
-
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
        single_sta = (wl->active_sta_count == 1);
@@ -146,7 +143,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
         * case FW-memory congestion is not a problem.
         */
        if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
-               wl1271_ps_link_start(wl, hlid, true);
+               wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
 bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
@@ -154,7 +151,8 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
        return wl->dummy_packet == skb;
 }
 
-u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb)
+u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                        struct sk_buff *skb)
 {
        struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb);
 
@@ -167,49 +165,51 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb)
        } else {
                struct ieee80211_hdr *hdr;
 
-               if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags))
+               if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))
                        return wl->system_hlid;
 
                hdr = (struct ieee80211_hdr *)skb->data;
                if (ieee80211_is_mgmt(hdr->frame_control))
-                       return wl->ap_global_hlid;
+                       return wlvif->ap.global_hlid;
                else
-                       return wl->ap_bcast_hlid;
+                       return wlvif->ap.bcast_hlid;
        }
 }
 
-static u8 wl1271_tx_get_hlid(struct wl1271 *wl, struct sk_buff *skb)
+u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       if (wl12xx_is_dummy_packet(wl, skb))
+       if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
                return wl->system_hlid;
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS)
-               return wl12xx_tx_get_hlid_ap(wl, skb);
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               return wl12xx_tx_get_hlid_ap(wl, wlvif, skb);
 
-       wl1271_tx_update_filters(wl, skb);
+       wl1271_tx_update_filters(wl, wlvif, skb);
 
-       if ((test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) ||
-            test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags)) &&
+       if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
+            test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
            !ieee80211_is_auth(hdr->frame_control) &&
            !ieee80211_is_assoc_req(hdr->frame_control))
-               return wl->sta_hlid;
+               return wlvif->sta.hlid;
        else
-               return wl->dev_hlid;
+               return wlvif->dev_hlid;
 }
 
 static unsigned int wl12xx_calc_packet_alignment(struct wl1271 *wl,
                                                unsigned int packet_length)
 {
-       if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT)
-               return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
-       else
+       if (wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT)
                return ALIGN(packet_length, WL1271_TX_ALIGN_TO);
+       else
+               return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
 }
 
-static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
-                               u32 buf_offset, u8 hlid)
+static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct sk_buff *skb, u32 extra, u32 buf_offset,
+                             u8 hlid)
 {
        struct wl1271_tx_hw_descr *desc;
        u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
@@ -217,6 +217,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
        u32 total_blocks;
        int id, ret = -EBUSY, ac;
        u32 spare_blocks = wl->tx_spare_blocks;
+       bool is_dummy = false;
 
        if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
                return -EAGAIN;
@@ -231,8 +232,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
        len = wl12xx_calc_packet_alignment(wl, total_len);
 
        /* in case of a dummy packet, use default amount of spare mem blocks */
-       if (unlikely(wl12xx_is_dummy_packet(wl, skb)))
+       if (unlikely(wl12xx_is_dummy_packet(wl, skb))) {
+               is_dummy = true;
                spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
+       }
 
        total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE +
                spare_blocks;
@@ -257,8 +260,9 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                wl->tx_allocated_pkts[ac]++;
 
-               if (wl->bss_type == BSS_TYPE_AP_BSS &&
-                   hlid >= WL1271_AP_STA_HLID_START)
+               if (!is_dummy && wlvif &&
+                   wlvif->bss_type == BSS_TYPE_AP_BSS &&
+                   test_bit(hlid, wlvif->ap.sta_hlid_map))
                        wl->links[hlid].allocated_pkts++;
 
                ret = 0;
@@ -273,15 +277,16 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra,
        return ret;
 }
 
-static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
-                             u32 extra, struct ieee80211_tx_info *control,
-                             u8 hlid)
+static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              struct sk_buff *skb, u32 extra,
+                              struct ieee80211_tx_info *control, u8 hlid)
 {
        struct timespec ts;
        struct wl1271_tx_hw_descr *desc;
        int aligned_len, ac, rate_idx;
        s64 hosttime;
        u16 tx_attr;
+       bool is_dummy;
 
        desc = (struct wl1271_tx_hw_descr *) skb->data;
 
@@ -298,7 +303,8 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
        hosttime = (timespec_to_ns(&ts) >> 10);
        desc->start_time = cpu_to_le32(hosttime - wl->time_offset);
 
-       if (wl->bss_type != BSS_TYPE_AP_BSS)
+       is_dummy = wl12xx_is_dummy_packet(wl, skb);
+       if (is_dummy || !wlvif || wlvif->bss_type != BSS_TYPE_AP_BSS)
                desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
        else
                desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);
@@ -307,39 +313,42 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
        ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
        desc->tid = skb->priority;
 
-       if (wl12xx_is_dummy_packet(wl, skb)) {
+       if (is_dummy) {
                /*
                 * FW expects the dummy packet to have an invalid session id -
                 * any session id that is different than the one set in the join
                 */
-               tx_attr = ((~wl->session_counter) <<
+               tx_attr = (SESSION_COUNTER_INVALID <<
                           TX_HW_ATTR_OFST_SESSION_COUNTER) &
                           TX_HW_ATTR_SESSION_COUNTER;
 
                tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
-       } else {
+       } else if (wlvif) {
                /* configure the tx attributes */
-               tx_attr =
-                       wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
+               tx_attr = wlvif->session_counter <<
+                         TX_HW_ATTR_OFST_SESSION_COUNTER;
        }
 
        desc->hlid = hlid;
-
-       if (wl->bss_type != BSS_TYPE_AP_BSS) {
+       if (is_dummy || !wlvif)
+               rate_idx = 0;
+       else if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
                /* 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)
-                       rate_idx = ACX_TX_AP_FULL_RATE;
+               if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+                       rate_idx = wlvif->sta.p2p_rate_idx;
+               else if (control->control.sta)
+                       rate_idx = wlvif->sta.ap_rate_idx;
                else
-                       rate_idx = ACX_TX_BASIC_RATE;
+                       rate_idx = wlvif->sta.basic_rate_idx;
        } else {
-               if (hlid == wl->ap_global_hlid)
-                       rate_idx = ACX_TX_AP_MODE_MGMT_RATE;
-               else if (hlid == wl->ap_bcast_hlid)
-                       rate_idx = ACX_TX_AP_MODE_BCST_RATE;
+               if (hlid == wlvif->ap.global_hlid)
+                       rate_idx = wlvif->ap.mgmt_rate_idx;
+               else if (hlid == wlvif->ap.bcast_hlid)
+                       rate_idx = wlvif->ap.bcast_rate_idx;
                else
-                       rate_idx = ac;
+                       rate_idx = wlvif->ap.ucast_rate_idx[ac];
        }
 
        tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
@@ -379,20 +388,24 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
 }
 
 /* caller must hold wl->mutex */
-static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
-                                                       u32 buf_offset)
+static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                  struct sk_buff *skb, u32 buf_offset)
 {
        struct ieee80211_tx_info *info;
        u32 extra = 0;
        int ret = 0;
        u32 total_len;
        u8 hlid;
+       bool is_dummy;
 
        if (!skb)
                return -EINVAL;
 
        info = IEEE80211_SKB_CB(skb);
 
+       /* TODO: handle dummy packets on multi-vifs */
+       is_dummy = wl12xx_is_dummy_packet(wl, skb);
+
        if (info->control.hw_key &&
            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
                extra = WL1271_TKIP_IV_SPACE;
@@ -405,29 +418,36 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
                is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
                         (cipher == WLAN_CIPHER_SUITE_WEP104);
 
-               if (unlikely(is_wep && wl->default_key != idx)) {
-                       ret = wl1271_set_default_wep_key(wl, idx);
+               if (unlikely(is_wep && wlvif->default_key != idx)) {
+                       ret = wl1271_set_default_wep_key(wl, wlvif, idx);
                        if (ret < 0)
                                return ret;
-                       wl->default_key = idx;
+                       wlvif->default_key = idx;
                }
        }
-
-       hlid = wl1271_tx_get_hlid(wl, skb);
+       hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
        if (hlid == WL12XX_INVALID_LINK_ID) {
-               wl1271_error("invalid hlid. dropping skb 0x%p", skb);
+               if (wlvif->bss_type == BSS_TYPE_IBSS &&
+                   !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) {
+                       /* It's ok to drop packets when not joined to IBSS */
+                       wl1271_debug(DEBUG_TX, "dropping skb while IBSS not "
+                                    " joined");
+               } else {
+                       wl1271_error("invalid hlid. dropping skb 0x%p", skb);
+               }
+
                return -EINVAL;
        }
 
-       ret = wl1271_tx_allocate(wl, skb, extra, buf_offset, hlid);
+       ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid);
        if (ret < 0)
                return ret;
 
-       wl1271_tx_fill_hdr(wl, skb, extra, info, hlid);
+       wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid);
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
+       if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
                wl1271_tx_ap_update_inconnection_sta(wl, skb);
-               wl1271_tx_regulate_link(wl, hlid);
+               wl1271_tx_regulate_link(wl, wlvif, hlid);
        }
 
        /*
@@ -444,7 +464,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
        memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len);
 
        /* Revert side effects in the dummy packet skb, so it can be reused */
-       if (wl12xx_is_dummy_packet(wl, skb))
+       if (is_dummy)
                skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
 
        return total_len;
@@ -522,19 +542,18 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
        return &queues[q];
 }
 
-static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl)
+static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
+                                             struct wl1271_link *lnk)
 {
-       struct sk_buff *skb = NULL;
+       struct sk_buff *skb;
        unsigned long flags;
        struct sk_buff_head *queue;
 
-       queue = wl1271_select_queue(wl, wl->tx_queue);
+       queue = wl1271_select_queue(wl, lnk->tx_queue);
        if (!queue)
-               goto out;
+               return NULL;
 
        skb = skb_dequeue(queue);
-
-out:
        if (skb) {
                int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                spin_lock_irqsave(&wl->wl_lock, flags);
@@ -545,43 +564,33 @@ out:
        return skb;
 }
 
-static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl)
+static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
+                                             struct wl12xx_vif *wlvif)
 {
        struct sk_buff *skb = NULL;
-       unsigned long flags;
        int i, h, start_hlid;
-       struct sk_buff_head *queue;
 
        /* start from the link after the last one */
-       start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS;
+       start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS;
 
        /* dequeue according to AC, round robin on each link */
-       for (i = 0; i < AP_MAX_LINKS; i++) {
-               h = (start_hlid + i) % AP_MAX_LINKS;
+       for (i = 0; i < WL12XX_MAX_LINKS; i++) {
+               h = (start_hlid + i) % WL12XX_MAX_LINKS;
 
                /* only consider connected stations */
-               if (h >= WL1271_AP_STA_HLID_START &&
-                   !test_bit(h - WL1271_AP_STA_HLID_START, wl->ap_hlid_map))
+               if (!test_bit(h, wlvif->links_map))
                        continue;
 
-               queue = wl1271_select_queue(wl, wl->links[h].tx_queue);
-               if (!queue)
+               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
+               if (!skb)
                        continue;
 
-               skb = skb_dequeue(queue);
-               if (skb)
-                       break;
+               wlvif->last_tx_hlid = h;
+               break;
        }
 
-       if (skb) {
-               int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
-               wl->last_tx_hlid = h;
-               spin_lock_irqsave(&wl->wl_lock, flags);
-               wl->tx_queue_count[q]--;
-               spin_unlock_irqrestore(&wl->wl_lock, flags);
-       } else {
-               wl->last_tx_hlid = 0;
-       }
+       if (!skb)
+               wlvif->last_tx_hlid = 0;
 
        return skb;
 }
@@ -589,12 +598,32 @@ static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl)
 static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
 {
        unsigned long flags;
+       struct wl12xx_vif *wlvif = wl->last_wlvif;
        struct sk_buff *skb = NULL;
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS)
-               skb = wl1271_ap_skb_dequeue(wl);
-       else
-               skb = wl1271_sta_skb_dequeue(wl);
+       if (wlvif) {
+               wl12xx_for_each_wlvif_continue(wl, wlvif) {
+                       skb = wl12xx_vif_skb_dequeue(wl, wlvif);
+                       if (skb) {
+                               wl->last_wlvif = wlvif;
+                               break;
+                       }
+               }
+       }
+
+       /* do another pass */
+       if (!skb) {
+               wl12xx_for_each_wlvif(wl, wlvif) {
+                       skb = wl12xx_vif_skb_dequeue(wl, wlvif);
+                       if (skb) {
+                               wl->last_wlvif = wlvif;
+                               break;
+                       }
+               }
+       }
+
+       if (!skb)
+               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
 
        if (!skb &&
            test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
@@ -610,21 +639,21 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
        return skb;
 }
 
-static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb)
+static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                 struct sk_buff *skb)
 {
        unsigned long flags;
        int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
 
        if (wl12xx_is_dummy_packet(wl, skb)) {
                set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
-       } else if (wl->bss_type == BSS_TYPE_AP_BSS) {
-               u8 hlid = wl1271_tx_get_hlid(wl, skb);
+       } else {
+               u8 hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
                skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
 
                /* make sure we dequeue the same packet next time */
-               wl->last_tx_hlid = (hlid + AP_MAX_LINKS - 1) % AP_MAX_LINKS;
-       } else {
-               skb_queue_head(&wl->tx_queue[q], skb);
+               wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) %
+                                     WL12XX_MAX_LINKS;
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
@@ -639,29 +668,71 @@ static bool wl1271_tx_is_data_present(struct sk_buff *skb)
        return ieee80211_is_data_present(hdr->frame_control);
 }
 
+void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids)
+{
+       struct wl12xx_vif *wlvif;
+       u32 timeout;
+       u8 hlid;
+
+       if (!wl->conf.rx_streaming.interval)
+               return;
+
+       if (!wl->conf.rx_streaming.always &&
+           !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))
+               return;
+
+       timeout = wl->conf.rx_streaming.duration;
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               bool found = false;
+               for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) {
+                       if (test_bit(hlid, wlvif->links_map)) {
+                               found  = true;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       continue;
+
+               /* enable rx streaming */
+               if (!test_bit(WLVIF_FLAG_RX_STREAMING_STARTED, &wlvif->flags))
+                       ieee80211_queue_work(wl->hw,
+                                            &wlvif->rx_streaming_enable_work);
+
+               mod_timer(&wlvif->rx_streaming_timer,
+                         jiffies + msecs_to_jiffies(timeout));
+       }
+}
+
 void wl1271_tx_work_locked(struct wl1271 *wl)
 {
+       struct wl12xx_vif *wlvif;
        struct sk_buff *skb;
+       struct wl1271_tx_hw_descr *desc;
        u32 buf_offset = 0;
        bool sent_packets = false;
-       bool had_data = false;
-       bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+       unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
        int ret;
 
        if (unlikely(wl->state == WL1271_STATE_OFF))
                return;
 
        while ((skb = wl1271_skb_dequeue(wl))) {
-               if (wl1271_tx_is_data_present(skb))
-                       had_data = true;
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               bool has_data = false;
+
+               wlvif = NULL;
+               if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
+                       wlvif = wl12xx_vif_to_data(info->control.vif);
 
-               ret = wl1271_prepare_tx_frame(wl, skb, buf_offset);
+               has_data = wlvif && wl1271_tx_is_data_present(skb);
+               ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset);
                if (ret == -EAGAIN) {
                        /*
                         * Aggregation buffer is full.
                         * Flush buffer and try again.
                         */
-                       wl1271_skb_queue_head(wl, skb);
+                       wl1271_skb_queue_head(wl, wlvif, skb);
                        wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf,
                                     buf_offset, true);
                        sent_packets = true;
@@ -672,7 +743,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
                         * Firmware buffer is full.
                         * Queue back last skb, and stop aggregating.
                         */
-                       wl1271_skb_queue_head(wl, skb);
+                       wl1271_skb_queue_head(wl, wlvif, skb);
                        /* No work left, avoid scheduling redundant tx work */
                        set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
                        goto out_ack;
@@ -682,6 +753,10 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
                }
                buf_offset += ret;
                wl->tx_packets_count++;
+               if (has_data) {
+                       desc = (struct wl1271_tx_hw_descr *) skb->data;
+                       __set_bit(desc->hlid, active_hlids);
+               }
        }
 
 out_ack:
@@ -701,19 +776,7 @@ out_ack:
 
                wl1271_handle_tx_low_watermark(wl);
        }
-       if (!is_ap && wl->conf.rx_streaming.interval && had_data &&
-           (wl->conf.rx_streaming.always ||
-            test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) {
-               u32 timeout = wl->conf.rx_streaming.duration;
-
-               /* enable rx streaming */
-               if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags))
-                       ieee80211_queue_work(wl->hw,
-                                            &wl->rx_streaming_enable_work);
-
-               mod_timer(&wl->rx_streaming_timer,
-                         jiffies + msecs_to_jiffies(timeout));
-       }
+       wl12xx_rearm_rx_streaming(wl, active_hlids);
 }
 
 void wl1271_tx_work(struct work_struct *work)
@@ -737,6 +800,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
                                      struct wl1271_tx_hw_res_descr *result)
 {
        struct ieee80211_tx_info *info;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
        struct sk_buff *skb;
        int id = result->id;
        int rate = -1;
@@ -756,11 +821,16 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
                return;
        }
 
+       /* info->control is valid as long as we don't update info->status */
+       vif = info->control.vif;
+       wlvif = wl12xx_vif_to_data(vif);
+
        /* update the TX status info */
        if (result->status == TX_SUCCESS) {
                if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
                        info->flags |= IEEE80211_TX_STAT_ACK;
-               rate = wl1271_rate_to_idx(result->rate_class_index, wl->band);
+               rate = wl1271_rate_to_idx(result->rate_class_index,
+                                         wlvif->band);
                retries = result->ack_failures;
        } else if (result->status == TX_RETRY_EXCEEDED) {
                wl->stats.excessive_retries++;
@@ -783,14 +853,14 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
             info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP ||
             info->control.hw_key->cipher == WL1271_CIPHER_SUITE_GEM)) {
                u8 fw_lsb = result->tx_security_sequence_number_lsb;
-               u8 cur_lsb = wl->tx_security_last_seq_lsb;
+               u8 cur_lsb = wlvif->tx_security_last_seq_lsb;
 
                /*
                 * update security sequence number, taking care of potential
                 * wrap-around
                 */
-               wl->tx_security_seq += (fw_lsb - cur_lsb + 256) % 256;
-               wl->tx_security_last_seq_lsb = fw_lsb;
+               wlvif->tx_security_seq += (fw_lsb - cur_lsb) & 0xff;
+               wlvif->tx_security_last_seq_lsb = fw_lsb;
        }
 
        /* remove private header from packet */
@@ -886,39 +956,30 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
 }
 
 /* caller must hold wl->mutex and TX must be stopped */
-void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
+void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int i;
-       struct sk_buff *skb;
-       struct ieee80211_tx_info *info;
 
        /* TX failure */
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
-               for (i = 0; i < AP_MAX_LINKS; i++) {
-                       wl1271_free_sta(wl, i);
-                       wl1271_tx_reset_link_queues(wl, i);
-                       wl->links[i].allocated_pkts = 0;
-                       wl->links[i].prev_freed_pkts = 0;
-               }
-
-               wl->last_tx_hlid = 0;
-       } else {
-               for (i = 0; i < NUM_TX_QUEUES; i++) {
-                       while ((skb = skb_dequeue(&wl->tx_queue[i]))) {
-                               wl1271_debug(DEBUG_TX, "freeing skb 0x%p",
-                                            skb);
-
-                               if (!wl12xx_is_dummy_packet(wl, skb)) {
-                                       info = IEEE80211_SKB_CB(skb);
-                                       info->status.rates[0].idx = -1;
-                                       info->status.rates[0].count = 0;
-                                       ieee80211_tx_status_ni(wl->hw, skb);
-                               }
-                       }
-               }
+       for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
+               if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+                       wl1271_free_sta(wl, wlvif, i);
+               else
+                       wlvif->sta.ba_rx_bitmap = 0;
 
-               wl->ba_rx_bitmap = 0;
+               wl1271_tx_reset_link_queues(wl, i);
+               wl->links[i].allocated_pkts = 0;
+               wl->links[i].prev_freed_pkts = 0;
        }
+       wlvif->last_tx_hlid = 0;
+
+}
+/* caller must hold wl->mutex and TX must be stopped */
+void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
+{
+       int i;
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *info;
 
        for (i = 0; i < NUM_TX_QUEUES; i++)
                wl->tx_queue_count[i] = 0;
index dc4f09adf088541b5ae642f84a7cfac960e9b3b7..2dbb24e6d541a8c9dbff30efd9144f922217fb92 100644 (file)
@@ -206,18 +206,23 @@ static inline int wl1271_tx_total_queue_count(struct wl1271 *wl)
 void wl1271_tx_work(struct work_struct *work);
 void wl1271_tx_work_locked(struct wl1271 *wl);
 void wl1271_tx_complete(struct wl1271 *wl);
-void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
+void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
 void wl1271_tx_flush(struct wl1271 *wl);
 u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
 u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
                                enum ieee80211_band rate_band);
 u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set);
-u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb);
+u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                        struct sk_buff *skb);
+u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct sk_buff *skb);
 void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid);
 void wl1271_handle_tx_low_watermark(struct wl1271 *wl);
 bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb);
+void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
 
 /* from main.c */
-void wl1271_free_sta(struct wl1271 *wl, u8 hlid);
+void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
 
 #endif
index 1ec90fc7505ebd3f0dffd6f8ef5ff9ca0e9118ce..e58e801f7aea9f90c0bc419caf93a4abd930bc4c 100644 (file)
@@ -35,9 +35,6 @@
 #include "conf.h"
 #include "ini.h"
 
-#define DRIVER_NAME "wl1271"
-#define DRIVER_PREFIX DRIVER_NAME ": "
-
 /*
  * FW versions support BA 11n
  * versions marks x.x.x.50-60.x
 #define WL12XX_BA_SUPPORT_FW_COST_VER2_START    50
 #define WL12XX_BA_SUPPORT_FW_COST_VER2_END      60
 
-enum {
-       DEBUG_NONE      = 0,
-       DEBUG_IRQ       = BIT(0),
-       DEBUG_SPI       = BIT(1),
-       DEBUG_BOOT      = BIT(2),
-       DEBUG_MAILBOX   = BIT(3),
-       DEBUG_TESTMODE  = BIT(4),
-       DEBUG_EVENT     = BIT(5),
-       DEBUG_TX        = BIT(6),
-       DEBUG_RX        = BIT(7),
-       DEBUG_SCAN      = BIT(8),
-       DEBUG_CRYPT     = BIT(9),
-       DEBUG_PSM       = BIT(10),
-       DEBUG_MAC80211  = BIT(11),
-       DEBUG_CMD       = BIT(12),
-       DEBUG_ACX       = BIT(13),
-       DEBUG_SDIO      = BIT(14),
-       DEBUG_FILTERS   = BIT(15),
-       DEBUG_ADHOC     = BIT(16),
-       DEBUG_AP        = BIT(17),
-       DEBUG_MASTER    = (DEBUG_ADHOC | DEBUG_AP),
-       DEBUG_ALL       = ~0,
-};
-
-extern u32 wl12xx_debug_level;
-
-#define DEBUG_DUMP_LIMIT 1024
-
-#define wl1271_error(fmt, arg...) \
-       pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
-
-#define wl1271_warning(fmt, arg...) \
-       pr_warning(DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
-
-#define wl1271_notice(fmt, arg...) \
-       pr_info(DRIVER_PREFIX fmt "\n", ##arg)
-
-#define wl1271_info(fmt, arg...) \
-       pr_info(DRIVER_PREFIX fmt "\n", ##arg)
-
-#define wl1271_debug(level, fmt, arg...) \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \
-       } while (0)
-
-/* TODO: use pr_debug_hex_dump when it will be available */
-#define wl1271_dump(level, prefix, buf, len)   \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
-                                      DUMP_PREFIX_OFFSET, 16, 1,       \
-                                      buf,                             \
-                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
-                                      0);                              \
-       } while (0)
-
-#define wl1271_dump_ascii(level, prefix, buf, len)     \
-       do { \
-               if (level & wl12xx_debug_level) \
-                       print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \
-                                      DUMP_PREFIX_OFFSET, 16, 1,       \
-                                      buf,                             \
-                                      min_t(size_t, len, DEBUG_DUMP_LIMIT), \
-                                      true);                           \
-       } while (0)
-
 #define WL127X_FW_NAME "ti-connectivity/wl127x-fw-3.bin"
 #define WL128X_FW_NAME "ti-connectivity/wl128x-fw-3.bin"
 
@@ -142,15 +72,11 @@ extern u32 wl12xx_debug_level;
 #define WL12XX_INVALID_ROLE_ID     0xff
 #define WL12XX_INVALID_LINK_ID     0xff
 
+#define WL12XX_MAX_RATE_POLICIES 16
+
 /* Defined by FW as 0. Will not be freed or allocated. */
 #define WL12XX_SYSTEM_HLID         0
 
-/*
- * TODO: we currently don't support multirole. remove
- * this constant from the code when we do.
- */
-#define WL1271_AP_STA_HLID_START   3
-
 /*
  * When in AP-mode, we allow (at least) this number of packets
  * to be transmitted to FW for a STA in PS-mode. Only when packets are
@@ -236,13 +162,6 @@ struct wl1271_stats {
 
 #define AP_MAX_STATIONS            8
 
-/* Broadcast and Global links + system link + links to stations */
-/*
- * TODO: when WL1271_AP_STA_HLID_START is no longer constant, change all
- * the places that use this.
- */
-#define AP_MAX_LINKS               (AP_MAX_STATIONS + WL1271_AP_STA_HLID_START)
-
 /* FW status registers */
 struct wl12xx_fw_status {
        __le32 intr;
@@ -299,17 +218,14 @@ struct wl1271_scan {
 };
 
 struct wl1271_if_operations {
-       void (*read)(struct wl1271 *wl, int addr, void *buf, size_t len,
+       void (*read)(struct device *child, int addr, void *buf, size_t len,
                     bool fixed);
-       void (*write)(struct wl1271 *wl, int addr, void *buf, size_t len,
+       void (*write)(struct device *child, int addr, void *buf, size_t len,
                     bool fixed);
-       void (*reset)(struct wl1271 *wl);
-       void (*init)(struct wl1271 *wl);
-       int (*power)(struct wl1271 *wl, bool enable);
-       struct device* (*dev)(struct wl1271 *wl);
-       void (*enable_irq)(struct wl1271 *wl);
-       void (*disable_irq)(struct wl1271 *wl);
-       void (*set_block_size) (struct wl1271 *wl, unsigned int blksz);
+       void (*reset)(struct device *child);
+       void (*init)(struct device *child);
+       int (*power)(struct device *child, bool enable);
+       void (*set_block_size) (struct device *child, unsigned int blksz);
 };
 
 #define MAX_NUM_KEYS 14
@@ -326,29 +242,32 @@ struct wl1271_ap_key {
 };
 
 enum wl12xx_flags {
-       WL1271_FLAG_STA_ASSOCIATED,
-       WL1271_FLAG_IBSS_JOINED,
        WL1271_FLAG_GPIO_POWER,
        WL1271_FLAG_TX_QUEUE_STOPPED,
        WL1271_FLAG_TX_PENDING,
        WL1271_FLAG_IN_ELP,
        WL1271_FLAG_ELP_REQUESTED,
-       WL1271_FLAG_PSM,
-       WL1271_FLAG_PSM_REQUESTED,
        WL1271_FLAG_IRQ_RUNNING,
        WL1271_FLAG_IDLE,
-       WL1271_FLAG_PSPOLL_FAILURE,
-       WL1271_FLAG_STA_STATE_SENT,
        WL1271_FLAG_FW_TX_BUSY,
-       WL1271_FLAG_AP_STARTED,
-       WL1271_FLAG_IF_INITIALIZED,
        WL1271_FLAG_DUMMY_PACKET_PENDING,
        WL1271_FLAG_SUSPENDED,
        WL1271_FLAG_PENDING_WORK,
        WL1271_FLAG_SOFT_GEMINI,
-       WL1271_FLAG_RX_STREAMING_STARTED,
        WL1271_FLAG_RECOVERY_IN_PROGRESS,
-       WL1271_FLAG_CS_PROGRESS,
+};
+
+enum wl12xx_vif_flags {
+       WLVIF_FLAG_INITIALIZED,
+       WLVIF_FLAG_STA_ASSOCIATED,
+       WLVIF_FLAG_IBSS_JOINED,
+       WLVIF_FLAG_AP_STARTED,
+       WLVIF_FLAG_PSM,
+       WLVIF_FLAG_PSM_REQUESTED,
+       WLVIF_FLAG_STA_STATE_SENT,
+       WLVIF_FLAG_RX_STREAMING_STARTED,
+       WLVIF_FLAG_PSPOLL_FAILURE,
+       WLVIF_FLAG_CS_PROGRESS,
 };
 
 struct wl1271_link {
@@ -366,10 +285,11 @@ struct wl1271_link {
 };
 
 struct wl1271 {
-       struct platform_device *plat_dev;
        struct ieee80211_hw *hw;
        bool mac80211_registered;
 
+       struct device *dev;
+
        void *if_priv;
 
        struct wl1271_if_operations *if_ops;
@@ -399,25 +319,20 @@ struct wl1271 {
 
        s8 hw_pg_ver;
 
-       u8 bssid[ETH_ALEN];
        u8 mac_addr[ETH_ALEN];
-       u8 bss_type;
-       u8 set_bss_type;
-       u8 p2p; /* we are using p2p role */
-       u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
-       u8 ssid_len;
        int channel;
-       u8 role_id;
-       u8 dev_role_id;
        u8 system_hlid;
-       u8 sta_hlid;
-       u8 dev_hlid;
-       u8 ap_global_hlid;
-       u8 ap_bcast_hlid;
 
        unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
        unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
        unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
+       unsigned long rate_policies_map[
+                       BITS_TO_LONGS(WL12XX_MAX_RATE_POLICIES)];
+
+       struct list_head wlvif_list;
+
+       u8 sta_count;
+       u8 ap_count;
 
        struct wl1271_acx_mem_map *target_mem_map;
 
@@ -440,11 +355,7 @@ struct wl1271 {
        /* Time-offset between host and chipset clocks */
        s64 time_offset;
 
-       /* Session counter for the chipset */
-       int session_counter;
-
        /* Frames scheduled for transmission, not handled yet */
-       struct sk_buff_head tx_queue[NUM_TX_QUEUES];
        int tx_queue_count[NUM_TX_QUEUES];
        long stopped_queues_map;
 
@@ -462,17 +373,6 @@ struct wl1271 {
        struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];
        int tx_frames_cnt;
 
-       /*
-        * Security sequence number
-        *     bits 0-15: lower 16 bits part of sequence number
-        *     bits 16-47: higher 32 bits part of sequence number
-        *     bits 48-63: not in use
-        */
-       u64 tx_security_seq;
-
-       /* 8 bits of the last sequence number in use */
-       u8 tx_security_last_seq_lsb;
-
        /* FW Rx counter */
        u32 rx_counter;
 
@@ -507,59 +407,21 @@ struct wl1271 {
        u32 mbox_ptr[2];
 
        /* Are we currently scanning */
+       struct ieee80211_vif *scan_vif;
        struct wl1271_scan scan;
        struct delayed_work scan_complete_work;
 
        bool sched_scanning;
 
-       /* probe-req template for the current AP */
-       struct sk_buff *probereq;
-
-       /* Our association ID */
-       u16 aid;
-
-       /*
-        * currently configured rate set:
-        *      bits  0-15 - 802.11abg rates
-        *      bits 16-23 - 802.11n   MCS index mask
-        * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
-        */
-       u32 basic_rate_set;
-       u32 basic_rate;
-       u32 rate_set;
-       u32 bitrate_masks[IEEE80211_NUM_BANDS];
-
        /* The current band */
        enum ieee80211_band band;
 
-       /* Beaconing interval (needed for ad-hoc) */
-       u32 beacon_int;
-
-       /* Default key (for WEP) */
-       u32 default_key;
-
-       /* Rx Streaming */
-       struct work_struct rx_streaming_enable_work;
-       struct work_struct rx_streaming_disable_work;
-       struct timer_list rx_streaming_timer;
-
        struct completion *elp_compl;
-       struct completion *ps_compl;
        struct delayed_work elp_work;
-       struct delayed_work pspoll_work;
-
-       /* counter for ps-poll delivery failures */
-       int ps_poll_failures;
-
-       /* retry counter for PSM entries */
-       u8 psm_entry_retry;
 
        /* in dBm */
        int power_level;
 
-       int rssi_thold;
-       int last_rssi_event;
-
        struct wl1271_stats stats;
 
        __le32 buffer_32;
@@ -583,20 +445,9 @@ struct wl1271 {
        /* Most recently reported noise in dBm */
        s8 noise;
 
-       /* map for HLIDs of associated stations - when operating in AP mode */
-       unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
-
-       /* recoreded keys for AP-mode - set here before AP startup */
-       struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS];
-
        /* bands supported by this instance of wl12xx */
        struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
 
-       /* RX BA constraint value */
-       bool ba_support;
-       u8 ba_rx_bitmap;
-       bool ba_allowed;
-
        int tcxo_clock;
 
        /*
@@ -610,10 +461,7 @@ struct wl1271 {
         * AP-mode - links indexed by HLID. The global and broadcast links
         * are always active.
         */
-       struct wl1271_link links[AP_MAX_LINKS];
-
-       /* the hlid of the link where the last transmitted skb came from */
-       int last_tx_hlid;
+       struct wl1271_link links[WL12XX_MAX_LINKS];
 
        /* AP-mode - a bitmap of links currently in PS mode according to FW */
        u32 ap_fw_ps_map;
@@ -632,21 +480,173 @@ struct wl1271 {
 
        /* AP-mode - number of currently connected stations */
        int active_sta_count;
+
+       /* last wlvif we transmitted from */
+       struct wl12xx_vif *last_wlvif;
 };
 
 struct wl1271_station {
        u8 hlid;
 };
 
+struct wl12xx_vif {
+       struct wl1271 *wl;
+       struct list_head list;
+       unsigned long flags;
+       u8 bss_type;
+       u8 p2p; /* we are using p2p role */
+       u8 role_id;
+
+       /* sta/ibss specific */
+       u8 dev_role_id;
+       u8 dev_hlid;
+
+       union {
+               struct {
+                       u8 hlid;
+                       u8 ba_rx_bitmap;
+
+                       u8 basic_rate_idx;
+                       u8 ap_rate_idx;
+                       u8 p2p_rate_idx;
+               } sta;
+               struct {
+                       u8 global_hlid;
+                       u8 bcast_hlid;
+
+                       /* HLIDs bitmap of associated stations */
+                       unsigned long sta_hlid_map[BITS_TO_LONGS(
+                                                       WL12XX_MAX_LINKS)];
+
+                       /* recoreded keys - set here before AP startup */
+                       struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
+
+                       u8 mgmt_rate_idx;
+                       u8 bcast_rate_idx;
+                       u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT];
+               } ap;
+       };
+
+       /* the hlid of the last transmitted skb */
+       int last_tx_hlid;
+
+       unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+       u8 ssid_len;
+
+       /* The current band */
+       enum ieee80211_band band;
+       int channel;
+
+       u32 bitrate_masks[IEEE80211_NUM_BANDS];
+       u32 basic_rate_set;
+
+       /*
+        * currently configured rate set:
+        *      bits  0-15 - 802.11abg rates
+        *      bits 16-23 - 802.11n   MCS index mask
+        * support only 1 stream, thus only 8 bits for the MCS rates (0-7).
+        */
+       u32 basic_rate;
+       u32 rate_set;
+
+       /* probe-req template for the current AP */
+       struct sk_buff *probereq;
+
+       /* Beaconing interval (needed for ad-hoc) */
+       u32 beacon_int;
+
+       /* Default key (for WEP) */
+       u32 default_key;
+
+       /* Our association ID */
+       u16 aid;
+
+       /* Session counter for the chipset */
+       int session_counter;
+
+       struct completion *ps_compl;
+       struct delayed_work pspoll_work;
+
+       /* counter for ps-poll delivery failures */
+       int ps_poll_failures;
+
+       /* retry counter for PSM entries */
+       u8 psm_entry_retry;
+
+       /* in dBm */
+       int power_level;
+
+       int rssi_thold;
+       int last_rssi_event;
+
+       /* RX BA constraint value */
+       bool ba_support;
+       bool ba_allowed;
+
+       /* Rx Streaming */
+       struct work_struct rx_streaming_enable_work;
+       struct work_struct rx_streaming_disable_work;
+       struct timer_list rx_streaming_timer;
+
+       /*
+        * This struct must be last!
+        * data that has to be saved acrossed reconfigs (e.g. recovery)
+        * should be declared in this struct.
+        */
+       struct {
+               u8 persistent[0];
+               /*
+                * Security sequence number
+                *     bits 0-15: lower 16 bits part of sequence number
+                *     bits 16-47: higher 32 bits part of sequence number
+                *     bits 48-63: not in use
+                */
+               u64 tx_security_seq;
+
+               /* 8 bits of the last sequence number in use */
+               u8 tx_security_last_seq_lsb;
+       };
+};
+
+static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
+{
+       return (struct wl12xx_vif *)vif->drv_priv;
+}
+
+static inline
+struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif)
+{
+       return container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
+}
+
+#define wl12xx_for_each_wlvif(wl, wlvif) \
+               list_for_each_entry(wlvif, &wl->wlvif_list, list)
+
+#define wl12xx_for_each_wlvif_continue(wl, wlvif) \
+               list_for_each_entry_continue(wlvif, &wl->wlvif_list, list)
+
+#define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type)   \
+               wl12xx_for_each_wlvif(wl, wlvif)                \
+                       if (wlvif->bss_type == _bss_type)
+
+#define wl12xx_for_each_wlvif_sta(wl, wlvif)   \
+               wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS)
+
+#define wl12xx_for_each_wlvif_ap(wl, wlvif)    \
+               wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS)
+
 int wl1271_plt_start(struct wl1271 *wl);
 int wl1271_plt_stop(struct wl1271 *wl);
-int wl1271_recalc_rx_streaming(struct wl1271 *wl);
+int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl12xx_queue_recovery_work(struct wl1271 *wl);
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
 
 #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */
 
-#define SESSION_COUNTER_MAX 7 /* maximum value for the session counter */
+#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */
+#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */
 
 #define WL1271_DEFAULT_POWER_LEVEL 0
 
@@ -669,8 +669,8 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen);
 /* Each RX/TX transaction requires an end-of-transaction transfer */
 #define WL12XX_QUIRK_END_OF_TRANSACTION                BIT(0)
 
-/* WL128X requires aggregated packets to be aligned to the SDIO block size */
-#define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT       BIT(2)
+/* wl127x and SPI don't support SDIO block size alignment */
+#define WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT    BIT(2)
 
 /* Older firmwares did not implement the FW logger over bus feature */
 #define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED     BIT(4)
index 973b11060a8f2674d2ff75b72209c51d4ec95ace..3c96b332184ee059c91ef3452e498a9e7ad7223d 100644 (file)
@@ -2,7 +2,7 @@
 #include <linux/err.h>
 #include <linux/wl12xx.h>
 
-static const struct wl12xx_platform_data *platform_data;
+static struct wl12xx_platform_data *platform_data;
 
 int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
 {
@@ -18,7 +18,7 @@ int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
        return 0;
 }
 
-const struct wl12xx_platform_data *wl12xx_get_platform_data(void)
+struct wl12xx_platform_data *wl12xx_get_platform_data(void)
 {
        if (!platform_data)
                return ERR_PTR(-ENODEV);
index 4b697395326eb4e660a099faf352a91c0c8df915..0d6373195d32c115a1d0f86b2a8ebe43e9ba7f46 100644 (file)
@@ -54,6 +54,9 @@ struct wl12xx_platform_data {
        int board_ref_clock;
        int board_tcxo_clock;
        unsigned long platform_quirks;
+       bool pwr_in_suspend;
+
+       struct wl1271_if_operations *ops;
 };
 
 /* Platform does not support level trigger interrupts */
@@ -73,6 +76,6 @@ int wl12xx_set_platform_data(const struct wl12xx_platform_data *data)
 
 #endif
 
-const struct wl12xx_platform_data *wl12xx_get_platform_data(void);
+struct wl12xx_platform_data *wl12xx_get_platform_data(void);
 
 #endif