staging: brcm80211: remove include file sbhndpio.h
[firefly-linux-kernel-4.4.55.git] / drivers / staging / brcm80211 / brcmsmac / wl_mac80211.c
index 65057329c994039aae87cea52071327d434689b4..137a382bac52b3d23ddd3c2087a9fe383d3c6dad 100644 (file)
 
 #include <linux/kernel.h>
 #include <linux/etherdevice.h>
-#include <linux/string.h>
+#include <linux/types.h>
 #include <linux/pci_ids.h>
-#include <bcmdefs.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/sched.h>
-#include <osl.h>
-#define WLC_MAXBSSCFG          1       /* single BSS configs */
-
-#include <wlc_cfg.h>
+#include <linux/firmware.h>
 #include <net/mac80211.h>
-#include <phy_version.h>
+
+#include <proto/802.11.h>
+#include <osl.h>
+#include <bcmdefs.h>
+#include <bcmwifi.h>
 #include <bcmutils.h>
 #include <pcicfg.h>
 #include <wlioctl.h>
-#include <wlc_key.h>
-#include <sbhndpio.h>
 #include <sbhnddma.h>
-#include <wlc_channel.h>
-#include <wlc_pub.h>
-#include <wlc_scb.h>
-#include <wl_dbg.h>
-#include <wl_export.h>
-
-#include <wl_mac80211.h>
-#include <linux/firmware.h>
-#include <wl_ucode.h>
-#include <d11ucode_ext.h>
 
+#include "phy/wlc_phy_int.h"
+#include "d11.h"
+#include "wlc_types.h"
+#include "wlc_cfg.h"
+#include "phy/phy_version.h"
+#include "wlc_key.h"
+#include "wlc_channel.h"
+#include "wlc_scb.h"
+#include "wlc_pub.h"
+#include "wl_dbg.h"
+#include "wl_export.h"
+#include "wl_ucode.h"
+#include "d11ucode_ext.h"
+#include "wl_mac80211.h"
 
 static void wl_timer(unsigned long data);
-static void _wl_timer(wl_timer_t *t);
+static void _wl_timer(struct wl_timer *t);
 
 
 static int ieee_hw_init(struct ieee80211_hw *hw);
@@ -78,6 +80,12 @@ static int wl_start(struct sk_buff *skb, struct wl_info *wl);
 static int wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw,
                        struct sk_buff *skb);
 static void wl_dpc(unsigned long data);
+static irqreturn_t wl_isr(int irq, void *dev_id);
+
+static int __devinit wl_pci_probe(struct pci_dev *pdev,
+                                 const struct pci_device_id *ent);
+static void wl_remove(struct pci_dev *pdev);
+static void wl_free(struct wl_info *wl);
 
 MODULE_AUTHOR("Broadcom Corporation");
 MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
@@ -93,8 +101,6 @@ static struct pci_device_id wl_id_table[] = {
 };
 
 MODULE_DEVICE_TABLE(pci, wl_id_table);
-static void wl_remove(struct pci_dev *pdev);
-
 
 #ifdef BCMDBG
 static int msglevel = 0xdeadbeef;
@@ -105,6 +111,8 @@ module_param(phymsglevel, int, 0);
 
 #define HW_TO_WL(hw)    (hw->priv)
 #define WL_TO_HW(wl)     (wl->pub->ieee_hw)
+
+/* MAC80211 callback functions */
 static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
 static int wl_ops_start(struct ieee80211_hw *hw);
 static void wl_ops_stop(struct ieee80211_hw *hw);
@@ -135,18 +143,21 @@ static void wl_ops_sta_notify(struct ieee80211_hw *hw,
 static int wl_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
                          const struct ieee80211_tx_queue_params *params);
 static u64 wl_ops_get_tsf(struct ieee80211_hw *hw);
-static int wl_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+static int wl_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                      struct ieee80211_sta *sta);
-static int wl_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+static int wl_ops_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                         struct ieee80211_sta *sta);
-static int wl_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                          enum ieee80211_ampdu_mlme_action action,
-                          struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+static int wl_ops_ampdu_action(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              enum ieee80211_ampdu_mlme_action action,
+                              struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+static void wl_ops_rfkill_poll(struct ieee80211_hw *hw);
 
 static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        int status;
        struct wl_info *wl = hw->priv;
+
        WL_LOCK(wl);
        if (!wl->pub->up) {
                WL_ERROR("ops->tx called while down\n");
@@ -162,28 +173,29 @@ static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 static int wl_ops_start(struct ieee80211_hw *hw)
 {
        struct wl_info *wl = hw->priv;
+       bool blocked;
        /*
          struct ieee80211_channel *curchan = hw->conf.channel;
          WL_NONE("%s : Initial channel: %d\n", __func__, curchan->hw_value);
        */
 
-       WL_LOCK(wl);
        ieee80211_wake_queues(hw);
+       WL_LOCK(wl);
+       blocked = wl_rfkill_set_hw_state(wl);
        WL_UNLOCK(wl);
+       if (!blocked)
+               wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
 
        return 0;
 }
 
 static void wl_ops_stop(struct ieee80211_hw *hw)
 {
+#ifdef BRCMDBG
        struct wl_info *wl = hw->priv;
        ASSERT(wl);
-       WL_LOCK(wl);
-       wl_down(wl);
+#endif /*BRCMDBG*/
        ieee80211_stop_queues(hw);
-       WL_UNLOCK(wl);
-
-       return;
 }
 
 static int
@@ -208,17 +220,28 @@ wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
        err = wl_up(wl);
        WL_UNLOCK(wl);
 
-       if (err != 0)
+       if (err != 0) {
                WL_ERROR("%s: wl_up() returned %d\n", __func__, err);
+       }
        return err;
 }
 
 static void
 wl_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
-       return;
+       struct wl_info *wl;
+
+       wl = HW_TO_WL(hw);
+
+       /* put driver in down state */
+       WL_LOCK(wl);
+       wl_down(wl);
+       WL_UNLOCK(wl);
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 static int
 ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
                 enum nl80211_channel_type type)
