#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);
static int wl_found;
-struct ieee80211_tkip_data {
-#define TKIP_KEY_LEN 32
- u8 key[TKIP_KEY_LEN];
- int key_set;
-
- u32 tx_iv32;
- u16 tx_iv16;
- u16 tx_ttak[5];
- int tx_phase1_done;
-
- u32 rx_iv32;
- u16 rx_iv16;
- u16 rx_ttak[5];
- int rx_phase1_done;
- u32 rx_iv32_new;
- u16 rx_iv16_new;
-
- u32 dot11RSNAStatsTKIPReplays;
- u32 dot11RSNAStatsTKIPICVErrors;
- u32 dot11RSNAStatsTKIPLocalMICFailures;
-
- int key_idx;
-
- struct crypto_tfm *tfm_arc4;
- struct crypto_tfm *tfm_michael;
-
- /* scratch buffers for virt_to_page() (crypto API) */
- u8 rx_hdr[16], tx_hdr[16];
-};
-
#define WL_DEV_IF(dev) ((struct wl_if *)netdev_priv(dev))
#define WL_INFO(dev) ((struct wl_info *)(WL_DEV_IF(dev)->wl))
static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev);
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.");
};
MODULE_DEVICE_TABLE(pci, wl_id_table);
-static void wl_remove(struct pci_dev *pdev);
-
#ifdef BCMDBG
static int msglevel = 0xdeadbeef;
#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);
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");
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
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)
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;
}
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",
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__);
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) {
}
config_out:
+ WL_UNLOCK(wl);
return err;
}
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;
}
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;
}
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;
}
}
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;
}
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) {
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;
}
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,
.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);
}
* 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)
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;
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);
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);
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;
}
};
+/*
+ * 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);
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
* 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;
return 0;
}
-#ifdef LINUXSTA_PS
static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct wl_info *wl;
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);
}
if (err)
return err;
- pci_restore_state(pdev, wl->pci_psstate);
+ pci_restore_state(pdev);
err = pci_enable_device(pdev);
if (err)
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);
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);
}
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,
};
* 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);
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)
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);
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);
INT_UNLOCK(wl, flags);
}
+/*
+ * precondition: perimeter lock has been acquired
+ */
bool wl_alloc_dma_resources(struct wl_info *wl, uint addrwidth)
{
return true;
INT_UNLOCK(wl, flags);
}
+/*
+ * precondition: perimeter lock has been acquired
+ */
int wl_up(struct wl_info *wl)
{
int error = 0;
return error;
}
+/*
+ * precondition: perimeter lock has been acquired
+ */
void wl_down(struct wl_info *wl)
{
uint callbacks, ret_val = 0;
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;
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);
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;
/* 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) {
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;
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);
}
+/*
+ * 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;
NULL
};
+/*
+ * precondition: perimeter lock has been acquired
+ */
int wl_ucode_init_buf(struct wl_info *wl, void **pbuf, u32 idx)
{
int i, entry;
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;
}
}
}
- 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;
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);
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] =
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;
/*
* 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)
{
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",
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;
+}