@@ -229,13 +252,12 @@ ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
        switch (type) {
        case NL80211_CHAN_HT20:
        case NL80211_CHAN_NO_HT:
-               WL_LOCK(wl);
                err = wlc_set(wl->wlc, WLC_SET_CHANNEL, chan->hw_value);
-               WL_UNLOCK(wl);
                break;
        case NL80211_CHAN_HT40MINUS:
        case NL80211_CHAN_HT40PLUS:
                WL_ERROR("%s: Need to implement 40 Mhz Channels!\n", __func__);
+               err = 1;
                break;
        }
 
@@ -251,9 +273,8 @@ static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
        int err = 0;
        int new_int;
 
+       WL_LOCK(wl);
        if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
-               WL_NONE("%s: Setting listen interval to %d\n",
-                       __func__, conf->listen_interval);
                if (wlc_iovar_setint
                    (wl->wlc, "bcn_li_bcn", conf->listen_interval)) {
                        WL_ERROR("%s: Error setting listen_interval\n",
@@ -265,13 +286,15 @@ static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
                ASSERT(new_int == conf->listen_interval);
        }
        if (changed & IEEE80211_CONF_CHANGE_MONITOR)
-               WL_NONE("Need to set monitor mode\n");
+               WL_ERROR("%s: change monitor mode: %s (implement)\n", __func__,
+                        conf->flags & IEEE80211_CONF_MONITOR ?
+                               "true" : "false");
        if (changed & IEEE80211_CONF_CHANGE_PS)
-               WL_NONE("Need to set Power-save mode\n");
+               WL_ERROR("%s: change power-save mode: %s (implement)\n",
+                        __func__, conf->flags & IEEE80211_CONF_PS ?
+                               "true" : "false");
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
-               WL_NONE("%s: Setting tx power to %d dbm\n",
-                       __func__, conf->power_level);
                if (wlc_iovar_setint
                    (wl->wlc, "qtxpower", conf->power_level * 4)) {
                        WL_ERROR("%s: Error setting power_level\n", __func__);
@@ -287,10 +310,6 @@ static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
                err = ieee_set_channel(hw, conf->channel, conf->channel_type);
        }
        if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
-               WL_NONE("%s: srl %d, lrl %d\n",
-                       __func__,
-                       conf->short_frame_max_tx_count,
-                       conf->long_frame_max_tx_count);
                if (wlc_set
                    (wl->wlc, WLC_SET_SRL,
                     conf->short_frame_max_tx_count) < 0) {
@@ -307,6 +326,7 @@ static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
        }
 
  config_out:
+       WL_UNLOCK(wl);
        return err;
 }
 
@@ -318,64 +338,103 @@ wl_ops_bss_info_changed(struct ieee80211_hw *hw,
        struct wl_info *wl = HW_TO_WL(hw);
        int val;
 
-
        if (changed & BSS_CHANGED_ASSOC) {
-               WL_ERROR("Associated:\t%s\n", info->assoc ? "True" : "False");
                /* association status changed (associated/disassociated)
                 * also implies a change in the AID.
                 */
+               WL_ERROR("%s: %s: %sassociated\n", KBUILD_MODNAME, __func__,
+                        info->assoc ? "" : "dis");
+               wlc_associate_upd(wl->wlc, info->assoc);
        }
        if (changed & BSS_CHANGED_ERP_CTS_PROT) {
-               WL_NONE("Use_cts_prot:\t%s Implement me\n",
-                       info->use_cts_prot ? "True" : "False");
                /* CTS protection changed */
+               WL_ERROR("%s: use_cts_prot: %s (implement)\n", __func__,
+                       info->use_cts_prot ? "true" : "false");
        }
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
-               WL_NONE("Short preamble:\t%s Implement me\n",
-                       info->use_short_preamble ? "True" : "False");
                /* preamble changed */
+               WL_ERROR("%s: short preamble: %s (implement)\n", __func__,
+                       info->use_short_preamble ? "true" : "false");
        }
        if (changed & BSS_CHANGED_ERP_SLOT) {
-               WL_NONE("Changing short slot:\t%s\n",
-                       info->use_short_slot ? "True" : "False");
+               /* slot timing changed */
                if (info->use_short_slot)
                        val = 1;
                else
                        val = 0;
+               WL_LOCK(wl);
                wlc_set(wl->wlc, WLC_SET_SHORTSLOT_OVERRIDE, val);
-               /* slot timing changed */
+               WL_UNLOCK(wl);
        }
 
        if (changed & BSS_CHANGED_HT) {
-               WL_NONE("%s: HT mode - Implement me\n", __func__);
                /* 802.11n parameters changed */
+               u16 mode = info->ht_operation_mode;
+               WL_NONE("%s: HT mode: 0x%04X\n", __func__, mode);
+               wlc_protection_upd(wl->wlc, WLC_PROT_N_CFG,
+                       mode & IEEE80211_HT_OP_MODE_PROTECTION);
+               wlc_protection_upd(wl->wlc, WLC_PROT_N_NONGF,
+                       mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+               wlc_protection_upd(wl->wlc, WLC_PROT_N_OBSS,
+                       mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
        }
        if (changed & BSS_CHANGED_BASIC_RATES) {
-               WL_NONE("Need to change Basic Rates:\t0x%x! Implement me\n",
-                       (u32) info->basic_rates);
                /* Basic rateset changed */
+               WL_ERROR("%s: Need to change Basic Rates: 0x%x (implement)\n",
+                        __func__, (u32) info->basic_rates);
        }
        if (changed & BSS_CHANGED_BEACON_INT) {
-               WL_NONE("Beacon Interval:\t%d Implement me\n",
-                       info->beacon_int);
                /* Beacon interval changed */
+               WL_NONE("%s: Beacon Interval: %d\n",
+                       __func__, info->beacon_int);
+               wlc_set(wl->wlc, WLC_SET_BCNPRD, info->beacon_int);
        }
        if (changed & BSS_CHANGED_BSSID) {
-               WL_NONE("new BSSID:\taid %d  bss:%pM\n",
-                       info->aid, info->bssid);
                /* BSSID changed, for whatever reason (IBSS and managed mode) */
-               /* FIXME: need to store bssid in bsscfg */
+               WL_NONE("%s: new BSSID: aid %d  bss:%pM\n", __func__,
+                       info->aid, info->bssid);
+               WL_LOCK(wl);
                wlc_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
                                  info->bssid);
+               WL_UNLOCK(wl);
        }
        if (changed & BSS_CHANGED_BEACON) {
-               WL_ERROR("BSS_CHANGED_BEACON\n");
                /* Beacon data changed, retrieve new beacon (beaconing modes) */
+               WL_ERROR("%s: beacon changed\n", __func__);
        }
        if (changed & BSS_CHANGED_BEACON_ENABLED) {
-               WL_ERROR("Beacon enabled:\t%s\n",
-                        info->enable_beacon ? "True" : "False");
                /* Beaconing should be enabled/disabled (beaconing modes) */
+               WL_ERROR("%s: Beacon enabled: %s\n", __func__,
+                        info->enable_beacon ? "true" : "false");
+       }
+       if (changed & BSS_CHANGED_CQM) {
+               /* Connection quality monitor config changed */
+               WL_ERROR("%s: cqm change: threshold %d, hys %d (implement)\n",
+                       __func__, info->cqm_rssi_thold, info->cqm_rssi_hyst);
+       }
+       if (changed & BSS_CHANGED_IBSS) {
+               /* IBSS join status changed */
+               WL_ERROR("%s: IBSS joined: %s (implement)\n", __func__,
+                       info->ibss_joined ? "true" : "false");
+       }
+       if (changed & BSS_CHANGED_ARP_FILTER) {
+               /* Hardware ARP filter address list or state changed */
+               WL_ERROR("%s: arp filtering: enabled %s, count %d (implement)\n",
+                       __func__, info->arp_filter_enabled ? "true" : "false",
+                       info->arp_addr_cnt);
+       }
+       if (changed & BSS_CHANGED_QOS) {
+               /*
+                * QoS for this association was enabled/disabled.
+                * Note that it is only ever disabled for station mode.
+                */
+               WL_ERROR("%s: qos enabled: %s (implement)\n", __func__,
+                       info->qos ? "true" : "false");
+       }
+       if (changed & BSS_CHANGED_IDLE) {
+               /* Idle changed for this BSS/interface */
+               WL_ERROR("%s: BSS idle: %s (implement)\n", __func__,
+                       info->idle ? "true" : "false");
        }
        return;
 }
@@ -419,19 +478,27 @@ wl_ops_configure_filter(struct ieee80211_hw *hw,
 static int
 wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
 {
-       WL_ERROR("%s: Enter\n", __func__);
+       WL_NONE("%s: Enter\n", __func__);
        return 0;
 }
 
 static void wl_ops_sw_scan_start(struct ieee80211_hw *hw)
 {
+       struct wl_info *wl = hw->priv;
        WL_NONE("Scan Start\n");
+       WL_LOCK(wl);
+       wlc_scan_start(wl->wlc);
+       WL_UNLOCK(wl);
        return;
 }
 
 static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw)
 {
+       struct wl_info *wl = hw->priv;
        WL_NONE("Scan Complete\n");
+       WL_LOCK(wl);
+       wlc_scan_stop(wl->wlc);
+       WL_UNLOCK(wl);
        return;
 }
 
@@ -445,13 +512,26 @@ static int
 wl_ops_get_stats(struct ieee80211_hw *hw,
                 struct ieee80211_low_level_stats *stats)
 {
-       WL_ERROR("%s: Enter\n", __func__);
+       struct wl_info *wl = hw->priv;
+       struct wl_cnt *cnt;
+
+       WL_LOCK(wl);
+       cnt = wl->pub->_cnt;
+       stats->dot11ACKFailureCount = cnt->txnoack;
+       stats->dot11RTSFailureCount = cnt->txnocts;
+       stats->dot11FCSErrorCount = cnt->rxcrc;
+       stats->dot11RTSSuccessCount = cnt->txrts;
+       WL_UNLOCK(wl);
        return 0;
 }
 
 static int wl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 {
-       WL_ERROR("%s: Enter\n", __func__);
+       struct wl_info *wl = hw->priv;
+
+       WL_LOCK(wl);
+       wlc_iovar_setint(wl->wlc, "rtsthresh", value & 0xFFFF);
+       WL_UNLOCK(wl);
        return 0;
 }
 
@@ -492,8 +572,8 @@ static u64 wl_ops_get_tsf(struct ieee80211_hw *hw)
 }
 
 static int
-wl_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-          struct ieee80211_sta *sta)
+wl_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+              struct ieee80211_sta *sta)
 {
        struct scb *scb;
 
@@ -527,23 +607,24 @@ wl_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 }
 
 static int
-wl_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-             struct ieee80211_sta *sta)
+wl_ops_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                 struct ieee80211_sta *sta)
 {
        WL_NONE("%s: Enter\n", __func__);
        return 0;
 }
 
 static int
-wl_ampdu_action(struct ieee80211_hw *hw,
-               struct ieee80211_vif *vif,
-               enum ieee80211_ampdu_mlme_action action,
-               struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+wl_ops_ampdu_action(struct ieee80211_hw *hw,
+                   struct ieee80211_vif *vif,
+                   enum ieee80211_ampdu_mlme_action action,
+                   struct ieee80211_sta *sta, u16 tid, u16 *ssn)
 {
 #if defined(BCMDBG)
        struct scb *scb = (struct scb *)sta->drv_priv;
 #endif
        struct wl_info *wl = hw->priv;
+       int status;
 
        ASSERT(scb->magic == SCB_MAGIC);
        switch (action) {
@@ -554,7 +635,10 @@ wl_ampdu_action(struct ieee80211_hw *hw,
                WL_NONE("%s: action = IEEE80211_AMPDU_RX_STOP\n", __func__);
                break;
        case IEEE80211_AMPDU_TX_START:
-               if (!wlc_aggregatable(wl->wlc, tid)) {
+               WL_LOCK(wl);
+               status = wlc_aggregatable(wl->wlc, tid);
+               WL_UNLOCK(wl);
+               if (!status) {
                        /* WL_ERROR("START: tid %d is not agg' able, return FAILURE to stack\n", tid); */
                        return -1;
                }
@@ -579,6 +663,19 @@ wl_ampdu_action(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void wl_ops_rfkill_poll(struct ieee80211_hw *hw)
+{
+       struct wl_info *wl = HW_TO_WL(hw);
+       bool blocked;
+
+       WL_LOCK(wl);
+       blocked = wlc_check_radio_disabled(wl->wlc);
+       WL_UNLOCK(wl);
+
+       WL_NONE("wl: rfkill_poll: %d\n", blocked);
+       wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
+}
+
 static const struct ieee80211_ops wl_ops = {
        .tx = wl_ops_tx,
        .start = wl_ops_start,
@@ -597,14 +694,18 @@ static const struct ieee80211_ops wl_ops = {
        .sta_notify = wl_ops_sta_notify,
        .conf_tx = wl_ops_conf_tx,
        .get_tsf = wl_ops_get_tsf,
-       .sta_add = wl_sta_add,
-       .sta_remove = wl_sta_remove,
-       .ampdu_action = wl_ampdu_action,
+       .sta_add = wl_ops_sta_add,
+       .sta_remove = wl_ops_sta_remove,
+       .ampdu_action = wl_ops_ampdu_action,
+       .rfkill_poll = wl_ops_rfkill_poll,
 };
 
+/*
+ * is called in wl_pci_probe() context, therefore no locking required.
+ */
 static int wl_set_hint(struct wl_info *wl, char *abbrev)
 {
-       WL_ERROR("%s: Sending country code %c%c to MAC80211\n",
+       WL_NONE("%s: Sending country code %c%c to MAC80211\n",
                 __func__, abbrev[0], abbrev[1]);
        return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
 }
@@ -619,6 +720,9 @@ static int wl_set_hint(struct wl_info *wl, char *abbrev)
  * is defined, wl_attach will never be called, and thus, gcc will issue
  * a warning that this function is defined but not used if we declare
  * it as static.
+ *
+ *
+ * is called in wl_pci_probe() context, therefore no locking required.
  */
 static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
                            uint bustype, void *btparam, uint irq)
@@ -676,9 +780,9 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
        spin_lock_init(&wl->isr_lock);
 
        /* prepare ucode */
-       if (wl_request_fw(wl, (struct pci_dev *)btparam)) {
-               printf("%s: Failed to find firmware usually in %s\n",
-                       KBUILD_MODNAME, "/lib/firmware/brcm");
+       if (wl_request_fw(wl, (struct pci_dev *)btparam) < 0) {
+               WL_ERROR("%s: Failed to find firmware usually in %s\n",
+                        KBUILD_MODNAME, "/lib/firmware/brcm");
                wl_release_fw(wl);
                wl_remove((struct pci_dev *)btparam);
                goto fail1;
@@ -689,8 +793,8 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
                             wl->regsva, wl->bcm_bustype, btparam, &err);
        wl_release_fw(wl);
        if (!wl->wlc) {
-               printf("%s: wlc_attach() failed with code %d\n",
-                       KBUILD_MODNAME, err);
+               WL_ERROR("%s: wlc_attach() failed with code %d\n",
+                        KBUILD_MODNAME, err);
                goto fail;
        }
        wl->pub = wlc_pub(wl->wlc);
@@ -720,7 +824,7 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
                goto fail;
        }
 
-       bcopy(&wl->pub->cur_etheraddr, perm, ETH_ALEN);
+       memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN);
        ASSERT(is_valid_ether_addr(perm));
        SET_IEEE80211_PERM_ADDR(hw, perm);
 
@@ -738,18 +842,11 @@ static struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
                WL_ERROR("%s: regulatory_hint failed, status %d\n",
                         __func__, err);
        }
-       WL_ERROR("wl%d: Broadcom BCM43xx 802.11 MAC80211 Driver (" PHY_VERSION_STR ")",
-                unit);
-
-#ifdef BCMDBG
-       printf(" (Compiled at " __TIME__ " on " __DATE__ ")");
-#endif                         /* BCMDBG */
-       printf("\n");
 
        wl_found++;
        return wl;
 
- fail:
+fail:
        wl_free(wl);
 fail1:
        return NULL;
@@ -923,6 +1020,9 @@ static struct ieee80211_supported_band wl_band_5GHz_nphy = {
                   }
 };
 
+/*
+ * is called in wl_pci_probe() context, therefore no locking required.
+ */
 static int ieee_hw_rate_init(struct ieee80211_hw *hw)
 {
        struct wl_info *wl = HW_TO_WL(hw);
@@ -967,6 +1067,9 @@ static int ieee_hw_rate_init(struct ieee80211_hw *hw)
        return 0;
 }
 
+/*
+ * is called in wl_pci_probe() context, therefore no locking required.
+ */
 static int ieee_hw_init(struct ieee80211_hw *hw)
 {
        hw->flags = IEEE80211_HW_SIGNAL_DBM
@@ -999,8 +1102,9 @@ static int ieee_hw_init(struct ieee80211_hw *hw)
  * This function determines if a device pointed to by pdev is a WL device,
  * and if so, performs a wl_attach() on it.
  *
+ * Perimeter lock is initialized in the course of this function.
  */
-int __devinit
+static int __devinit
 wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        int rc;
@@ -1060,7 +1164,6 @@ wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        return 0;
 }
 
-#ifdef LINUXSTA_PS
 static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct wl_info *wl;
@@ -1075,11 +1178,12 @@ static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
                return -ENODEV;
        }
 
+       /* only need to flag hw is down for proper resume */
        WL_LOCK(wl);
-       wl_down(wl);
        wl->pub->hw_up = false;
        WL_UNLOCK(wl);
-       pci_save_state(pdev, wl->pci_psstate);
+
+       pci_save_state(pdev);
        pci_disable_device(pdev);
        return pci_set_power_state(pdev, PCI_D3hot);
 }
@@ -1103,7 +1207,7 @@ static int wl_resume(struct pci_dev *pdev)
        if (err)
                return err;
 
-       pci_restore_state(pdev, wl->pci_psstate);
+       pci_restore_state(pdev);
 
        err = pci_enable_device(pdev);
        if (err)
@@ -1115,18 +1219,22 @@ static int wl_resume(struct pci_dev *pdev)
        if ((val & 0x0000ff00) != 0)
                pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
-       WL_LOCK(wl);
-       err = wl_up(wl);
-       WL_UNLOCK(wl);
-
+       /*
+       *  done. driver will be put in up state
+       *  in wl_ops_add_interface() call.
+       */
        return err;
 }
-#endif                         /* LINUXSTA_PS */
 
+/*
+* called from both kernel as from wl_*()
+* precondition: perimeter lock is not acquired.
+*/
 static void wl_remove(struct pci_dev *pdev)
 {
        struct wl_info *wl;
        struct ieee80211_hw *hw;
+       int status;
 
        hw = pci_get_drvdata(pdev);
        wl = HW_TO_WL(hw);
@@ -1134,11 +1242,17 @@ static void wl_remove(struct pci_dev *pdev)
                WL_ERROR("wl: wl_remove: pci_get_drvdata failed\n");
                return;
        }
-       if (!wlc_chipmatch(pdev->vendor, pdev->device)) {
+
+       WL_LOCK(wl);
+       status = wlc_chipmatch(pdev->vendor, pdev->device);
+       WL_UNLOCK(wl);
+       if (!status) {
                WL_ERROR("wl: wl_remove: wlc_chipmatch failed\n");
                return;
        }
        if (wl->wlc) {
+               wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
+               wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
                ieee80211_unregister_hw(hw);
                WL_LOCK(wl);
                wl_down(wl);
@@ -1154,12 +1268,10 @@ static void wl_remove(struct pci_dev *pdev)
 }
 
 static struct pci_driver wl_pci_driver = {
-       .name  = KBUILD_MODNAME,
-       .probe = wl_pci_probe,
-#ifdef LINUXSTA_PS
-       .suspend = wl_suspend,
-       .resume  = wl_resume,
-#endif                         /* LINUXSTA_PS */
+       .name     = KBUILD_MODNAME,
+       .probe    = wl_pci_probe,
+       .suspend  = wl_suspend,
+       .resume   = wl_resume,
        .remove   = __devexit_p(wl_remove),
        .id_table = wl_id_table,
 };
@@ -1227,10 +1339,12 @@ module_exit(wl_module_exit);
  * This function frees resources owned by the WL device pointed to
  * by the wl parameter.
  *
+ * precondition: can both be called locked and unlocked
+ *
  */
-void wl_free(struct wl_info *wl)
+static void wl_free(struct wl_info *wl)
 {
-       wl_timer_t *t, *next;
+       struct wl_timer *t, *next;
        struct osl_info *osh;
 
        ASSERT(wl);
@@ -1286,7 +1400,10 @@ void wl_free(struct wl_info *wl)
        osl_detach(osh);
 }
 
-/* transmit a packet */
+/*
+ * transmit a packet
+ * precondition: perimeter lock has been acquired
+ */
 static int BCMFASTPATH wl_start(struct sk_buff *skb, struct wl_info *wl)
 {
        if (!wl)
@@ -1302,12 +1419,18 @@ wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, struct sk_buff *skb)
        return NETDEV_TX_OK;
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
                      int prio)
 {
        WL_ERROR("Shouldn't be here %s\n", __func__);
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 void wl_init(struct wl_info *wl)
 {
        WL_TRACE("wl%d: wl_init\n", wl->pub->unit);
@@ -1317,6 +1440,9 @@ void wl_init(struct wl_info *wl)
        wlc_init(wl->wlc);
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 uint wl_reset(struct wl_info *wl)
 {
        WL_TRACE("wl%d: wl_reset\n", wl->pub->unit);
@@ -1342,6 +1468,9 @@ void BCMFASTPATH wl_intrson(struct wl_info *wl)
        INT_UNLOCK(wl, flags);
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 bool wl_alloc_dma_resources(struct wl_info *wl, uint addrwidth)
 {
        return true;
@@ -1367,6 +1496,9 @@ void wl_intrsrestore(struct wl_info *wl, u32 macintmask)
        INT_UNLOCK(wl, flags);
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 int wl_up(struct wl_info *wl)
 {
        int error = 0;
@@ -1379,6 +1511,9 @@ int wl_up(struct wl_info *wl)
        return error;
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 void wl_down(struct wl_info *wl)
 {
        uint callbacks, ret_val = 0;
@@ -1398,7 +1533,7 @@ void wl_down(struct wl_info *wl)
        WL_LOCK(wl);
 }
 
-irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id)
+static irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id)
 {
        struct wl_info *wl;
        bool ours, wantdpc;
@@ -1463,38 +1598,18 @@ static void BCMFASTPATH wl_dpc(unsigned long data)
        WL_UNLOCK(wl);
 }
 
-static void wl_link_up(struct wl_info *wl, char *ifname)
-{
-       WL_ERROR("wl%d: link up (%s)\n", wl->pub->unit, ifname);
-}
-
-static void wl_link_down(struct wl_info *wl, char *ifname)
-{
-       WL_ERROR("wl%d: link down (%s)\n", wl->pub->unit, ifname);
-}
-
-void wl_event(struct wl_info *wl, char *ifname, wlc_event_t *e)
-{
-
-       switch (e->event.event_type) {
-       case WLC_E_LINK:
-       case WLC_E_NDIS_LINK:
-               if (e->event.flags & WLC_EVENT_MSG_LINK)
-                       wl_link_up(wl, ifname);
-               else
-                       wl_link_down(wl, ifname);
-               break;
-       case WLC_E_RADIO:
-               break;
-       }
-}
-
+/*
+ * is called by the kernel from software irq context
+ */
 static void wl_timer(unsigned long data)
 {
-       _wl_timer((wl_timer_t *) data);
+       _wl_timer((struct wl_timer *) data);
 }
 
-static void _wl_timer(wl_timer_t *t)
+/*
+* precondition: perimeter lock is not acquired
+ */
+static void _wl_timer(struct wl_timer *t)
 {
        WL_LOCK(t->wl);
 
@@ -1515,18 +1630,24 @@ static void _wl_timer(wl_timer_t *t)
        WL_UNLOCK(t->wl);
 }
 
-wl_timer_t *wl_init_timer(struct wl_info *wl, void (*fn) (void *arg), void *arg,
-                         const char *name)
+/*
+ * Adds a timer to the list. Caller supplies a timer function.
+ * Is called from wlc.
+ *
+ * precondition: perimeter lock has been acquired
+ */
+struct wl_timer *wl_init_timer(struct wl_info *wl, void (*fn) (void *arg),
+                              void *arg, const char *name)
 {
-       wl_timer_t *t;
+       struct wl_timer *t;
 
-       t = kmalloc(sizeof(wl_timer_t), GFP_ATOMIC);
+       t = kmalloc(sizeof(struct wl_timer), GFP_ATOMIC);
        if (!t) {
                WL_ERROR("wl%d: wl_init_timer: out of memory\n", wl->pub->unit);
                return 0;
        }
 
-       memset(t, 0, sizeof(wl_timer_t));
+       memset(t, 0, sizeof(struct wl_timer));
 
        init_timer(&t->timer);
        t->timer.data = (unsigned long) t;
@@ -1548,8 +1669,10 @@ wl_timer_t *wl_init_timer(struct wl_info *wl, void (*fn) (void *arg), void *arg,
 
 /* BMAC_NOTE: Add timer adds only the kernel timer since it's going to be more accurate
  * as well as it's easier to make it periodic
+ *
+ * precondition: perimeter lock has been acquired
  */
-void wl_add_timer(struct wl_info *wl, wl_timer_t *t, uint ms, int periodic)
+void wl_add_timer(struct wl_info *wl, struct wl_timer *t, uint ms, int periodic)
 {
 #ifdef BCMDBG
        if (t->set) {
@@ -1568,8 +1691,12 @@ void wl_add_timer(struct wl_info *wl, wl_timer_t *t, uint ms, int periodic)
        add_timer(&t->timer);
 }
 
-/* return true if timer successfully deleted, false if still pending */
-bool wl_del_timer(struct wl_info *wl, wl_timer_t *t)
+/*
+ * return true if timer successfully deleted, false if still pending
+ *
+ * precondition: perimeter lock has been acquired
+ */
+bool wl_del_timer(struct wl_info *wl, struct wl_timer *t)
 {
        if (t->set) {
                t->set = false;
@@ -1582,9 +1709,12 @@ bool wl_del_timer(struct wl_info *wl, wl_timer_t *t)
        return true;
 }
 
-void wl_free_timer(struct wl_info *wl, wl_timer_t *t)
+/*
+ * precondition: perimeter lock has been acquired
+ */
+void wl_free_timer(struct wl_info *wl, struct wl_timer *t)
 {
-       wl_timer_t *tmp;
+       struct wl_timer *tmp;
 
        /* delete the timer in case it is active */
        wl_del_timer(wl, t);
@@ -1616,37 +1746,42 @@ void wl_free_timer(struct wl_info *wl, wl_timer_t *t)
 
 }
 
+/*
+ * runs in software irq context
+ *
+ * precondition: perimeter lock is not acquired
+ */
 static int wl_linux_watchdog(void *ctx)
 {
        struct wl_info *wl = (struct wl_info *) ctx;
+       struct wl_cnt *cnt;
        struct net_device_stats *stats = NULL;
        uint id;
        /* refresh stats */
        if (wl->pub->up) {
                ASSERT(wl->stats_id < 2);
 
+               cnt = wl->pub->_cnt;
                id = 1 - wl->stats_id;
-
                stats = &wl->stats_watchdog[id];
-               stats->rx_packets = WLCNTVAL(wl->pub->_cnt->rxframe);
-               stats->tx_packets = WLCNTVAL(wl->pub->_cnt->txframe);
-               stats->rx_bytes = WLCNTVAL(wl->pub->_cnt->rxbyte);
-               stats->tx_bytes = WLCNTVAL(wl->pub->_cnt->txbyte);
-               stats->rx_errors = WLCNTVAL(wl->pub->_cnt->rxerror);
-               stats->tx_errors = WLCNTVAL(wl->pub->_cnt->txerror);
+               stats->rx_packets = cnt->rxframe;
+               stats->tx_packets = cnt->txframe;
+               stats->rx_bytes = cnt->rxbyte;
+               stats->tx_bytes = cnt->txbyte;
+               stats->rx_errors = cnt->rxerror;
+               stats->tx_errors = cnt->txerror;
                stats->collisions = 0;
 
                stats->rx_length_errors = 0;
-               stats->rx_over_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
-               stats->rx_crc_errors = WLCNTVAL(wl->pub->_cnt->rxcrc);
+               stats->rx_over_errors = cnt->rxoflo;
+               stats->rx_crc_errors = cnt->rxcrc;
                stats->rx_frame_errors = 0;
-               stats->rx_fifo_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
+               stats->rx_fifo_errors = cnt->rxoflo;
                stats->rx_missed_errors = 0;
 
-               stats->tx_fifo_errors = WLCNTVAL(wl->pub->_cnt->txuflo);
+               stats->tx_fifo_errors = cnt->txuflo;
 
                wl->stats_id = id;
-
        }
 
        return 0;
@@ -1663,6 +1798,9 @@ char *wl_firmwares[WL_MAX_FW] = {
        NULL
 };
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
 int wl_ucode_init_buf(struct wl_info *wl, void **pbuf, u32 idx)
 {
        int i, entry;
@@ -1676,19 +1814,25 @@ int wl_ucode_init_buf(struct wl_info *wl, void **pbuf, u32 idx)
                                pdata = wl->fw.fw_bin[i]->data + hdr->offset;
                                *pbuf = kmalloc(hdr->len, GFP_ATOMIC);
                                if (*pbuf == NULL) {
-                                       printf("fail to alloc %d bytes\n",
-                                              hdr->len);
+                                       WL_ERROR("fail to alloc %d bytes\n",
+                                                hdr->len);
+                                       goto fail;
                                }
-                               bcopy(pdata, *pbuf, hdr->len);
+                               memcpy(*pbuf, pdata, hdr->len);
                                return 0;
                        }
                }
        }
-       printf("ERROR: ucode buf tag:%d can not be found!\n", idx);
+       WL_ERROR("ERROR: ucode buf tag:%d can not be found!\n", idx);
        *pbuf = NULL;
-       return -1;
+fail:
+       return BCME_NOTFOUND;
 }
 
+/*
+ * Precondition: Since this function is called in wl_pci_probe() context,
+ * no locking is required.
+ */
 int wl_ucode_init_uint(struct wl_info *wl, u32 *data, u32 idx)
 {
        int i, entry;
@@ -1706,10 +1850,14 @@ int wl_ucode_init_uint(struct wl_info *wl, u32 *data, u32 idx)
                        }
                }
        }
-       printf("ERROR: ucode tag:%d can not be found!\n", idx);
+       WL_ERROR("ERROR: ucode tag:%d can not be found!\n", idx);
        return -1;
 }
 
+/*
+ * Precondition: Since this function is called in wl_pci_probe() context,
+ * no locking is required.
+ */
 static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
 {
        int status;
@@ -1726,9 +1874,8 @@ static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
                WL_NONE("request fw %s\n", fw_name);
                status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
                if (status) {
-                       printf("%s: fail to load firmware %s\n",
-                               KBUILD_MODNAME, fw_name);
-                       wl_release_fw(wl);
+                       WL_ERROR("%s: fail to load firmware %s\n",
+                                KBUILD_MODNAME, fw_name);
                        return status;
                }
                WL_NONE("request fw %s\n", fw_name);
@@ -1736,9 +1883,8 @@ static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
                        UCODE_LOADER_API_VER);
                status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
                if (status) {
-                       printf("%s: fail to load firmware %s\n",
-                               KBUILD_MODNAME, fw_name);
-                       wl_release_fw(wl);
+                       WL_ERROR("%s: fail to load firmware %s\n",
+                                KBUILD_MODNAME, fw_name);
                        return status;
                }
                wl->fw.hdr_num_entries[i] =
@@ -1750,11 +1896,18 @@ static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
        return wl_ucode_data_init(wl);
 }
 
+/*
+ * precondition: can both be called locked and unlocked
+ */
 void wl_ucode_free_buf(void *p)
 {
        kfree(p);
 }
 
+/*
+ * Precondition: Since this function is called in wl_pci_probe() context,
+ * no locking is required.
+ */
 static void wl_release_fw(struct wl_info *wl)
 {
        int i;
@@ -1767,6 +1920,9 @@ static void wl_release_fw(struct wl_info *wl)
 
 /*
  * checks validity of all firmware images loaded from user space
+ *
+ * Precondition: Since this function is called in wl_pci_probe() context,
+ * no locking is required.
  */
 int wl_check_firmwares(struct wl_info *wl)
 {
@@ -1785,19 +1941,19 @@ int wl_check_firmwares(struct wl_info *wl)
                        WL_ERROR("%s: invalid bin/hdr fw\n", __func__);
                        rc = -EBADF;
                } else if (fw_hdr->size % sizeof(struct wl_fw_hdr)) {
-                       WL_ERROR("%s: non integral fw hdr file size %d/%zu\n",
+                       WL_ERROR("%s: non integral fw hdr file size %zu/%zu\n",
                                 __func__, fw_hdr->size,
                                 sizeof(struct wl_fw_hdr));
                        rc = -EBADF;
                } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
-                       WL_ERROR("%s: out of bounds fw file size %d\n",
+                       WL_ERROR("%s: out of bounds fw file size %zu\n",
                                 __func__, fw->size);
                        rc = -EBADF;
                } else {
                        /* check if ucode section overruns firmware image */
                        ucode_hdr = (struct wl_fw_hdr *)fw_hdr->data;
-                       for (entry = 0; entry < wl->fw.hdr_num_entries[i] && rc;
-                            entry++, ucode_hdr++) {
+                       for (entry = 0; entry < wl->fw.hdr_num_entries[i] &&
+                            !rc; entry++, ucode_hdr++) {
                                if (ucode_hdr->offset + ucode_hdr->len >
                                    fw->size) {
                                        WL_ERROR("%s: conflicting bin/hdr\n",
@@ -1814,3 +1970,19 @@ int wl_check_firmwares(struct wl_info *wl)
        return rc;
 }
 
+/*
+ * precondition: perimeter lock has been acquired
+ */
+bool wl_rfkill_set_hw_state(struct wl_info *wl)
+{
+       bool blocked = wlc_check_radio_disabled(wl->wlc);
+
+       WL_NONE("%s: update hw state: blocked=%s\n", __func__,
+               blocked ? "true" : "false");
+       WL_UNLOCK(wl);
+       wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
+       if (blocked)
+               wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
+       WL_LOCK(wl);
+       return blocked;
+}